Simon Peyton Jones pushed to branch wip/T27737 at Glasgow Haskell Compiler / GHC
Commits:
-
4ae4a61b
by Simon Peyton Jones at 2026-10-28T10:28:12+00:00
-
1db13ad7
by Simon Peyton Jones at 2026-10-28T10:28:49+00:00
4 changed files:
- compiler/GHC/Core/Opt/Simplify/Env.hs
- compiler/GHC/Core/Opt/Simplify/Iteration.hs
- compiler/GHC/Core/Opt/Simplify/Utils.hs
- compiler/GHC/Core/SimpleOpt.hs
Changes:
| ... | ... | @@ -43,8 +43,7 @@ module GHC.Core.Opt.Simplify.Env ( |
| 43 | 43 | extendFloats, isEmptyJoinFloats,
|
| 44 | 44 | doFloatFromRhs, getTopFloatBinds,
|
| 45 | 45 | |
| 46 | - -- * LetFloats
|
|
| 47 | - LetFloats, FloatEnable(..), emptyLetFloats, mapLetFloats,
|
|
| 46 | + FloatEnable(..), mapLetFloats,
|
|
| 48 | 47 | |
| 49 | 48 | -- * JoinFloats
|
| 50 | 49 | JoinFloat, JoinFloats, emptyJoinFloats,
|
| ... | ... | @@ -452,8 +451,8 @@ isEmptyFloats :: SimplFloats -> Bool |
| 452 | 451 | -- Precondition: used only when sfJoins is empty
|
| 453 | 452 | isEmptyFloats (SimplFloats { sfBinds = fs
|
| 454 | 453 | , sfJoins = js })
|
| 455 | - = assertPpr (isNilOL js) (ppr js ) $
|
|
| 456 | - isNilOL fs
|
|
| 454 | + = assertPpr (isEmptyJoinFloats js) (ppr js ) $
|
|
| 455 | + isEmptyFloatBinds fs
|
|
| 457 | 456 | |
| 458 | 457 | pprSimplEnv :: SimplEnv -> SDoc
|
| 459 | 458 | -- Used for debugging; selective
|
| ... | ... | @@ -738,7 +737,7 @@ setSubstEnv env tvs cvs ids = env { seTvSubst = tvs, seCvSubst = cvs, seIdSubst |
| 738 | 737 | {-
|
| 739 | 738 | ************************************************************************
|
| 740 | 739 | * *
|
| 741 | -\subsection{LetFloats}
|
|
| 740 | +\subsection{Floats}
|
|
| 742 | 741 | * *
|
| 743 | 742 | ************************************************************************
|
| 744 | 743 | |
| ... | ... | @@ -891,15 +890,13 @@ let-bindings that can, and wrap the rest back around the RHS. We tried this |
| 891 | 890 | difference to compile time, so we did not keep it.
|
| 892 | 891 | -}
|
| 893 | 892 | |
| 894 | -type LetFloats = OrdList OutBind
|
|
| 895 | - -- Let-bindings only: no case-floats or tick-floats
|
|
| 896 | - -- Invariant: every binding satisfies the let-can-float invariant
|
|
| 897 | - |
|
| 898 | 893 | type JoinFloat = OutBind
|
| 899 | 894 | type JoinFloats = OrdList JoinFloat
|
| 900 | 895 | |
| 901 | 896 | doFloatFromRhs :: SimplEnv -> TopLevelFlag -> RecFlag
|
| 902 | - -> SimplFloats -> LetFloats
|
|
| 897 | + -> SimplFloats
|
|
| 898 | + -> OrdList OutBind -- Let-bindings only (from prepareRhs); they
|
|
| 899 | + -- satisfy the let-can-float invariant
|
|
| 903 | 900 | -> OutExpr
|
| 904 | 901 | -> Maybe SimplFloats -- Contains let-bindings only
|
| 905 | 902 | -- See Note [doFloatFromRhs]
|
| ... | ... | @@ -908,10 +905,11 @@ doFloatFromRhs :: SimplEnv -> TopLevelFlag -> RecFlag |
| 908 | 905 | --
|
| 909 | 906 | -- If you change this function look also at FloatIn.noFloatIntoRhs
|
| 910 | 907 | doFloatFromRhs env lvl rec floats anf_lets rhs
|
| 911 | - | not float_enabled = Nothing
|
|
| 912 | - | not want_to_float = Nothing
|
|
| 913 | - | not can_float = Nothing
|
|
| 914 | - | otherwise = Just new_floats
|
|
| 908 | + | isNilOL fs, isNilOL anf_lets = Nothing -- Short cut only
|
|
| 909 | + | not float_enabled = Nothing
|
|
| 910 | + | not want_to_float = Nothing
|
|
| 911 | + | not can_float = Nothing
|
|
| 912 | + | otherwise = Just new_floats
|
|
| 915 | 913 | where
|
| 916 | 914 | !(SimplFloats { sfBinds = fs, sfJoins = jfs, sfInScope = in_scope }) = floats
|
| 917 | 915 | new_floats = assertPpr (isNilOL jfs) (ppr jfs) $
|
| ... | ... | @@ -952,9 +950,6 @@ doFloatFromRhs env lvl rec floats anf_lets rhs |
| 952 | 950 | FloatNestedOnly -> not (isTopLevel lvl)
|
| 953 | 951 | FloatEnabled -> True
|
| 954 | 952 | |
| 955 | -emptyLetFloats :: LetFloats
|
|
| 956 | -emptyLetFloats = nilOL
|
|
| 957 | - |
|
| 958 | 953 | emptyJoinFloats :: JoinFloats
|
| 959 | 954 | emptyJoinFloats = nilOL
|
| 960 | 955 | |
| ... | ... | @@ -1023,11 +1018,11 @@ extendFloats floats@(SimplFloats { sfJoins = jfloats, sfInScope = in_scope }) bi |
| 1023 | 1018 | | otherwise
|
| 1024 | 1019 | = floats `addLetFloat` bind
|
| 1025 | 1020 | |
| 1026 | -addLetFloats :: SimplFloats -> LetFloats -> SimplFloats
|
|
| 1021 | +addLetFloats :: SimplFloats -> OrdList OutBind -> SimplFloats
|
|
| 1027 | 1022 | -- Add the let-bindings /inside/ the existing floats,
|
| 1028 | 1023 | -- and extend the in-scope set
|
| 1029 | --- No need for addLetFloat's needsCaseBinding test: LetFloats satisfy the
|
|
| 1030 | --- let-can-float invariant (see the declaration of LetFloats)
|
|
| 1024 | +-- Precondition: the bindings satisfy the let-can-float invariant,
|
|
| 1025 | +-- so there is no need for addLetFloat's needsCaseBinding test
|
|
| 1031 | 1026 | addLetFloats floats binds = foldlOL add floats binds
|
| 1032 | 1027 | where
|
| 1033 | 1028 | add fs bind = assertPpr (okLetFloat bind) (ppr bind) $
|
| ... | ... | @@ -1091,7 +1086,7 @@ addJoinFlts = appOL |
| 1091 | 1086 | |
| 1092 | 1087 | mkRecFloats :: SimplFloats -> SimplFloats
|
| 1093 | 1088 | -- Flattens the floats into a single Rec group,
|
| 1094 | --- They must either all be lifted LetFloats or all JoinFloats
|
|
| 1089 | +-- They must either all be lifted let-floats or all JoinFloats
|
|
| 1095 | 1090 | mkRecFloats floats@(SimplFloats { sfBinds = bs
|
| 1096 | 1091 | , sfJoins = jbs
|
| 1097 | 1092 | , sfInScope = in_scope })
|
| ... | ... | @@ -1125,7 +1120,7 @@ getTopFloatBinds floats@(SimplFloats { sfJoins = jbs }) |
| 1125 | 1120 | letFloatBinds floats
|
| 1126 | 1121 | |
| 1127 | 1122 | {-# INLINE mapLetFloats #-}
|
| 1128 | -mapLetFloats :: LetFloats -> ((Id,CoreExpr) -> (Id,CoreExpr)) -> LetFloats
|
|
| 1123 | +mapLetFloats :: OrdList OutBind -> ((Id,CoreExpr) -> (Id,CoreExpr)) -> OrdList OutBind
|
|
| 1129 | 1124 | mapLetFloats binds fun
|
| 1130 | 1125 | = binds1
|
| 1131 | 1126 | where
|
| ... | ... | @@ -64,7 +64,7 @@ import GHC.Builtin.WiredIn.Ids( seqId ) |
| 64 | 64 | import qualified GHC.Data.List.Infinite as Inf
|
| 65 | 65 | import GHC.Data.Maybe ( isNothing, orElse, mapMaybe )
|
| 66 | 66 | import GHC.Data.FastString
|
| 67 | -import GHC.Data.OrdList ( unitOL, isNilOL, appOL, snocOL )
|
|
| 67 | +import GHC.Data.OrdList ( OrdList, nilOL, unitOL, isNilOL, appOL, snocOL )
|
|
| 68 | 68 | import GHC.Unit.Module ( moduleName )
|
| 69 | 69 | import GHC.Utils.Outputable
|
| 70 | 70 | import GHC.Utils.Panic
|
| ... | ... | @@ -764,7 +764,7 @@ prepareRhs :: HasDebugCallStack |
| 764 | 764 | => SimplEnvIS -> TopLevelFlag
|
| 765 | 765 | -> FastString -- Base for any new variables
|
| 766 | 766 | -> OutExpr
|
| 767 | - -> SimplM (LetFloats, OutExpr)
|
|
| 767 | + -> SimplM (OrdList OutBind, OutExpr)
|
|
| 768 | 768 | -- Transforms a RHS into a better RHS by ANF'ing args
|
| 769 | 769 | -- for expandable RHSs: constructors and PAPs
|
| 770 | 770 | -- e.g x = Just e
|
| ... | ... | @@ -773,7 +773,7 @@ prepareRhs :: HasDebugCallStack |
| 773 | 773 | -- See Note [prepareRhs]
|
| 774 | 774 | prepareRhs env top_lvl occ rhs0
|
| 775 | 775 | | is_expandable = anfise rhs0
|
| 776 | - | otherwise = return (emptyLetFloats, rhs0)
|
|
| 776 | + | otherwise = return (nilOL, rhs0)
|
|
| 777 | 777 | where
|
| 778 | 778 | -- We can't use exprIsExpandable because the WHOLE POINT is that
|
| 779 | 779 | -- we want to treat (K <big>) as expandable, because we are just
|
| ... | ... | @@ -789,7 +789,7 @@ prepareRhs env top_lvl occ rhs0 |
| 789 | 789 | go (Tick _ rhs) n_val_args = go rhs n_val_args
|
| 790 | 790 | go _ _ = False
|
| 791 | 791 | |
| 792 | - anfise :: OutExpr -> SimplM (LetFloats, OutExpr)
|
|
| 792 | + anfise :: OutExpr -> SimplM (OrdList OutBind, OutExpr)
|
|
| 793 | 793 | anfise (Cast rhs co)
|
| 794 | 794 | = do { (floats, rhs') <- anfise rhs
|
| 795 | 795 | ; return (floats, Cast rhs' co) }
|
| ... | ... | @@ -801,7 +801,7 @@ prepareRhs env top_lvl occ rhs0 |
| 801 | 801 | ; (floats2, arg') <- makeTrivial env top_lvl topDmd occ arg
|
| 802 | 802 | ; return (floats1 `appOL` floats2, App fun' arg') }
|
| 803 | 803 | anfise (Var fun)
|
| 804 | - = return (emptyLetFloats, Var fun)
|
|
| 804 | + = return (nilOL, Var fun)
|
|
| 805 | 805 | |
| 806 | 806 | anfise (Tick t rhs)
|
| 807 | 807 | -- We want to be able to float bindings past this tick.
|
| ... | ... | @@ -819,28 +819,28 @@ prepareRhs env top_lvl occ rhs0 |
| 819 | 819 | floats' = mapLetFloats floats tickIt
|
| 820 | 820 | ; return (floats', Tick t rhs') }
|
| 821 | 821 | |
| 822 | - anfise other = return (emptyLetFloats, other)
|
|
| 822 | + anfise other = return (nilOL, other)
|
|
| 823 | 823 | |
| 824 | 824 | makeTrivialArg :: HasDebugCallStack => SimplEnvIS -> ArgSpec
|
| 825 | - -> SimplM (LetFloats, ArgSpec)
|
|
| 825 | + -> SimplM (OrdList OutBind, ArgSpec)
|
|
| 826 | 826 | makeTrivialArg env arg@(ValArg { as_arg = e, as_dmd = dmd })
|
| 827 | 827 | = do { (floats, e') <- makeTrivial env NotTopLevel dmd (fsLit "arg") e
|
| 828 | 828 | ; return (floats, arg { as_arg = e' }) }
|
| 829 | 829 | makeTrivialArg _ arg@(TyArg {})
|
| 830 | - = return (emptyLetFloats, arg)
|
|
| 830 | + = return (nilOL, arg)
|
|
| 831 | 831 | |
| 832 | 832 | makeTrivial :: HasDebugCallStack
|
| 833 | 833 | => SimplEnvIS -> TopLevelFlag -> Demand
|
| 834 | 834 | -> FastString -- ^ A "friendly name" to build the new binder from
|
| 835 | 835 | -> OutExpr
|
| 836 | - -> SimplM (LetFloats, OutExpr)
|
|
| 836 | + -> SimplM (OrdList OutBind, OutExpr)
|
|
| 837 | 837 | -- Binds the expression to a variable, if it's not trivial, returning the variable
|
| 838 | 838 | -- For the Demand argument, see Note [Keeping demand info in StrictArg Plan A]
|
| 839 | 839 | makeTrivial env top_lvl dmd occ_fs expr
|
| 840 | 840 | | exprIsTrivial expr -- Already trivial
|
| 841 | 841 | || not (bindingOk top_lvl expr expr_ty) -- Cannot trivialise
|
| 842 | 842 | -- See Note [Cannot trivialise]
|
| 843 | - = return (emptyLetFloats, expr)
|
|
| 843 | + = return (nilOL, expr)
|
|
| 844 | 844 | |
| 845 | 845 | | Cast expr' co <- expr
|
| 846 | 846 | = do { (floats, triv_expr) <- makeTrivial env top_lvl dmd occ_fs expr'
|
| ... | ... | @@ -889,7 +889,7 @@ into |
| 889 | 889 | and thence, by floating, into
|
| 890 | 890 | z = f y
|
| 891 | 891 | x = Just z
|
| 892 | -so that x is bound to a value. It returns only let-bindings (LetFloats), so
|
|
| 892 | +so that x is bound to a value. It returns only let-bindings, so
|
|
| 893 | 893 | sometimes it cannot trivialise its argument, and must return it unchanged.
|
| 894 | 894 | |
| 895 | 895 | (CT1) Nested bindings. Suppose that (f y) had type Int#, with a different data
|
| ... | ... | @@ -2015,7 +2015,13 @@ simpl_lam env bndr body cont |
| 2015 | 2015 | ; (env', bndrs') <- simplLamBndrs env (bndr:inner_bndrs)
|
| 2016 | 2016 | ; body' <- simplExpr env' inner_body
|
| 2017 | 2017 | ; new_lam <- rebuildLam env' bndrs' body' cont
|
| 2018 | - ; rebuild env' new_lam cont }
|
|
| 2018 | + ; rebuild env new_lam cont }
|
|
| 2019 | + -- Subtle point: rebuild with env, not env': the lambda binders are not in
|
|
| 2020 | + -- scope outside the lambda, and should not end up in the in-scope set of the
|
|
| 2021 | + -- floats we return. Why not? Because a too-large in-scope set can lead to
|
|
| 2022 | + -- gratuitous renaming, and a non-empty substitution makes `substTy` do real
|
|
| 2023 | + -- work. This happens, for example, if the function has a stable unfolding
|
|
| 2024 | + -- that uses the same binders, and cost 9% in compile time in T14683.
|
|
| 2019 | 2025 | |
| 2020 | 2026 | -------------
|
| 2021 | 2027 | simplLamBndr :: SimplEnv -> InBndr -> SimplM (SimplEnv, OutBndr)
|
| ... | ... | @@ -74,6 +74,7 @@ import GHC.Types.InlinePragma |
| 74 | 74 | import GHC.Types.Tickish
|
| 75 | 75 | import GHC.Types.Demand
|
| 76 | 76 | import GHC.Types.Var.Set
|
| 77 | +import GHC.Types.Var.Env ( extendInScopeSetList )
|
|
| 77 | 78 | import GHC.Types.Basic
|
| 78 | 79 | import GHC.Types.Name.Env
|
| 79 | 80 | |
| ... | ... | @@ -2518,7 +2519,8 @@ new binding is abstracted. Several points worth noting |
| 2518 | 2519 | manifestly a literal string.
|
| 2519 | 2520 | -}
|
| 2520 | 2521 | |
| 2521 | -abstractFloats :: SimplEnv -> TopLevelFlag -> [OutTyVar]
|
|
| 2522 | +abstractFloats :: SimplEnv -- Env outside the whole binding
|
|
| 2523 | + -> TopLevelFlag -> [OutTyVar]
|
|
| 2522 | 2524 | -> SimplFloats -- Let-bindings only: postcondition of `prepareBinding`
|
| 2523 | 2525 | -> OutExpr -- The right-hand side of the binding
|
| 2524 | 2526 | -> SimplM (SimplFloats, OutExpr)
|
| ... | ... | @@ -2535,16 +2537,19 @@ abstractFloats env top_lvl main_tvs floats rhs |
| 2535 | 2537 | |
| 2536 | 2538 | ; return ( SimplFloats { sfBinds = foldr (consOL . FloatLet) nilOL poly_binds
|
| 2537 | 2539 | , sfJoins = emptyJoinFloats
|
| 2538 | - , sfInScope = substInScopeSet subst }
|
|
| 2540 | + , sfInScope = foldl' extendInScopeSetBind in_scope poly_binds }
|
|
| 2539 | 2541 | , GHC.Core.Subst.substExpr subst rhs) }
|
| 2540 | - -- NB: the returned in-scope set includes `env` and all the
|
|
| 2542 | + -- NB: the returned in-scope set is the outer `in_scope` and all the
|
|
| 2541 | 2543 | -- new floated bindings; but not main_tvs, nor the original floats
|
| 2542 | - -- The caller will used the returned in-scope set to cover the body of the let,
|
|
| 2543 | - -- and main_tvs and original floats are not in scope there
|
|
| 2544 | + -- The caller will use the returned in-scope set to cover the body of the let,
|
|
| 2545 | + -- and `main_tvs` and original floats are not in scope there
|
|
| 2544 | 2546 | -- That reduces unnecessary renaming (see !7777 and test T16577)
|
| 2545 | 2547 | where
|
| 2546 | 2548 | !(SimplFloats { sfBinds = float_binds, sfJoins = jfs }) = floats
|
| 2547 | - empty_subst = GHC.Core.Subst.mkEmptySubst (seInScope env)
|
|
| 2549 | + in_scope = seInScope env
|
|
| 2550 | + empty_subst = GHC.Core.Subst.mkEmptySubst (in_scope `extendInScopeSetList` main_tvs)
|
|
| 2551 | + -- main_tvs: the RHSs of the floats mention the main_tvs, so they must be
|
|
| 2552 | + -- in scope for substExpr (the original float binders are in the substitution)
|
|
| 2548 | 2553 | is_top_lvl = isTopLevel top_lvl
|
| 2549 | 2554 | uf_opts = seUnfoldingOpts env
|
| 2550 | 2555 |
| ... | ... | @@ -1712,8 +1712,7 @@ exprIsConApp_maybe ise@(ISE in_scope id_unf) expr |
| 1712 | 1712 | subst_co (Right s) co = GHC.Core.Subst.substCo s co
|
| 1713 | 1713 | |
| 1714 | 1714 | subst_expr (Left {}) e = e
|
| 1715 | - subst_expr (Right s) e = -- pprTrace "subst_expr" (text "subst" <+> ppr s $$ text "expr" <+> ppr e) $
|
|
| 1716 | - substExpr s e
|
|
| 1715 | + subst_expr (Right s) e = substExpr s e
|
|
| 1717 | 1716 | |
| 1718 | 1717 | subst_bndrs subst bs = mapAccumL subst_bndr subst bs
|
| 1719 | 1718 | |
| ... | ... | @@ -1723,8 +1722,7 @@ exprIsConApp_maybe ise@(ISE in_scope id_unf) expr |
| 1723 | 1722 | subst_bndr (Right subst) bndr = subst_bndr1 subst bndr
|
| 1724 | 1723 | |
| 1725 | 1724 | subst_bndr1 subst bndr
|
| 1726 | - = -- pprTrace "subst_bndr1" (ppr bndr <+> ppr bndr' $$ ppr subst) $
|
|
| 1727 | - (Right subst', bndr')
|
|
| 1725 | + = (Right subst', bndr')
|
|
| 1728 | 1726 | where
|
| 1729 | 1727 | (subst', bndr') = substBndr subst bndr
|
| 1730 | 1728 |