[Git][ghc/ghc][wip/tick-prefer-anf] Core: Preserve ANF when ticking a tick-headed expression (#27415)
Ben Gamari pushed to branch wip/tick-prefer-anf at Glasgow Haskell Compiler / GHC Commits: 5db84ad6 by Ben Gamari at 2026-06-21T10:16:00-04:00 Core: Preserve ANF when ticking a tick-headed expression (#27415) `mkTickCpe` promises not to push a tick onto the arguments of an application, so that CorePrep's ANF invariant is maintained: an argument must be trivial and, in particular, tick-free (see the `arg` non-terminal in Note [CorePrep invariants] and Note [mkTick breaks ANF]). `getStgArgFromTrivialArg` relies on this. However, its handling of a tick-headed expression delegated to `tickTickedExpr`, which, when commuting the new tick past the existing tick stack and pushing it inwards, called `mkTick` (i.e. `mk_tick False`) directly -- silently dropping ANF preservation and re-enabling PSCC3. With `-prof -fprof-auto -finfo-table-map`, a definition such as foo = Just id has its body wrapped in an SCC by `-fprof-auto`. The SCC is commuted past the source notes heading the `Just` application and, with `preserve_anf` lost, pushed onto the (function-valued) argument as `(scc<foo> breakpoint) @a`. That argument is no longer trivial, so `getStgArgFromTrivialArg` panicked: getStgArgFromTrivialArg (scc<foo> id) @a_aiNd Fixes #27415. - - - - - 4 changed files: - compiler/GHC/Core/Utils.hs - compiler/GHC/CoreToStg/Prep.hs - + testsuite/tests/profiling/should_compile/T27415.hs - testsuite/tests/profiling/should_compile/all.T Changes: ===================================== compiler/GHC/Core/Utils.hs ===================================== @@ -343,7 +343,7 @@ mk_tick preserve_anf t orig_expr = mkTick' orig_expr mkTick' expr -- Deal with ticking a expression headed by one or more ticks. | Just (ts, e) <- tickedExpr_maybe expr - = tickTickedExpr t ts e + = tickTickedExpr preserve_anf t ts e mkTick' expr = case expr of Lam x e @@ -413,11 +413,13 @@ mk_tick preserve_anf t orig_expr = mkTick' orig_expr -- | Apply a tick to an expression headed by ticks. tickTickedExpr - :: CoreTickish -- ^ tick to add + :: Bool -- ^ preserve ANF? (See 'mkTickCpe' and + -- Note [mkTick breaks ANF] in GHC.CoreToStg.Prep) + -> CoreTickish -- ^ tick to add -> NE.NonEmpty CoreTickish -- ^ existing stack of ticks -> CoreExpr -- ^ inner core expression -> CoreExpr -tickTickedExpr t1 t2s e +tickTickedExpr preserve_anf t1 t2s e -- Case 1: common up 't1' with a tick in the stack. -- @@ -431,8 +433,24 @@ tickTickedExpr t1 t2s e -- Case 2: 't1' can be commuted past all the ticks in the stack, e.g. because -- it has tighter placement properties than all the ticks in the stack. -- Push it inwards to expose cancellation opportunities. + -- + -- We must thread 'preserve_anf' through here. Otherwise we may turn the + -- argument + -- + -- scc<f> (src<l> (Just x)) + -- + -- into the ill-formed argument + -- + -- src<l> (Just (scc<f> x)) -- SCC on an argument: not a CpeArg! + -- + -- whereas with preserve_anf we keep the SCC outside the application, + -- respecting ANF: + -- + -- src<l> (scc<f> (Just breakpoint)) + -- + -- See Note [mkTick breaks ANF] in GHC.CoreToStg.Prep and #27415. | all (tickishCommutable t1) t2s - = apply_ticks t2s $ mkTick t1 e + = apply_ticks t2s $ mk_tick preserve_anf t1 e -- Fallback: keep the new tick on the outside. | otherwise @@ -455,7 +473,7 @@ tickTickedExpr t1 t2s e {- Note [Pushing SCCs inwards] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Amongst all ticks, SCCs have the laxest placement properties (PlaceCostCentre, +Amongst all ticks, SCCs have the most lax placement properties (PlaceCostCentre, as described in Note [Tickish placement] GHC.Types.Tickish): (PSCC1) SCCs around non-function variables can be eliminated. ===================================== compiler/GHC/CoreToStg/Prep.hs ===================================== @@ -918,10 +918,14 @@ mkTick will push the SCC into the constructor application, resulting in: \ (eta :: Char -> Bool) -> BindP (p :: Int) (scc<oneM> eta) +i.e. the SCC ends up on the argument `eta`, which is no longer a valid `arg`. + To avoid this problem (at least until 'mkTick' is more thoroughly reworked to avoid this infelicity, see #27141), we define a variant of 'mkTick', called 'mkTickCpe', which does not push ticks into constructor applications (this is -the only optimisation done by 'mkTick' that can break ANF). +the only optimisation done by 'mkTick' that can break ANF). This is the concrete +meaning of the 'preserve_anf' flag threaded through 'GHC.Core.Utils.mk_tick': +when set, 'mk_tick' will not push a tick onto an application's arguments (PSCC3). We prefer using a small variant of 'mkTick' rather than using the 'Tick' constructor, as the latter can slightly degrade profiling reports by failing to ===================================== testsuite/tests/profiling/should_compile/T27415.hs ===================================== @@ -0,0 +1,10 @@ +{-# LANGUAGE ExistentialQuantification #-} + +module T27415 where + +import Control.Concurrent.MVar + +data ResultVar b = forall a . ResultVar (a -> b) (MVar (Maybe a)) + +mkResultVar :: MVar (Maybe a) -> ResultVar a +mkResultVar = ResultVar id ===================================== testsuite/tests/profiling/should_compile/all.T ===================================== @@ -23,3 +23,4 @@ test('T20938', [test_opts], compile, ['-O -prof']) test('T26056', [test_opts], compile, ['-O -prof']) test('T27121', [test_opts, extra_files(['T27121_aux.hs'])], multimod_compile, ['T27121', '-v0 -O -prof -fprof-auto']) test('T27182', [test_opts], compile, ['-O -prof -fprof-late']) +test('T27415', [test_opts], compile, ['-O -prof -fprof-auto -finfo-table-map']) View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/5db84ad62896fbf5f8e2f2d70dd9e469... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/5db84ad62896fbf5f8e2f2d70dd9e469... You're receiving this email because of your account on gitlab.haskell.org.
participants (1)
-
Ben Gamari (@bgamari)