Forked from
Glasgow Haskell Compiler / GHC
1218 commits behind the upstream repository.
-
This patch makes the STG->Cmm backend avoid saving foreign call target to local when there are no caller-save GlobalRegs. Since 321941a8, when we lower a foreign call, we unconditionally save the foreign call target to a temporary local first, then rely on cmmSink to clean it up later, which only happens with -fcmm-sink (implied by -O) and not in unoptimized code. And this is troublesome for the wasm backend NCG, which needs to infer a foreign call target symbol's type signature from the Cmm call site. Previously, the NCG has been emitting incorrect type signatures for unoptimized code, which happens to work with `wasm-ld` most of the time, but this is never future-proof against upstream toolchain updates, and it causes horrible breakages when LTO objects are included in linker input. Hence this patch.
This patch makes the STG->Cmm backend avoid saving foreign call target to local when there are no caller-save GlobalRegs. Since 321941a8, when we lower a foreign call, we unconditionally save the foreign call target to a temporary local first, then rely on cmmSink to clean it up later, which only happens with -fcmm-sink (implied by -O) and not in unoptimized code. And this is troublesome for the wasm backend NCG, which needs to infer a foreign call target symbol's type signature from the Cmm call site. Previously, the NCG has been emitting incorrect type signatures for unoptimized code, which happens to work with `wasm-ld` most of the time, but this is never future-proof against upstream toolchain updates, and it causes horrible breakages when LTO objects are included in linker input. Hence this patch.
StgToCmm.hs 5.05 KiB
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}
module GHC.Driver.Config.StgToCmm
( initStgToCmmConfig
) where
import GHC.Prelude.Basic
import GHC.StgToCmm.Config
import GHC.Cmm.MachOp ( FMASign(..))
import GHC.Driver.Backend
import GHC.Driver.Session
import GHC.Platform
import GHC.Platform.Profile
import GHC.Platform.Regs
import GHC.Utils.Error
import GHC.Unit.Module
import GHC.Utils.Outputable
initStgToCmmConfig :: DynFlags -> Module -> StgToCmmConfig
initStgToCmmConfig dflags mod = StgToCmmConfig
-- settings
{ stgToCmmProfile = profile
, stgToCmmThisModule = mod
, stgToCmmTmpDir = tmpDir dflags
, stgToCmmContext = initSDocContext dflags defaultDumpStyle
, stgToCmmEmitDebugInfo = debugLevel dflags > 0
, stgToCmmBinBlobThresh = b_blob
, stgToCmmMaxInlAllocSize = maxInlineAllocSize dflags
-- ticky options
, stgToCmmDoTicky = gopt Opt_Ticky dflags
, stgToCmmTickyAllocd = gopt Opt_Ticky_Allocd dflags
, stgToCmmTickyLNE = gopt Opt_Ticky_LNE dflags
, stgToCmmTickyDynThunk = gopt Opt_Ticky_Dyn_Thunk dflags
, stgToCmmTickyTag = gopt Opt_Ticky_Tag dflags
-- flags
, stgToCmmLoopification = gopt Opt_Loopification dflags
, stgToCmmAlignCheck = gopt Opt_AlignmentSanitisation dflags
, stgToCmmOptHpc = gopt Opt_Hpc dflags
, stgToCmmFastPAPCalls = gopt Opt_FastPAPCalls dflags
, stgToCmmSCCProfiling = sccProfilingEnabled dflags
, stgToCmmEagerBlackHole = gopt Opt_EagerBlackHoling dflags
, stgToCmmOrigThunkInfo = gopt Opt_OrigThunkInfo dflags
, stgToCmmInfoTableMap = gopt Opt_InfoTableMap dflags
, stgToCmmInfoTableMapWithFallback = gopt Opt_InfoTableMapWithFallback dflags
, stgToCmmInfoTableMapWithStack = gopt Opt_InfoTableMapWithStack dflags
, stgToCmmOmitYields = gopt Opt_OmitYields dflags
, stgToCmmOmitIfPragmas = gopt Opt_OmitInterfacePragmas dflags
, stgToCmmPIC = gopt Opt_PIC dflags
, stgToCmmPIE = gopt Opt_PIE dflags
, stgToCmmExtDynRefs = gopt Opt_ExternalDynamicRefs dflags
, stgToCmmDoBoundsCheck = gopt Opt_DoBoundsChecking dflags
, stgToCmmDoTagCheck = gopt Opt_DoTagInferenceChecks dflags
-- backend flags
, stgToCmmAllowBigArith = not ncg || platformArch platform == ArchWasm32 || platformArch platform == ArchX86
, stgToCmmAllowBigQuot = not ncg || platformArch platform == ArchWasm32
, stgToCmmAllowQuotRemInstr = ncg && (x86ish || ppc)
, stgToCmmAllowQuotRem2 = (ncg && (x86ish || ppc)) || llvm
, stgToCmmAllowExtendedAddSubInstrs = (ncg && (x86ish || ppc)) || llvm
, stgToCmmAllowFMAInstr =
if
| not (isFmaEnabled dflags)
|| not (ncg || llvm)
-- If we're not using the native code generator or LLVM,
-- fall back to the generic implementation.
|| platformArch platform == ArchWasm32
-- WASM doesn't support native FMA instructions (at the time of writing).
-> const False
-- FNMSub and FNMAdd have different semantics on PowerPC,
-- so we avoid using them.
| ppc
-> \ case { FMAdd -> True; FMSub -> True; _ -> False }
| otherwise
-> const True
, stgToCmmAllowIntMul2Instr = (ncg && (x86ish || aarch64)) || llvm
, stgToCmmAllowWordMul2Instr = (ncg && (x86ish || ppc || aarch64)) || llvm
-- SIMD flags
, stgToCmmVecInstrsErr = vec_err
, stgToCmmAvx = isAvxEnabled dflags
, stgToCmmAvx2 = isAvx2Enabled dflags
, stgToCmmAvx512f = isAvx512fEnabled dflags
, stgToCmmTickyAP = gopt Opt_Ticky_AP dflags
-- See Note [Saving foreign call target to local]
, stgToCmmSaveFCallTargetToLocal = any (callerSaves platform) $ activeStgRegs platform
} where profile = targetProfile dflags
platform = profilePlatform profile
bk_end = backend dflags
b_blob = if not ncg then Nothing else binBlobThreshold dflags
(ncg, llvm) = case backendPrimitiveImplementation bk_end of
GenericPrimitives -> (False, False)
JSPrimitives -> (False, False)
NcgPrimitives -> (True, False)
LlvmPrimitives -> (False, True)
aarch64 = case platformArch platform of
ArchAArch64 -> True
_ -> False
x86ish = case platformArch platform of
ArchX86 -> True
ArchX86_64 -> True
_ -> False
ppc = case platformArch platform of
ArchPPC -> True
ArchPPC_64 _ -> True
_ -> False
vec_err = case backendSimdValidity (backend dflags) of
IsValid -> Nothing
NotValid msg -> Just msg