summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/GHC/Cmm.hs5
-rw-r--r--compiler/GHC/Cmm/CLabel.hs60
-rw-r--r--compiler/GHC/Cmm/CLabel.hs-boot9
-rw-r--r--compiler/GHC/Cmm/InitFini.hs78
-rw-r--r--compiler/GHC/Cmm/Ppr/Decl.hs2
-rw-r--r--compiler/GHC/CmmToAsm/PPC/Ppr.hs2
-rw-r--r--compiler/GHC/CmmToAsm/Ppr.hs12
-rw-r--r--compiler/GHC/CmmToC.hs20
-rw-r--r--compiler/GHC/CmmToLlvm/Data.hs45
-rw-r--r--compiler/GHC/Driver/CodeOutput.hs83
-rw-r--r--compiler/GHC/HsToCore/Coverage.hs27
-rw-r--r--compiler/GHC/HsToCore/Foreign/Decl.hs38
-rw-r--r--compiler/GHC/Iface/Tidy/StaticPtrTable.hs21
-rw-r--r--compiler/GHC/Llvm/Ppr.hs1
-rw-r--r--compiler/GHC/Llvm/Types.hs2
-rw-r--r--compiler/GHC/Types/ForeignStubs.hs55
-rw-r--r--compiler/ghc.cabal.in1
17 files changed, 372 insertions, 89 deletions
diff --git a/compiler/GHC/Cmm.hs b/compiler/GHC/Cmm.hs
index 893ca556db..4f8bdbd77a 100644
--- a/compiler/GHC/Cmm.hs
+++ b/compiler/GHC/Cmm.hs
@@ -184,6 +184,9 @@ data SectionType
| RelocatableReadOnlyData
| UninitialisedData
| ReadOnlyData16 -- .rodata.cst16 on x86_64, 16-byte aligned
+ -- See Note [Initializers and finalizers in Cmm] in GHC.Cmm.InitFini
+ | InitArray -- .init_array on ELF, .ctor on Windows
+ | FiniArray -- .fini_array on ELF, .dtor on Windows
| CString
| OtherSection String
deriving (Show)
@@ -201,6 +204,8 @@ sectionProtection (Section t _) = case t of
ReadOnlyData -> ReadOnlySection
RelocatableReadOnlyData -> WriteProtectedSection
ReadOnlyData16 -> ReadOnlySection
+ InitArray -> ReadOnlySection
+ FiniArray -> ReadOnlySection
CString -> ReadOnlySection
Data -> ReadWriteSection
UninitialisedData -> ReadWriteSection
diff --git a/compiler/GHC/Cmm/CLabel.hs b/compiler/GHC/Cmm/CLabel.hs
index c6f29ece0b..6d0870e281 100644
--- a/compiler/GHC/Cmm/CLabel.hs
+++ b/compiler/GHC/Cmm/CLabel.hs
@@ -39,6 +39,11 @@ module GHC.Cmm.CLabel (
mkBitmapLabel,
mkStringLitLabel,
+ mkInitializerStubLabel,
+ mkInitializerArrayLabel,
+ mkFinalizerStubLabel,
+ mkFinalizerArrayLabel,
+
mkAsmTempLabel,
mkAsmTempDerivedLabel,
mkAsmTempEndLabel,
@@ -255,6 +260,8 @@ data CLabel
| CCS_Label CostCentreStack
| IPE_Label InfoProvEnt
+ -- | A per-module metadata label.
+ | ModuleLabel !Module ModuleLabelKind
-- | These labels are generated and used inside the NCG only.
-- They are special variants of a label used for dynamic linking
@@ -271,7 +278,6 @@ data CLabel
-- | A label before an info table to prevent excessive dead-stripping on darwin
| DeadStripPreventer CLabel
-
-- | Per-module table of tick locations
| HpcTicksLabel Module
@@ -291,6 +297,19 @@ instance Show CLabel where
instance Outputable CLabel where
ppr = text . show
+data ModuleLabelKind
+ = MLK_Initializer String
+ | MLK_InitializerArray
+ | MLK_Finalizer String
+ | MLK_FinalizerArray
+ deriving (Eq, Ord)
+
+instance Outputable ModuleLabelKind where
+ ppr MLK_InitializerArray = text "init_arr"
+ ppr (MLK_Initializer s) = text ("init__" ++ s)
+ ppr MLK_FinalizerArray = text "fini_arr"
+ ppr (MLK_Finalizer s) = text ("fini__" ++ s)
+
isIdLabel :: CLabel -> Bool
isIdLabel IdLabel{} = True
isIdLabel _ = False
@@ -355,6 +374,9 @@ instance Ord CLabel where
compare a1 a2
compare (IPE_Label a1) (IPE_Label a2) =
compare a1 a2
+ compare (ModuleLabel m1 k1) (ModuleLabel m2 k2) =
+ compare m1 m2 `thenCmp`
+ compare k1 k2
compare (DynamicLinkerLabel a1 b1) (DynamicLinkerLabel a2 b2) =
compare a1 a2 `thenCmp`
compare b1 b2
@@ -399,6 +421,8 @@ instance Ord CLabel where
compare _ SRTLabel{} = GT
compare (IPE_Label {}) _ = LT
compare _ (IPE_Label{}) = GT
+ compare (ModuleLabel {}) _ = LT
+ compare _ (ModuleLabel{}) = GT
-- | Record where a foreign label is stored.
data ForeignLabelSource
@@ -842,6 +866,19 @@ mkDeadStripPreventer lbl = DeadStripPreventer lbl
mkStringLitLabel :: Unique -> CLabel
mkStringLitLabel = StringLitLabel
+mkInitializerStubLabel :: Module -> String -> CLabel
+mkInitializerStubLabel mod s = ModuleLabel mod (MLK_Initializer s)
+
+mkInitializerArrayLabel :: Module -> CLabel
+mkInitializerArrayLabel mod = ModuleLabel mod MLK_InitializerArray
+
+
+mkFinalizerStubLabel :: Module -> String -> CLabel
+mkFinalizerStubLabel mod s = ModuleLabel mod (MLK_Finalizer s)
+
+mkFinalizerArrayLabel :: Module -> CLabel
+mkFinalizerArrayLabel mod = ModuleLabel mod MLK_FinalizerArray
+
mkAsmTempLabel :: Uniquable a => a -> CLabel
mkAsmTempLabel a = AsmTempLabel (getUnique a)
@@ -964,11 +1001,20 @@ needsCDecl l@(ForeignLabel{}) = not (isMathFun l)
needsCDecl (CC_Label _) = True
needsCDecl (CCS_Label _) = True
needsCDecl (IPE_Label {}) = True
+needsCDecl (ModuleLabel _ kind) = modLabelNeedsCDecl kind
needsCDecl (HpcTicksLabel _) = True
needsCDecl (DynamicLinkerLabel {}) = panic "needsCDecl DynamicLinkerLabel"
needsCDecl PicBaseLabel = panic "needsCDecl PicBaseLabel"
needsCDecl (DeadStripPreventer {}) = panic "needsCDecl DeadStripPreventer"
+modLabelNeedsCDecl :: ModuleLabelKind -> Bool
+-- Code for finalizers and initializers are emitted in stub objects
+modLabelNeedsCDecl (MLK_Initializer _) = True
+modLabelNeedsCDecl (MLK_Finalizer _) = True
+-- The finalizer and initializer arrays are emitted in the code of the module
+modLabelNeedsCDecl MLK_InitializerArray = False
+modLabelNeedsCDecl MLK_FinalizerArray = False
+
-- | If a label is a local block label then return just its 'BlockId', otherwise
-- 'Nothing'.
maybeLocalBlockLabel :: CLabel -> Maybe BlockId
@@ -1087,6 +1133,7 @@ externallyVisibleCLabel (IdLabel name _ info) = isExternalName name && externa
externallyVisibleCLabel (CC_Label _) = True
externallyVisibleCLabel (CCS_Label _) = True
externallyVisibleCLabel (IPE_Label {}) = True
+externallyVisibleCLabel (ModuleLabel {}) = True
externallyVisibleCLabel (DynamicLinkerLabel _ _) = False
externallyVisibleCLabel (HpcTicksLabel _) = True
externallyVisibleCLabel (LargeBitmapLabel _) = False
@@ -1147,12 +1194,21 @@ labelType (StringLitLabel _) = DataLabel
labelType (CC_Label _) = DataLabel
labelType (CCS_Label _) = DataLabel
labelType (IPE_Label {}) = DataLabel
+labelType (ModuleLabel _ kind) = moduleLabelKindType kind
labelType (DynamicLinkerLabel _ _) = DataLabel -- Is this right?
labelType PicBaseLabel = DataLabel
labelType (DeadStripPreventer _) = DataLabel
labelType (HpcTicksLabel _) = DataLabel
labelType (LargeBitmapLabel _) = DataLabel
+moduleLabelKindType :: ModuleLabelKind -> CLabelType
+moduleLabelKindType kind =
+ case kind of
+ MLK_Initializer _ -> CodeLabel
+ MLK_InitializerArray -> DataLabel
+ MLK_Finalizer _ -> CodeLabel
+ MLK_FinalizerArray -> DataLabel
+
idInfoLabelType :: IdLabelInfo -> CLabelType
idInfoLabelType info =
case info of
@@ -1467,7 +1523,7 @@ pprCLabel !platform !sty lbl = -- see Note [Bangs in CLabel]
CC_Label cc -> maybe_underscore $ ppr cc
CCS_Label ccs -> maybe_underscore $ ppr ccs
IPE_Label (InfoProvEnt l _ _ m _) -> maybe_underscore $ (pprCode CStyle (pdoc platform l) <> text "_" <> ppr m <> text "_ipe")
-
+ ModuleLabel mod kind -> maybe_underscore $ ppr mod <> text "_" <> ppr kind
CmmLabel _ _ fs CmmCode -> maybe_underscore $ ftext fs
CmmLabel _ _ fs CmmData -> maybe_underscore $ ftext fs
diff --git a/compiler/GHC/Cmm/CLabel.hs-boot b/compiler/GHC/Cmm/CLabel.hs-boot
new file mode 100644
index 0000000000..8fb1b74423
--- /dev/null
+++ b/compiler/GHC/Cmm/CLabel.hs-boot
@@ -0,0 +1,9 @@
+module GHC.Cmm.CLabel where
+
+import GHC.Utils.Outputable
+import GHC.Platform
+
+data CLabel
+
+pprCLabel :: Platform -> LabelStyle -> CLabel -> SDoc
+
diff --git a/compiler/GHC/Cmm/InitFini.hs b/compiler/GHC/Cmm/InitFini.hs
new file mode 100644
index 0000000000..2a4a12ed08
--- /dev/null
+++ b/compiler/GHC/Cmm/InitFini.hs
@@ -0,0 +1,78 @@
+-- | Utilities for dealing with constructors/destructors.
+module GHC.Cmm.InitFini
+ ( InitOrFini(..)
+ , isInitOrFiniArray
+ ) where
+
+import GHC.Prelude
+
+import GHC.Cmm.CLabel
+import GHC.Cmm
+import GHC.Utils.Panic
+import GHC.Utils.Outputable
+
+{-
+Note [Initializers and finalizers in Cmm]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Most platforms support some mechanism for marking a procedure to be run when a
+program is loaded (in which case the procedure is known as an "initializer",
+"constructor", or "ctor") or unloaded (a "finalizer", "deconstructor", or
+"dtor").
+
+For instance, on ELF platforms pointers to initializer and finalizer functions
+are listed in .init_array and .fini_array sections, which are traversed by libc
+during program startup and shutdown.
+
+In GHC-generated code, initializers are used for a few things:
+
+ * registration of cost-centres and cost-centre stacks for profiling
+ * registration of info-table provenance entries
+ * registration of ticky tickers
+ * registration of HPC ticks
+
+All of these initializers are implemented as C functions, emitted by the
+compiler as ForeignStubs. Consequently the GHC.Types.ForeignStubs.CStub type
+carries with it lists of functions which should be marked as initializers or
+finalizers.
+
+These initializer and finalizer lists are then turned into CmmData declarations
+which are fed to the backend. These declarations are distinguished by their
+Section (e.g. InitArray or FiniArray) and consist of an array of words, where each
+word is a pointer to an initializer/finalizer function. Since this is the same
+form that most platforms expect initializer or finalizer lists to appear in
+assembler, the NCG backends naturally emit the appropriate assembler.
+
+However, for non-NCG backends (e.g. the C and LLVM backends) these
+initializer/finalizer list declarations need to be detected and dealt with
+appropriately. We provide isInitOrFiniArray to distinguish such declarations
+and turn them back into a list of CLabels.
+
+On Windows initializers/finalizers are a bit tricky due to the inability to
+merge objects (due to the lld linker's lack of `-r` support on Windows; see
+#21068) since we instead must package foreign stubs into static archives.
+However, the linker is free to not include any constituent objects of a static
+library in the final object code if nothing depends upon them. Consequently, we
+must ensure that the initializer list for a module is defined in the module's
+object code, not its foreign stubs. This happens naturally with the plan laid
+out above.
+
+Note that we maintain the invariant that at most one initializer and one
+finalizer CmmDecl will be emitted per module.
+-}
+
+data InitOrFini = IsInitArray | IsFiniArray
+
+isInitOrFiniArray :: RawCmmDecl -> Maybe (InitOrFini, [CLabel])
+isInitOrFiniArray (CmmData sect (CmmStaticsRaw _ lits))
+ | Just initOrFini <- isInitOrFiniSection sect
+ = Just (initOrFini, map get_label lits)
+ where
+ get_label :: CmmStatic -> CLabel
+ get_label (CmmStaticLit (CmmLabel lbl)) = lbl
+ get_label static = pprPanic "isInitOrFiniArray: invalid entry" (ppr static)
+isInitOrFiniArray _ = Nothing
+
+isInitOrFiniSection :: Section -> Maybe InitOrFini
+isInitOrFiniSection (Section InitArray _) = Just IsInitArray
+isInitOrFiniSection (Section FiniArray _) = Just IsFiniArray
+isInitOrFiniSection _ = Nothing
diff --git a/compiler/GHC/Cmm/Ppr/Decl.hs b/compiler/GHC/Cmm/Ppr/Decl.hs
index b6a2e1992e..3b1eff78ff 100644
--- a/compiler/GHC/Cmm/Ppr/Decl.hs
+++ b/compiler/GHC/Cmm/Ppr/Decl.hs
@@ -166,5 +166,7 @@ pprSectionType s = doubleQuotes $ case s of
ReadOnlyData16 -> text "readonly16"
RelocatableReadOnlyData -> text "relreadonly"
UninitialisedData -> text "uninitialised"
+ InitArray -> text "initarray"
+ FiniArray -> text "finiarray"
CString -> text "cstring"
OtherSection s' -> text s'
diff --git a/compiler/GHC/CmmToAsm/PPC/Ppr.hs b/compiler/GHC/CmmToAsm/PPC/Ppr.hs
index 99776cc7f4..a842eef998 100644
--- a/compiler/GHC/CmmToAsm/PPC/Ppr.hs
+++ b/compiler/GHC/CmmToAsm/PPC/Ppr.hs
@@ -299,6 +299,8 @@ pprAlignForSection platform seg =
ReadOnlyData16 -> text ".align 4"
-- TODO: This is copied from the ReadOnlyData case, but it can likely be
-- made more efficient.
+ InitArray -> text ".align 3"
+ FiniArray -> text ".align 3"
CString
| ppc64 -> text ".align 3"
| otherwise -> text ".align 2"
diff --git a/compiler/GHC/CmmToAsm/Ppr.hs b/compiler/GHC/CmmToAsm/Ppr.hs
index 441ee75348..0379ed6464 100644
--- a/compiler/GHC/CmmToAsm/Ppr.hs
+++ b/compiler/GHC/CmmToAsm/Ppr.hs
@@ -227,6 +227,14 @@ pprGNUSectionHeader config t suffix =
ReadOnlyData16 | OSMinGW32 <- platformOS platform
-> text ".rdata$cst16"
| otherwise -> text ".rodata.cst16"
+ InitArray
+ | OSMinGW32 <- platformOS platform
+ -> text ".ctors"
+ | otherwise -> text ".init_array"
+ FiniArray
+ | OSMinGW32 <- platformOS platform
+ -> text ".dtors"
+ | otherwise -> text ".fini_array"
CString
| OSMinGW32 <- platformOS platform
-> text ".rdata"
@@ -251,7 +259,7 @@ pprXcoffSectionHeader t = case t of
ReadOnlyData16 -> text ".csect .text[PR] # ReadOnlyData16"
CString -> text ".csect .text[PR] # CString"
UninitialisedData -> text ".csect .data[BS]"
- OtherSection _ -> panic "pprXcoffSectionHeader: unknown section type"
+ _ -> panic "pprXcoffSectionHeader: unknown section type"
pprDarwinSectionHeader :: SectionType -> SDoc
pprDarwinSectionHeader t = case t of
@@ -261,5 +269,7 @@ pprDarwinSectionHeader t = case t of
RelocatableReadOnlyData -> text ".const_data"
UninitialisedData -> text ".data"
ReadOnlyData16 -> text ".const"
+ InitArray -> text ".section\t__DATA,__mod_init_func,mod_init_funcs"
+ FiniArray -> panic "pprDarwinSectionHeader: fini not supported"
CString -> text ".section\t__TEXT,__cstring,cstring_literals"
OtherSection _ -> panic "pprDarwinSectionHeader: unknown section type"
diff --git a/compiler/GHC/CmmToC.hs b/compiler/GHC/CmmToC.hs
index da0b54dd9f..c9f86e9afe 100644
--- a/compiler/GHC/CmmToC.hs
+++ b/compiler/GHC/CmmToC.hs
@@ -40,6 +40,7 @@ import GHC.Cmm.Dataflow.Collections
import GHC.Cmm.Dataflow.Graph
import GHC.Cmm.Utils
import GHC.Cmm.Switch
+import GHC.Cmm.InitFini
import GHC.Types.ForeignCall
import GHC.Types.Unique.Set
@@ -100,6 +101,9 @@ pprTop platform = \case
-- We only handle (a) arrays of word-sized things and (b) strings.
+ cmm_data | Just (initOrFini, clbls) <- isInitOrFiniArray cmm_data ->
+ pprCtorArray platform initOrFini clbls
+
(CmmData section (CmmStaticsRaw lbl [CmmString str])) ->
pprExternDecl platform lbl $$
hcat [
@@ -1487,3 +1491,19 @@ pprHexVal platform w rep = parens ctype <> rawlit
(q,r) = w' `quotRem` 16
dig | r < 10 = char (chr (fromInteger r + ord '0'))
| otherwise = char (chr (fromInteger r - 10 + ord 'a'))
+
+-- | Construct a constructor/finalizer function. Instead of emitting a
+-- initializer/finalizer array we rather just emit a single function, annotated
+-- with the appropriate C attribute, which then calls each of the initializers.
+pprCtorArray :: Platform -> InitOrFini -> [CLabel] -> SDoc
+pprCtorArray platform initOrFini lbls =
+ decls
+ <> text "static __attribute__((" <> attribute <> text "))"
+ <> text "void _hs_" <> attribute <> text "()"
+ <> braces body
+ where
+ body = vcat [ pprCLabel platform CStyle lbl <> text " ();" | lbl <- lbls ]
+ decls = vcat [ text "void" <+> pprCLabel platform CStyle lbl <> text " (void);" | lbl <- lbls ]
+ attribute = case initOrFini of
+ IsInitArray -> text "constructor"
+ IsFiniArray -> text "destructor"
diff --git a/compiler/GHC/CmmToLlvm/Data.hs b/compiler/GHC/CmmToLlvm/Data.hs
index 0e5679887d..edab12a6ef 100644
--- a/compiler/GHC/CmmToLlvm/Data.hs
+++ b/compiler/GHC/CmmToLlvm/Data.hs
@@ -15,6 +15,7 @@ import GHC.CmmToLlvm.Config
import GHC.Cmm.BlockId
import GHC.Cmm.CLabel
+import GHC.Cmm.InitFini
import GHC.Cmm
import GHC.Platform
@@ -65,6 +66,14 @@ genLlvmData (_, CmmStaticsRaw alias [CmmStaticLit (CmmLabel lbl), CmmStaticLit i
pure ([LMGlobal aliasDef $ Just orig], [tyAlias])
+-- See Note [Initializers and finalizers in Cmm] in GHC.Cmm.InitFini.
+genLlvmData (sect, statics)
+ | Just (initOrFini, clbls) <- isInitOrFiniArray (CmmData sect statics)
+ = let var = case initOrFini of
+ IsInitArray -> fsLit "llvm.global_ctors"
+ IsFiniArray -> fsLit "llvm.global_dtors"
+ in genGlobalLabelArray var clbls
+
genLlvmData (sec, CmmStaticsRaw lbl xs) = do
label <- strCLabel_llvm lbl
static <- mapM genData xs
@@ -88,6 +97,37 @@ genLlvmData (sec, CmmStaticsRaw lbl xs) = do
return ([globDef], [tyAlias])
+-- | Produce an initializer or finalizer array declaration.
+-- See Note [Initializers and finalizers in Cmm] in GHC.Cmm.InitFini for
+-- details.
+genGlobalLabelArray :: FastString -> [CLabel] -> LlvmM LlvmData
+genGlobalLabelArray var_nm clbls = do
+ lbls <- mapM strCLabel_llvm clbls
+ decls <- mapM mkFunDecl lbls
+ let entries = map toArrayEntry lbls
+ static = LMStaticArray entries arr_ty
+ arr = LMGlobal arr_var (Just static)
+ return ([arr], decls)
+ where
+ mkFunDecl :: LMString -> LlvmM LlvmType
+ mkFunDecl fn_lbl = do
+ let fn_ty = mkFunTy fn_lbl
+ funInsert fn_lbl fn_ty
+ return (fn_ty)
+
+ toArrayEntry :: LMString -> LlvmStatic
+ toArrayEntry fn_lbl =
+ let fn_var = LMGlobalVar fn_lbl (LMPointer $ mkFunTy fn_lbl) Internal Nothing Nothing Global
+ fn = LMStaticPointer fn_var
+ null = LMStaticLit (LMNullLit i8Ptr)
+ prio = LMStaticLit $ LMIntLit 0xffff i32
+ in LMStaticStrucU [prio, fn, null] entry_ty
+
+ arr_var = LMGlobalVar var_nm arr_ty Internal Nothing Nothing Global
+ mkFunTy lbl = LMFunction $ LlvmFunctionDecl lbl ExternallyVisible CC_Ccc LMVoid FixedArgs [] Nothing
+ entry_ty = LMStructU [i32, LMPointer $ mkFunTy $ fsLit "placeholder", LMPointer i8]
+ arr_ty = LMArray (length clbls) entry_ty
+
-- | Format the section type part of a Cmm Section
llvmSectionType :: Platform -> SectionType -> FastString
llvmSectionType p t = case t of
@@ -106,7 +146,10 @@ llvmSectionType p t = case t of
CString -> case platformOS p of
OSMinGW32 -> fsLit ".rdata$str"
_ -> fsLit ".rodata.str"
- (OtherSection _) -> panic "llvmSectionType: unknown section type"
+
+ InitArray -> panic "llvmSectionType: InitArray"
+ FiniArray -> panic "llvmSectionType: FiniArray"
+ OtherSection _ -> panic "llvmSectionType: unknown section type"
-- | Format a Cmm Section into a LLVM section name
llvmSection :: Section -> LlvmM LMSection
diff --git a/compiler/GHC/Driver/CodeOutput.hs b/compiler/GHC/Driver/CodeOutput.hs
index 80a8277283..5f5f9882c2 100644
--- a/compiler/GHC/Driver/CodeOutput.hs
+++ b/compiler/GHC/Driver/CodeOutput.hs
@@ -4,7 +4,7 @@
\section{Code output phase}
-}
-
+{-# LANGUAGE ScopedTypeVariables #-}
module GHC.Driver.CodeOutput
( codeOutput
@@ -23,7 +23,7 @@ import GHC.CmmToLlvm ( llvmCodeGen )
import GHC.CmmToC ( cmmToC )
import GHC.Cmm.Lint ( cmmLint )
-import GHC.Cmm ( RawCmmGroup )
+import GHC.Cmm
import GHC.Cmm.CLabel
import GHC.Driver.Session
@@ -70,7 +70,8 @@ import qualified Data.Set as Set
-}
codeOutput
- :: Logger
+ :: forall a.
+ Logger
-> TmpFs
-> DynFlags
-> UnitState
@@ -110,18 +111,39 @@ codeOutput logger tmpfs dflags unit_state this_mod filenm location genForeignStu
; return cmm
}
- ; a <- case backend dflags of
+ ; let final_stream :: Stream IO RawCmmGroup (ForeignStubs, a)
+ final_stream = do
+ { a <- linted_cmm_stream
+ ; let stubs = genForeignStubs a
+ ; emitInitializerDecls this_mod stubs
+ ; return (stubs, a) }
+
+ ; (stubs, a) <- case backend dflags of
NCG -> outputAsm logger dflags this_mod location filenm
- linted_cmm_stream
- ViaC -> outputC logger dflags filenm linted_cmm_stream pkg_deps
- LLVM -> outputLlvm logger dflags filenm linted_cmm_stream
+ final_stream
+ ViaC -> outputC logger dflags filenm final_stream pkg_deps
+ LLVM -> outputLlvm logger dflags filenm final_stream
Interpreter -> panic "codeOutput: Interpreter"
NoBackend -> panic "codeOutput: NoBackend"
- ; let stubs = genForeignStubs a
; stubs_exist <- outputForeignStubs logger tmpfs dflags unit_state this_mod location stubs
; return (filenm, stubs_exist, foreign_fps, a)
}
+-- | See Note [Initializers and finalizers in Cmm] in GHC.Cmm.InitFini for details.
+emitInitializerDecls :: Module -> ForeignStubs -> Stream IO RawCmmGroup ()
+emitInitializerDecls this_mod (ForeignStubs _ cstub)
+ | initializers <- getInitializers cstub
+ , not $ null initializers =
+ let init_array = CmmData sect statics
+ lbl = mkInitializerArrayLabel this_mod
+ sect = Section InitArray lbl
+ statics = CmmStaticsRaw lbl
+ [ CmmStaticLit $ CmmLabel fn_name
+ | fn_name <- initializers
+ ]
+ in Stream.yield [init_array]
+emitInitializerDecls _ _ = return ()
+
doOutput :: String -> (Handle -> IO a) -> IO a
doOutput filenm io_action = bracket (openFile filenm WriteMode) hClose io_action
@@ -221,7 +243,7 @@ outputForeignStubs logger tmpfs dflags unit_state mod location stubs
NoStubs ->
return (False, Nothing)
- ForeignStubs (CHeader h_code) (CStub c_code) -> do
+ ForeignStubs (CHeader h_code) (CStub c_code _ _) -> do
let
stub_c_output_d = pprCode CStyle c_code
stub_c_output_w = showSDoc dflags stub_c_output_d
@@ -298,20 +320,18 @@ outputForeignStubs_help fname doc_str header footer
-- | Generate code to initialise cost centres
profilingInitCode :: Platform -> Module -> CollectedCCs -> CStub
profilingInitCode platform this_mod (local_CCs, singleton_CCSs)
- = CStub $ vcat
- $ map emit_cc_decl local_CCs
- ++ map emit_ccs_decl singleton_CCSs
- ++ [emit_cc_list local_CCs]
- ++ [emit_ccs_list singleton_CCSs]
- ++ [ text "static void prof_init_" <> ppr this_mod
- <> text "(void) __attribute__((constructor));"
- , text "static void prof_init_" <> ppr this_mod <> text "(void)"
- , braces (vcat
- [ text "registerCcList" <> parens local_cc_list_label <> semi
- , text "registerCcsList" <> parens singleton_cc_list_label <> semi
- ])
- ]
+ = initializerCStub platform fn_name decls body
where
+ fn_name = mkInitializerStubLabel this_mod "prof_init"
+ decls = vcat
+ $ map emit_cc_decl local_CCs
+ ++ map emit_ccs_decl singleton_CCSs
+ ++ [emit_cc_list local_CCs]
+ ++ [emit_ccs_list singleton_CCSs]
+ body = vcat
+ [ text "registerCcList" <> parens local_cc_list_label <> semi
+ , text "registerCcsList" <> parens singleton_cc_list_label <> semi
+ ]
emit_cc_decl cc =
text "extern CostCentre" <+> cc_lbl <> text "[];"
where cc_lbl = pdoc platform (mkCCLabel cc)
@@ -343,19 +363,14 @@ ipInitCode
-> [InfoProvEnt]
-> CStub
ipInitCode do_info_table platform this_mod ents
- = if not do_info_table
- then mempty
- else CStub $ vcat
- $ map emit_ipe_decl ents
- ++ [emit_ipe_list ents]
- ++ [ text "static void ip_init_" <> ppr this_mod
- <> text "(void) __attribute__((constructor));"
- , text "static void ip_init_" <> ppr this_mod <> text "(void)"
- , braces (vcat
- [ text "registerInfoProvList" <> parens local_ipe_list_label <> semi
- ])
- ]
+ | not do_info_table = mempty
+ | otherwise = initializerCStub platform fn_nm decls body
where
+ fn_nm = mkInitializerStubLabel this_mod "ip_init"
+ decls = vcat
+ $ map emit_ipe_decl ents
+ ++ [emit_ipe_list ents]
+ body = text "registerInfoProvList" <> parens local_ipe_list_label <> semi
emit_ipe_decl ipe =
text "extern InfoProvEnt" <+> ipe_lbl <> text "[];"
where ipe_lbl = pprCLabel platform CStyle (mkIPELabel ipe)
diff --git a/compiler/GHC/HsToCore/Coverage.hs b/compiler/GHC/HsToCore/Coverage.hs
index e1e8489fe1..96439a837d 100644
--- a/compiler/GHC/HsToCore/Coverage.hs
+++ b/compiler/GHC/HsToCore/Coverage.hs
@@ -1343,23 +1343,18 @@ static void hpc_init_Main(void)
hpcInitCode :: Platform -> Module -> HpcInfo -> CStub
hpcInitCode _ _ (NoHpcInfo {}) = mempty
hpcInitCode platform this_mod (HpcInfo tickCount hashNo)
- = CStub $ vcat
- [ text "static void hpc_init_" <> ppr this_mod
- <> text "(void) __attribute__((constructor));"
- , text "static void hpc_init_" <> ppr this_mod <> text "(void)"
- , braces (vcat [
- text "extern StgWord64 " <> tickboxes <>
- text "[]" <> semi,
- text "hs_hpc_module" <>
- parens (hcat (punctuate comma [
- doubleQuotes full_name_str,
- int tickCount, -- really StgWord32
- int hashNo, -- really StgWord32
- tickboxes
- ])) <> semi
- ])
- ]
+ = initializerCStub platform fn_name decls body
where
+ fn_name = mkInitializerStubLabel this_mod "hpc"
+ decls = text "extern StgWord64 " <> tickboxes <> text "[]" <> semi
+ body = text "hs_hpc_module" <>
+ parens (hcat (punctuate comma [
+ doubleQuotes full_name_str,
+ int tickCount, -- really StgWord32
+ int hashNo, -- really StgWord32
+ tickboxes
+ ])) <> semi
+
tickboxes = pprCLabel platform CStyle (mkHpcTicksLabel $ this_mod)
module_name = hcat (map (text.charToC) $ BS.unpack $
diff --git a/compiler/GHC/HsToCore/Foreign/Decl.hs b/compiler/GHC/HsToCore/Foreign/Decl.hs
index 6f9118604d..68980f5b12 100644
--- a/compiler/GHC/HsToCore/Foreign/Decl.hs
+++ b/compiler/GHC/HsToCore/Foreign/Decl.hs
@@ -43,6 +43,7 @@ import GHC.Tc.Utils.TcType
import GHC.Cmm.Expr
import GHC.Cmm.Utils
+import GHC.Cmm.CLabel
import GHC.Driver.Ppr
import GHC.Types.ForeignCall
import GHC.Builtin.Types
@@ -95,11 +96,12 @@ dsForeigns' []
= return (NoStubs, nilOL)
dsForeigns' fos = do
mod <- getModule
+ platform <- targetPlatform <$> getDynFlags
fives <- mapM do_ldecl fos
let
(hs, cs, idss, bindss) = unzip4 fives
fe_ids = concat idss
- fe_init_code = foreignExportsInitialiser mod fe_ids
+ fe_init_code = foreignExportsInitialiser platform mod fe_ids
--
return (ForeignStubs
(mconcat hs)
@@ -298,7 +300,7 @@ dsFCall fn_id co fcall mDeclHeader = do
simpl_opts
wrap_rhs'
- return ([(work_id, work_rhs), (fn_id_w_inl, wrap_rhs')], mempty, CStub cDoc)
+ return ([(work_id, work_rhs), (fn_id_w_inl, wrap_rhs')], mempty, CStub cDoc [] [])
{-
************************************************************************
@@ -526,7 +528,9 @@ mkFExportCBits :: DynFlags
Int -- total size of arguments
)
mkFExportCBits dflags c_nm maybe_target arg_htys res_hty is_IO_res_ty cc
- = (header_bits, c_bits, type_string,
+ = ( header_bits
+ , CStub body [] []
+ , type_string,
sum [ widthInBytes (typeWidth rep) | (_,_,_,rep) <- aug_arg_info] -- all the args
-- NB. the calculation here isn't strictly speaking correct.
-- We have a primitive Haskell type (eg. Int#, Double#), and
@@ -646,7 +650,7 @@ mkFExportCBits dflags c_nm maybe_target arg_htys res_hty is_IO_res_ty cc
-- finally, the whole darn thing
- c_bits = CStub $
+ body =
space $$
extern_decl $$
fun_proto $$
@@ -682,10 +686,9 @@ mkFExportCBits dflags c_nm maybe_target arg_htys res_hty is_IO_res_ty cc
, rbrace
]
-
-foreignExportsInitialiser :: Module -> [Id] -> CStub
-foreignExportsInitialiser _ [] = mempty
-foreignExportsInitialiser mod hs_fns =
+foreignExportsInitialiser :: Platform -> Module -> [Id] -> CStub
+foreignExportsInitialiser _ _ [] = mempty
+foreignExportsInitialiser platform mod hs_fns =
-- Initialise foreign exports by registering a stable pointer from an
-- __attribute__((constructor)) function.
-- The alternative is to do this from stginit functions generated in
@@ -696,21 +699,18 @@ foreignExportsInitialiser mod hs_fns =
-- (this is bad for big umbrella modules like Graphics.Rendering.OpenGL)
--
-- See Note [Tracking foreign exports] in rts/ForeignExports.c
- CStub $ vcat
- [ text "static struct ForeignExportsList" <+> list_symbol <+> equals
+ initializerCStub platform fn_nm list_decl fn_body
+ where
+ fn_nm = mkInitializerStubLabel mod "fexports"
+ mod_str = pprModuleName (moduleName mod)
+ fn_body = text "registerForeignExports" <> parens (char '&' <> list_symbol) <> semi
+ list_symbol = text "stg_exports_" <> mod_str
+ list_decl = text "static struct ForeignExportsList" <+> list_symbol <+> equals
<+> braces (
text ".exports = " <+> export_list <> comma <+>
text ".n_entries = " <+> ppr (length hs_fns))
<> semi
- , text "static void " <> ctor_symbol <> text "(void)"
- <+> text " __attribute__((constructor));"
- , text "static void " <> ctor_symbol <> text "()"
- , braces (text "registerForeignExports" <> parens (char '&' <> list_symbol) <> semi)
- ]
- where
- mod_str = pprModuleName (moduleName mod)
- ctor_symbol = text "stginit_export_" <> mod_str
- list_symbol = text "stg_exports_" <> mod_str
+
export_list = braces $ pprWithCommas closure_ptr hs_fns
closure_ptr :: Id -> SDoc
diff --git a/compiler/GHC/Iface/Tidy/StaticPtrTable.hs b/compiler/GHC/Iface/Tidy/StaticPtrTable.hs
index ab29a395da..3107a593b1 100644
--- a/compiler/GHC/Iface/Tidy/StaticPtrTable.hs
+++ b/compiler/GHC/Iface/Tidy/StaticPtrTable.hs
@@ -240,11 +240,12 @@ sptCreateStaticBinds opts this_mod binds = do
-- its fingerprint.
sptModuleInitCode :: Platform -> Module -> [SptEntry] -> CStub
sptModuleInitCode _ _ [] = mempty
-sptModuleInitCode platform this_mod entries = CStub $ vcat
- [ text "static void hs_spt_init_" <> ppr this_mod
- <> text "(void) __attribute__((constructor));"
- , text "static void hs_spt_init_" <> ppr this_mod <> text "(void)"
- , braces $ vcat $
+sptModuleInitCode platform this_mod entries =
+ initializerCStub platform init_fn_nm empty init_fn_body `mappend`
+ finalizerCStub platform fini_fn_nm empty fini_fn_body
+ where
+ init_fn_nm = mkInitializerStubLabel this_mod "spt"
+ init_fn_body = vcat
[ text "static StgWord64 k" <> int i <> text "[2] = "
<> pprFingerprint fp <> semi
$$ text "extern StgPtr "
@@ -258,17 +259,15 @@ sptModuleInitCode platform this_mod entries = CStub $ vcat
<> semi
| (i, SptEntry n fp) <- zip [0..] entries
]
- , text "static void hs_spt_fini_" <> ppr this_mod
- <> text "(void) __attribute__((destructor));"
- , text "static void hs_spt_fini_" <> ppr this_mod <> text "(void)"
- , braces $ vcat $
+
+ fini_fn_nm = mkFinalizerStubLabel this_mod "spt"
+ fini_fn_body = vcat
[ text "StgWord64 k" <> int i <> text "[2] = "
<> pprFingerprint fp <> semi
$$ text "hs_spt_remove" <> parens (char 'k' <> int i) <> semi
| (i, (SptEntry _ fp)) <- zip [0..] entries
]
- ]
- where
+
pprFingerprint :: Fingerprint -> SDoc
pprFingerprint (Fingerprint w1 w2) =
braces $ hcat $ punctuate comma
diff --git a/compiler/GHC/Llvm/Ppr.hs b/compiler/GHC/Llvm/Ppr.hs
index f02d2e1024..f48e7aa034 100644
--- a/compiler/GHC/Llvm/Ppr.hs
+++ b/compiler/GHC/Llvm/Ppr.hs
@@ -569,6 +569,7 @@ ppStatic opts st = case st of
LMStaticStr s t -> ppr t <> text " c\"" <> ftext s <> text "\\00\""
LMStaticArray d t -> ppr t <> text " [" <> ppCommaJoin (map (ppStatic opts) d) <> char ']'
LMStaticStruc d t -> ppr t <> text "<{" <> ppCommaJoin (map (ppStatic opts) d) <> text "}>"
+ LMStaticStrucU d t -> ppr t <> text "{" <> ppCommaJoin (map (ppStatic opts) d) <> text "}"
LMStaticPointer v -> ppVar opts v
LMTrunc v t -> ppr t <> text " trunc (" <> ppStatic opts v <> text " to " <> ppr t <> char ')'
LMBitc v t -> ppr t <> text " bitcast (" <> ppStatic opts v <> text " to " <> ppr t <> char ')'
diff --git a/compiler/GHC/Llvm/Types.hs b/compiler/GHC/Llvm/Types.hs
index f5db5a96aa..79dccfac88 100644
--- a/compiler/GHC/Llvm/Types.hs
+++ b/compiler/GHC/Llvm/Types.hs
@@ -140,6 +140,7 @@ data LlvmStatic
| LMStaticStr LMString LlvmType -- ^ Defines a static 'LMString'
| LMStaticArray [LlvmStatic] LlvmType -- ^ A static array
| LMStaticStruc [LlvmStatic] LlvmType -- ^ A static structure type
+ | LMStaticStrucU [LlvmStatic] LlvmType -- ^ A static structure type
| LMStaticPointer LlvmVar -- ^ A pointer to other data
-- static expressions, could split out but leave
@@ -191,6 +192,7 @@ getStatType (LMUninitType t) = t
getStatType (LMStaticStr _ t) = t
getStatType (LMStaticArray _ t) = t
getStatType (LMStaticStruc _ t) = t
+getStatType (LMStaticStrucU _ t) = t
getStatType (LMStaticPointer v) = getVarType v
getStatType (LMTrunc _ t) = t
getStatType (LMBitc _ t) = t
diff --git a/compiler/GHC/Types/ForeignStubs.hs b/compiler/GHC/Types/ForeignStubs.hs
index 0a37d230fe..b92bfd9b64 100644
--- a/compiler/GHC/Types/ForeignStubs.hs
+++ b/compiler/GHC/Types/ForeignStubs.hs
@@ -4,29 +4,74 @@ module GHC.Types.ForeignStubs
( ForeignStubs (..)
, CHeader(..)
, CStub(..)
+ , initializerCStub
+ , finalizerCStub
, appendStubC
)
where
+import {-# SOURCE #-} GHC.Cmm.CLabel
+
+import GHC.Platform
import GHC.Utils.Outputable
+import Data.List ((++))
import Data.Monoid
import Data.Semigroup
import Data.Coerce
-newtype CStub = CStub { getCStub :: SDoc }
+data CStub = CStub { getCStub :: SDoc
+ , getInitializers :: [CLabel]
+ -- ^ Initializers to be run at startup
+ -- See Note [Initializers and finalizers in Cmm] in
+ -- "GHC.Cmm.InitFini".
+ , getFinalizers :: [CLabel]
+ -- ^ Finalizers to be run at shutdown
+ }
emptyCStub :: CStub
-emptyCStub = CStub empty
+emptyCStub = CStub empty [] []
instance Monoid CStub where
mempty = emptyCStub
- mconcat = coerce vcat
instance Semigroup CStub where
- (<>) = coerce ($$)
+ CStub a0 b0 c0 <> CStub a1 b1 c1 =
+ CStub (a0 $$ a1) (b0 ++ b1) (c0 ++ c1)
+
+functionCStub :: Platform -> CLabel -> SDoc -> SDoc -> CStub
+functionCStub platform clbl declarations body =
+ CStub body' [] []
+ where
+ body' = vcat
+ [ declarations
+ , hsep [text "void", pprCLabel platform CStyle clbl, text "(void)"]
+ , braces body
+ ]
+
+-- | @initializerCStub fn_nm decls body@ is a 'CStub' containing C initializer
+-- function (e.g. an entry of the @.init_array@ section) named
+-- @fn_nm@ with the given body and the given set of declarations.
+initializerCStub :: Platform -> CLabel -> SDoc -> SDoc -> CStub
+initializerCStub platform clbl declarations body =
+ functionCStub platform clbl declarations body
+ `mappend` CStub empty [clbl] []
+
+-- | @finalizerCStub fn_nm decls body@ is a 'CStub' containing C finalizer
+-- function (e.g. an entry of the @.fini_array@ section) named
+-- @fn_nm@ with the given body and the given set of declarations.
+finalizerCStub :: Platform -> CLabel -> SDoc -> SDoc -> CStub
+finalizerCStub platform clbl declarations body =
+ functionCStub platform clbl declarations body
+ `mappend` CStub empty [] [clbl]
newtype CHeader = CHeader { getCHeader :: SDoc }
- deriving (Monoid, Semigroup) via CStub
+
+instance Monoid CHeader where
+ mempty = CHeader empty
+ mconcat = coerce vcat
+
+instance Semigroup CHeader where
+ (<>) = coerce ($$)
-- | Foreign export stubs
data ForeignStubs
diff --git a/compiler/ghc.cabal.in b/compiler/ghc.cabal.in
index 9aae391a1b..f4c1a41dd3 100644
--- a/compiler/ghc.cabal.in
+++ b/compiler/ghc.cabal.in
@@ -189,6 +189,7 @@ Library
GHC.Cmm.Graph
GHC.Cmm.Info
GHC.Cmm.Info.Build
+ GHC.Cmm.InitFini
GHC.Cmm.LayoutStack
GHC.Cmm.Lexer
GHC.Cmm.Lint