[Git][ghc/ghc][wip/marge_bot_batch_merge_job] 8 commits: testsuite: Fix out-of-bounds access in T3586
Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC Commits: 4bfbf5c8 by ARATA Mizuki at 2026-09-01T18:55:08-04:00 testsuite: Fix out-of-bounds access in T3586 unsafeRead and unsafeWrite use 0-based index. Looking at #3586, the expected output seems to be 2.8e8. Addresses #27596 - - - - - 3f9db6d4 by ARATA Mizuki at 2026-09-01T18:55:08-04:00 testsuite: Fix out-of-bounds access in T21305 writeInt64Array# takes an index measured in units of Int64 elements. Fixes #27596 - - - - - 44d7788f by Simon Peyton Jones at 2026-09-01T18:55:53-04:00 Fix buglet in INLINE-arity calculation for pattern synonyms This fixes #27744. The buglet was accidentally introduced by commit 3a0f9a51c1dacc474c7fd128082edd8bf4081256 Author: Simon Peyton Jones <simon.peytonjones@gmail.com> Date: Sat Aug 1 00:13:02 2026 +0100 Fix three bugs related to required type args and INLINE pragmas I failed to find all the calls to `addInlinePragArity`! - - - - - b439404f by Simon Jakobi at 2026-09-02T03:07:53-04:00 testsuite: Migrate perf tests off collect_compiler_stats('all') The 'all' metric argument applies a single tolerance to bytes allocated, max_bytes_used and peak_megabytes_allocated, although their noise profiles are incompatible (#27653): allocations are nearly deterministic, residency needs 10-20%, and peak is quantized to 1 MB. Any single tolerance is too tight for one metric or too slack for another. This migrates the remaining users of 'all' (and of the 'all' default) to explicit per-metric collection, ahead of removing 'all' from the driver. peak_megabytes_allocated is dropped everywhere: its 1 MB granularity makes tight relative windows meaningless (#27613), and it is sensitive to GC timing. In #27489 it drifted by -5.3% while max_bytes_used moved by less than 0.1%. Where a test guards a memory property, max_bytes_used covers it at byte granularity. Where the motivating ticket was about compile-time memory (T11545, T15304, T26425), residency remains gated via max_bytes_used, now with a residency-appropriate tolerance. max_bytes_used is dropped where residency was only ever an accident of 'all': * T15630, T15630a, T20261: the underlying tickets (#15630, #20261) contain no memory data at all. One is a simplifier-ticks blowup and the other is stated entirely in allocation numbers, so the 20% window never had teeth. * T21839c: #21839's measurements show residency essentially flat (+0.16%) while allocations moved +7%, so allocations are the discriminating metric. They are already gated at 1% via collect_compiler_runtime. The ghc/max gate had previously broken CI spuriously (9fd11585eb widened it from 1% to 10% for that reason). Allocation tolerances are tightened to the testsuite's conventional 2% where 'all' previously left them at 10-20%. Assisted-by: Claude Fable 5 - - - - - 9a2f5d93 by Simon Jakobi at 2026-09-02T03:07:53-04:00 testsuite: Remove the 'all' metric argument of collect_stats 'all' gated bytes allocated, max_bytes_used and peak_megabytes_allocated at a single tolerance, although their noise profiles are incompatible, making such tests either flaky or toothless (#27653). Closes #27653. Assisted-by: Claude Fable 5 - - - - - 073a165e by Simon Jakobi at 2026-09-02T03:07:53-04:00 testsuite: Make the deviation argument of collect_stats mandatory Almost every caller passes an explicit tolerance matched to the metric's noise profile, and the silent 20% default is far slacker than 'bytes allocated' merits. Only two tests relied on it. They now state their tolerance explicitly: large-project gets 10%, in line with other large compile-time tests. T9848 gets 2%: its metric is byte-for-byte deterministic across CI jobs and platforms of a given test_env, has drifted only about 2.5% since 2015, and the fusion failure it guards against would show up as a roughly +30000% jump. Assisted-by: Claude Fable 5 - - - - - 71c7a928 by Cheng Shao at 2026-09-02T03:07:53-04:00 autoconf/ghc-toolchain: bump llvm upper bound to support llvm 23 This commit bumps llvm upper bound to support llvm 23. - - - - - 0b38115b by Cheng Shao at 2026-09-02T03:07:53-04:00 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. - - - - - 19 changed files: - + changelog.d/T27744 - + changelog.d/llvm-23 - compiler/GHC/Tc/TyCl/PatSyn.hs - configure.ac - libraries/base/tests/all.T - rts/ProfHeap.c - rts/eventlog/EventLog.c - rts/prim/atomic.c - testsuite/driver/README.md - testsuite/driver/testlib.py - testsuite/tests/ffi/should_run/T21305.hs - + testsuite/tests/patsyn/should_compile/T27744.hs - testsuite/tests/patsyn/should_compile/all.T - testsuite/tests/perf/compiler/all.T - testsuite/tests/perf/compiler/large-project/all.T - testsuite/tests/perf/should_run/T3586.hs - testsuite/tests/perf/should_run/T3586.stdout - testsuite/tests/perf/space_leaks/all.T - utils/ghc-toolchain/src/GHC/Toolchain/Program.hs Changes: ===================================== changelog.d/T27744 ===================================== @@ -0,0 +1,4 @@ +section: compiler +synopsis: Fixes a bug in arity computation for INLINE pattern synonyms, which led to a compiler crash +issues: #27744 +mrs: !16602 ===================================== changelog.d/llvm-23 ===================================== @@ -0,0 +1,4 @@ +section: llvm-backend +synopsis: Bump LlvmMaxVersion to support LLVM 23.x releases. +issues: #27764 +mrs: !16617 ===================================== compiler/GHC/Tc/TyCl/PatSyn.hs ===================================== @@ -875,8 +875,9 @@ tcPatSynMatcher (L loc ps_name) lpat prag_fn mg = MG{ mg_alts = L (l2l $ getLoc match) [match] , mg_ext = MatchGroupTc [] res_ty gen } - matcher_arity = length req_theta + 3 - -- See Note [Pragmas for pattern synonyms] + + matcher_arity :: VisArity -- VisArity excludes dictionary arguments! + matcher_arity = 3 -- See Note [Pragmas for pattern synonyms] -- Add INLINE pragmas; see Note [Pragmas for pattern synonyms] -- NB: prag_fn is keyed by the PatSyn Name, not the (internal) matcher name @@ -967,9 +968,8 @@ tcPatSynBuilderBind prag_fn (PSB { psb_id = ps_lname@(L loc ps_name) let builder_id = mkExportedVanillaId builder_name builder_ty -- See Note [Exported LocalIds] in GHC.Types.Id - (_, req_theta, _, prov_theta, arg_tys, _) = patSynSigBndr patsyn - builder_arity = length req_theta + length prov_theta - + length arg_tys + builder_arity :: VisArity -- VisArity excludes dictionary arguments! + builder_arity = length (patSynArgs patsyn) + (if need_dummy_arg then 1 else 0) -- Add INLINE pragmas; see Note [Pragmas for pattern synonyms] @@ -1348,13 +1348,20 @@ entire pattern synonym is supported. For example: When no pragma is provided for a pattern, the inlining decision might change between different versions of GHC. -Implementation notes. The prag_fn passed in to tcPatSynDecl will have a binding -for the /pattern synonym/ Name, thus - InlinedPattern :-> INLINE -From this we cook up an INLINE pragma for the matcher (in tcPatSynMatcher) -and builder (in tcPatSynBuilderBind), by looking up the /pattern synonym/ -Name in the prag_fn, and then using addInlinePragArity to add the right -inl_sat field to that INLINE pragma for the matcher or builder respectively. +Implementation notes. + +* The prag_fn passed in to tcPatSynDecl will have a binding + for the /pattern synonym/ Name, thus + InlinedPattern :-> INLINE + From this we cook up an INLINE pragma for the matcher (in tcPatSynMatcher) + and builder (in tcPatSynBuilderBind), by looking up the /pattern synonym/ + Name in the prag_fn, and then using `addInlinePragArity` to add the right + inl_sat field to that INLINE pragma for the matcher or builder respectively. + +* Note that the arity passed to `addInlinePragArity` is the `VisArity`, the /visible/ + arity. That specifically /excludes/ dictionary arguments, which are dealt with + later by `GHC.HsToCore.Binds.makeCorePair`. The builder and matcher have no Required + type args, so we don't need to worry about them in the `VisArity`. -} ===================================== configure.ac ===================================== @@ -557,7 +557,7 @@ AC_SUBST(InstallNameToolCmd) # versions of LLVM simultaneously, but that stopped working around # 3.5/3.6 release of LLVM. LlvmMinVersion=13 # inclusive -LlvmMaxVersion=23 # not inclusive +LlvmMaxVersion=24 # not inclusive AC_SUBST([LlvmMinVersion]) AC_SUBST([LlvmMaxVersion]) ===================================== libraries/base/tests/all.T ===================================== @@ -243,7 +243,7 @@ test('T8684', expect_broken(8684), compile_and_run, ['']) test('hWaitForInput-accurate-stdin', [js_broken(22349), expect_broken_for(16535, threaded_ways), req_process], compile_and_run, ['']) test('T9826',normal, compile_and_run,['']) test('T9848', - [ collect_stats('bytes allocated') + [ collect_stats('bytes allocated', 2) , only_ways(['normal']) , js_broken(22261) ], ===================================== rts/ProfHeap.c ===================================== @@ -141,7 +141,10 @@ restore_locale( void ) * store only up to (max_era - 1) as its creation or last use time. * -------------------------------------------------------------------------- */ unsigned int era; + +#if defined(PROFILING) static uint32_t max_era; +#endif StgWord user_era; @@ -518,8 +521,10 @@ initHeapProfiling(void) n_censuses = 1; } +#if defined(PROFILING) // max_era = 2^LDV_SHIFT max_era = 1 << LDV_SHIFT; +#endif censuses = stgMallocBytes(sizeof(Census) * n_censuses, "initHeapProfiling"); ===================================== rts/eventlog/EventLog.c ===================================== @@ -113,8 +113,6 @@ static eventlog_init_func_t *eventlog_header_funcs = NULL; // See Note [Maximum event length] #define EVENT_LOG_SIZE 2 * (1024 * 1024) // 2MB -static int flushCount = 0; - // Struct for record keeping of buffer to store event types and events. // // Invariant: The event buffer will always begin with a block-start marker. @@ -1514,7 +1512,6 @@ void printAndClearEventBuf (EventsBuf *ebuf) } resetEventsBuf(ebuf); - flushCount++; postBlockMarker(ebuf); } ===================================== rts/prim/atomic.c ===================================== @@ -96,8 +96,6 @@ StgWord64 hs_atomic_nand64(StgWord x, StgWord64 val) return __atomic_fetch_nand((volatile StgWord64 *) x, val, __ATOMIC_SEQ_CST); } -#pragma GCC diagnostic pop - // FetchOrByteArrayOp_Int StgWord hs_atomic_or8(StgWord x, StgWord val) ===================================== testsuite/driver/README.md ===================================== @@ -35,11 +35,13 @@ differences, and things such as that are not necessary to be considered by the test writer anymore. This is due to the fact that the test comparison relies entirely on locally collected metrics on the testing machine. -As such, it is perfectly sufficient to write `collect_stats('all',20)` in the -".T" files to measure the 3 potential stats that can be collected for that test -and automatically test them for regressions, failing if there is more than a 20% -change in any direction. In fact, even that is not necessary as -`collect_stats()` defaults to 'all', and 20% deviation allowed. +A test states which metric to measure and how much deviation to allow, e.g. +`collect_stats('bytes allocated', 2)` in the ".T" files. Such a test fails if +the metric changes by more than 2% in either direction. To gate several metrics, +use one `collect_stats` call per metric, so that each gets a tolerance matched +to its noise profile: allocations are nearly deterministic and support tight +windows, while the residency metrics need considerably slacker ones (see +Note [Measuring residency] in testlib.py). The function `collect_compiler_stats()` is completely equivalent in every way to `collect_stats` except that it measures the performance of the compiler itself ===================================== testsuite/driver/testlib.py ===================================== @@ -24,7 +24,7 @@ import subprocess from testglobals import config, ghc_env, default_testopts, brokens, t, \ TestRun, TestResult, TestOptions, PerfMetric from testutil import strip_quotes, lndir, link_or_copy_file, passed, \ - failBecause, testing_metrics, residency_testing_metrics, \ + failBecause, residency_testing_metrics, \ stable_perf_counters, \ PassFail, badResult, str_warn, str_removeprefix from term_color import Color, colored_if @@ -816,28 +816,20 @@ def _collect_generic_stat(name : TestName, opts, metric_infos): # ----- -# Defaults to "test everything, and only break on extreme cases" -# -# The inputs to this function are slightly interesting: -# metric can be either: -# - 'all', in which case all 3 possible metrics are collected and compared. -# - The specific metric one wants to use in the test. -# - A set of the metrics one wants to use in the test. -# -# Deviation defaults to 20% because the goal is correctness over performance. -# The testsuite should avoid breaking when there is not an actual error. -# Instead, the testsuite should notify of regressions in a non-breaking manner. +# metric is either a single metric name or a set of metric names. Use one +# call per metric so each gets a tolerance matched to its noise profile. +# See Note [Measuring residency] for the residency metrics. # # collect_compiler_stats is used when the metrics collected are about the compiler. # collect_stats is used in the majority case when the metrics to be collected # are about the performance of the runtime code generated by the compiler. -def collect_compiler_stats(metric='all',deviation=20): +def collect_compiler_stats(metric, deviation): def f(name, opts, m=metric, d=deviation): no_lint(name, opts) return _collect_stats(name, opts, m, d, None, True) return f -def collect_stats(metric='all', deviation=20, static_stats_file=None): +def collect_stats(metric, deviation, static_stats_file=None): return lambda name, opts, m=metric, d=deviation, s=static_stats_file: _collect_stats(name, opts, m, d, s) def statsFile(comp_test: bool, name: str) -> str: @@ -864,12 +856,9 @@ def _collect_stats(name: TestName, opts, metrics, deviation: Optional[int], # This is a bit weird, though. return - # Normalize metrics to a list of strings. + # Normalize metrics to a set of strings. if isinstance(metrics, str): - if metrics == 'all': - metrics = testing_metrics() - else: - metrics = { metrics } + metrics = { metrics } opts.is_stats_test = True if is_compiler_stats_test: ===================================== testsuite/tests/ffi/should_run/T21305.hs ===================================== @@ -45,4 +45,4 @@ writeByteArray# :: MutableByteArray# RealWorld writeByteArray# _ _ [] s = s writeByteArray# mba off (I64# i:is) s = case writeInt64Array# mba off i s of - s' -> writeByteArray# mba (off +# 8#) is s' + s' -> writeByteArray# mba (off +# 1#) is s' ===================================== testsuite/tests/patsyn/should_compile/T27744.hs ===================================== @@ -0,0 +1,46 @@ +{-# LANGUAGE CPP #-} + +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE PolyKinds #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE RoleAnnotations #-} +{-# LANGUAGE StandaloneKindSignatures #-} +{-# LANGUAGE ViewPatterns #-} + +-- | A facility for faking GADTs that work sufficiently similarly +-- to unary natural numbers. +module T27744 + ( Nattish (Zeroy, Succy) + ) + where +import Unsafe.Coerce (unsafeCoerce) +import Data.Kind (Type) + +type Nattish :: forall k. k -> (k -> k) -> k -> Type +newtype Nattish zero succ n = Nattish Word +type role Nattish nominal nominal nominal + +data Res zero succ n where + ResZero :: Res zero succ zero + ResSucc :: !(Nattish zero succ n) -> Res zero succ (succ n) + +check :: Nattish zero succ n -> Res zero succ n +check (Nattish 0) = unsafeCoerce ResZero +check (Nattish n) = unsafeCoerce $ ResSucc (Nattish (n - 1)) + +pattern Zeroy :: forall {k} zero succ (n :: k). () => n ~ zero => Nattish zero succ n +pattern Zeroy <- (check -> ResZero) + where + Zeroy = Nattish 0 +{-# INLINE Zeroy #-} + +pattern Succy :: forall {k} zero succ (n :: k). () => forall (n' :: k). n ~ succ n' => Nattish zero succ n' -> Nattish zero succ n +pattern Succy n <- (check -> ResSucc n) + where + Succy (Nattish n) = Nattish (n + 1) +{-# INLINE Succy #-} + +{-# COMPLETE Zeroy, Succy #-} ===================================== testsuite/tests/patsyn/should_compile/all.T ===================================== @@ -94,3 +94,4 @@ test('T26331a', normal, compile, ['']) test('T27440a', normal, compile, ['']) test('T27440b', normal, compile, ['']) test('T27440c', normal, compile, ['']) +test('T27744', normal, compile, ['']) ===================================== testsuite/tests/perf/compiler/all.T ===================================== @@ -638,13 +638,15 @@ test ('T15164', ], compile, ['-v0 -O']) +# T15630/T15630a guard against exponential simplifier blowup when inlining +# join points (#15630). test('T15630', - [collect_compiler_stats() + [collect_compiler_stats('bytes allocated', 2) ], compile, ['-O2']) test('T15630a', - [collect_compiler_stats() + [collect_compiler_stats('bytes allocated', 2) ], compile, ['-O2']) @@ -768,12 +770,17 @@ test ('T9198', compile, ['']) +# Guards against quadratic demand-analysis cost on wide recursive data +# types, which manifested as a compile-time memory blowup (#11545). test('T11545', - [ collect_compiler_stats('all', 15) ], + [ collect_compiler_stats('bytes allocated', 2) + , collect_compiler_residency(15) ], compile, ['-O']) +# Guards against the compile-time memory blowup of #15304 test('T15304', - [ collect_compiler_stats('all', 10) ], + [ collect_compiler_stats('bytes allocated', 2) + , collect_compiler_residency(10) ], compile, ['-O']) test ('T20049', [ collect_compiler_stats('bytes allocated',2) ], @@ -795,8 +802,10 @@ test('T16875', # Testing one hole-fit with a lot in scope for #16875 collect_compiler_runtime(4), compile, ['-fdefer-type-errors -fno-max-valid-hole-fits -package ghc']) +# Guards against renamer/typechecker allocation regressions on very large +# generated modules (#20261, a Happy-generated parser). test ('T20261', - [collect_compiler_stats('all')], + [collect_compiler_stats('bytes allocated', 2)], compile, ['']) @@ -805,8 +814,7 @@ test ('T20261', # does not sensibly handle one test acting as both # a compile-time and a run-time performance test test('T21839c', - [ collect_compiler_stats('all', 10), - collect_compiler_runtime(1), + [ collect_compiler_runtime(1), only_ways(['normal'])], compile, ['-O']) @@ -872,8 +880,10 @@ test('interpreter_steplocal', ghci_script, ['interpreter_steplocal.script']) +# Guards against the compile-time memory blowup of #26425 test ('T26425', - [ collect_compiler_stats('all',20) ], + [ collect_compiler_stats('bytes allocated', 2) + , collect_compiler_residency(20) ], compile, ['-O']) ===================================== testsuite/tests/perf/compiler/large-project/all.T ===================================== @@ -7,7 +7,7 @@ def large_project_makedepend(num): return test( f'large-project-makedepend-{num}', [ - collect_compiler_stats('bytes allocated'), + collect_compiler_stats('bytes allocated', 10), pre_cmd(f'./large-project.sh {num}'), extra_files(['large-project.sh']), ignore_stderr, ===================================== testsuite/tests/perf/should_run/T3586.hs ===================================== @@ -14,7 +14,8 @@ go :: STUArray s Int Double -> Int -> Double -> ST s Double go !a i !acc | i < 1 = return acc | otherwise = do - b <- unsafeRead a i - unsafeWrite a i (b+3.0) - c <- unsafeRead a i + -- unsafeRead/unsafeWrite use 0-based index + b <- unsafeRead a (i-1) + unsafeWrite a (i-1) (b+3.0) + c <- unsafeRead a (i-1) go a (i-1) (c+acc) ===================================== testsuite/tests/perf/should_run/T3586.stdout ===================================== @@ -1 +1 @@ -2.79999863e8 +2.8e8 ===================================== testsuite/tests/perf/space_leaks/all.T ===================================== @@ -1,9 +1,6 @@ setTestOpts(js_skip) test('space_leak_001', - # This could potentially be replaced with - # collect_stats('all',5) to test all 3 with - # 5% possible deviation. [ collect_stats('bytes allocated',5), collect_runtime_residency(15), omit_ways(['profasm','profthreaded','threaded1','threaded2', ===================================== utils/ghc-toolchain/src/GHC/Toolchain/Program.hs ===================================== @@ -167,7 +167,7 @@ minLlvmVersion = 13 -- Max llvm version (exclusive) maxLlvmVersionExcl :: Int -maxLlvmVersionExcl = 23 +maxLlvmVersionExcl = 24 -- Max llvm version (inclusive) maxLlvmVersion :: Int View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/37eb9acc81811abe605fafef6dda69b... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/37eb9acc81811abe605fafef6dda69b... 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
participants (1)
-
Marge Bot (@marge-bot)