[Git][ghc/ghc] Pushed new branch wip/TTG-ModuleName
by recursion-ninja (@recursion-ninja) 18 Aug '26
by recursion-ninja (@recursion-ninja) 18 Aug '26
18 Aug '26
recursion-ninja pushed new branch wip/TTG-ModuleName at Glasgow Haskell Compiler / GHC
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/tree/wip/TTG-ModuleName
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/az/exactprint-annotation-rationalisation] 48 commits: loopImports: Don't dup ms_uid in summary imports
by Alan Zimmerman (@alanz) 18 Aug '26
by Alan Zimmerman (@alanz) 18 Aug '26
18 Aug '26
Alan Zimmerman pushed to branch wip/az/exactprint-annotation-rationalisation at Glasgow Haskell Compiler / GHC
Commits:
c130188d by Rodrigo Mesquita at 2026-08-15T06:29:53-04:00
loopImports: Don't dup ms_uid in summary imports
We were writing the ms_unitid of the mod summary with every single
import of that module
That complicated the code (as though the UnitId in that list could ever
be something else) and also allocates unnecessarily per every mod
import. Very slight allocation decrease measured locally in a few tests:
(MultiComponentModulesRecomp: -0.06%; MultiComponentModulesRecomp100: -0.05%)
Purely a clean up.
- - - - -
c71166a8 by Rodrigo Mesquita at 2026-08-15T06:29:53-04:00
downsweep: make control flow simpler and cache correct
This refactor extracts the control flow of downsweep into a single
function `dfsBuild`, which takes care of iteratively expanding and
traversing all nodes of the in-construction module graph necessary to
build a full `ModuleGraph`.
There are three levels of caching going on, all of which are necessary
to make sure we don't do repeated work (notably, NEVER summarise the
same module twice).
1. `dfsBuild` accumulates the final module graph and never revisits the
same node of the module graph. Cache is keyed by the final
`ModuleGraph`s `NodeKey`s.
2. For Module A in home-unit u1, each import in the list of imports
needs to be *found* (call to `findImportedModuleWithIsBoot`): at this
point, we only have the `ModuleName` of the import, not the `Module`.
This *finding* is somewhat expensive, so we cache it as well
(`ImportsCache`). The cache key is the home-unit to which the module
belongs~[1], the import package qualifier, and the ModuleName.
[1] Different home-units will have different package flags, which means
potentially different `Module` resolution for the same `ModuleName`.
3. The most expensive operation we want to avoid is summarising a
`Module` into a `ModSummary`, which notably involves parsing the
module header from scratch.
The third cache, in essence, maps a `Module` to its `ModSummary`
(named `ModSummaryCache`). This cache upholds the invariant: we NEVER
summarise the same module twice. In practice, the cache key is the
Module's UnitId and the Source path; the reason is we need to
distinguish between `.hs` and `.hs-boot` files, as their summaries
will differ.
Note that (2) can't guarantee this alone: Two ModuleName imports in
separate units can (and likely do) map to the same `Module`.
Note that the previous implementation failed to achieve the
no-duplicate-work summarisation invariant, and we ended up doing a
quadratic amount of processing in scenarios like test
`MultiComponentModules100`.
See also Note [Downsweep Control Flow and Caching]
Fixes #27461
Perf changes:
MultiComponentModules(normal) ghc/alloc 2,097,389,264 1,992,186,736 -5.0% GOOD
MultiComponentModules100(normal) ghc/alloc 24,310,173,770 21,293,867,360 -12.4% GOOD
MultiComponentModulesRecomp(normal) ghc/alloc 602,761,394 498,543,984 -17.3% GOOD
MultiComponentModulesRecomp100(normal) ghc/alloc 11,885,968,240 8,895,404,864 -25.2% GOOD
-------------------------
Metric Decrease:
MultiComponentModules
MultiComponentModules100
MultiComponentModulesRecomp
MultiComponentModulesRecomp100
-------------------------
- - - - -
ebc4047b by Rodrigo Mesquita at 2026-08-15T06:29:53-04:00
implicitRequirementsShallow can never reach HoleUnit
findImportedModule will never return `HoleUnit` for a `ModuleName`
(a `HoleUnit` can only be found as a signature instantiation, never as a
directly *imported* thing)
Therefore, we can drop `[ModuleName]` returned by
`implicitRequirementsShallow`, which makes many things dead code.
Namely, the call to `implicitRequirementsShallow` from
GHC.Driver.Downsweep which was a performance bottleneck (for doing lots
of duplicate work in findImportedModule) is now entirely gone.
Fixes #27053
In an MR with this patch and the downsweep refactor (previous commit), CI says:
MultiComponentModules(normal) ghc/alloc 2,097,396,728 1,943,662,304 -7.3% GOOD
MultiComponentModules100(normal) ghc/alloc 24,310,182,136 17,227,574,440 -29.1% GOOD
MultiComponentModulesRecomp(normal) ghc/alloc 602,769,518 449,973,656 -25.3% GOOD
MultiComponentModulesRecomp100(normal) ghc/alloc 11,885,976,408 4,828,894,160 -59.4% GOOD
-------------------------
Metric Decrease:
MultiComponentModules
MultiComponentModules100
MultiComponentModulesRecomp
MultiComponentModulesRecomp100
-------------------------
- - - - -
85a6ab01 by Rodrigo Mesquita at 2026-08-15T06:29:53-04:00
downsweep: Cache negative results
When traversing a module graph structure, a uniquely identified node
should always expand to the same thing.
I don't see how visiting the same node which failed to be expanded a
first time would ever successfully expand the second time we try to
expand it (eg. when coming from a different edge to it -- it is still
the same node!). The node expansion is local, based just based on the
node itself, not on the path to get there.
Therefore, this patch removes the weird behavior and commentary of
`dfsBuild` wrt to `Nothing` not being cached and being potentially
expanded a second time around to something different, which was
misleading and, ultimately, incorrect.
Now, we have a `MGRes`, which is more explicit about a node being
Skipped just being a node that is ignored whenever it is found (and that
skip is cached) -- and we may want to do this due to failures or due to
just trying nodes which might not work on purpose, like hs-boots.
We uniformly cache positive and negative results and remove the
assumption that there might be an ordering in which the same node
visited at a later time might be expanded differently.
This makes it possible to traverse the module nodes in parallel without
a change in behavior, since there's no longer a hidden ordering
requirement.
- - - - -
56747c3f by Rodrigo Mesquita at 2026-08-15T06:29:53-04:00
Organize and clean-up GHC.Driver.Downsweep
Simply some cosmetic changes, moving definitions around to structure the
module better into its relevant sections
(In go (ns ++ ss), it's not a problem to use ++ because it's a good
producer and we won't have to append fully before processing the next
item in go)
- - - - -
12f64118 by Wolfgang Jeltsch at 2026-08-15T06:31:12-04:00
Add support for textual output of bytecode file content
- - - - -
a737df91 by Brandon Chinn at 2026-08-15T12:40:25-04:00
Add law in qualified strings doc
- - - - -
e3188581 by Zubin Duggal at 2026-08-15T12:41:06-04:00
DmdAnal: Fix maxDmdType
We need to eta expand the smaller DmdType using defaultArgDmd, like in lubDmdType.
Introduce zipDmdType as a common combinator to implement both maxDmdType and lubDmdType
uniformly.
fixes #27626
- - - - -
ca9b0b22 by mangoiv at 2026-08-15T12:41:47-04:00
hadrian: set the executable bit for hie-bios.bat
- - - - -
1aac7095 by sheaf at 2026-08-16T04:37:16-04:00
Avoid wasteful allocations in mkTyConAppCo
The idiom "traverse isReflCo_maybe" followed by "map fst" used in
'GHC.Core.Coercion.mkTyConAppCo' was allocating a lot of waste.
This commit uses 'GHC.Data.Unboxed.traverseMaybeUB' to avoid all these
unnecessary intermediate allocations.
In a quick microbenchmark for 'mkTyConAppCo', this change resulted in:
- refl case (all argument coercions are reflexive):
- -60% runtime
- -80% allocations
- non-refl case:
- from 0% to -12% runtime (depending on which argument is non-refl)
- from 0% to -70% allocations ( -- '' -- )
Fixes #27648
-------------------------
Metric Decrease:
FamAppCachePerf
SimplCastPerf
T12425
T15703
T26426
T3064
T9872a
T9872b
T9872b_defer
T9872c
T9872d
T5321Fun
T9020
T9630
TcPlugin_RewritePerf
Metric Increase:
LinkableUsage02
-------------------------
- - - - -
3915e982 by Alan Zimmerman at 2026-08-16T04:37:58-04:00
EPA: Remove al_trailing from AnnList
It was not being used
- - - - -
fed942ac by Andreas Klebinger at 2026-08-17T12:11:57-04:00
testsuite: Use sigkill in process009.
SIGHUB might have been blocked by a (grand)*parent of the test.
In such cases the test would fail as the python process would simply
exist gracefully instead of committing to a premature end.
By using SIGKILL we can rely on the signal not being blocked, fixing #27578
in the process.
- - - - -
574c875f by Andreas Klebinger at 2026-08-17T12:12:39-04:00
Simplify comparison in DFM.hs
Fixes #27669
- - - - -
d8f1a2a3 by Alan Zimmerman at 2026-08-17T12:13:18-04:00
TTG: Add extension points to BooleanFormula
They are currently unused, but will be used for exact print
annotations next, allowing us to get rid of LocatedBF / SrcSpanAnnBF
- - - - -
9a82d258 by Alan Zimmerman at 2026-08-17T19:25:52+01:00
EPA: Remove LocatedBC / SrcSpanBF
The custom annotations are now in the BooleanFormula TTG extension
points, so LBooleanFormula can now use the standard LocatedA.
- - - - -
c58f6867 by Alan Zimmerman at 2026-08-17T19:26:59+01:00
EPA: remove unused addTrailingAnnToL. Squash appropriately
- - - - -
0feb735d by Alan Zimmerman at 2026-08-17T19:26:59+01:00
EPS: Remove NoEpTok/NoEpUniTok, using an unhelpful SrcSpan instead
Also introduce helper functions noEpTok and noEpUniTok to serve
as simple replacements in code inserting an token annotation without
location information.
- - - - -
d3af13a3 by Alan Zimmerman at 2026-08-17T19:26:59+01:00
EPA: Some haddock processing tweaks
- - - - -
0feaf34d by Alan Zimmerman at 2026-08-17T19:26:59+01:00
Some haddock exactprint tests
- - - - -
243430f8 by Alan Zimmerman at 2026-08-17T19:26:59+01:00
EPA: When adding comments honour trailing anns
- - - - -
d410e63c by Alan Zimmerman at 2026-08-17T19:26:59+01:00
EPA: Uses Parsers.parseModule for exactprint tests
This is the advertised way to parse for use for exact printing in the
ghc-exactprint library, make sure we test using it.
- - - - -
ea7a19f3 by Alan Zimmerman at 2026-08-17T19:26:59+01:00
EPA Fix HsCmdDo exact print with comments
TODO: add test based on proc-do-complex-four-out.hs
- - - - -
3ed851b0 by Alan Zimmerman at 2026-08-17T19:26:59+01:00
EPA: Add comments about remaining Anno SrcSpan instances
- - - - -
3aa090d6 by Alan Zimmerman at 2026-08-17T19:26:59+01:00
EPA: Plan for Fixing AnnList Layout Properly
- - - - -
74849f3e by Alan Zimmerman at 2026-08-17T19:26:59+01:00
EPA: Remove ListBanana / ListParens from AnnListBrackets
- - - - -
19b5c3d2 by Alan Zimmerman at 2026-08-17T19:26:59+01:00
EPA: Tidy up mkHsDoPv, take AnnList directly
And some extra stuff by accident.
The main goal is to slim down AnnListBrackets to just have braces or
none.
This makes it clear that it serves only for lists which may have layout,
and opens the route to use EpLayout instead of AnnListBrackets
- - - - -
835ebf03 by Alan Zimmerman at 2026-08-17T19:26:59+01:00
EPA: Add specific layout field to AnnList
- - - - -
94b8264a by Alan Zimmerman at 2026-08-17T19:26:59+01:00
EPA: Use EpaLocation in EpVirtualBraces
reword:
EPA Use vocurly as basis for AnnListLayout
For the existing cases that populate AnnList.
This also shows up AnnList usages that can never capture layout.
- - - - -
ef1b285b by Alan Zimmerman at 2026-08-17T19:26:59+01:00
EPA Use vocurly as basis for AnnListLayout
For the existing cases that populate AnnList.
This also shows up AnnList usages that can never capture layout.
- - - - -
336af5b0 by Alan Zimmerman at 2026-08-17T19:26:59+01:00
EPA: AnnList clean up patch for empty where clause
- - - - -
959ab6e3 by Alan Zimmerman at 2026-08-17T19:26:59+01:00
EPA: Add TTG fields to FamilyInfo
This commit is a simple add, with unused extension fields.
It lays the groundwork for using them for exact print annotations
- - - - -
808b9851 by Alan Zimmerman at 2026-08-17T19:26:59+01:00
WIP annclassdecl
- - - - -
28589c84 by Alan Zimmerman at 2026-08-17T19:26:59+01: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
- - - - -
b0b3106c by Alan Zimmerman at 2026-08-17T19:26:59+01:00
EPA: Tidy up markAnnListA'
It takes an action which can modify the AnnList, but this
is never used. So remove it.
- - - - -
acd033f2 by Alan Zimmerman at 2026-08-17T19:26:59+01:00
EPA: Use AnnList for GADT declarations
- - - - -
e2509e78 by Alan Zimmerman at 2026-08-17T19:26:59+01:00
EPA: Use AnnList in AnnClassDecl
- - - - -
8444d44e by Alan Zimmerman at 2026-08-17T19:37:00+01:00
EPA: Make local binds located
Use LHsLocalBinds instead of HsLocalBinds
This reverses an earlier change, and brings processing of local binds
into alignement with the exact printing principle that the LocatedA
contents provide the location and extent of the enclosed item,
together with any comments or context-specific trailing items like
commas or semi colons.
The internal TTG extension points only carry exact print annotations
related to printing the item within its bounds.
This change brings back the problem of EmptyLocalBinds, which by
definition cannot have a location. These get a noSrcSpan location,
which is ignored in the exact printing process.
- - - - -
8f2ad624 by Alan Zimmerman at 2026-08-17T19:37:00+01:00
EPA: Use AnnList in HsMultiIf
It introduces layout, capture it
- - - - -
2618238b by Alan Zimmerman at 2026-08-17T19:37:00+01:00
EPA: Use AnnList in DecBrl
- - - - -
a0b0d98d by Alan Zimmerman at 2026-08-17T21:39:38+01:00
EPA: Replace AnnListLayout / AnnListBrackets with EpLayout
- - - - -
51af2cd1 by Alan Zimmerman at 2026-08-17T23:27:08+01:00
WIP: setLayoutBoth inside markAnnListA
Current problem is it is a global one-shot, and MG has one too
Check: is it needed?
if so, honouring EpLayout may be needed
- - - - -
b2876e77 by Alan Zimmerman at 2026-08-17T23:27:08+01:00
Update AnnList plan. do not put into MR
- - - - -
c7ff8d0a by Alan Zimmerman at 2026-08-17T23:27:08+01:00
EPA: First pass implementation of HsList, for ClassDecls
Just as a straight list replacement to start with, no payload.
This shows the scope and invasiveness of the initial change
- - - - -
39b59224 by Alan Zimmerman at 2026-08-17T23:27:08+01:00
EPA: HsList attempt WIP
- - - - -
dd2560bf by Alan Zimmerman at 2026-08-17T23:27:08+01:00
Enable ppr test for Haddock1. It currently fails
- - - - -
91918304 by Alan Zimmerman at 2026-08-17T23:27:08+01:00
WIP on removing NoEpAnn. Likely abandon
- - - - -
0b77f7c3 by Alan Zimmerman at 2026-08-17T23:27:08+01:00
EPA: Add an overview doc for exact printing
- - - - -
57acfce1 by Simon Peyton Jones at 2026-08-17T23:27:08+01:00
Added an intro section
- - - - -
153 changed files:
- + ANNLIST-LAYOUT-PLAN.md
- + ExactPrint.md
- + changelog.d/27626
- + changelog.d/downsweep-refactor
- + changelog.d/show-byte-code
- compiler/GHC/ByteCode/Serialize.hs
- + compiler/GHC/ByteCode/Show.hs
- compiler/GHC/Core/Class.hs
- compiler/GHC/Core/Coercion.hs
- compiler/GHC/CoreToIface.hs
- compiler/GHC/Data/BooleanFormula.hs
- compiler/GHC/Data/Unboxed.hs
- compiler/GHC/Data/Word64Map/Internal.hs
- compiler/GHC/Data/Word64Map/Lazy.hs
- compiler/GHC/Data/Word64Map/Strict.hs
- compiler/GHC/Data/Word64Map/Strict/Internal.hs
- compiler/GHC/Driver/Backpack.hs
- compiler/GHC/Driver/Downsweep.hs
- compiler/GHC/Driver/Env.hs
- compiler/GHC/Driver/Main/Interactive.hs
- compiler/GHC/Hs.hs
- compiler/GHC/Hs/Binds.hs
- compiler/GHC/Hs/Decls.hs
- compiler/GHC/Hs/Doc.hs
- compiler/GHC/Hs/DocString.hs
- compiler/GHC/Hs/Dump.hs
- compiler/GHC/Hs/Expr.hs
- compiler/GHC/Hs/Extension/Pass.hs
- compiler/GHC/Hs/ImpExp.hs
- compiler/GHC/Hs/Instances.hs
- compiler/GHC/Hs/Pat.hs
- compiler/GHC/Hs/Stats.hs
- compiler/GHC/Hs/Utils.hs
- compiler/GHC/HsToCore/Arrows.hs
- compiler/GHC/HsToCore/Expr.hs
- compiler/GHC/HsToCore/Expr.hs-boot
- compiler/GHC/HsToCore/Match.hs
- compiler/GHC/HsToCore/Pmc/Desugar.hs
- compiler/GHC/HsToCore/Quote.hs
- compiler/GHC/HsToCore/Ticks.hs
- compiler/GHC/Iface/Ext/Ast.hs
- compiler/GHC/Iface/Syntax.hs
- compiler/GHC/IfaceToCore.hs
- compiler/GHC/Parser.y
- compiler/GHC/Parser/Annotation.hs
- compiler/GHC/Parser/PostProcess.hs
- compiler/GHC/Parser/PostProcess/Haddock.hs
- compiler/GHC/Rename/Bind.hs
- compiler/GHC/Rename/Expr.hs
- compiler/GHC/Rename/Module.hs
- compiler/GHC/Rename/Splice.hs
- compiler/GHC/Rename/Utils.hs
- compiler/GHC/Runtime/Eval.hs
- compiler/GHC/Tc/Deriv/Functor.hs
- compiler/GHC/Tc/Deriv/Generate.hs
- compiler/GHC/Tc/Gen/Arrow.hs
- compiler/GHC/Tc/Gen/Expr.hs
- compiler/GHC/Tc/Gen/Match.hs
- compiler/GHC/Tc/Module.hs
- compiler/GHC/Tc/TyCl.hs
- compiler/GHC/Tc/TyCl/Class.hs
- compiler/GHC/Tc/TyCl/PatSyn.hs
- compiler/GHC/Tc/Types/Origin.hs
- compiler/GHC/Tc/Utils/Backpack.hs
- compiler/GHC/Tc/Zonk/Type.hs
- compiler/GHC/ThToHs.hs
- compiler/GHC/Types/Demand.hs
- compiler/GHC/Types/Unique/DFM.hs
- compiler/GHC/Unit/Env.hs
- compiler/Language/Haskell/Syntax.hs
- compiler/Language/Haskell/Syntax/Basic.hs
- compiler/Language/Haskell/Syntax/BooleanFormula.hs
- compiler/Language/Haskell/Syntax/Decls.hs
- compiler/Language/Haskell/Syntax/Expr.hs
- compiler/Language/Haskell/Syntax/Extension.hs
- compiler/ghc.cabal.in
- docs/users_guide/exts/qualified_strings.rst
- docs/users_guide/using.rst
- ghc/GHC/Driver/Session/Mode.hs
- ghc/GHCi/UI.hs
- ghc/Main.hs
- hadrian/hie-bios.bat
- testsuite/tests/count-deps/CountDepsParser.stdout
- + testsuite/tests/dmdanal/should_run/M2.hs
- + testsuite/tests/dmdanal/should_run/T27626.hs
- + testsuite/tests/dmdanal/should_run/T27626.stdout
- testsuite/tests/dmdanal/should_run/all.T
- + testsuite/tests/driver/T27461/Main1.hs
- + testsuite/tests/driver/T27461/Main2.hs
- + testsuite/tests/driver/T27461/Makefile
- + testsuite/tests/driver/T27461/T27461a.stderr
- + testsuite/tests/driver/T27461/T27461b.script
- + testsuite/tests/driver/T27461/T27461b.stderr
- + testsuite/tests/driver/T27461/T27461b.stdout
- + testsuite/tests/driver/T27461/all.T
- + testsuite/tests/driver/T27461/src/Bar.hs
- testsuite/tests/ghc-api/T25121_status.stdout
- testsuite/tests/ghc-api/exactprint/T22919.stderr
- testsuite/tests/ghc-api/exactprint/Test20239.stderr
- testsuite/tests/ghc-api/exactprint/ZeroWidthSemi.stderr
- testsuite/tests/ghc-api/fixed-nodes/FixedNodes.hs
- testsuite/tests/ghc-api/fixed-nodes/InterfaceModuleGraph.hs
- testsuite/tests/ghc-api/fixed-nodes/ModuleGraphInvariants.hs
- testsuite/tests/haddock/should_compile_flag_haddock/T17544.stderr
- testsuite/tests/haddock/should_compile_flag_haddock/T17544_kw.stderr
- testsuite/tests/haddock/should_compile_flag_haddock/T24221.stderr
- testsuite/tests/module/mod185.stderr
- testsuite/tests/parser/should_compile/DumpParsedAst.stderr
- testsuite/tests/parser/should_compile/DumpParsedAstComments.stderr
- testsuite/tests/parser/should_compile/DumpRenamedAst.stderr
- testsuite/tests/parser/should_compile/DumpSemis.stderr
- testsuite/tests/parser/should_compile/DumpTypecheckedAst.stderr
- testsuite/tests/parser/should_compile/KindSigs.stderr
- testsuite/tests/parser/should_compile/T14189.stderr
- testsuite/tests/parser/should_compile/T15279.stderr
- testsuite/tests/parser/should_compile/T15323.stderr
- testsuite/tests/parser/should_compile/T20452.stderr
- testsuite/tests/parser/should_compile/T20718.stderr
- testsuite/tests/parser/should_compile/T20718b.stderr
- testsuite/tests/parser/should_compile/T20846.stderr
- testsuite/tests/parser/should_compile/T23315/T23315.stderr
- testsuite/tests/perf/compiler/hard_hole_fits.stderr
- testsuite/tests/printer/AnnotationNoListTuplePuns.stdout
- + testsuite/tests/printer/Haddock1.hs
- testsuite/tests/printer/Makefile
- testsuite/tests/printer/T18791.stderr
- testsuite/tests/printer/Test20297.stdout
- testsuite/tests/printer/Test24533.stdout
- testsuite/tests/printer/all.T
- testsuite/tests/process/process009.hs
- testsuite/tests/process/process009.stdout
- + testsuite/tests/show-bytecode/Example.hs
- + testsuite/tests/show-bytecode/Makefile
- + testsuite/tests/show-bytecode/all.T
- + testsuite/tests/show-bytecode/normalize
- + testsuite/tests/show-bytecode/show-bytecode-breakpoints.stdout
- + testsuite/tests/show-bytecode/show-bytecode-breakpoints.stdout-javascript-unknown-ghcjs
- + testsuite/tests/show-bytecode/show-bytecode-hpc.stdout
- + testsuite/tests/show-bytecode/show-bytecode-vanilla.stdout
- + testsuite/tests/show-bytecode/show-bytecode-vanilla.stdout-javascript-unknown-ghcjs
- testsuite/tests/splice-imports/SI35.hs
- utils/check-exact/ExactPrint.hs
- utils/check-exact/Main.hs
- utils/check-exact/Parsers.hs
- utils/check-exact/Transform.hs
- utils/check-exact/Utils.hs
- utils/check-ppr/Main.hs
- utils/haddock/haddock-api/src/Haddock/Backends/Hoogle.hs
- utils/haddock/haddock-api/src/Haddock/Backends/LaTeX.hs
- utils/haddock/haddock-api/src/Haddock/Backends/Xhtml/Decl.hs
- utils/haddock/haddock-api/src/Haddock/Convert.hs
- utils/haddock/haddock-api/src/Haddock/Interface/Rename.hs
- utils/haddock/haddock-api/src/Haddock/Types.hs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/c5fc327eca1bc6a7853ed4f2d20e0c…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/c5fc327eca1bc6a7853ed4f2d20e0c…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/marge_bot_batch_merge_job] 5 commits: testsuite: Use sigkill in process009.
by Marge Bot (@marge-bot) 18 Aug '26
by Marge Bot (@marge-bot) 18 Aug '26
18 Aug '26
Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC
Commits:
fed942ac by Andreas Klebinger at 2026-08-17T12:11:57-04:00
testsuite: Use sigkill in process009.
SIGHUB might have been blocked by a (grand)*parent of the test.
In such cases the test would fail as the python process would simply
exist gracefully instead of committing to a premature end.
By using SIGKILL we can rely on the signal not being blocked, fixing #27578
in the process.
- - - - -
574c875f by Andreas Klebinger at 2026-08-17T12:12:39-04:00
Simplify comparison in DFM.hs
Fixes #27669
- - - - -
d8f1a2a3 by Alan Zimmerman at 2026-08-17T12:13:18-04:00
TTG: Add extension points to BooleanFormula
They are currently unused, but will be used for exact print
annotations next, allowing us to get rid of LocatedBF / SrcSpanAnnBF
- - - - -
c556ed58 by Andreas Klebinger at 2026-08-17T17:50:10-04:00
Fix a number of incorrect module references:
Fix module reference in Note [DataCon wrappers are conlike].
Fix module reference in Note [Detailed InertCans Invariants].
Fix module reference in Note [GHC's data format representations].
Fix module reference in Note [Grand plan for static forms].
Fix module reference in Note [How tuples work].
Fix module reference in Note [Solved dictionaries].
Fix module reference in Note [Stacktraces from Info Table Provenance Entries (IPE based stack unwinding)].
Fix module reference in Note [The VarBndr type and its uses].
Found the incorrect references with a llm.
- - - - -
bb344b5f by Simon Jakobi at 2026-08-17T17:50:11-04:00
ci: Run stack-hadrian-build only in full-ci pipelines
The job exists to catch changes that break hadrian/build-stack (#18726),
but nothing in the pipeline depends on it, and it can only break when
hadrian's dependencies change. Restricting it to full-ci (like
hadrian-multi) still covers marge-bot merge batches, so such breakage
cannot reach master unnoticed, while ordinary validate pipelines skip
the job.
Assisted-by: Claude Fable 5
- - - - -
29 changed files:
- .gitlab-ci.yml
- compiler/GHC/Builtin/WiredIn/Types.hs
- compiler/GHC/CmmToAsm/Format.hs
- compiler/GHC/Core/Class.hs
- compiler/GHC/CoreToIface.hs
- compiler/GHC/Data/BooleanFormula.hs
- compiler/GHC/Data/Word64Map/Internal.hs
- compiler/GHC/Data/Word64Map/Lazy.hs
- compiler/GHC/Data/Word64Map/Strict.hs
- compiler/GHC/Data/Word64Map/Strict/Internal.hs
- compiler/GHC/Driver/GenerateCgIPEStub.hs
- compiler/GHC/Iface/Ext/Ast.hs
- compiler/GHC/Iface/Syntax.hs
- compiler/GHC/Iface/Tidy/StaticPtrTable.hs
- compiler/GHC/IfaceToCore.hs
- compiler/GHC/Parser.y
- compiler/GHC/Tc/Solver/InertSet.hs
- compiler/GHC/Tc/TyCl/Class.hs
- compiler/GHC/Types/Id/Make.hs
- compiler/GHC/Types/Unique/DFM.hs
- compiler/GHC/Types/Var.hs
- compiler/Language/Haskell/Syntax/BooleanFormula.hs
- compiler/Language/Haskell/Syntax/Extension.hs
- testsuite/tests/process/process009.hs
- testsuite/tests/process/process009.stdout
- utils/check-exact/ExactPrint.hs
- utils/haddock/haddock-api/src/Haddock/Backends/Xhtml/Decl.hs
- utils/haddock/haddock-api/src/Haddock/Interface/Rename.hs
- utils/haddock/haddock-api/src/Haddock/Types.hs
Changes:
=====================================
.gitlab-ci.yml
=====================================
@@ -552,6 +552,8 @@ stack-hadrian-build:
- . .gitlab/ci.sh setup
- . .gitlab/ci.sh configure
- hadrian/build-stack --version
+ rules:
+ - *full-ci
####################################
# Testing reinstallable ghc codepath
=====================================
compiler/GHC/Builtin/WiredIn/Types.hs
=====================================
@@ -783,7 +783,7 @@ typeSymbolKind = mkTyConTy typeSymbolKindCon
Note [How tuples work]
~~~~~~~~~~~~~~~~~~~~~~
* There are three families of tuple TyCons and corresponding
- DataCons, expressed by the type BasicTypes.TupleSort:
+ DataCons, expressed by the type GHC.Types.Basic.TupleSort:
data TupleSort = BoxedTuple | UnboxedTuple | ConstraintTuple
* All three families are AlgTyCons, whose AlgTyConRhs is TupleTyCon
@@ -818,7 +818,7 @@ Note [How tuples work]
stores them in the context field of types like HsQualTy.
* In quite a lot of places things are restricted just to
- BoxedTuple/UnboxedTuple, and then we used BasicTypes.Boxity to distinguish
+ BoxedTuple/UnboxedTuple, and then we used GHC.Types.Basic.Boxity to distinguish
E.g. tupleTyCon has a Boxity argument
* When looking up an OccName in the original-name cache
=====================================
compiler/GHC/CmmToAsm/Format.hs
=====================================
@@ -52,20 +52,20 @@ import GHC.Utils.Panic
GHC has severals types that represent various aspects of data format.
These include:
- * 'CmmType.CmmType': The data classification used throughout the C--
+ * 'GHC.Cmm.Type.CmmType': The data classification used throughout the C--
pipeline. This is a pair of a CmmCat and a Width.
- * 'CmmType.CmmCat': What the bits in a C-- value mean (e.g. a pointer, integer, or floating-point value)
+ * 'GHC.Cmm.Type.CmmCat': What the bits in a C-- value mean (e.g. a pointer, integer, or floating-point value)
- * 'CmmType.Width': The width of a C-- value.
+ * 'GHC.Cmm.Type.Width': The width of a C-- value.
- * 'CmmType.Length': The width (measured in number of scalars) of a vector value.
+ * 'GHC.Cmm.Type.Length': The width (measured in number of scalars) of a vector value.
* 'Format.Format': The data format representation used by much of the backend.
- * 'Format.ScalarFormat': The format of a 'Format.VecFormat'\'s scalar.
+ * 'GHC.CmmToAsm.Format.ScalarFormat': The format of a 'GHC.CmmToAsm.Format.VecFormat'\'s scalar.
- * 'RegClass.RegClass': Whether a register is an integer or a floating point/vector register.
+ * 'GHC.Platform.Reg.Class.RegClass': Whether a register is an integer or a floating point/vector register.
-}
-- It looks very like the old MachRep, but it's now of purely local
=====================================
compiler/GHC/Core/Class.hs
=====================================
@@ -41,6 +41,7 @@ import GHC.Utils.Panic
import GHC.Types.SrcLoc
import GHC.Types.Var.Set
import GHC.Utils.Outputable
+import GHC.Data.BooleanFormula () -- for BooleanFormulaDefault instance
import Language.Haskell.Syntax.BooleanFormula ( BooleanFormula, mkTrue )
import qualified Data.Data as Data
=====================================
compiler/GHC/CoreToIface.hs
=====================================
@@ -542,10 +542,11 @@ toIfGuidance src guidance
toIfaceBooleanFormula :: BF.BooleanFormula GhcRn -> IfaceBooleanFormula
toIfaceBooleanFormula = go
where
- go (BF.Var nm ) = IfVar $ mkIfLclName . getOccFS . unLoc $ nm
- go (BF.And bfs ) = IfAnd $ map (go . unLoc) bfs
- go (BF.Or bfs ) = IfOr $ map (go . unLoc) bfs
- go (BF.Parens bf) = IfParens $ (go . unLoc) bf
+ go (BF.Var _ nm ) = IfVar $ mkIfLclName . getOccFS . unLoc $ nm
+ go (BF.And _ bfs ) = IfAnd $ map (go . unLoc) bfs
+ go (BF.Or _ bfs ) = IfOr $ map (go . unLoc) bfs
+ go (BF.Parens _ bf) = IfParens $ (go . unLoc) bf
+ go (BF.XBooleanFormula _) = panic "toIfaceBooleanFormula"
{-
************************************************************************
=====================================
compiler/GHC/Data/BooleanFormula.hs
=====================================
@@ -27,7 +27,10 @@ import GHC.Types.SrcLoc (unLoc)
import GHC.Utils.Outputable
import GHC.Parser.Annotation ( SrcSpanAnnBF )
import GHC.Hs.Extension (GhcPass (..), OutputableBndrId)
-import Language.Haskell.Syntax.Extension (Anno, LIdP, IdP)
+import Language.Haskell.Syntax.Extension (Anno, LIdP, IdP,
+ noExtField, NoExtField, DataConCantHappen,
+ XBFVar, XBFAnd, XBFOr, XBFParens,
+ XXBooleanFormula)
import Language.Haskell.Syntax.BooleanFormula
@@ -37,6 +40,17 @@ import Language.Haskell.Syntax.BooleanFormula
type instance Anno (BooleanFormula (GhcPass p)) = SrcSpanAnnBF
+type instance XBFVar (GhcPass _) = NoExtField
+type instance XBFAnd (GhcPass _) = NoExtField
+type instance XBFOr (GhcPass _) = NoExtField
+type instance XBFParens (GhcPass _) = NoExtField
+type instance XXBooleanFormula (GhcPass _) = DataConCantHappen
+
+instance BooleanFormulaDefault (GhcPass p) where
+ bfAnnAnd = noExtField
+ bfAnnOr = noExtField
+
+
-- if we had Functor/Traversable (LbooleanFormula p) we could use that
-- as a constraint and we wouldn't need to specialize to just GhcPass p,
-- but becuase LBooleanFormula is a type synonym such a constraint is
@@ -49,10 +63,10 @@ bfMap :: (LIdP (GhcPass p) -> LIdP (GhcPass p'))
-> BooleanFormula (GhcPass p) -> BooleanFormula (GhcPass p')
bfMap f = go
where
- go (Var a ) = Var $ f a
- go (And bfs) = And $ map (fmap go) bfs
- go (Or bfs) = Or $ map (fmap go) bfs
- go (Parens bf ) = Parens $ fmap go bf
+ go (Var x a ) = Var x $ f a
+ go (And x bfs) = And x $ map (fmap go) bfs
+ go (Or x bfs) = Or x $ map (fmap go) bfs
+ go (Parens x bf ) = Parens x $ fmap go bf
bfTraverse :: Applicative f
=> (LIdP (GhcPass p) -> f (LIdP (GhcPass p')))
@@ -60,10 +74,10 @@ bfTraverse :: Applicative f
-> f (BooleanFormula (GhcPass p'))
bfTraverse f = go
where
- go (Var a ) = Var <$> f a
- go (And bfs) = And <$> traverse @[] (traverse go) bfs
- go (Or bfs) = Or <$> traverse @[] (traverse go) bfs
- go (Parens bf ) = Parens <$> traverse go bf
+ go (Var x a ) = Var x <$> f a
+ go (And x bfs) = And x <$> traverse @[] (traverse go) bfs
+ go (Or x bfs) = Or x <$> traverse @[] (traverse go) bfs
+ go (Parens x bf ) = Parens x <$> traverse go bf
@@ -106,18 +120,18 @@ We don't show a ridiculous error message like
----------------------------------------------------------------------
isFalse :: BooleanFormula (GhcPass p) -> Bool
-isFalse (Or []) = True
+isFalse (Or _ []) = True
isFalse _ = False
isTrue :: BooleanFormula (GhcPass p) -> Bool
-isTrue (And []) = True
+isTrue (And _ []) = True
isTrue _ = False
eval :: (LIdP (GhcPass p) -> Bool) -> BooleanFormula (GhcPass p) -> Bool
-eval f (Var x) = f x
-eval f (And xs) = all (eval f . unLoc) xs
-eval f (Or xs) = any (eval f . unLoc) xs
-eval f (Parens x) = eval f (unLoc x)
+eval f (Var _ x) = f x
+eval f (And _ xs) = all (eval f . unLoc) xs
+eval f (Or _ xs) = any (eval f . unLoc) xs
+eval f (Parens _ x) = eval f (unLoc x)
-- Simplify a boolean formula.
-- The argument function should give the truth of the atoms, or Nothing if undecided.
@@ -125,12 +139,12 @@ simplify :: forall p. Eq (LIdP (GhcPass p))
=> (LIdP (GhcPass p) -> Maybe Bool)
-> BooleanFormula (GhcPass p)
-> BooleanFormula (GhcPass p)
-simplify f (Var a) = case f a of
- Nothing -> Var a
+simplify f (Var x a) = case f a of
+ Nothing -> Var x a
Just b -> mkBool b
-simplify f (And xs) = mkAnd (map (fmap (simplify f)) xs)
-simplify f (Or xs) = mkOr (map (fmap (simplify f)) xs)
-simplify f (Parens x) = simplify f (unLoc x)
+simplify f (And _ xs) = mkAnd (map (fmap (simplify f)) xs)
+simplify f (Or _ xs) = mkOr (map (fmap (simplify f)) xs)
+simplify f (Parens _ x) = simplify f (unLoc x)
-- Test if a boolean formula is satisfied when the given values are assigned to the atoms
-- if it is, returns Nothing
@@ -152,11 +166,11 @@ isUnsatisfied f bf
-- If the boolean formula holds, does that mean that the given atom is always true?
impliesAtom :: Eq (IdP (GhcPass p)) => BooleanFormula (GhcPass p) -> LIdP (GhcPass p) -> Bool
-Var x `impliesAtom` y = (unLoc x) == (unLoc y)
-And xs `impliesAtom` y = any (\x -> unLoc x `impliesAtom` y) xs
- -- we have all of xs, so one of them implying y is enough
-Or xs `impliesAtom` y = all (\x -> unLoc x `impliesAtom` y) xs
-Parens x `impliesAtom` y = unLoc x `impliesAtom` y
+Var _ x `impliesAtom` y = (unLoc x) == (unLoc y)
+And _ xs `impliesAtom` y = any (\x -> unLoc x `impliesAtom` y) xs
+ -- we have all of xs, so one of them implying y is enough
+Or _ xs `impliesAtom` y = all (\x -> unLoc x `impliesAtom` y) xs
+Parens _ x `impliesAtom` y = unLoc x `impliesAtom` y
implies :: (Uniquable (IdP (GhcPass p))) => BooleanFormula (GhcPass p) -> BooleanFormula (GhcPass p) -> Bool
implies e1 e2 = go (Clause emptyUniqSet [e1]) (Clause emptyUniqSet [e2])
@@ -164,18 +178,18 @@ implies e1 e2 = go (Clause emptyUniqSet [e1]) (Clause emptyUniqSet [e2])
go :: Uniquable (IdP (GhcPass p)) => Clause (GhcPass p) -> Clause (GhcPass p) -> Bool
go l@Clause{ clauseExprs = hyp:hyps } r =
case hyp of
- Var x | memberClauseAtoms (unLoc x) r -> True
- | otherwise -> go (extendClauseAtoms l (unLoc x)) { clauseExprs = hyps } r
- Parens hyp' -> go l { clauseExprs = unLoc hyp':hyps } r
- And hyps' -> go l { clauseExprs = map unLoc hyps' ++ hyps } r
- Or hyps' -> all (\hyp' -> go l { clauseExprs = unLoc hyp':hyps } r) hyps'
+ Var _ x | memberClauseAtoms (unLoc x) r -> True
+ | otherwise -> go (extendClauseAtoms l (unLoc x)) { clauseExprs = hyps } r
+ Parens _ hyp' -> go l { clauseExprs = unLoc hyp':hyps } r
+ And _ hyps' -> go l { clauseExprs = map unLoc hyps' ++ hyps } r
+ Or _ hyps' -> all (\hyp' -> go l { clauseExprs = unLoc hyp':hyps } r) hyps'
go l r@Clause{ clauseExprs = con:cons } =
case con of
- Var x | memberClauseAtoms (unLoc x) l -> True
- | otherwise -> go l (extendClauseAtoms r (unLoc x)) { clauseExprs = cons }
- Parens con' -> go l r { clauseExprs = unLoc con':cons }
- And cons' -> all (\con' -> go l r { clauseExprs = unLoc con':cons }) cons'
- Or cons' -> go l r { clauseExprs = map unLoc cons' ++ cons }
+ Var _ x | memberClauseAtoms (unLoc x) l -> True
+ | otherwise -> go l (extendClauseAtoms r (unLoc x)) { clauseExprs = cons }
+ Parens _ con' -> go l r { clauseExprs = unLoc con':cons }
+ And _ cons' -> all (\con' -> go l r { clauseExprs = unLoc con':cons }) cons'
+ Or _ cons' -> go l r { clauseExprs = map unLoc cons' ++ cons }
go _ _ = False
-- A small sequent calculus proof engine.
@@ -201,12 +215,12 @@ pprBooleanFormula' :: (Rational -> LIdP (GhcPass p) -> SDoc)
-> Rational -> BooleanFormula (GhcPass p) -> SDoc
pprBooleanFormula' pprVar pprAnd pprOr = go
where
- go p (Var x) = pprVar p x
- go p (And []) = cparen (p > 0) empty
- go p (And xs) = pprAnd p (map (go 3 . unLoc) xs)
- go _ (Or []) = keyword $ text "FALSE"
- go p (Or xs) = pprOr p (map (go 2 . unLoc) xs)
- go p (Parens x) = go p (unLoc x)
+ go p (Var _ x) = pprVar p x
+ go p (And _ []) = cparen (p > 0) empty
+ go p (And _ xs) = pprAnd p (map (go 3 . unLoc) xs)
+ go _ (Or _ []) = keyword $ text "FALSE"
+ go p (Or _ xs) = pprOr p (map (go 2 . unLoc) xs)
+ go p (Parens _ x) = go p (unLoc x)
-- Pretty print in source syntax, "a | b | c,d,e"
pprBooleanFormula :: (Rational -> LIdP (GhcPass p) -> SDoc)
@@ -233,8 +247,9 @@ instance OutputableBndrId p => Outputable (BooleanFormula (GhcPass p)) where
pprBooleanFormulaNormal :: OutputableBndrId p => BooleanFormula (GhcPass p) -> SDoc
pprBooleanFormulaNormal = go
where
- go (Var x) = pprPrefixOcc (unLoc x)
- go (And xs) = fsep $ punctuate comma (map (go . unLoc) xs)
- go (Or []) = keyword $ text "FALSE"
- go (Or xs) = fsep $ intersperse vbar (map (go . unLoc) xs)
- go (Parens x) = parens (go $ unLoc x)
+ go (Var _ x) = pprPrefixOcc (unLoc x)
+ go (And _ xs) = fsep $ punctuate comma (map (go . unLoc) xs)
+ go (Or _ []) = keyword $ text "FALSE"
+ go (Or _ xs) = fsep $ intersperse vbar (map (go . unLoc) xs)
+ go (Parens _ x) = parens (go $ unLoc x)
+ go (XBooleanFormula _) = text "XBooleanFormula"
=====================================
compiler/GHC/Data/Word64Map/Internal.hs
=====================================
@@ -72,6 +72,7 @@ module GHC.Data.Word64Map.Internal (
-- * Query
, null
, size
+ , sizeAtMost
, compareSize
, member
, notMember
@@ -533,6 +534,10 @@ size = go 0
go acc (Tip _ _) = 1 + acc
go acc Nil = acc
+-- | Check if the map is <= n in O(min(|map|,n))
+sizeAtMost :: Word64Map a -> Int -> Bool
+sizeAtMost map n = compareSize map n /= GT
+
-- | \(O(\min(n,c))\). Compare the number of entries in the map to an @Int@.
--
-- @compareSize m c@ returns the same result as @compare ('size' m) c@ but is
=====================================
compiler/GHC/Data/Word64Map/Lazy.hs
=====================================
@@ -113,6 +113,7 @@ module GHC.Data.Word64Map.Lazy (
-- ** Size
, WM.null
, size
+ , sizeAtMost
, compareSize
-- * Combine
=====================================
compiler/GHC/Data/Word64Map/Strict.hs
=====================================
@@ -130,6 +130,7 @@ module GHC.Data.Word64Map.Strict (
-- ** Size
, null
, size
+ , sizeAtMost
, compareSize
-- * Combine
=====================================
compiler/GHC/Data/Word64Map/Strict/Internal.hs
=====================================
@@ -132,6 +132,7 @@ module GHC.Data.Word64Map.Strict.Internal (
-- ** Size
, null
, size
+ , sizeAtMost
, compareSize
-- * Combine
@@ -324,6 +325,7 @@ import GHC.Data.Word64Map.Internal
, spanAntitone
, restrictKeys
, size
+ , sizeAtMost
, compareSize
, split
, splitLookup
=====================================
compiler/GHC/Driver/GenerateCgIPEStub.hs
=====================================
@@ -62,7 +62,7 @@ looking up source locations for stack info tables in the map generated during th
The rest of this note will document exactly how the first pass generates the map from labels to
estimated source positions. The algorithms are different depending on whether tables-next-to-code
-is on or off. Both algorithms have in common that we are looking for a `CmmNode.CmmTick`
+is on or off. Both algorithms have in common that we are looking for a `GHC.Cmm.Node.CmmTick`
(containing a `SourceNote`) that is near what we estimate to be the label of a return stack frame.
With tables-next-to-code
@@ -112,14 +112,14 @@ open or closed on exit (one can fallthrough from them to the next node).
Please refer to the paper "Hoopl: A Modular, Reusable Library for Dataflow Analysis and Transformation"
for a detailed explanation.
-Here we use the fact, that calls (represented by `CmmNode.CmmCall`) are always closed on exit
+Here we use the fact, that calls (represented by `GHC.Cmm.Node.CmmCall`) are always closed on exit
(`CmmNode O C`, `O` means open, `C` closed). In other words, they are always at the end of a block.
So, given a `CmmGraph`:
- - Look at the end of every block: If it is a `CmmNode.CmmCall` returning to some label, lookup
- the nearest `CmmNode.CmmTick` by traversing the middle part of the block backwards (from end to
+ - Look at the end of every block: If it is a `GHC.Cmm.Node.CmmCall` returning to some label, lookup
+ the nearest `GHC.Cmm.Node.CmmTick` by traversing the middle part of the block backwards (from end to
beginning).
- - Take the first `CmmNode.CmmTick` that contains a `Tickish.SourceNote` and map the label we
+ - Take the first `GHC.Cmm.Node.CmmTick` that contains a `Tickish.SourceNote` and map the label we
found to it's payload as an `IpeSourceLocation`. (There are other `Tickish` constructors like
`ProfNote` or `HpcTick`, these are ignored.)
=====================================
compiler/GHC/Iface/Ext/Ast.hs
=====================================
@@ -2083,16 +2083,16 @@ instance ToHie PendingRnSplice where
instance (HiePass p, Data (IdGhcP p))
=> ToHie (GenLocated SrcSpanAnnBF (BooleanFormula (GhcPass p))) where
toHie (L span form) = concatM $ makeNode form (locA span) : case form of
- Var a ->
+ Var _ a ->
[ toHie $ C Use a
]
- And forms ->
+ And _ forms ->
[ toHie forms
]
- Or forms ->
+ Or _ forms ->
[ toHie forms
]
- Parens f ->
+ Parens _ f ->
[ toHie f
]
=====================================
compiler/GHC/Iface/Syntax.hs
=====================================
@@ -103,6 +103,7 @@ import GHC.Data.FastString
import GHC.Data.BooleanFormula (pprBooleanFormula, isTrue)
import Language.Haskell.Syntax.BooleanFormula(BooleanFormula(..))
+import Language.Haskell.Syntax.Extension (noExtField)
import Language.Haskell.Syntax.Text
import Control.Monad
@@ -1326,10 +1327,10 @@ pprIfaceDecl ss decl@(IfaceClass { ifName = clas
fromIfaceBooleanFormula :: IfaceBooleanFormula -> BooleanFormula GhcRn
-- `mkUnboundName` here is fine because the Name generated is only used for pretty printing and nothing else.
- fromIfaceBooleanFormula (IfVar nm ) = Var $ noLocA . mkUnboundName . mkVarOccFS . ifLclNameFS $ nm
- fromIfaceBooleanFormula (IfAnd bfs ) = And $ map (noLocA . fromIfaceBooleanFormula) bfs
- fromIfaceBooleanFormula (IfOr bfs ) = Or $ map (noLocA . fromIfaceBooleanFormula) bfs
- fromIfaceBooleanFormula (IfParens bf) = Parens $ (noLocA . fromIfaceBooleanFormula) bf
+ fromIfaceBooleanFormula (IfVar nm ) = Var noExtField $ noLocA . mkUnboundName . mkVarOccFS . ifLclNameFS $ nm
+ fromIfaceBooleanFormula (IfAnd bfs ) = And noExtField $ map (noLocA . fromIfaceBooleanFormula) bfs
+ fromIfaceBooleanFormula (IfOr bfs ) = Or noExtField $ map (noLocA . fromIfaceBooleanFormula) bfs
+ fromIfaceBooleanFormula (IfParens bf) = Parens noExtField $ (noLocA . fromIfaceBooleanFormula) bf
-- See Note [Suppressing binder signatures] in GHC.Iface.Type
=====================================
compiler/GHC/Iface/Tidy/StaticPtrTable.hs
=====================================
@@ -82,7 +82,7 @@ Here is a running example:
(SF4) The desugarer replaces a nested expression (static e) with a top-level
binding for an application of the function 'makeStatic' (defined in module
- GHC.StaticPtr.Internal of base). So we get
+ GHC.Internal.StaticPtr.Internal of base). So we get
s = /\abc. makeStatic location e
f x = ...(fromStaticPtr s)...
=====================================
compiler/GHC/IfaceToCore.hs
=====================================
@@ -883,10 +883,10 @@ tc_iface_decl _parent ignore_prags
return (ATI tc mb_def)
tc_boolean_formula :: IfaceBooleanFormula -> IfL (BooleanFormula GhcRn)
- tc_boolean_formula (IfAnd ibfs ) = BF.And . map noLocA <$> traverse tc_boolean_formula ibfs
- tc_boolean_formula (IfOr ibfs ) = BF.Or . map noLocA <$> traverse tc_boolean_formula ibfs
- tc_boolean_formula (IfParens ibf) = BF.Parens . noLocA <$> tc_boolean_formula ibf
- tc_boolean_formula (IfVar nm ) = BF.Var . noLocA <$> (lookupIfaceTop . mkVarOccFS . ifLclNameFS $ nm)
+ tc_boolean_formula (IfAnd ibfs ) = BF.And NoExtField . map noLocA <$> traverse tc_boolean_formula ibfs
+ tc_boolean_formula (IfOr ibfs ) = BF.Or NoExtField . map noLocA <$> traverse tc_boolean_formula ibfs
+ tc_boolean_formula (IfParens ibf) = BF.Parens NoExtField . noLocA <$> tc_boolean_formula ibf
+ tc_boolean_formula (IfVar nm ) = BF.Var NoExtField . noLocA <$> (lookupIfaceTop . mkVarOccFS . ifLclNameFS $ nm)
mk_sc_doc pred = text "Superclass" <+> ppr pred
mk_at_doc tc = text "Associated type" <+> ppr tc
=====================================
compiler/GHC/Parser.y
=====================================
@@ -3821,11 +3821,11 @@ name_boolformula :: { LBooleanFormula GhcPs }
: name_boolformula_and { $1 }
| name_boolformula_and '|' name_boolformula
{% do { h <- addTrailingVbarBF $1 (epTok $2)
- ; return (sLLa $1 $> (Or [h,$3])) } }
+ ; return (sLLa $1 $> (Or noExtField [h,$3])) } }
name_boolformula_and :: { LBooleanFormula GhcPs }
: name_boolformula_and_list
- { sLLa (head $1) (last $1) (And (toList $1)) }
+ { sLLa (head $1) (last $1) (And noExtField (toList $1)) }
name_boolformula_and_list :: { NonEmpty (LBooleanFormula GhcPs) }
: name_boolformula_atom { NE.singleton $1 }
@@ -3834,9 +3834,9 @@ name_boolformula_and_list :: { NonEmpty (LBooleanFormula GhcPs) }
; return (h NE.<| $3) } }
name_boolformula_atom :: { LBooleanFormula GhcPs }
- : '(' name_boolformula ')' {% amsr (sLL $1 $> (Parens $2))
+ : '(' name_boolformula ')' {% amsr (sLL $1 $> (Parens noExtField $2))
(AnnBooleanFormula (epTok $1) (epTok $3) []) }
- | name_var { sL1a $1 (Var $1) }
+ | name_var { sL1a $1 (Var noExtField $1) }
namelist :: { Located [LocatedN RdrName] }
namelist : name_var { sL1 $1 [$1] }
=====================================
compiler/GHC/Tc/Solver/InertSet.hs
=====================================
@@ -460,7 +460,7 @@ In implementation terms
- It is only called when applying an instance decl,
in GHC.Tc.Solver.Dict.tryInstances
- - ClsInst.InstanceWhat says what kind of instance was
+ - GHC.Tc.Instance.Class.InstanceWhat says what kind of instance was
used to solve the constraint. In particular
* LocalInstance identifies quantified constraints
* BuiltinEqInstance identifies the strange built-in
@@ -800,7 +800,7 @@ The InertCans represents a collection of constraints with the following properti
eg a wanted cannot rewrite a given)
* CEqCan equalities: see Note [inert_eqs: the inert equalities]
- Also see documentation in Constraint.Ct for a list of invariants
+ Also see documentation in GHC.Tc.Types.Constraint.Ct for a list of invariants
Note [inert_eqs: the inert equalities]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
=====================================
compiler/GHC/Tc/TyCl/Class.hs
=====================================
@@ -345,7 +345,7 @@ tcClassMinimalDef _clas sigs op_info
where
-- By default require all methods without a default implementation
defMindef :: ClassMinimalDef
- defMindef = mkAnd [ noLocA (mkVar (noLocA name))
+ defMindef = mkAnd [ noLocA (mkVar NoExtField (noLocA name))
| (name, _, Nothing) <- op_info ]
instantiateMethod :: Class -> TcId -> [TcType] -> TcType
=====================================
compiler/GHC/Types/Id/Make.hs
=====================================
@@ -987,7 +987,7 @@ until the final simplifier phase; see Note [Activation for data
constructor wrappers].
For further reading, see:
- * (IA1) in Note [Interesting arguments] in GHC.Core.Op.Simplify.Utils
+ * (IA1) in Note [Interesting arguments] in GHC.Core.Opt.Simplify.Utils
* Note [Lone variables] in GHC.Core.Unfold
* Note [exprIsConApp_maybe on data constructors with wrappers]
in GHC.Core.SimpleOpt
=====================================
compiler/GHC/Types/Unique/DFM.hs
=====================================
@@ -351,8 +351,8 @@ foldUDFM :: (elt -> a -> a) -> a -> UniqDFM key elt -> a
{-# INLINE foldUDFM #-}
-- Specialises k and z into M.foldr on the small-map path.
foldUDFM k z (UDFM m ub)
- | M.compareSize m 1 /= GT = M.foldr (k . taggedFst) z m
- | otherwise = fold_udfm k z m ub
+ | M.sizeAtMost m 1 = M.foldr (k . taggedFst) z m
+ | otherwise = fold_udfm k z m ub
fold_udfm :: (elt -> a -> a) -> a -> M.Word64Map (TaggedVal elt) -> Int -> a
{-# NOINLINE fold_udfm #-}
@@ -402,8 +402,8 @@ eltsUDFM :: UniqDFM key elt -> [elt]
{-# INLINE eltsUDFM #-} -- so the small case is a good producer
-- This matters for T13719.
eltsUDFM (UDFM m ub)
- | M.compareSize m 1 /= GT = build (\c n -> M.foldr (c . taggedFst) n m)
- | otherwise = elts_udfm m ub
+ | M.sizeAtMost m 1 = build (\c n -> M.foldr (c . taggedFst) n m)
+ | otherwise = elts_udfm m ub
elts_udfm :: M.Word64Map (TaggedVal elt) -> Int -> [elt]
{-# NOINLINE elts_udfm #-}
@@ -511,7 +511,7 @@ udfmToList :: UniqDFM key elt -> [(Unique, elt)]
-- traverseUSDFM in the pattern-match checker, which doesn't fuse. Inlining
-- the size dispatch into it regresses T17836.
udfmToList (UDFM m ub)
- | M.compareSize m 1 /= GT =
+ | M.sizeAtMost m 1 =
M.foldrWithKey (\k tv r -> (mkUniqueGrimily k, taggedFst tv) : r) [] m
| usePigeonholeSort m ub = pigeonholeSort ub
(\k tv -> TaggedVal (mkUniqueGrimily k, taggedFst tv) (taggedSnd tv)) m
=====================================
compiler/GHC/Types/Var.hs
=====================================
@@ -621,27 +621,27 @@ Note [Types for coercions, predicates, and evidence]
VarBndr is polymorphic in both var and visibility fields.
Currently there are nine different uses of 'VarBndr':
-* Var.ForAllTyBinder = VarBndr TyCoVar ForAllTyFlag
+* GHC.Types.Var.ForAllTyBinder = VarBndr TyCoVar ForAllTyFlag
Binder of a forall-type; see ForAllTy in GHC.Core.TyCo.Rep
-* Var.TyVarBinder = VarBndr TyVar ForAllTyFlag
+* GHC.Types.Var.TyVarBinder = VarBndr TyVar ForAllTyFlag
Subset of ForAllTyBinder when we are sure the binder is a TyVar
-* Var.InvisTVBinder = VarBndr TyVar Specificity
+* GHC.Types.Var.InvisTVBinder = VarBndr TyVar Specificity
Specialised form of TyVarBinder, when ForAllTyFlag = Invisible s
See GHC.Core.Type.splitForAllInvisTVBinders
-* Var.ReqTVBinder = VarBndr TyVar ()
+* GHC.Types.Var.ReqTVBinder = VarBndr TyVar ()
Specialised form of TyVarBinder, when ForAllTyFlag = Required
See GHC.Core.Type.splitForAllReqTVBinders
This one is barely used
-* TyCon.TyConBinder = VarBndr TyVar TyConBndrVis
+* GHC.Core.TyCon.TyConBinder = VarBndr TyVar TyConBndrVis
Binders of a TyCon; see TyCon in GHC.Core.TyCon
-* IfaceType.IfaceForAllBndr = VarBndr IfaceBndr ForAllTyFlag
-* IfaceType.IfaceForAllSpecBndr = VarBndr IfaceBndr Specificity
-* IfaceType.IfaceTyConBinder = VarBndr IfaceBndr TyConBndrVis
+* GHC.Iface.Type.IfaceForAllBndr = VarBndr IfaceBndr ForAllTyFlag
+* GHC.Iface.Type.IfaceForAllSpecBndr = VarBndr IfaceBndr Specificity
+* GHC.Iface.Type.IfaceTyConBinder = VarBndr IfaceBndr TyConBndrVis
-}
data VarBndr var argf = Bndr var argf
=====================================
compiler/Language/Haskell/Syntax/BooleanFormula.hs
=====================================
@@ -4,59 +4,78 @@
module Language.Haskell.Syntax.BooleanFormula(
BooleanFormula(..), LBooleanFormula,
- mkVar, mkFalse, mkTrue, mkBool, mkAnd, mkOr
+ mkVar, mkFalse, mkTrue, mkBool, mkAnd, mkOr,
+ BooleanFormulaDefault(..)
) where
import Prelude hiding ( init, last )
import Data.List ( nub )
-import Language.Haskell.Syntax.Extension (XRec, UnXRec (..), LIdP)
+import Language.Haskell.Syntax.Extension (XRec, UnXRec (..), LIdP,
+ XBFVar, XBFAnd, XBFOr, XBFParens,
+ XXBooleanFormula)
-- types
type LBooleanFormula p = XRec p (BooleanFormula p)
-data BooleanFormula p = Var (LIdP p) | And [LBooleanFormula p] | Or [LBooleanFormula p]
- | Parens (LBooleanFormula p)
+data BooleanFormula p
+ = Var (XBFVar p) (LIdP p)
+ | And (XBFAnd p) [LBooleanFormula p]
+ | Or (XBFOr p) [LBooleanFormula p]
+ | Parens (XBFParens p) (LBooleanFormula p)
+ | XBooleanFormula !(XXBooleanFormula p)
+
+class BooleanFormulaDefault p where
+ bfAnnAnd :: XBFAnd p
+ bfAnnOr :: XBFOr p
-- instances
-deriving instance (Eq (LIdP p), Eq (LBooleanFormula p)) => Eq (BooleanFormula p)
+deriving instance (Eq (LIdP p), Eq (LBooleanFormula p),
+ Eq (XBFVar p),
+ Eq (XBFAnd p),
+ Eq (XBFOr p),
+ Eq (XBFParens p),
+ Eq (XXBooleanFormula p)
+ ) => Eq (BooleanFormula p)
-- smart constructors
-- see note [Simplification of BooleanFormulas]
-mkVar :: LIdP p -> BooleanFormula p
+mkVar :: XBFVar p -> LIdP p -> BooleanFormula p
mkVar = Var
-mkFalse, mkTrue :: BooleanFormula p
-mkFalse = Or []
-mkTrue = And []
+mkFalse, mkTrue :: forall p. BooleanFormulaDefault p => BooleanFormula p
+mkFalse = Or (bfAnnOr @p) []
+mkTrue = And (bfAnnAnd @p) []
-- Convert a Bool to a BooleanFormula
-mkBool :: Bool -> BooleanFormula p
+mkBool :: BooleanFormulaDefault p => Bool -> BooleanFormula p
mkBool False = mkFalse
mkBool True = mkTrue
-- Make a conjunction, and try to simplify
-mkAnd :: forall p. (UnXRec p, Eq (LIdP p), Eq (LBooleanFormula p)) => [LBooleanFormula p] -> BooleanFormula p
+mkAnd :: forall p. (UnXRec p, Eq (LIdP p), Eq (LBooleanFormula p), BooleanFormulaDefault p)
+ => [LBooleanFormula p] -> BooleanFormula p
mkAnd = maybe mkFalse (mkAnd' . nub . concat) . mapM fromAnd
where
-- See Note [Simplification of BooleanFormulas]
fromAnd :: LBooleanFormula p -> Maybe [LBooleanFormula p]
fromAnd bf = case unXRec @p bf of
- (And xs) -> Just xs
+ (And _ xs) -> Just xs
-- assume that xs are already simplified
-- otherwise we would need: fromAnd (And xs) = concat <$> traverse fromAnd xs
- (Or []) -> Nothing
+ (Or _ []) -> Nothing
-- in case of False we bail out, And [..,mkFalse,..] == mkFalse
_ -> Just [bf]
mkAnd' [x] = unXRec @p x
- mkAnd' xs = And xs
+ mkAnd' xs = And (bfAnnAnd @p) xs
-mkOr :: forall p. (UnXRec p, Eq (LIdP p), Eq (LBooleanFormula p)) => [LBooleanFormula p] -> BooleanFormula p
+mkOr :: forall p. (UnXRec p, Eq (LIdP p), Eq (LBooleanFormula p), BooleanFormulaDefault p)
+ => [LBooleanFormula p] -> BooleanFormula p
mkOr = maybe mkTrue (mkOr' . nub . concat) . mapM fromOr
where
-- See Note [Simplification of BooleanFormulas]
fromOr bf = case unXRec @p bf of
- (Or xs) -> Just xs
- (And []) -> Nothing
- _ -> Just [bf]
+ (Or _ xs) -> Just xs
+ (And _ []) -> Nothing
+ _ -> Just [bf]
mkOr' [x] = unXRec @p x
- mkOr' xs = Or xs
+ mkOr' xs = Or (bfAnnOr @p) xs
=====================================
compiler/Language/Haskell/Syntax/Extension.hs
=====================================
@@ -841,6 +841,14 @@ type family XXIntegralLit x
type family XStringLit x
type family XXStringLit x
+-- =====================================================================
+-- BooleanFormula type families
+
+type family XBFVar p
+type family XBFAnd p
+type family XBFOr p
+type family XBFParens p
+type family XXBooleanFormula p
-- =====================================================================
-- Misc
=====================================
testsuite/tests/process/process009.hs
=====================================
@@ -11,7 +11,7 @@ main = do
let script = intercalate " "
[ "exec python3 2>/dev/null"
, "-c"
- , "'import os; os.kill(os.getpid(), 1)'"
+ , "'import os; os.kill(os.getpid(), 9)'" -- SIGKILL
]
(_,_,_,p) <- createProcess (shell script)
waitForProcess p >>= print
=====================================
testsuite/tests/process/process009.stdout
=====================================
@@ -1,3 +1,3 @@
-ExitFailure (-1)
-Just (ExitFailure (-1))
-Just (ExitFailure (-1))
+ExitFailure (-9)
+Just (ExitFailure (-9))
+Just (ExitFailure (-9))
=====================================
utils/check-exact/ExactPrint.hs
=====================================
@@ -2722,18 +2722,18 @@ instance ExactPrint (BF.BooleanFormula GhcPs) where
getAnnotationEntry = const NoEntryVal
setAnnotationAnchor a _ _ _ = a
- exact (BF.Var x) = do
+ exact (BF.Var e x) = do
x' <- markAnnotated x
- return (BF.Var x')
- exact (BF.Or ls) = do
+ return (BF.Var e x')
+ exact (BF.Or e ls) = do
ls' <- mapM markAnnotated ls
- return (BF.Or ls')
- exact (BF.And ls) = do
+ return (BF.Or e ls')
+ exact (BF.And e ls) = do
ls' <- mapM markAnnotated ls
- return (BF.And ls')
- exact (BF.Parens x) = do
+ return (BF.And e ls')
+ exact (BF.Parens e x) = do
x' <- markAnnotated x
- return (BF.Parens x')
+ return (BF.Parens e x')
-- ---------------------------------------------------------------------
=====================================
utils/haddock/haddock-api/src/Haddock/Backends/Xhtml/Decl.hs
=====================================
@@ -998,26 +998,26 @@ ppClassDecl
-- Minimal complete definition
minimalBit = case [s | MinimalSig _ (L _ s) <- sigs] of
-- Miminal complete definition = every shown method
- And xs : _
- | sort [getName n | L _ (Var (L _ n)) <- xs]
+ And _ xs : _
+ | sort [getName n | L _ (Var _ (L _ n)) <- xs]
== sort [getName n | ClassOpSig _ _ ns _ <- sigs, L _ n <- ns] ->
noHtml
-- Minimal complete definition = the only shown method
- Var (L _ n) : _
+ Var _ (L _ n) : _
| [getName n]
== [getName n' | ClassOpSig _ _ ns _ <- sigs, L _ n' <- ns] ->
noHtml
-- Minimal complete definition = nothing
- And [] : _ -> subMinimal $ toHtml ("Nothing" :: LText)
+ And _ [] : _ -> subMinimal $ toHtml ("Nothing" :: LText)
m : _ -> subMinimal $ ppMinimal False m
_ -> noHtml
- ppMinimal _ (Var (L _ n)) = ppDocName qual Prefix True n
- ppMinimal _ (And fs) = foldr1 (\a b -> a +++ (", " :: LText) +++ b) $ map (ppMinimal True . unLoc) fs
- ppMinimal p (Or fs) = wrap $ foldr1 (\a b -> a +++ (" | " :: LText) +++ b) $ map (ppMinimal False . unLoc) fs
+ ppMinimal _ (Var _ (L _ n)) = ppDocName qual Prefix True n
+ ppMinimal _ (And _ fs) = foldr1 (\a b -> a +++ (", " :: LText) +++ b) $ map (ppMinimal True . unLoc) fs
+ ppMinimal p (Or _ fs) = wrap $ foldr1 (\a b -> a +++ (" | " :: LText) +++ b) $ map (ppMinimal False . unLoc) fs
where
wrap | p = parens | otherwise = id
- ppMinimal p (Parens x) = ppMinimal p (unLoc x)
+ ppMinimal p (Parens _ x) = ppMinimal p (unLoc x)
-- Instances
instancesBit =
=====================================
utils/haddock/haddock-api/src/Haddock/Interface/Rename.hs
=====================================
@@ -858,10 +858,10 @@ bfTraverse :: Applicative f
-> f (BooleanFormula DocNameI)
bfTraverse f = go
where
- go (Var a ) = Var <$> f a
- go (And bfs) = And <$> traverse @[] (traverse go) bfs
- go (Or bfs) = Or <$> traverse @[] (traverse go) bfs
- go (Parens bf ) = Parens <$> traverse go bf
+ go (Var x a ) = Var x <$> f a
+ go (And x bfs) = And x <$> traverse @[] (traverse go) bfs
+ go (Or x bfs) = Or x <$> traverse @[] (traverse go) bfs
+ go (Parens x bf ) = Parens x <$> traverse go bf
renameForD :: ForeignDecl GhcRn -> RnM (ForeignDecl DocNameI)
renameForD (ForeignImport _ modifiers lname ltype x) = do
=====================================
utils/haddock/haddock-api/src/Haddock/Types.hs
=====================================
@@ -1038,6 +1038,12 @@ type instance XCTyFamInstDecl DocNameI = NoExtField
type instance XHsContext DocNameI = NoExtField
type instance XXHsContextDetails DocNameI = DataConCantHappen
+type instance XBFVar DocNameI = NoExtField
+type instance XBFAnd DocNameI = NoExtField
+type instance XBFOr DocNameI = NoExtField
+type instance XBFParens DocNameI = NoExtField
+type instance XXBooleanFormula DocNameI = DataConCantHappen
+
-----------------------------------------------------------------------------
-- * NFData instances for GHC types
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/2667cef68d74b079c03cb895a25c49…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/2667cef68d74b079c03cb895a25c49…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/sjakobi/T27619-liveness-fixpoint-formats] NCG: compare register formats by width, remove Ord Format
by Simon Jakobi (@sjakobi) 18 Aug '26
by Simon Jakobi (@sjakobi) 18 Aug '26
18 Aug '26
Simon Jakobi pushed to branch wip/sjakobi/T27619-liveness-fixpoint-formats at Glasgow Haskell Compiler / GHC
Commits:
1ad7425a by Simon Jakobi at 2026-08-17T23:12:15+02:00
NCG: compare register formats by width, remove Ord Format
maxRegWithFormat, minusCoveredRegs and the caller-less shrinkingRegs
compared formats with Format's derived Ord, which ordered by
constructor, not by width: For example, VecFormat 16 FmtInt8 (16 bytes)
sorted above a hypothetical VecFormat 8 FmtDouble (64 bytes). This is
harmless while all vector vregs are 128-bit, but once wider vectors land
in the NCG it becomes unsound in both directions of Note [Register
formats in liveness analysis]: the maxRegWithFormat join could record a
live format narrower than a read, violating (FmtBwd1), and
minusCoveredRegs could treat a narrow vector write as covering a wider
live format, violating (FmtBwd2). Noted in #27619.
The comparisons now go by formatToWidth, except the liveness join
maxRegWithFormat, which needs a total order to keep the liveness
fixpoint convergent and uses the new width-major compareFormat. See
Note [Convergence of the liveness fixpoint] in GHC.CmmToAsm.Reg.Liveness.
Apart from future-proofing for larger vector formats, these changes also
make the code more obviously correct today.
Also remove the Ord instances of Format and ScalarFormat, so future
users need to make a conscious choice between formatToWidth and
compareFormat.
Assisted-by: Claude Fable 5
- - - - -
4 changed files:
- compiler/GHC/CmmToAsm/Format.hs
- compiler/GHC/CmmToAsm/PPC/CodeGen.hs
- compiler/GHC/CmmToAsm/Reg/Liveness.hs
- compiler/GHC/CmmToAsm/Reg/Regs.hs
Changes:
=====================================
compiler/GHC/CmmToAsm/Format.hs
=====================================
@@ -23,6 +23,7 @@ module GHC.CmmToAsm.Format (
vecFormat,
isVecFormat,
cmmTypeFormat,
+ compareFormat,
formatToWidth,
scalarWidth,
formatInBytes,
@@ -40,11 +41,13 @@ where
import GHC.Prelude
+import Data.Semigroup ( (<>) )
+
import GHC.Cmm
import GHC.Platform.Reg ( Reg(..), RealReg, VirtualReg )
import GHC.Types.Unique ( Uniquable(..) )
import GHC.Types.Unique.Set
-import GHC.Utils.Outputable
+import GHC.Utils.Outputable hiding ( (<>) )
import GHC.Utils.Panic
{- Note [GHC's data format representations]
@@ -92,7 +95,9 @@ data Format
| FF64
| VecFormat !Length -- ^ number of elements (always at least 2)
!ScalarFormat -- ^ format of each element
- deriving (Show, Eq, Ord)
+ deriving (Show, Eq)
+ -- No Ord: compare via 'formatToWidth', or use 'compareFormat' where a
+ -- total order is needed.
pattern IntegerFormat :: Format
pattern IntegerFormat <- ( isIntegerFormat -> True )
@@ -117,7 +122,7 @@ data ScalarFormat
| FmtInt64
| FmtFloat
| FmtDouble
- deriving (Show, Eq, Ord)
+ deriving (Show, Eq)
scalarFormatFormat :: ScalarFormat -> Format
scalarFormatFormat = \case
@@ -248,6 +253,33 @@ scalarWidth = \case
formatInBytes :: Format -> Int
formatInBytes = widthInBytes . formatToWidth
+-- | Total order on formats: by width, with an arbitrary but fixed tiebreak
+-- between distinct formats of the same width.
+--
+-- See Note [Convergence of the liveness fixpoint] in GHC.CmmToAsm.Reg.Liveness.
+compareFormat :: Format -> Format -> Ordering
+compareFormat f1 f2 =
+ compare (formatToWidth f1) (formatToWidth f2) <> compare (tag f1) (tag f2)
+ where
+ tag :: Format -> (Int, Length)
+ tag = \case
+ II8 -> (0, 0)
+ II16 -> (1, 0)
+ II32 -> (2, 0)
+ II64 -> (3, 0)
+ FF32 -> (4, 0)
+ FF64 -> (5, 0)
+ VecFormat l s -> (6 + scalarTag s, l)
+
+ scalarTag :: ScalarFormat -> Int
+ scalarTag = \case
+ FmtInt8 -> 0
+ FmtInt16 -> 1
+ FmtInt32 -> 2
+ FmtInt64 -> 3
+ FmtFloat -> 4
+ FmtDouble -> 5
+
--------------------------------------------------------------------------------
-- | A typed virtual register: a virtual register, together with the specific
=====================================
compiler/GHC/CmmToAsm/PPC/CodeGen.hs
=====================================
@@ -484,7 +484,7 @@ getRegister' _ _ (CmmMachOp (MO_SS_Conv src tgt) [CmmLoad mem pk _])
, src < tgt = do
let format = cmmTypeFormat pk
-- lwa is DS-form. See Note [Power instruction format]
- let form = if format >= II32 then DS else D
+ let form = if formatToWidth format >= W32 then DS else D
Amode addr addr_code <- getAmode form mem
let code dst = assert (format == intFormat src)
$ addr_code `snocOL` LA format dst addr
=====================================
compiler/GHC/CmmToAsm/Reg/Liveness.hs
=====================================
@@ -937,9 +937,9 @@ test:
dependency order. Comparing the whole accumulated block map would make the
fixpoint quadratic in procedure size (#27437).
-The fixpoint terminates because the entries can only grow: registers are
-only added, formats only increase via 'maxRegWithFormat' joins, and both
-lattices are finite.
+To ensure termination, 'maxRegWithFormat' uses 'compareFormat' which defines a
+total order on formats. Comparing the widths only would introduce the risk of
+an infinite loop where each iteration swaps two formats of the same width.
-}
=====================================
compiler/GHC/CmmToAsm/Reg/Regs.hs
=====================================
@@ -20,7 +20,8 @@ module GHC.CmmToAsm.Reg.Regs (
import GHC.Prelude
import GHC.Platform.Reg ( Reg )
-import GHC.CmmToAsm.Format ( Format, RegWithFormat(..), isVecFormat )
+import GHC.CmmToAsm.Format ( Format, RegWithFormat(..), isVecFormat,
+ compareFormat, formatToWidth )
import GHC.Utils.Outputable ( Outputable )
import GHC.Types.Unique ( Uniquable(..) )
@@ -39,11 +40,12 @@ newtype Regs = Regs { getRegs :: UniqSet RegWithFormat }
maxRegWithFormat :: RegWithFormat -> RegWithFormat -> RegWithFormat
maxRegWithFormat r1@(RegWithFormat _ fmt1) r2@(RegWithFormat _ fmt2)
- = if fmt1 >= fmt2
- then r1
- else r2
- -- Re-using one of the arguments avoids allocating a new 'RegWithFormat',
- -- compared with returning 'RegWithFormat r1 (max fmt1 fmt2)'.
+ | LT <- compareFormat fmt1 fmt2 = r2
+ | otherwise = r1
+ -- See Note [Convergence of the liveness fixpoint] in
+ -- GHC.CmmToAsm.Reg.Liveness.
+ --
+ -- Re-using one of the arguments avoids allocating a new 'RegWithFormat'.
noRegs :: Regs
noRegs = Regs emptyUniqSet
@@ -66,7 +68,7 @@ minusCoveredRegs = coerce $ minusUniqSet_C f
where
f :: RegWithFormat -> RegWithFormat -> Maybe RegWithFormat
f r1@(RegWithFormat _ fmt1) (RegWithFormat _ fmt2) =
- if fmt2 >= fmt1
+ if formatToWidth fmt2 >= formatToWidth fmt1
||
not ( isVecFormat fmt1 )
-- See Wrinkle [Don't allow scalar partial writes]
@@ -99,7 +101,7 @@ shrinkingRegs = coerce $ minusUniqSet_C f
where
f :: RegWithFormat -> RegWithFormat -> Maybe RegWithFormat
f (RegWithFormat _ fmt1) r2@(RegWithFormat _ fmt2)
- | fmt2 < fmt1
+ | formatToWidth fmt2 < formatToWidth fmt1
= Just r2
| otherwise
= Nothing
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/1ad7425a61af41a5e26fa79b3ba9e1c…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/1ad7425a61af41a5e26fa79b3ba9e1c…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/sjakobi/T27602] 2 commits: testsuite: Fold Baseline into CommitMetric
by Simon Jakobi (@sjakobi) 18 Aug '26
by Simon Jakobi (@sjakobi) 18 Aug '26
18 Aug '26
Simon Jakobi pushed to branch wip/sjakobi/T27602 at Glasgow Haskell Compiler / GHC
Commits:
6155fe6b by Simon Jakobi at 2026-08-17T22:51:46+02:00
testsuite: Fold Baseline into CommitMetric
A Baseline was just a CommitMetric plus the commit it came from, built
by copying fields across. Since get_commit_metric already knows that
commit, record it on CommitMetric itself and drop Baseline. This also
collapses both branches of find_baseline into plain returns.
Assisted-by: Claude Fable 5
- - - - -
b6b01ca0 by Simon Jakobi at 2026-08-17T22:51:46+02:00
ci: Clarify comment on pushing perf notes after failures
Context: #27602
Assisted-by: Claude Fable 5
- - - - -
3 changed files:
- .gitlab/ci.sh
- testsuite/driver/perf_notes.py
- testsuite/driver/testglobals.py
Changes:
=====================================
.gitlab/ci.sh
=====================================
@@ -1120,9 +1120,10 @@ case ${1:-help} in
setup) setup && cleanup_submodules ;;
configure) time_it "configure" configure ;;
build_hadrian) time_it "build" build_hadrian ;;
- # N.B. Always push notes, even if the build fails. This is okay to do as the
- # testsuite driver doesn't record notes for tests that fail due to
- # correctness.
+ # N.B. Always push notes, even if the build fails. Metrics from runs failing
+ # a perf stat check are deliberately recorded too — discarding them would
+ # bias the baseline towards whichever sample came first. Only correctness
+ # failures record nothing.
test_hadrian)
fetch_perf_notes
res=0
=====================================
testsuite/driver/perf_notes.py
=====================================
@@ -84,17 +84,12 @@ PerfStat = NamedTuple('PerfStat', [('test_env', TestEnv),
('value', float)])
# A test's metric recovered from a commit's git note: the raw sample values
-# recorded there, and a PerfStat whose value is their mean.
+# recorded there, and a PerfStat whose value is their mean. Serves as the
+# baseline when comparing a test run against an earlier commit.
class CommitMetric(NamedTuple):
- perfStat: PerfStat
- samples: List[float]
-
-# A baseline recovered form stored metrics.
-class Baseline(NamedTuple):
perfStat: PerfStat
commit: GitHash
- # The raw samples the baseline value was averaged over.
- samples: List[float] = []
+ samples: List[float]
# The type of exceptions which are thrown when computing the current stat value
# fails.
@@ -486,7 +481,7 @@ _commit_metric_cache: Dict[Tuple[NoteNamespace, GitHash],
# instead when looking for ci results)
# metric: str - test metric
# way: str - test way
-# returns: the Baseline or None if no metric was found within
+# returns: the baseline CommitMetric or None if no metric was found within
# BaselineSearchDepth commits and since the last expected change
# (ignoring any expected change in the given commit).
def baseline_metric(commit: GitHash,
@@ -495,7 +490,7 @@ def baseline_metric(commit: GitHash,
metric: MetricName,
way: WayName,
baseline_ref: Optional[GitRef]
- ) -> Optional[Baseline]:
+ ) -> Optional[CommitMetric]:
# For performance reasons (in order to avoid calling commit_hash), we assert
# commit is already a commit hash.
assert is_commit_hash(commit)
@@ -511,22 +506,16 @@ def baseline_metric(commit: GitHash,
# Searches through previous commits trying local then ci for each commit in.
def find_baseline(namespace: NoteNamespace,
test_env: TestEnv
- ) -> Optional[Baseline]:
+ ) -> Optional[CommitMetric]:
if baseline_commit is not None:
- current_metric = get_commit_metric(namespace, baseline_commit, test_env, name, metric, way)
- if current_metric is not None:
- return Baseline(current_metric.perfStat, baseline_commit,
- current_metric.samples)
- else:
- return None
+ return get_commit_metric(namespace, baseline_commit, test_env, name, metric, way)
for depth, current_commit in list(enumerate(commit_hashes)):
if current_commit == commit: continue
# Check for a metric on this commit.
current_metric = get_commit_metric(namespace, current_commit, test_env, name, metric, way)
if current_metric is not None:
- return Baseline(current_metric.perfStat, current_commit,
- current_metric.samples)
+ return current_metric
# Stop if there is an expected change at this commit. In that case
# metrics on ancestor commits will not be a valid baseline.
@@ -538,7 +527,7 @@ def baseline_metric(commit: GitHash,
# Test environment to use when comparing against CI namespace
ci_test_env = best_fit_ci_test_env()
- baseline = find_baseline(LocalNamespace, test_env) # type: Optional[Baseline]
+ baseline = find_baseline(LocalNamespace, test_env) # type: Optional[CommitMetric]
if baseline is None and ci_test_env is not None:
baseline = find_baseline(CiNamespace, ci_test_env)
@@ -608,6 +597,7 @@ def get_commit_metric(gitNoteRef,
currentCacheKey[3],
currentCacheKey[2],
sum(currentValues) / len(currentValues)),
+ commit,
currentValues)
# Save metrics to the cache.
@@ -621,7 +611,7 @@ def format_samples(samples: List[float]) -> str:
return ', '.join(format_sample(s) for s in samples)
def check_stats_change(actual: PerfStat,
- baseline: Baseline,
+ baseline: CommitMetric,
acceptance_window: MetricAcceptanceWindow,
allowed_perf_changes: Dict[TestName, List[AllowedPerfChange]] = {},
force_print = False
@@ -631,8 +621,8 @@ def check_stats_change(actual: PerfStat,
Parameters:
actual: the PerfStat with actual value
- baseline: the expected Baseline value (this should generally be derived
- from baseline_metric())
+ baseline: the CommitMetric to compare against (this should generally be
+ derived from baseline_metric())
acceptance_window: allowed deviation of the actual value from the expected
value.
allowed_perf_changes: allowed changes in stats. This is a dictionary as
=====================================
testsuite/driver/testglobals.py
=====================================
@@ -4,7 +4,7 @@
from my_typing import *
from pathlib import Path
-from perf_notes import MetricChange, PerfStat, Baseline, GitRef
+from perf_notes import MetricChange, PerfStat, CommitMetric, GitRef
from datetime import datetime
# -----------------------------------------------------------------------------
@@ -312,7 +312,7 @@ class TestResult:
PerfMetric = NamedTuple('PerfMetric',
[('change', MetricChange),
('stat', PerfStat),
- ('baseline', Optional[Baseline]) ])
+ ('baseline', Optional[CommitMetric]) ])
class TestRun:
def __init__(self) -> None:
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/311048aa5dc176fb36dc6eb1ed0a90…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/311048aa5dc176fb36dc6eb1ed0a90…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc] Pushed new branch wip/az/epa-tidy-locatedxxx-18
by Alan Zimmerman (@alanz) 18 Aug '26
by Alan Zimmerman (@alanz) 18 Aug '26
18 Aug '26
Alan Zimmerman pushed new branch wip/az/epa-tidy-locatedxxx-18 at Glasgow Haskell Compiler / GHC
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/tree/wip/az/epa-tidy-locatedxxx-18
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/sjakobi/T27368-cbe-compress] Cmm: lint that call continuations are reachable
by Simon Jakobi (@sjakobi) 18 Aug '26
by Simon Jakobi (@sjakobi) 18 Aug '26
18 Aug '26
Simon Jakobi pushed to branch wip/sjakobi/T27368-cbe-compress at Glasgow Haskell Compiler / GHC
Commits:
cbe671f4 by Simon Jakobi at 2026-08-17T19:52:52+02:00
Cmm: lint that call continuations are reachable
The breakage behind #27368 was invisible to -dcmm-lint: the common
block eliminator never deletes the blocks it eliminates, they merely
become unreachable, so the stranded continuation label still named a
block in the map and the existing "Branch to nonexistent id" check
passed at every stage. The panic only surfaced four passes later, in
setInfoTableStackMap.
Check the violated invariant directly: a call continuation must be
reachable even when the call itself sits in unreachable code. See
Note [unreachable blocks] in GHC.Cmm.Pipeline. Without the preceding
fix, this check turns the #27368 panic into a lint error located at
"Post common block elimination".
Assisted-by: Claude Fable 5
- - - - -
2 changed files:
- compiler/GHC/Cmm/Lint.hs
- compiler/GHC/Cmm/Pipeline.hs
Changes:
=====================================
compiler/GHC/Cmm/Lint.hs
=====================================
@@ -68,18 +68,19 @@ lintCmmGraph g = do
let
blocks = toBlockList g
labels = setFromList (map entryLabel blocks)
- cmmLocalLiveness platform g `seq` mapM_ (lintCmmBlock labels) blocks
+ reachable = setFromList (map entryLabel (revPostorder g))
+ cmmLocalLiveness platform g `seq` mapM_ (lintCmmBlock labels reachable) blocks
-- cmmLiveness throws an error if there are registers
-- live on entry to the graph (i.e. undefined
-- variables)
-lintCmmBlock :: LabelSet -> CmmBlock -> CmmLint ()
-lintCmmBlock labels block
+lintCmmBlock :: LabelSet -> LabelSet -> CmmBlock -> CmmLint ()
+lintCmmBlock labels reachable block
= addLintInfo (text "in basic block " <> ppr (entryLabel block)) $ do
let (_, middle, last) = blockSplit block
mapM_ lintCmmMiddle (blockToList middle)
- lintCmmLast labels last
+ lintCmmLast labels reachable last
-- -----------------------------------------------------------------------------
-- lintCmmExpr
@@ -188,8 +189,8 @@ lintCmmMiddle node = case node of
lintTarget arg_tys target
-lintCmmLast :: LabelSet -> CmmNode O C -> CmmLint ()
-lintCmmLast labels node = case node of
+lintCmmLast :: LabelSet -> LabelSet -> CmmNode O C -> CmmLint ()
+lintCmmLast labels reachable node = case node of
CmmBranch id -> checkTarget id
CmmCondBranch e t f _ -> do
@@ -208,7 +209,7 @@ lintCmmLast labels node = case node of
CmmCall { cml_target = target, cml_cont = cont } -> do
_ <- lintCmmExpr target
- maybe (return ()) checkTarget cont
+ maybe (return ()) checkCont cont
CmmForeignCall tgt _ args succ _ _ _ -> do
let lintArg expr = do
@@ -222,12 +223,22 @@ lintCmmLast labels node = case node of
lintCmmExpr expr
arg_tys <- mapM lintArg args
lintTarget arg_tys tgt
- checkTarget succ
+ checkCont succ
where
checkTarget id
| setMember id labels = return ()
| otherwise = cmmLintErr (text "Branch to nonexistent id" <+> ppr id)
+ -- A call continuation must be reachable even when the call itself is
+ -- in an unreachable block: callProcPoints collects continuations from
+ -- the whole block map, but only reachable blocks get stack maps
+ -- (#27368). See Note [unreachable blocks] in GHC.Cmm.Pipeline.
+ checkCont id = do
+ checkTarget id
+ unless (setMember id reachable) $
+ cmmLintErr (text "Call continuation" <+> ppr id
+ <+> text "is not reachable")
+
lintTarget :: [CmmType] -> ForeignTarget -> CmmLint ()
lintTarget _arg_tys (ForeignTarget e _) = do
mayNotMentionCallerSavedRegs (text "foreign target") e
=====================================
compiler/GHC/Cmm/Pipeline.hs
=====================================
@@ -368,6 +368,9 @@ but no stack map, and setInfoTableStackMap would panic (#27368).
The common block eliminator therefore ensures that the labels in its
unreachable leftovers coincide with labels of reachable code, see
Note [Resolving the CBE substitution] in GHC.Cmm.CommonBlockElim.
+-dcmm-lint checks the invariant: a call continuation must be
+reachable even when the call itself is not (see checkCont in
+GHC.Cmm.Lint).
To make unreachable blocks visible in -ddump-cmm-* output, add -dppr-debug.
-}
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/cbe671f40f376ff7a5bae61d69e9e49…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/cbe671f40f376ff7a5bae61d69e9e49…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/sjakobi/T27368-cbe-compress] 2 commits: Sharpen Note [Resolving the CBE substitution]
by Simon Jakobi (@sjakobi) 18 Aug '26
by Simon Jakobi (@sjakobi) 18 Aug '26
18 Aug '26
Simon Jakobi pushed to branch wip/sjakobi/T27368-cbe-compress at Glasgow Haskell Compiler / GHC
Commits:
5a1c5f69 by Simon Jakobi at 2026-08-17T19:18:55+02:00
Sharpen Note [Resolving the CBE substitution]
The Note claimed that CBE's input contains no unreachable blocks, but
the control-flow optimiser can leave unreachable goto-only blocks
behind (see the comment at incPreds/decPreds in GHC.Cmm.ContFlowOpt).
Scope the reachability claim to the blocks the resolved substitution
maps to, which is what the argument actually needs.
Assisted-by: Claude Fable 5
- - - - -
c61c4b5d by Simon Jakobi at 2026-08-17T19:29:24+02:00
Cmm: rewrite Note [unreachable blocks]
The Note mentioned only the control-flow optimiser and framed
unreachable blocks as a mere matter of code size. In fact common
block elimination leaves them behind too, and #27368 showed how a
pass can go wrong when they leak into the generated code. State the
actual invariant: only removeUnreachableBlocksProc guarantees their
removal, so every earlier pass must tolerate them.
Also:
* Turn elimCommonBlocks's intro comment into a haddock and document
its unreachable-block behaviour there.
* Drop the stale claim that ContFlowOpt had already removed
unreachable blocks -- shortcutting creates new ones.
Adapted from the doc changes in !16169.
Assisted-by: Claude Fable 5
- - - - -
2 changed files:
- compiler/GHC/Cmm/CommonBlockElim.hs
- compiler/GHC/Cmm/Pipeline.hs
Changes:
=====================================
compiler/GHC/Cmm/CommonBlockElim.hs
=====================================
@@ -30,34 +30,6 @@ import Control.Arrow (first, second)
import Data.List.NonEmpty (NonEmpty (..))
import qualified Data.List.NonEmpty as NE
--- -----------------------------------------------------------------------------
--- Eliminate common blocks
-
--- If two blocks are identical except for the label on the first node,
--- then we can eliminate one of the blocks. To ensure that the semantics
--- of the program are preserved, we have to rewrite each predecessor of the
--- eliminated block to proceed with the block we keep.
-
--- The algorithm iterates over the blocks in the graph,
--- checking whether it has seen another block that is equal modulo labels.
--- If so, then it adds an entry in a map indicating that the new block
--- is made redundant by the old block.
--- Otherwise, it is added to the useful blocks.
-
--- To avoid comparing every block with every other block repeatedly, we group
--- them by
--- * a hash of the block, ignoring labels (explained below)
--- * the list of outgoing labels
--- The hash is invariant under relabeling, so we only ever compare within
--- the same group of blocks.
---
--- The list of outgoing labels is updated as we merge blocks (that is why they
--- are not included in the hash, which we want to calculate only once).
---
--- All in all, two blocks should never be compared if they have different
--- hashes, and at most once otherwise. Previously, we were slower, and people
--- rightfully complained: #10397
-
{- Note [Resolving the CBE substitution]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The substitution built by `iterate` can contain chains: the winner of a
@@ -79,10 +51,13 @@ produced no stack map for it, and setInfoTableStackMap panicked.
With the substitution resolved, every edge -- including those in the
unreachable losing copies -- points at a surviving block, and every
-surviving block is reachable: the input graph contains no unreachable
-blocks (the control-flow optimiser runs first and drops them), and
-merging only diverts paths from losers to their body-equal winners.
-In particular the continuation of a call in a losing copy is also the
+block the resolved substitution maps to is reachable: merging diverts
+each loser's predecessors to its winner, so a winner inherits its
+losers' reachability. (The input graph is not entirely free of
+unreachable blocks -- the control-flow optimiser that runs first can
+leave unreachable goto-only blocks behind -- but such blocks contain
+no calls, and if one wins a merge it thereby becomes reachable.) In
+particular the continuation of a call in a losing copy is also the
continuation of its reachable winner, so stack layout has a stack map
for it.
@@ -90,17 +65,46 @@ Resolving also improves copyTicks: each loser's ticks are copied into
its final representative instead of into a dead intermediate block.
-}
--- TODO: Use optimization fuel
+-- | Merge identical blocks
+--
+-- If two blocks are identical except for the label on the first node,
+-- then we can eliminate one of the blocks. To ensure that the semantics
+-- of the program are preserved, we have to rewrite each predecessor of the
+-- eliminated block to proceed with the block we keep.
+--
+-- Unreachable blocks may occur both in the input and in the output:
+-- we process whatever the block map contains, and a merge leaves the
+-- losing copy behind, unreachable. See Note [unreachable blocks] in
+-- GHC.Cmm.Pipeline.
+--
+-- The algorithm iterates over the blocks in the graph,
+-- checking whether it has seen another block that is equal modulo labels.
+-- If so, then it adds an entry in a map indicating that the new block
+-- is made redundant by the old block.
+-- Otherwise, it is added to the useful blocks.
+--
+-- To avoid comparing every block with every other block repeatedly, we group
+-- them by
+-- * a hash of the block, ignoring labels (explained below)
+-- * the list of outgoing labels
+-- The hash is invariant under relabeling, so we only ever compare within
+-- the same group of blocks.
+--
+-- The list of outgoing labels is updated as we merge blocks (that is why they
+-- are not included in the hash, which we want to calculate only once).
+--
+-- All in all, two blocks should never be compared if they have different
+-- hashes, and at most once otherwise. Previously, we were slower, and people
+-- rightfully complained: #10397
elimCommonBlocks :: CmmGraph -> CmmGraph
+-- TODO: Use optimization fuel
elimCommonBlocks g = replaceLabels env' $ copyTicks env' g
where
-- See Note [Resolving the CBE substitution]
env' = mapMap (lookupBid env) env
env = iterate mapEmpty blocks_with_key
- -- The order of blocks doesn't matter here. While we could use
- -- revPostorder which drops unreachable blocks this is done in
- -- ContFlowOpt already which runs before this pass. So we use
- -- toBlockList since it is faster.
+ -- Block order is irrelevant here, so we use toBlockList, which
+ -- is cheaper than revPostorder.
groups = groupByInt hash_block (toBlockList g) :: [[CmmBlock]]
blocks_with_key = [ [ (successors b, [b]) | b <- bs] | bs <- groups]
=====================================
compiler/GHC/Cmm/Pipeline.hs
=====================================
@@ -353,10 +353,21 @@ _GLOBAL_OFFSET_TABLE_, regardless of which entry point we arrived via.
{- Note [unreachable blocks]
~~~~~~~~~~~~~~~~~~~~~~~~~
-The control-flow optimiser sometimes leaves unreachable blocks behind
-containing junk code. These aren't necessarily a problem, but
-removing them is good because it might save time in the native code
-generator later.
+Both the control-flow optimiser and the common block eliminator leave
+unreachable blocks behind. Several of the later passes drop them
+incidentally, by rebuilding the graph from a reachability traversal,
+but only removeUnreachableBlocksProc at the end of the pipeline
+guarantees a graph without them. Every pass in between must
+therefore tolerate unreachable blocks in its input, and must not let
+them affect the code generated for the reachable part.
+
+The latter requirement is easy to violate: callProcPoints collects
+call continuations from the whole block map, so a label mentioned
+only in unreachable code would become a proc point with an info table
+but no stack map, and setInfoTableStackMap would panic (#27368).
+The common block eliminator therefore ensures that the labels in its
+unreachable leftovers coincide with labels of reachable code, see
+Note [Resolving the CBE substitution] in GHC.Cmm.CommonBlockElim.
To make unreachable blocks visible in -ddump-cmm-* output, add -dppr-debug.
-}
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/a9cb1e12cfaee716f77c74e3e5bc81…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/a9cb1e12cfaee716f77c74e3e5bc81…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/sjakobi/T27368-cbe-compress] Cmm: resolve substitution chains in common block elimination
by Simon Jakobi (@sjakobi) 18 Aug '26
by Simon Jakobi (@sjakobi) 18 Aug '26
18 Aug '26
Simon Jakobi pushed to branch wip/sjakobi/T27368-cbe-compress at Glasgow Haskell Compiler / GHC
Commits:
a9cb1e12 by Simon Jakobi at 2026-08-17T18:43:13+02:00
Cmm: resolve substitution chains in common block elimination
The substitution built by elimCommonBlocks can map a label to another
eliminated label, but replaceLabels looks up each label only once. An
edge in a losing copy of a merged block could thus be rewritten to an
eliminated label. When that label was a call continuation,
callProcPoints turned it into a proc point, attachContInfoTables gave
it an info table, but stack layout produced no stack map for it, and
setInfoTableStackMap panicked.
Resolve the substitution before rewriting, so that every label in the
graph is mapped directly to its final representative. See
Note [Resolving the CBE substitution] in GHC.Cmm.CommonBlockElim.
Also: copyTicks now copies each loser's ticks into its final
representative instead of into a dead intermediate block.
The regression test distills the code shape that triggered the panic
when compiling GHC.CmmToAsm.Dwarf.Types with -O2 on top of !16168.
Fixes #27368
Assisted-by: Claude Fable 5
- - - - -
4 changed files:
- + changelog.d/27368
- compiler/GHC/Cmm/CommonBlockElim.hs
- + testsuite/tests/codeGen/should_compile/T27368.hs
- testsuite/tests/codeGen/should_compile/all.T
Changes:
=====================================
changelog.d/27368
=====================================
@@ -0,0 +1,4 @@
+section: compiler
+synopsis: Fix a ``setInfoTableStackMap`` panic caused by calls in unreachable Cmm blocks.
+issues: #27368
+mrs: !16543
=====================================
compiler/GHC/Cmm/CommonBlockElim.hs
=====================================
@@ -58,10 +58,44 @@ import qualified Data.List.NonEmpty as NE
-- hashes, and at most once otherwise. Previously, we were slower, and people
-- rightfully complained: #10397
+{- Note [Resolving the CBE substitution]
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The substitution built by `iterate` can contain chains: the winner of a
+merge may itself lose a later merge, giving k1 :-> k2 and k2 :-> k3.
+`lookupBid` resolves such chains transitively, but `replaceLabels`
+looks up each label only once. So before rewriting the graph we
+resolve the substitution, mapping every eliminated label directly to
+its final representative.
+
+This matters because the losing blocks of the merges stay in the block
+map, unreachable (removing them here would take an extra reachability
+pass). With an unresolved
+substitution, an edge in a losing block could be rewritten to k2 --
+itself an eliminated label with no live twin. In #27368 such a stranded
+label was a call continuation: callProcPoints folds over the whole
+block map, so the label became a proc point, attachContInfoTables gave
+it an info table, but stack layout (which walks reachable blocks only)
+produced no stack map for it, and setInfoTableStackMap panicked.
+
+With the substitution resolved, every edge -- including those in the
+unreachable losing copies -- points at a surviving block, and every
+surviving block is reachable: the input graph contains no unreachable
+blocks (the control-flow optimiser runs first and drops them), and
+merging only diverts paths from losers to their body-equal winners.
+In particular the continuation of a call in a losing copy is also the
+continuation of its reachable winner, so stack layout has a stack map
+for it.
+
+Resolving also improves copyTicks: each loser's ticks are copied into
+its final representative instead of into a dead intermediate block.
+-}
+
-- TODO: Use optimization fuel
elimCommonBlocks :: CmmGraph -> CmmGraph
-elimCommonBlocks g = replaceLabels env $ copyTicks env g
+elimCommonBlocks g = replaceLabels env' $ copyTicks env' g
where
+ -- See Note [Resolving the CBE substitution]
+ env' = mapMap (lookupBid env) env
env = iterate mapEmpty blocks_with_key
-- The order of blocks doesn't matter here. While we could use
-- revPostorder which drops unreachable blocks this is done in
=====================================
testsuite/tests/codeGen/should_compile/T27368.hs
=====================================
@@ -0,0 +1,20 @@
+-- Regression test for #27368: setInfoTableStackMap panicked because a
+-- call in an unreachable block returned to an eliminated label. The two
+-- branches below have identical suffixes from the inner case onwards,
+-- so common block elimination merges the duplicated call blocks in
+-- several rounds, building a substitution chain. See Note [Resolving
+-- the CBE substitution] in GHC.Cmm.CommonBlockElim.
+module T27368 (f) where
+
+{-# NOINLINE put #-}
+put :: Int -> Int -> IO ()
+put h x = if h + x == 12345 then errorWithoutStackTrace "boom" else pure ()
+
+data T = N | J Int | K
+
+f :: Int -> Bool -> T -> IO ()
+f h a t = do
+ if a
+ then do put h 1; case t of { N -> pure (); J _ -> put h 3; K -> put h 4 }; put h 0; put h 0
+ else do put h 2; case t of { N -> pure (); J _ -> put h 3; K -> put h 4 }; put h 0; put h 0
+ put h 0
=====================================
testsuite/tests/codeGen/should_compile/all.T
=====================================
@@ -150,3 +150,5 @@ test('T16351', normal, compile, ['-O2 -ddump-simpl -dno-typeable-binds -dsuppres
test('T20298a', normal, compile, ['-O2 -ddump-simpl -dno-typeable-binds -dsuppress-all -dsuppress-uniques'])
test('T20298b', normal, compile, ['-O2 -dno-bignum-rules -ddump-simpl -dno-typeable-binds -dsuppress-all -dsuppress-uniques'])
test('T20298c', normal, compile, ['-O2 -dno-builtin-rules -ddump-simpl -dno-typeable-binds -dsuppress-all -dsuppress-uniques'])
+
+test('T27368', normal, compile, ['-O2'])
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/a9cb1e12cfaee716f77c74e3e5bc81e…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/a9cb1e12cfaee716f77c74e3e5bc81e…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc] Pushed new branch wip/sjakobi/T27368-cbe-compress
by Simon Jakobi (@sjakobi) 18 Aug '26
by Simon Jakobi (@sjakobi) 18 Aug '26
18 Aug '26
Simon Jakobi pushed new branch wip/sjakobi/T27368-cbe-compress at Glasgow Haskell Compiler / GHC
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/tree/wip/sjakobi/T27368-cbe-compress
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0