-
26b9a8a0
by Arkissa at 2026-10-17T16:04:10+00:00
ghci: Prefer exact source spans in :type-at
Prefer expression types for identical source spans.
Fixes #27412.
-
70dc4e61
by sheaf at 2026-10-18T18:15:15-04:00
Carefully implement coercion zapping
This commit implements coercion zapping in the rewriter, controlled by
the -fzap-coercions flag (on by default). This significantly reduces
the size of coercions produced when rewriting type family applications,
resulting in much faster compilation. See Note [Coercion zapping]
in GHC.Tc.Solver.Rewrite for an explanation and further details.
The rewriter now keeps track of the `CoVar`s and `CoercionHole`s it uses
when rewriting, which allows computing the dependencies of a zapped
coercion without traversing the coercion the rewriter produced, as
explained in Note [Computing coercion dependencies] in GHC.Tc.Solver.Rewrite.
Accompanying changes:
- Rework how dependencies of 'UnivCo's are tracked, purely for
performance reasons.
We now use the 'UnivCoDeps' datatype instead of '[Coercion]', and
we now insist that 'uco_deps' also contains the CoVars that appear
in the LHS and RHS types of the UnivCo (as a caching mechanism).
- Several performance improvements to the rewriter (e.g. increased
strictness).
- Improvements to the family application cache, including:
- Better documentation.
- Only cache a whole chain of reduction steps.
- Don't put coercion holes in the cache, fixing #27518.
- Allow Core Lint to be called from within the typechecker (and from
within the coercion optimiser).
- Move out "annotation linting" from Core Lint into the general Core
pipeline, to avoid the dependency GHC.Core.Coercion.Opt->GHC.Core.Lint
causing a regression in CountDepsParser due to transitive
dependencies of GHC.Core.Opt.Monad and GHC.Unit.Module.ModGuts.
Fixes #8095 #27336 #27518
Summary of performance numbers
Compile-time byte allocated in GHC's testsuite (0 regressions)
CoOpt_Singletons(normal) ghc/alloc 634,040,332 595,448,728 -6.1% GOOD
FamAppCachePerf(normal) ghc/alloc 495,629,812 191,612,920 -61.3% GOOD
LargeRecord(normal) ghc/alloc 1,226,806,434 966,260,976 -21.2% GOOD
SimplCastPerf(normal) ghc/alloc 160,505,678 148,112,688 -7.7% GOOD
T12227(normal) ghc/alloc 244,668,982 233,037,216 -4.8% GOOD
T12545(normal) ghc/alloc 697,804,842 424,705,152 -39.1% GOOD
T13386(normal) ghc/alloc 422,871,076 35,213,656 -91.7% GOOD
T15703(normal) ghc/alloc 288,402,364 273,692,560 -5.1% GOOD
T26426(normal) ghc/alloc 86,002,052 78,152,104 -9.1% GOOD
T27336(normal) ghc/alloc 498,932,268 35,886,016 -92.8% GOOD
T5030(normal) ghc/alloc 117,057,706 88,785,648 -24.2% GOOD
T5321Fun(normal) ghc/alloc 252,552,628 243,820,176 -3.5% GOOD
T8095(normal) ghc/alloc 768,198,564 127,779,520 -83.4% GOOD
T9872a(normal) ghc/alloc 1,655,675,020 1,406,017,008 -15.1% GOOD
T9872b(normal) ghc/alloc 1,864,180,628 1,644,706,696 -11.8% GOOD
T9872b_defer(normal) ghc/alloc 2,834,585,468 1,657,729,760 -41.5% GOOD
T9872c(normal) ghc/alloc 1,572,573,116 1,354,055,424 -13.9% GOOD
T9872d(normal) ghc/alloc 351,516,004 331,071,424 -5.8% GOOD
TcPlugin_RewritePerf(normal) ghc/alloc 2,063,065,680 1,847,464,272 -10.5% GOOD
Chesskell (github.com/Proxymoron461/chesskell/issues)
Typechecking that the first N plies of kholmovVsBronsteinShort
are a valid chess game:
Plies | GHC 9.14 time/alloc | This commit time/alloc |
-----:|---------------------|------------------------|
2 | 41s/20GB | 0.2s/19MB |
4 | 95s/49GB | 0.3s/25MB |
6 | timed out | 0.6s/522MB |
8 | timed out | 7s/673MB |
10 | timed out | timed out |
Compilation of singletons-base: compile time -28%, allocations -25%
-------------------------
Metric Decrease:
CoOpt_Singletons
FamAppCachePerf
LargeRecord
SimplCastPerf
T12227
T12545
T13386
T15703
T24984
T26426
T27336
T5030
T5321Fun
T8095
T9872a
T9872b
T9872b_defer
T9872c
T9872d
TcPlugin_RewritePerf
-------------------------
Co-Authored-By: Sylvain Henry <sylvain@haskus.fr>
-
3d326d7d
by Sylvain Henry at 2026-10-18T18:16:09-04:00
JS: fix stack corruption in h$ap_gen_fast's pushAllRegs (#27624)
The loop added in 9e46990976 ("JS: support more than 128 registers
(#26558)") pushed register N to stack[sp - N - 2] while the switch it
falls through into, and every reader of the h$ap_gen frame, uses
stack[sp - (N - 2)]. Registers >= R32 were therefore written 4 slots
too low: the slots the readers consult were left stale and the frame
below was silently corrupted.
Fix the loop to use the same slot formula as the switch, and add a
regression test: an unknown call to a thunk with 35 argument register
slots, which goes through the generic apply.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
-
6bbca569
by Andreas Klebinger at 2026-10-21T10:01:10+00:00
Fix a few tag inference bugs.
* Treat bottoming functions and nullary join points as functions.
* When generating bytecode don't rely on imported tag sigs.
Fixes #27831, #27841 and #27842.
-
71808e29
by Sylvain Henry at 2026-10-22T04:50:24-04:00
Add GHC.Internal.Unboxed.Do: do-notation for unboxed values (#27944)
QualifiedDo operators can be representation-polymorphic: a do-block is
expanded into ordinary applications before typechecking, and the only
check on a bound variable is the deferred fixed-runtime-rep check. A
representation-polymorphic (>>=) cannot be defined directly, though,
because its body would bind a variable of unknown representation.
The new module provides such operators for State#-passing code as
methods of a class indexed by RuntimeRep, with one instance per
representation. With optimisation the dictionaries disappear and a
block compiles to the usual nested case expressions:
Ubx.do { x <- readWordArray# mba 0# -- x :: Word#
; writeWordArray# mba 1# x -- unit actions are statements
; Ubx.pure x }
Also add users guide documentation and tests.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
-
33247780
by Sylvain Henry at 2026-10-22T04:50:24-04:00
Bignum: use Ubx.do for State#-passing code (#27944)
Rewrite the State#-passing code of the GHC.Internal.Bignum modules
(Primitives, WordArray, BigNat, Natural, Integer, Backend.Native,
Backend.GMP) with the qualified do-notation of GHC.Internal.Unboxed.Do.
Nested case chains such as
case newWordArray# sz s of { (# s, mwa #) ->
case act mwa s of { s ->
unsafeFreezeByteArray# mwa s }}
become
Ubx.do
mwa <- newWordArray# sz
act mwa
unsafeFreezeByteArray# mwa
The optimised Core is unchanged (compared per top-level binding).
BigNat now enables MonoLocalBinds: local loops written with Ubx.do
must stay monomorphic in the state type, otherwise GHC does not turn
them into join points.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
-
593f5f5b
by Sylvain Henry at 2026-10-22T04:51:16-04:00
JS: remove archive padding after wasm objects (#25665)
On Darwin hosts, emar (llvm-ar) writes archives in the Darwin format when
the first member is not a known object file, which is the case for the
archives of the JS backend. This format pads each member to 8 bytes with
'\n' and includes the padding in the member size. Wasm objects do not
accept trailing bytes: wasm-ld failed with "section too large".
The JS linker now removes this padding when it extracts a wasm object from
an archive. In addition, Hadrian passes --format=gnu to emar for the JS
target. See Note [Wasm objects in Darwin archives].
Also document the format of Cc (wasm) objects in Note [Cc object format].
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-
13dff5d0
by L0neGamer at 2026-10-22T21:37:31-04:00
add options for resizing bytearrays (#27244)
add five mutable bytearray resizing operations
for various kinds of pinnedness and aligning.
- prefer unpinned when allocating new array (``resizeMutableByteArrayPreferUnpinned#``; the ``resizeMutableByteArray#`` default)
- prefer pinned when allocating new array (``resizeMutableByteArrayPreferPinned#``)
- prefer aligned and pinned when allocating new array (``resizeMutableByteArrayPreferAlignedPinned#``)
- prefer same as input bytearray when allocating new array (``resizeMutableByteArrayKeepPinnedness#``)
- ensure the output array is aligned and pinned after operation (``resizeMutableByteArrayMakeAlignedPinned#``)
-
0c17201c
by Sylvain Henry at 2026-10-22T21:38:23-04:00
JS: implement performMinorGC and performBlockingMajorGC (#28012)
GHC.Internal.System.Mem imports three C entry points: performMajorGC,
performGC (for performMinorGC) and performBlockingMajorGC. The JS RTS
only defined h$performMajorGC, so calling performMinorGC or
performBlockingMajorGC threw "ReferenceError: h$performGC is not
defined" at runtime.
The JS RTS has a single non-generational collector, so both missing
entry points are now aliases of h$performMajorGC.
Add a test that calls every GC entry point of System.Mem.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
-
7a631ff2
by L0neGamer at 2026-10-23T09:11:39-04:00
Mon(a|oi)d of No (Return|Mappend) progress (!15795)
Make non-canonical definitions of return and mappend
non-suppressible errors, and make canonical definitions
of those methods warnings.
CLC proposal: https://github.com/haskell/core-libraries-committee/issues/328
-
14656fd4
by Simon Jakobi at 2026-10-23T09:12:27-04:00
Cmm: keep the CBE substitution idempotent
When blocks are merged over multiple rounds during common block
elimination, a block that won a merge in an earlier round can still
lose a merge in a later round. The substitution map (Subst) used to
record this as a chain: the earlier loser mapped to a block that was
itself eliminated. In PLT terminology, the substitution became
*non-idempotent*. Applying such a substitution with a single lookup
left edges to unreachable blocks in the Cmm graph, eventually leading
to a setInfoTableStackMap panic (#27368).
With this patch, each round's fresh substitutions are applied to the old
ones as they are added (extendSubst), so every eliminated block maps
directly to its final replacement and the substitution stays idempotent.
Fixes #27368.
Assisted-by: Claude Fable 5.1
-
92848e61
by Evgeny Malyshev at 2026-10-23T20:26:04-04:00
hadrian: Include transitive dependencies in Haddock builds
Haddock needs indirect dependency interfaces to resolve references to
definitions outside a package's direct dependencies. Expose Cabal's
configured dependency closure through ContextData and use it alongside
the direct list, preserving direct-interface precedence.
Bump Shake's cache version for the serialized oracle change. Add a
Hadrian selftest requiring template-haskell's indirect ghc-prim interface.
Fixes #25850.
Fixes #27786.
Assisted-By: OpenAI Codex
Metric Increase:
haddock.Cabal
-
5e8ae81b
by Zubin Duggal at 2026-10-23T20:26:47-04:00
WinIO: Don't confuse Win32 errors and NTSTATUS codes
WinIO misinterpreted NTSTATUS codes as Win32 errors, and had some ad hoc logic to deal with this.
However, this logic was incomplete and resulted in the mishandling of results of certain I/O operations
(see testcase).
Now the `ErrCode` argument in `CompletionCallback` is always the Win32 error code,
and we convert the NTSTATUS using RtlNtStatusToDosError.
Also when an overlapped/async operation such as ReadFile fails immediately,
with a Win32 error such as ERROR_BROKEN_PIPE, we don't get a further completion
notification, so we never recorded the failure. This worked by coincidence
earlier, because `STATUS_PENDING` happened to have the same numerical code as
`ERROR_NO_MORE_ITEMS`, but it doesn't now that we translate `STATUS_PENDING`
into the correct Win32 error code (`ERROR_IO_PENDING`). Fix this by using
the Win32 error value we obtained when an operation returns `STATUS_PENDING`.
Fixes #27971
-
c42ed66a
by Martijn Bastiaan at 2026-10-24T16:00:42+02:00
Add single-threaded test for #27526 + #27123
Issue #27526 is a duplicate of #27123, which has already been fixed in
HEAD. The proposed patch in the former carries a "nicer" regression test
in that it doesn't rely on multi-threading to trigger.
Closes #27526
-
7f73f515
by Simon Peyton Jones at 2026-10-24T17:29:19-04:00
Refactor QuickLook a bit
This MR simply refactors QuickLook a bit. In particular, it
simplifies `tcApp`, and moves shared code into `finishApp`.
It also adds `Note [QuickLook: arguments before result]`
responding to #26599.
There should be no user-visible changes, except perhaps in error messages,
which improve marginally.
For some reason T24984 compiles 8% faster. I'll take the win!
Metric Decrease:
T24984
-
a4a3a964
by Alan Zimmerman at 2026-10-24T17:30:03-04:00
EPA: Use AnnList for XClosedTypeFamily
We now have TTG annotations for FamilyInfo.
Make use of it to store the AnnList annotations for a closed type
family.
-
953e4c7f
by sheaf at 2026-10-26T18:19:10-04:00
Rework LogQueue to be a proper queue
Now 'LogQueue' uses a 'TQueue' with a separate 'TVar' to indicate
whether the log queue is finished logging, instead of the previous weird
data representation using a reverse-order '[Maybe Msg]' with 'Nothing'
denoting the end of the queue.
-
ec29c5fa
by Rodrigo Mesquita at 2026-10-26T18:19:10-04:00
Refactor GHC.Driver.MakeAction
Pull out of 'runParPipelines' the logic for creating a 'MakeEnv' ready
to be used by multiple threads, as that will be useful for downsweep as
well (which doesn't fit the 'runPipelines' flow), rather than being just
for upsweep.
The code is moved and re-structured to match the export list, simplify
the sequentiality checks previously both in 'runPipelines' and
'runAllPipelines', which were weirdly similar and confusing; into the
two part step where we need these checks: (1) to construct the MakeEnv,
(2) to run the MakeActions in parallel. These two steps are separate and
used to be too mixed up.
Some additional little simplifications or clean ups here and there.
-
3fb22073
by Rodrigo Mesquita at 2026-10-26T18:19:11-04:00
Refactor GHC.Driver.Downsweep in preparation for parallel downsweep
Pure refactor to improve the code to facilitate implementing parallel
downsweep in the next commit.
This commit puts a MakeEnv into the DownsweepEnv, gives the fields
proper names and uses RecordWildcards to simplify, rather than passing
around all diagnostic wrappers, driver-message-things and using 10s of
positional fields.
No behavior changes here!
-
52f385a7
by sheaf at 2026-10-26T18:19:11-04:00
Driver: enableCodeGenForTH in load', not downsweep
enableCodeGenForTH modifies the module graph for planning, creating
fresh temporary paths. So it should only be run when we are about to
execute a build plan (e.g. in load'), so that consumers that want to
know facts about the module graph don't go looking for files that don't
exist yet (in particular 'haddock --no-compilation').
-
82402d14
by sheaf at 2026-10-26T18:19:11-04:00
2-phase Cache/Search Finder monad
This commit restructures the finder abstraction by introducing the
'FinderM' monad, which splits module lookup operations into two phases:
- a cache-only phase, performing no filesystem access,
- from the first cache miss onwards, a search action which may access
the filesystem.
This allows consumers to distinguish between quick cached results versus
more expensive filesystem search operations.
-
5c302b40
by sheaf at 2026-10-26T18:19:11-04:00
Separate Home/External finder caches
The caches for home modules and modules from external units have
different lifetimes, needing to be invalidated at different points.
This commit moves the cache for modules from external units into the
'UnitState' type, which keeps the cache correct whenever package flags
or package databases change (setSessionDynFlags, :set -package ...,
Backpack's addUnit).
This also makes it easier to clear the cache of home modules, as they
are now stored separately (obviating the 'isUnitEnvInstalledModule'
check we used to perform).
-
605fbb96
by sheaf at 2026-10-26T18:19:11-04:00
Add known home modules to the finder cache
The finder looks in the file system for the on-disk source that
corresponds to a Haskell module name. There are two kinds of modules
where we need to bypass this search:
- a file passed directly in the command line, which may not be in any
search path and whose module name may differ from the file name;
- Backpack signatures and modules, which do not exist as files at all.
This commit handles these two situations by adding an immutable
'KnownHomeModules' in the finder cache, which is always consulted first
before doing any search.
This removes the ad-hoc imperative logic which relied on mutating the
finder cache at careful points.
'setModuleGraph' now also takes the known home modules, so that the
module graph and the known home modules are always set consistently.
See Note [Known home modules] in GHC.Unit.Finder.Types.
-
82718f44
by sheaf at 2026-10-26T18:19:11-04:00
Make 'UnresolvedImport' into a suitable cache key
The goal of this commit is to make 'UnresolvedImport' suitable for use
as a cache key. Specifically, during downsweep we want to be able to
cache module resolution queries. We want to do this using some kind of
map, e.g. (roughly speaking):
Map UnresolvedImport ResolvedImport
For this to work, 'UnresolvedImport' must contain **precisely** the
input data to each resolution computation. In other words:
1. If something changes that materially affects resolution, it must
be stored in the cache key.
2. If changing some input does not materially affect the outcome,
then it must not be stored in the cache key.
To satisfy (1), we change 'UnitNode' to also keep track of the home
unit in whose unit state the unit's dependencies were looked up.
For (2), 'ImportResolution' now stores a 'ModuleLookupScope' instead of an
'UnresolvedImportOrigin'. Plugin imports are expressed with a new
'LookupPlugin' scope. The source location of an import, which is
immaterial to the resolution, is stored out of band outside
'UnresolvedImport', in 'ModSummary'.
-
d4635c7c
by sheaf at 2026-10-26T18:19:11-04:00
Add GHC.Data.Dependent
This commit adds a minimal implementation of dependent sums
('Some', 'DSum') and dependent maps ('DMap').
The implementation is not entirely performance optimal, as there are a
few extraneous allocations compared to the full-blown implementation in
the 'dependent-map' package. For our use case (caching downsweep
computations), this does not matter much.
See GHC.Data.Dependent.
-
6759533e
by sheaf at 2026-10-26T18:19:11-04:00
Driver: structured concurrent worker abstraction
This commit introduces a structured concurrency framework in the style
of the 'ki' library: a collection of threads within a scope.
We implement two kinds of concurrent workers on top of this framework:
- Independent workers cannot wait for one another at all. The only
scheduling operation is to wait for quiescence.
- Coordinating workers declare an STM readiness condition (waiting on
other workers to complete) which gates their start.
See Note [Deterministic concurrent workers] in GHC.Driver.Concurrency.
This commit ports upsweep to this new framework, with downsweep being
left as subsequent work.
Further changes along the way:
- Refactoring of how concurrency is acquired to avoid the footgun of
trying to use a no-op 'AbstractSem' as a lock in the serial case.
- The "re-run with -j1" logic for semaphore opening errors no longer
triggers on late semaphore failures (part-way through a lengthy
computation).
- Logger threads are properly cleaned up on exception, with each
concurrent worker's log queue and local TmpFs properly bracketed.
- The 'GhcMessage -> AnyGhcDiagnostic' and 'Maybe Messager'
arguments of 'depanalE', 'depanalPartial' and 'downsweep', which
were all dead in practice, have been dropped.
- Concurrent logging has been completely overhauled. Each worker has
its own log queue. Workers whose logs are printed as we go are
spawned in the order in which their logs must be printed, now using
a TQueue instead of the old complicated LogQueueQueue mechanism.
'logThread' is replaced by 'withLogPrinter', which runs the thread
that prints the log queues within a concurrent worker scope.
These changes allow us to fix issues with the exception handling:
we now always flush remaining log queues before exiting (even when
the action throws an exception), and exceptions that arise while
printing are properly rethrown. This fixes #28009.
-
38a99006
by sheaf at 2026-10-26T18:19:11-04:00
Rule-based deterministic concurrent downsweep
This commit rewrites downsweep as a single query-answering rule
(see 'DownsweepRule') that can be executed by concurrent worker threads.
The design allows every expensive operation (preprocessing files with CPP,
parsing headers, reading interfaces) to be performed concurrently
according to the -j<N>/-jsem flags.
See Note [Rules-based downsweep] in GHC.Driver.Downsweep.
The rules are run by "GHC.Driver.Concurrency.runRules" in a demand-driven
way: each worker can demand other workers to run, but never wait on
another worker. Every piece of work is done at most once.
Fixes #27514
Memory usage can increase due to doing more work concurrently:
-------------------------
Metric Increase:
MultiComponentModulesRecomp
MultiComponentModulesRecomp100
mhu-perf
-------------------------
-
9fa7b7be
by sheaf at 2026-10-26T18:19:11-04:00
Acquire/release semaphore tokens more readily
This commit reworks GHC.Driver.MakeSem to use the scoped worker
abstraction of GHC.Utils.Concurrent.Scope. This allows us to get rid of
a lot of the tricky logic in GHC.Driver.MakeSem involving manual
exception handling, thread spawning and lifetime management, etc.
The main user-visible change is that token acquisition is no longer
rate limited (due to a bug, there previously was a 1s rate limit on
acquisition of semaphore tokens), with the release debounce period
shortened from 1s to 10ms.
Other changes:
- With -jsem, the number of capabilities is now capped at the number
of processors, bringing it in line with the -jN behaviour.
- Pending jobs whose wait for a token is interrupted by an exception
are now withdrawn from the jobserver, instead of acquiring a token
that it hung onto without doing useful work.
Fixes #27763
-
c83c6733
by Alan Zimmerman at 2026-10-26T18:19:55-04:00
EPA: Update AnnClsInstDecl to contain AnnList
It was already separately tracking the '{', '}' and leading ';' values.
This aligns it with layout-introducing parser productions ('vocurly'),
so the layout processing can be handled uniformly
-
4448da04
by Simon Peyton Jones at 2026-10-29T12:42:39+00:00
Redesign the noinline magic
Main idea:
* When simplifying `noinline (f e1 e2)`, switch off inlining when simplifying `f`
* Mechanism: see `simplOutId`, the `noinlineIdKey` case
* If we see `noinline (f e1) e2`, the simplifier moves the `e2` inside; see the `splitContArgs` call.
This is important so you can write `noinliine f e1 e2` and have it mean the same as `noinline (f e1 e2)`.
* Now `f` sees all its arguments, henc:
* Fixes #27724
* Fixes #23911
* Also fixes #27803 where we were not respecting `noinline` if the RHS
was trivial
See Note [noinlineId magic] for lots of specifics. It's surprisingly tricky!
For some reason a couple of perf tests allocate 1.2% less at compile time,n
namely T29872 and T8095. Geom mean decease of 0.1%
Metric Decrease:
T9872d