Magnus pushed to branch wip/mangoiv/ghc-9.12-bp at Glasgow Haskell Compiler / GHC
Commits:
-
9ba34598
by Brian J. Cardiff at 2026-05-20T15:17:39+02:00
-
4a9baf14
by sheaf at 2026-05-20T15:28:16+02:00
4 changed files:
- compiler/GHC/Core/Utils.hs
- m4/fptools_happy.m4
- + testsuite/tests/simplCore/should_compile/T26642.hs
- testsuite/tests/simplCore/should_compile/all.T
Changes:
| ... | ... | @@ -709,11 +709,16 @@ mergeCaseAlts outer_bndr (Alt DEFAULT _ deflt_rhs : outer_alts) |
| 709 | 709 | | otherwise
|
| 710 | 710 | = Nothing
|
| 711 | 711 | |
| 712 | - -- We don't want ticks to get in the way; just push them inwards.
|
|
| 713 | - -- (This happens when you add SourceTicks e.g. GHC.Num.Integer.integerLt#)
|
|
| 712 | + -- Push ticks **inwards** (when possible).
|
|
| 713 | + -- See (MC5) in Note [Merge Nested Cases].
|
|
| 714 | 714 | go (Tick t body)
|
| 715 | - = do { (joins, alts) <- go body
|
|
| 716 | - ; return (joins, [Alt con bs (Tick t rhs) | Alt con bs rhs <- alts]) }
|
|
| 715 | + = do { (joins, alts) <- go body -- (MC4): any join points inside are floated out of the tick.
|
|
| 716 | + |
|
| 717 | + -- Abort if this would put a non-soft-scope tick in between
|
|
| 718 | + -- a join point binding and its jumps. See (MC6).
|
|
| 719 | + ; guard $ null joins || t `tickishScopesLike` SoftScope
|
|
| 720 | + ; return (joins, [Alt con bs (mkTick t rhs) | Alt con bs rhs <- alts])
|
|
| 721 | + }
|
|
| 717 | 722 | |
| 718 | 723 | go _ = Nothing
|
| 719 | 724 | |
| ... | ... | @@ -924,9 +929,70 @@ Wrinkles |
| 924 | 929 | |
| 925 | 930 | So `mergeCaseAlts` floats out any join points. It doesn't float out
|
| 926 | 931 | non-join-points unless the /outer/ case has just one alternative; doing
|
| 927 | - so would risk more allocation
|
|
| 932 | + so would risk more allocation.
|
|
| 933 | + |
|
| 934 | + Note also that `mergeCaseAlts` floats join points out of ticks, for which
|
|
| 935 | + we need to be extra careful; see (MC6).
|
|
| 936 | + |
|
| 937 | +(MC5) We want to move ticks out of the way if possible, to prevent them from
|
|
| 938 | + inhibiting optimisation. For example, say we have:
|
|
| 939 | + |
|
| 940 | + case expensive of r {
|
|
| 941 | + C1 -> rhs1; -- happy path
|
|
| 942 | + _ -> scctick<doEdgeCase> (case r of { C2 -> rhs2; C3 -> rhs3 })
|
|
| 943 | + }
|
|
| 944 | + |
|
| 945 | + In this situation, we push the "doEdgeCase" tick **inwards** and proceed
|
|
| 946 | + to merge cases, like so:
|
|
| 947 | + |
|
| 948 | + case expensive of
|
|
| 949 | + C1 -> rhs1
|
|
| 950 | + C2 -> scctick<doEdgeCase> rhs2
|
|
| 951 | + C3 -> scctick<doEdgeCase> rhs3
|
|
| 952 | + |
|
| 953 | + This preserves the tick semantics, because this transformation:
|
|
| 954 | + |
|
| 955 | + 1. preserves counts,
|
|
| 956 | + 2. does not move cost in or out of the tick scope.
|
|
| 957 | + |
|
| 958 | + (1) is clear: we will tick 'doEdgeCase' exactly in the C2/C3 alternatives,
|
|
| 959 | + and we won't otherwise.
|
|
| 960 | + For (2), recall that case is strict in Core. We already evaluated 'expensive',
|
|
| 961 | + so re-scrutinising 'r' is free.
|
|
| 962 | + |
|
| 963 | + This means that, perhaps surprisingly, this transformation is valid for
|
|
| 964 | + **all** ticks, including non-floatable ones.
|
|
| 965 | + |
|
| 966 | + In contrast, we would not want to move the tick outwards, because this:
|
|
| 967 | + |
|
| 968 | + - will lead to additional counting of 'doEdgeCase' in the 'C1' (happy path) case,
|
|
| 969 | + - risks attributing the cost of evaluating 'expensive' to 'doEdgeCase'.
|
|
| 970 | + |
|
| 971 | +(MC6) There is a dangerous interaction between (MC4) and (MC5), which can lead
|
|
| 972 | + to invalid Core (as reported in #26642, #26929). Suppose we have:
|
|
| 973 | + |
|
| 974 | + case f x of r ->
|
|
| 975 | + scctick<foo>
|
|
| 976 | + join j y = rhs in
|
|
| 977 | + case r of { C1 -> j 1; C2 -> bar }
|
|
| 978 | + |
|
| 979 | + If we naively carried out (MC4) and (MC5) together, this would result in:
|
|
| 980 | + |
|
| 981 | + join j y = rhs in
|
|
| 982 | + case f x of
|
|
| 983 | + C1 -> scctick<foo> (j 1)
|
|
| 984 | + C2 -> scctick<foo> bar
|
|
| 985 | + |
|
| 986 | + This has moved the tick in between the join point binding 'j' and the
|
|
| 987 | + join point jump, which is invalid. The simplifier cannot deal with such
|
|
| 988 | + Core, resulting in #26642.
|
|
| 989 | + |
|
| 990 | + The solution: abort whenever we would position a non-soft-scope tick
|
|
| 991 | + inside a join point in this manner.
|
|
| 992 | + An alternative would be to float the tick outwards, but as we saw in (MC5)
|
|
| 993 | + this risks a grave misattribution of profiling costs, so we don't do that.
|
|
| 928 | 994 | |
| 929 | -(MC5) See Note [Cascading case merge]
|
|
| 995 | +(MC7) See Note [Cascading case merge]
|
|
| 930 | 996 | |
| 931 | 997 | See also Note [Example of case-merging and caseRules] in GHC.Core.Opt.Simplify.Utils
|
| 932 | 998 |
| ... | ... | @@ -24,13 +24,18 @@ changequote([, ])dnl |
| 24 | 24 | ])
|
| 25 | 25 | if test ! -f compiler/GHC/Parser.hs || test ! -f compiler/GHC/Cmm/Parser.hs
|
| 26 | 26 | then
|
| 27 | - failure_msg="Happy version == 1.20.* || >= 2.0.2 && < 2.2 is required to compile GHC"
|
|
| 27 | + if test x"$fptools_cv_happy_version" != x; then
|
|
| 28 | + fptools_cv_happy_version_display="version $fptools_cv_happy_version";
|
|
| 29 | + else
|
|
| 30 | + fptools_cv_happy_version_display="none";
|
|
| 31 | + fi;
|
|
| 32 | + failure_msg="Happy version == 1.20.* || >= 2.0.2 && < 2.3 is required to compile GHC. (Found: $fptools_cv_happy_version_display)"
|
|
| 28 | 33 | FP_COMPARE_VERSIONS([$fptools_cv_happy_version],[-lt],[1.20.0],
|
| 29 | 34 | [AC_MSG_ERROR([$failure_msg])])[]
|
| 30 | 35 | FP_COMPARE_VERSIONS([$fptools_cv_happy_version],[-ge],[1.21.0],
|
| 31 | 36 | FP_COMPARE_VERSIONS([$fptools_cv_happy_version], [-le], [2.0.1],
|
| 32 | 37 | [AC_MSG_ERROR([$failure_msg])])[])[]
|
| 33 | - FP_COMPARE_VERSIONS([$fptools_cv_happy_version],[-ge],[2.2.0],
|
|
| 38 | + FP_COMPARE_VERSIONS([$fptools_cv_happy_version],[-ge],[2.3.0],
|
|
| 34 | 39 | [AC_MSG_ERROR([$failure_msg])])[]
|
| 35 | 40 | |
| 36 | 41 | fi
|
| 1 | +module T26642 ( saveClobberedTemps ) where
|
|
| 2 | + |
|
| 3 | +import Prelude ( IO, Bool(..), Int, (>>=), (==), return )
|
|
| 4 | +import Data.Word ( Word64 )
|
|
| 5 | + |
|
| 6 | +-------------------------------------------------------------------------------
|
|
| 7 | + |
|
| 8 | +data Word64Map a
|
|
| 9 | + = Bin (Word64Map a) (Word64Map a)
|
|
| 10 | + | Tip a
|
|
| 11 | + | Nil
|
|
| 12 | + |
|
| 13 | +{-# NOINLINE myFoldr #-}
|
|
| 14 | +myFoldr :: (a -> b -> b) -> b -> Word64Map a -> b
|
|
| 15 | +myFoldr f = go
|
|
| 16 | + where
|
|
| 17 | + {-# NOINLINE go #-}
|
|
| 18 | + go z' Nil = z'
|
|
| 19 | + go z' (Tip x) = f x z'
|
|
| 20 | + go z' (Bin l r) = go (go z' r) l
|
|
| 21 | + |
|
| 22 | +{-# NOINLINE nonDetFold #-}
|
|
| 23 | +nonDetFold :: (b -> elt -> IO b) -> b -> Word64Map elt -> IO b
|
|
| 24 | +nonDetFold f z0 xs = myFoldr c return xs z0
|
|
| 25 | + where
|
|
| 26 | + {-# NOINLINE c #-}
|
|
| 27 | + c x k z = f z x >>= k
|
|
| 28 | + |
|
| 29 | +{-# NOINLINE myFalse #-}
|
|
| 30 | +myFalse :: Bool
|
|
| 31 | +myFalse = False
|
|
| 32 | + |
|
| 33 | +type RealReg = Int
|
|
| 34 | +data Loc = InReg RealReg | InMem
|
|
| 35 | + |
|
| 36 | +saveClobberedTemps :: forall instr. [RealReg] -> IO [instr]
|
|
| 37 | +saveClobberedTemps clobbered = nonDetFold maybe_spill [] Nil
|
|
| 38 | + where
|
|
| 39 | + {-# NOINLINE maybe_spill #-}
|
|
| 40 | + maybe_spill :: [instr] -> Loc -> IO [instr]
|
|
| 41 | + maybe_spill instrs !loc =
|
|
| 42 | + case loc of
|
|
| 43 | + InReg reg
|
|
| 44 | + | myFalse
|
|
| 45 | + -> return []
|
|
| 46 | + _ -> return instrs |
| ... | ... | @@ -470,6 +470,8 @@ test('T22272', normal, multimod_compile, ['T22272', '-O -fexpose-all-unfoldings |
| 470 | 470 | # go should become a join point
|
| 471 | 471 | test('T22428', [grep_errmsg(r'jump go') ], compile, ['-O -ddump-simpl -dsuppress-uniques -dno-typeable-binds -dsuppress-unfoldings'])
|
| 472 | 472 | |
| 473 | +test('T26642', [unless(have_profiling(), skip)], compile, ['-O -prof -fprof-auto-calls'])
|
|
| 474 | + |
|
| 473 | 475 | test('T22459', normal, compile, [''])
|
| 474 | 476 | test('T22623', normal, multimod_compile, ['T22623', '-O -v0'])
|
| 475 | 477 | test('T22662', normal, compile, [''])
|