1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
|
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE LambdaCase #-}
-- | Platform description
module GHC.Platform
( Platform (..)
, PlatformWordSize(..)
, platformArch
, platformOS
, ArchOS(..)
, Arch(..)
, OS(..)
, ArmISA(..)
, ArmISAExt(..)
, ArmABI(..)
, PPC_64ABI(..)
, ByteOrder(..)
, target32Bit
, isARM
, osElfTarget
, osMachOTarget
, osSubsectionsViaSymbols
, platformUsesFrameworks
, platformWordSizeInBytes
, platformWordSizeInBits
, platformMinInt
, platformMaxInt
, platformMaxWord
, platformInIntRange
, platformInWordRange
, platformCConvNeedsExtension
, PlatformMisc(..)
, SseVersion (..)
, BmiVersion (..)
, wordAlignment
-- * SSE and AVX
, isSseEnabled
, isSse2Enabled
-- * Platform constants
, PlatformConstants(..)
, lookupPlatformConstants
, platformConstants
-- * Shared libraries
, platformSOName
, platformHsSOName
, platformSOExt
, genericPlatform
)
where
import Prelude -- See Note [Why do we import Prelude here?]
import GHC.Read
import GHC.ByteOrder (ByteOrder(..))
import GHC.Platform.Constants
import GHC.Platform.ArchOS
import GHC.Types.Basic (Alignment, alignmentOf)
import GHC.Utils.Panic.Plain
import Data.Word
import Data.Int
import System.FilePath
import System.Directory
-- | Platform description
--
-- This is used to describe platforms so that we can generate code for them.
data Platform = Platform
{ platformArchOS :: !ArchOS -- ^ Architecture and OS
, platformWordSize :: !PlatformWordSize -- ^ Word size
, platformByteOrder :: !ByteOrder -- ^ Byte order (endianness)
, platformUnregisterised :: !Bool
, platformHasGnuNonexecStack :: !Bool
, platformHasIdentDirective :: !Bool
, platformHasSubsectionsViaSymbols :: !Bool
, platformIsCrossCompiling :: !Bool
, platformLeadingUnderscore :: !Bool -- ^ Symbols need underscore prefix
, platformTablesNextToCode :: !Bool
-- ^ Determines whether we will be compiling info tables that reside just
-- before the entry code, or with an indirection to the entry code. See
-- TABLES_NEXT_TO_CODE in rts/include/rts/storage/InfoTables.h.
, platformHasLibm :: !Bool
-- ^ Some platforms require that we explicitly link against @libm@ if any
-- math-y things are used (which we assume to include all programs). See
-- #14022.
, platform_constants :: !(Maybe PlatformConstants)
-- ^ Constants such as structure offsets, type sizes, etc.
}
deriving (Read, Show, Eq, Ord)
wordAlignment :: Platform -> Alignment
wordAlignment platform = alignmentOf (platformWordSizeInBytes platform)
-- -----------------------------------------------------------------------------
-- SSE and AVX
-- TODO: Instead of using a separate predicate (i.e. isSse2Enabled) to
-- check if SSE is enabled, we might have x86-64 imply the -msse2
-- flag.
isSseEnabled :: Platform -> Bool
isSseEnabled platform = case platformArch platform of
ArchX86_64 -> True
ArchX86 -> True
_ -> False
isSse2Enabled :: Platform -> Bool
isSse2Enabled platform = case platformArch platform of
-- We assume SSE1 and SSE2 operations are available on both
-- x86 and x86_64. Historically we didn't default to SSE2 and
-- SSE1 on x86, which results in defacto nondeterminism for how
-- rounding behaves in the associated x87 floating point instructions
-- because variations in the spill/fpu stack placement of arguments for
-- operations would change the precision and final result of what
-- would otherwise be the same expressions with respect to single or
-- double precision IEEE floating point computations.
ArchX86_64 -> True
ArchX86 -> True
_ -> False
-- -----------------------------------------------------------------------------
-- Platform Constants
platformConstants :: Platform -> PlatformConstants
platformConstants platform = case platform_constants platform of
Nothing -> panic "Platform constants not available!"
Just c -> c
genericPlatform :: Platform
genericPlatform = Platform
{ platformArchOS = ArchOS ArchX86_64 OSLinux
, platformWordSize = PW8
, platformByteOrder = LittleEndian
, platformUnregisterised = False
, platformHasGnuNonexecStack = False
, platformHasIdentDirective = False
, platformHasSubsectionsViaSymbols= False
, platformHasLibm = False
, platformIsCrossCompiling = False
, platformLeadingUnderscore = False
, platformTablesNextToCode = True
, platform_constants = Nothing
}
data PlatformWordSize
= PW4 -- ^ A 32-bit platform
| PW8 -- ^ A 64-bit platform
deriving (Eq, Ord)
instance Show PlatformWordSize where
show PW4 = "4"
show PW8 = "8"
instance Read PlatformWordSize where
readPrec = do
i :: Int <- readPrec
case i of
4 -> return PW4
8 -> return PW8
other -> fail ("Invalid PlatformWordSize: " ++ show other)
platformWordSizeInBytes :: Platform -> Int
platformWordSizeInBytes p =
case platformWordSize p of
PW4 -> 4
PW8 -> 8
platformWordSizeInBits :: Platform -> Int
platformWordSizeInBits p = platformWordSizeInBytes p * 8
-- | Platform architecture
platformArch :: Platform -> Arch
platformArch platform = case platformArchOS platform of
ArchOS arch _ -> arch
-- | Platform OS
platformOS :: Platform -> OS
platformOS platform = case platformArchOS platform of
ArchOS _ os -> os
isARM :: Arch -> Bool
isARM (ArchARM {}) = True
isARM ArchAArch64 = True
isARM _ = False
-- | This predicate tells us whether the platform is 32-bit.
target32Bit :: Platform -> Bool
target32Bit p =
case platformWordSize p of
PW4 -> True
PW8 -> False
-- | This predicate tells us whether the OS supports ELF-like shared libraries.
osElfTarget :: OS -> Bool
osElfTarget OSLinux = True
osElfTarget OSFreeBSD = True
osElfTarget OSDragonFly = True
osElfTarget OSOpenBSD = True
osElfTarget OSNetBSD = True
osElfTarget OSSolaris2 = True
osElfTarget OSDarwin = False
osElfTarget OSMinGW32 = False
osElfTarget OSKFreeBSD = True
osElfTarget OSHaiku = True
osElfTarget OSQNXNTO = False
osElfTarget OSAIX = False
osElfTarget OSHurd = True
osElfTarget OSWasi = False
osElfTarget OSGhcjs = False
osElfTarget OSUnknown = False
-- Defaulting to False is safe; it means don't rely on any
-- ELF-specific functionality. It is important to have a default for
-- portability, otherwise we have to answer this question for every
-- new platform we compile on (even unreg).
-- | This predicate tells us whether the OS support Mach-O shared libraries.
osMachOTarget :: OS -> Bool
osMachOTarget OSDarwin = True
osMachOTarget _ = False
osUsesFrameworks :: OS -> Bool
osUsesFrameworks OSDarwin = True
osUsesFrameworks _ = False
platformUsesFrameworks :: Platform -> Bool
platformUsesFrameworks = osUsesFrameworks . platformOS
osSubsectionsViaSymbols :: OS -> Bool
osSubsectionsViaSymbols OSDarwin = True
osSubsectionsViaSymbols _ = False
-- | Minimum representable Int value for the given platform
platformMinInt :: Platform -> Integer
platformMinInt p = case platformWordSize p of
PW4 -> toInteger (minBound :: Int32)
PW8 -> toInteger (minBound :: Int64)
-- | Maximum representable Int value for the given platform
platformMaxInt :: Platform -> Integer
platformMaxInt p = case platformWordSize p of
PW4 -> toInteger (maxBound :: Int32)
PW8 -> toInteger (maxBound :: Int64)
-- | Maximum representable Word value for the given platform
platformMaxWord :: Platform -> Integer
platformMaxWord p = case platformWordSize p of
PW4 -> toInteger (maxBound :: Word32)
PW8 -> toInteger (maxBound :: Word64)
-- | Test if the given Integer is representable with a platform Int
platformInIntRange :: Platform -> Integer -> Bool
platformInIntRange platform x = x >= platformMinInt platform && x <= platformMaxInt platform
-- | Test if the given Integer is representable with a platform Word
platformInWordRange :: Platform -> Integer -> Bool
platformInWordRange platform x = x >= 0 && x <= platformMaxWord platform
-- | For some architectures the C calling convention is that any
-- integer shorter than 64 bits is replaced by its 64 bits
-- representation using sign or zero extension.
platformCConvNeedsExtension :: Platform -> Bool
platformCConvNeedsExtension platform = case platformArch platform of
ArchPPC_64 _ -> True
ArchS390X -> True
ArchRISCV64 -> True
ArchLoongArch64 -> True
ArchAArch64
-- Apple's AArch64 ABI requires that the caller sign-extend
-- small integer arguments. See
-- https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms
| OSDarwin <- platformOS platform -> True
_ -> False
--------------------------------------------------
-- Instruction sets
--------------------------------------------------
-- | x86 SSE instructions
data SseVersion
= SSE1
| SSE2
| SSE3
| SSE4
| SSE42
deriving (Eq, Ord)
-- | x86 BMI (bit manipulation) instructions
data BmiVersion
= BMI1
| BMI2
deriving (Eq, Ord)
-- | Platform-specific settings formerly hard-coded in Config.hs.
--
-- These should probably be all be triaged whether they can be computed from
-- other settings or belong in another another place (like 'Platform' above).
data PlatformMisc = PlatformMisc
{ -- TODO Recalculate string from richer info?
platformMisc_targetPlatformString :: String
, platformMisc_ghcWithInterpreter :: Bool
, platformMisc_libFFI :: Bool
, platformMisc_llvmTarget :: String
}
platformSOName :: Platform -> FilePath -> FilePath
platformSOName platform root = case platformOS platform of
OSMinGW32 -> root <.> platformSOExt platform
_ -> ("lib" ++ root) <.> platformSOExt platform
platformHsSOName :: Platform -> FilePath -> FilePath
platformHsSOName platform root = ("lib" ++ root) <.> platformSOExt platform
platformSOExt :: Platform -> FilePath
platformSOExt platform
= case platformOS platform of
OSDarwin -> "dylib"
OSMinGW32 -> "dll"
_ -> "so"
-- Note [Platform constants]
-- ~~~~~~~~~~~~~~~~~~~~~~~~~
--
-- The RTS is partly written in C, hence we use an external C compiler to build
-- it. Thus GHC must somehow retrieve some information about the produced code
-- (sizes of types, offsets of struct fields, etc.) to produce compatible code.
--
-- This is the role of utils/deriveConstants utility: it produces a C
-- source, compiles it with the same toolchain that will be used to build the
-- RTS, and finally retrieves the constants from the built artefact. We can't
-- directly run the produced program because we may be cross-compiling.
--
-- These constants are then stored in GhclibDerivedConstants.h header file that is
-- bundled with the RTS unit. This file is directly imported by Cmm codes and it
-- is also read by GHC. deriveConstants also produces the Haskell definition of
-- the PlatformConstants datatype and the Haskell parser for the
-- GhclibDerivedConstants.h file.
--
-- For quite some time, constants used by GHC were globally installed in
-- ${libdir}/platformConstants but now GHC reads the GhclibDerivedConstants.h header
-- bundled with the RTS unit. GHC detects when it builds the RTS unit itself and
-- in this case it loads the header from the include-dirs passed on the
-- command-line.
--
-- Note that GHC doesn't parse every "#define SOME_CONSTANT 123" individually.
-- Instead there is a single #define that contains all the constants useful to
-- GHC in a comma separated list:
--
-- #define HS_CONSTANTS "123,45,..."
--
-- Note that GHC mustn't directly import GhclibDerivedConstants.h as these constants
-- are only valid for a specific target platform and we want GHC to be target
-- agnostic.
--
-- | Try to locate "GhclibDerivedConstants.h" file in the given dirs and to parse the
-- PlatformConstants from it.
--
-- See Note [Platform constants]
lookupPlatformConstants :: [FilePath] -> IO (Maybe PlatformConstants)
lookupPlatformConstants include_dirs = find_constants include_dirs
where
try_parse d = do
let p = d </> "GhclibDerivedConstants.h"
doesFileExist p >>= \case
True -> Just <$> parseConstantsHeader p
False -> return Nothing
find_constants [] = return Nothing
find_constants (x:xs) = try_parse x >>= \case
Nothing -> find_constants xs
Just c -> return (Just c)
|