Simon Jakobi pushed to branch wip/sjakobi/mr16259 at Glasgow Haskell Compiler / GHC
Commits:
-
781b0ee3
by Simon Jakobi at 2026-07-10T14:56:38+02:00
-
a26c7358
by Simon Jakobi at 2026-07-10T14:56:53+02:00
-
2d19d76a
by Simon Jakobi at 2026-07-10T14:57:07+02:00
-
85c2b5ac
by Simon Jakobi at 2026-07-10T14:57:27+02:00
-
b7a5055e
by Simon Jakobi at 2026-07-10T14:57:38+02:00
13 changed files:
- + changelog.d/enter-taggable-invariant-23173
- compiler/GHC/CmmToAsm/Config.hs
- compiler/GHC/CmmToLlvm/Config.hs
- compiler/GHC/Iface/Make.hs
- compiler/GHC/StgToCmm.hs
- compiler/GHC/StgToCmm/Env.hs
- compiler/GHC/Types/Name.hs
- compiler/GHC/Types/Unique.hs
- libraries/ghc-internal/cbits/StackCloningDecoding.cmm
- libraries/ghci/GHCi/ObjLink.hs
- rts/Compact.cmm
- rts/RtsMessages.c
- rts/sm/Compact.c
Changes:
| 1 | +section: codegen
|
|
| 2 | +synopsis: Pointers to boxed unlifted primitives (``ByteArray#``, ``Array#``,
|
|
| 3 | + ``MVar#``, ...) are now tagged, and entering a taggable normal form is
|
|
| 4 | + reported.
|
|
| 5 | +issues: #23173
|
|
| 6 | +mrs: !16259
|
|
| 7 | + |
|
| 8 | +description: {
|
|
| 9 | + References to boxed unlifted primitive values such as ``ByteArray#``,
|
|
| 10 | + ``Array#`` or ``MVar#`` now carry pointer tag 1, like single-constructor
|
|
| 11 | + data types. Together with this, GHC now enforces the invariant that the
|
|
| 12 | + entry code of a taggable normal form is unreachable: entering such a
|
|
| 13 | + closure prints a one-shot warning at runtime, or aborts the program when
|
|
| 14 | + the new RTS flag ``--fatal-enter-taggable`` is given.
|
|
| 15 | +} |
| ... | ... | @@ -14,6 +14,7 @@ import GHC.Cmm.Type (Width(..)) |
| 14 | 14 | import GHC.Cmm.CLabel (CLabel, hasHaskellName)
|
| 15 | 15 | import GHC.CmmToAsm.CFG.Weight
|
| 16 | 16 | import GHC.Unit.Module (Module)
|
| 17 | +import GHC.Types.Name (isBootIndName)
|
|
| 17 | 18 | import GHC.Types.Name.Set (NameSet, elemNameSet)
|
| 18 | 19 | import GHC.Utils.Outputable
|
| 19 | 20 | |
| ... | ... | @@ -66,12 +67,13 @@ data NCGConfig = NCGConfig |
| 66 | 67 | -- | May a static indirection labelled @symbol@ be eliminated by redirecting it
|
| 67 | 68 | -- to its indirectee (the @.equiv@ trick, see Note [emit-time elimination of
|
| 68 | 69 | -- static indirections] in "GHC.Cmm.CLabel")? No, if @symbol@ is exported by
|
| 69 | --- this module's hs-boot file: SOURCE importers reference it untagged and must be
|
|
| 70 | +-- this module's hs-boot file, or is itself a boot indirection ('mkBootIndName'
|
|
| 71 | +-- in "GHC.Types.Name"): SOURCE importers reference it untagged and must be
|
|
| 70 | 72 | -- able to enter it to obtain the (tagged) indirectee.
|
| 71 | 73 | ncgLabelMayBeRedirected :: NCGConfig -> CLabel -> Bool
|
| 72 | 74 | ncgLabelMayBeRedirected config symbol =
|
| 73 | 75 | case hasHaskellName symbol of
|
| 74 | - Just nm -> not (nm `elemNameSet` ncgBootExports config)
|
|
| 76 | + Just nm -> not (isBootIndName nm || nm `elemNameSet` ncgBootExports config)
|
|
| 75 | 77 | Nothing -> True
|
| 76 | 78 | |
| 77 | 79 | -- | Return Word size
|
| ... | ... | @@ -12,6 +12,7 @@ import GHC.Prelude |
| 12 | 12 | import GHC.Platform
|
| 13 | 13 | |
| 14 | 14 | import GHC.Cmm.CLabel (CLabel, hasHaskellName)
|
| 15 | +import GHC.Types.Name (isBootIndName)
|
|
| 15 | 16 | import GHC.Types.Name.Set (NameSet, elemNameSet)
|
| 16 | 17 | import GHC.Utils.Outputable
|
| 17 | 18 | import GHC.Settings.Utils
|
| ... | ... | @@ -40,13 +41,14 @@ data LlvmCgConfig = LlvmCgConfig |
| 40 | 41 | -- | May a static indirection labelled @symbol@ be eliminated by redirecting it
|
| 41 | 42 | -- to its indirectee (the alias trick, see Note [emit-time elimination of static
|
| 42 | 43 | -- indirections] in "GHC.Cmm.CLabel")? No, if @symbol@ is exported by this
|
| 43 | --- module's hs-boot file: SOURCE importers reference it untagged and must be able
|
|
| 44 | +-- module's hs-boot file, or is itself a boot indirection ('mkBootIndName' in
|
|
| 45 | +-- "GHC.Types.Name"): SOURCE importers reference it untagged and must be able
|
|
| 44 | 46 | -- to enter it to obtain the (tagged) indirectee. Mirrors 'ncgLabelMayBeRedirected'
|
| 45 | 47 | -- in "GHC.CmmToAsm.Config".
|
| 46 | 48 | llvmLabelMayBeRedirected :: LlvmCgConfig -> CLabel -> Bool
|
| 47 | 49 | llvmLabelMayBeRedirected config symbol =
|
| 48 | 50 | case hasHaskellName symbol of
|
| 49 | - Just nm -> not (nm `elemNameSet` llvmCgBootExports config)
|
|
| 51 | + Just nm -> not (isBootIndName nm || nm `elemNameSet` llvmCgBootExports config)
|
|
| 50 | 52 | Nothing -> True
|
| 51 | 53 | |
| 52 | 54 | data LlvmTarget = LlvmTarget
|
| ... | ... | @@ -68,7 +68,6 @@ import GHC.Types.TyThing |
| 68 | 68 | import GHC.Types.CompleteMatch
|
| 69 | 69 | import GHC.Types.Name.Cache
|
| 70 | 70 | |
| 71 | -import GHC.Utils.Outputable
|
|
| 72 | 71 | import GHC.Utils.Panic
|
| 73 | 72 | import GHC.Utils.Logger
|
| 74 | 73 | import GHC.Utils.Binary
|
| ... | ... | @@ -139,7 +138,12 @@ mkFullIface hsc_env partial_iface mb_stg_infos mb_cmm_infos stubs foreign_files |
| 139 | 138 | -- value must carry the value's pointer tag, which needs its LambdaFormInfo),
|
| 140 | 139 | -- not an inlining pragma. Attach it regardless of -fomit-interface-pragmas
|
| 141 | 140 | -- so imported value references are tagged at every optimisation level.
|
| 142 | - let decls = updateDecl (mi_decls partial_iface) mb_stg_infos mb_cmm_infos
|
|
| 141 | + -- (At -O0 the code generator only conveys the correctness-relevant
|
|
| 142 | + -- LFInfos; see generatedInfo in GHC.StgToCmm.) CAF-info and tag sigs
|
|
| 143 | + -- remain ordinary pragmas, omitted under -fomit-interface-pragmas.
|
|
| 144 | + let omit_prags = gopt Opt_OmitInterfacePragmas (hsc_dflags hsc_env)
|
|
| 145 | + mb_stg_infos' = if omit_prags then Nothing else mb_stg_infos
|
|
| 146 | + decls = updateDecl (mi_decls partial_iface) mb_stg_infos' omit_prags mb_cmm_infos
|
|
| 143 | 147 | |
| 144 | 148 | -- See Note [Foreign stubs and TH bytecode linking]
|
| 145 | 149 | mi_simplified_core <- for (mi_simplified_core partial_iface) $ \simpl_core -> do
|
| ... | ... | @@ -189,13 +193,15 @@ shareIface nc compressionLevel mi = do |
| 189 | 193 | initBinMemSize :: Int
|
| 190 | 194 | initBinMemSize = 1024 * 1024 -- 1 MB
|
| 191 | 195 | |
| 192 | -updateDecl :: [IfaceDecl] -> Maybe StgCgInfos -> Maybe CmmCgInfos -> [IfaceDecl]
|
|
| 193 | -updateDecl decls Nothing Nothing = decls
|
|
| 194 | -updateDecl decls m_stg_infos m_cmm_infos
|
|
| 196 | +updateDecl :: [IfaceDecl] -> Maybe StgCgInfos -> Bool -> Maybe CmmCgInfos -> [IfaceDecl]
|
|
| 197 | +updateDecl decls Nothing _ Nothing = decls
|
|
| 198 | +updateDecl decls m_stg_infos omit_prags m_cmm_infos
|
|
| 195 | 199 | = map update_decl decls
|
| 196 | 200 | where
|
| 197 | 201 | (non_cafs,lf_infos) = maybe (mempty, mempty)
|
| 198 | - (\cmm_info -> (ncs_nameSet (cgNonCafs cmm_info), cgLFInfos cmm_info))
|
|
| 202 | + (\cmm_info -> ( if omit_prags then mempty
|
|
| 203 | + else ncs_nameSet (cgNonCafs cmm_info)
|
|
| 204 | + , cgLFInfos cmm_info ))
|
|
| 199 | 205 | m_cmm_infos
|
| 200 | 206 | tag_sigs = fromMaybe mempty m_stg_infos
|
| 201 | 207 | |
| ... | ... | @@ -203,9 +209,8 @@ updateDecl decls m_stg_infos m_cmm_infos |
| 203 | 209 | | let not_caffy = elemNameSet nm non_cafs
|
| 204 | 210 | , let mb_lf_info = lookupNameEnv lf_infos nm
|
| 205 | 211 | , let sig = lookupNameEnv tag_sigs nm
|
| 206 | - -- NB: with LFInfo now attached at every optimisation level, a missing
|
|
| 207 | - -- LFInfo is unremarkable (e.g. at -O0), so we do not trace it here.
|
|
| 208 | - , warnPprTrace False "updateDecl" (text "Name without LFInfo:" <+> ppr nm) True
|
|
| 212 | + -- A missing LFInfo is unremarkable: at -O0 only the
|
|
| 213 | + -- correctness-relevant LFInfos are conveyed (see GHC.StgToCmm).
|
|
| 209 | 214 | -- Only allocate a new IfaceId if we're going to update the infos
|
| 210 | 215 | , isJust mb_lf_info || not_caffy || isJust sig
|
| 211 | 216 | = IfaceId nm ty details $
|
| ... | ... | @@ -21,9 +21,11 @@ import GHC.StgToCmm.Utils |
| 21 | 21 | import GHC.StgToCmm.Closure
|
| 22 | 22 | import GHC.StgToCmm.Config
|
| 23 | 23 | import GHC.StgToCmm.Ticky
|
| 24 | -import GHC.StgToCmm.Types (ModuleLFInfos)
|
|
| 24 | +import GHC.StgToCmm.Types (ModuleLFInfos, LambdaFormInfo(..))
|
|
| 25 | 25 | import GHC.StgToCmm.CgUtils (CgStream)
|
| 26 | 26 | |
| 27 | +import GHC.Platform.Profile (profileIsProfiling)
|
|
| 28 | + |
|
| 27 | 29 | import GHC.Cmm
|
| 28 | 30 | import GHC.Cmm.Utils
|
| 29 | 31 | import GHC.Cmm.CLabel
|
| ... | ... | @@ -137,11 +139,21 @@ codeGen logger tmpfs cfg (InfoTableProvMap denv _ _) tycons |
| 137 | 139 | !lf = cg_lf info
|
| 138 | 140 | |
| 139 | 141 | -- LFInfo is part of the STG-ABI (a reference to an imported value
|
| 140 | - -- must carry its pointer tag), not an inlining pragma, so collect
|
|
| 141 | - -- it for every binding regardless of -fomit-interface-pragmas.
|
|
| 142 | - -- (Only LFInfo is conveyed here, never unfoldings.)
|
|
| 142 | + -- must carry its pointer tag), not an inlining pragma, so even
|
|
| 143 | + -- under -fomit-interface-pragmas we must convey the LFInfos that
|
|
| 144 | + -- pointer-tagging correctness depends on: values without entry
|
|
| 145 | + -- code that may be entered (LFCon under the tag-test in
|
|
| 146 | + -- emitEnter; LFScalar/LFPrim never). Functions and thunks are
|
|
| 147 | + -- safely enterable, so their LFInfo remains a mere optimisation
|
|
| 148 | + -- and is omitted at -O0 to keep interfaces small.
|
|
| 149 | + keep_lf_info lf = case lf of
|
|
| 150 | + LFCon{} -> True
|
|
| 151 | + LFScalar -> True
|
|
| 152 | + LFPrim -> True
|
|
| 153 | + _ -> not (stgToCmmOmitIfPragmas cfg)
|
|
| 143 | 154 | !generatedInfo
|
| 144 | - = mkNameEnv (Prelude.map extractInfo (nonDetEltsUFM cg_id_infos))
|
|
| 155 | + = mkNameEnv [ i | i@(_, lf) <- Prelude.map extractInfo (nonDetEltsUFM cg_id_infos)
|
|
| 156 | + , keep_lf_info lf ]
|
|
| 145 | 157 | |
| 146 | 158 | ; rn_mapping <- liftIO (readIORef uniqRnRef)
|
| 147 | 159 | ; liftIO $ debugTraceMsg logger 3 (text "DetRnM mapping:" <+> ppr rn_mapping)
|
| ... | ... | @@ -376,7 +388,11 @@ cgDataCon mn data_con |
| 376 | 388 | -- Larger families have no spare tag, so their values are entered
|
| 377 | 389 | -- as normal and the entry returns them tagged with the
|
| 378 | 390 | -- family-saturating tag.
|
| 379 | - ; when taggable $
|
|
| 391 | + -- When profiling, entering tagged constructors is sanctioned:
|
|
| 392 | + -- LDV profiling relies on it to mark closures as used (ENTER()
|
|
| 393 | + -- in rts/include/Cmm.h does not shortcut on the tag), so the
|
|
| 394 | + -- check would fire on every constructor use.
|
|
| 395 | + ; when (taggable && not (profileIsProfiling profile)) $
|
|
| 380 | 396 | emitCheckEnteredTaggable (showPprUnsafe data_con)
|
| 381 | 397 | ; void $ emitReturn
|
| 382 | 398 | [cmmOffsetB platform node (fromDynTag (tagForCon platform data_con))]
|
| ... | ... | @@ -45,6 +45,8 @@ import GHC.Types.Var.Env |
| 45 | 45 | import GHC.Utils.Outputable
|
| 46 | 46 | import GHC.Utils.Panic
|
| 47 | 47 | |
| 48 | +import Data.Maybe (isNothing)
|
|
| 49 | + |
|
| 48 | 50 | import GHC.Builtin.Names (getUnique)
|
| 49 | 51 | |
| 50 | 52 | |
| ... | ... | @@ -151,7 +153,12 @@ getCgIdInfo id |
| 151 | 153 | -- cgTopBinding, so no indirection exists for them; reference them
|
| 152 | 154 | -- directly. See Note [Boot-exported constructors and pointer
|
| 153 | 155 | -- tagging] in "GHC.StgToCmm.DataCon".
|
| 156 | + -- If the Id does carry LFInfo (its real interface got loaded for
|
|
| 157 | + -- some other reason), reference the value's own closure with its
|
|
| 158 | + -- proper tag: diverting would combine that tag with the
|
|
| 159 | + -- indirection's symbol, mistagging an IND_STATIC.
|
|
| 154 | 160 | use_boot_ind = needsBootInd id
|
| 161 | + && isNothing (idLFInfo_maybe id)
|
|
| 155 | 162 | && not (isDataConWorkId id)
|
| 156 | 163 | && nameModule name `elemModuleSet` stgToCmmSourceImports cfg
|
| 157 | 164 | ; if isExternalName name then
|
| ... | ... | @@ -47,7 +47,7 @@ module GHC.Types.Name ( |
| 47 | 47 | mkInternalName, mkClonedInternalName, mkDerivedInternalName,
|
| 48 | 48 | mkSystemVarName, mkSysTvName,
|
| 49 | 49 | mkFCallName,
|
| 50 | - mkExternalName, mkWiredInName, mkBootIndName,
|
|
| 50 | + mkExternalName, mkWiredInName, mkBootIndName, isBootIndName,
|
|
| 51 | 51 | |
| 52 | 52 | -- ** Manipulating and deconstructing 'Name's
|
| 53 | 53 | nameUnique, setNameUnique,
|
| ... | ... | @@ -545,9 +545,20 @@ mkExternalName uniq mod occ loc |
| 545 | 545 | -- pointer tagging] in "GHC.StgToCmm.DataCon".
|
| 546 | 546 | mkBootIndName :: Name -> Name
|
| 547 | 547 | mkBootIndName name =
|
| 548 | - mkExternalName (nameUnique name) (nameModule name)
|
|
| 548 | + -- Retag the unique rather than reuse it: reusing would make the indirection's
|
|
| 549 | + -- Name (and hence its CLabels) equal to the original's under Eq (by unique)
|
|
| 550 | + -- yet distinct under Ord (stableNameCmp, by occ). Retagging stays
|
|
| 551 | + -- deterministic, so the external symbol is still a pure function of module
|
|
| 552 | + -- and occurrence.
|
|
| 553 | + mkExternalName (newTagUnique (nameUnique name) BootIndTag) (nameModule name)
|
|
| 549 | 554 | (mkBootIndOcc (nameOccName name)) (nameSrcSpan name)
|
| 550 | 555 | |
| 556 | +-- | Is this a name made by 'mkBootIndName'? Recognised by its unique's tag,
|
|
| 557 | +-- so it works on names reconstructed from labels as well.
|
|
| 558 | +isBootIndName :: Name -> Bool
|
|
| 559 | +isBootIndName name = case unpkUnique (nameUnique name) of
|
|
| 560 | + (tag, _) -> tag == BootIndTag
|
|
| 561 | + |
|
| 551 | 562 | -- | Create a name which is actually defined by the compiler itself
|
| 552 | 563 | mkWiredInName :: Module -> OccName -> Unique -> TyThing -> BuiltInSyntax -> Name
|
| 553 | 564 | {-# INLINE mkWiredInName #-}
|
| ... | ... | @@ -133,6 +133,7 @@ data UniqueTag |
| 133 | 133 | = AlphaTyVarTag
|
| 134 | 134 | | BcoTag
|
| 135 | 135 | | BlockIdTag
|
| 136 | + | BootIndTag
|
|
| 136 | 137 | | BoxedTupleDataTag
|
| 137 | 138 | | BoxedTupleTyConTag
|
| 138 | 139 | | BoxingTyConTag
|
| ... | ... | @@ -184,6 +185,7 @@ uniqueTag :: UniqueTag -> Char |
| 184 | 185 | uniqueTag AlphaTyVarTag = '1'
|
| 185 | 186 | uniqueTag BcoTag = 'I'
|
| 186 | 187 | uniqueTag BlockIdTag = 'L'
|
| 188 | +uniqueTag BootIndTag = 'h'
|
|
| 187 | 189 | uniqueTag BoxedTupleDataTag = '7'
|
| 188 | 190 | uniqueTag BoxedTupleTyConTag = '4'
|
| 189 | 191 | uniqueTag BoxingTyConTag = 'b'
|
| ... | ... | @@ -253,6 +255,7 @@ charToUniqueTag 'D' = DsTag |
| 253 | 255 | charToUniqueTag 'E' = PseudoTag
|
| 254 | 256 | charToUniqueTag 'f' = FldNSTag
|
| 255 | 257 | charToUniqueTag 'g' = StgPTag
|
| 258 | +charToUniqueTag 'h' = BootIndTag
|
|
| 256 | 259 | charToUniqueTag 'H' = VirtualRegTag
|
| 257 | 260 | charToUniqueTag 'i' = IfaceTag
|
| 258 | 261 | charToUniqueTag 'I' = BcoTag
|
| ... | ... | @@ -9,7 +9,8 @@ stg_cloneMyStackzh () { |
| 9 | 9 | |
| 10 | 10 | ("ptr" clonedStack) = ccall cloneStack(MyCapability() "ptr", stgStack "ptr");
|
| 11 | 11 | |
| 12 | - return (clonedStack);
|
|
| 12 | + // Boxed unlifted primitives are tagged with 1.
|
|
| 13 | + return (clonedStack + 1);
|
|
| 13 | 14 | }
|
| 14 | 15 | |
| 15 | 16 | stg_sendCloneStackMessagezh (gcptr threadId, gcptr mVarStablePtr) {
|
| ... | ... | @@ -302,6 +302,10 @@ isWindowsHost = False |
| 302 | 302 | #endif
|
| 303 | 303 | |
| 304 | 304 | #if defined(wasm32_HOST_ARCH)
|
| 305 | +-- The wasm dynamic linker resolves symbols out of process, so the RTS
|
|
| 306 | +-- helper below is unavailable; looked-up constructor closures stay
|
|
| 307 | +-- untagged and forcing one triggers the (non-fatal) enter-taggable
|
|
| 308 | +-- warning.
|
|
| 305 | 309 | tagClosurePtr :: Ptr a -> Ptr a
|
| 306 | 310 | tagClosurePtr = id
|
| 307 | 311 | #else
|
| ... | ... | @@ -143,7 +143,7 @@ eval: |
| 143 | 143 | case ARR_WORDS: {
|
| 144 | 144 | |
| 145 | 145 | (should) = ccall shouldCompact(compact "ptr", p "ptr");
|
| 146 | - if (should == SHOULDCOMPACT_IN_CNF) { P_[pp] = p; return(); }
|
|
| 146 | + if (should == SHOULDCOMPACT_IN_CNF) { P_[pp] = tag | p; return(); }
|
|
| 147 | 147 | if (should == SHOULDCOMPACT_PINNED) {
|
| 148 | 148 | jump stg_raisezh(HsIface_cannotCompactPinned_closure(W_[ghc_hs_iface]));
|
| 149 | 149 | }
|
| ... | ... | @@ -154,7 +154,7 @@ eval: |
| 154 | 154 | W_ size;
|
| 155 | 155 | size = SIZEOF_StgArrBytes + StgArrBytes_bytes(p);
|
| 156 | 156 | ALLOCATE(compact, ROUNDUP_BYTES_TO_WDS(size), p, to, tag);
|
| 157 | - P_[pp] = to;
|
|
| 157 | + P_[pp] = tag | to;
|
|
| 158 | 158 | prim %memcpy(to, p, size, 1);
|
| 159 | 159 | return();
|
| 160 | 160 | }
|
| ... | ... | @@ -164,7 +164,7 @@ eval: |
| 164 | 164 | MUT_ARR_PTRS_FROZEN_CLEAN: {
|
| 165 | 165 | |
| 166 | 166 | (should) = ccall shouldCompact(compact "ptr", p "ptr");
|
| 167 | - if (should == SHOULDCOMPACT_IN_CNF) { P_[pp] = p; return(); }
|
|
| 167 | + if (should == SHOULDCOMPACT_IN_CNF) { P_[pp] = tag | p; return(); }
|
|
| 168 | 168 | |
| 169 | 169 | CHECK_HASH();
|
| 170 | 170 | |
| ... | ... | @@ -196,7 +196,7 @@ eval: |
| 196 | 196 | SMALL_MUT_ARR_PTRS_FROZEN_CLEAN: {
|
| 197 | 197 | |
| 198 | 198 | (should) = ccall shouldCompact(compact "ptr", p "ptr");
|
| 199 | - if (should == SHOULDCOMPACT_IN_CNF) { P_[pp] = p; return(); }
|
|
| 199 | + if (should == SHOULDCOMPACT_IN_CNF) { P_[pp] = tag | p; return(); }
|
|
| 200 | 200 | |
| 201 | 201 | CHECK_HASH();
|
| 202 | 202 | |
| ... | ... | @@ -459,5 +459,6 @@ stg_compactFixupPointerszh ( W_ first_block, W_ root ) |
| 459 | 459 | // guaranteed to be valid
|
| 460 | 460 | // (this is true even if the fixup phase failed)
|
| 461 | 461 | gcstr = str;
|
| 462 | - return (gcstr, ok);
|
|
| 462 | + // Boxed unlifted primitives are tagged with 1.
|
|
| 463 | + return (gcstr + 1, ok);
|
|
| 463 | 464 | } |
| ... | ... | @@ -88,9 +88,9 @@ checkEnteredTaggable(const char *con) |
| 88 | 88 | ssbarf("entered a taggable normal form: %s", con);
|
| 89 | 89 | // ssbarf does not return
|
| 90 | 90 | }
|
| 91 | - static int warned = 0;
|
|
| 92 | - if (!warned) {
|
|
| 93 | - warned = 1;
|
|
| 91 | + static StgWord warned = 0;
|
|
| 92 | + if (!RELAXED_LOAD(&warned)) {
|
|
| 93 | + RELAXED_STORE(&warned, 1);
|
|
| 94 | 94 | debugBelch("warning: entered a taggable normal form: %s\n"
|
| 95 | 95 | "(further occurrences suppressed; rerun with "
|
| 96 | 96 | "+RTS --fatal-enter-taggable to abort)\n",
|
| ... | ... | @@ -122,6 +122,30 @@ get_iptr_tag(StgInfoTable *iptr) |
| 122 | 122 | }
|
| 123 | 123 | }
|
| 124 | 124 | |
| 125 | + // Boxed unlifted primitives are tagged with 1 (see Note [Pointer tagging
|
|
| 126 | + // of unlifted boxed primitives] in GHC.StgToCmm.Prim). unthread() applies
|
|
| 127 | + // this tag only to fields that were tagged before threading, so a type
|
|
| 128 | + // listed here never gains a tag it did not have.
|
|
| 129 | + case ARR_WORDS:
|
|
| 130 | + case MUT_ARR_PTRS_CLEAN:
|
|
| 131 | + case MUT_ARR_PTRS_DIRTY:
|
|
| 132 | + case MUT_ARR_PTRS_FROZEN_CLEAN:
|
|
| 133 | + case MUT_ARR_PTRS_FROZEN_DIRTY:
|
|
| 134 | + case SMALL_MUT_ARR_PTRS_CLEAN:
|
|
| 135 | + case SMALL_MUT_ARR_PTRS_DIRTY:
|
|
| 136 | + case SMALL_MUT_ARR_PTRS_FROZEN_CLEAN:
|
|
| 137 | + case SMALL_MUT_ARR_PTRS_FROZEN_DIRTY:
|
|
| 138 | + case MUT_VAR_CLEAN:
|
|
| 139 | + case MUT_VAR_DIRTY:
|
|
| 140 | + case MVAR_CLEAN:
|
|
| 141 | + case MVAR_DIRTY:
|
|
| 142 | + case TVAR:
|
|
| 143 | + case WEAK:
|
|
| 144 | + case PRIM:
|
|
| 145 | + case MUT_PRIM:
|
|
| 146 | + case BLOCKING_QUEUE:
|
|
| 147 | + return 1;
|
|
| 148 | + |
|
| 125 | 149 | default:
|
| 126 | 150 | return 0;
|
| 127 | 151 | }
|