07 May '26
Wen Kokke pushed new branch wip/wenkokke/trace-ipec at Glasgow Haskell Compiler / GHC
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/tree/wip/wenkokke/trace-ipec
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/spj-reinstallable-base3] Comments and notes
by Simon Peyton Jones (@simonpj) 07 May '26
by Simon Peyton Jones (@simonpj) 07 May '26
07 May '26
Simon Peyton Jones pushed to branch wip/spj-reinstallable-base3 at Glasgow Haskell Compiler / GHC
Commits:
0d92b00e by Simon Peyton Jones at 2026-05-07T16:00:28+01:00
Comments and notes
- - - - -
3 changed files:
- compiler/GHC/Utils/Binary/Typeable.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Typeable/Internal.hs
- libraries/ghc-internal/src/GHC/Internal/Types.hs
Changes:
=====================================
compiler/GHC/Utils/Binary/Typeable.hs
=====================================
@@ -18,11 +18,19 @@ import GHC.Exts (Levity(Lifted, Unlifted))
import GHC.Serialized
import Foreign
-import Type.Reflection
+import Type.Reflection -- TyCon from bootstrap compiler e.g GHC 9.6
import Type.Reflection.Unsafe
import Data.Kind (Type)
+-- The entire purpose of this module is to provide
+-- instance Binary Serialized
+-- so that we can serialise annotations into an interface file
+--
+-- To serialise an annotation we need to serialise a TypeRep
+-- So if the inner representationns of TypeRep change, you'll need CPP
+-- so that this module can be compiled both by the bootstrap compiler and the stage-1 compiler
+
getSomeTypeRep :: ReadBinHandle -> IO SomeTypeRep
getSomeTypeRep = error "getSomeTypeRep"
@@ -31,7 +39,7 @@ instance Binary TyCon where
put_ bh tc = do
put_ bh (tyConPackage tc)
put_ bh (tyConModule tc)
- put_ bh (tyConName tc)
+` put_ bh (tyConName tc)
put_ bh (tyConKindArgs tc)
put_ bh (tyConKindRep tc)
get bh =
=====================================
libraries/ghc-internal/src/GHC/Internal/Data/Typeable/Internal.hs
=====================================
@@ -616,11 +616,14 @@ someTypeRepTyCon :: SomeTypeRep -> TyCon
someTypeRepTyCon (SomeTypeRep t) = typeRepTyCon t
-- | Observe the type constructor of a type representation
+-- E.g typeRepTyCon (T a b) = T
+-- typeRepTyCon (a -> b) = tyConFun
+-- typeRepTyCon Type = tyConTYPE
typeRepTyCon :: TypeRep a -> TyCon
-typeRepTyCon TrType = tyConTYPE
+typeRepTyCon TrType = tyConTYPE
typeRepTyCon (TrTyCon {trTyCon = tc}) = tc
typeRepTyCon (TrApp {trAppFun = a}) = typeRepTyCon a
-typeRepTyCon (TrFun {}) = typeRepTyCon $ typeRep @(->)
+typeRepTyCon (TrFun {}) = tyConArrow
-- | Type equality
--
@@ -694,9 +697,13 @@ tyConKind (TyCon _ _ _ _ nKindVars# kindRep) kindVars =
in instantiateKindRep kindVarsArr kindRep
instantiateKindRep :: A.Array KindBndr SomeTypeRep -> KindRep -> SomeTypeRep
+-- This function is THE principal consumer of KindRep
instantiateKindRep vars = go
where
go :: KindRep -> SomeTypeRep
+ go KindRepType = SomeTypeRep TrType -- Special magic for TrType
+ go KindRepConstraint = SomeTypeRep (typeRep @Constraint)
+
go (KindRepTyConApp tc args)
= let n_kind_args = tyConKindArgs tc
(kind_args, ty_args) = splitAt n_kind_args args
@@ -714,8 +721,7 @@ instantiateKindRep vars = go
= SomeTypeRep $ mkTrApp (unsafeCoerceRep $ go f) (unsafeCoerceRep $ go a)
go (KindRepFun a b)
= SomeTypeRep $ mkTrFun trMany (unsafeCoerceRep $ go a) (unsafeCoerceRep $ go b)
- go KindRepType = SomeTypeRep TrType -- Special magic for TrType
- go KindRepConstraint = unkindedTypeRep @(RuntimeRep -> Type) @Constraint
+
go (KindRepTypeLitS sort s)
= mkTypeLitFromString sort (unpackCStringUtf8# s)
go (KindRepTypeLitD sort s)
@@ -988,6 +994,9 @@ tyCon'Lifted = typeRepTyCon (typeRep @'Lifted)
tyCon'BoxedRep :: TyCon
tyCon'BoxedRep = typeRepTyCon (typeRep @'BoxedRep)
+tyConArrow :: TyCon
+tyConArrow = typeRepTyCon $ typeRep @(->)
+
{- OLD
tyConRuntimeRep :: TyCon
tyConRuntimeRep = mkTyCon ghcPrimPackage "GHC.Internal.Types" "RuntimeRep" 0
=====================================
libraries/ghc-internal/src/GHC/Internal/Types.hs
=====================================
@@ -897,10 +897,10 @@ type KindBndr = Int
-- See Note [Representing TyCon kinds: KindRep] in GHC.Tc.Instance.Typeable.
data KindRep = KindRepTyConApp TyCon [KindRep]
| KindRepVar !KindBndr
- | KindRepApp KindRep KindRep
- | KindRepFun KindRep KindRep
- | KindRepType
- | KindRepConstraint
+ | KindRepApp KindRep KindRep -- The kind (k1 k2)
+ | KindRepFun KindRep KindRep -- The kind k1->k2
+ | KindRepType -- The kind Type
+ | KindRepConstraint -- The kind Constraint
| KindRepTypeLitS TypeLitSort Addr#
| KindRepTypeLitD TypeLitSort [Char]
@@ -916,6 +916,18 @@ data TyCon = TyCon Word64# -- ^ Fingerprint (high)
Int# -- ^ How many kind variables do we accept?
KindRep -- ^ A representation of the type's kind
+intTyCon :: TyCon
+intTyCon = TyCon ... intKindRep
+
+-- Int :: TYPE (BoxedRep Lifted)
+intKindRep :: KindRep
+intKindRep = KindRepTyConApp tYPETyCon [t...]
+
+-- TYPE :: Type -> Type
+tYPETyCon :: TyCon
+tYPETyCon = TyCon ... (KindRepRun typeTyCon typTyCon)
+
+
{- *********************************************************************
* *
Unboxed tuples and sums
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/0d92b00e6cb0896cb2aee1de751e7f2…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/0d92b00e6cb0896cb2aee1de751e7f2…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/dcoutts/posix-ticker] 17 commits: Use __attribute__((dllimport)) for external RTS symbol declarations
by Duncan Coutts (@dcoutts) 07 May '26
by Duncan Coutts (@dcoutts) 07 May '26
07 May '26
Duncan Coutts pushed to branch wip/dcoutts/posix-ticker at Glasgow Haskell Compiler / GHC
Commits:
9a9ae4df by Duncan Coutts at 2026-05-05T14:44:37-04:00
Use __attribute__((dllimport)) for external RTS symbol declarations
This is needed to be hygenic about DLL symbol imports and exports.
The attribute is ignored on platforms other than Windows.
Use of the attribute however means that external data symbols do not
have a compile-time constant address (they are loaded using an
indirection). This means we have to adjust the rtsSyms initial linker
table so that it is a local constant in a function, rather than a global
constant. We now define it within a function that pre-populates the
symbol table with the RTS symbols.
- - - - -
2ad3e01e by Duncan Coutts at 2026-05-05T14:44:37-04:00
Fix the rts linker declarations for a few data symbols
and ensure that the (windows only) rts_IOManagerIsWin32Native data
symbol is marked as externally visible.
- - - - -
8ff4fdb5 by David Eichmann at 2026-05-05T14:44:37-04:00
Hadrian: Disable runtime pseudo relocations for RTS on windows hosts
- - - - -
96974723 by Teo Camarasu at 2026-05-05T14:45:20-04:00
ghci/TH: refactor to use IORef QState
This is a pure refactor and shouldn't modify semantics at all
- - - - -
eff6bfaf by Teo Camarasu at 2026-05-05T14:45:20-04:00
iserv: recover/getQ/putQ should behave same as internal interpreter
The internal and external interpreter should behave the same when
handling `recover`, the exeception recovery method of Q.
In practice, they diverge. In case of failure, the internal interpreter
only restores error message state to before the computation, wheras the
external interperter restores error message state *and* the state of putQ/getQ.
As far as I can tell this is a simple mistake in the implementation.
Note [TH recover with -fexternal-interpreter] describes the correct
behaviour but the implementation doesn't mirror this.
This change restores the correct behaviour by keeping the effects of
putQ in the erroring computation.
This is a breaking change since it modifies the behaviour of programs
that rely on recover ignoring putQ from failling computations when used
with the external interpreter. Although I highly doubt anyone relies on
this behaviour.
This divergence was first introduced in d00c308633fe7d216d31a1087e00e63532d87d6d.
As far as I can tell this was unintentional and tha commit was trying to solve a different bug.
Resolves #27022
- - - - -
1cb1d672 by Wen Kokke at 2026-05-06T09:53:40-04:00
rts: Add dynamic trace flags API
This commit adds an API to the RTS (exposed via Rts.h) that allows users to dynamically change the trace flags.
Prior to this commit, users were able to stop and start the profiling and heap profiling timers (via startProfTimer/stopProfTimer and startHeapProfTimer/stopHeapProfTimer).
This extends that functionality to also cover the core event types.
The getTraceFlag/setTraceFlag functions read and write the values of the trace flag cache, which is allocated by Trace.c, rather than modifying the members of RtsFlags.TraceFlags.
This is done under the assumption that the members of RtsFlags should not be modified after RTS initialisation.
Consequently, if the user modifies the trace flags using setTraceFlag, the object returned by getTraceFlags (from base) will not reflect these changes.
The trace flags are not protected by locks of any sort.
Hence, these functions are not thread-safe.
However, the trace flags are not modified by the RTS after initialisation, only read, so the race conditions introduced by one user modifying them are most likely benign.
This PR also puts the trace flag cache in a single global struct, as opposed to a collection of global variables, and changes the types of the individual flags from uint8_t to bool, as these have the same size on both Clang and GCC and are a better semantic match.
Prior to the change to uint8_t, they had type int, see 42c47cd6.
Even with its deprecation in C23, I don't think there should be any issue depending on stdbool.h.
The TRACE_X macros are redefined to access the global struct, with values cast to const bool to ensure they are read-only.
- - - - -
9d54dc94 by Wen Kokke at 2026-05-06T09:53:40-04:00
rts: Ensure TRACE_X values are used in place of RtsFlags.TraceFlags.X
- - - - -
418d737b by Wen Kokke at 2026-05-06T09:53:40-04:00
rts: Fix nonmoving-GC tracing
The current nonmoving-GC tracing functions were written in a different
style from the other tracing functions. They were directly implemented
as, e.g., a traceConcMarkEnd function that called postConcMarkEnd.
The other tracing functions are implemented as, e.g., traceThreadLabel_,
a function that posts the thread label event, and traceThreadLabel, a
macro that checks whether TRACE_scheduler is set. This commit fixes that
implementation, and ensures that the nonmoving-GC tracing functions only
emit events if nonmoving-GC tracing is enabled.
- - - - -
99f4afa4 by Wen Kokke at 2026-05-06T09:53:40-04:00
rts: Add SymI_HasProto for get/setTraceFlag
- - - - -
7e9eb8b9 by Wen Kokke at 2026-05-06T09:53:40-04:00
rts: Add SymI_HasProto for start/endEventLogging
- - - - -
3a3045fb by Wen Kokke at 2026-05-06T09:53:41-04:00
rts: Add changelog entry
- - - - -
a3b339a4 by Teo Camarasu at 2026-05-06T09:54:25-04:00
interface-stability/base: don't distinguish ws-32
The interface of base is identical when the Word size is 32bits.
Therefore, there is no need to have another file for this case.
So, we delete it.
Step towards: #26752
- - - - -
eb922183 by Duncan Coutts at 2026-05-07T14:28:50+01:00
Add a rts posix FdWakup utility module
This will be used to implement wakeupIOManager for in-RTS I/O managers.
It provides a notification/wakeup mechanism using FDs, suitable for
situations when a thread is blocked on a set of fds anyway. It uses the
classic self-pipe trick, or equivalently eventfd on supported platforms.
This will initially be used to implement prompt interrupt or shutdown of
the posix ticker thread.
- - - - -
01b0e233 by Duncan Coutts at 2026-05-07T14:28:50+01:00
Add prompt shutdown to the pthread ticker implementation.
The Linux timerfd ticker monitors a pipe which is used by exitTicker to
ensure a prompt wakeup and shutdown. The pthread ticker lacked this and
so would only exit at the next ticker wakeup (10ms by default).
This patch adds the same mechanism to the pthread ticker.
This changes the pthread ticker from waiting by using nanosleep() to
waiting using either ppoll() or select(), so that it can wait on both
a time and a file descriptor. On Linux at least, a test program to
compare the timing jitter of these APIs shows that using nanpsleep,
ppoll or select makes no statistical difference to the maximum or
average jitter.
This is a step towards unifying the posix ticker implementations, so
that we can have just one portable one (albeit with some limited cpp).
It is also a step towards using the ticker as part of a more general
implementation of wakeUpRts, since this will require a method to wake
the rts from a signal handler context (ctl-c handler).
- - - - -
bc41d646 by Duncan Coutts at 2026-05-07T14:28:50+01:00
Update ticker header commentary
It was antique and didn't apply even to the previous implementation, and
certainly not to the updated one.
- - - - -
4ed9a386 by Duncan Coutts at 2026-05-07T14:28:50+01:00
Remove the timerfd-based ticker implementation
There does not appear to be any remaining advantage on Linux to using
the timerfd ticker implementation over the portable one (using ppoll on
Linux for precise timing).
The eventfd implementation was originally added at a time when Linux was
still using a signal based implementation. So it made sense at the time.
See (closed) issue #10840.
- - - - -
97504fa6 by Duncan Coutts at 2026-05-07T14:28:50+01:00
Consolidate to a single posix ticker implementation
Previously we had four implementations, two using signals and two using
threads. Having just one should make behaviour more consistent between
platforms, and should make maintenance easier.
- - - - -
24 changed files:
- + changelog.d/T27022
- + changelog.d/dynamic-trace-flags
- hadrian/src/Settings/Packages.hs
- libraries/ghci/GHCi/TH.hs
- rts/IOManager.h
- rts/Linker.c
- rts/LinkerInternals.h
- rts/RtsSymbols.c
- rts/RtsSymbols.h
- rts/Trace.c
- rts/Trace.h
- rts/include/rts/EventLogWriter.h
- rts/linker/Elf.c
- + rts/posix/FdWakeup.c
- + rts/posix/FdWakeup.h
- rts/posix/Ticker.c
- − rts/posix/ticker/Pthread.c
- − rts/posix/ticker/TimerFd.c
- rts/rts.cabal
- rts/sm/NonMoving.c
- − testsuite/tests/interface-stability/base-exports.stdout-ws-32
- + testsuite/tests/th/T27022.hs
- + testsuite/tests/th/T27022.stdout
- testsuite/tests/th/all.T
Changes:
=====================================
changelog.d/T27022
=====================================
@@ -0,0 +1,11 @@
+section: compiler
+synopsis: Fix a divergence in the interaction between ``recover`` and ``putQ`` between the internal and external interpreter
+description: The ``recover`` method in TemplateHaskell now behaves the same
+ with the internal and external interpreter.
+ In the past, when an error was encountered in a computation in a ``recover`` block,
+ the external interpreter would discard any state changes from ``putQ``,
+ whereas the internal interpreter would not.
+ This was a long-standing error in the implementation of the external interpreter.
+ Both now keep state changes from ``putQ`` in ``recover`` blocks.
+mrs: !15994
+issues: #27022
=====================================
changelog.d/dynamic-trace-flags
=====================================
@@ -0,0 +1,10 @@
+section: compiler
+synopsis: Support dynamic trace flags in RTS
+issues: #27186
+mrs: !15936
+
+description: {
+ The RTS API now exposes the `RUNTIME_TRACE_FLAG` type and
+ the `getTraceFlags` and `setTraceFlags` functions that can be used to
+ change the trace flags at runtime.
+}
=====================================
hadrian/src/Settings/Packages.hs
=====================================
@@ -322,6 +322,7 @@ rtsPackageArgs = package rts ? do
, Profiling `wayUnit` way ? arg "-DPROFILING"
, Threaded `wayUnit` way ? arg "-DTHREADED_RTS"
, notM targetSupportsSMP ? arg "-optc-DNOSMP"
+ , isWinHost ? arg "-optl-Wl,--disable-runtime-pseudo-reloc"
-- See Note [AutoApply.cmm for vectors] in genapply/Main.hs
--
=====================================
libraries/ghci/GHCi/TH.hs
=====================================
@@ -119,7 +119,7 @@ initQState :: Pipe -> QState
initQState p = QState M.empty Nothing p
-- | The monad in which we run TH computations on the server
-newtype GHCiQ a = GHCiQ { runGHCiQ :: QState -> IO (a, QState) }
+newtype GHCiQ a = GHCiQ { runGHCiQ :: IORef QState -> IO a }
-- | The exception thrown by "fail" in the GHCiQ monad
data GHCiQException = GHCiQException QState String
@@ -128,52 +128,54 @@ data GHCiQException = GHCiQException QState String
instance Exception GHCiQException
instance Functor GHCiQ where
- fmap f (GHCiQ s) = GHCiQ $ fmap (\(x,s') -> (f x,s')) . s
+ fmap f (GHCiQ m) = GHCiQ $ fmap f . m
instance Applicative GHCiQ where
f <*> a = GHCiQ $ \s ->
- do (f',s') <- runGHCiQ f s
- (a',s'') <- runGHCiQ a s'
- return (f' a', s'')
- pure x = GHCiQ (\s -> return (x,s))
+ do f' <- runGHCiQ f s
+ a' <- runGHCiQ a s
+ return $ f' a'
+ pure x = GHCiQ $ \_ -> return x
instance Monad GHCiQ where
m >>= f = GHCiQ $ \s ->
- do (m', s') <- runGHCiQ m s
- (a, s'') <- runGHCiQ (f m') s'
- return (a, s'')
+ do m' <- runGHCiQ m s
+ a <- runGHCiQ (f m') s
+ return a
instance MonadFail GHCiQ where
- fail err = GHCiQ $ \s -> throwIO (GHCiQException s err)
+ fail err = GHCiQ $ \sRef -> readIORef sRef >>= \s -> throwIO (GHCiQException s err)
getState :: GHCiQ QState
-getState = GHCiQ $ \s -> return (s,s)
+getState = GHCiQ $ \sRef -> readIORef sRef
noLoc :: TH.Loc
noLoc = TH.Loc "<no file>" "<no package>" "<no module>" (0,0) (0,0)
-- | Send a 'THMessage' to GHC and return the result.
ghcCmd :: Binary a => THMessage (THResult a) -> GHCiQ a
-ghcCmd m = GHCiQ $ \s -> do
+ghcCmd m = GHCiQ $ \sRef -> do
+ s <- readIORef sRef
r <- remoteTHCall (qsPipe s) m
case r of
THException str -> throwIO (GHCiQException s str)
- THComplete res -> return (res, s)
+ THComplete res -> return res
instance MonadIO GHCiQ where
- liftIO m = GHCiQ $ \s -> fmap (,s) m
+ liftIO m = GHCiQ $ \_ -> m
instance TH.Quasi GHCiQ where
qNewName str = ghcCmd (NewName str)
qReport isError msg = ghcCmd (Report isError msg)
-- See Note [TH recover with -fexternal-interpreter] in GHC.Tc.Gen.Splice
- qRecover (GHCiQ h) a = GHCiQ $ \s -> mask $ \unmask -> do
+ qRecover (GHCiQ h) a = GHCiQ $ \sRef -> mask $ \unmask -> do
+ s <- readIORef sRef
remoteTHCall (qsPipe s) StartRecover
- e <- try $ unmask $ runGHCiQ (a <* ghcCmd FailIfErrs) s
+ e <- try $ unmask $ runGHCiQ (a <* ghcCmd FailIfErrs) sRef
remoteTHCall (qsPipe s) (EndRecover (isLeft e))
case e of
- Left GHCiQException{} -> h s
+ Left GHCiQException{} -> h sRef
Right r -> return r
qLookupName isType occ = ghcCmd (LookupName isType occ)
qReify name = ghcCmd (Reify name)
@@ -200,15 +202,16 @@ instance TH.Quasi GHCiQ where
qAddTempFile suffix = ghcCmd (AddTempFile suffix)
qAddTopDecls decls = ghcCmd (AddTopDecls decls)
qAddForeignFilePath lang fp = ghcCmd (AddForeignFilePath lang fp)
- qAddModFinalizer fin = GHCiQ (\s -> mkRemoteRef fin >>= return . (, s)) >>=
+ qAddModFinalizer fin = GHCiQ (\_ -> mkRemoteRef fin) >>=
ghcCmd . AddModFinalizer
qAddCorePlugin str = ghcCmd (AddCorePlugin str)
- qGetQ = GHCiQ $ \s ->
+ qGetQ = do
+ s <- getState
let lookup :: forall a. Typeable a => Map TypeRep Dynamic -> Maybe a
lookup m = fromDynamic =<< M.lookup (typeOf (undefined::a)) m
- in return (lookup (qsMap s), s)
- qPutQ k = GHCiQ $ \s ->
- return ((), s { qsMap = M.insert (typeOf k) (toDyn k) (qsMap s) })
+ return $ lookup (qsMap s)
+ qPutQ k = GHCiQ $ \sRef ->
+ modifyIORef' sRef (\s -> s { qsMap = M.insert (typeOf k) (toDyn k) (qsMap s) })
qIsExtEnabled x = ghcCmd (IsExtEnabled x)
qExtsEnabled = ghcCmd ExtsEnabled
qPutDoc l s = ghcCmd (PutDoc l s)
@@ -231,7 +234,8 @@ runModFinalizerRefs pipe rstate qrefs = do
qs <- mapM localRef qrefs
qstateref <- localRef rstate
qstate <- readIORef qstateref
- _ <- runGHCiQ (TH.runQ $ sequence_ qs) qstate { qsPipe = pipe }
+ qstate' <- newIORef $ qstate { qsPipe = pipe }
+ _ <- runGHCiQ (TH.runQ $ sequence_ qs) qstate'
return ()
-- | The implementation of the 'RunTH' message
@@ -267,8 +271,6 @@ runTHQ
-> IO ByteString
runTHQ pipe rstate mb_loc ghciq = do
qstateref <- localRef rstate
- qstate <- readIORef qstateref
- let st = qstate { qsLocation = mb_loc, qsPipe = pipe }
- (r,new_state) <- runGHCiQ (TH.runQ ghciq) st
- writeIORef qstateref new_state
+ modifyIORef' qstateref (\qstate -> qstate { qsLocation = mb_loc, qsPipe = pipe })
+ r <- runGHCiQ (TH.runQ ghciq) qstateref
return $! LB.toStrict (runPut (put r))
=====================================
rts/IOManager.h
=====================================
@@ -21,6 +21,15 @@
#include "sm/GC.h" // for evac_fn
+#if defined(mingw32_HOST_OS)
+/* Global var (only on Windows) that is exported (hence before BeginPrivate.h)
+ * to be shared with the I/O code in the base library to tell us which style
+ * of I/O manager we are using: one that uses the Windows native API HANDLEs,
+ * or one that uses Posix style fds.
+ */
+extern bool rts_IOManagerIsWin32Native;
+#endif
+
#include "BeginPrivate.h"
/* The ./configure gives us a set of CPP flags, one for each named I/O manager:
@@ -160,14 +169,6 @@ typedef enum {
/* Global var to tell us which I/O manager impl we are using */
extern IOManagerType iomgr_type;
-#if defined(mingw32_HOST_OS)
-/* Global var (only on Windows) that is exported to be shared with the I/O code
- * in the base library to tell us which style of I/O manager we are using: one
- * that uses the Windows native API HANDLEs, or one that uses Posix style fds.
- */
-extern bool rts_IOManagerIsWin32Native;
-#endif
-
/* The CapIOManager is the per-capability data structure belonging to the I/O
* manager. It is defined in full in IOManagerInternals.h. The opaque forward
=====================================
rts/Linker.c
=====================================
@@ -478,16 +478,7 @@ initLinker_ (int retain_cafs)
symhash = allocStrHashTable();
/* populate the symbol table with stuff from the RTS */
- IF_DEBUG(linker, debugBelch("populating linker symbol table with built-in RTS symbols\n"));
- for (const RtsSymbolVal *sym = rtsSyms; sym->lbl != NULL; sym++) {
- IF_DEBUG(linker, debugBelch("initLinker: inserting rts symbol %s, %p\n", sym->lbl, sym->addr));
- if (! ghciInsertSymbolTable(WSTR("(GHCi built-in symbols)"),
- symhash, sym->lbl, sym->addr,
- sym->strength, sym->type, 0, NULL)) {
- barf("ghciInsertSymbolTable failed");
- }
- }
- IF_DEBUG(linker, debugBelch("done with built-in RTS symbols\n"));
+ initLinkerRtsSyms(symhash);
/* Add extra symbols. rtsExtraSyms() is a weakly defined symbol in the rts,
* that can be overrided by linking in an object with a corresponding
=====================================
rts/LinkerInternals.h
=====================================
@@ -502,4 +502,6 @@ ObjectCode* mkOc( ObjectType type, pathchar *path, char *image, int imageSize,
void initSegment(Segment *s, void *start, size_t size, SegmentProt prot, int n_sections);
void freeSegments(ObjectCode *oc);
+void initLinkerRtsSyms(StrHashTable *symhash);
+
#include "EndPrivate.h"
=====================================
rts/RtsSymbols.c
=====================================
@@ -9,6 +9,8 @@
#include "ghcplatform.h"
#include "Rts.h"
#include "RtsSymbols.h"
+#include "LinkerInternals.h"
+#include "PathUtils.h"
#include "TopHandler.h"
#include "HsFFI.h"
@@ -51,6 +53,20 @@ extern char **environ;
/* -----------------------------------------------------------------------------
* Symbols to be inserted into the RTS symbol table.
+ *
+ * Note [Naming Scheme for Symbol Macros]
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * SymI_*: symbol is internal to the RTS. It resides in an object
+ * file/library that is linked into the RTS library (as a static
+ * archive or dynamic shared library).
+ * SymE_*: symbol is external to the RTS library. It might be linked
+ * dynamically.
+ *
+ * Sym*_HasProto : the symbol prototype is imported in an include file
+ * or defined explicitly
+ * Sym*_NeedsProto: the symbol is undefined and we add a dummy
+ * default proto extern void sym(void);
*/
#define Maybe_Stable_Names SymI_HasProto(stg_mkWeakzh) \
@@ -162,7 +178,7 @@ extern char **environ;
SymI_HasProto(stg_asyncWritezh) \
SymI_HasProto(stg_asyncDoProczh) \
SymI_HasProto(rts_InstallConsoleEvent) \
- SymI_HasProto(rts_IOManagerIsWin32Native) \
+ SymI_HasDataProto(rts_IOManagerIsWin32Native) \
SymI_HasProto(rts_ConsoleHandlerDone) \
SymI_NeedsProto(__mingw_module_is_dll) \
RTS_WIN64_ONLY(SymI_NeedsProto(___chkstk_ms)) \
@@ -524,7 +540,12 @@ extern char **environ;
SymI_HasProto(__word_encodeFloat) \
SymI_HasDataProto(stg_atomicallyzh) \
SymI_HasProto(barf) \
+ SymI_HasProto(startEventLogging) \
+ SymI_HasProto(endEventLogging) \
SymI_HasProto(flushEventLog) \
+ SymI_HasProto(flushEventLog) \
+ SymI_HasProto(getTraceFlag) \
+ SymI_HasProto(setTraceFlag) \
SymI_HasProto(deRefStablePtr) \
SymI_HasProto(debugBelch) \
SymI_HasProto(errorBelch) \
@@ -914,7 +935,7 @@ extern char **environ;
SymI_HasProto(freeExecPage) \
SymI_HasProto(getAllocations) \
SymI_HasProto(revertCAFs) \
- SymI_HasProto(RtsFlags) \
+ SymI_HasDataProto(RtsFlags) \
SymI_NeedsDataProto(rts_breakpoint_io_action) \
SymI_NeedsDataProto(rts_stop_next_breakpoint) \
SymI_NeedsDataProto(rts_stop_on_exception) \
@@ -925,9 +946,9 @@ extern char **environ;
SymI_NeedsProto(rts_enableStopAfterReturn) \
SymI_NeedsProto(rts_disableStopAfterReturn) \
SymI_HasProto(stopTimer) \
- SymI_HasProto(n_capabilities) \
- SymI_HasProto(max_n_capabilities) \
- SymI_HasProto(enabled_capabilities) \
+ SymI_HasDataProto(n_capabilities) \
+ SymI_HasDataProto(max_n_capabilities) \
+ SymI_HasDataProto(enabled_capabilities) \
SymI_HasDataProto(stg_traceEventzh) \
SymI_HasDataProto(stg_traceMarkerzh) \
SymI_HasDataProto(stg_traceBinaryEventzh) \
@@ -1145,12 +1166,27 @@ extern char **environ;
SymI_HasProto(hs_word2float64)
-/* entirely bogus claims about types of these symbols */
-#define SymI_NeedsProto(vvv) extern void vvv(void);
-#define SymI_NeedsDataProto(vvv) extern StgWord vvv[];
-#define SymE_NeedsProto(vvv) SymI_NeedsProto(vvv);
-#define SymE_NeedsDataProto(vvv) SymI_NeedsDataProto(vvv);
-#define SymE_HasProto(vvv) SymI_HasProto(vvv);
+/* Declare prototypes for the symbols that need it, so we can refer
+ * to them in the rtsSyms table below.
+ *
+ * In particular, for the external ones (SymE_*) we use the dllimport attribute
+ * to indicate that (on Windows) they come from external DLLs. This attribute
+ * is ignored on other platforms.
+ *
+ * The claims about the types of these symbols are entirely bogus.
+ */
+#if defined(mingw32_HOST_OS) && defined(DYNAMIC)
+#define DLLIMPORT __attribute__((dllimport))
+#else
+#define DLLIMPORT /**/
+#endif
+
+#define SymI_NeedsProto(vvv) extern void vvv(void);
+#define SymI_NeedsDataProto(vvv) extern StgWord vvv[];
+#define SymE_NeedsProto(vvv) extern DLLIMPORT void vvv(void);
+#define SymE_NeedsDataProto(vvv) extern DLLIMPORT StgWord vvv[];
+
+#define SymE_HasProto(vvv) /**/
#define SymI_HasProto(vvv) /**/
#define SymI_HasDataProto(vvv) /**/
#define SymI_HasProto_redirect(vvv,xxx,strength,ty) /**/
@@ -1179,6 +1215,8 @@ RTS_SYMBOLS_PRIM
#undef SymE_NeedsProto
#undef SymE_NeedsDataProto
+/* See Note [Naming Scheme for Symbol Macros] */
+
#define SymI_HasProto(vvv) { MAYBE_LEADING_UNDERSCORE_STR(#vvv), \
(void*)(&(vvv)), STRENGTH_NORMAL, SYM_TYPE_CODE },
#define SymI_HasDataProto(vvv) { MAYBE_LEADING_UNDERSCORE_STR(#vvv), \
@@ -1199,7 +1237,16 @@ RTS_SYMBOLS_PRIM
{ MAYBE_LEADING_UNDERSCORE_STR(#vvv), \
(void*)(&(xxx)), strength, ty },
-RtsSymbolVal rtsSyms[] = {
+
+
+/* Initialize (if not already initialized) and return an array of symbols with stuff from the RTS. */
+void initLinkerRtsSyms (StrHashTable *symhash) {
+ /* The address of data symbols with the dllimport attribute are not
+ * compile-time constants and so cannot be used in constant initialisers.
+ * For this reason, rtsSyms is a local variable within this function
+ * rather than a global constant (as it was historically).
+ */
+ const RtsSymbolVal rtsSyms[] = {
RTS_SYMBOLS
RTS_RET_SYMBOLS
RTS_POSIX_ONLY_SYMBOLS
@@ -1214,7 +1261,20 @@ RtsSymbolVal rtsSyms[] = {
RTS_SYMBOLS_PRIM
SymI_HasDataProto(nonmoving_write_barrier_enabled)
{ 0, 0, STRENGTH_NORMAL, SYM_TYPE_CODE } /* sentinel */
-};
+ };
+
+ IF_DEBUG(linker, debugBelch("populating linker symbol table with built-in RTS symbols\n"));
+ for (const RtsSymbolVal *sym = rtsSyms; sym->lbl != NULL; sym++) {
+ IF_DEBUG(linker, debugBelch("initLinker: inserting rts symbol %s, %p\n", sym->lbl, sym->addr));
+ if (! ghciInsertSymbolTable(WSTR("(GHCi built-in symbols)"),
+ symhash, sym->lbl, sym->addr,
+ sym->strength, sym->type, 0, NULL)) {
+ barf("ghciInsertSymbolTable failed");
+ }
+ }
+ IF_DEBUG(linker, debugBelch("done with built-in RTS symbols\n"));
+}
+
// Note [Extra RTS symbols]
=====================================
rts/RtsSymbols.h
=====================================
@@ -46,8 +46,6 @@ typedef struct _RtsSymbolVal {
SymType type;
} RtsSymbolVal;
-extern RtsSymbolVal rtsSyms[];
-
extern RtsSymbolVal* __attribute__((weak)) rtsExtraSyms(void);
/* See Note [_iob_func symbol]. */
=====================================
rts/Trace.c
=====================================
@@ -29,14 +29,54 @@
#include <unistd.h>
#endif
-// events
-uint8_t TRACE_sched;
-uint8_t TRACE_gc;
-uint8_t TRACE_nonmoving_gc;
-uint8_t TRACE_spark_sampled;
-uint8_t TRACE_spark_full;
-uint8_t TRACE_user;
-uint8_t TRACE_cap;
+RUNTIME_TRACE_FLAG_CACHE RuntimeTraceFlagCache = {0};
+
+bool getTraceFlag(RUNTIME_TRACE_FLAG flag) {
+ switch (flag) {
+ case TRACE_SCHEDULER:
+ return RuntimeTraceFlagCache.scheduler;
+ case TRACE_GC:
+ return RuntimeTraceFlagCache.gc;
+ case TRACE_NONMOVING_GC:
+ return RuntimeTraceFlagCache.nonmoving_gc;
+ case TRACE_SPARK_SAMPLED:
+ return RuntimeTraceFlagCache.spark_sampled;
+ case TRACE_SPARK_FULL:
+ return RuntimeTraceFlagCache.spark_full;
+ case TRACE_USER:
+ return RuntimeTraceFlagCache.user;
+ case TRACE_CAP:
+ return RuntimeTraceFlagCache.cap;
+ default:
+ return false;
+ }
+}
+
+void setTraceFlag(RUNTIME_TRACE_FLAG flag, bool value) {
+ switch (flag) {
+ case TRACE_SCHEDULER:
+ RuntimeTraceFlagCache.scheduler = value;
+ break;
+ case TRACE_GC:
+ RuntimeTraceFlagCache.gc = value;
+ break;
+ case TRACE_NONMOVING_GC:
+ RuntimeTraceFlagCache.nonmoving_gc = value;
+ break;
+ case TRACE_SPARK_SAMPLED:
+ RuntimeTraceFlagCache.spark_sampled = value;
+ break;
+ case TRACE_SPARK_FULL:
+ RuntimeTraceFlagCache.spark_full = value;
+ break;
+ case TRACE_USER:
+ RuntimeTraceFlagCache.user = value;
+ break;
+ case TRACE_CAP:
+ RuntimeTraceFlagCache.cap = value;
+ break;
+ }
+}
#if defined(THREADED_RTS)
static Mutex trace_utx;
@@ -51,43 +91,41 @@ static void traceCap_stderr(Capability *cap, char *msg, ...);
--------------------------------------------------------------------------- */
/*
- * Update the TRACE_* globals. Must be called whenever RtsFlags.TraceFlags is
- * modified.
+ * Initialise the runtime trace flags from RtsFlags.TraceFlags.
*/
-static void updateTraceFlagCache (void)
-{
- // -Ds turns on scheduler tracing too
- TRACE_sched =
- RtsFlags.TraceFlags.scheduler ||
- RtsFlags.DebugFlags.scheduler;
-
- // -Dg turns on gc tracing too
- TRACE_gc =
- RtsFlags.TraceFlags.gc ||
- RtsFlags.DebugFlags.gc ||
- RtsFlags.DebugFlags.scheduler;
-
- TRACE_nonmoving_gc =
- RtsFlags.TraceFlags.nonmoving_gc;
-
- TRACE_spark_sampled =
- RtsFlags.TraceFlags.sparks_sampled;
-
- // -Dr turns on full spark tracing
- TRACE_spark_full =
- RtsFlags.TraceFlags.sparks_full ||
- RtsFlags.DebugFlags.sparks;
-
- TRACE_user =
- RtsFlags.TraceFlags.user;
-
- // We trace cap events if we're tracing anything else
- TRACE_cap =
- TRACE_sched ||
- TRACE_gc ||
- TRACE_spark_sampled ||
- TRACE_spark_full ||
- TRACE_user;
+static void updateTraceFlagCache(void) {
+ // -Ds turns on scheduler tracing too
+ RuntimeTraceFlagCache.scheduler =
+ RtsFlags.TraceFlags.scheduler ||
+ RtsFlags.DebugFlags.scheduler;
+
+ // -Dg turns on gc tracing too
+ RuntimeTraceFlagCache.gc =
+ RtsFlags.TraceFlags.gc ||
+ RtsFlags.DebugFlags.gc ||
+ RtsFlags.DebugFlags.scheduler;
+
+ RuntimeTraceFlagCache.nonmoving_gc =
+ RtsFlags.TraceFlags.nonmoving_gc;
+
+ RuntimeTraceFlagCache.spark_sampled =
+ RtsFlags.TraceFlags.sparks_sampled;
+
+ // -Dr turns on full spark tracing
+ RuntimeTraceFlagCache.spark_full =
+ RtsFlags.TraceFlags.sparks_full ||
+ RtsFlags.DebugFlags.sparks;
+
+ RuntimeTraceFlagCache.user =
+ RtsFlags.TraceFlags.user;
+
+ // We trace cap events if we're tracing anything else
+ RuntimeTraceFlagCache.cap =
+ TRACE_sched ||
+ TRACE_gc ||
+ TRACE_spark_sampled ||
+ TRACE_spark_full ||
+ TRACE_user;
}
void initTracing (void)
@@ -880,59 +918,65 @@ void traceThreadLabel_(Capability *cap,
}
}
-void traceConcMarkBegin(void)
+void traceNonmovingGcEvent_ (EventTypeNum tag)
{
- if (eventlog_enabled)
- postEventNoCap(EVENT_CONC_MARK_BEGIN);
+#if defined(DEBUG)
+ if (RtsFlags.TraceFlags.tracing == TRACE_STDERR) {
+ /* nothing - no string representation for nonmoving GC events */
+ } else
+#endif
+ {
+ /* currently most non-moving GC events are nullary events */
+ postEventNoCap(tag);
+ }
}
-void traceConcMarkEnd(StgWord32 marked_obj_count)
+void traceConcMarkEnd_(StgWord32 marked_obj_count)
{
- if (eventlog_enabled)
+#if defined(DEBUG)
+ if (RtsFlags.TraceFlags.tracing == TRACE_STDERR) {
+ /* nothing - no string representation for nonmoving GC events */
+ } else
+#endif
+ {
postConcMarkEnd(marked_obj_count);
+ }
}
-void traceConcSyncBegin(void)
-{
- if (eventlog_enabled)
- postEventNoCap(EVENT_CONC_SYNC_BEGIN);
-}
-
-void traceConcSyncEnd(void)
-{
- if (eventlog_enabled)
- postEventNoCap(EVENT_CONC_SYNC_END);
-}
-
-void traceConcSweepBegin(void)
-{
- if (eventlog_enabled)
- postEventNoCap(EVENT_CONC_SWEEP_BEGIN);
-}
-
-void traceConcSweepEnd(void)
-{
- if (eventlog_enabled)
- postEventNoCap(EVENT_CONC_SWEEP_END);
-}
-
-void traceConcUpdRemSetFlush(Capability *cap)
+void traceConcUpdRemSetFlush_(Capability *cap)
{
- if (eventlog_enabled)
+#if defined(DEBUG)
+ if (RtsFlags.TraceFlags.tracing == TRACE_STDERR) {
+ /* nothing - no string representation for nonmoving GC events */
+ } else
+#endif
+ {
postConcUpdRemSetFlush(cap);
+ }
}
-void traceNonmovingHeapCensus(uint16_t blk_size,
- const struct NonmovingAllocCensus *census)
+void traceNonmovingHeapCensus_(uint16_t blk_size, const struct NonmovingAllocCensus *census)
{
- if (eventlog_enabled && TRACE_nonmoving_gc)
+#if defined(DEBUG)
+ if (RtsFlags.TraceFlags.tracing == TRACE_STDERR) {
+ /* nothing - no string representation for nonmoving GC events */
+ } else
+#endif
+ {
postNonmovingHeapCensus(blk_size, census);
+ }
}
-void traceNonmovingPrunedSegments(uint32_t pruned_segments, uint32_t free_segments)
+void traceNonmovingPrunedSegments_(uint32_t pruned_segments, uint32_t free_segments)
{
- if (eventlog_enabled && TRACE_nonmoving_gc)
+#if defined(DEBUG)
+ if (RtsFlags.TraceFlags.tracing == TRACE_STDERR) {
+ /* nothing - no string representation for nonmoving GC events */
+ } else
+#endif
+ {
postNonmovingPrunedSegments(pruned_segments, free_segments);
+ }
}
void traceThreadStatus_ (StgTSO *tso USED_IF_DEBUG)
=====================================
rts/Trace.h
=====================================
@@ -70,16 +70,35 @@ enum CapsetType { CapsetTypeCustom = CAPSET_TYPE_CUSTOM,
#define DEBUG_continuation RtsFlags.DebugFlags.continuation
#define DEBUG_iomanager RtsFlags.DebugFlags.iomanager
-// Event-enabled flags
-// These semantically booleans but we use a dense packing to minimize their
-// cache impact.
-extern uint8_t TRACE_sched;
-extern uint8_t TRACE_gc;
-extern uint8_t TRACE_nonmoving_gc;
-extern uint8_t TRACE_spark_sampled;
-extern uint8_t TRACE_spark_full;
-extern uint8_t TRACE_cap;
-/* extern uint8_t TRACE_user; */ // only used in Trace.c
+// These trace flags are shorthand for the members of the RuntimeTraceFlagCache
+// struct. Within the RTS, these should be treated as read-only variables.
+#define TRACE_sched ((const bool)RuntimeTraceFlagCache.scheduler)
+#define TRACE_gc ((const bool)RuntimeTraceFlagCache.gc)
+#define TRACE_nonmoving_gc ((const bool)RuntimeTraceFlagCache.nonmoving_gc)
+#define TRACE_spark_sampled ((const bool)RuntimeTraceFlagCache.spark_sampled)
+#define TRACE_spark_full ((const bool)RuntimeTraceFlagCache.spark_full)
+#define TRACE_user ((const bool)RuntimeTraceFlagCache.user)
+#define TRACE_cap ((const bool)RuntimeTraceFlagCache.cap)
+
+/*
+ * Runtime trace flags.
+ */
+typedef struct {
+ bool scheduler;
+ bool gc;
+ bool nonmoving_gc;
+ bool spark_sampled;
+ bool spark_full;
+ bool user;
+ bool cap;
+} RUNTIME_TRACE_FLAG_CACHE;
+
+/*
+ * These flags should be used to determine whether or not some value should
+ * be traced at runtime, rather than the values in RtsFlags. These flags can
+ * be modified at runtime using setTraceFlag in `rts/EventLogWriter.h`.
+ */
+extern RUNTIME_TRACE_FLAG_CACHE RuntimeTraceFlagCache;
// -----------------------------------------------------------------------------
// Posting events
@@ -136,6 +155,52 @@ void traceGcEvent_ (Capability *cap, EventTypeNum tag);
void traceGcEventAtT_ (Capability *cap, StgWord64 ts, EventTypeNum tag);
+/*
+ * Record a nonmoving GC event.
+ */
+#define traceConcMarkBegin() \
+ if (RTS_UNLIKELY(TRACE_nonmoving_gc)) { \
+ traceNonmovingGcEvent_(EVENT_CONC_MARK_BEGIN); \
+ }
+#define traceConcMarkEnd(marked_obj_count) \
+ if (RTS_UNLIKELY(TRACE_nonmoving_gc)) { \
+ traceConcMarkEnd_(marked_obj_count); \
+ }
+#define traceConcSyncBegin() \
+ if (RTS_UNLIKELY(TRACE_nonmoving_gc)) { \
+ traceNonmovingGcEvent_(EVENT_CONC_SYNC_BEGIN); \
+ }
+#define traceConcSyncEnd() \
+ if (RTS_UNLIKELY(TRACE_nonmoving_gc)) { \
+ traceNonmovingGcEvent_(EVENT_CONC_SYNC_END); \
+ }
+#define traceConcSweepBegin() \
+ if (RTS_UNLIKELY(TRACE_nonmoving_gc)) { \
+ traceNonmovingGcEvent_(EVENT_CONC_SWEEP_BEGIN); \
+ }
+#define traceConcSweepEnd() \
+ if (RTS_UNLIKELY(TRACE_nonmoving_gc)) { \
+ traceNonmovingGcEvent_(EVENT_CONC_SWEEP_END); \
+ }
+#define traceConcUpdRemSetFlush(cap) \
+ if (RTS_UNLIKELY(TRACE_nonmoving_gc)) { \
+ traceConcUpdRemSetFlush_(cap); \
+ }
+#define traceNonmovingHeapCensus(blk_size, census) \
+ if (RTS_UNLIKELY(TRACE_nonmoving_gc)) { \
+ traceNonmovingHeapCensus_(blk_size, census); \
+ }
+#define traceNonmovingPrunedSegments(pruned_segments, free_segments) \
+ if (RTS_UNLIKELY(TRACE_nonmoving_gc)) { \
+ traceNonmovingPrunedSegments_(pruned_segments, free_segments); \
+ }
+
+void traceNonmovingGcEvent_ (EventTypeNum tag);
+void traceConcMarkEnd_(StgWord32 marked_obj_count);
+void traceConcUpdRemSetFlush_(Capability *cap);
+void traceNonmovingHeapCensus_(uint16_t blk_size, const struct NonmovingAllocCensus *census);
+void traceNonmovingPrunedSegments_(uint32_t pruned_segments, uint32_t free_segments);
+
/*
* Record a heap event
*/
@@ -321,17 +386,6 @@ void traceProfSampleCostCentre(Capability *cap,
void traceProfBegin(void);
#endif /* PROFILING */
-void traceConcMarkBegin(void);
-void traceConcMarkEnd(StgWord32 marked_obj_count);
-void traceConcSyncBegin(void);
-void traceConcSyncEnd(void);
-void traceConcSweepBegin(void);
-void traceConcSweepEnd(void);
-void traceConcUpdRemSetFlush(Capability *cap);
-void traceNonmovingHeapCensus(uint16_t blk_size,
- const struct NonmovingAllocCensus *census);
-void traceNonmovingPrunedSegments(uint32_t pruned_segments, uint32_t free_segments);
-
void traceIPE(const InfoProvEnt *ipe);
void flushTrace(void);
@@ -384,6 +438,7 @@ void flushTrace(void);
#define traceConcSweepEnd() /* nothing */
#define traceConcUpdRemSetFlush(cap) /* nothing */
#define traceNonmovingHeapCensus(blk_size, census) /* nothing */
+#define traceNonmovingPrunedSegments(pruned_segments, free_segments) /* nothing */
#define flushTrace() /* nothing */
=====================================
rts/include/rts/EventLogWriter.h
=====================================
@@ -78,3 +78,34 @@ void endEventLogging(void);
* Flush the eventlog. cap can be NULL if one is not held.
*/
void flushEventLog(Capability **cap);
+
+/*
+ * An enumeration for the runtime trace flags.
+ */
+typedef enum {
+ TRACE_SCHEDULER,
+ TRACE_GC,
+ TRACE_NONMOVING_GC,
+ TRACE_SPARK_SAMPLED,
+ TRACE_SPARK_FULL,
+ TRACE_USER,
+ TRACE_CAP,
+} RUNTIME_TRACE_FLAG;
+
+/*
+ * Get the value of the given runtime trace flag.
+ *
+ * Warning: The trace flag cache is not thread-safe. After initialisation, the
+ * RTS never writes to these values, but concurrently using getTraceFlag and
+ * setTraceFlag for the same flag is a race condition.
+ */
+bool getTraceFlag(RUNTIME_TRACE_FLAG flag);
+
+/*
+ * Set the value of the given runtime trace flag.
+ *
+ * Warning: The trace flag cache is not thread-safe. After initialisation, the
+ * RTS never writes to these values. However, inconsistent reads may lead to
+ * incorrect tracing for a short time after setting a trace flag.
+ */
+void setTraceFlag(RUNTIME_TRACE_FLAG flag, bool value);
=====================================
rts/linker/Elf.c
=====================================
@@ -76,18 +76,6 @@
*
* See bug #781
* See thread http://www.haskell.org/pipermail/cvs-ghc/2007-September/038458.html
- *
- * Naming Scheme for Symbol Macros
- *
- * SymI_*: symbol is internal to the RTS. It resides in an object
- * file/library that is statically.
- * SymE_*: symbol is external to the RTS library. It might be linked
- * dynamically.
- *
- * Sym*_HasProto : the symbol prototype is imported in an include file
- * or defined explicitly
- * Sym*_NeedsProto: the symbol is undefined and we add a dummy
- * default proto extern void sym(void);
*/
#define X86_64_ELF_NONPIC_HACK (!RtsFlags.MiscFlags.linkerAlwaysPic)
=====================================
rts/posix/FdWakeup.c
=====================================
@@ -0,0 +1,141 @@
+/* -----------------------------------------------------------------------------
+ *
+ * (c) The GHC Team 2025
+ *
+ * Utilities for a simple fd-based cross-thread wakeup mechanism.
+ *
+ * This is used to provide a mechanism to wake a thread when it is blocked
+ * waiting on fds and timeouts. The mechanism works by including the read end
+ * fd into the set of fds the thread waits on, and when a wake up is needed,
+ * the write end fd is used.
+ *
+ * This is implemented using either eventfd() or pipe().
+ *
+ * Linux 2.6.22+ and FreeBSD 13+ support eventfd. It is a single fd with a
+ * 64bit counter. It uses fewer resources than a pipe (less memory and one
+ * rather than two fds), and is a tad faster (on the order of 5-10%). Using
+ * write() adds to the counter, while read() reads and resets it. Thus
+ * multiple writes are combined automatically into a single corresponding
+ * read.
+ *
+ * Otherwise we use a classic unix pipe.
+ *
+ * In both implementations, multiple sendFdWakeup notifcations (without
+ * interleaved collectFdWakeup) are combined to a single notification. This
+ * is automatic given the semantics of eventfd, while for pipe we implement
+ * it explicitly by draining the pipe in collectFdWakeup.
+ *
+ * -------------------------------------------------------------------------*/
+
+#include "rts/PosixSource.h"
+#include "Rts.h"
+
+#include "FdWakeup.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#ifdef HAVE_SYS_EVENTFD_H
+#include <sys/eventfd.h>
+#endif
+
+#if !defined(HAVE_EVENTFD) \
+ || (defined(HAVE_EVENTFD) && !(defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK)))
+static void fcntl_CLOEXEC_NONBLOCK(int fd)
+{
+ int res1 = fcntl(fd, F_SETFD, FD_CLOEXEC);
+ int res2 = fcntl(fd, F_SETFL, O_NONBLOCK);
+ if (RTS_UNLIKELY(res1 < 0 || res2 < 0)) {
+ sysErrorBelch("newFdWakeup fcntl()");
+ stg_exit(EXIT_FAILURE);
+ }
+}
+#endif
+
+void newFdWakeup(int *wakeup_fd_r, int *wakeup_fd_w)
+{
+#if defined(HAVE_EVENTFD)
+ int wakeup_fd;
+#if defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK)
+ wakeup_fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
+#else
+ wakeup_fd = eventfd(0, 0);
+ if (wakeup_fd >= 0) fcntl_CLOEXEC_NONBLOCK(wakeup_fd);
+#endif
+ if (RTS_UNLIKELY(wakeup_fd < 0)) {
+ sysErrorBelch("newFdWakeup eventfd()");
+ stg_exit(EXIT_FAILURE);
+ }
+ /* eventfd uses the same fd for each end */
+ *wakeup_fd_r = wakeup_fd;
+ *wakeup_fd_w = wakeup_fd;
+#else
+ int pipefd[2];
+ int res;
+ res = pipe(pipefd);
+ if (RTS_UNLIKELY(res < 0)) {
+ sysErrorBelch("newFdWakeup pipe");
+ stg_exit(EXIT_FAILURE);
+ }
+ fcntl_CLOEXEC_NONBLOCK(pipefd[0]);
+ fcntl_CLOEXEC_NONBLOCK(pipefd[1]);
+ *wakeup_fd_r = pipefd[0]; /* read end */
+ *wakeup_fd_w = pipefd[1]; /* write end */
+#endif
+}
+
+void closeFdWakeup(int wakeup_fd_r, int wakeup_fd_w)
+{
+#if defined(HAVE_EVENTFD)
+ ASSERT(wakeup_fd_r == wakeup_fd_w);
+ close(wakeup_fd_r);
+#else
+ ASSERT(wakeup_fd_r != wakeup_fd_w);
+ close(wakeup_fd_r);
+ close(wakeup_fd_w);
+#endif
+}
+
+/* This is safe to use from a signal handler. Using write() to a pipe
+ * or eventfd is fine. */
+void sendFdWakeup(int wakeup_fd_w)
+{
+ int res;
+#if defined(HAVE_EVENTFD)
+ uint64_t val = 1;
+ res = write(wakeup_fd_w, &val, 8);
+#else
+ unsigned char buf = 1;
+ res = write(wakeup_fd_w, &buf, 1);
+#endif
+ if (RTS_UNLIKELY(res < 0)) {
+ /* Unlikely the pipe buffer will fill, but it would not be an error. */
+ if (errno == EAGAIN) return;
+ sysErrorBelch("sendFdWakeup write");
+ stg_exit(EXIT_FAILURE);
+ }
+}
+
+void collectFdWakeup(int wakeup_fd_r)
+{
+ int res;
+#if defined(HAVE_EVENTFD)
+ uint64_t buf;
+ /* eventfd combines events into one counter, so a single read is enough */
+ res = read(wakeup_fd_r, &buf, 8);
+#else
+ /* Drain the pipe buffer. Multiple wakeup notifications could
+ * have been sent before we have a chance to collect them.
+ */
+ uint64_t buf;
+ do {
+ res = read(wakeup_fd_r, &buf, 8);
+ } while (res == 8);
+#endif
+ if (RTS_UNLIKELY(res < 0)) {
+ /* After the first pipe read, it could block */
+ if (errno == EAGAIN) return;
+ sysErrorBelch("collectFdWakeup read");
+ stg_exit(EXIT_FAILURE);
+ }
+}
=====================================
rts/posix/FdWakeup.h
=====================================
@@ -0,0 +1,40 @@
+/* -----------------------------------------------------------------------------
+ *
+ * (c) The GHC Team 2025
+ *
+ * Utilities for a simple fd-based cross-thread wakeup mechanism.
+ *
+ * It provides a mechanism for a thread that block on fds to add a simple
+ * wakeup/notification feature.
+ *
+ * Start with newFdWakeup, and pass the fd_r to the thread that needs the
+ * wakeup feature. The thread that needs to be woken should include the fd_r
+ * into the set of fds that the thread waits on (e.g. using poll or similar).
+ * If this fd becomes ready for read, the thread must call collectFdWakeup,
+ * and when a wake up is needed, the write end fd is used. In any other thread
+ * (or in a signal handler), call sendFdWakeup(fd_w) to (asynchronously) cause
+ * the wakeup.
+ *
+ * There is no message payload. Multiple wakeups may be combined (if they're
+ * sent multiple times before the notified thread can wake and call
+ * collectFdWakeup).
+ *
+ * The implementation uses pipe() or eventfd() on supported OSs.
+ *
+ * Prototypes for functions in FdWakeup.c
+ *
+ * -------------------------------------------------------------------------*/
+
+#pragma once
+
+#include "BeginPrivate.h"
+
+void newFdWakeup(int *fd_r, int *fd_w);
+void closeFdWakeup(int fd_r, int fd_w);
+
+/* This is safe to use from a signal handler */
+void sendFdWakeup(int fd_w);
+void collectFdWakeup(int fd_r);
+
+#include "EndPrivate.h"
+
=====================================
rts/posix/Ticker.c
=====================================
@@ -1,19 +1,53 @@
/* -----------------------------------------------------------------------------
*
- * (c) The GHC Team, 1995-2007
+ * (c) The GHC Team, 1995-2026
*
- * Posix implementation(s) of the interval timer for profiling and pre-emptive
- * scheduling.
+ * The posix implementation of the interval timer, used for pre-emptive
+ * scheduling of Haskell threads, and for sample based profiling.
+ *
+ * This file defines the "ticker": the platform-specific service to install and
+ * run the timer. See rts/Timer.c for the platform-dependent view of interval
+ * timing.
*
* ---------------------------------------------------------------------------*/
-/* The interval timer is used for profiling and for context switching.
- * This file defines the platform-specific services to install and run the
- * timers, and we call this the ticker. See rts/Timer.c for the
- * platform-dependent view of interval timing.
+/* This implementation uses a posix thread which repeatedly blocks on a timeout
+ * using either the ppoll() or select() API. This lets it also block on a file
+ * descriptor for early wakeup.
+ *
+ * The design uses a simple relative time delay with no catchup. That is, time
+ * spent by the ticker thread itself (e.g. flushing eventlog buffers) is not
+ * accounted for, and the next tick is delayed by that much (modulo wakeup
+ * jitter). This is probably the right thing to do: generally in realtime
+ * systems one does not want to try to catch up when behind, since that tends
+ * towards oversubscribing resources. Graceful degredation is usually
+ * preferable.
+ *
+ * Experimental results (on Linux 6.18 on x86-64) to measure the typical
+ * difference between the requested wakeup time and actual wakeup time for
+ * different delay intervals:
+ *
+ * interval typical actual wakeup time after due time
+ * 10000us 340 -- 400us (this is the default interval)
+ * 1000us 55 -- 100us
+ * 100us 55us
+ * 10us 55us
+ *
+ * While there's quite a bit of variance to these numbers, the results do not
+ * vary significantly between using select, ppoll or nanosleep.
+ *
+ * On Linux at least, for longer delays the kernel allows itself lower wakeup
+ * accuracy (which allows it to save power by coalescing multiple wakeups).
+ * Similarly, the reason for 55us on the low end is that the default thread
+ * timer slack on Linux is 50us, and context switch time accounts for the
+ * remainder.
+ *
+ * In conclusion, on Linux at least, the accuracy is fine, both for the
+ * default interval (10ms, 10000us) and for shorter intervals used during
+ * profiling.
*
* Historically we had ticker implementations using signals. This was always a
- * rather shakey thing to do but we had few alternatives.
+ * rather shakey thing to do but we originally had few alternatives.
* - One problem with using signals is that there are severe limits on what
* code can be called from signal handlers. In particular it's not possible
* to take locks in a signal handler contex. This was enough for contex
@@ -23,17 +57,245 @@
* calls (#10840) or can be overwritten by user code.
*/
-/* Select a ticker implementation to use:
- *
- * On modern Linux, FreeBSD and NetBSD we can use timerfd_create and a thread
- * that waits on it using poll. Linux has had timerfd since version 2.6.25.
- * NetBSD has had timerfd since version 10, and FreeBSD since version 15.
- *
- * For older version of linux/bsd without timerfd, and for all other posix
- * platforms, we use the implementation using posix pthreads and nanosleep().
+#include "rts/PosixSource.h"
+#include "Rts.h"
+
+#include "Ticker.h"
+#include "RtsUtils.h"
+#include "Proftimer.h"
+#include "Schedule.h"
+#include "posix/Clock.h"
+#include "posix/FdWakeup.h"
+
+#if defined(HAVE_DECL_PPOLL) && HAVE_DECL_PPOLL == 1
+/* We prefer the ppoll() function if available since it allows sanely waiting
+ * on a single fd with precise timeouts (nanosecond precision). It is not in
+ * the posix standard however and some platforms (notably glibc and freebsd)
+ * need special CPP defines to make it available:
+ */
+#define _GNU_SOURCE 1
+#define __BSD_VISIBLE 1
+#include <signal.h>
+#include <poll.h>
+#else
+/* Otherwise we use the classic select(), which does have microsecond
+ * precision, but requires we build three whole 1024 bit (128 byte) fd sets
+ * just to wait on one fd.
*/
-#if defined(HAVE_SYS_TIMERFD_H)
-#include "ticker/TimerFd.c"
+#include <sys/select.h>
+#endif
+
+#include <time.h>
+#if HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#if defined(HAVE_SIGNAL_H)
+# include <signal.h>
+#endif
+
+#include <string.h>
+
+#include <pthread.h>
+#if defined(HAVE_PTHREAD_NP_H)
+#include <pthread_np.h>
+#endif
+#include <unistd.h>
+#include <fcntl.h>
+
+static Time itimer_interval = DEFAULT_TICK_INTERVAL;
+
+// Should we be firing ticks?
+// Writers to this must hold the mutex below.
+static bool stopped = false;
+
+// should the ticker thread exit?
+// This can be set without holding the mutex.
+static bool exited = true;
+
+// Signaled when we want to (re)start the timer
+static Condition start_cond;
+static Mutex mutex;
+static OSThreadId thread;
+
+// fds for interrupting the ticker
+static int interruptfd_r = -1, interruptfd_w = -1;
+
+static void *itimer_thread_func(void *_handle_tick)
+{
+ TickProc handle_tick = _handle_tick;
+
+#if defined(HAVE_DECL_PPOLL) && HAVE_DECL_PPOLL == 1
+ struct pollfd pollfds[1];
+
+ pollfds[0].fd = interruptfd_r;
+ pollfds[0].events = POLLIN;
+
+ struct timespec ts = { .tv_sec = TimeToSeconds(itimer_interval)
+ , .tv_nsec = TimeToNS(itimer_interval) % 1000000000
+ };
#else
-#include "ticker/Pthread.c"
+ fd_set selectfds;
+ FD_ZERO(&selectfds);
+ FD_SET(interruptfd_r, &selectfds);
+
+ struct timeval tv = { .tv_sec = TimeToSeconds(itimer_interval)
+ /* convert remainder time in nanoseconds
+ to microseconds, rounding up: */
+ , .tv_usec = ((TimeToNS(itimer_interval) % 1000000000)
+ + 999) / 1000
+ };
+#endif
+
+ // Relaxed is sufficient: If we don't see that exited was set in one iteration we will
+ // see it next time.
+ while (!RELAXED_LOAD_ALWAYS(&exited)) {
+
+#if defined(HAVE_DECL_PPOLL) && HAVE_DECL_PPOLL == 1
+ int nfds = 1;
+ int nready = ppoll(pollfds, nfds, &ts, NULL);
+#else
+ struct timeval tv_tmp = tv; // copy since select may change this value.
+ int nfds = interruptfd_r+1;
+ int nready = select(nfds, &selectfds, NULL, NULL, &tv_tmp);
+#endif
+ // In either case (ppoll or select), the result nready is the number
+ // of fds that are ready.
+ if (RTS_LIKELY(nready == 0)) {
+ // Timer expired, not interrupted, continue.
+ } else if (nready > 0) {
+ // We only monitor one fd (the interruptfd_r), so we know
+ // it is that fd that is ready without any further checks.
+ collectFdWakeup(interruptfd_r);
+ // No further action needed, continue on to handling the final tick
+ // and then stop.
+
+ // Note that we rely on sendFdWakeup and select/poll to provide the
+ // happens-before relation. So if 'exited' was set before calling
+ // sendFdWakeup, then we should be able to reliably read it after.
+ // And thus reading 'exited' in the while loop guard is ok.
+ } else {
+ // While the RTS attempts to mask signals, some foreign libraries
+ // that rely on signal delivery may unmask them. Consequently we
+ // may see EINTR. See #24610.
+ if (errno != EINTR) {
+ sysErrorBelch("Ticker: poll failed: %s", strerror(errno));
+ }
+ }
+
+ // first try a cheap test
+ if (RELAXED_LOAD_ALWAYS(&stopped)) {
+ OS_ACQUIRE_LOCK(&mutex);
+ // should we really stop?
+ if (stopped) {
+ waitCondition(&start_cond, &mutex);
+ }
+ OS_RELEASE_LOCK(&mutex);
+ } else {
+ handle_tick(0);
+ }
+ }
+
+ return NULL;
+}
+
+void
+initTicker (Time interval, TickProc handle_tick)
+{
+ itimer_interval = interval;
+ stopped = true;
+ exited = false;
+#if defined(HAVE_SIGNAL_H)
+ sigset_t mask, omask;
+ int sigret;
+#endif
+ int ret;
+
+ initCondition(&start_cond);
+ initMutex(&mutex);
+
+ /* Open the interrupt fd synchronously.
+ *
+ * We used to do it in itimer_thread_func (i.e. in the timer thread) but it
+ * meant that some user code could run before it and get confused by the
+ * allocation of the timerfd.
+ *
+ * See hClose002 which unsafely closes a file descriptor twice expecting an
+ * exception the second time: it sometimes failed when the second call to
+ * "close" closed our own timerfd which inadvertently reused the same file
+ * descriptor closed by the first call! (see #20618)
+ */
+
+ if (interruptfd_r != -1) {
+ // don't leak the old file descriptors after a fork (#25280)
+ closeFdWakeup(interruptfd_r, interruptfd_w);
+ }
+ newFdWakeup(&interruptfd_r, &interruptfd_w);
+
+ /*
+ * Create the thread with all blockable signals blocked, leaving signal
+ * handling to the main and/or other threads. This is especially useful in
+ * the non-threaded runtime, where applications might expect sigprocmask(2)
+ * to effectively block signals.
+ */
+#if defined(HAVE_SIGNAL_H)
+ sigfillset(&mask);
+ sigret = pthread_sigmask(SIG_SETMASK, &mask, &omask);
+#endif
+ ret = createAttachedOSThread(&thread, "ghc_ticker", itimer_thread_func, (void*)handle_tick);
+#if defined(HAVE_SIGNAL_H)
+ if (sigret == 0)
+ pthread_sigmask(SIG_SETMASK, &omask, NULL);
#endif
+
+ if (ret != 0) {
+ barf("Ticker: Failed to spawn thread: %s", strerror(errno));
+ }
+}
+
+void
+startTicker(void)
+{
+ OS_ACQUIRE_LOCK(&mutex);
+ RELAXED_STORE(&stopped, false);
+ signalCondition(&start_cond);
+ OS_RELEASE_LOCK(&mutex);
+}
+
+/* There may be at most one additional tick fired after a call to this */
+void
+stopTicker(void)
+{
+ OS_ACQUIRE_LOCK(&mutex);
+ RELAXED_STORE(&stopped, true);
+ OS_RELEASE_LOCK(&mutex);
+}
+
+/* There may be at most one additional tick fired after a call to this */
+void
+exitTicker (bool wait)
+{
+ ASSERT(!SEQ_CST_LOAD(&exited));
+ SEQ_CST_STORE(&exited, true);
+ // ensure that ticker wakes up if stopped
+ startTicker();
+ sendFdWakeup(interruptfd_w);
+
+ // wait for ticker to terminate if necessary
+ if (wait) {
+ if (pthread_join(thread, NULL)) {
+ sysErrorBelch("Ticker: Failed to join: %s", strerror(errno));
+ }
+ closeFdWakeup(interruptfd_r, interruptfd_w);
+ closeMutex(&mutex);
+ closeCondition(&start_cond);
+ } else {
+ pthread_detach(thread);
+ }
+}
+
+int
+rtsTimerSignal(void)
+{
+ return SIGALRM;
+}
=====================================
rts/posix/ticker/Pthread.c deleted
=====================================
@@ -1,195 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * (c) The GHC Team, 1995-2007
- *
- * Interval timer for profiling and pre-emptive scheduling.
- *
- * ---------------------------------------------------------------------------*/
-
-/*
- * We use a realtime timer by default. I found this much more
- * reliable than a CPU timer:
- *
- * Experiments with different frequencies: using
- * CLOCK_REALTIME/CLOCK_MONOTONIC on Linux 2.6.32,
- * 1000us has <1% impact on runtime
- * 100us has ~2% impact on runtime
- * 10us has ~40% impact on runtime
- *
- * using CLOCK_PROCESS_CPUTIME_ID on Linux 2.6.32,
- * I cannot get it to tick faster than 10ms (10000us)
- * which isn't great for profiling.
- *
- * In the threaded RTS, we can't tick in CPU time because the thread
- * which has the virtual timer might be idle, so the tick would never
- * fire. Therefore we used to tick in realtime in the threaded RTS and
- * in CPU time otherwise, but now we always tick in realtime, for
- * several reasons:
- *
- * - resolution (see above)
- * - consistency (-threaded is the same as normal)
- * - more consistency: Windows only has a realtime timer
- *
- * Note we want to use CLOCK_MONOTONIC rather than CLOCK_REALTIME,
- * because the latter may jump around (NTP adjustments, leap seconds
- * etc.).
- */
-
-#include "rts/PosixSource.h"
-#include "Rts.h"
-
-#include "Ticker.h"
-#include "RtsUtils.h"
-#include "Proftimer.h"
-#include "Schedule.h"
-#include "posix/Clock.h"
-#include <poll.h>
-
-#include <time.h>
-#if HAVE_SYS_TIME_H
-# include <sys/time.h>
-#endif
-
-#if defined(HAVE_SIGNAL_H)
-# include <signal.h>
-#endif
-
-#include <string.h>
-
-#include <pthread.h>
-#if defined(HAVE_PTHREAD_NP_H)
-#include <pthread_np.h>
-#endif
-#include <unistd.h>
-#include <fcntl.h>
-
-/*
- * TFD_CLOEXEC has been added in Linux 2.6.26.
- * If it is not available, we use fcntl(F_SETFD).
- */
-#if !defined(TFD_CLOEXEC)
-#define TFD_CLOEXEC 0
-#endif
-
-static Time itimer_interval = DEFAULT_TICK_INTERVAL;
-
-// Should we be firing ticks?
-// Writers to this must hold the mutex below.
-static bool stopped = false;
-
-// should the ticker thread exit?
-// This can be set without holding the mutex.
-static bool exited = true;
-
-// Signaled when we want to (re)start the timer
-static Condition start_cond;
-static Mutex mutex;
-static OSThreadId thread;
-
-static void *itimer_thread_func(void *_handle_tick)
-{
- TickProc handle_tick = _handle_tick;
-
- // Relaxed is sufficient: If we don't see that exited was set in one iteration we will
- // see it next time.
- while (!RELAXED_LOAD_ALWAYS(&exited)) {
- if (rtsSleep(itimer_interval) != 0) {
- sysErrorBelch("Ticker: sleep failed: %s", strerror(errno));
- }
-
- // first try a cheap test
- if (RELAXED_LOAD_ALWAYS(&stopped)) {
- OS_ACQUIRE_LOCK(&mutex);
- // should we really stop?
- if (stopped) {
- waitCondition(&start_cond, &mutex);
- }
- OS_RELEASE_LOCK(&mutex);
- } else {
- handle_tick(0);
- }
- }
-
- return NULL;
-}
-
-void
-initTicker (Time interval, TickProc handle_tick)
-{
- itimer_interval = interval;
- stopped = true;
- exited = false;
-#if defined(HAVE_SIGNAL_H)
- sigset_t mask, omask;
- int sigret;
-#endif
- int ret;
-
- initCondition(&start_cond);
- initMutex(&mutex);
-
- /*
- * Create the thread with all blockable signals blocked, leaving signal
- * handling to the main and/or other threads. This is especially useful in
- * the non-threaded runtime, where applications might expect sigprocmask(2)
- * to effectively block signals.
- */
-#if defined(HAVE_SIGNAL_H)
- sigfillset(&mask);
- sigret = pthread_sigmask(SIG_SETMASK, &mask, &omask);
-#endif
- ret = createAttachedOSThread(&thread, "ghc_ticker", itimer_thread_func, (void*)handle_tick);
-#if defined(HAVE_SIGNAL_H)
- if (sigret == 0)
- pthread_sigmask(SIG_SETMASK, &omask, NULL);
-#endif
-
- if (ret != 0) {
- barf("Ticker: Failed to spawn thread: %s", strerror(errno));
- }
-}
-
-void
-startTicker(void)
-{
- OS_ACQUIRE_LOCK(&mutex);
- RELAXED_STORE(&stopped, false);
- signalCondition(&start_cond);
- OS_RELEASE_LOCK(&mutex);
-}
-
-/* There may be at most one additional tick fired after a call to this */
-void
-stopTicker(void)
-{
- OS_ACQUIRE_LOCK(&mutex);
- RELAXED_STORE(&stopped, true);
- OS_RELEASE_LOCK(&mutex);
-}
-
-/* There may be at most one additional tick fired after a call to this */
-void
-exitTicker (bool wait)
-{
- ASSERT(!SEQ_CST_LOAD(&exited));
- SEQ_CST_STORE(&exited, true);
- // ensure that ticker wakes up if stopped
- startTicker();
-
- // wait for ticker to terminate if necessary
- if (wait) {
- if (pthread_join(thread, NULL)) {
- sysErrorBelch("Ticker: Failed to join: %s", strerror(errno));
- }
- closeMutex(&mutex);
- closeCondition(&start_cond);
- } else {
- pthread_detach(thread);
- }
-}
-
-int
-rtsTimerSignal(void)
-{
- return SIGALRM;
-}
=====================================
rts/posix/ticker/TimerFd.c deleted
=====================================
@@ -1,291 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * (c) The GHC Team, 1995-2023
- *
- * Interval timer for profiling and pre-emptive scheduling.
- *
- * ---------------------------------------------------------------------------*/
-
-/*
- * We use a realtime timer by default. I found this much more
- * reliable than a CPU timer:
- *
- * Experiments with different frequencies: using
- * CLOCK_REALTIME/CLOCK_MONOTONIC on Linux 2.6.32,
- * 1000us has <1% impact on runtime
- * 100us has ~2% impact on runtime
- * 10us has ~40% impact on runtime
- *
- * using CLOCK_PROCESS_CPUTIME_ID on Linux 2.6.32,
- * I cannot get it to tick faster than 10ms (10000us)
- * which isn't great for profiling.
- *
- * In the threaded RTS, we can't tick in CPU time because the thread
- * which has the virtual timer might be idle, so the tick would never
- * fire. Therefore we used to tick in realtime in the threaded RTS and
- * in CPU time otherwise, but now we always tick in realtime, for
- * several reasons:
- *
- * - resolution (see above)
- * - consistency (-threaded is the same as normal)
- * - more consistency: Windows only has a realtime timer
- *
- * Note we want to use CLOCK_MONOTONIC rather than CLOCK_REALTIME,
- * because the latter may jump around (NTP adjustments, leap seconds
- * etc.).
- */
-
-#include "rts/PosixSource.h"
-#include "Rts.h"
-
-#include "Ticker.h"
-#include "RtsUtils.h"
-#include "Proftimer.h"
-#include "Schedule.h"
-#include "posix/Clock.h"
-#include <poll.h>
-
-#include <time.h>
-#if HAVE_SYS_TIME_H
-# include <sys/time.h>
-#endif
-
-#if defined(HAVE_SIGNAL_H)
-# include <signal.h>
-#endif
-
-#include <string.h>
-
-#include <pthread.h>
-#if defined(HAVE_PTHREAD_NP_H)
-#include <pthread_np.h>
-#endif
-#include <unistd.h>
-#include <fcntl.h>
-
-#include <sys/timerfd.h>
-
-
-/*
- * TFD_CLOEXEC has been added in Linux 2.6.26.
- * If it is not available, we use fcntl(F_SETFD).
- */
-#if !defined(TFD_CLOEXEC)
-#define TFD_CLOEXEC 0
-#endif
-
-static Time itimer_interval = DEFAULT_TICK_INTERVAL;
-
-// Should we be firing ticks?
-// Writers to this must hold the mutex below.
-static bool stopped = false;
-
-// should the ticker thread exit?
-// This can be set without holding the mutex.
-static bool exited = true;
-
-// Signaled when we want to (re)start the timer
-static Condition start_cond;
-static Mutex mutex;
-static OSThreadId thread;
-
-// file descriptor for the timer (Linux only)
-static int timerfd = -1;
-
-// pipe for signaling exit
-static int pipefds[2];
-
-static void *itimer_thread_func(void *_handle_tick)
-{
- TickProc handle_tick = _handle_tick;
- uint64_t nticks;
- ssize_t r = 0;
- struct pollfd pollfds[2];
-
- pollfds[0].fd = pipefds[0];
- pollfds[0].events = POLLIN;
- pollfds[1].fd = timerfd;
- pollfds[1].events = POLLIN;
-
- // Relaxed is sufficient: If we don't see that exited was set in one iteration we will
- // see it next time.
- while (!RELAXED_LOAD_ALWAYS(&exited)) {
- if (poll(pollfds, 2, -1) == -1) {
- // While the RTS attempts to mask signals, some foreign libraries
- // may rely on signal delivery may unmask them. Consequently we may
- // see EINTR. See #24610.
- if (errno != EINTR) {
- sysErrorBelch("Ticker: poll failed: %s", strerror(errno));
- }
- }
-
- // We check the pipe first, even though the timerfd may also have triggered.
- if (pollfds[0].revents & POLLIN) {
- // the pipe is ready for reading, the only possible reason is that we're exiting
- exited = true; // set this again to make sure even RELAXED_LOAD will read the proper value
- // no further action needed, skip ahead to handling the final tick and then stopping
- }
- else if (pollfds[1].revents & POLLIN) { // the timerfd is ready for reading
- r = read(timerfd, &nticks, sizeof(nticks)); // this should never block now
-
- if ((r == 0) && (errno == 0)) {
- /* r == 0 is expected only for non-blocking fd (in which case
- * errno should be EAGAIN) but we use a blocking fd.
- *
- * Due to a kernel bug (cf https://lkml.org/lkml/2019/8/16/335)
- * on some platforms we could see r == 0 and errno == 0.
- */
- IF_DEBUG(scheduler, debugBelch("read(timerfd) returned 0 with errno=0. This is a known kernel bug. We just ignore it."));
- }
- else if (r != sizeof(nticks) && errno != EINTR) {
- barf("Ticker: read(timerfd) failed with %s and returned %zd", strerror(errno), r);
- }
- }
-
- // first try a cheap test
- if (RELAXED_LOAD_ALWAYS(&stopped)) {
- OS_ACQUIRE_LOCK(&mutex);
- // should we really stop?
- if (stopped) {
- waitCondition(&start_cond, &mutex);
- }
- OS_RELEASE_LOCK(&mutex);
- } else {
- handle_tick(0);
- }
- }
-
- close(timerfd);
- return NULL;
-}
-
-void
-initTicker (Time interval, TickProc handle_tick)
-{
- itimer_interval = interval;
- stopped = true;
- exited = false;
-#if defined(HAVE_SIGNAL_H)
- sigset_t mask, omask;
- int sigret;
-#endif
- int ret;
-
- initCondition(&start_cond);
- initMutex(&mutex);
-
- /* Open the file descriptor for the timer synchronously.
- *
- * We used to do it in itimer_thread_func (i.e. in the timer thread) but it
- * meant that some user code could run before it and get confused by the
- * allocation of the timerfd.
- *
- * See hClose002 which unsafely closes a file descriptor twice expecting an
- * exception the second time: it sometimes failed when the second call to
- * "close" closed our own timerfd which inadvertently reused the same file
- * descriptor closed by the first call! (see #20618)
- */
- struct itimerspec it;
- it.it_value.tv_sec = TimeToSeconds(itimer_interval);
- it.it_value.tv_nsec = TimeToNS(itimer_interval) % 1000000000;
- it.it_interval = it.it_value;
-
- if (timerfd != -1) {
- // don't leak the old file descriptors after a fork (#25280)
- close(timerfd);
- close(pipefds[0]);
- close(pipefds[1]);
- timerfd = -1;
- }
-
- timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
- if (timerfd == -1) {
- barf("timerfd_create: %s", strerror(errno));
- }
- if (!TFD_CLOEXEC) {
- fcntl(timerfd, F_SETFD, FD_CLOEXEC);
- }
- if (timerfd_settime(timerfd, 0, &it, NULL)) {
- barf("timerfd_settime: %s", strerror(errno));
- }
-
- if (pipe(pipefds) < 0) {
- barf("pipe: %s", strerror(errno));
- }
-
- /*
- * Create the thread with all blockable signals blocked, leaving signal
- * handling to the main and/or other threads. This is especially useful in
- * the non-threaded runtime, where applications might expect sigprocmask(2)
- * to effectively block signals.
- */
-#if defined(HAVE_SIGNAL_H)
- sigfillset(&mask);
- sigret = pthread_sigmask(SIG_SETMASK, &mask, &omask);
-#endif
- ret = createAttachedOSThread(&thread, "ghc_ticker", itimer_thread_func, (void*)handle_tick);
-#if defined(HAVE_SIGNAL_H)
- if (sigret == 0)
- pthread_sigmask(SIG_SETMASK, &omask, NULL);
-#endif
-
- if (ret != 0) {
- barf("Ticker: Failed to spawn thread: %s", strerror(errno));
- }
-}
-
-void
-startTicker(void)
-{
- OS_ACQUIRE_LOCK(&mutex);
- RELAXED_STORE(&stopped, false);
- signalCondition(&start_cond);
- OS_RELEASE_LOCK(&mutex);
-}
-
-/* There may be at most one additional tick fired after a call to this */
-void
-stopTicker(void)
-{
- OS_ACQUIRE_LOCK(&mutex);
- RELAXED_STORE(&stopped, true);
- OS_RELEASE_LOCK(&mutex);
-}
-
-/* There may be at most one additional tick fired after a call to this */
-void
-exitTicker (bool wait)
-{
- ASSERT(!SEQ_CST_LOAD(&exited));
- SEQ_CST_STORE(&exited, true);
- // ensure that ticker wakes up if stopped
- startTicker();
-
- // wait for ticker to terminate if necessary
- if (wait) {
- // write anything to the pipe to trigger poll() in the ticker thread
- if (write(pipefds[1], "stop", 5) < 0) {
- sysErrorBelch("Ticker: Failed to write to pipe: %s", strerror(errno));
- }
-
- if (pthread_join(thread, NULL)) {
- sysErrorBelch("Ticker: Failed to join: %s", strerror(errno));
- }
-
- // These need to happen AFTER the ticker thread has finished to prevent a race condition
- // where the ticker thread closes the read end of the pipe before we're done writing to it.
- close(pipefds[0]);
- close(pipefds[1]);
-
- closeMutex(&mutex);
- closeCondition(&start_cond);
- } else {
- pthread_detach(thread);
- }
-}
-
-int
-rtsTimerSignal(void)
-{
- return SIGALRM;
-}
=====================================
rts/rts.cabal
=====================================
@@ -582,11 +582,9 @@ library
posix/Ticker.c
posix/OSMem.c
posix/OSThreads.c
+ posix/FdWakeup.c
posix/Poll.c
posix/Select.c
posix/Signals.c
posix/Timeout.c
posix/TTY.c
- -- ticker/*.c
- -- We don't want to compile posix/ticker/*.c, these will be #included
- -- from Ticker.c
=====================================
rts/sm/NonMoving.c
=====================================
@@ -1339,7 +1339,7 @@ concurrent_marking:
nonmovingPrintAllocatorCensus(!concurrent);
#endif
#if defined(TRACING)
- if (RtsFlags.TraceFlags.nonmoving_gc)
+ if (RTS_UNLIKELY(TRACE_nonmoving_gc))
nonmovingTraceAllocatorCensus();
#endif
=====================================
testsuite/tests/interface-stability/base-exports.stdout-ws-32 deleted
=====================================
The diff for this file was not included because it is too large.
=====================================
testsuite/tests/th/T27022.hs
=====================================
@@ -0,0 +1,8 @@
+{-# LANGUAGE TemplateHaskell #-}
+-- | This tests the behaviour of TH's recover method.
+-- It should behave the same in the internal and external interperter.
+-- In the past, they have diverged, and the external interpreter would roll back the state of putQ/getQ whereas the internal interpreter would not.
+module Main where
+
+import Language.Haskell.TH.Syntax
+main = print $(putQ "0" >> recover (pure ()) (putQ "42" >> fail "oops") >> getQ @String >>= lift )
=====================================
testsuite/tests/th/T27022.stdout
=====================================
@@ -0,0 +1 @@
+Just "42"
=====================================
testsuite/tests/th/all.T
=====================================
@@ -650,3 +650,4 @@ test('GadtConSigs_th_dump1', normal, compile, ['-v0 -ddump-splices -dsuppress-un
test('T26099', normal, compile_fail, [''])
test('T8306_th', only_ways(['ghci']), ghci_script, ['T8306_th.script'])
test('T26862_th', only_ways(['ghci']), ghci_script, ['T26862_th.script'])
+test('T27022', normal, compile_and_run, [''])
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/159d82655c8e85c7ef777f2d3d5cba…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/159d82655c8e85c7ef777f2d3d5cba…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/mangoiv/ci-backports] Deleted 1 commit: ci: increased low speed time, removed post buffer increase
by Magnus (@MangoIV) 07 May '26
by Magnus (@MangoIV) 07 May '26
07 May '26
Magnus pushed to branch wip/mangoiv/ci-backports at Glasgow Haskell Compiler / GHC
WARNING: The push did not contain any new commits, but force pushed to delete the commits and changes below.
Deleted commits:
3f4db087 by mangoiv at 2026-05-07T13:06:18+02:00
ci: increased low speed time, removed post buffer increase
- - - - -
1 changed file:
- .gitlab/test-metrics.sh
Changes:
=====================================
.gitlab/test-metrics.sh
=====================================
@@ -22,7 +22,10 @@ function pull() {
# to set a larger http.postBuffer, although this is definitely a workaround.
# The default should work just fine. The error could be in git, GitLab, or
# perhaps the networking tube (including all proxies etc) between the two.
- run git -c http.postBuffer=2097152 fetch -f "$NOTES_ORIGIN" "$ref:$ref"
+ # 2026-05-07: git fetch started failing again on darwin. Increased the low speed
+ # time. Removed the postbuffer change since we shouldn't actually be PUSHing anything
+ export GIT_CURL_VERBOSE=1
+ run git -c http.lowSpeedTime=600 fetch -f "$NOTES_ORIGIN" "$ref:$ref"
echo "perf notes ref $ref is $(git rev-parse $ref)"
}
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/3f4db087bfd12a145a997745c5022ab…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/3f4db087bfd12a145a997745c5022ab…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc] Pushed new branch wip/spj-reinstallable-base3
by Simon Peyton Jones (@simonpj) 07 May '26
by Simon Peyton Jones (@simonpj) 07 May '26
07 May '26
Simon Peyton Jones pushed new branch wip/spj-reinstallable-base3 at Glasgow Haskell Compiler / GHC
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/tree/wip/spj-reinstallable-base3
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/mangoiv/ci-backports] ci: increased low speed time, removed post buffer increase
by Magnus (@MangoIV) 07 May '26
by Magnus (@MangoIV) 07 May '26
07 May '26
Magnus pushed to branch wip/mangoiv/ci-backports at Glasgow Haskell Compiler / GHC
Commits:
3f4db087 by mangoiv at 2026-05-07T13:06:18+02:00
ci: increased low speed time, removed post buffer increase
- - - - -
1 changed file:
- .gitlab/test-metrics.sh
Changes:
=====================================
.gitlab/test-metrics.sh
=====================================
@@ -22,7 +22,10 @@ function pull() {
# to set a larger http.postBuffer, although this is definitely a workaround.
# The default should work just fine. The error could be in git, GitLab, or
# perhaps the networking tube (including all proxies etc) between the two.
- run git -c http.postBuffer=2097152 fetch -f "$NOTES_ORIGIN" "$ref:$ref"
+ # 2026-05-07: git fetch started failing again on darwin. Increased the low speed
+ # time. Removed the postbuffer change since we shouldn't actually be PUSHing anything
+ export GIT_CURL_VERBOSE=1
+ run git -c http.lowSpeedTime=600 fetch -f "$NOTES_ORIGIN" "$ref:$ref"
echo "perf notes ref $ref is $(git rev-parse $ref)"
}
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/3f4db087bfd12a145a997745c5022ab…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/3f4db087bfd12a145a997745c5022ab…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/mangoiv/ci-backports] ci: increased low speed time, removed post buffer increase
by Magnus (@MangoIV) 07 May '26
by Magnus (@MangoIV) 07 May '26
07 May '26
Magnus pushed to branch wip/mangoiv/ci-backports at Glasgow Haskell Compiler / GHC
Commits:
f3e714d4 by mangoiv at 2026-05-07T11:26:31+02:00
ci: increased low speed time, removed post buffer increase
- - - - -
1 changed file:
- .gitlab/test-metrics.sh
Changes:
=====================================
.gitlab/test-metrics.sh
=====================================
@@ -22,7 +22,9 @@ function pull() {
# to set a larger http.postBuffer, although this is definitely a workaround.
# The default should work just fine. The error could be in git, GitLab, or
# perhaps the networking tube (including all proxies etc) between the two.
- run git -c http.postBuffer=2097152 fetch -f "$NOTES_ORIGIN" "$ref:$ref"
+ # 2026-05-07: git fetch started failing again on darwin. Increased the low speed
+ # time. Removed the postbuffer change since we shouldn't actually be PUSHing anything
+ run GIT_CURL_VERBOSE=1 git -c http.lowSpeedTime=600 fetch -f "$NOTES_ORIGIN" "$ref:$ref"
echo "perf notes ref $ref is $(git rev-parse $ref)"
}
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/f3e714d4caf4536e8e82d45cd71ea26…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/f3e714d4caf4536e8e82d45cd71ea26…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/dcoutts/windows-rts-dll] 5 commits: Add minimal dlltool support into ./configure
by David Eichmann (@DavidEichmann) 06 May '26
by David Eichmann (@DavidEichmann) 06 May '26
06 May '26
David Eichmann pushed to branch wip/dcoutts/windows-rts-dll at Glasgow Haskell Compiler / GHC
Commits:
3eed75d2 by Duncan Coutts at 2026-05-06T17:58:12+01:00
Add minimal dlltool support into ./configure
Find dlltool, and hopefully support finding it within the bundled llvm
toolchain on windows.
- - - - -
9a074a69 by Duncan Coutts at 2026-05-06T17:58:14+01:00
Update the default host and target files for dlltool support
- - - - -
5fdb97fd by Duncan Coutts at 2026-05-06T17:58:14+01:00
Add dlltool as a hadrian builder
Optional except on windows.
- - - - -
6b7d555f by Duncan Coutts at 2026-05-06T17:58:14+01:00
Update and generate libHSghc-internal.def from .def.in file
The only symbol that the rts imports from the ghc-internal package now
is init_ghc_hs_iface. So the rts only needs an import lib that defines
that one symbol.
Also, remove the libHSghc-prim.def because it is redundant. The rts no
longer imports anything from ghc-prim.
Keep libHSffi.def for now. We may yet need it once it is clear how
libffi is going to be built/used for ghc.
- - - - -
f195d87e by Duncan Coutts at 2026-05-06T17:58:14+01:00
Add rule to build libHSghc-internal.dll.a and link into the rts
On windows only, with dynamic linking.
This is needed because on windows, all symbols in dlls must be resolved.
No dangling symbols allowed. References to external symbols must be
explicit. We resolve this with an import library. We create an import
library for ghc-internal, a .dll.a file. This is a static archive
containing .o files that define the symbols we need, and crucially have
".idata" sections that specifies the symbols the dll imports and from
where.
Note that we do not install this libHSghc-internal.dll.a, and it does
not need to list all the symbols exported by that package. We create a
special purpose import lib and only use it when linking the rts dll, so
it only has to list the symbols that the rts uses from ghc-internal
(which is exactly one symbol: init_ghc_hs_iface).
- - - - -
15 changed files:
- configure.ac
- distrib/configure.ac.in
- hadrian/cfg/default.host.target.in
- hadrian/cfg/default.target.in
- hadrian/src/Builder.hs
- hadrian/src/Rules/Generate.hs
- hadrian/src/Rules/Library.hs
- hadrian/src/Rules/Rts.hs
- m4/find_llvm_prog.m4
- m4/fp_setup_windows_toolchain.m4
- m4/ghc_toolchain.m4
- m4/prep_target_file.m4
- rts/.gitignore
- + rts/win32/libHSghc-internal.def.in
- utils/ghc-toolchain/exe/Main.hs
Changes:
=====================================
configure.ac
=====================================
@@ -314,13 +314,16 @@ else
AC_CHECK_TOOL([RANLIB],[ranlib])
AC_CHECK_TOOL([OBJDUMP],[objdump])
AC_CHECK_TOOL([WindresCmd],[windres])
+ AC_CHECK_TOOL([DlltoolCmd],[llvm-dlltool])
AC_CHECK_TOOL([Genlib],[genlib])
if test "$HostOS" = "mingw32"; then
AC_CHECK_TARGET_TOOL([WindresCmd],[windres])
+ AC_CHECK_TARGET_TOOL([DlltoolCmd],[llvm-dlltool])
AC_CHECK_TARGET_TOOL([OBJDUMP],[objdump])
WindresCmd="$(cygpath -m $WindresCmd)"
+ DlltoolCmd="$(cygpath -m $DlltoolCmd)"
if test "$Genlib" != ""; then
GenlibCmd="$(cygpath -m $Genlib)"
@@ -568,6 +571,11 @@ FIND_LLVM_PROG([OPT], [opt], [$LlvmMinVersion], [$LlvmMaxVersion])
OptCmd="$OPT"
AC_SUBST([OptCmd])
+dnl ** Which LLVM llvm-dlltool to use?
+dnl --------------------------------------------------------------
+AC_ARG_VAR(DlltoolCmd,[Use as the path to LLVM's llvm-dlltool [default=autodetect]])
+FIND_LLVM_PROG([DlltoolCmd], [llvm-dlltool], [$LlvmMinVersion], [$LlvmMaxVersion])
+
dnl ** look to see if we have a C compiler using an llvm back end.
dnl
FP_CC_LLVM_BACKEND
@@ -1080,9 +1088,10 @@ echo "\
libdw : $UseLibdw
Using LLVM tools
- llc : $LlcCmd
- opt : $OptCmd
- llvm-as : $LlvmAsCmd"
+ llc : $LlcCmd
+ opt : $OptCmd
+ llvm-as : $LlvmAsCmd
+ llvm-dlltool : $DlltoolCmd"
if test "$HSCOLOUR" = ""; then
echo "
=====================================
distrib/configure.ac.in
=====================================
@@ -229,6 +229,13 @@ FIND_LLVM_PROG([LLVMAS], [clang], [$LlvmMinVersion], [$LlvmMaxVersion])
LlvmAsCmd="$LLVMAS"
AC_SUBST([LlvmAsCmd])
+dnl ** Which LLVM llvm-dlltool to use?
+dnl --------------------------------------------------------------
+AC_CHECK_TARGET_TOOL([DlltoolCmd],[llvm-dlltool])
+AC_ARG_VAR(DlltoolCmd,[Use as the path to LLVM's llvm-dlltool [default=autodetect]])
+FIND_LLVM_PROG([DlltoolCmd], [llvm-dlltool], [$LlvmMinVersion], [$LlvmMaxVersion])
+AC_SUBST([DlltoolCmd])
+
dnl We know that `clang` supports `--target` and it is necessary to pass it
dnl lest we see #25793.
if test -z "$LlvmAsFlags" && ! test -z "$LlvmTarget"; then
=====================================
hadrian/cfg/default.host.target.in
=====================================
@@ -45,6 +45,7 @@ Target
, tgtOpt = Nothing
, tgtLlvmAs = Nothing
, tgtWindres = Nothing
+, tgtDlltool = Nothing
, tgtOtool = Nothing
, tgtInstallNameTool = Nothing
}
=====================================
hadrian/cfg/default.target.in
=====================================
@@ -45,6 +45,7 @@ Target
, tgtOpt = @OptCmdMaybeProg@
, tgtLlvmAs = @LlvmAsCmdMaybeProg@
, tgtWindres = @WindresCmdMaybeProg@
+, tgtDlltool = @DlltoolCmdMaybeProg@
, tgtOtool = @OtoolCmdMaybeProg@
, tgtInstallNameTool = @InstallNameToolCmdMaybeProg@
}
=====================================
hadrian/src/Builder.hs
=====================================
@@ -17,7 +17,7 @@ import Development.Shake.Classes
import Development.Shake.Command
import Development.Shake.FilePath
import GHC.Generics
-import GHC.Platform.ArchOS (ArchOS(..), Arch(..))
+import GHC.Platform.ArchOS (ArchOS(..), Arch(..), OS(..))
import qualified Hadrian.Builder as H
import Hadrian.Builder hiding (Builder)
import Hadrian.Builder.Ar
@@ -180,6 +180,7 @@ data Builder = Alex
| Objdump
| Python
| Ranlib
+ | Dlltool
| Testsuite TestMode
| Sphinx SphinxMode
| Tar TarMode
@@ -418,6 +419,7 @@ isOptional target = \case
Alex -> True
-- Most ar implemententions no longer need ranlib, but some still do
Ranlib -> not $ Toolchain.arNeedsRanlib (tgtAr target)
+ Dlltool -> archOS_OS (tgtArchOs target) /= OSMinGW32
JsCpp -> not $ (archOS_arch . tgtArchOs) target == ArchJavaScript -- ArchWasm32 too?
_ -> False
@@ -442,6 +444,7 @@ systemBuilderPath builder = case builder of
Objdump -> fromKey "objdump"
Python -> fromKey "python"
Ranlib -> fromTargetTC "ranlib" (maybeProg Toolchain.ranlibProgram . tgtRanlib)
+ Dlltool -> fromTargetTC "dlltool" (maybeProg id . tgtDlltool)
Testsuite _ -> fromKey "python"
Sphinx _ -> fromKey "sphinx-build"
Tar _ -> fromKey "tar"
=====================================
hadrian/src/Rules/Generate.hs
=====================================
@@ -377,6 +377,7 @@ templateRules = do
, interpolateSetting "ProjectPatchLevel1" ProjectPatchLevel1
, interpolateSetting "ProjectPatchLevel2" ProjectPatchLevel2
]
+ templateRule "rts/win32/libHSghc-internal.def" projectVersion
templateRule "docs/index.html" $ packageUnitIds Stage1
templateRule "docs/users_guide/ghc_config.py" $ mconcat
[ projectVersion
=====================================
hadrian/src/Rules/Library.hs
=====================================
@@ -4,6 +4,8 @@ import Hadrian.BuildPath
import Hadrian.Haskell.Cabal
import Hadrian.Haskell.Cabal.Type
import qualified Text.Parsec as Parsec
+import GHC.Platform.ArchOS (ArchOS(archOS_OS), OS(..))
+import GHC.Toolchain.Target (Target(tgtArchOs))
import Base
import Context
@@ -185,9 +187,13 @@ jsObjects context = do
srcs <- interpretInContext context (getContextData jsSrcs)
mapM (objectPath context) srcs
--- | Return extra object files needed to build the given library context. The
--- resulting list is currently non-empty only when the package from the
--- 'Context' is @ghc-internal@ built with in-tree GMP backend.
+-- | Return extra object files needed to build the given library context.
+--
+-- This is non-empty for:
+--
+-- * @ghc-internal@ when built with in-tree GMP backend
+-- * @rts@ on Windows when linking dynamically
+--
extraObjects :: Context -> Action [FilePath]
extraObjects context
| package context == ghcInternal = do
@@ -195,6 +201,13 @@ extraObjects context
"gmp" -> gmpObjects (stage context)
_ -> return []
+ | package context == rts = do
+ target <- interpretInContext context getStagedTarget
+ builddir <- buildPath context
+ return [ builddir -/- "libHSghc-internal.dll.a"
+ | archOS_OS (tgtArchOs target) == OSMinGW32
+ , Dynamic `wayUnit` way context ]
+
| otherwise = return []
-- | Return all the object files to be put into the library we're building for
=====================================
hadrian/src/Rules/Rts.hs
=====================================
@@ -24,6 +24,20 @@ rtsRules = priority 3 $ do
(addRtsDummyVersion $ takeFileName rtsLibFilePath')
rtsLibFilePath'
+ -- Solve the recursive dependency between the rts and ghc-internal
+ -- on Windows by creating an import lib for the ghc-internal dll,
+ -- to be linked into the rts dll.
+ forM_ [Stage1, Stage2, Stage3 ] $ \ stage -> do
+ let buildPath = root -/- buildDir (rtsContext stage)
+ buildPath -/- "libHSghc-internal.dll.a" %> buildGhcInternalImportLib
+
+buildGhcInternalImportLib :: FilePath -> Action ()
+buildGhcInternalImportLib target = do
+ let input = "rts/win32/libHSghc-internal.def"
+ output = target -- the .dll.a import lib
+ need [input]
+ runBuilder Dlltool ["-d", input, "-l", output] [input] [output]
+
-- Need symlinks generated by rtsRules.
needRtsSymLinks :: Stage -> Set.Set Way -> Action ()
needRtsSymLinks stage rtsWays
=====================================
m4/find_llvm_prog.m4
=====================================
@@ -16,9 +16,12 @@ AC_DEFUN([FIND_LLVM_PROG],[
AS_IF([test x"$$1" != x],[
PROG_VERSION=`$$1 --version | sed -n -e 's/.*version \(\([[0-9]][[0-9]]*\.\)\([[0-9]][[0-9]]*\.\)*[[0-9]][[0-9]]*\).*/\1/gp'`
AS_IF([test x"$PROG_VERSION" = x],
- [AC_MSG_RESULT(no)
- $1=""
- AC_MSG_NOTICE([We only support llvm $3 upto $4 (non-inclusive) (no version found).])],
+ [AS_IF(
+ [test x"$2" = x"llvm-dlltool"],
+ [AC_MSG_RESULT(yes)], # llvm-dlltool doesn't have a --version
+ [AC_MSG_RESULT(no)
+ AC_MSG_NOTICE([We only support llvm $3 upto $4 (non-inclusive) (no version found).])]
+ )],
[AC_MSG_CHECKING([$$1 version ($PROG_VERSION) is between $3 and $4])
AX_COMPARE_VERSION([$PROG_VERSION], [lt], [$3],
[AC_MSG_RESULT(no)
=====================================
m4/fp_setup_windows_toolchain.m4
=====================================
@@ -131,8 +131,8 @@ AC_DEFUN([FP_SETUP_WINDOWS_TOOLCHAIN],[
AR="${mingwbin}llvm-ar.exe"
RANLIB="${mingwbin}llvm-ranlib.exe"
OBJDUMP="${mingwbin}llvm-objdump.exe"
- DLLTOOL="${mingwbin}llvm-dlltool.exe"
WindresCmd="${mingwbin}llvm-windres.exe"
+ DlltoolCmd="${mingwbin}llvm-dlltool.exe"
LLC="${mingwbin}llc.exe"
OPT="${mingwbin}opt.exe"
LLVMAS="${mingwbin}clang.exe"
=====================================
m4/ghc_toolchain.m4
=====================================
@@ -95,6 +95,7 @@ AC_DEFUN([FIND_GHC_TOOLCHAIN],
echo "--merge-objs=$MergeObjsCmd" >> acargs
echo "--readelf=$READELF" >> acargs
echo "--windres=$WindresCmd" >> acargs
+ echo "--dlltool=$DlltoolCmd" >> acargs
echo "--llc=$LlcCmd" >> acargs
echo "--opt=$OptCmd" >> acargs
echo "--llvm-as=$LlvmAsCmd" >> acargs
=====================================
m4/prep_target_file.m4
=====================================
@@ -191,6 +191,7 @@ AC_DEFUN([PREP_TARGET_FILE],[
PREP_MAYBE_SIMPLE_PROGRAM([OptCmd])
PREP_MAYBE_PROGRAM([LlvmAsCmd], [LlvmAsFlags])
PREP_MAYBE_SIMPLE_PROGRAM([WindresCmd])
+ PREP_MAYBE_SIMPLE_PROGRAM([DlltoolCmd])
PREP_MAYBE_SIMPLE_PROGRAM([OtoolCmd])
PREP_MAYBE_SIMPLE_PROGRAM([InstallNameToolCmd])
PREP_MAYBE_STRING([TargetVendor_CPP])
=====================================
rts/.gitignore
=====================================
@@ -20,3 +20,4 @@
/ghcautoconf.h.autoconf.in
/ghcautoconf.h.autoconf
/include/ghcautoconf.h
+/win32/libHSghc-internal.def
=====================================
rts/win32/libHSghc-internal.def.in
=====================================
@@ -0,0 +1,4 @@
+LIBRARY libHSghc-internal-@ProjectVersionForLib@.0-ghc@ProjectVersion@.dll
+
+EXPORTS
+ init_ghc_hs_iface
=====================================
utils/ghc-toolchain/exe/Main.hs
=====================================
@@ -486,7 +486,7 @@ mkTarget opts = do
-- for windows, also used for cross compiling
windres <- optional $ findProgram "windres" (optWindres opts) ["windres"]
- dlltool <- optional $ findProgram "dlltool" (optDlltool opts) ["dlltool", "llvm-dlltool"]
+ dlltool <- optional $ findProgram "dlltool" (optDlltool opts) ["llvm-dlltool"]
-- Darwin-specific utilities
(otool, installNameTool) <-
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/3ea6bb3f3c11c0e300ac2c627d05cf…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/3ea6bb3f3c11c0e300ac2c627d05cf…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/spj-reinstallable-base2] unsafeCoercePrimName
by Rodrigo Mesquita (@alt-romes) 06 May '26
by Rodrigo Mesquita (@alt-romes) 06 May '26
06 May '26
Rodrigo Mesquita pushed to branch wip/spj-reinstallable-base2 at Glasgow Haskell Compiler / GHC
Commits:
25497cb4 by Rodrigo Mesquita at 2026-05-06T17:51:42+01:00
unsafeCoercePrimName
- - - - -
5 changed files:
- compiler/GHC/Builtin/KnownKeys.hs
- compiler/GHC/HsToCore.hs
- compiler/GHC/Tc/Module.hs
- compiler/GHC/Tc/Utils/Concrete.hs
- libraries/base/src/GHC/Essentials.hs
Changes:
=====================================
compiler/GHC/Builtin/KnownKeys.hs
=====================================
@@ -271,6 +271,9 @@ knownKeyTable
, (mkDataOcc ":$$:", typeErrorVAppendDataConKey)
, (mkDataOcc "ShowType", typeErrorShowTypeDataConKey)
+ -- Unsafe coercion proofs
+ , (mkVarOcc "unsafeCoerce#", unsafeCoercePrimIdKey)
+
-- Plugins
, (mkTcOcc "Plugin", pluginTyConKey)
, (mkTcOcc "FrontendPlugin", frontendPluginTyConKey)
@@ -372,10 +375,7 @@ basicKnownKeyNames
starKindRepName,
starArrStarKindRepName,
starArrStarArrStarKindRepName,
- constraintKindRepName,
-
- -- Unsafe coercion proofs
- unsafeCoercePrimName
+ constraintKindRepName
]
@@ -437,10 +437,6 @@ constraintKindRepName = varQual gHC_TYPES (fsLit "krep$Constraint") con
withDictClassName :: Name
withDictClassName = clsQual gHC_MAGIC_DICT (fsLit "WithDict") withDictClassKey
--- Unsafe coercion proofs
-unsafeCoercePrimName:: Name
-unsafeCoercePrimName = varQual gHC_INTERNAL_UNSAFE_COERCE (fsLit "unsafeCoerce#") unsafeCoercePrimIdKey
-
genericClassKeys :: [KnownKey]
genericClassKeys = [genClassKey, gen1ClassKey]
=====================================
compiler/GHC/HsToCore.hs
=====================================
@@ -86,7 +86,6 @@ import GHC.Types.SourceFile
import GHC.Types.TypeEnv
import GHC.Types.Name
import GHC.Types.Name.Set
-import GHC.Types.Name.Env
import GHC.Types.Name.Ppr
import GHC.Types.HpcInfo
@@ -99,6 +98,7 @@ import Data.List (partition)
import Data.IORef
import GHC.Iface.Make (mkRecompUsageInfo)
import GHC.Runtime.Interpreter (interpreterProfiled)
+import GHC.Types.Unique.FM
{-
************************************************************************
@@ -684,13 +684,14 @@ patchMagicDefns pairs
-- optimization: check whether we're in a magic module before looking
-- at all the ids
= do { this_mod <- getModule
+ ; magicDefnModules <- mkMagicDefnModules
; if this_mod `elemModuleSet` magicDefnModules
then traverse patchMagicDefn pairs
else return pairs }
patchMagicDefn :: (Id, CoreExpr) -> DsM (Id, CoreExpr)
patchMagicDefn orig_pair@(orig_id, orig_rhs)
- | Just mk_magic_pair <- lookupNameEnv magicDefnsEnv (getName orig_id)
+ | Just mk_magic_pair <- lookupUFM magicDefnsEnv (getUnique orig_id)
= do { magic_pair@(magic_id, _) <- mk_magic_pair orig_id orig_rhs
-- Patching should not change the Name or the type of the Id
@@ -701,22 +702,25 @@ patchMagicDefn orig_pair@(orig_id, orig_rhs)
| otherwise
= return orig_pair
-magicDefns :: [(Name, Id -> CoreExpr -- old Id and RHS
+magicDefns :: [(KnownKey, Id -> CoreExpr -- old Id and RHS
-> DsM (Id, CoreExpr) -- new Id and RHS
)]
-magicDefns = [ (unsafeCoercePrimName, mkUnsafeCoercePrimPair) ]
+magicDefns = [ (unsafeCoercePrimIdKey, mkUnsafeCoercePrimPair) ]
-magicDefnsEnv :: NameEnv (Id -> CoreExpr -> DsM (Id, CoreExpr))
-magicDefnsEnv = mkNameEnv magicDefns
+magicDefnsEnv :: UniqFM KnownKey (Id -> CoreExpr -> DsM (Id, CoreExpr))
+magicDefnsEnv = listToUFM magicDefns
-magicDefnModules :: ModuleSet
-magicDefnModules = mkModuleSet $ map (nameModule . getName . fst) magicDefns
+mkMagicDefnModules :: DsM ModuleSet
+mkMagicDefnModules = do
+ mods <- mapM (fmap nameModule . dsLookupKnownKeyName . fst) magicDefns
+ pure $ mkModuleSet mods
mkUnsafeCoercePrimPair :: Id -> CoreExpr -> DsM (Id, CoreExpr)
-- See Note [Wiring in unsafeCoerce#] for the defn we are creating here
mkUnsafeCoercePrimPair _old_id old_expr
= do { unsafe_equality_proof_id <- dsLookupKnownKeyId unsafeEqualityProofIdKey
; unsafe_equality_tc <- dsLookupKnownKeyTyCon unsafeEqualityTyConKey
+ ; unsafeCoercePrimName <- dsLookupKnownKeyName unsafeCoercePrimIdKey
; let [unsafe_refl_data_con] = tyConDataCons unsafe_equality_tc
=====================================
compiler/GHC/Tc/Module.hs
=====================================
@@ -2566,7 +2566,7 @@ tcGhciStmts stmts
-- We use Any rather than a dummy type such as () because of
-- the rules of unsafeCoerce#; see Unsafe/Coerce.hs for the details.
- ; AnId unsafe_coerce_id <- tcLookupGlobal unsafeCoercePrimName
+ ; AnId unsafe_coerce_id <- tcLookupKnownKeyGlobal unsafeCoercePrimIdKey
-- We use unsafeCoerce# here because of (U11) in
-- Note [Implementing unsafeCoerce] in base:Unsafe.Coerce
=====================================
compiler/GHC/Tc/Utils/Concrete.hs
=====================================
@@ -18,7 +18,7 @@ module GHC.Tc.Utils.Concrete
import GHC.Prelude
-import GHC.Builtin.KnownKeys ( unsafeCoercePrimName )
+import GHC.Builtin.KnownKeys ( unsafeCoercePrimIdKey )
import GHC.Builtin.WiredIn.Types
import GHC.Core.Coercion
@@ -45,6 +45,7 @@ import GHC.Utils.Outputable
import GHC.Data.FastString ( FastString, fsLit )
import Control.Monad ( void )
+import GHC.Types.Name (hasKnownKey)
{- Note [Concrete overview]
@@ -857,7 +858,7 @@ idConcreteTvs id
-- in the correct information in the desugarer).
-- So, for the time being, we manually inspect the type of the original,
-- unpatched Id to retrieve which of its outer forall-d tyvars should be concrete.
- | idName id == unsafeCoercePrimName
+ | id `hasKnownKey`unsafeCoercePrimIdKey
, (a_rep:_b_rep:a:_b:_, _) <- tcSplitForAllTyVars $ idType id
-- NB: only check the argument representation, not the result representation.
-- This is because the following is OK:
@@ -866,7 +867,7 @@ idConcreteTvs id
-- unsafeCoerceWordRep = unsafeCoerce#
= mkNameEnv
[(tyVarName a_rep, ConcreteFRR $ FixedRuntimeRepOrigin (mkTyVarTy a)
- $ FRRRepPolyId unsafeCoercePrimName RepPolyFunction
+ $ FRRRepPolyId (idName id) RepPolyFunction
$ mkArgPos 1 Top)]
| otherwise
=====================================
libraries/base/src/GHC/Essentials.hs
=====================================
@@ -161,7 +161,7 @@ module GHC.Essentials
, CS.unpackAppendCStringUtf8#, CS.cstringLength#
, eqString, inline
- , UnsafeEquality( UnsafeRefl ), unsafeEqualityProof
+ , UnsafeEquality( UnsafeRefl ), unsafeEqualityProof, unsafeCoerce#
-- Typeable and type representations
, SomeTypeRep( SomeTypeRep ), TR.Module( Module )
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/25497cb478856fc82cdc5277f85eebb…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/25497cb478856fc82cdc5277f85eebb…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/spj-reinstallable-base2] 4 commits: Document in commit message cycle with unpack
by Rodrigo Mesquita (@alt-romes) 06 May '26
by Rodrigo Mesquita (@alt-romes) 06 May '26
06 May '26
Rodrigo Mesquita pushed to branch wip/spj-reinstallable-base2 at Glasgow Haskell Compiler / GHC
Commits:
ede44664 by Rodrigo Mesquita at 2026-05-06T14:06:59+01:00
Document in commit message cycle with unpack
- GHC.Internal.CString defines a known-key name unpackCString# (unpackCStringIdKey)
- IIUC, this known-key is looked up essentially for all modules, as long as there’s some literal string in it (coreStringLit uses unpackCStringIdKey)
- GHC.Internal.Types has literal strings, so it needs to lookup unpackCString. It fails with this error:
libraries/ghc-internal/src/GHC/Internal/Types.hs:653:1: error: [GHC-99040]
• Could not find known-key entity ‘unpackCString#’
in the top-level environment (unqualified, or qualified as Rebindable)
Consider importing it
Call stack:
lookupKnownGRE, called at compiler/GHC/Iface/Load.hs:196:10 in ghc-10.1-inplace:GHC.Iface.Load
[…]
tcLookupKnownKeyId, called at compiler/GHC/Tc/Solver/Monad.hs:1192:36 in ghc-10.1-inplace:GHC.Tc.Solver.Monad
• In the kind ‘k’
In the class declaration for ‘Coercible’
|
653 | class Coercible (a :: k) (b :: k)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- This makes sense, unpackCString must be in scope (this is GHC.Internal.Types, i.e. ghc-internal, i.e. compiled with -frebindable-known-names)
- For GHC.Internal.Types, unpackCString is looked up to later construct a string expression in `evCallStack`
- Thus, GHC.Internal.Types MUST import unpackCString from GHC.Internal.String
- BUT! GHC.Internal.CString imports GHC.Internal.Types…
- - - - -
61d6b31d by Rodrigo Mesquita at 2026-05-06T14:20:35+01:00
ptrTyConName, funPtrTyConName
- - - - -
6decd81e by Rodrigo Mesquita at 2026-05-06T14:24:17+01:00
word8TyConName is wired in
- - - - -
9dec147e by Rodrigo Mesquita at 2026-05-06T17:29:51+01:00
pluginTyConName, frontendPluginTyConName
- - - - -
7 changed files:
- compiler/GHC/Builtin/KnownKeys.hs
- compiler/GHC/Builtin/WiredIn/Types.hs
- compiler/GHC/Driver/Plugins.hs
- compiler/GHC/HsToCore/Foreign/C.hs
- compiler/GHC/Runtime/Loader.hs
- libraries/base/src/GHC/Essentials.hs
- libraries/ghc-internal/src/GHC/Internal/Ptr.hs
Changes:
=====================================
compiler/GHC/Builtin/KnownKeys.hs
=====================================
@@ -163,6 +163,8 @@ knownKeyRdrName key = knownOccRdrName (knownKeyOccName key)
This section tells what the compiler knows about the association of
names with uniques. These ones are the *non* wired-in ones. The
wired in ones are defined in GHC.Builtin.Types etc.
+
+See Note [Overview of known entities] in GHC.Builtin
-}
knownKeyTable :: [(OccName, KnownKey)]
@@ -269,6 +271,10 @@ knownKeyTable
, (mkDataOcc ":$$:", typeErrorVAppendDataConKey)
, (mkDataOcc "ShowType", typeErrorShowTypeDataConKey)
+ -- Plugins
+ , (mkTcOcc "Plugin", pluginTyConKey)
+ , (mkTcOcc "FrontendPlugin", frontendPluginTyConKey)
+
-- Base strings Strings
, (mkVarOcc "unpackCString#", unpackCStringIdKey)
, (mkVarOcc "unpackCStringUtf8#", unpackCStringUtf8IdKey)
@@ -367,16 +373,9 @@ basicKnownKeyNames
starArrStarKindRepName,
starArrStarArrStarKindRepName,
constraintKindRepName,
- -- FFI primitive types that are not wired-in.
- ptrTyConName, funPtrTyConName,
- word8TyConName,
-
- -- Plugins
- pluginTyConName
- , frontendPluginTyConName
-- Unsafe coercion proofs
- , unsafeCoercePrimName
+ unsafeCoercePrimName
]
@@ -445,23 +444,6 @@ unsafeCoercePrimName = varQual gHC_INTERNAL_UNSAFE_COERCE (fsLit "unsafeCoerc
genericClassKeys :: [KnownKey]
genericClassKeys = [genClassKey, gen1ClassKey]
--- Word module
-word8TyConName :: Name
-word8TyConName = tcQual gHC_INTERNAL_WORD (fsLit "Word8") word8TyConKey
-
--- PrelPtr module
-ptrTyConName, funPtrTyConName :: Name
-ptrTyConName = tcQual gHC_INTERNAL_PTR (fsLit "Ptr") ptrTyConKey
-funPtrTyConName = tcQual gHC_INTERNAL_PTR (fsLit "FunPtr") funPtrTyConKey
-
--- plugins
-pLUGINS :: Module
-pLUGINS = mkThisGhcModule (fsLit "GHC.Driver.Plugins")
-pluginTyConName :: Name
-pluginTyConName = tcQual pLUGINS (fsLit "Plugin") pluginTyConKey
-frontendPluginTyConName :: Name
-frontendPluginTyConName = tcQual pLUGINS (fsLit "FrontendPlugin") frontendPluginTyConKey
-
{-
************************************************************************
* *
=====================================
compiler/GHC/Builtin/WiredIn/Types.hs
=====================================
@@ -58,7 +58,7 @@ module GHC.Builtin.WiredIn.Types (
wordTyCon, wordDataCon, wordTyConName, wordTy,
-- * Word8
- word8TyCon, word8DataCon, word8Ty,
+ word8TyCon, word8DataCon, word8TyConName, word8Ty,
-- * List
listTyCon, listTyConName, listTyConKey,
@@ -395,10 +395,11 @@ nothingDataConName = mkWiredInDataConName UserSyntax gHC_INTERNAL_MAYBE (fsLit "
justDataConName = mkWiredInDataConName UserSyntax gHC_INTERNAL_MAYBE (fsLit "Just")
justDataConKey justDataCon
-wordTyConName, wordDataConName, word8DataConName :: Name
-wordTyConName = mkWiredInTyConName UserSyntax gHC_TYPES (fsLit "Word") wordTyConKey wordTyCon
-wordDataConName = mkWiredInDataConName UserSyntax gHC_TYPES (fsLit "W#") wordDataConKey wordDataCon
-word8DataConName = mkWiredInDataConName UserSyntax gHC_INTERNAL_WORD (fsLit "W8#") word8DataConKey word8DataCon
+wordTyConName, wordDataConName, word8TyConName, word8DataConName :: Name
+wordTyConName = mkWiredInTyConName UserSyntax gHC_TYPES (fsLit "Word") wordTyConKey wordTyCon
+wordDataConName = mkWiredInDataConName UserSyntax gHC_TYPES (fsLit "W#") wordDataConKey wordDataCon
+word8TyConName = mkWiredInTyConName UserSyntax gHC_INTERNAL_WORD (fsLit "Word8") word8TyConKey word8TyCon
+word8DataConName = mkWiredInDataConName UserSyntax gHC_INTERNAL_WORD (fsLit "W8#") word8DataConKey word8DataCon
floatTyConName, floatDataConName, doubleTyConName, doubleDataConName :: Name
floatTyConName = mkWiredInTyConName UserSyntax gHC_TYPES (fsLit "Float") floatTyConKey floatTyCon
=====================================
compiler/GHC/Driver/Plugins.hs
=====================================
@@ -5,6 +5,12 @@
{-# LANGUAGE UnboxedTuples #-}
#endif
+#if __GLASGOW_HASKELL__ > 1000
+{-# OPTIONS_GHC -fdefines-known-key-names #-}
+ -- 'Plugin' and 'FrontendPlugin' are known-key names
+ -- (they have a fixed known unique) (from GHC 10.2)
+ -- See Note [Overview of known entities] in GHC.Builtin
+#endif
-- | Definitions for writing /plugins/ for GHC. Plugins can hook into
-- several areas of the compiler. See the 'Plugin' type. These plugins
=====================================
compiler/GHC/HsToCore/Foreign/C.hs
=====================================
@@ -365,7 +365,7 @@ toCType t = case f False t of
-- If the inner type is void-based, we collapse the pointer
-- chain to just "void*". See Note [Collapsing void pointer chains].
| Just (ptr, [t']) <- splitTyConApp_maybe t
- , tyConName ptr `elem` [ptrTyConName, funPtrTyConName]
+ , tyConUnique ptr `elem` [ptrTyConKey, funPtrTyConKey]
= case f True t' of
(mh, True, _) ->
(mh, True, text "void*")
=====================================
compiler/GHC/Runtime/Loader.hs
=====================================
@@ -37,7 +37,7 @@ import GHC.Rename.Names ( gresFromAvails )
import GHC.Tc.Utils.Monad ( initTcInteractive, initIfaceTcRn )
import GHC.Iface.Load ( loadPluginInterface, cannotFindModule )
-import GHC.Builtin.KnownKeys ( pluginTyConName, frontendPluginTyConName )
+import GHC.Builtin.KnownKeys ( pluginTyConKey, frontendPluginTyConKey )
import GHC.Driver.Env
import GHCi.RemoteTypes ( HValue )
@@ -47,7 +47,7 @@ import GHC.Core.TyCon ( TyCon(tyConName) )
import GHC.Types.SrcLoc ( noSrcSpan )
-import GHC.Types.Name ( Name, nameModule, nameModule_maybe )
+import GHC.Types.Name ( Name, nameModule, nameModule_maybe, KnownKey, mkKnownKeyName, mkTcOcc )
import GHC.Types.Id ( idType )
import GHC.Types.PkgQual
import GHC.Types.TyThing
@@ -75,6 +75,7 @@ import GHC.Linker.Types
import Data.List (unzip4)
import GHC.Iface.Errors.Ppr
import GHC.Driver.Monad
+import GHC.Builtin.Modules
{- Note [Timing of plugin initialization]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -170,12 +171,13 @@ loadPlugins hsc_env
where
options = [ option | (opt_mod_nm, option) <- pluginModNameOpts dflags
, opt_mod_nm == mod_nm ]
+ pluginTyConName = mkKnownKeyGhcPluginsName (mkTcOcc "Plugin") pluginTyConKey
loadPlugin = loadPlugin' (mkVarOccFS (fsLit "plugin")) pluginTyConName hsc_env
-
loadFrontendPlugin :: HscEnv -> ModuleName -> IO (FrontendPlugin, [LinkableUsage], PkgsLoaded)
loadFrontendPlugin hsc_env mod_name = do
checkExternalInterpreter hsc_env
+ let frontendPluginTyConName = mkKnownKeyGhcPluginsName (mkTcOcc "FrontendPlugin") frontendPluginTyConKey
(plugin, _iface, links, pkgs)
<- loadPlugin' (mkVarOccFS (fsLit "frontendPlugin")) frontendPluginTyConName
hsc_env mod_name
@@ -188,6 +190,10 @@ checkExternalInterpreter hsc_env = case interpInstance <$> hsc_interp hsc_env of
-> throwIO (InstallationError "Plugins require -fno-external-interpreter")
_ -> pure ()
+mkKnownKeyGhcPluginsName :: OccName -> KnownKey -> Name
+mkKnownKeyGhcPluginsName occ kk =
+ mkKnownKeyName kk (mkThisGhcModule (fsLit "GHC.Driver.Plugins")) occ noSrcSpan
+
loadPlugin' :: OccName -> Name -> HscEnv -> ModuleName -> IO (a, ModIface, [LinkableUsage], PkgsLoaded)
loadPlugin' occ_name plugin_name hsc_env mod_name
= do { let plugin_rdr_name = mkRdrQual mod_name occ_name
=====================================
libraries/base/src/GHC/Essentials.hs
=====================================
@@ -76,6 +76,7 @@ module GHC.Essentials
, Void
-- FFI
+ , Ptr, FunPtr
, ConstPtr
-- Show internals
@@ -302,6 +303,7 @@ import GHC.Internal.GHCi
import GHC.Internal.Desugar (toAnnotationWrapper)
import GHC.Internal.Stack.Types
import GHC.Internal.Exception.Context
+import GHC.Internal.Ptr
import GHC.Internal.Foreign.C.ConstPtr
#if defined(javascript_HOST_ARCH)
import GHC.Internal.JS.Prim (unsafeUnpackJSStringUtf8##)
=====================================
libraries/ghc-internal/src/GHC/Internal/Ptr.hs
=====================================
@@ -2,6 +2,7 @@
{-# LANGUAGE CPP, NoImplicitPrelude, MagicHash, RoleAnnotations #-}
{-# LANGUAGE UnboxedTuples #-}
{-# OPTIONS_HADDOCK not-home #-}
+{-# OPTIONS_GHC -fdefines-known-key-names #-}
-----------------------------------------------------------------------------
-- |
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/64ed2f412343e5cdc5c0694b79f61e…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/64ed2f412343e5cdc5c0694b79f61e…
You're receiving this email because of your account on gitlab.haskell.org.
1
0