[Git][ghc/ghc][wip/9.14.2-rc2] 50 commits: Fix errors in the documentation of the eventlog STOP_THREAD status codes
by Zubin (@wz1000) 05 Sep '26
by Zubin (@wz1000) 05 Sep '26
05 Sep '26
Zubin pushed to branch wip/9.14.2-rc2 at Glasgow Haskell Compiler / GHC
Commits:
fe3d06ae by Duncan Coutts at 2026-09-05T17:04:57+05:30
Fix errors in the documentation of the eventlog STOP_THREAD status codes
Fix the code for BlockedOnMsgThrowTo.
Document all the known historical warts.
Fixes issue #26867
(cherry picked from commit 10b4d3641f6fe7a7443bd621d62d1355a66f5a7c)
- - - - -
6041c2aa by Duncan Coutts at 2026-09-05T17:04:57+05:30
Document that eventlog thread stop code ThreadBlocked is no longer used
It has not been used since GHC 7.0.x (2011). In 7.2 all the BlockedOn*
codes were added, and these were and are used instead of ThreadBlocked.
(cherry picked from commit e1cece79a6c8e53796188acd543a8b565b18a3fb)
- - - - -
5d781adc by Duncan Coutts at 2026-09-05T17:04:57+05:30
Add a proper mapping to eventlog external thread stop status
That is the mapping from rts-internal codes, to the coes used in the
status field in the eventlog EVENT_STOP_THREAD event.
See issue #9003 for what goes wrong when we mess this up. In that
ticket, people note that we should really not require the internal
tso->why_blocked codes to leak into the external eventlog thread stop
codes. The same principle applies to the StgThreadReturnCode.
This change properly separates them, and explicitly maps between them
using a pair of (compact, constant) tables. These tables are pretty
small (with no alignment constraints) and will soon shrink so it seems
a sensible tradeoff.
We also introduce and use proper EVENT_STOP_THREAD constants in the
event log format header. Previously there was not specification in the
code for these (only in the docs): the values were encoded into the
conversion code.
This will allow us to renumber the internal why_blockd codes without
breaking the eventlog output.
(cherry picked from commit 795db1151237be5a3d0d2cdccb4c43309b587cdf)
- - - - -
5edbdaf0 by Duncan Coutts at 2026-09-05T17:04:57+05:30
Remove unused tso->block_info.wakeup member
Presumably it was used once, but not now.
(cherry picked from commit 6f1c8efa1b4c78273daca99f9dda0a7e3d771330)
- - - - -
68335668 by Duncan Coutts at 2026-09-05T17:04:57+05:30
Document StgTSOBlockInfo to say what cases use what members
In principle, tso->why_blocked is the tag for the StgTSOBlockInfo union,
so we should be able to say for each union member the why_blocked cases
that use that member.
(cherry picked from commit 740b88a980c4aa7f28fcf3aa89f7df8aea2813a9)
- - - - -
db985b1d by Duncan Coutts at 2026-09-05T17:04:57+05:30
Add a tso->block_info.mvar member and use it
in preference to the generic block_info.closure union member, with
casts.
The plan is that when we know what case we're in (via tso->why_blocked)
then we can always access the correct union member, and so we will only
need to access block_info.closure for generic cases where we don't know
or don't care.
(cherry picked from commit 5b92eae2638db71ff8725295ec2a8ca254c0720b)
- - - - -
08f71147 by Duncan Coutts at 2026-09-05T17:04:57+05:30
Add a tso->block_info.unused member and use it
in preference to the generic block_info.closure union member, with
casts.
The plan is that when we know what case we're in (via tso->why_blocked)
then we can always access the correct union member, and so we will only
need to access block_info.closure for generic cases where we don't know
or don't care.
(cherry picked from commit d931715fbea83ccd402d280965a5d096a9f72949)
- - - - -
54ea84e0 by Duncan Coutts at 2026-09-05T17:04:57+05:30
Avoid storing to tso->block_info.closure
In one case we can use a specific union member (.prev) instead. In
several cases the stores were in fact redundant because of subsequent
overwrites.
In scavengeTSO we replace setting tso->block_info.closure to a valid
closure, with an assertion that the block_info.unused is already set to
END_TSO_QUEUE which is a valid (static) closure.
(cherry picked from commit 47e28ebbb4e22ad2ddb27c3f4acc5df291276743)
- - - - -
e65235ff by Duncan Coutts at 2026-09-05T17:04:57+05:30
Renumber the tso->why_blocked constants
We can do this now because we have separated the internal values from
the external ones used in the eventlog.
This lets us put them back into a deliberate order and consolodate some
gaps.
More importantly, it is a prepation for a slightly more sophisticated
encoding.
(cherry picked from commit 96e4749d9f9b75e99d7b860325b92a5b3704e784)
- - - - -
bd345a3f by Duncan Coutts at 2026-09-05T17:04:57+05:30
Define constants for the existing stg_threadStatuszh return codes
The stg_threadStatuszh reuses the internal tso->why_blocked codes but
also extends them with a couple previously magic values. This is awkward
since we need to know what those magic values are so we don't
accidentally use those values to mean something else. By pulling a
definition up to where the why_blocked codes are defined we will be able
to avoid mistakenly assining those codes some meaning (or just changing
the BlockedThreadComplete, BlockedThreadKilled code if necessary).
(cherry picked from commit 8f62661c133338100803347084de3566e325e64c)
- - - - -
8e5bab1a by Duncan Coutts at 2026-09-05T17:04:57+05:30
Extend the tso->why_blocked encoding to indicate block_info closures
We use some bit tricks to cheaply and generically test if a
tso->why_blocked tag implies that the corresponding tso->block_info will
contain a non-trivial valid closure (i.e. not just block_info.unused set
to END_TSO_QUEUE).
In particular we arrange for most why_blocked values to naturally have a
distinguishing bit, but for the BlockedOn{Read,Write,Delay} cases, they
can come in either non-closure or closure forms. We allow an additional
bit to distinguish these cases. The non-closure forms are only from
legacy I/O managers: select and win32-legacy. So this extra bit
mechanism will be able to be retired once the legacy I/O managers are
themselves retired.
This means in a few places we need to untag the why_blocked value before
inspecting it, but in most places we do not.
(cherry picked from commit 42c69ae2d58310c703abaa4ec933973fa9b136d3)
- - - - -
abf3d259 by Duncan Coutts at 2026-09-05T17:04:57+05:30
Use BlockInfoForceNonClosure in the select I/O manager
(cherry picked from commit 7c64632b46ac660fc0779ae96d9a05d11537b743)
- - - - -
0038fe45 by Duncan Coutts at 2026-09-05T17:04:57+05:30
Use BlockInfoForceNonClosure in the win32-legacy I/O manager
for the BlockedOn{Read,Write} since these use the non-heap allocated
StgAsyncIOResult.
(cherry picked from commit 8fd7104a0d63208f8408a7e5cd185bd2020225c8)
- - - - -
820442e1 by Duncan Coutts at 2026-09-05T17:04:57+05:30
Enforce the why_blocked and block_info rules in checkTSO
We now check the cases wher IsBlockInfoClosure should hold, the cases
that are supposed to use block_info.unused == END_TSO_QUEUE, and which
cases are allowed to use BlockInfoForceNonClosure.
This partially enforces the use of why_blocked as a tag for the
block_info union. We could be stricter and check for the correct
expected info table for the closure cases.
(cherry picked from commit e0da603b8e24cfc633f29990be9ca7008f45b3c2)
- - - - -
f8cc6bb4 by Duncan Coutts at 2026-09-05T17:04:57+05:30
Use IsBlockInfoClosure to simplify several tests
In GC and generic traversal we need to know if we should look at the
block_info.closure or not. Now we can do just that using a cheap bit
test on the why_blocked tag.
This fixes issue 26717, where the problem was that some GC modes did not
know when to look at block_info.closure, because the poll I/O manager
uses a closure for BlockedOn{Read,Write} while the select I/O manager
uses a non-closure. Now this information is in the why_blocked tag
itself.
(cherry picked from commit 1dd0f381ab96d3f46e113c90858870e95bd3caba)
- - - - -
9715df03 by Duncan Coutts at 2026-09-05T17:04:57+05:30
Remove the now-unused scavengeTSOIOManager
The GC no longer has to delegate to the I/O manager, since it can use
IsBlockInfoClosure to decide things itself.
(cherry picked from commit 7a00ffbc311e6ee53e8b1b9181e37c3795a7b884)
- - - - -
2ab14436 by Duncan Coutts at 2026-09-05T17:04:57+05:30
Follow atomic access rules more consistently for tso->why_blocked
The rule is this:
store block_info *before* why_blocked
store why_blocked using store release
load why_blocked using load acquire
load block_info *after* why_blocked
This is a an atomic store release / load acquire pair and (if the reads
are in a separate thread to the writes, and the read receives the value
stored) then this guarantees a full "happens before" relationship of
these stores and loads.
In some cases, we do not need a full load acquire, because we don't read
the block_info at all and so do not need any ordering. In this case we
just need an atomic relaxed load.
This was being followed in most places, but not all. If there's good
reason in any case that we don't need atomic access, then we should
document that in a comment. In the absence of that I think it's easier
to follow the rule everywhere.
(cherry picked from commit 0874d965ef1fec847561418857e63c067b91b0b7)
- - - - -
75e63b6e by Duncan Coutts at 2026-09-05T17:04:57+05:30
Add a changelog entry
(cherry picked from commit 8f0bdbe138a2e1914cba7963b6931402f99c8710)
- - - - -
c1d2fdcc by Ben Gamari at 2026-09-05T17:04:57+05:30
base: Don't drop exception context in SomeException(toException)
For reasons that are lost to time, the implementation of [CLC #200]
that was merged inappropriately dropped `ExceptionContext` in the
`toException` implementation given to `SomeException`.
Fix this infelicity.
[CLC #200]: https://github.com/haskell/core-libraries-committee/issues/200
(cherry picked from commit 2ab02c579a9625438f5281a258051cb32ba40004)
- - - - -
d3d6c3aa by Vladislav Zavialov at 2026-09-05T17:04:57+05:30
Discard type arguments in tcPatToExpr (#27440, #27583)
The builder expression of an implicitly bidirectional pattern synonym must not
mention types written in the RHS:
* Invisible type arguments led to a panic (#27440)
* Required type arguments failed with out-of-scope variables (#27583)
Both are now discarded, following the precedent established by pattern
signatures (#9867).
Discarding type arguments takes some care: a type pattern cannot be told from a
value pattern by syntax alone, as the `type` keyword may be omitted. Consider:
data T a b c where
MkT :: forall a. forall b c -> a -> T a b c
pattern P :: x -> T x y z
pattern P x = MkT @a (type b) c x
In P's right-hand side, `@a` and `type b` are clearly type arguments, but what
about `c` and `x`? We can only tell by matching the patterns against MkT's
type. So tcPatToExpr now runs in TcM and matches the arguments against the
constructor's TyVarBinders using zipPatsBndrs, which is made public for this
purpose. The resulting builder is $bP x = MkT _ _ x.
See Note [Discarding types in the builder expression].
Test cases: T27440a T27440b T27440c T27440d T27440e
T27583a T27583b T27583c T27583d T27583e T27583f T27583g
Metric Increase: LinkableUsage02
Metric Decrease: T27336
Assisted-by: Claude Opus 5
(cherry picked from commit 4f98510802423dcd98fa62af77997189ee97a111)
- - - - -
869d6792 by sheaf at 2026-09-05T17:04:57+05:30
mkWpFun_FRR: fix ordering of coercion composition
When the subsumption machinery generates an eta-expansion, we must
perform a representation polymorphism check to ensure the lambda binder
it introduces has a fixed runtime representation.
This is done in GHC.Tc.Utils.mkWpFun_FRR.
This check involves composing quite a few coercions, arising from
representation-polymorphism checks on both the actual and expected
argument types. These coercions are then chained using HsWrapper
composition, <.>. The ordering of composition was incorrect, leading to
the Core Lint failure reported in #27639. This commit fixes that.
Fixes #27639
(cherry picked from commit eb1dcd4d98548b7bc64c323dc352ddbf149a91ec)
- - - - -
64ba5a42 by Vladislav Zavialov at 2026-09-05T17:04:58+05:30
Fix tcLookupId panic with RequiredTypeArguments and PatternSynonyms (#27586)
The arguments declared on the left-hand side of a pattern synonym are looked up
as term variables bound by its right-hand side. Prior to this patch, that lookup
panicked with RequiredTypeArguments:
data T a where
MkT :: forall a -> T a
pattern P :: Int -> T Int
pattern P x = MkT x
On the RHS, `x` looks like a term argument, so the renamer binds it in the term
namespace. Only during type checking does it turn out to be a type variable, so
the lookup on the LHS finds an ATyVar rather than an ATcId. As the lookup was
done with tcLookupId, it resulted in a panic.
Now the arguments are looked up with tcLookupPatSynArg, which reports an illegal
term-level use of `x`, just as an ordinary function definition `f (MkT x) = x`
does.
Test cases: T27586a T27586b T27586c
Assisted-by: Claude Opus 5
(cherry picked from commit b757727a78613e7437a713058c24b94e10697f47)
- - - - -
94c1ce13 by Zubin Duggal at 2026-09-05T17:04:58+05:30
ghc-internal: annotateSTM should use catchSTM# rather than catch#
A catch# frame inside a transaction breaks retry and async exception
delivery.
Fixes #27657
(cherry picked from commit fd22f71ee92595f4634206455d8eab111488d836)
- - - - -
0644c91c by fendor at 2026-09-05T17:04:58+05:30
GHCi: Fix order of `PackageDBFlag`s for interactive home unit
`PackageDBFlag`s are stored in reverse order of cli specification.
When sorting the `PackageDBFlag`s by longest common prefix, we need thus
to reverse the package db stacks before calculating the prefix.
We make sure to reverse the package db stack for the interactive home
unit to uphold that later specified package dbs overwrite earlier ones.
Resolved and adds regression test for #27640
(cherry picked from commit 06fde293f2e8c80db11f4d01fcdfc482c1128db5)
- - - - -
4ae61128 by Simon Peyton Jones at 2026-09-05T17:04:58+05:30
Never make an absent filler for a constraint type
mkAbsentFiller used isTerminatingType to decide, but that is not enough.
Consider
class Eq a => UC a where {}
let u :: UC Int -- UC Int is a "non-terminating type"
u = error "Absent"
let e :: Eq Int -- Eq Int is a "terminating type"
e = $p1UC u
We clearly must not make a filler for `e`, because we speculatively
evaluate it. But speculatively evaluating `e` forces `u`, so we must not
make one for `u` either.
Asking isDictTy instead is not enough either, because it does not catch a
constraint hidden behind an unreduced type family application:
type family F a :: Constraint
type instance F W = TC W
a :: F W => Int -> Int -- (F W) argument is absent
Oops! Entered absent arg Arg: irred
Type: F W
So play safe and use isPredTy: never make an absent filler for any
constraint-kinded type.
Fixes #27627
(cherry picked from commit 7bf546fc423e23cb649580016ee494efbfe144ab)
- - - - -
64deed29 by Zubin Duggal at 2026-09-05T17:04:58+05:30
Add tests for absent fillers at dictionary types
T27627 a unary class whose superclass is a non-unary class
T27627a ...whose superclass is a Constraint-kinded type family
T27627b ...whose superclass is a quantified constraint
T27627c a unary class applied to itself, (UC (UC (TC a)))
T27627e a (forall b. P b) dictionary that loops
(cherry picked from commit 5f474953d1880232b5e6c5741f08746e28cd25ab)
- - - - -
a99812b5 by Zubin Duggal at 2026-09-05T17:04:58+05:30
An abstract TyCon may hide a unary class
A class declared in an hs-boot file is an AbstractTyCon inside the
module loop, and compiling the real declaration may reveal it to be a
UnaryClassTyCon.
- isTerminatingType returned True for such AbstractTyCons
- IfaceToCore set the unary flag to False in the DFunId
So we could end up speculating bottom dictionaries because inside a module
loop we see an UnaryClassTyCon as an AbstractTyCon
Use isTerminatingTyCon, which returns False for an abstract TyCon.
The Bool in DFunId is now a cache for isTerminatingTyCon, set in
mkDFunIdDetails.
Fixes #27704
(cherry picked from commit cd5c6bcc0a59c7b4dc4627fdc7471dc7938d07c2)
- - - - -
5372f4e0 by Zubin Duggal at 2026-09-05T17:04:58+05:30
Specialise: don't replace dead args with absent fillers
specHeader decides an argument is dead by calling isDeadBinder on a binder of
the /optimised RHS/, then applies the filler to the /stable unfolding/
template instead. The two may differ, so the argument can be dead in
the RHS and not in the template.
The specialised function's unfolding then has an absent filler, and any call
site that inlines it evaluates the error thunk.
Dropping dead args in the specialiser is rarely worth it, to quote Simon,
"The later worker/wrapper pass will pick up the dead arg later if it is really dead. Keeps the specialiser simpler."
So instead of trying to check if the arg really is dead in the stable unfolding,
just drop the logic for dropping dead args in the specialiser altogeher.
Fixes #27703
(cherry picked from commit abfc224a27cf499390efc5fe1348301fefebb910)
- - - - -
ed00042b by Zubin Duggal at 2026-09-05T17:04:58+05:30
CorePrep: don't speculate a call across an hs-boot edge
We take care not to evaluate things that might be bottom, like a
looping dictionary group, but our analysis is defeated by boot files.
We only track recursion within a module, so two dictionaries that
depend on each other across a module loop each look non-recursive, and
we might speculate them.
Any recursion we cannot see must cross an hs-boot edge, so refuse to
speculate calls that cross one.
Fixes #27717
(cherry picked from commit 1557fd1cfb802d2608cb5b138eab664564f57fb7)
- - - - -
cbae9a67 by Cheng Shao at 2026-09-05T17:04:58+05:30
configure: bump LlvmMaxVersion to 23
This patch bumps `LlvmMaxVersion` to 23 to support LLVM 22.x releases.
(cherry picked from commit cc9cc6d5df7fb3845b1409fe708e1097896252a7)
- - - - -
1bdbfae8 by Cheng Shao at 2026-09-05T17:04:58+05:30
autoconf/ghc-toolchain: bump llvm upper bound to support llvm 23
This commit bumps llvm upper bound to support llvm 23.
(cherry picked from commit 56291fc550ec00784e46ec0c89ee65081090ae9f)
- - - - -
f920c42e by Cheng Shao at 2026-09-05T17:04:58+05:30
rts: fix compilation issues with clang 23
clang 23 has broadened `-Wall`/`-Wextra` ranges, exposing some minor
issues in the rts when building with validate flavours:
- Unused locals
- `#pragma GCC diagnostic pop` mismatch
This commit fixes those.
(cherry picked from commit 20eb3f415f9e04a003322d1a49d95e0aaf1029f2)
- - - - -
7b2c5057 by Cheng Shao at 2026-09-05T17:04:58+05:30
libffi: update to 3.7.1
Bumps libffi submodule.
(cherry picked from commit d7e36a486b224908b1e1f6e5d9eb04084819c805)
- - - - -
70df7c7a by Andreas Klebinger at 2026-09-05T17:04:58+05:30
cmm dumps: Add machop width info with -dppr-debug for infix ops.
(cherry picked from commit e9bbe8f924ec0b9d0772cf3d4f20aa6a25f28345)
- - - - -
877745f3 by Andreas Klebinger at 2026-09-05T17:04:58+05:30
CmmLint: Check for unsupported MachOp widths
machOpArgReps now maps MachOp + Width to a list of supported
argument widths or Nothing if the given operation is not supported
at the given width.
This allows us to check for nonsensical combinations like FloatToInt
at Word16.
Similarly we now check that every address is actually wordwidth.
(cherry picked from commit 86e3a9d8d0a85b5c80dad212cf3ec8fbf1ba72e6)
- - - - -
4037fda8 by Andreas Klebinger at 2026-09-05T17:04:58+05:30
arm64 ncg: The big subword truncation fix.
A set of slightly related fixes to arm subword handling:
Bitmask immediates:
Don't produce overflowing assembly literals.
There is still another bug here that causes us to miss some valid
literals but we will fix that later.
Improve subword truncation handling:
We now use a small set of helpers to truncate `Register` values rather
than truncating immediate `Reg` values which greatly simplifies the code
structure. This fixes a great many bugs to do with sign/zero extending subwords
or the lack thereof.
We now establish the invariant that subword values are zero-extended at
every site at which they come into "scope" of the ncg, and rely on the
invariant throughout rather than pessimistically inserting redundant
extensions in a hodgepodge manner at the use sites of these values.
This fixes at least the bugs described in issues #27533, #27430
#27537, #27538, #27539, and #27550. But likely more bugs yet not
found.
Subword ffi results:
Apply truncations when calling functions returning
subword values.
genCondJump:
Don't sign extend signed values in the input register as
it might map to a local variable, corrupting the value stored within.
Fix subword store/load instructions.:
We used to read those at 32bit width even for smaller values possibly
resulting in invalid memory access. Now we construct the suffix for
subword variants based on the instruction format for these.
(cherry picked from commit 13781cca5c24c2671d651fd2b13a561c7386fe3a)
- - - - -
d85ead2a by Andreas Klebinger at 2026-09-05T17:04:58+05:30
Add some test cases covering bugs in the arm ncg.
* Test for #27430 (subword ffi results)
* #27537 - subword conversions
* #27538 - subwords used in conditional
* #27533 - single byte read
(cherry picked from commit 94822c951c2a6f77c7766720e0d4ff7d4afb7290)
- - - - -
0be1741f by Andreas Klebinger at 2026-09-05T17:04:58+05:30
cmmLint: Lint against MO_FS_Truncate subword use.
(cherry picked from commit dd1ba88a70f47bead722e9dae8e91f9a617258aa)
- - - - -
c9fe6e61 by Zubin Duggal at 2026-09-05T17:08:44+05:30
Move HsStatic free-var test to typechecker
The renamer cannot tell free type variables from term variables, so
the warning fired on static forms mentioning a scoped type variable.
Don't do this.
Also correct the warning to say GHC 10.0 rather than 9.16.
Fixes #27664
- - - - -
c0a69e43 by fendor at 2026-09-05T17:08:44+05:30
Drop `preloadClosure` from `UnitState`
It is always hard-coded to the same value.
Backpack Unit instantiation isn't using it any more.
Allows us to simplify the API and get rid of `improveUnit`.
(cherry picked from commit fb5246adb7e10bd9ef07de314eaf98fbcfb729a1)
- - - - -
530b4c14 by fendor at 2026-09-05T17:08:44+05:30
Introduce global unit database cache
As a first step for better sharing of `UnitInfo` across `UnitEnv`,
we introduce a new datatype called `ExternalUnitDatabases`.
It primarily serves as an in-memory representation of *all*
`UnitDatabase`s across `UnitEnv`. This means, if multiple `HomeUnitEnv`s
depend on the same database, one way or another, we make sure that we
don't parse from disk every time.
Instead, we store the in-memory representation in `ExternalUnitDatabases`.
`ExternalUnitDatabaseCache` is the equivalent of `ExternalUnitState` in
the `UnitEnv`. It is a mutable variable wrapping `ExternalUnitDatabases`.
The mutable `ExternalUnitDatabaseCache` is used in `initUnits` to make
sure we don't parse the same unit database multiple times.
Almost by accident, we change the semantics of `initUnits` to honour
modifications to `packageDBFlags`.
The inability to change `packageDBFlags` while also reusing the already
parsed `UnitDatabase`s was reported in #26423 as a bug.
Hence, we think this behaviour change is warranted and acceptable,
especially since it comes with a breaking change to the `initUnits` API.
Add regression test for #26423
Closes #26423
(cherry picked from commit 5d0ab71ac1d01c2ef19bf3146adb7dac8733dca5)
- - - - -
01ad4c42 by fendor at 2026-09-05T17:08:44+05:30
Introduce UnitIndex for global external unit caching
`UnitInfo`s have been observed to cause a lot of memory usage in #27500.
Especially with multiple home units, as the same (external) units are
processed from scratch, even though most of the time we end up with
exactly the same `UnitInfo`.
We introduce a `UnitEnv` global cache that allows us to store external
unit information that is used across all `HomeUnitEnv`s.
The most important change in this commit is the introduction of the `UnitIndex`.
It stores a global mapping of `UnitId` -> `UnitInfo`, and `initUnits`
always uses the cached `UnitInfo` entry to populate each
`HomeUnitEnv`'s `UnitState`.
This allows us to ensure the following property:
> Each `UnitInfo` should be alive exactly once in GHC.
All `UnitState`s should reference 'UnitInfo's stored in the 'UnitIndex'.
This ensured by calling 'initUnits' with the 'UnitIndex'.
In addition, the `ExternalUnitDatabases` may also hold a reference
to each on-disk representation of `UnitInfo`.
This means, we impose an hard upper bound on the number of `UnitInfo`s
alive in the GHC session:
> The number of alive `UnitInfo`s closure objects must be the
> sum of all loaded unit database times two.
We add performance regression tests that make sure the number of live
`UnitInfo` cannot exceed this threshold.
Closes #27500
-------------------------
Metric Decrease:
MultiComponentModules
MultiComponentModulesRecomp
MultiComponentModulesRecomp100
mhu-perf
LinkableUsage02
-------------------------
These metrics increases are especially notable, as we are not even
sharing anything big but merely the global package database with 50
entries.
It shows how careful sharing of `UnitInfo` can improve memory usage.
We expect this to be much more notable when the whole cabal package
database is shared across multiple home units.
`LinkableUsage02` metric decreases on unreg and i386 platform, only.
---
Technical details
To share the `UnitInfo`s correctly, it is important that we extract
the `WireMap` into the `UnitIndex`. At the moment of writing, `WireMap`
must be globally the same for all `HomeUnitEnv`s.
This is important, as we could otherwise not cache the "fully-resolved"
`UnitInfo`, as we don't change the `UnitId` or `unitAbiHash` when
resolving wired-in units. Thus, there could be ambiguities, when the
`WireMap` is not the same for all `UnitState`s across the `UnitEnv`.
We consider a `UnitInfo` fully-resolved, if wired-in units have been
updated, the `UnitInfo` has been validated and variables in the unit
config, such as `${pkgroot}` have been resolved.
Updating the wired-in units requires the `WireMap` to be globally the
same.
(cherry picked from commit 6cce494a7ec43953c7a949e0d8ce712abfe73da7)
- - - - -
0a4461cf by fendor at 2026-09-05T17:08:44+05:30
Reuse the UnitIndexCache after initialising multiple home units
(cherry picked from commit 024c4d04a98f7e27b3dcb93f9b038f9b6b5a1ad9)
- - - - -
6db2f312 by Andreas Klebinger at 2026-09-05T17:08:44+05:30
Specialise: Stop looping on recursive dictionaries in interestingDict
interestingDict now doesn't look through loopbreaker unfoldings.
Doing so would cause infinite loops on certain dictionaries.
Fixes #27705.
(cherry picked from commit 578bd18509f0d2aeb004231a197f7f3898f86a2a)
- - - - -
879cb156 by Zubin Duggal at 2026-09-05T17:08:44+05:30
hadrian: constrain splitmix to keep building on Debian 9
- - - - -
0214fea4 by Zubin Duggal at 2026-09-05T17:08:44+05:30
Bump file-io submodule to 0.1.6
- - - - -
ddb4069b by Zubin Duggal at 2026-09-05T17:08:44+05:30
Bump haskeline submodule to 0.8.5.0
- - - - -
1ae612e4 by Zubin Duggal at 2026-09-05T17:08:44+05:30
Bump os-string submodule to 2.0.10
- - - - -
2b053def by Zubin Duggal at 2026-09-05T17:08:44+05:30
Bump directory submodule to 1.3.11.0
- - - - -
6a0257f8 by Zubin Duggal at 2026-09-05T17:08:44+05:30
Prepare 9.14.2-rc2
-------------------------
Metric Decrease:
T4029
-------------------------
- - - - -
230 changed files:
- − changelog.d/27626
- compiler/GHC.hs
- compiler/GHC/Cmm/Expr.hs
- compiler/GHC/Cmm/Lint.hs
- compiler/GHC/Cmm/MachOp.hs
- compiler/GHC/Cmm/Parser.y
- compiler/GHC/CmmToAsm/AArch64/CodeGen.hs
- compiler/GHC/CmmToAsm/AArch64/Instr.hs
- compiler/GHC/CmmToAsm/AArch64/Ppr.hs
- compiler/GHC/Core.hs
- compiler/GHC/Core/Opt/Specialise.hs
- compiler/GHC/Core/Opt/WorkWrap/Utils.hs
- compiler/GHC/Core/TyCon.hs
- compiler/GHC/Core/Type.hs
- compiler/GHC/Core/Utils.hs
- compiler/GHC/CoreToStg/Prep.hs
- compiler/GHC/Driver/Backpack.hs
- compiler/GHC/Driver/Env.hs
- compiler/GHC/Driver/Main.hs
- compiler/GHC/Driver/Session/Units.hs
- compiler/GHC/Iface/Load.hs
- compiler/GHC/Iface/Recomp.hs
- compiler/GHC/IfaceToCore.hs
- compiler/GHC/Rename/Expr.hs
- compiler/GHC/Tc/Errors/Ppr.hs
- compiler/GHC/Tc/Gen/Expr.hs
- compiler/GHC/Tc/Gen/Pat.hs
- compiler/GHC/Tc/TyCl/PatSyn.hs
- compiler/GHC/Tc/Utils/Unify.hs
- compiler/GHC/Types/Demand.hs
- compiler/GHC/Types/Id/Info.hs
- compiler/GHC/Types/Id/Make.hs
- compiler/GHC/Types/Literal.hs
- compiler/GHC/Types/Unique.hs
- compiler/GHC/Unit.hs
- compiler/GHC/Unit/Env.hs
- + compiler/GHC/Unit/External/Database.hs
- + compiler/GHC/Unit/External/Index.hs
- + compiler/GHC/Unit/External/ModuleOrigin.hs
- + compiler/GHC/Unit/External/Providers.hs
- + compiler/GHC/Unit/External/Query.hs
- + compiler/GHC/Unit/External/Substitution.hs
- + compiler/GHC/Unit/External/Validate.hs
- + compiler/GHC/Unit/External/Visibility.hs
- + compiler/GHC/Unit/External/Wired.hs
- compiler/GHC/Unit/Home/Graph.hs
- compiler/GHC/Unit/Info.hs
- compiler/GHC/Unit/State.hs
- compiler/GHC/Unit/Types.hs
- compiler/ghc.cabal.in
- configure.ac
- docs/users_guide/9.14.2-notes.rst
- docs/users_guide/eventlog-formats.rst
- ghc/GHCi/UI.hs
- hadrian/cabal.project
- hadrian/src/Rules/Generate.hs
- libffi-tarballs
- libraries/base/changelog.md
- libraries/directory
- libraries/file-io
- libraries/ghc-heap/GHC/Exts/Heap/FFIClosures_ProfilingDisabled.hsc
- libraries/ghc-heap/GHC/Exts/Heap/FFIClosures_ProfilingEnabled.hsc
- libraries/ghc-internal/src/GHC/Internal/Conc/Sync.hs
- libraries/ghc-internal/src/GHC/Internal/Exception/Type.hs
- libraries/ghc-internal/src/GHC/Internal/STM.hs
- libraries/haskeline
- libraries/os-string
- rts/IOManager.c
- rts/IOManager.h
- rts/Messages.c
- rts/PrimOps.cmm
- rts/ProfHeap.c
- rts/RaiseAsync.c
- rts/RaiseAsync.h
- rts/STM.c
- rts/Schedule.c
- rts/Threads.c
- rts/Trace.c
- rts/Trace.h
- rts/TraverseHeap.c
- rts/eventlog/EventLog.c
- rts/include/rts/Constants.h
- rts/include/rts/EventLogFormat.h
- rts/include/rts/storage/TSO.h
- rts/posix/Select.c
- rts/prim/atomic.c
- rts/sm/Compact.c
- rts/sm/NonMovingMark.c
- rts/sm/Sanity.c
- rts/sm/Scav.c
- rts/win32/AsyncMIO.c
- + testsuite/tests/codeGen/should_run/T27430.hs
- + testsuite/tests/codeGen/should_run/T27430.stdout
- + testsuite/tests/codeGen/should_run/T27430_c.c
- + testsuite/tests/codeGen/should_run/T27533.hs
- + testsuite/tests/codeGen/should_run/T27533.stdout
- + testsuite/tests/codeGen/should_run/T27533_cmm.cmm
- + testsuite/tests/codeGen/should_run/T27537.hs
- + testsuite/tests/codeGen/should_run/T27537.stdout
- + testsuite/tests/codeGen/should_run/T27538.hs
- + testsuite/tests/codeGen/should_run/T27538.stdout
- testsuite/tests/codeGen/should_run/all.T
- + testsuite/tests/concurrent/should_run/T27657a.hs
- + testsuite/tests/concurrent/should_run/T27657a.stdout
- + testsuite/tests/concurrent/should_run/T27657b.hs
- + testsuite/tests/concurrent/should_run/T27657b.stdout
- testsuite/tests/concurrent/should_run/all.T
- + testsuite/tests/core-to-stg/T27627/Callee.hs
- + testsuite/tests/core-to-stg/T27627/Caller.hs
- + testsuite/tests/core-to-stg/T27627/Main.hs
- + testsuite/tests/core-to-stg/T27627/T27627.stdout
- + testsuite/tests/core-to-stg/T27627/all.T
- + testsuite/tests/core-to-stg/T27627a/Callee.hs
- + testsuite/tests/core-to-stg/T27627a/Caller.hs
- + testsuite/tests/core-to-stg/T27627a/Main.hs
- + testsuite/tests/core-to-stg/T27627a/T27627a.stdout
- + testsuite/tests/core-to-stg/T27627a/all.T
- + testsuite/tests/core-to-stg/T27627b/Callee.hs
- + testsuite/tests/core-to-stg/T27627b/Caller.hs
- + testsuite/tests/core-to-stg/T27627b/Main.hs
- + testsuite/tests/core-to-stg/T27627b/T27627b.stdout
- + testsuite/tests/core-to-stg/T27627b/all.T
- + testsuite/tests/core-to-stg/T27627c/Callee.hs
- + testsuite/tests/core-to-stg/T27627c/Caller.hs
- + testsuite/tests/core-to-stg/T27627c/Main.hs
- + testsuite/tests/core-to-stg/T27627c/T27627c.stdout
- + testsuite/tests/core-to-stg/T27627c/all.T
- + testsuite/tests/core-to-stg/T27627e.hs
- + testsuite/tests/core-to-stg/T27627e.stdout
- + testsuite/tests/core-to-stg/T27627f/Callee.hs
- + testsuite/tests/core-to-stg/T27627f/Caller.hs
- + testsuite/tests/core-to-stg/T27627f/Inst.hs
- + testsuite/tests/core-to-stg/T27627f/Main.hs
- + testsuite/tests/core-to-stg/T27627f/T27627f.stdout
- + testsuite/tests/core-to-stg/T27627f/all.T
- + testsuite/tests/core-to-stg/T27704/Callee.hs
- + testsuite/tests/core-to-stg/T27704/Callee.hs-boot
- + testsuite/tests/core-to-stg/T27704/Main.hs
- + testsuite/tests/core-to-stg/T27704/Mid.hs
- + testsuite/tests/core-to-stg/T27704/T27704.stdout
- + testsuite/tests/core-to-stg/T27704/all.T
- + testsuite/tests/core-to-stg/T27704a/Callee.hs
- + testsuite/tests/core-to-stg/T27704a/Callee.hs-boot
- + testsuite/tests/core-to-stg/T27704a/Main.hs
- + testsuite/tests/core-to-stg/T27704a/Mid.hs
- + testsuite/tests/core-to-stg/T27704a/T27704a.stdout
- + testsuite/tests/core-to-stg/T27704a/all.T
- + testsuite/tests/core-to-stg/T27717/Callee.hs
- + testsuite/tests/core-to-stg/T27717/Callee.hs-boot
- + testsuite/tests/core-to-stg/T27717/Main.hs
- + testsuite/tests/core-to-stg/T27717/Mid.hs
- + testsuite/tests/core-to-stg/T27717/T27717.stdout
- + testsuite/tests/core-to-stg/T27717/Ty.hs
- + testsuite/tests/core-to-stg/T27717/all.T
- testsuite/tests/core-to-stg/all.T
- testsuite/tests/count-deps/CountDepsAst.stdout
- testsuite/tests/count-deps/CountDepsParser.stdout
- + testsuite/tests/driver/T26423/Hello.hs
- + testsuite/tests/driver/T26423/Makefile
- + testsuite/tests/driver/T26423/T26423.hs
- + testsuite/tests/driver/T26423/T26423.stderr
- + testsuite/tests/driver/T26423/T26423.stdout
- + testsuite/tests/driver/T26423/all.T
- + testsuite/tests/driver/T26423/test/Test.hs
- + testsuite/tests/driver/T26423/test/test.pkg
- + testsuite/tests/driver/TUnitInfo/Foo.hs
- + testsuite/tests/driver/TUnitInfo/Makefile
- + testsuite/tests/driver/TUnitInfo/all.T
- + testsuite/tests/driver/TUnitInfo/genMhu.sh
- + testsuite/tests/driver/TUnitInfo/generic-unit-info-space-mhu.stdout
- + testsuite/tests/driver/TUnitInfo/generic-unit-info-space-single.stdout
- + testsuite/tests/driver/TUnitInfo/generic-unit-info-space.hs
- + testsuite/tests/driver/TUnitInfo/generic-unit-info-space.stdout
- testsuite/tests/ghc-e/should_fail/T9930fail.stderr
- testsuite/tests/ghc-e/should_run/ghc-e005.stderr
- + testsuite/tests/ghci/prog-mhu007/Makefile
- + testsuite/tests/ghci/prog-mhu007/a/A.hs
- + testsuite/tests/ghci/prog-mhu007/all.T
- + testsuite/tests/ghci/prog-mhu007/b/B.hs
- + testsuite/tests/ghci/prog-mhu007/prog-mhu007.script
- + testsuite/tests/ghci/prog-mhu007/prog-mhu007.stdout
- + testsuite/tests/ghci/prog-mhu007/testpkg-bar/Bar.hs
- + testsuite/tests/ghci/prog-mhu007/testpkg-bar/testpkg-bar.pkg
- + testsuite/tests/ghci/prog-mhu007/testpkg-foo/Foo.hs
- + testsuite/tests/ghci/prog-mhu007/testpkg-foo/testpkg-foo.pkg
- + testsuite/tests/ghci/prog-mhu007/unitA
- + testsuite/tests/ghci/prog-mhu007/unitB
- + testsuite/tests/patsyn/should_compile/T27440a.hs
- + testsuite/tests/patsyn/should_compile/T27440b.hs
- + testsuite/tests/patsyn/should_compile/T27440c.hs
- testsuite/tests/patsyn/should_compile/all.T
- + testsuite/tests/patsyn/should_fail/T27440d.hs
- + testsuite/tests/patsyn/should_fail/T27440d.stderr
- testsuite/tests/patsyn/should_fail/all.T
- + testsuite/tests/rep-poly/T27639.hs
- testsuite/tests/rep-poly/all.T
- testsuite/tests/simplCore/should_compile/T17966.stderr
- testsuite/tests/simplCore/should_compile/T7785.stderr
- testsuite/tests/simplCore/should_compile/spec004.hs
- testsuite/tests/simplCore/should_compile/spec004.stderr
- + testsuite/tests/simplCore/should_run/T27703/Lib.hs
- + testsuite/tests/simplCore/should_run/T27703/Main.hs
- + testsuite/tests/simplCore/should_run/T27703/T27703.stdout
- + testsuite/tests/simplCore/should_run/T27703/all.T
- + testsuite/tests/simplCore/should_run/T27705.hs
- + testsuite/tests/simplCore/should_run/T27705.stdout
- + testsuite/tests/simplCore/should_run/T27705_Inst.hs
- testsuite/tests/simplCore/should_run/all.T
- testsuite/tests/typecheck/should_compile/T26718.stderr
- + testsuite/tests/typecheck/should_compile/T27664.hs
- testsuite/tests/typecheck/should_compile/all.T
- + testsuite/tests/vdq-rta/should_compile/T27583a.hs
- + testsuite/tests/vdq-rta/should_compile/T27583b.hs
- + testsuite/tests/vdq-rta/should_compile/T27583c.hs
- + testsuite/tests/vdq-rta/should_compile/T27583d.hs
- + testsuite/tests/vdq-rta/should_compile/T27583e.hs
- + testsuite/tests/vdq-rta/should_compile/T27583g.hs
- testsuite/tests/vdq-rta/should_compile/all.T
- + testsuite/tests/vdq-rta/should_fail/T27440e.hs
- + testsuite/tests/vdq-rta/should_fail/T27440e.stderr
- + testsuite/tests/vdq-rta/should_fail/T27583f.hs
- + testsuite/tests/vdq-rta/should_fail/T27583f.stderr
- + testsuite/tests/vdq-rta/should_fail/T27586a.hs
- + testsuite/tests/vdq-rta/should_fail/T27586a.stderr
- + testsuite/tests/vdq-rta/should_fail/T27586b.hs
- + testsuite/tests/vdq-rta/should_fail/T27586b.stderr
- + testsuite/tests/vdq-rta/should_fail/T27586c.hs
- + testsuite/tests/vdq-rta/should_fail/T27586c.stderr
- testsuite/tests/vdq-rta/should_fail/all.T
- utils/haddock/haddock-api/src/Haddock.hs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/1ec75528d360e85631a871ff147282…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/1ec75528d360e85631a871ff147282…
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/epa-tidy-locatedxxx-22] 5 commits: Parentheses in prefix GADT constructors (#27423)
by Alan Zimmerman (@alanz) 05 Sep '26
by Alan Zimmerman (@alanz) 05 Sep '26
05 Sep '26
Alan Zimmerman pushed to branch wip/az/epa-tidy-locatedxxx-22 at Glasgow Haskell Compiler / GHC
Commits:
eaa95a6f by Andrei Borzenkov at 2026-09-04T08:08:40-04:00
Parentheses in prefix GADT constructors (#27423)
Updated `splitLHsGadtTy` to allow looking
through the parentheses for inner binders. General example
of a code pattern that's allowed now:
data S a where
MkS :: (forall a. S a)
That should work now with any combination of nested
foralls and parentheses.
We don't perform parenthesis unwrapping for record
GADT constructors in accordance with GHC Proposal #402.
To this end `con_inner_bndrs` no longer stores plain forall
telescopes: `[HsForAllTelescope pass]` is replaced with
`[LHsGadtTelescope pass]`, a new `HsArg`-style type whose
`HsGadtForAll` holds an inner telescope and whose `HsGadtPar`
holds a pair of parentheses. The parentheses carry no meaning
for renaming or type checking; the only reason to record them
is exact-printing.
Updated `pprConDecl` to improve the `parse == parse . ppr . parse`
property of GADT pretty-printing.
The pretty printer can now output code that's similar to this:
data T a where
MkT1 :: (forall a. T a)
MkT2 :: forall . forall a. T a
These are special cases of inner forall binders
for prefix GADT constructors, when we have either
implicit or zero explicit outer binders.
- - - - -
639456a8 by Evgeny Malyshev at 2026-09-04T08:09:25-04:00
HsPat: Add spacing to unary tuple patterns
Use hsep when printing unary boxed TuplePat applications so that the
MkSolo constructor is separated from its argument. The old hcat rendered
wildcard and parenthesized arguments as MkSolo_ and MkSolo().
Add T27034 to exercise the HsPat splice-dump path with wildcard and
variable arguments, and update existing affected golden output.
Fixes #27034
Assisted-By: OpenAI Codex
- - - - -
2908ab36 by Simon Jakobi at 2026-09-05T07:22:17-04:00
X86 NCG: use btr/bts/btc for single-bit operations
Previously the Cmm patterns
x & ~(1 << i)
x | (1 << i)
x ^ (1 << i)
compiled to mov/shl/not/and-style sequences of 3-4 instructions. Now
they compile to a single btr, bts or btc, matching what C compilers
produce.
When the bit index is a literal, constant folding has already collapsed
these patterns into ones with a literal mask, such as
x & 0xfffffeffffffffff for x & ~(1 << 40). Such masks are now also
compiled to a bit-test instruction when they don't fit in an imm32 and
would otherwise have to be loaded into a register first.
For a variable bit index, this applies only when the shift is unchecked
(uncheckedShiftL#, Data.Bits.unsafeShiftL): the bounds-checked shiftL
used by e.g. the default clearBit/setBit/complementBit implementations
wraps the shift in a bounds mask that this optimisation does not see
through. With a literal index, the bounds mask is constant-folded away,
so the checked operations benefit too.
See Note [Bit-test instructions] in GHC.CmmToAsm.X86.CodeGen.
Fixes #25233.
Assisted-by: Claude Fable 5
- - - - -
c673ecf0 by Simon Peyton Jones at 2026-09-05T07:22:59-04:00
Move HsStatic free-var test to typechecker
A `static` form should have no free *term* variables, but it
can have free *type* variables. Alas, the renamer does not really know what
is a term variable and what is a type variable, because of required type
arguments. This patch moves the test to the typechecker, which does know.
Addresses #27664
- - - - -
d78b5aaa by Alan Zimmerman at 2026-09-05T11:32:16+00:00
EPA Fix HsCmdDo exact print with comments
Exact printing of HsCmdDo was ignoring the location for the do
statements, and this is an annotation that can have comments in it.
Update it so we print the statements as a unit, including any
comments.
Also add the result of auditing that we capture comments in all needed
places, noting that the remaining Anno SrcSpan instances are benign.
- - - - -
56 changed files:
- + changelog.d/T27764
- + changelog.d/allow-gadt-prefix-con-parens
- + changelog.d/ncg-x86-bit-test-instructions
- compiler/GHC/CmmToAsm/X86/CodeGen.hs
- compiler/GHC/CmmToAsm/X86/Instr.hs
- compiler/GHC/CmmToAsm/X86/Ppr.hs
- compiler/GHC/Hs/Decls.hs
- compiler/GHC/Hs/Doc.hs
- compiler/GHC/Hs/Expr.hs
- compiler/GHC/Hs/Extension/Pass.hs
- compiler/GHC/Hs/Instances.hs
- compiler/GHC/Hs/Pat.hs
- compiler/GHC/Hs/Type.hs
- compiler/GHC/HsToCore/Quote.hs
- compiler/GHC/Iface/Ext/Ast.hs
- compiler/GHC/Rename/Expr.hs
- compiler/GHC/Rename/HsType.hs
- compiler/GHC/Rename/Module.hs
- compiler/GHC/Tc/Gen/Expr.hs
- compiler/GHC/Tc/Gen/HsType.hs
- compiler/GHC/Tc/TyCl.hs
- compiler/Language/Haskell/Syntax/Decls.hs
- compiler/Language/Haskell/Syntax/Type.hs
- docs/users_guide/exts/gadt_syntax.rst
- + testsuite/tests/codeGen/should_gen_asm/T25233.asm
- + testsuite/tests/codeGen/should_gen_asm/T25233.hs
- + testsuite/tests/codeGen/should_gen_asm/T25233b.asm
- + testsuite/tests/codeGen/should_gen_asm/T25233b.cmm
- testsuite/tests/codeGen/should_gen_asm/all.T
- − testsuite/tests/gadt/T14320.stderr
- testsuite/tests/gadt/T18191.hs
- testsuite/tests/gadt/T18191.stderr
- + testsuite/tests/gadt/T27423a.hs
- + testsuite/tests/gadt/T27423b.hs
- + testsuite/tests/gadt/T27423b.stderr
- testsuite/tests/gadt/all.T
- testsuite/tests/printer/Makefile
- + testsuite/tests/printer/T27423c.hs
- testsuite/tests/printer/all.T
- testsuite/tests/rename/should_fail/RnStaticPointersFail01.stderr
- testsuite/tests/rename/should_fail/RnStaticPointersFail03.stderr
- testsuite/tests/rename/should_fail/T26545.stderr
- testsuite/tests/th/T17380.stderr
- testsuite/tests/th/T18612.stderr
- + testsuite/tests/th/T27034.hs
- + testsuite/tests/th/T27034.stderr
- testsuite/tests/th/T8761.stderr
- testsuite/tests/th/all.T
- + testsuite/tests/typecheck/should_compile/T27664.hs
- testsuite/tests/typecheck/should_compile/all.T
- utils/check-exact/ExactPrint.hs
- utils/haddock/haddock-api/src/Haddock/Backends/Hoogle.hs
- utils/haddock/haddock-api/src/Haddock/Convert.hs
- utils/haddock/haddock-api/src/Haddock/GhcUtils.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/d7fd9a062f1e27a42c6708357eb20a…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/d7fd9a062f1e27a42c6708357eb20a…
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][master] Move HsStatic free-var test to typechecker
by Marge Bot (@marge-bot) 05 Sep '26
by Marge Bot (@marge-bot) 05 Sep '26
05 Sep '26
Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
c673ecf0 by Simon Peyton Jones at 2026-09-05T07:22:59-04:00
Move HsStatic free-var test to typechecker
A `static` form should have no free *term* variables, but it
can have free *type* variables. Alas, the renamer does not really know what
is a term variable and what is a type variable, because of required type
arguments. This patch moves the test to the typechecker, which does know.
Addresses #27664
- - - - -
9 changed files:
- + changelog.d/T27764
- compiler/GHC/Hs/Expr.hs
- compiler/GHC/Rename/Expr.hs
- compiler/GHC/Tc/Gen/Expr.hs
- testsuite/tests/rename/should_fail/RnStaticPointersFail01.stderr
- testsuite/tests/rename/should_fail/RnStaticPointersFail03.stderr
- testsuite/tests/rename/should_fail/T26545.stderr
- + testsuite/tests/typecheck/should_compile/T27664.hs
- testsuite/tests/typecheck/should_compile/all.T
Changes:
=====================================
changelog.d/T27764
=====================================
@@ -0,0 +1,8 @@
+section: compiler
+synopsis: Fix bug in ``static`` forms
+issues: #27664
+mrs: !16624
+description: A ``static`` form should have no free *term* variables, but it
+ can have free *type* variables. Alas, the renamer does not really know what
+ is a term variable and what is a type variable, because of required type
+ arguments. This patch moves the test to the typechecker, which does know.
=====================================
compiler/GHC/Hs/Expr.hs
=====================================
@@ -39,6 +39,7 @@ import GHC.Tc.Types.ErrCtxt
import GHC.Types.Id.Info ( RecSelParent )
import GHC.Types.Name
import GHC.Types.Name.Reader
+import GHC.Types.Name.Set( FreeNames )
import GHC.Types.Basic
import GHC.Types.Fixity
import GHC.Types.SourceText
@@ -363,7 +364,9 @@ type instance XArithSeq GhcTc = PostTcExpr
type instance XProc (GhcPass _) = (EpToken "proc", TokRarrow)
type instance XStatic GhcPs = EpToken "static"
-type instance XStatic GhcRn = NoExtField
+type instance XStatic GhcRn = FreeNames
+ -- Free variables of the body; we can't tell if they are
+ -- type or term variables until we are typechecking
type instance XStatic GhcTc = (Type, HsExpr GhcTc)
-- Type of expression, and the (fromStaticPtr function)
-- These are stored for convenience as the wiring in
=====================================
compiler/GHC/Rename/Expr.hs
=====================================
@@ -667,18 +667,7 @@ rnExpr e@(HsStatic _ expr)
-- Rename the payload
; (expr',fvs) <- rnLExpr expr
- -- Check that the free variables of the static form are top-level defined
- -- It's OK to use nonDetEltsUniqSet here as the only side effects of
- -- checkClosedInStaticForm are error messages.
- -- See (SF2) Note [Grand plan for static forms] in GHC.Iface.Tidy.StaticPtrTable
- ; mapM_ check_fv (nonDetEltsUniqSet fvs)
-
- ; return (HsStatic noExtField expr', fvs) }
- where
- check_fv :: Name -> RnM ()
- -- Check for free vars not defined at top level
- check_fv n = unless (isExternalName n) $
- addErrTc (TcRnStaticFormNotClosed n)
+ ; return (HsStatic fvs expr', fvs) }
{-
************************************************************************
=====================================
compiler/GHC/Tc/Gen/Expr.hs
=====================================
@@ -602,13 +602,19 @@ tcExpr (HsProc x pat cmd) res_ty
-- and wrap (static e) in a call to
-- fromStaticPtr :: IsStatic p => StaticPtr a -> p a
-tcExpr (HsStatic _ expr) res_ty
+tcExpr (HsStatic free_names expr) res_ty
= do { res_ty <- expTypeToType res_ty
; (co, (p_ty, expr_ty)) <- matchExpectedAppTy res_ty
; (expr', lie) <- captureConstraints $
addErrCtxt (StaticFormCtxt expr) $
tcCheckPolyExprNC expr expr_ty
+ -- Check that the free variables of the static form are top-level defined
+ -- It's OK to use nonDetEltsUniqSet here as the only side effects of
+ -- checkClosedInStaticForm are error messages.
+ -- See (SF2) Note [Grand plan for static forms] in GHC.Iface.Tidy.StaticPtrTable
+ ; mapM_ check_free_name (nonDetEltsUniqSet free_names)
+
-- Emit an implication that captures the constraints of `expr`,
-- but with a `ic_info` of StaticFormSkol
-- See #13499 for an explanation of why this is the right thing to do:
@@ -637,6 +643,24 @@ tcExpr (HsStatic _ expr) res_ty
HsStatic (static_expr_ty, mkHsWrap wrap fromStaticPtr)
expr''
}
+ where
+ check_free_name :: Name -> TcM ()
+ -- Check for free /term/ vars not defined at top level
+ -- We use isExternalName as a proxy for top-level-defined
+ check_free_name n
+ = do { mb_thing <- tcLookupLcl_maybe n
+ ; case mb_thing of
+ Nothing -> return () -- Imports, tycons, classes allowed
+ Just (ATcId {}) -> unless (isExternalName n) $
+ addErrTc (TcRnStaticFormNotClosed n)
+
+ Just (ATyVar {}) -> return () -- Free type variables are allowed
+
+ -- Not really expecting these, but we'll get an error from
+ -- elsewhere, so don't produce an error here
+ Just (ATcTyCon {}) -> return ()
+ Just (APromotionErr {}) -> return ()
+ Just (AGlobal {}) -> return () }
tcExpr (HsEmbTy _ _) _ = failWith (TcRnIllegalTypeExpr TypeKeywordSyntax)
tcExpr (HsQual _ _ _) _ = failWith (TcRnIllegalTypeExpr ContextArrowSyntax)
=====================================
testsuite/tests/rename/should_fail/RnStaticPointersFail01.stderr
=====================================
@@ -1,3 +1,5 @@
RnStaticPointersFail01.hs:5:7: error: [GHC-88431]
- ‘x’ is used in a static form but it is not defined at top level
+ • ‘x’ is used in a static form but it is not defined at top level
+ • In the expression: static x
+ In an equation for ‘f’: f x = static x
=====================================
testsuite/tests/rename/should_fail/RnStaticPointersFail03.stderr
=====================================
@@ -1,12 +1,34 @@
RnStaticPointersFail03.hs:8:7: error: [GHC-88431]
- ‘x’ is used in a static form but it is not defined at top level
+ • ‘x’ is used in a static form but it is not defined at top level
+ • In the expression: static (x . id)
+ In an equation for ‘f’: f x = static (x . id)
RnStaticPointersFail03.hs:10:8: error: [GHC-88431]
- ‘k’ is used in a static form but it is not defined at top level
+ • ‘k’ is used in a static form but it is not defined at top level
+ • In the expression: static (k . id)
+ In an equation for ‘f0’:
+ f0 x
+ = static (k . id)
+ where
+ k = const (const () x)
RnStaticPointersFail03.hs:14:8: error: [GHC-88431]
- ‘k’ is used in a static form but it is not defined at top level
+ • ‘k’ is used in a static form but it is not defined at top level
+ • In the expression: static (k . id)
+ In an equation for ‘f1’:
+ f1 x
+ = static (k . id)
+ where
+ k = id
RnStaticPointersFail03.hs:19:15: error: [GHC-88431]
- ‘g’ is used in a static form but it is not defined at top level
+ • ‘g’ is used in a static form but it is not defined at top level
+ • In the first argument of ‘const’, namely ‘(static (g undefined))’
+ In the expression: const (static (g undefined)) (h x)
+ In an equation for ‘f2’:
+ f2 x
+ = const (static (g undefined)) (h x)
+ where
+ g = h
+ h = typeOf
=====================================
testsuite/tests/rename/should_fail/T26545.stderr
=====================================
@@ -1,3 +1,6 @@
T26545.hs:12:23: error: [GHC-88431]
- ‘v’ is used in a static form but it is not defined at top level
+ • ‘v’ is used in a static form but it is not defined at top level
+ • In the expression: static (I# (v +# 1#))
+ In the expression: let v = f 3# in static (I# (v +# 1#))
+ In an equation for ‘h’: h x = let v = f 3# in static (I# (v +# 1#))
=====================================
testsuite/tests/typecheck/should_compile/T27664.hs
=====================================
@@ -0,0 +1,11 @@
+{-# LANGUAGE StaticPointers, ScopedTypeVariables #-}
+module Repro where
+
+import Data.Typeable
+import GHC.StaticPtr
+
+f1 :: forall a. Typeable a => StaticPtr (a -> a)
+f1 = static (id :: a -> a)
+
+f2 :: forall a. Typeable a => StaticPtr (a -> a)
+f2 = static (id) :: StaticPtr (a->a)
=====================================
testsuite/tests/typecheck/should_compile/all.T
=====================================
@@ -969,3 +969,4 @@ test('ExpansionQLIm', normal, compile, [''])
test('T23135', normal, compile, [''])
test('LazyFieldAnnotations', normal, compile, [''])
test('T27557', normal, compile, [''])
+test('T27664', normal, compile, [''])
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/c673ecf057deee858acbbaa7e6c9207…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/c673ecf057deee858acbbaa7e6c9207…
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][master] X86 NCG: use btr/bts/btc for single-bit operations
by Marge Bot (@marge-bot) 05 Sep '26
by Marge Bot (@marge-bot) 05 Sep '26
05 Sep '26
Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
2908ab36 by Simon Jakobi at 2026-09-05T07:22:17-04:00
X86 NCG: use btr/bts/btc for single-bit operations
Previously the Cmm patterns
x & ~(1 << i)
x | (1 << i)
x ^ (1 << i)
compiled to mov/shl/not/and-style sequences of 3-4 instructions. Now
they compile to a single btr, bts or btc, matching what C compilers
produce.
When the bit index is a literal, constant folding has already collapsed
these patterns into ones with a literal mask, such as
x & 0xfffffeffffffffff for x & ~(1 << 40). Such masks are now also
compiled to a bit-test instruction when they don't fit in an imm32 and
would otherwise have to be loaded into a register first.
For a variable bit index, this applies only when the shift is unchecked
(uncheckedShiftL#, Data.Bits.unsafeShiftL): the bounds-checked shiftL
used by e.g. the default clearBit/setBit/complementBit implementations
wraps the shift in a bounds mask that this optimisation does not see
through. With a literal index, the bounds mask is constant-folded away,
so the checked operations benefit too.
See Note [Bit-test instructions] in GHC.CmmToAsm.X86.CodeGen.
Fixes #25233.
Assisted-by: Claude Fable 5
- - - - -
9 changed files:
- + changelog.d/ncg-x86-bit-test-instructions
- compiler/GHC/CmmToAsm/X86/CodeGen.hs
- compiler/GHC/CmmToAsm/X86/Instr.hs
- compiler/GHC/CmmToAsm/X86/Ppr.hs
- + testsuite/tests/codeGen/should_gen_asm/T25233.asm
- + testsuite/tests/codeGen/should_gen_asm/T25233.hs
- + testsuite/tests/codeGen/should_gen_asm/T25233b.asm
- + testsuite/tests/codeGen/should_gen_asm/T25233b.cmm
- testsuite/tests/codeGen/should_gen_asm/all.T
Changes:
=====================================
changelog.d/ncg-x86-bit-test-instructions
=====================================
@@ -0,0 +1,20 @@
+section: compiler
+synopsis: The x86 native code generator now uses the bit-test instructions
+ ``btr``/``bts``/``btc`` to clear, set or complement a single bit
+description:
+ Cmm patterns such as ``x & ~(1 << i)``, ``x | (1 << i)`` and
+ ``x ^ (1 << i)`` now compile to a single ``btr``/``bts``/``btc``
+ instruction instead of a mov/shl/not/and-style sequence, matching what C
+ compilers produce. The same applies to the literal masks that constant
+ folding produces from these patterns when ``i`` is constant, in the cases
+ where the mask doesn't fit in an imm32 operand.
+
+ For a variable bit index this applies only when the shift is unchecked,
+ as with ``uncheckedShiftL#`` or ``Data.Bits.unsafeShiftL``. The
+ bounds-checked ``shiftL`` — used, for example, by the default
+ implementations of ``clearBit``, ``setBit`` and ``complementBit`` —
+ wraps the shift in a bounds mask that this optimisation does not see
+ through. With a literal index, the bounds mask is constant-folded away,
+ so the checked operations benefit too.
+mrs: !16311
+issues: #25233
=====================================
compiler/GHC/CmmToAsm/X86/CodeGen.hs
=====================================
@@ -1442,6 +1442,22 @@ getRegister' platform is32Bit (CmmMachOp mop [x]) = do -- unary MachOps
(PUNPCKLQDQ fmt (OpReg dst) dst)
)
+-- Use the bit-test instructions btr/bts/btc for clearing, setting and
+-- complementing a single bit: e.g. x .&. complement (1 `shiftL` i) is btr.
+-- See Note [Bit-test instructions].
+getRegister' platform is32Bit (CmmMachOp (MO_And w) [x, y])
+ | bitTestOpWidthOK is32Bit w
+ , Just (opnd, ix) <- clearBitArgs_maybe platform w x y
+ = genBitTestCode (intFormat w) BTR opnd ix
+getRegister' platform is32Bit (CmmMachOp (MO_Or w) [x, y])
+ | bitTestOpWidthOK is32Bit w
+ , Just (opnd, ix) <- setBitArgs_maybe platform w x y
+ = genBitTestCode (intFormat w) BTS opnd ix
+getRegister' platform is32Bit (CmmMachOp (MO_Xor w) [x, y])
+ | bitTestOpWidthOK is32Bit w
+ , Just (opnd, ix) <- setBitArgs_maybe platform w x y
+ = genBitTestCode (intFormat w) BTC opnd ix
+
getRegister' platform is32Bit (CmmMachOp mop [x, y]) = do -- dyadic MachOps
sse4_1 <- sse4_1Enabled
sse4_2 <- sse4_2Enabled
@@ -5883,6 +5899,138 @@ genTrivialCode rep instr a b = do
instr b_op dst
return (Any rep code)
+{- Note [Bit-test instructions]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+x86 has dedicated instructions for clearing (btr), setting (bts) and
+complementing (btc) a single bit whose index is given in a register. We use
+them for Cmm patterns such as
+
+ x & ~(1 << i) ==> btr i, x (#25233)
+
+replacing a mov/shl/not/and sequence with a single instruction. The
+shift-count register operand of shl is masked modulo the operand width, and
+the bit-offset register operand of btr/bts/btc is masked the same way, so
+the replacement is faithful even for out-of-range i (where the Cmm shift is
+in any case undefined).
+
+The bit-offset operand of these instructions must be an immediate or a
+register. When the bit index is a literal, no shift reaches the NCG:
+constant folding has already turned the whole mask into a literal. If that
+mask fits in an imm32, we keep the ordinary and/or/xor with an immediate:
+it has the same latency and better throughput (more execution ports) than
+the bit-test instructions, and at worst two bytes of extra code size for bit
+indices 7..30.
+But a W64 mask touching the upper bits, e.g. ~(1 << 40), would have to be moved
+into a register first. For such masks we recognise the folded literal itself
+(exactly one bit clear resp. set) and emit btr/bts/btc with an immediate
+bit offset.
+
+We restrict the pattern to W32 and native-width W64: the instructions do not
+exist at width 8, and sub-word Cmm operations at W8/W16 are rare enough that
+they are not worth the extra care.
+-}
+
+-- | Match @1 << i@, returning @i@.
+--
+-- The returned expression is always at word width ('machOpArgReps' fixes
+-- shift amounts at 'wordWidth'). See Note [Bit-test instructions].
+singleBit_maybe :: CmmExpr -> Maybe CmmExpr
+singleBit_maybe (CmmMachOp (MO_Shl _) [CmmLit (CmmInt 1 _), i]) = Just i
+singleBit_maybe _ = Nothing
+
+-- | If exactly one bit of @m@, taken at width @w@, is set, return its index.
+--
+-- See Note [Bit-test instructions].
+setBitLit_maybe :: Width -> Integer -> Maybe Int
+setBitLit_maybe w m
+ | popCount m' == 1 = Just (countTrailingZeros m')
+ | otherwise = Nothing
+ where
+ -- w <= W64 in this X86-specific code, so a Word64 suffices.
+ m' = fromInteger (narrowU w m) :: Word64
+
+-- | If exactly one bit of @m@, taken at width @w@, is clear, return its
+-- index.
+--
+-- See Note [Bit-test instructions].
+clearBitLit_maybe :: Width -> Integer -> Maybe Int
+clearBitLit_maybe w m = setBitLit_maybe w (complement m)
+
+bitTestOpWidthOK :: Bool -> Width -> Bool
+bitTestOpWidthOK is32Bit w = w == W32 || (w == W64 && not is32Bit)
+
+-- | The bit-offset operand of a bit-test instruction (btr/bts/btc).
+data BitIndex
+ = BitIndexReg CmmExpr -- ^ variable index, computed into a register
+ | BitIndexImm Int -- ^ literal index, emitted as an immediate
+
+-- | Match the operands of a single-bit set or complement operation: one
+-- operand is a mask @1 << i@, or a literal with exactly one bit set that
+-- does not fit in an imm32. Returns the other operand and the bit index.
+--
+-- Both operand orders are matched, e.g. @x | (1 << i)@ and @(1 << i) | x@.
+--
+-- See Note [Bit-test instructions].
+setBitArgs_maybe :: Platform -> Width -> CmmExpr -> CmmExpr
+ -> Maybe (CmmExpr, BitIndex)
+setBitArgs_maybe platform w x y = go x y `mplus` go y x
+ where
+ go opnd mask
+ | Just i <- singleBit_maybe mask
+ = Just (opnd, BitIndexReg i)
+ | CmmLit lit@(CmmInt m _) <- mask
+ , Just i <- setBitLit_maybe w m
+ , not (is32BitLit platform lit)
+ = Just (opnd, BitIndexImm i)
+ | otherwise
+ = Nothing
+
+-- | As 'setBitArgs_maybe', for a single-bit clear operation: the mask is
+-- @~(1 << i)@, or a literal with exactly one bit clear.
+clearBitArgs_maybe :: Platform -> Width -> CmmExpr -> CmmExpr
+ -> Maybe (CmmExpr, BitIndex)
+clearBitArgs_maybe platform w x y = go x y `mplus` go y x
+ where
+ go opnd mask
+ | CmmMachOp (MO_Not _) [b] <- mask
+ , Just i <- singleBit_maybe b
+ = Just (opnd, BitIndexReg i)
+ | CmmLit lit@(CmmInt m _) <- mask
+ , Just i <- clearBitLit_maybe w m
+ , not (is32BitLit platform lit)
+ = Just (opnd, BitIndexImm i)
+ | otherwise
+ = Nothing
+
+-- | Generate code for @dst := x@ followed by a bit-test instruction
+-- (btr/bts/btc).
+--
+-- See Note [Bit-test instructions].
+genBitTestCode :: Format -> (Format -> Operand -> Operand -> Instr)
+ -> CmmExpr -> BitIndex -> NatM Register
+genBitTestCode rep instr x (BitIndexImm i) = do
+ x_code <- getAnyReg x
+ let code dst = x_code dst `snocOL` instr rep (OpImm (ImmInt i)) (OpReg dst)
+ return (Any rep code)
+genBitTestCode rep instr x (BitIndexReg i) = do
+ (i_reg, i_code) <- getNonClobberedReg i
+ x_code <- getAnyReg x
+ tmp <- getNewRegNat rep
+ let
+ -- As in genTrivialCode, 'i' must stay alive across the computation of
+ -- 'x' into dst, so save it in a temporary if dst holds 'i'.
+ code dst
+ | dst == i_reg =
+ i_code `appOL`
+ unitOL (MOV rep (OpReg i_reg) (OpReg tmp)) `appOL`
+ x_code dst `snocOL`
+ instr rep (OpReg tmp) (OpReg dst)
+ | otherwise =
+ i_code `appOL`
+ x_code dst `snocOL`
+ instr rep (OpReg i_reg) (OpReg dst)
+ return (Any rep code)
+
regClashesWithOp :: Reg -> Operand -> Bool
reg `regClashesWithOp` OpReg reg2 = reg == reg2
reg `regClashesWithOp` OpAddr amode = any (==reg) (addrModeRegs amode)
=====================================
compiler/GHC/CmmToAsm/X86/Instr.hs
=====================================
@@ -193,6 +193,12 @@ data Instr
| SHLD Format Operand{-amount-} Operand Operand
| BT Format Imm Operand
+ -- | Bit test-and-reset
+ | BTR Format Operand{- ^ bit offset (imm/reg) -} Operand
+ -- | Bit set
+ | BTS Format Operand{- ^ bit offset (imm/reg) -} Operand
+ -- | Bit complement
+ | BTC Format Operand{- ^ bit offset (imm/reg) -} Operand
| NOP
@@ -496,6 +502,9 @@ regUsageOfInstr platform instr
SHLD fmt imm dst1 dst2 -> usageRMM fmt imm dst1 dst2
SHRD fmt imm dst1 dst2 -> usageRMM fmt imm dst1 dst2
BT fmt _ src -> mkRUR (use_R fmt src [])
+ BTR fmt off dst -> usageRM fmt off dst
+ BTS fmt off dst -> usageRM fmt off dst
+ BTC fmt off dst -> usageRM fmt off dst
PUSH fmt op -> mkRUR (use_R fmt op [])
POP fmt op -> mkRU [] (def_W fmt op)
@@ -830,6 +839,9 @@ patchRegsOfInstr platform instr env
SHLD fmt imm dst1 dst2 -> patch2 (SHLD fmt imm) dst1 dst2
SHRD fmt imm dst1 dst2 -> patch2 (SHRD fmt imm) dst1 dst2
BT fmt imm src -> patch1 (BT fmt imm) src
+ BTR fmt off dst -> patch2 (BTR fmt) off dst
+ BTS fmt off dst -> patch2 (BTS fmt) off dst
+ BTC fmt off dst -> patch2 (BTC fmt) off dst
TEST fmt src dst -> patch2 (TEST fmt) src dst
CMP fmt src dst -> patch2 (CMP fmt) src dst
PUSH fmt op -> patch1 (PUSH fmt) op
=====================================
compiler/GHC/CmmToAsm/X86/Ppr.hs
=====================================
@@ -862,6 +862,15 @@ pprInstr platform i = case i of
BT format imm src
-> pprFormatImmOp (text "bt") format imm src
+ BTR format off dst
+ -> pprFormatOpOp (text "btr") format off dst
+
+ BTS format off dst
+ -> pprFormatOpOp (text "bts") format off dst
+
+ BTC format off dst
+ -> pprFormatOpOp (text "btc") format off dst
+
CMP format src dst
| isFloatFormat format -> pprFormatOpOp (text "ucomi") format src dst -- SSE2
| otherwise -> pprFormatOpOp (text "cmp") format src dst
=====================================
testsuite/tests/codeGen/should_gen_asm/T25233.asm
=====================================
@@ -0,0 +1,9 @@
+btrq
+btsq
+btcq
+btrl
+btsl
+btcl
+btrq $40,
+btsq $40,
+btcq $40,
=====================================
testsuite/tests/codeGen/should_gen_asm/T25233.hs
=====================================
@@ -0,0 +1,39 @@
+{-# LANGUAGE MagicHash #-}
+
+-- Check that clearing/setting/complementing a single, variable bit
+-- uses the btr/bts/btc instructions (#25233).
+module T25233 where
+
+import GHC.Exts
+
+myClearBit :: Word# -> Int# -> Word#
+myClearBit x i = x `and#` not# (1## `uncheckedShiftL#` i)
+
+mySetBit :: Word# -> Int# -> Word#
+mySetBit x i = x `or#` (1## `uncheckedShiftL#` i)
+
+myComplementBit :: Word# -> Int# -> Word#
+myComplementBit x i = x `xor#` (1## `uncheckedShiftL#` i)
+
+myClearBit32 :: Word32# -> Int# -> Word32#
+myClearBit32 x i =
+ x `andWord32#` notWord32# (wordToWord32# 1## `uncheckedShiftLWord32#` i)
+
+mySetBit32 :: Word32# -> Int# -> Word32#
+mySetBit32 x i = x `orWord32#` (wordToWord32# 1## `uncheckedShiftLWord32#` i)
+
+myComplementBit32 :: Word32# -> Int# -> Word32#
+myComplementBit32 x i =
+ x `xorWord32#` (wordToWord32# 1## `uncheckedShiftLWord32#` i)
+
+-- With a constant bit index >= 32, the mask constant-folds to a literal
+-- that does not fit in an imm32, so a bit-test instruction with an
+-- immediate offset is used.
+myClearBit40 :: Word# -> Word#
+myClearBit40 x = x `and#` not# (1## `uncheckedShiftL#` 40#)
+
+mySetBit40 :: Word# -> Word#
+mySetBit40 x = x `or#` (1## `uncheckedShiftL#` 40#)
+
+myComplementBit40 :: Word# -> Word#
+myComplementBit40 x = x `xor#` (1## `uncheckedShiftL#` 40#)
=====================================
testsuite/tests/codeGen/should_gen_asm/T25233b.asm
=====================================
@@ -0,0 +1,6 @@
+btrq %
+btsq %
+btcq %
+btrq $40,
+btsq $40,
+btcq $40,
=====================================
testsuite/tests/codeGen/should_gen_asm/T25233b.cmm
=====================================
@@ -0,0 +1,27 @@
+#include "Cmm.h"
+
+// Single-bit masks written on the left of the operator (#25233).
+
+clearBitVar (W_ x, W_ i) {
+ return ((~((1 :: bits64) << i)) & x);
+}
+
+setBitVar (W_ x, W_ i) {
+ return (((1 :: bits64) << i) | x);
+}
+
+complementBitVar (W_ x, W_ i) {
+ return (((1 :: bits64) << i) ^ x);
+}
+
+clearBit40 (W_ x) {
+ return ((0xFFFFFEFFFFFFFFFF :: bits64) & x);
+}
+
+setBit40 (W_ x) {
+ return ((0x10000000000 :: bits64) | x);
+}
+
+complementBit40 (W_ x) {
+ return ((0x10000000000 :: bits64) ^ x);
+}
=====================================
testsuite/tests/codeGen/should_gen_asm/all.T
=====================================
@@ -23,6 +23,10 @@ test('avx512-int64-minmax', [unless(arch('x86_64'), skip),
when(unregisterised(), skip)], compile_grep_asm, ['hs', True, '-mavx512vl'])
test('avx512-word64-minmax', [unless(arch('x86_64'), skip),
when(unregisterised(), skip)], compile_grep_asm, ['hs', True, '-mavx512vl'])
+test('T25233', [unless(arch('x86_64'), skip),
+ when(unregisterised(), skip)], compile_grep_asm, ['hs', True, '-O'])
+test('T25233b', [unless(arch('x86_64'), skip),
+ when(unregisterised(), skip)], compile_grep_asm, ['cmm', True, ''])
is_aarch64_codegen = [
unless(arch('aarch64'), skip),
when(unregisterised(), skip),
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/2908ab363dd269c62f07316ec2fa8f5…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/2908ab363dd269c62f07316ec2fa8f5…
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/rafl/hpc-instances] 6 commits: hpc: Test desired HPC behaviour for class instances
by Florian Ragwitz (@rafl) 05 Sep '26
by Florian Ragwitz (@rafl) 05 Sep '26
05 Sep '26
Florian Ragwitz pushed to branch wip/rafl/hpc-instances at Glasgow Haskell Compiler / GHC
Commits:
cec8c909 by Florian Ragwitz at 2026-09-04T21:35:49-07:00
hpc: Test desired HPC behaviour for class instances
- - - - -
7bda6078 by Florian Ragwitz at 2026-09-04T21:35:49-07:00
Extend TcGblEnv with a mapping from instance method Ids to DFunIds
We want to use this in HPC to provide better support for instance methods.
This is working towards #17155.
- - - - -
75a32565 by Florian Ragwitz at 2026-09-04T21:36:38-07:00
hpc: Do not create top-level boxes for generated instance methods
Users care about which of their written methods are or aren't covered, not
whether all of the inherited methods are covered as well. Those should be
covered where they are implemented.
We achieve that by stopping to generate top-level boxes which will often appear
as uncovered with no clear indication as to why, and no way to fix other than
excercising code the user has not written.
Fixes #17155.
- - - - -
db2f0950 by Florian Ragwitz at 2026-09-04T21:36:38-07:00
hpc: Provide coverage information for class instances
After fixing #17155, we no longer create top-level boxes for the source spans
of instance heads (previously that was the source span used for
inherited/generated instance methods).
With those boxes gone, we can introduce a new top-level box for each class
instance with that instance head source span, and provide more meaningful
coverage semantics for it.
We make the use of any of the instance's methods (user-written or generated)
tick this box, providing the programmer with useful information about whether
or not the instance was exercised at run-time, and whether the instance might
be unnecessary.
- - - - -
6d177210 by Florian Ragwitz at 2026-09-04T21:36:38-07:00
hpc: Don't allocate boxes for methodless classes
With the plumbing we currently have, these could never be covered, which is not
helpful to hpc users. This could be improved upon further.
- - - - -
c5f3a857 by Florian Ragwitz at 2026-09-04T21:36:38-07:00
Add a changelog entry for new HPC features
- - - - -
10 changed files:
- + changelog.d/hpc-classinsts
- compiler/GHC/HsToCore.hs
- compiler/GHC/HsToCore/Ticks.hs
- compiler/GHC/Tc/Module.hs
- compiler/GHC/Tc/TyCl/Instance.hs
- compiler/GHC/Tc/Types.hs
- compiler/GHC/Tc/Utils/Monad.hs
- + testsuite/tests/hpc/instmeths/instmeths.hs
- + testsuite/tests/hpc/instmeths/instmeths.stdout
- + testsuite/tests/hpc/instmeths/test.T
Changes:
=====================================
changelog.d/hpc-classinsts
=====================================
@@ -0,0 +1,23 @@
+section: compiler
+synopsis: Improve coverage reporting for class instances and their methods
+description:
+
+ Coverage reporting (:ghc-flag:`-fhpc`) now treats class instances more
+ naturally.
+
+ Compiler-generated instance methods, such as inherited default methods, no
+ longer receive separate top-level coverage boxes. Explicitly implemented
+ instance methods retain their own declaration coverage.
+
+ This means that it is no longer necessary to exercise all of a class' methods
+ in order for it to appear "covered".
+
+ Additionally, top-level coverage boxes are now created for all instances of
+ classes with methods, and ticked by the use of *any* of the class' methods.
+ This indicates that an instance was in fact used at run-time.
+
+ Note that classes without any instance methods are currently excluded from
+ this, and won't have any coverage boxes created.
+
+mrs: !16632
+issues: #17155
=====================================
compiler/GHC/HsToCore.hs
=====================================
@@ -147,6 +147,7 @@ deSugar hsc_env
tcg_tcs = tcs,
tcg_default_exports = defaults,
tcg_insts = insts,
+ tcg_inst_meths = inst_meths,
tcg_fam_insts = fam_insts,
tcg_complete_matches = complete_matches,
tcg_self_boot = self_boot
@@ -170,7 +171,8 @@ deSugar hsc_env
(hsc_logger hsc_env)
(initTicksConfig (hsc_dflags hsc_env))
mod mod_loc
- export_set (typeEnvTyCons type_env) binds
+ export_set (typeEnvTyCons type_env)
+ insts inst_meths binds
else return (binds, Nothing)
; let modBreaks
| Just (_, _, breakpointSpecs) <- m_tickInfo
=====================================
compiler/GHC/HsToCore/Ticks.hs
=====================================
@@ -5,7 +5,7 @@
{-
(c) Galois, 2006
(c) University of Glasgow, 2007
-(c) Florian Ragwitz, 2025
+(c) Florian Ragwitz, 2025-2026
-}
module GHC.HsToCore.Ticks
@@ -24,6 +24,8 @@ import GHC.Unit
import GHC.Core.Type
import GHC.Core.TyCon
+import GHC.Core.InstEnv
+import GHC.Core.Class
import GHC.Data.Maybe
import GHC.Data.FastString
@@ -56,6 +58,7 @@ import Data.Foldable (toList)
import Trace.Hpc.Mix
import Data.Bifunctor (second)
+import Data.Functor
import Data.List.NonEmpty (NonEmpty (..))
import Data.Set (Set)
import qualified Data.Set as Set
@@ -100,11 +103,13 @@ addTicksToBinds
-- isExportedId doesn't work yet (the desugarer
-- hasn't set it), so we have to work from this set.
-> [TyCon] -- ^ Type constructors in this module
+ -> [ClsInst]
+ -> IdEnv DFunId
-> LHsBinds GhcTc
-> IO (LHsBinds GhcTc, Maybe (FilePath, SizedSeq Tick, SizedSeq Tick))
addTicksToBinds logger cfg
- mod mod_loc exports tyCons binds
+ mod mod_loc exports tyCons insts inst_meths binds
| let passes = ticks_passes cfg
, not (null passes)
, Just orig_file <- ml_hs_file mod_loc = do
@@ -129,8 +134,13 @@ addTicksToBinds logger cfg
, this_mod = mod
, tickishType = tickish
, recSelBinds = emptyVarEnv
+ , instMeths = inst_meths
+ , instTicks = emptyVarEnv
}
- (binds',_,st') = unTM (addTickLHsBinds binds) env st
+ addTick = do
+ instTicks <- allocInstTicks insts
+ withEnv (\e -> e{ instTicks }) $ addTickLHsBinds binds
+ (binds',_,st') = unTM addTick env st
in (binds', st')
(binds1,st) = foldr tickPass (binds, initTTState) passes
@@ -231,7 +241,7 @@ addTickLHsBind :: LHsBind GhcTc -> TM (LHsBind GhcTc)
addTickLHsBind (L pos (XHsBindsLR bind@(AbsBinds { abs_binds = binds
, abs_exports = abs_exports
}))) =
- withEnv (add_rec_sels . add_inlines . add_exports) $ do
+ withEnv (add_inst_meths . add_rec_sels . add_inlines . add_exports) $ do
binds' <- addTickLHsBinds binds
return $ L pos $ XHsBindsLR $ bind { abs_binds = binds' }
where
@@ -259,19 +269,24 @@ addTickLHsBind (L pos (XHsBindsLR bind@(AbsBinds { abs_binds = binds
| ABE{ abe_poly, abe_mono } <- abs_exports
, RecSelId{} <- [idDetails abe_poly] ] }
+ add_inst_meths env =
+ env{ instMeths = instMeths env `extendVarEnvList`
+ [ (abe_mono, dfun)
+ | ABE{ abe_poly, abe_mono } <- abs_exports
+ , Just dfun <- [lookupVarEnv (instMeths env) abe_poly] ] }
+
addTickLHsBind (L pos (funBind@(FunBind { fun_id = L _ id, fun_matches = matches }))) = do
let name = getOccString id
decl_path <- getPathEntry
density <- getDensity
+ env <- getEnv
- inline_ids <- liftM inlines getEnv
-- See Note [inline sccs]
let inline = isInlinePragma (idInlinePragma id)
- || id `elemVarSet` inline_ids
+ || id `elemVarSet` inlines env
-- See Note [inline sccs]
- tickish <- tickishType `liftM` getEnv
- case tickish of { ProfNotes | inline -> return (L pos funBind); _ -> do
+ case tickishType env of { ProfNotes | inline -> return (L pos funBind); _ -> do
-- See Note [Record-selector ticks]
selTicks <- recSelTick id
@@ -294,16 +309,24 @@ addTickLHsBind (L pos (funBind@(FunBind { fun_id = L _ id, fun_matches = matches
toplev = null decl_path
exported = idName id `elemNameSet` exported_names
- tick <- if not blackListed &&
- shouldTickBind density toplev exported simple inline
+ let generatedInstMeth = density == TickForCoverage
+ && id `elemVarEnv` instMeths env
+ && isGenerated (mg_origin (mg_ext matches))
+
+ tick <- if not generatedInstMeth
+ && not blackListed
+ && shouldTickBind density toplev exported simple inline
then
bindTick density name (locA pos) fvs
else
return Nothing
+ instTick <- instMethTick id
+
let mbCons = maybe Prelude.id (:)
return $ L pos $ funBind { fun_matches = mg
- , fun_ext = second (tick `mbCons`) (fun_ext funBind) }
+ , fun_ext = second ((instTick `mbCons`) . (tick `mbCons`))
+ (fun_ext funBind) }
} }
where
-- See Note [Record-selector ticks]
@@ -1098,6 +1121,8 @@ data TickTransEnv = TTE { fileName :: FastString
, this_mod :: Module
, tickishType :: TickishType
, recSelBinds :: IdEnv DVarSet
+ , instMeths :: IdEnv DFunId
+ , instTicks :: IdEnv CoreTickish
}
-- deriving Show
@@ -1238,6 +1263,18 @@ isBlackListed (RealSrcSpan pos _) = TM $ \ env st -> (Set.member pos (blackList
isBlackListed GeneratedSrcSpan{} = return False
isBlackListed UnhelpfulSpan{} = return False
+allocInstTicks :: [ClsInst] -> TM (IdEnv CoreTickish)
+allocInstTicks insts =
+ ifDensity TickForCoverage (mkVarEnv . catMaybes <$> mapM alloc insts) (pure emptyVarEnv)
+ where
+ alloc ClsInst{ is_dfun, is_cls }
+ | null (classMethods is_cls) = pure Nothing
+ | otherwise = fmap (is_dfun,) <$> allocATickBox (TopLevelBox [getOccString is_dfun])
+ False True (getSrcSpan is_dfun) noFVs
+
+instMethTick :: Id -> TM (Maybe CoreTickish)
+instMethTick id = getEnv <&> \e -> lookupVarEnv (instMeths e) id >>= lookupVarEnv (instTicks e)
+
-- the tick application inherits the source position of its
-- expression argument to support nested box allocations
allocTickBox :: BoxLabel -> Bool -> Bool -> SrcSpan -> TM (HsExpr GhcTc)
=====================================
compiler/GHC/Tc/Module.hs
=====================================
@@ -1770,7 +1770,7 @@ tcTopSrcDecls (HsGroup { hs_tyclds = tycl_decls,
-- Second pass over class and instance declarations,
-- now using the kind-checked decls
traceTc "Tc6" empty ;
- inst_binds <- tcInstDecls2 (tyClGroupTyClDecls tycl_decls) inst_infos ;
+ (inst_binds, inst_meths) <- tcInstDecls2 (tyClGroupTyClDecls tycl_decls) inst_infos ;
-- Foreign exports
traceTc "Tc7" empty ;
@@ -1802,8 +1802,9 @@ tcTopSrcDecls (HsGroup { hs_tyclds = tycl_decls,
, tcg_anns = tcg_anns tcg_env ++ annotations
, tcg_ann_env = extendAnnEnvList (tcg_ann_env tcg_env) annotations
, tcg_fords = tcg_fords tcg_env ++ foe_decls ++ fi_decls
- , tcg_dus = tcg_dus tcg_env `plusDU` usesOnly fo_fvs } } ;
+ , tcg_dus = tcg_dus tcg_env `plusDU` usesOnly fo_fvs
-- tcg_dus: see Note [Newtype constructor usage in foreign declarations]
+ , tcg_inst_meths = tcg_inst_meths tcg_env `plusVarEnv` inst_meths } } ;
-- See Note [Newtype constructor usage in foreign declarations]
addUsedGREs NoDeprecationWarnings (bagToList fo_gres) ;
=====================================
compiler/GHC/Tc/TyCl/Instance.hs
=====================================
@@ -1282,7 +1282,7 @@ takes a slightly different approach.
********************************************************************* -}
tcInstDecls2 :: [LTyClDecl GhcRn] -> [InstInfo GhcRn]
- -> TcM (LHsBinds GhcTc)
+ -> TcM (LHsBinds GhcTc, IdEnv DFunId)
-- (a) From each class declaration,
-- generate any default-method bindings
-- (b) From each instance decl
@@ -1299,11 +1299,11 @@ tcInstDecls2 tycl_decls inst_decls
-- Add the default method Ids (again)
-- (they were already added in GHC.Tc.TyCl.Utils.tcAddImplicits)
-- See Note [Default methods in the type environment]
- ; inst_binds_s <- tcExtendGlobalValEnv dm_ids $
- mapM tcInstDecl2 inst_decls
+ ; (inst_binds_s, inst_meths_s) <- unzip <$> (tcExtendGlobalValEnv dm_ids $
+ mapM tcInstDecl2 inst_decls)
-- Done
- ; return (dm_binds ++ concat inst_binds_s) }
+ ; return (dm_binds ++ concat inst_binds_s, plusVarEnvList inst_meths_s) }
{- Note [Default methods in the type environment]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1319,12 +1319,12 @@ So right here in tcInstDecls2 we must re-extend the type envt with
the default method Ids replete with their INLINE pragmas. Urk.
-}
-tcInstDecl2 :: InstInfo GhcRn -> TcM (LHsBinds GhcTc)
+tcInstDecl2 :: InstInfo GhcRn -> TcM (LHsBinds GhcTc, IdEnv DFunId)
-- Returns a binding for the dfun
tcInstDecl2 (InstInfo { iSpec = ispec, iBinds = ibinds })
- = recoverM (return emptyLHsBinds) $
- setSrcSpan loc $
- addErrCtxt (instDeclCtxt2 dfun_ty) $
+ = recoverM (return (emptyLHsBinds, emptyVarEnv)) $
+ setSrcSpan loc $
+ addErrCtxt (instDeclCtxt2 dfun_ty) $
do { -- Instantiate the instance decl with skolem constants
(skol_info, inst_tyvars, dfun_theta, clas, inst_tys) <- tcSkolDFunType dfun_ty
; dfun_ev_vars <- newEvVars dfun_theta
@@ -1342,7 +1342,7 @@ tcInstDecl2 (InstInfo { iSpec = ispec, iBinds = ibinds })
-- See Note [Typechecking plan for instance declarations]
; dfun_ev_binds_var <- newTcEvBinds
; let dfun_ev_binds = TcEvBinds dfun_ev_binds_var
- ; (tclvl, (sc_meth_ids, sc_meth_binds, sc_meth_implics))
+ ; (tclvl, (sc_meth_ids, sc_meth_binds, sc_meth_implics, meth_ids))
<- pushTcLevelM $
do { (sc_ids, sc_binds, sc_implics)
<- tcSuperClasses skol_info dfun_id clas inst_tyvars
@@ -1356,7 +1356,8 @@ tcInstDecl2 (InstInfo { iSpec = ispec, iBinds = ibinds })
; return ( sc_ids ++ meth_ids
, sc_binds ++ meth_binds
- , sc_implics `unionBags` meth_implics ) }
+ , sc_implics `unionBags` meth_implics
+ , meth_ids ) }
; imp <- newImplication
; emitImplication $
@@ -1413,7 +1414,7 @@ tcInstDecl2 (InstInfo { iSpec = ispec, iBinds = ibinds })
, abs_binds = [dict_bind]
, abs_sig = True }
- ; return (L loc' main_bind : sc_meth_binds)
+ ; return (L loc' main_bind : sc_meth_binds, mkVarEnv $ (,dfun_id) <$> meth_ids)
}
where
dfun_id = instanceDFunId ispec
=====================================
compiler/GHC/Tc/Types.hs
=====================================
@@ -157,6 +157,7 @@ import GHC.Types.Name.Env
import GHC.Types.Name.Set
import GHC.Types.Avail
import GHC.Types.Var
+import GHC.Types.Var.Env
import GHC.Types.TypeEnv
import GHC.Types.SourceFile
import GHC.Types.SrcLoc
@@ -530,6 +531,8 @@ data TcGblEnv
-- NB. BangPattern is to fix a leak, see #15111
tcg_fam_inst_env :: !FamInstEnv, -- ^ Ditto for family instances
-- NB. BangPattern is to fix a leak, see #15111
+ tcg_inst_meths :: IdEnv DFunId,
+ -- ^ Maps instance method Ids to the DFunId of the instance they belong to.
tcg_ann_env :: AnnEnv, -- ^ And for annotations
tcg_complete_match_env :: CompleteMatches,
-- ^ The complete matches for all /home-package/ modules;
=====================================
compiler/GHC/Tc/Utils/Monad.hs
=====================================
@@ -409,6 +409,7 @@ initTcGblEnv hsc_env hsc_src keep_rn_syntax mod loc =
, tcg_knot_vars = hsc_type_env_vars hsc_env
, tcg_known_key_maps = known_key_maps_var
, tcg_inst_env = emptyInstEnv
+ , tcg_inst_meths = emptyVarEnv
, tcg_fam_inst_env = emptyFamInstEnv
, tcg_ann_env = emptyAnnEnv
, tcg_complete_match_env = []
=====================================
testsuite/tests/hpc/instmeths/instmeths.hs
=====================================
@@ -0,0 +1,57 @@
+import Data.List
+import Data.Maybe
+import Trace.Hpc.Mix
+import Trace.Hpc.Reflect
+import Trace.Hpc.Tix
+import Trace.Hpc.Util
+
+class Foo a where
+ defMeth, defMeth', reqMeth :: a -> String
+ defMeth = const "class default"
+ defMeth' = const "class default"
+
+newtype T a = T a deriving (Show, Eq, Functor)
+instance Foo (T a) where
+ reqMeth = const "Foo (T a) instance"
+
+newtype T' a = T' a
+
+instance Foo (T' a) where
+ reqMeth = const "Foo (T' a) instance"
+ defMeth = const "Foo (T' a) instance"
+
+-- no method use could "cover" this instance, so we (for now) omit the top-level box for it
+class Marker a
+instance Marker (T Bool)
+
+interesting :: (Int, MixEntry) -> Maybe (Int, HpcPos, String)
+interesting (n, (pos, TopLevelBox [name])) | isInteresting name = Just (n, pos, name)
+ where isInteresting = (||) <$> isInst <*> isMeth
+ isInst = ("$f" `isPrefixOf`)
+ isMeth = (||) <$> (`elem` ["(==)", "(/=)", "show", "showList", "showsPrec", "fmap", "(<$)"])
+ <*> ("Meth" `isSubsequenceOf`)
+interesting _ = Nothing
+
+-- candidate for HPC.Utils, maybe?
+sourceAt :: String -> HpcPos -> String
+sourceAt src pos
+ | l1 == l2 = take (c2 - c1 + 1) . drop (c1 - 1) $ ls !! (l1 - 1)
+ | otherwise = intercalate "\n" $ first : (middle ++ [last])
+ where
+ (l1, c1, l2, c2) = fromHpcPos pos
+ ls = lines src
+ first = drop (c1 - 1) $ ls !! (l1 - 1)
+ middle = take (l2 - l1 - 1) $ drop l1 ls
+ last = take c2 $ ls !! (l2 - 1)
+
+main :: IO ()
+main = do
+ print (T 23 == (succ <$> T 22)) -- tick Eq and Functor by using any of their methods
+ print (reqMeth (T' 1)) -- tick `Foo (T' a)` and that instance's `reqMeth`
+
+ Mix source _ _ _ mixEntries <- readMix [".hpc"] (Left "Main")
+ src <- readFileUtf8 source
+ let boxes = mapMaybe interesting $ zip [0 ..] mixEntries
+ Tix [TixModule "Main" _ _ counts] <- examineTix
+ mapM_ print [ (counts !! n, path, sourceAt src pos)
+ | (n, pos, path) <- sortOn (\(_, _, s) -> s) boxes ]
=====================================
testsuite/tests/hpc/instmeths/instmeths.stdout
=====================================
@@ -0,0 +1,12 @@
+True
+"Foo (T' a) instance"
+(1,"$fEqT","Eq")
+(0,"$fFooT","Foo (T a)")
+(1,"$fFooT'","Foo (T' a)")
+(1,"$fFunctorT","Functor")
+(0,"$fShowT","Show")
+(0,"defMeth","defMeth = const \"class default\"")
+(0,"defMeth","defMeth = const \"Foo (T' a) instance\"")
+(0,"defMeth'","defMeth' = const \"class default\"")
+(0,"reqMeth","reqMeth = const \"Foo (T a) instance\"")
+(1,"reqMeth","reqMeth = const \"Foo (T' a) instance\"")
=====================================
testsuite/tests/hpc/instmeths/test.T
=====================================
@@ -0,0 +1,5 @@
+setTestOpts([omit_ghci, when(fast(), skip), js_skip])
+
+test('instmeths',
+ [ignore_extension],
+ compile_and_run, ['-fhpc'])
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/2d7a4cd438eca329f047a59f28d0bd…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/2d7a4cd438eca329f047a59f28d0bd…
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/rafl/hpc-instances] 5 commits: Extend TcGblEnv with a mapping from instance method Ids to DFunIds
by Florian Ragwitz (@rafl) 05 Sep '26
by Florian Ragwitz (@rafl) 05 Sep '26
05 Sep '26
Florian Ragwitz pushed to branch wip/rafl/hpc-instances at Glasgow Haskell Compiler / GHC
Commits:
4da9faec by Florian Ragwitz at 2026-09-04T21:19:21-07:00
Extend TcGblEnv with a mapping from instance method Ids to DFunIds
We want to use this in HPC to provide better support for instance methods.
This is working towards #17155.
- - - - -
f5d90d7f by Florian Ragwitz at 2026-09-04T21:19:22-07:00
hpc: Do not create top-level boxes for generated instance methods
Users care about which of their written methods are or aren't covered, not
whether all of the inherited methods are covered as well. Those should be
covered where they are implemented.
We achieve that by stopping to generate top-level boxes which will often appear
as uncovered with no clear indication as to why, and no way to fix other than
excercising code the user has not written.
Fixes #17155.
- - - - -
4a5316fb by Florian Ragwitz at 2026-09-04T21:19:22-07:00
hpc: Provide coverage information for class instances
After fixing #17155, we no longer create top-level boxes for the source spans
of instance heads (previously that was the source span used for
inherited/generated instance methods).
With those boxes gone, we can introduce a new top-level box for each class
instance with that instance head source span, and provide more meaningful
coverage semantics for it.
We make the use of any of the instance's methods (user-written or generated)
tick this box, providing the programmer with useful information about whether
or not the instance was exercised at run-time, and whether the instance might
be unnecessary.
- - - - -
113f3d08 by Florian Ragwitz at 2026-09-04T21:19:22-07:00
hpc: Don't allocate boxes for methodless classes
With the plumbing we currently have, these could never be covered, which is not
helpful to hpc users. This could be improved upon further.
- - - - -
2d7a4cd4 by Florian Ragwitz at 2026-09-04T21:19:22-07:00
Add a changelog entry for new HPC features
- - - - -
7 changed files:
- + changelog.d/hpc-classinsts
- compiler/GHC/HsToCore.hs
- compiler/GHC/HsToCore/Ticks.hs
- compiler/GHC/Tc/Module.hs
- compiler/GHC/Tc/TyCl/Instance.hs
- compiler/GHC/Tc/Types.hs
- compiler/GHC/Tc/Utils/Monad.hs
Changes:
=====================================
changelog.d/hpc-classinsts
=====================================
@@ -0,0 +1,23 @@
+section: compiler
+synopsis: Improve coverage reporting for class instances and their methods
+description:
+
+ Coverage reporting (:ghc-flag:`-fhpc`) now treats class instances more
+ naturally.
+
+ Compiler-generated instance methods, such as inherited default methods, no
+ longer receive separate top-level coverage boxes. Explicitly implemented
+ instance methods retain their own declaration coverage.
+
+ This means that it is no longer necessary to exercise all of a class' methods
+ in order for it to appear "covered".
+
+ Additionally, top-level coverage boxes are now created for all instances of
+ classes with methods, and ticked by the use of *any* of the class' methods.
+ This indicates that an instance was in fact used at run-time.
+
+ Note that classes without any instance methods are currently excluded from
+ this, and won't have any coverage boxes created.
+
+mrs: !16632
+issues: #17155
=====================================
compiler/GHC/HsToCore.hs
=====================================
@@ -147,6 +147,7 @@ deSugar hsc_env
tcg_tcs = tcs,
tcg_default_exports = defaults,
tcg_insts = insts,
+ tcg_inst_meths = inst_meths,
tcg_fam_insts = fam_insts,
tcg_complete_matches = complete_matches,
tcg_self_boot = self_boot
@@ -170,7 +171,8 @@ deSugar hsc_env
(hsc_logger hsc_env)
(initTicksConfig (hsc_dflags hsc_env))
mod mod_loc
- export_set (typeEnvTyCons type_env) binds
+ export_set (typeEnvTyCons type_env)
+ insts inst_meths binds
else return (binds, Nothing)
; let modBreaks
| Just (_, _, breakpointSpecs) <- m_tickInfo
=====================================
compiler/GHC/HsToCore/Ticks.hs
=====================================
@@ -5,7 +5,7 @@
{-
(c) Galois, 2006
(c) University of Glasgow, 2007
-(c) Florian Ragwitz, 2025
+(c) Florian Ragwitz, 2025-2026
-}
module GHC.HsToCore.Ticks
@@ -24,6 +24,8 @@ import GHC.Unit
import GHC.Core.Type
import GHC.Core.TyCon
+import GHC.Core.InstEnv
+import GHC.Core.Class
import GHC.Data.Maybe
import GHC.Data.FastString
@@ -56,6 +58,7 @@ import Data.Foldable (toList)
import Trace.Hpc.Mix
import Data.Bifunctor (second)
+import Data.Functor
import Data.List.NonEmpty (NonEmpty (..))
import Data.Set (Set)
import qualified Data.Set as Set
@@ -100,11 +103,13 @@ addTicksToBinds
-- isExportedId doesn't work yet (the desugarer
-- hasn't set it), so we have to work from this set.
-> [TyCon] -- ^ Type constructors in this module
+ -> [ClsInst]
+ -> IdEnv DFunId
-> LHsBinds GhcTc
-> IO (LHsBinds GhcTc, Maybe (FilePath, SizedSeq Tick, SizedSeq Tick))
addTicksToBinds logger cfg
- mod mod_loc exports tyCons binds
+ mod mod_loc exports tyCons insts inst_meths binds
| let passes = ticks_passes cfg
, not (null passes)
, Just orig_file <- ml_hs_file mod_loc = do
@@ -129,8 +134,13 @@ addTicksToBinds logger cfg
, this_mod = mod
, tickishType = tickish
, recSelBinds = emptyVarEnv
+ , instMeths = inst_meths
+ , instTicks = emptyVarEnv
}
- (binds',_,st') = unTM (addTickLHsBinds binds) env st
+ addTick = do
+ instTicks <- allocInstTicks insts
+ withEnv (\e -> e{ instTicks }) $ addTickLHsBinds binds
+ (binds',_,st') = unTM addTick env st
in (binds', st')
(binds1,st) = foldr tickPass (binds, initTTState) passes
@@ -231,7 +241,7 @@ addTickLHsBind :: LHsBind GhcTc -> TM (LHsBind GhcTc)
addTickLHsBind (L pos (XHsBindsLR bind@(AbsBinds { abs_binds = binds
, abs_exports = abs_exports
}))) =
- withEnv (add_rec_sels . add_inlines . add_exports) $ do
+ withEnv (add_inst_meths . add_rec_sels . add_inlines . add_exports) $ do
binds' <- addTickLHsBinds binds
return $ L pos $ XHsBindsLR $ bind { abs_binds = binds' }
where
@@ -259,19 +269,24 @@ addTickLHsBind (L pos (XHsBindsLR bind@(AbsBinds { abs_binds = binds
| ABE{ abe_poly, abe_mono } <- abs_exports
, RecSelId{} <- [idDetails abe_poly] ] }
+ add_inst_meths env =
+ env{ instMeths = instMeths env `extendVarEnvList`
+ [ (abe_mono, dfun)
+ | ABE{ abe_poly, abe_mono } <- abs_exports
+ , Just dfun <- [lookupVarEnv (instMeths env) abe_poly] ] }
+
addTickLHsBind (L pos (funBind@(FunBind { fun_id = L _ id, fun_matches = matches }))) = do
let name = getOccString id
decl_path <- getPathEntry
density <- getDensity
+ env <- getEnv
- inline_ids <- liftM inlines getEnv
-- See Note [inline sccs]
let inline = isInlinePragma (idInlinePragma id)
- || id `elemVarSet` inline_ids
+ || id `elemVarSet` inlines env
-- See Note [inline sccs]
- tickish <- tickishType `liftM` getEnv
- case tickish of { ProfNotes | inline -> return (L pos funBind); _ -> do
+ case tickishType env of { ProfNotes | inline -> return (L pos funBind); _ -> do
-- See Note [Record-selector ticks]
selTicks <- recSelTick id
@@ -294,16 +309,23 @@ addTickLHsBind (L pos (funBind@(FunBind { fun_id = L _ id, fun_matches = matches
toplev = null decl_path
exported = idName id `elemNameSet` exported_names
- tick <- if not blackListed &&
- shouldTickBind density toplev exported simple inline
+ let generatedInstMeth = id `elemVarEnv` instMeths env
+ && isGenerated (mg_origin (mg_ext matches))
+
+ tick <- if not generatedInstMeth
+ && not blackListed
+ && shouldTickBind density toplev exported simple inline
then
bindTick density name (locA pos) fvs
else
return Nothing
+ instTick <- instMethTick id
+
let mbCons = maybe Prelude.id (:)
return $ L pos $ funBind { fun_matches = mg
- , fun_ext = second (tick `mbCons`) (fun_ext funBind) }
+ , fun_ext = second ((instTick `mbCons`) . (tick `mbCons`))
+ (fun_ext funBind) }
} }
where
-- See Note [Record-selector ticks]
@@ -1098,6 +1120,8 @@ data TickTransEnv = TTE { fileName :: FastString
, this_mod :: Module
, tickishType :: TickishType
, recSelBinds :: IdEnv DVarSet
+ , instMeths :: IdEnv DFunId
+ , instTicks :: IdEnv CoreTickish
}
-- deriving Show
@@ -1238,6 +1262,18 @@ isBlackListed (RealSrcSpan pos _) = TM $ \ env st -> (Set.member pos (blackList
isBlackListed GeneratedSrcSpan{} = return False
isBlackListed UnhelpfulSpan{} = return False
+allocInstTicks :: [ClsInst] -> TM (IdEnv CoreTickish)
+allocInstTicks insts =
+ ifDensity TickForCoverage (mkVarEnv . catMaybes <$> mapM alloc insts) (pure emptyVarEnv)
+ where
+ alloc ClsInst{ is_dfun, is_cls }
+ | null (classMethods is_cls) = pure Nothing
+ | otherwise = fmap (is_dfun,) <$> allocATickBox (TopLevelBox [getOccString is_dfun])
+ False True (getSrcSpan is_dfun) noFVs
+
+instMethTick :: Id -> TM (Maybe CoreTickish)
+instMethTick id = getEnv <&> \e -> lookupVarEnv (instMeths e) id >>= lookupVarEnv (instTicks e)
+
-- the tick application inherits the source position of its
-- expression argument to support nested box allocations
allocTickBox :: BoxLabel -> Bool -> Bool -> SrcSpan -> TM (HsExpr GhcTc)
=====================================
compiler/GHC/Tc/Module.hs
=====================================
@@ -1770,7 +1770,7 @@ tcTopSrcDecls (HsGroup { hs_tyclds = tycl_decls,
-- Second pass over class and instance declarations,
-- now using the kind-checked decls
traceTc "Tc6" empty ;
- inst_binds <- tcInstDecls2 (tyClGroupTyClDecls tycl_decls) inst_infos ;
+ (inst_binds, inst_meths) <- tcInstDecls2 (tyClGroupTyClDecls tycl_decls) inst_infos ;
-- Foreign exports
traceTc "Tc7" empty ;
@@ -1802,8 +1802,9 @@ tcTopSrcDecls (HsGroup { hs_tyclds = tycl_decls,
, tcg_anns = tcg_anns tcg_env ++ annotations
, tcg_ann_env = extendAnnEnvList (tcg_ann_env tcg_env) annotations
, tcg_fords = tcg_fords tcg_env ++ foe_decls ++ fi_decls
- , tcg_dus = tcg_dus tcg_env `plusDU` usesOnly fo_fvs } } ;
+ , tcg_dus = tcg_dus tcg_env `plusDU` usesOnly fo_fvs
-- tcg_dus: see Note [Newtype constructor usage in foreign declarations]
+ , tcg_inst_meths = tcg_inst_meths tcg_env `plusVarEnv` inst_meths } } ;
-- See Note [Newtype constructor usage in foreign declarations]
addUsedGREs NoDeprecationWarnings (bagToList fo_gres) ;
=====================================
compiler/GHC/Tc/TyCl/Instance.hs
=====================================
@@ -1282,7 +1282,7 @@ takes a slightly different approach.
********************************************************************* -}
tcInstDecls2 :: [LTyClDecl GhcRn] -> [InstInfo GhcRn]
- -> TcM (LHsBinds GhcTc)
+ -> TcM (LHsBinds GhcTc, IdEnv DFunId)
-- (a) From each class declaration,
-- generate any default-method bindings
-- (b) From each instance decl
@@ -1299,11 +1299,11 @@ tcInstDecls2 tycl_decls inst_decls
-- Add the default method Ids (again)
-- (they were already added in GHC.Tc.TyCl.Utils.tcAddImplicits)
-- See Note [Default methods in the type environment]
- ; inst_binds_s <- tcExtendGlobalValEnv dm_ids $
- mapM tcInstDecl2 inst_decls
+ ; (inst_binds_s, inst_meths_s) <- unzip <$> (tcExtendGlobalValEnv dm_ids $
+ mapM tcInstDecl2 inst_decls)
-- Done
- ; return (dm_binds ++ concat inst_binds_s) }
+ ; return (dm_binds ++ concat inst_binds_s, plusVarEnvList inst_meths_s) }
{- Note [Default methods in the type environment]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1319,12 +1319,12 @@ So right here in tcInstDecls2 we must re-extend the type envt with
the default method Ids replete with their INLINE pragmas. Urk.
-}
-tcInstDecl2 :: InstInfo GhcRn -> TcM (LHsBinds GhcTc)
+tcInstDecl2 :: InstInfo GhcRn -> TcM (LHsBinds GhcTc, IdEnv DFunId)
-- Returns a binding for the dfun
tcInstDecl2 (InstInfo { iSpec = ispec, iBinds = ibinds })
- = recoverM (return emptyLHsBinds) $
- setSrcSpan loc $
- addErrCtxt (instDeclCtxt2 dfun_ty) $
+ = recoverM (return (emptyLHsBinds, emptyVarEnv)) $
+ setSrcSpan loc $
+ addErrCtxt (instDeclCtxt2 dfun_ty) $
do { -- Instantiate the instance decl with skolem constants
(skol_info, inst_tyvars, dfun_theta, clas, inst_tys) <- tcSkolDFunType dfun_ty
; dfun_ev_vars <- newEvVars dfun_theta
@@ -1342,7 +1342,7 @@ tcInstDecl2 (InstInfo { iSpec = ispec, iBinds = ibinds })
-- See Note [Typechecking plan for instance declarations]
; dfun_ev_binds_var <- newTcEvBinds
; let dfun_ev_binds = TcEvBinds dfun_ev_binds_var
- ; (tclvl, (sc_meth_ids, sc_meth_binds, sc_meth_implics))
+ ; (tclvl, (sc_meth_ids, sc_meth_binds, sc_meth_implics, meth_ids))
<- pushTcLevelM $
do { (sc_ids, sc_binds, sc_implics)
<- tcSuperClasses skol_info dfun_id clas inst_tyvars
@@ -1356,7 +1356,8 @@ tcInstDecl2 (InstInfo { iSpec = ispec, iBinds = ibinds })
; return ( sc_ids ++ meth_ids
, sc_binds ++ meth_binds
- , sc_implics `unionBags` meth_implics ) }
+ , sc_implics `unionBags` meth_implics
+ , meth_ids ) }
; imp <- newImplication
; emitImplication $
@@ -1413,7 +1414,7 @@ tcInstDecl2 (InstInfo { iSpec = ispec, iBinds = ibinds })
, abs_binds = [dict_bind]
, abs_sig = True }
- ; return (L loc' main_bind : sc_meth_binds)
+ ; return (L loc' main_bind : sc_meth_binds, mkVarEnv $ (,dfun_id) <$> meth_ids)
}
where
dfun_id = instanceDFunId ispec
=====================================
compiler/GHC/Tc/Types.hs
=====================================
@@ -157,6 +157,7 @@ import GHC.Types.Name.Env
import GHC.Types.Name.Set
import GHC.Types.Avail
import GHC.Types.Var
+import GHC.Types.Var.Env
import GHC.Types.TypeEnv
import GHC.Types.SourceFile
import GHC.Types.SrcLoc
@@ -530,6 +531,8 @@ data TcGblEnv
-- NB. BangPattern is to fix a leak, see #15111
tcg_fam_inst_env :: !FamInstEnv, -- ^ Ditto for family instances
-- NB. BangPattern is to fix a leak, see #15111
+ tcg_inst_meths :: IdEnv DFunId,
+ -- ^ Maps instance method Ids to the DFunId of the instance they belong to.
tcg_ann_env :: AnnEnv, -- ^ And for annotations
tcg_complete_match_env :: CompleteMatches,
-- ^ The complete matches for all /home-package/ modules;
=====================================
compiler/GHC/Tc/Utils/Monad.hs
=====================================
@@ -409,6 +409,7 @@ initTcGblEnv hsc_env hsc_src keep_rn_syntax mod loc =
, tcg_knot_vars = hsc_type_env_vars hsc_env
, tcg_known_key_maps = known_key_maps_var
, tcg_inst_env = emptyInstEnv
+ , tcg_inst_meths = emptyVarEnv
, tcg_fam_inst_env = emptyFamInstEnv
, tcg_ann_env = emptyAnnEnv
, tcg_complete_match_env = []
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/d4c60f391830dda7778971a8060d38…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/d4c60f391830dda7778971a8060d38…
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/rafl/hpc-instances] 10 commits: hpc: Test desired HPC behaviour for class instances
by Florian Ragwitz (@rafl) 05 Sep '26
by Florian Ragwitz (@rafl) 05 Sep '26
05 Sep '26
Florian Ragwitz pushed to branch wip/rafl/hpc-instances at Glasgow Haskell Compiler / GHC
Commits:
40681d2c by Florian Ragwitz at 2026-09-04T18:19:40-07:00
hpc: Test desired HPC behaviour for class instances
- - - - -
50d5df6d by Florian Ragwitz at 2026-09-04T18:19:40-07:00
hpc: Make instances available in TickTransEnv
- - - - -
94a10668 by Florian Ragwitz at 2026-09-04T18:19:40-07:00
hpc: Capture which method Ids belong to which dictionary function Id
- - - - -
bb8f91e5 by Florian Ragwitz at 2026-09-04T18:19:40-07:00
hpc: Carry method id mapping in TcGblEnv
- - - - -
98d4c514 by Florian Ragwitz at 2026-09-04T18:19:40-07:00
hpc: pass instance method mapping to addTicksToBinds and propagate ABEs
- - - - -
1eb8f430 by Florian Ragwitz at 2026-09-04T18:19:40-07:00
hpc: Do not create top-level boxes for generated instance methods
Users care about which of their written methods are or aren't covered, not
whether all of the inherited methods are covered as well. Those should be
covered where they are implemented.
This set of changes achieves the above by stopping to generate top-level boxes
which will often appear as uncovered with no clear indication as to why, and no
way to fix other than excercising code the user has not written.
- - - - -
988521cd by Florian Ragwitz at 2026-09-04T18:19:40-07:00
hpc: Allocate one top-level box for each class instance for coverage
- - - - -
8350e6a1 by Florian Ragwitz at 2026-09-04T18:19:40-07:00
hpc: Tick top-level boxes for instances when any method is used at run-time
- - - - -
9e958e61 by Florian Ragwitz at 2026-09-04T18:19:40-07:00
hpc: Don't allocate boxes for methodless classes
With the plumbing we currently have, these could never be covered, which is not
helpful to hpc users. This could be improved upon further.
- - - - -
d4c60f39 by Florian Ragwitz at 2026-09-04T18:19:40-07:00
Add a changelog entry for new HPC features
- - - - -
10 changed files:
- + changelog.d/hpc-classinsts
- compiler/GHC/HsToCore.hs
- compiler/GHC/HsToCore/Ticks.hs
- compiler/GHC/Tc/Module.hs
- compiler/GHC/Tc/TyCl/Instance.hs
- compiler/GHC/Tc/Types.hs
- compiler/GHC/Tc/Utils/Monad.hs
- + testsuite/tests/hpc/instmeths/instmeths.hs
- + testsuite/tests/hpc/instmeths/instmeths.stdout
- + testsuite/tests/hpc/instmeths/test.T
Changes:
=====================================
changelog.d/hpc-classinsts
=====================================
@@ -0,0 +1,23 @@
+section: compiler
+synopsis: Improve coverage reporting for class instances and their methods
+description:
+
+ Coverage reporting (:ghc-flag:`-fhpc`) now treats class instances more
+ naturally.
+
+ Compiler-generated instance methods, such as inherited default methods, no
+ longer receive separate top-level coverage boxes. Explicitly implemented
+ instance methods retain their own declaration coverage.
+
+ This means that it is no longer necessary to exercise all of a class' methods
+ in order for it to appear "covered".
+
+ Additionally, top-level coverage boxes are now created for all instances of
+ classes with methods, and ticked by the use of *any* of the class' methods.
+ This indicates that an instance was in fact used at run-time.
+
+ Note that classes without any instance methods are currently excluded from
+ this, and won't have any coverage boxes created.
+
+mrs: !16632
+issues: #17155
=====================================
compiler/GHC/HsToCore.hs
=====================================
@@ -147,6 +147,7 @@ deSugar hsc_env
tcg_tcs = tcs,
tcg_default_exports = defaults,
tcg_insts = insts,
+ tcg_inst_meths = inst_meths,
tcg_fam_insts = fam_insts,
tcg_complete_matches = complete_matches,
tcg_self_boot = self_boot
@@ -170,7 +171,8 @@ deSugar hsc_env
(hsc_logger hsc_env)
(initTicksConfig (hsc_dflags hsc_env))
mod mod_loc
- export_set (typeEnvTyCons type_env) binds
+ export_set (typeEnvTyCons type_env)
+ insts inst_meths binds
else return (binds, Nothing)
; let modBreaks
| Just (_, _, breakpointSpecs) <- m_tickInfo
=====================================
compiler/GHC/HsToCore/Ticks.hs
=====================================
@@ -24,6 +24,8 @@ import GHC.Unit
import GHC.Core.Type
import GHC.Core.TyCon
+import GHC.Core.InstEnv
+import GHC.Core.Class
import GHC.Data.Maybe
import GHC.Data.FastString
@@ -100,11 +102,13 @@ addTicksToBinds
-- isExportedId doesn't work yet (the desugarer
-- hasn't set it), so we have to work from this set.
-> [TyCon] -- ^ Type constructors in this module
+ -> [ClsInst]
+ -> IdEnv DFunId
-> LHsBinds GhcTc
-> IO (LHsBinds GhcTc, Maybe (FilePath, SizedSeq Tick, SizedSeq Tick))
addTicksToBinds logger cfg
- mod mod_loc exports tyCons binds
+ mod mod_loc exports tyCons insts inst_meths binds
| let passes = ticks_passes cfg
, not (null passes)
, Just orig_file <- ml_hs_file mod_loc = do
@@ -129,8 +133,13 @@ addTicksToBinds logger cfg
, this_mod = mod
, tickishType = tickish
, recSelBinds = emptyVarEnv
+ , instMeths = inst_meths
+ , instTicks = emptyVarEnv
}
- (binds',_,st') = unTM (addTickLHsBinds binds) env st
+ addTick = do
+ instTicks <- allocInstTicks insts
+ withEnv (\e -> e{ instTicks }) $ addTickLHsBinds binds
+ (binds',_,st') = unTM addTick env st
in (binds', st')
(binds1,st) = foldr tickPass (binds, initTTState) passes
@@ -231,7 +240,7 @@ addTickLHsBind :: LHsBind GhcTc -> TM (LHsBind GhcTc)
addTickLHsBind (L pos (XHsBindsLR bind@(AbsBinds { abs_binds = binds
, abs_exports = abs_exports
}))) =
- withEnv (add_rec_sels . add_inlines . add_exports) $ do
+ withEnv (add_inst_meths . add_rec_sels . add_inlines . add_exports) $ do
binds' <- addTickLHsBinds binds
return $ L pos $ XHsBindsLR $ bind { abs_binds = binds' }
where
@@ -259,19 +268,26 @@ addTickLHsBind (L pos (XHsBindsLR bind@(AbsBinds { abs_binds = binds
| ABE{ abe_poly, abe_mono } <- abs_exports
, RecSelId{} <- [idDetails abe_poly] ] }
+ add_inst_meths env =
+ env{ instMeths = instMeths env `extendVarEnvList`
+ [ (abe_mono, dfun)
+ | ABE{ abe_poly, abe_mono } <- abs_exports
+ , Just dfun <- [lookupVarEnv (instMeths env) abe_poly] ] }
+
addTickLHsBind (L pos (funBind@(FunBind { fun_id = L _ id, fun_matches = matches }))) = do
let name = getOccString id
decl_path <- getPathEntry
density <- getDensity
+ env <- getEnv
- inline_ids <- liftM inlines getEnv
-- See Note [inline sccs]
let inline = isInlinePragma (idInlinePragma id)
- || id `elemVarSet` inline_ids
+ || id `elemVarSet` inlines env
-- See Note [inline sccs]
- tickish <- tickishType `liftM` getEnv
- case tickish of { ProfNotes | inline -> return (L pos funBind); _ -> do
+ case tickishType env of { ProfNotes | inline -> return (L pos funBind); _ -> do
+
+ instTick <- instMethTick id
-- See Note [Record-selector ticks]
selTicks <- recSelTick id
@@ -294,8 +310,12 @@ addTickLHsBind (L pos (funBind@(FunBind { fun_id = L _ id, fun_matches = matches
toplev = null decl_path
exported = idName id `elemNameSet` exported_names
- tick <- if not blackListed &&
- shouldTickBind density toplev exported simple inline
+ let generatedInstMeth = id `elemVarEnv` instMeths env
+ && isGenerated (mg_origin (mg_ext matches))
+
+ tick <- if not generatedInstMeth
+ && not blackListed
+ && shouldTickBind density toplev exported simple inline
then
bindTick density name (locA pos) fvs
else
@@ -303,7 +323,8 @@ addTickLHsBind (L pos (funBind@(FunBind { fun_id = L _ id, fun_matches = matches
let mbCons = maybe Prelude.id (:)
return $ L pos $ funBind { fun_matches = mg
- , fun_ext = second (tick `mbCons`) (fun_ext funBind) }
+ , fun_ext = second ((instTick `mbCons`) . (tick `mbCons`))
+ (fun_ext funBind) }
} }
where
-- See Note [Record-selector ticks]
@@ -1098,6 +1119,8 @@ data TickTransEnv = TTE { fileName :: FastString
, this_mod :: Module
, tickishType :: TickishType
, recSelBinds :: IdEnv DVarSet
+ , instMeths :: IdEnv DFunId
+ , instTicks :: IdEnv CoreTickish
}
-- deriving Show
@@ -1238,6 +1261,21 @@ isBlackListed (RealSrcSpan pos _) = TM $ \ env st -> (Set.member pos (blackList
isBlackListed GeneratedSrcSpan{} = return False
isBlackListed UnhelpfulSpan{} = return False
+allocInstTicks :: [ClsInst] -> TM (IdEnv CoreTickish)
+allocInstTicks insts =
+ ifDensity TickForCoverage (mkVarEnv . catMaybes <$> mapM alloc insts) (pure emptyVarEnv)
+ where
+ alloc inst
+ | null (classMethods (is_cls inst)) = pure Nothing
+ | otherwise = do
+ let dfun = is_dfun inst
+ fmap (dfun,) <$> allocATickBox (TopLevelBox [getOccString dfun])
+ False True (getSrcSpan dfun) noFVs
+
+instMethTick :: Id -> TM (Maybe CoreTickish)
+instMethTick id = getEnv >>= \e ->
+ pure $ lookupVarEnv (instMeths e) id >>= lookupVarEnv (instTicks e)
+
-- the tick application inherits the source position of its
-- expression argument to support nested box allocations
allocTickBox :: BoxLabel -> Bool -> Bool -> SrcSpan -> TM (HsExpr GhcTc)
=====================================
compiler/GHC/Tc/Module.hs
=====================================
@@ -1770,7 +1770,7 @@ tcTopSrcDecls (HsGroup { hs_tyclds = tycl_decls,
-- Second pass over class and instance declarations,
-- now using the kind-checked decls
traceTc "Tc6" empty ;
- inst_binds <- tcInstDecls2 (tyClGroupTyClDecls tycl_decls) inst_infos ;
+ (inst_binds, inst_meths) <- tcInstDecls2 (tyClGroupTyClDecls tycl_decls) inst_infos ;
-- Foreign exports
traceTc "Tc7" empty ;
@@ -1802,8 +1802,9 @@ tcTopSrcDecls (HsGroup { hs_tyclds = tycl_decls,
, tcg_anns = tcg_anns tcg_env ++ annotations
, tcg_ann_env = extendAnnEnvList (tcg_ann_env tcg_env) annotations
, tcg_fords = tcg_fords tcg_env ++ foe_decls ++ fi_decls
- , tcg_dus = tcg_dus tcg_env `plusDU` usesOnly fo_fvs } } ;
+ , tcg_dus = tcg_dus tcg_env `plusDU` usesOnly fo_fvs
-- tcg_dus: see Note [Newtype constructor usage in foreign declarations]
+ , tcg_inst_meths = tcg_inst_meths tcg_env `plusVarEnv` inst_meths } } ;
-- See Note [Newtype constructor usage in foreign declarations]
addUsedGREs NoDeprecationWarnings (bagToList fo_gres) ;
=====================================
compiler/GHC/Tc/TyCl/Instance.hs
=====================================
@@ -1282,7 +1282,7 @@ takes a slightly different approach.
********************************************************************* -}
tcInstDecls2 :: [LTyClDecl GhcRn] -> [InstInfo GhcRn]
- -> TcM (LHsBinds GhcTc)
+ -> TcM (LHsBinds GhcTc, IdEnv DFunId)
-- (a) From each class declaration,
-- generate any default-method bindings
-- (b) From each instance decl
@@ -1299,11 +1299,11 @@ tcInstDecls2 tycl_decls inst_decls
-- Add the default method Ids (again)
-- (they were already added in GHC.Tc.TyCl.Utils.tcAddImplicits)
-- See Note [Default methods in the type environment]
- ; inst_binds_s <- tcExtendGlobalValEnv dm_ids $
- mapM tcInstDecl2 inst_decls
+ ; (inst_binds_s, inst_meths_s) <- unzip <$> (tcExtendGlobalValEnv dm_ids $
+ mapM tcInstDecl2 inst_decls)
-- Done
- ; return (dm_binds ++ concat inst_binds_s) }
+ ; return (dm_binds ++ concat inst_binds_s, plusVarEnvList inst_meths_s) }
{- Note [Default methods in the type environment]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1319,12 +1319,12 @@ So right here in tcInstDecls2 we must re-extend the type envt with
the default method Ids replete with their INLINE pragmas. Urk.
-}
-tcInstDecl2 :: InstInfo GhcRn -> TcM (LHsBinds GhcTc)
+tcInstDecl2 :: InstInfo GhcRn -> TcM (LHsBinds GhcTc, IdEnv DFunId)
-- Returns a binding for the dfun
tcInstDecl2 (InstInfo { iSpec = ispec, iBinds = ibinds })
- = recoverM (return emptyLHsBinds) $
- setSrcSpan loc $
- addErrCtxt (instDeclCtxt2 dfun_ty) $
+ = recoverM (return (emptyLHsBinds, emptyVarEnv)) $
+ setSrcSpan loc $
+ addErrCtxt (instDeclCtxt2 dfun_ty) $
do { -- Instantiate the instance decl with skolem constants
(skol_info, inst_tyvars, dfun_theta, clas, inst_tys) <- tcSkolDFunType dfun_ty
; dfun_ev_vars <- newEvVars dfun_theta
@@ -1342,7 +1342,7 @@ tcInstDecl2 (InstInfo { iSpec = ispec, iBinds = ibinds })
-- See Note [Typechecking plan for instance declarations]
; dfun_ev_binds_var <- newTcEvBinds
; let dfun_ev_binds = TcEvBinds dfun_ev_binds_var
- ; (tclvl, (sc_meth_ids, sc_meth_binds, sc_meth_implics))
+ ; (tclvl, (sc_meth_ids, sc_meth_binds, sc_meth_implics, meth_ids))
<- pushTcLevelM $
do { (sc_ids, sc_binds, sc_implics)
<- tcSuperClasses skol_info dfun_id clas inst_tyvars
@@ -1356,7 +1356,8 @@ tcInstDecl2 (InstInfo { iSpec = ispec, iBinds = ibinds })
; return ( sc_ids ++ meth_ids
, sc_binds ++ meth_binds
- , sc_implics `unionBags` meth_implics ) }
+ , sc_implics `unionBags` meth_implics
+ , meth_ids ) }
; imp <- newImplication
; emitImplication $
@@ -1413,7 +1414,7 @@ tcInstDecl2 (InstInfo { iSpec = ispec, iBinds = ibinds })
, abs_binds = [dict_bind]
, abs_sig = True }
- ; return (L loc' main_bind : sc_meth_binds)
+ ; return (L loc' main_bind : sc_meth_binds, mkVarEnv $ (,dfun_id) <$> meth_ids)
}
where
dfun_id = instanceDFunId ispec
=====================================
compiler/GHC/Tc/Types.hs
=====================================
@@ -157,6 +157,7 @@ import GHC.Types.Name.Env
import GHC.Types.Name.Set
import GHC.Types.Avail
import GHC.Types.Var
+import GHC.Types.Var.Env
import GHC.Types.TypeEnv
import GHC.Types.SourceFile
import GHC.Types.SrcLoc
@@ -528,6 +529,7 @@ data TcGblEnv
-- ^ Instance envt for all /home-package/ modules;
-- Includes the dfuns in tcg_insts
-- NB. BangPattern is to fix a leak, see #15111
+ tcg_inst_meths :: IdEnv DFunId,
tcg_fam_inst_env :: !FamInstEnv, -- ^ Ditto for family instances
-- NB. BangPattern is to fix a leak, see #15111
tcg_ann_env :: AnnEnv, -- ^ And for annotations
=====================================
compiler/GHC/Tc/Utils/Monad.hs
=====================================
@@ -409,6 +409,7 @@ initTcGblEnv hsc_env hsc_src keep_rn_syntax mod loc =
, tcg_knot_vars = hsc_type_env_vars hsc_env
, tcg_known_key_maps = known_key_maps_var
, tcg_inst_env = emptyInstEnv
+ , tcg_inst_meths = emptyVarEnv
, tcg_fam_inst_env = emptyFamInstEnv
, tcg_ann_env = emptyAnnEnv
, tcg_complete_match_env = []
=====================================
testsuite/tests/hpc/instmeths/instmeths.hs
=====================================
@@ -0,0 +1,58 @@
+import Data.Bifunctor
+import Data.List
+import Data.Maybe
+import Trace.Hpc.Mix
+import Trace.Hpc.Reflect
+import Trace.Hpc.Tix
+import Trace.Hpc.Util
+
+class Foo a where
+ defMeth, defMeth', reqMeth :: a -> String
+ defMeth = const "class default"
+ defMeth' = const "class default"
+
+newtype T a = T a deriving (Show, Eq, Functor)
+instance Foo (T a) where
+ reqMeth = const "Foo (T a) instance"
+
+newtype T' a = T' a
+
+instance Foo (T' a) where
+ reqMeth = const "Foo (T' a) instance"
+ defMeth = const "Foo (T' a) instance"
+
+-- no method use could "cover" this instance, so we (for now) omit the top-level box for it
+class Marker a
+instance Marker (T Bool)
+
+interesting :: (Int, MixEntry) -> Maybe (Int, HpcPos, String)
+interesting (n, (pos, TopLevelBox [name])) | isInteresting name = Just (n, pos, name)
+ where isInteresting = (||) <$> isInst <*> isMeth
+ isInst = ("$f" `isPrefixOf`)
+ isMeth = (||) <$> (`elem` ["(==)", "(/=)", "show", "showList", "showsPrec", "fmap", "(<$)"])
+ <*> ("Meth" `isSubsequenceOf`)
+interesting _ = Nothing
+
+-- candidate for HPC.Utils, maybe?
+sourceAt :: String -> HpcPos -> String
+sourceAt src pos
+ | l1 == l2 = take (c2 - c1 + 1) . drop (c1 - 1) $ ls !! (l1 - 1)
+ | otherwise = intercalate "\n" $ first : (middle ++ [last])
+ where
+ (l1, c1, l2, c2) = fromHpcPos pos
+ ls = lines src
+ first = drop (c1 - 1) $ ls !! (l1 - 1)
+ middle = take (l2 - l1 - 1) $ drop l1 ls
+ last = take c2 $ ls !! (l2 - 1)
+
+main :: IO ()
+main = do
+ print (T 23 == (succ <$> T 22)) -- tick Eq and Functor by using any of their methods
+ print (reqMeth (T' 1)) -- tick `Foo (T' a)` and that instance's `reqMeth`
+
+ Mix source _ _ _ mixEntries <- readMix [".hpc"] (Left "Main")
+ src <- readFileUtf8 source
+ let boxes = mapMaybe interesting $ zip [0 ..] mixEntries
+ Tix [TixModule "Main" _ _ counts] <- examineTix
+ mapM_ print [ (counts !! n, path, sourceAt src pos)
+ | (n, pos, path) <- sortOn (\(_, _, s) -> s) boxes ]
=====================================
testsuite/tests/hpc/instmeths/instmeths.stdout
=====================================
@@ -0,0 +1,12 @@
+True
+"Foo (T' a) instance"
+(1,"$fEqT","Eq")
+(0,"$fFooT","Foo (T a)")
+(1,"$fFooT'","Foo (T' a)")
+(1,"$fFunctorT","Functor")
+(0,"$fShowT","Show")
+(0,"defMeth","defMeth = const \"class default\"")
+(0,"defMeth","defMeth = const \"Foo (T' a) instance\"")
+(0,"defMeth'","defMeth' = const \"class default\"")
+(0,"reqMeth","reqMeth = const \"Foo (T a) instance\"")
+(1,"reqMeth","reqMeth = const \"Foo (T' a) instance\"")
=====================================
testsuite/tests/hpc/instmeths/test.T
=====================================
@@ -0,0 +1,5 @@
+setTestOpts([omit_ghci, when(fast(), skip), js_skip])
+
+test('instmeths',
+ [ignore_extension],
+ compile_and_run, ['-fhpc'])
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/989054b8d826df2810e115378b3f0b…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/989054b8d826df2810e115378b3f0b…
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/T25450-march-native] Implement -march=native for x86/x86_64
by Simon Jakobi (@sjakobi) 05 Sep '26
by Simon Jakobi (@sjakobi) 05 Sep '26
05 Sep '26
Simon Jakobi pushed to branch wip/sjakobi/T25450-march-native at Glasgow Haskell Compiler / GHC
Commits:
cfca4580 by Simon Jakobi at 2026-09-05T00:32:47+02:00
Implement -march=native for x86/x86_64
Add a -march=native flag that probes the host CPU and enables the
matching CPU-feature DynFlags, so it affects both the NCG and the LLVM
backend. Detected features are folded additively into the existing
flags, so explicit -m... options are never disabled.
The probe is a small C shim over GCC/Clang's __builtin_cpu_supports.
The deviations that remain are documented in
compiler/cbits/cpu_features_x86.c.
The RTS exports the runtime symbols behind the builtin (__cpu_model &
co.) so GHCi's runtime linker can load the ghc library where GHCi links
statically, e.g. on Windows or musl.
The flag is registered in expected-undocumented-flags.txt due to #27321.
Thanks to @aratamizuki for pointing out the macOS AVX-512 XSAVE quirk
and for testing!
Closes #25450
Assisted-by: Claude Fable 5
- - - - -
17 changed files:
- + changelog.d/march-native
- changelog.d/print-enabled-cpu-features
- + compiler/GHC/Driver/CpuFeatures.hs
- compiler/GHC/Driver/DynFlags.hs
- compiler/GHC/Driver/Session.hs
- + compiler/cbits/cpu_features_x86.c
- compiler/ghc.cabal.in
- docs/users_guide/expected-undocumented-flags.txt
- docs/users_guide/using.rst
- rts/RtsSymbols.c
- testsuite/tests/codeGen/should_gen_asm/all.T
- + testsuite/tests/codeGen/should_gen_asm/march-native-enables-popcnt.asm
- + testsuite/tests/codeGen/should_gen_asm/march-native-enables-popcnt.hs
- testsuite/tests/driver/all.T
- + testsuite/tests/driver/march_native.stdout
- + testsuite/tests/driver/march_native_additive.stdout
- + testsuite/tests/driver/march_native_unsupported_arch.stderr
Changes:
=====================================
changelog.d/march-native
=====================================
@@ -0,0 +1,12 @@
+section: compiler
+synopsis: Add -march=native flag
+issues: #25450
+mrs: !16126
+
+description:
+ GHC now supports ``-march=native`` on x86 and x86_64. It probes the CPU of the
+ machine running GHC and enables all of the corresponding ``-m...`` CPU-feature
+ options automatically (such as ``-msse4.2``, ``-mavx2``, ``-mbmi2`` and
+ ``-mfma``), for both the native code generator and the LLVM backend. The
+ detected features are enabled in addition to any explicitly requested feature
+ flags. The flag is rejected for non-x86 targets and when cross-compiling.
=====================================
changelog.d/print-enabled-cpu-features
=====================================
@@ -8,9 +8,11 @@ description:
prints a JSON object describing the CPU features currently enabled for code
generation, together with a set of ``-m...`` flags that reproduce the
effective feature set for the current target.
- Dynamic options such as ``-mavx2`` and ``-mbmi2`` are respected. ::
+ Dynamic options such as ``-mavx2`` and ``-mbmi2`` are respected, so the flag
+ can also be used to inspect which features :ghc-flag:`-march=native` detected
+ and enabled. ::
- $ ghc -mavx2 --print-enabled-cpu-features
+ $ ghc -march=native --print-enabled-cpu-features
{"tag":"enabled-cpu-features","version":1,"target":"x86_64-linux-gnu",
- "features":["SSE","SSE2","SSE3","SSSE3","SSE4.1","SSE4.2","AVX","AVX2"],
- "as_m_flags":["-mavx2"]}
+ "features":["SSE","SSE2","SSE3","SSSE3","SSE4.1","SSE4.2","AVX","AVX2","BMI1","BMI2","FMA"],
+ "as_m_flags":["-mavx2","-mbmi2","-mfma"]}
=====================================
compiler/GHC/Driver/CpuFeatures.hs
=====================================
@@ -0,0 +1,64 @@
+{-# LANGUAGE CPP #-}
+
+module GHC.Driver.CpuFeatures
+ ( X86CpuFeature(..)
+ , cachedX86CpuFeatures
+ ) where
+
+import GHC.Prelude
+
+import Data.Word (Word64)
+import System.IO.Unsafe (unsafePerformIO)
+
+-- | x86 CPU features understood by GHC's native CPU feature probe.
+data X86CpuFeature
+ = SSE2
+ | SSE3
+ | SSSE3
+ | SSE4_1
+ | SSE4_2
+ | AVX
+ | AVX2
+ | AVX512F
+ | AVX512BW
+ | AVX512CD
+ | AVX512DQ
+ | AVX512VL
+ | BMI1
+ | BMI2
+ | FMA
+ | GFNI
+ deriving (Eq, Ord, Show, Enum, Bounded)
+
+-- | Decode the bitmask returned by 'ghc_detect_x86_cpu_features'.
+--
+-- Bit positions are constructor order, which must match the enum in
+-- @compiler/cbits/cpu_features_x86.c@.
+decodeX86CpuFeatureMask :: Word64 -> [X86CpuFeature]
+decodeX86CpuFeatureMask mask =
+ [ feat
+ | feat <- [minBound .. maxBound]
+ , testBit mask (fromEnum feat)
+ ]
+
+-- | Low-level FFI access to the C probe.
+detectX86CpuFeatureMask :: IO Word64
+#if defined(javascript_HOST_ARCH)
+detectX86CpuFeatureMask = pure 0
+#else
+detectX86CpuFeatureMask = c_ghc_detect_x86_cpu_features
+#endif
+
+-- | Probe host x86 CPU features and decode them into an ordered feature list.
+detectX86CpuFeatures :: IO [X86CpuFeature]
+detectX86CpuFeatures = decodeX86CpuFeatureMask <$> detectX86CpuFeatureMask
+
+-- | The host's x86 CPU features, probed once and memoized.
+cachedX86CpuFeatures :: [X86CpuFeature]
+cachedX86CpuFeatures = unsafePerformIO detectX86CpuFeatures
+{-# NOINLINE cachedX86CpuFeatures #-}
+
+#if !defined(javascript_HOST_ARCH)
+foreign import ccall unsafe "ghc_detect_x86_cpu_features"
+ c_ghc_detect_x86_cpu_features :: IO Word64
+#endif
=====================================
compiler/GHC/Driver/DynFlags.hs
=====================================
@@ -473,6 +473,8 @@ data DynFlags = DynFlags {
fma :: Bool, -- ^ Enable FMA instructions.
gfni :: Bool, -- ^ Enable GFNI Instructions.
la664 :: Bool, -- ^ Enable LA664 instructions
+ marchNative :: Bool, -- ^ @-march=native@ was requested; the host
+ -- CPU features are applied during flag parsing.
-- Constants used to control the amount of optimization done.
@@ -765,6 +767,7 @@ defaultDynFlags mySettings =
gfni = False,
-- For LoongArch, la464 is used by default.
la664 = False,
+ marchNative = False,
maxInlineAllocSize = 128,
maxInlineMemcpyInsns = 32,
=====================================
compiler/GHC/Driver/Session.hs
=====================================
@@ -245,6 +245,8 @@ import GHC.Platform
import GHC.Platform.Ways
import GHC.Platform.Profile
import GHC.Platform.ArchOS
+import GHC.Platform.Host (hostPlatformArch)
+import qualified GHC.Driver.CpuFeatures as Cpu
import GHC.Unit.Types
import GHC.Unit.Parser
@@ -907,8 +909,10 @@ parseDynamicFlagsFull activeFlags cmdline logger dflags0 args = do
unless (null errs) $ liftIO $ throwGhcExceptionIO $ errorsToGhcException $
map ((rdr . ppr . getLoc &&& unLoc) . errMsg) $ errs
+ dflags1' <- applyMarchNative dflags1
+
-- check for disabled flags in safe haskell
- let (dflags2, sh_warns) = safeFlagCheck cmdline dflags1
+ let (dflags2, sh_warns) = safeFlagCheck cmdline dflags1'
theWays = ways dflags2
unless (allowed_combination theWays) $ liftIO $
@@ -1743,6 +1747,7 @@ dynamic_flags_deps = [
, make_ord_flag defGhcFlag "mavx512vl" (noArg (\d -> d { avx512vl = True }))
, make_ord_flag defGhcFlag "mfma" (noArg (\d -> d { fma = True }))
, make_ord_flag defGhcFlag "mgfni" (noArg (\d -> d { gfni = True }))
+ , make_ord_flag defGhcFlag "march=native" (noArg (\d -> d { marchNative = True }))
, make_ord_flag defGhcFlag "mla664" (noArg (\d -> d { la664 = True }))
@@ -3826,6 +3831,53 @@ x86AsMFlags dflags =
gfniFlags = [ "-mgfni" | gfni dflags ]
+-- | Apply a requested @-march=native@ by probing the host CPU and enabling the
+-- matching CPU-feature flags.
+applyMarchNative :: MonadIO m => DynFlags -> m DynFlags
+applyMarchNative dflags
+ | not (marchNative dflags) = return dflags
+ | otherwise = do
+ let arch = platformArch (targetPlatform dflags)
+ unless (arch == ArchX86 || arch == ArchX86_64) $ liftIO $
+ throwGhcExceptionIO $ CmdLineError
+ "-march=native is only supported on x86 and x86_64 targets"
+ unless (arch == hostPlatformArch) $ liftIO $
+ throwGhcExceptionIO $ CmdLineError
+ "-march=native is not supported when cross-compiling"
+ return (applyX86CpuFeatures Cpu.cachedX86CpuFeatures dflags)
+
+-- | Enable the 'DynFlags' CPU-feature fields corresponding to a probed set of
+-- host x86 features.
+applyX86CpuFeatures :: [Cpu.X86CpuFeature] -> DynFlags -> DynFlags
+applyX86CpuFeatures feats dflags = dflags
+ { sseAvxVersion = foldr (max . Just) (sseAvxVersion dflags) sseLevels
+ , bmiVersion = foldr (max . Just) (bmiVersion dflags) bmiLevels
+ , avx512f = avx512f dflags || has Cpu.AVX512F
+ , avx512bw = avx512bw dflags || has Cpu.AVX512BW
+ , avx512cd = avx512cd dflags || has Cpu.AVX512CD
+ , avx512dq = avx512dq dflags || has Cpu.AVX512DQ
+ , avx512vl = avx512vl dflags || has Cpu.AVX512VL
+ , fma = fma dflags || has Cpu.FMA
+ , gfni = gfni dflags || has Cpu.GFNI
+ }
+ where
+ has feat = feat `elem` feats
+ sseLevels = [ lvl | feat <- feats, Just lvl <- [sseLevelOf feat] ]
+ bmiLevels = [ lvl | feat <- feats, Just lvl <- [bmiLevelOf feat] ]
+ sseLevelOf feat = case feat of
+ Cpu.SSE2 -> Just SSE2
+ Cpu.SSE3 -> Just SSE3
+ Cpu.SSSE3 -> Just SSSE3
+ Cpu.SSE4_1 -> Just SSE4
+ Cpu.SSE4_2 -> Just SSE42
+ Cpu.AVX -> Just AVX1
+ Cpu.AVX2 -> Just AVX2
+ _ -> Nothing
+ bmiLevelOf feat = case feat of
+ Cpu.BMI1 -> Just BMI1
+ Cpu.BMI2 -> Just BMI2
+ _ -> Nothing
+
-- | Query if the target RTS has the given 'Ways'. It's computed from
-- the @"RTS ways"@ field in the settings file.
targetHasRTSWays :: DynFlags -> Ways -> Bool
=====================================
compiler/cbits/cpu_features_x86.c
=====================================
@@ -0,0 +1,97 @@
+#include <HsFFI.h>
+
+/* Runtime x86 CPU feature detection backing GHC.Driver.CpuFeatures
+ (-march=native).
+
+ The bit positions in the returned mask are GHC's own; the enum below
+ must match the constructor order of X86CpuFeature in
+ GHC.Driver.CpuFeatures.
+
+ The __builtin_cpu_* calls below compile to references to libgcc/compiler-rt
+ symbols (__cpu_model & co.), which GHCi's runtime linker can only resolve
+ because the RTS exports them: see RTS_X86_CPU_MODEL_SYMBOLS in
+ rts/RtsSymbols.c and keep its guards in sync with this file's. */
+
+enum {
+ GHC_X86_FEAT_SSE2 = 0,
+ GHC_X86_FEAT_SSE3,
+ GHC_X86_FEAT_SSSE3,
+ GHC_X86_FEAT_SSE4_1,
+ GHC_X86_FEAT_SSE4_2,
+ GHC_X86_FEAT_AVX,
+ GHC_X86_FEAT_AVX2,
+ GHC_X86_FEAT_AVX512F,
+ GHC_X86_FEAT_AVX512BW,
+ GHC_X86_FEAT_AVX512CD,
+ GHC_X86_FEAT_AVX512DQ,
+ GHC_X86_FEAT_AVX512VL,
+ GHC_X86_FEAT_BMI1,
+ GHC_X86_FEAT_BMI2,
+ GHC_X86_FEAT_FMA,
+ GHC_X86_FEAT_GFNI
+};
+
+#if (defined(__i386__) || defined(__x86_64__)) \
+ && (defined(__GNUC__) || defined(__clang__))
+
+/* GFNI joined the __builtin_cpu_supports feature lists later than the
+ other features here (GCC 11; compiler-rt's cpu_model in LLVM 12).
+ Under an older bootstrap compiler we conservatively report no GFNI. */
+#if (defined(__clang_major__) && __clang_major__ >= 12) \
+ || (!defined(__clang__) && defined(__GNUC__) && __GNUC__ >= 11)
+#define GHC_HAS_CPU_SUPPORTS_GFNI 1
+#else
+#define GHC_HAS_CPU_SUPPORTS_GFNI 0
+#endif
+
+HsWord64 ghc_detect_x86_cpu_features(void)
+{
+ __builtin_cpu_init();
+
+ HsWord64 feats = 0;
+
+ /* __builtin_cpu_supports returns the feature's (nonzero) mask bit,
+ not 0/1, so it must be normalized before shifting. */
+#define GHC_CPU_FEATURE(name, bit) \
+ if (__builtin_cpu_supports(name)) { feats |= ((HsWord64)1 << (bit)); }
+
+ GHC_CPU_FEATURE("sse2", GHC_X86_FEAT_SSE2)
+ GHC_CPU_FEATURE("sse3", GHC_X86_FEAT_SSE3)
+ GHC_CPU_FEATURE("ssse3", GHC_X86_FEAT_SSSE3)
+ GHC_CPU_FEATURE("sse4.1", GHC_X86_FEAT_SSE4_1)
+ GHC_CPU_FEATURE("sse4.2", GHC_X86_FEAT_SSE4_2)
+ GHC_CPU_FEATURE("avx", GHC_X86_FEAT_AVX)
+ GHC_CPU_FEATURE("avx2", GHC_X86_FEAT_AVX2)
+ GHC_CPU_FEATURE("avx512f", GHC_X86_FEAT_AVX512F)
+ GHC_CPU_FEATURE("avx512bw", GHC_X86_FEAT_AVX512BW)
+ GHC_CPU_FEATURE("avx512cd", GHC_X86_FEAT_AVX512CD)
+ GHC_CPU_FEATURE("avx512dq", GHC_X86_FEAT_AVX512DQ)
+ GHC_CPU_FEATURE("avx512vl", GHC_X86_FEAT_AVX512VL)
+ GHC_CPU_FEATURE("bmi", GHC_X86_FEAT_BMI1)
+ GHC_CPU_FEATURE("bmi2", GHC_X86_FEAT_BMI2)
+
+ /* FMA is usable only when the OS saves the AVX register state. However
+ compiler-rt's cpu_model/x86.c reports "fma" from the raw CPUID bit. */
+ if (__builtin_cpu_supports("fma") && __builtin_cpu_supports("avx")) {
+ feats |= ((HsWord64)1 << GHC_X86_FEAT_FMA);
+ }
+
+#if GHC_HAS_CPU_SUPPORTS_GFNI
+ GHC_CPU_FEATURE("gfni", GHC_X86_FEAT_GFNI)
+#endif
+
+#undef GHC_CPU_FEATURE
+
+ return feats;
+}
+
+#else
+
+/* Non-x86 host, or a compiler without __builtin_cpu_supports (e.g. MSVC):
+ report no features. */
+HsWord64 ghc_detect_x86_cpu_features(void)
+{
+ return 0;
+}
+
+#endif
=====================================
compiler/ghc.cabal.in
=====================================
@@ -187,6 +187,7 @@ Library
else
c-sources:
cbits/cutils.c
+ cbits/cpu_features_x86.c
cbits/genSym.c
cbits/keepCAFsForGHCi.c
@@ -518,6 +519,7 @@ Library
GHC.Driver.Config.StgToCmm
GHC.Driver.Config.Tidy
GHC.Driver.Config.StgToJS
+ GHC.Driver.CpuFeatures
GHC.Driver.DynFlags
GHC.Driver.IncludeSpecs
GHC.Driver.Downsweep
=====================================
docs/users_guide/expected-undocumented-flags.txt
=====================================
@@ -74,6 +74,7 @@
-instantiated-with
-keep-hi-file
-keep-o-file
+-march=native
-n
-no-keep-hi-file
-no-keep-o-file
=====================================
docs/users_guide/using.rst
=====================================
@@ -539,7 +539,9 @@ The available mode flags are:
Print a JSON object describing the CPU features currently enabled for code
generation, together with a set of ``-m...`` flags that reproduce the
effective feature set for the current target.
- Dynamic options such as ``-mavx2`` and ``-mbmi2`` are respected.
+ Dynamic options such as ``-mavx2`` and ``-mbmi2`` are respected, so this flag
+ can also be used to inspect which features :ghc-flag:`-march=native` detected
+ and enabled.
.. ghc-flag:: --print-debug-on
:shortdesc: print whether GHC was built with ``-DDEBUG``
@@ -1897,6 +1899,34 @@ Some flags only make sense for particular target platforms.
so this flag has no effect when used with the :ref:`native code generator <native-code-gen>`
or the :ref:`LLVM backend <llvm-code-gen>`.
+.. ghc-flag:: -march=native
+ :shortdesc: (x86 only) Enable all CPU features supported by the host
+ :type: dynamic
+ :category: platform-options
+
+ (x86/x86_64 only) Probe the CPU of the machine running GHC and enable all of
+ the corresponding ``-m...`` CPU-feature options automatically (for example
+ ``-msse4.2``, ``-mavx2``, ``-mbmi2``, ``-mfma``). The detected features apply
+ to both the :ref:`native code generator <native-code-gen>` and the
+ :ref:`LLVM backend <llvm-code-gen>`.
+
+ The detected features are enabled *in addition* to any CPU-feature flags you
+ pass explicitly, regardless of their order on the command line; ``-march=native``
+ never disables a feature.
+
+ The features that were detected and enabled can be inspected with
+ :ghc-flag:`--print-enabled-cpu-features`.
+
+ .. warning::
+
+ Code compiled with ``-march=native`` may use instructions that are not
+ available on other CPUs, and is therefore not portable to a different
+ machine.
+
+ Only x86 and x86_64 targets are supported so far; the flag is rejected on
+ other targets. It is also rejected when cross-compiling, since the host CPU
+ is then unrelated to the target.
+
Haddock
-------
=====================================
rts/RtsSymbols.c
=====================================
@@ -1039,6 +1039,30 @@ extern char **environ;
#define RTS_ARM_OUTLINE_ATOMIC_SYMBOLS
#endif
+// libgcc/compiler-rt symbols behind __builtin_cpu_supports/__builtin_cpu_init,
+// referenced by compiler/cbits/cpu_features_x86.c (-march=native).
+// Without them the runtime linker cannot load the ghc library's object code
+// (e.g. for plugins in a statically linked GHCi). Keep the guards in sync
+// with cpu_features_x86.c.
+#if (defined(i386_HOST_ARCH) || defined(x86_64_HOST_ARCH)) \
+ && (defined(__GNUC__) || defined(__clang__))
+// __cpu_features2 holds the feature words beyond the first 32 (e.g. GFNI)
+// and only exists in the runtimes of GCC >= 11 resp. LLVM >= 12.
+#if (defined(__clang_major__) && __clang_major__ >= 12) \
+ || (!defined(__clang__) && defined(__GNUC__) && __GNUC__ >= 11)
+#define RTS_X86_CPU_FEATURES2_SYMBOLS \
+ SymI_NeedsDataProto(__cpu_features2)
+#else
+#define RTS_X86_CPU_FEATURES2_SYMBOLS
+#endif
+#define RTS_X86_CPU_MODEL_SYMBOLS \
+ SymI_NeedsProto(__cpu_indicator_init) \
+ SymI_NeedsDataProto(__cpu_model) \
+ RTS_X86_CPU_FEATURES2_SYMBOLS
+#else
+#define RTS_X86_CPU_MODEL_SYMBOLS
+#endif
+
// Symbols defined by libc
#define RTS_LIBC_SYMBOLS \
SymI_HasProto_redirect(atexit, atexit, STRENGTH_STRONG, SYM_TYPE_CODE) /* See Note [Strong symbols] */ \
@@ -1199,6 +1223,7 @@ RTS_OPENBSD_ONLY_SYMBOLS
RTS_LIBC_SYMBOLS
RTS_LIBGCC_SYMBOLS
RTS_ARCH_LIBGCC_SYMBOLS
+RTS_X86_CPU_MODEL_SYMBOLS
RTS_FINI_ARRAY_SYMBOLS
RTS_LIBFFI_SYMBOLS
RTS_ARM_OUTLINE_ATOMIC_SYMBOLS
@@ -1254,6 +1279,7 @@ void initLinkerRtsSyms (StrHashTable *symhash) {
RTS_OPENBSD_ONLY_SYMBOLS
RTS_LIBGCC_SYMBOLS
RTS_ARCH_LIBGCC_SYMBOLS
+ RTS_X86_CPU_MODEL_SYMBOLS
RTS_FINI_ARRAY_SYMBOLS
RTS_LIBFFI_SYMBOLS
RTS_ARM_OUTLINE_ATOMIC_SYMBOLS
=====================================
testsuite/tests/codeGen/should_gen_asm/all.T
=====================================
@@ -17,6 +17,11 @@ test('msse-option-order', [unless(arch('x86_64') or arch('i386'), skip),
when(unregisterised(), skip)], compile_grep_asm, ['hs', False, '-msse4.2 -msse2'])
test('mavx-should-enable-popcnt', [unless(arch('x86_64') or arch('i386'), skip),
when(unregisterised(), skip)], compile_grep_asm, ['hs', False, '-mavx'])
+# -march=native probes the host CPU, so gate on the host actually having SSE4.2
+# (have_cpu_feature reports nothing under cross, skipping the test there too).
+test('march-native-enables-popcnt',
+ [unless((arch('x86_64') or arch('i386')) and have_cpu_feature('sse4_2'), skip),
+ when(unregisterised(), skip)], compile_grep_asm, ['hs', False, '-march=native'])
test('avx512-int64-mul', [unless(arch('x86_64'), skip),
when(unregisterised(), skip)], compile_grep_asm, ['hs', True, '-mavx512dq -mavx512vl'])
test('avx512-int64-minmax', [unless(arch('x86_64'), skip),
=====================================
testsuite/tests/codeGen/should_gen_asm/march-native-enables-popcnt.asm
=====================================
@@ -0,0 +1 @@
+popcnt(?![0-9])
\ No newline at end of file
=====================================
testsuite/tests/codeGen/should_gen_asm/march-native-enables-popcnt.hs
=====================================
@@ -0,0 +1,11 @@
+-- `-march=native` enables the host's CPU features. On a host with SSE4.2
+-- (gated in all.T via have_cpu_feature) this makes popCount compile to a
+-- `popcnt` instruction rather than the SSE2-baseline software fallback.
+import Data.Bits
+
+{-# NOINLINE foo #-}
+foo :: Int -> Int
+foo x = 1 + popCount x
+
+main :: IO ()
+main = print (foo 42)
=====================================
testsuite/tests/driver/all.T
=====================================
@@ -7,6 +7,12 @@ def normalise_unknown_flag(msg):
m = re.search(r'unrecognised flag: \S+', msg)
return m.group(0) + '\n' if m else msg
+def normalise_march_native_error(msg):
+ # Keep only the stable '-march=native ...' diagnostic; the program-name
+ # prefix and any usage trailer vary across configurations.
+ m = re.search(r'-march=native is [^\n]+', msg)
+ return m.group(0) + '\n' if m else msg
+
test('driver011', [extra_files(['A011.hs'])], makefile_test, ['test011'])
test('driver012', [extra_files(['A012.hs'])], makefile_test, ['test012'])
@@ -265,6 +271,29 @@ test('print_enabled_cpu_features_unknown_flag',
run_command,
['{compiler} -mavx22 --print-enabled-cpu-features'])
+# -march=native enables at least the x86_64 baseline (SSE2). The full feature
+# set is host-dependent, so we only assert the always-present baseline.
+test('march_native',
+ [unless(arch('x86_64') or arch('i386'), skip)],
+ run_command,
+ ['{compiler} -march=native --print-enabled-cpu-features | grep -o SSE2'])
+
+# On non-x86 targets -march=native must be rejected.
+test('march_native_unsupported_arch',
+ [when(arch('x86_64') or arch('i386'), skip),
+ normalise_errmsg_fun(normalise_march_native_error), exit_code(1)],
+ run_command,
+ ['{compiler} -march=native --print-enabled-cpu-features'])
+
+# -march=native is additive with explicit -m flags, regardless of order: an
+# explicitly requested feature (here AVX2, forced on independent of the host) is
+# still present whether the flag comes before or after -march=native.
+test('march_native_additive',
+ [unless(arch('x86_64') or arch('i386'), skip)],
+ run_command,
+ ['{compiler} -mavx2 -march=native --print-enabled-cpu-features | grep -o AVX2 && '
+ '{compiler} -march=native -mavx2 --print-enabled-cpu-features | grep -o AVX2'])
+
test('T10219', normal, run_command,
# `-x hspp` in make mode should work.
# Note: need to specify `-x hspp` before the filename.
=====================================
testsuite/tests/driver/march_native.stdout
=====================================
@@ -0,0 +1 @@
+SSE2
=====================================
testsuite/tests/driver/march_native_additive.stdout
=====================================
@@ -0,0 +1,2 @@
+AVX2
+AVX2
=====================================
testsuite/tests/driver/march_native_unsupported_arch.stderr
=====================================
@@ -0,0 +1 @@
+-march=native is only supported on x86 and x86_64 targets
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/cfca4580536b7bc4dd9713bec527844…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/cfca4580536b7bc4dd9713bec527844…
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/rafl/hpc-instances] Add a changelog entry for new HPC features
by Florian Ragwitz (@rafl) 04 Sep '26
by Florian Ragwitz (@rafl) 04 Sep '26
04 Sep '26
Florian Ragwitz pushed to branch wip/rafl/hpc-instances at Glasgow Haskell Compiler / GHC
Commits:
989054b8 by Florian Ragwitz at 2026-09-04T12:52:35-07:00
Add a changelog entry for new HPC features
- - - - -
1 changed file:
- + changelog.d/hpc-classinsts
Changes:
=====================================
changelog.d/hpc-classinsts
=====================================
@@ -0,0 +1,23 @@
+section: compiler
+synopsis: Improve coverage reporting for class instances and their methods
+description:
+
+ Coverage reporting (:ghc-flag:`-fhpc`) now treats class instances more
+ naturally.
+
+ Compiler-generated instance methods, such as inherited default methods, no
+ longer receive separate top-level coverage boxes. Explicitly implemented
+ instance methods retain their own declaration coverage.
+
+ This means that it is no longer necessary to exercise all of a class' methods
+ in order for it to appear "covered".
+
+ Additionally, top-level coverage boxes are now created for all instances of
+ classes with methods, and ticked by the use of *any* of the class' methods.
+ This indicates that an instance was in fact used at run-time.
+
+ Note that classes without any instance methods are currently excluded from
+ this, and won't have any coverage boxes created.
+
+mrs: !16632
+issues: #17155
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/989054b8d826df2810e115378b3f0b0…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/989054b8d826df2810e115378b3f0b0…
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/rafl/hpc-instances] 13 commits: Add -Wimplicit-field-strictness (#16836)
by Florian Ragwitz (@rafl) 04 Sep '26
by Florian Ragwitz (@rafl) 04 Sep '26
04 Sep '26
Florian Ragwitz pushed to branch wip/rafl/hpc-instances at Glasgow Haskell Compiler / GHC
Commits:
9e7739ab by Simon Jakobi at 2026-09-03T13:45:43-04:00
Add -Wimplicit-field-strictness (#16836)
This opt-in warning fires when a data constructor field lacks an
explicit strictness annotation (`!` or `~`). It complements the
LazyFieldAnnotations extension (4762a8bf30f) from GHC proposal 752,
which makes `~` annotations available for this purpose.
Deciding which fields to report requires their levity, so the check
runs after typechecking. To keep the noise down, the diagnostic is
emitted once per data declaration, grouped by constructor.
Closes #16836.
Assisted-by: Claude Fable 5
- - - - -
6a44d591 by mangoiv at 2026-09-03T13:46:22-04:00
simplifier: remove a bogus `assert` in `rebuild_app'` in `GHC.CoreToStg.Prep.cpeApp`.
Prior to this commit
commit 08bc245be70d95801bc1138804ed1de9474fbdc0
Author: sheaf <sam.derbyshire(a)gmail.com>
Date: Sat Feb 28 16:30:43 2026 +0100
Clean up join points, casts & ticks
This commit shores up the logic dealing with casts and ticks occurring
in between a join point binding and a jump
any `PlaceRuntime` ticks we observed were profiling ticks, even though that
isn't necessary.
The more liberal rules in the above commit allow e.g. breakpoint ticks
(which are valid PlaceRuntime ticks) to legitimately appear in an argument
position.
Fixes #27556
- - - - -
eaa95a6f by Andrei Borzenkov at 2026-09-04T08:08:40-04:00
Parentheses in prefix GADT constructors (#27423)
Updated `splitLHsGadtTy` to allow looking
through the parentheses for inner binders. General example
of a code pattern that's allowed now:
data S a where
MkS :: (forall a. S a)
That should work now with any combination of nested
foralls and parentheses.
We don't perform parenthesis unwrapping for record
GADT constructors in accordance with GHC Proposal #402.
To this end `con_inner_bndrs` no longer stores plain forall
telescopes: `[HsForAllTelescope pass]` is replaced with
`[LHsGadtTelescope pass]`, a new `HsArg`-style type whose
`HsGadtForAll` holds an inner telescope and whose `HsGadtPar`
holds a pair of parentheses. The parentheses carry no meaning
for renaming or type checking; the only reason to record them
is exact-printing.
Updated `pprConDecl` to improve the `parse == parse . ppr . parse`
property of GADT pretty-printing.
The pretty printer can now output code that's similar to this:
data T a where
MkT1 :: (forall a. T a)
MkT2 :: forall . forall a. T a
These are special cases of inner forall binders
for prefix GADT constructors, when we have either
implicit or zero explicit outer binders.
- - - - -
639456a8 by Evgeny Malyshev at 2026-09-04T08:09:25-04:00
HsPat: Add spacing to unary tuple patterns
Use hsep when printing unary boxed TuplePat applications so that the
MkSolo constructor is separated from its argument. The old hcat rendered
wildcard and parenthesized arguments as MkSolo_ and MkSolo().
Add T27034 to exercise the HsPat splice-dump path with wildcard and
variable arguments, and update existing affected golden output.
Fixes #27034
Assisted-By: OpenAI Codex
- - - - -
866efbe1 by Florian Ragwitz at 2026-09-04T12:33:44-07:00
hpc: Test desired HPC behaviour for class instances
- - - - -
14ec4671 by Florian Ragwitz at 2026-09-04T12:33:44-07:00
hpc: Make instances available in TickTransEnv
- - - - -
3a888eb6 by Florian Ragwitz at 2026-09-04T12:33:44-07:00
hpc: Capture which method Ids belong to which dictionary function Id
- - - - -
e88eb168 by Florian Ragwitz at 2026-09-04T12:33:44-07:00
hpc: Carry method id mapping in TcGblEnv
- - - - -
57ec525b by Florian Ragwitz at 2026-09-04T12:33:44-07:00
hpc: pass instance method mapping to addTicksToBinds and propagate ABEs
- - - - -
1852aeef by Florian Ragwitz at 2026-09-04T12:33:44-07:00
hpc: Do not create top-level boxes for generated instance methods
Users care about which of their written methods are or aren't covered, not
whether all of the inherited methods are covered as well. Those should be
covered where they are implemented.
This set of changes achieves the above by stopping to generate top-level boxes
which will often appear as uncovered with no clear indication as to why, and no
way to fix other than excercising code the user has not written.
- - - - -
7bc54197 by Florian Ragwitz at 2026-09-04T12:33:45-07:00
hpc: Allocate one top-level box for each class instance for coverage
- - - - -
37739055 by Florian Ragwitz at 2026-09-04T12:33:45-07:00
hpc: Tick top-level boxes for instances when any method is used at run-time
- - - - -
da64ce47 by Florian Ragwitz at 2026-09-04T12:33:45-07:00
hpc: Don't allocate boxes for methodless classes
With the plumbing we currently have, these could never be covered, which is not
helpful to hpc users. This could be improved upon further.
- - - - -
70 changed files:
- + changelog.d/27556
- + changelog.d/allow-gadt-prefix-con-parens
- + changelog.d/implicit-field-strictness-warning
- changelog.d/lazy-field-annotations
- compiler/GHC/CoreToStg/Prep.hs
- compiler/GHC/Driver/Flags.hs
- compiler/GHC/Driver/Session.hs
- compiler/GHC/Hs/Decls.hs
- compiler/GHC/Hs/Instances.hs
- compiler/GHC/Hs/Pat.hs
- compiler/GHC/Hs/Type.hs
- compiler/GHC/HsToCore.hs
- compiler/GHC/HsToCore/Quote.hs
- compiler/GHC/HsToCore/Ticks.hs
- compiler/GHC/Iface/Ext/Ast.hs
- compiler/GHC/Rename/HsType.hs
- compiler/GHC/Rename/Module.hs
- compiler/GHC/Tc/Errors/Ppr.hs
- compiler/GHC/Tc/Errors/Types.hs
- compiler/GHC/Tc/Gen/HsType.hs
- compiler/GHC/Tc/Module.hs
- compiler/GHC/Tc/TyCl.hs
- compiler/GHC/Tc/TyCl/Instance.hs
- compiler/GHC/Tc/Types.hs
- compiler/GHC/Tc/Utils/Monad.hs
- compiler/GHC/Types/Error/Codes.hs
- compiler/GHC/Types/Hint.hs
- compiler/GHC/Types/Hint/Ppr.hs
- compiler/GHC/Types/Tickish.hs
- compiler/Language/Haskell/Syntax/Decls.hs
- compiler/Language/Haskell/Syntax/Type.hs
- docs/users_guide/exts/gadt_syntax.rst
- docs/users_guide/exts/strict.rst
- docs/users_guide/using-warnings.rst
- − testsuite/tests/gadt/T14320.stderr
- testsuite/tests/gadt/T18191.hs
- testsuite/tests/gadt/T18191.stderr
- + testsuite/tests/gadt/T27423a.hs
- + testsuite/tests/gadt/T27423b.hs
- + testsuite/tests/gadt/T27423b.stderr
- testsuite/tests/gadt/all.T
- + testsuite/tests/hpc/instmeths/instmeths.hs
- + testsuite/tests/hpc/instmeths/instmeths.stdout
- + testsuite/tests/hpc/instmeths/test.T
- testsuite/tests/printer/Makefile
- + testsuite/tests/printer/T27423c.hs
- testsuite/tests/printer/all.T
- + testsuite/tests/simplCore/should_compile/T27556.hs
- + testsuite/tests/simplCore/should_compile/T27556.script
- testsuite/tests/simplCore/should_compile/all.T
- testsuite/tests/th/T17380.stderr
- testsuite/tests/th/T18612.stderr
- + testsuite/tests/th/T27034.hs
- + testsuite/tests/th/T27034.stderr
- testsuite/tests/th/T8761.stderr
- testsuite/tests/th/all.T
- + testsuite/tests/warnings/should_compile/T16836a.hs
- + testsuite/tests/warnings/should_compile/T16836a.stderr
- + testsuite/tests/warnings/should_compile/T16836b.hs
- + testsuite/tests/warnings/should_compile/T16836c.hs
- + testsuite/tests/warnings/should_compile/T16836c.stderr
- + testsuite/tests/warnings/should_compile/T16836d.hs
- + testsuite/tests/warnings/should_compile/T16836d.stderr
- testsuite/tests/warnings/should_compile/all.T
- utils/check-exact/ExactPrint.hs
- utils/haddock/haddock-api/src/Haddock/Backends/Hoogle.hs
- utils/haddock/haddock-api/src/Haddock/Convert.hs
- utils/haddock/haddock-api/src/Haddock/GhcUtils.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/46aeba43193a1399ef9c93387abe25…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/46aeba43193a1399ef9c93387abe25…
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