Simon Peyton Jones pushed to branch wip/case-bndr-unf at Glasgow Haskell Compiler / GHC
Commits:
-
24d1ab3b
by Simon Peyton Jones at 2026-10-27T17:27:22+00:00
10 changed files:
- + changelog.d/T27962
- + changelog.d/T27963
- compiler/GHC/Core/Opt/Simplify/Env.hs
- compiler/GHC/Core/Opt/Simplify/Iteration.hs
- compiler/GHC/Core/Opt/Simplify/Utils.hs
- testsuite/tests/simplCore/should_compile/T15445.stderr
- + testsuite/tests/simplCore/should_compile/T27962.hs
- + testsuite/tests/simplCore/should_compile/T27963.hs
- + testsuite/tests/simplCore/should_compile/T27963.stderr
- testsuite/tests/simplCore/should_compile/all.T
Changes:
| 1 | +section: compiler
|
|
| 2 | +issues: #27962
|
|
| 3 | +mrs: !16869
|
|
| 4 | +synopsis:
|
|
| 5 | + Fix a Lint error when using `runRW#` and `coerce` together
|
|
| 6 | +description:
|
|
| 7 | + Just an internal compiler bug, of long standing. |
| 1 | +section: compiler
|
|
| 2 | +issues: #27963
|
|
| 3 | +mrs: !16869
|
|
| 4 | +synopsis:
|
|
| 5 | + Fix a rather obscure interaction of `coerce` and RULES
|
|
| 6 | +description:
|
|
| 7 | + A `coerce` could prevent a RULE firing. |
| ... | ... | @@ -28,7 +28,7 @@ module GHC.Core.Opt.Simplify.Env ( |
| 28 | 28 | SimplEnvIS, checkSimplEnvIS, pprBadSimplEnvIS,
|
| 29 | 29 | |
| 30 | 30 | -- * Substitution results
|
| 31 | - SimplSR(..), substId, lookupRecBndr,
|
|
| 31 | + SimplClo(..), simplCloExpr, substId, lookupRecBndr,
|
|
| 32 | 32 | |
| 33 | 33 | -- * Simplifying 'Id' binders
|
| 34 | 34 | simplNonRecBndr, simplNonRecJoinBndr, simplRecBndrs, simplRecJoinBndrs,
|
| ... | ... | @@ -61,7 +61,7 @@ import GHC.Core.Opt.Simplify.Monad |
| 61 | 61 | import GHC.Core.Rules.Config ( RuleOpts(..) )
|
| 62 | 62 | import GHC.Core
|
| 63 | 63 | import GHC.Core.Utils
|
| 64 | -import GHC.Core.Subst( substExprSC )
|
|
| 64 | +import GHC.Core.Subst( substExpr )
|
|
| 65 | 65 | import GHC.Core.Unfold
|
| 66 | 66 | import GHC.Core.TyCo.Subst (emptyIdSubstEnv, mkSubst)
|
| 67 | 67 | import GHC.Core.Multiplicity( Scaled(..), mkMultMul )
|
| ... | ... | @@ -470,18 +470,26 @@ pprSimplEnv env |
| 470 | 470 | ppr_one v | isId v = ppr v <+> ppr (idUnfolding v)
|
| 471 | 471 | | otherwise = ppr v
|
| 472 | 472 | |
| 473 | -type SimplIdSubst = IdEnv SimplSR -- IdId |--> OutExpr
|
|
| 473 | +type SimplIdSubst = IdEnv SimplClo -- IdId |--> OutExpr
|
|
| 474 | 474 | -- See Note [Extending the IdSubstEnv] in GHC.Core.Subst
|
| 475 | 475 | |
| 476 | --- | A substitution result.
|
|
| 477 | -data SimplSR
|
|
| 476 | +-- | A "closure" used in the Simplifier. It is used
|
|
| 477 | +-- * as the range of the substitution, 'SimplIdSubst'
|
|
| 478 | +-- * as the argument in an 'ApplyToVal' continuation
|
|
| 479 | +-- Roughly: either an (InExpr, StaticEnv) pair for an
|
|
| 480 | +-- as-yet-unsimplified expression
|
|
| 481 | +-- or an OutExpr, for an already-simplified one
|
|
| 482 | +-- It's a bit like thunk: we may "evaluate" the SimplClo in a ApplyToVal
|
|
| 483 | +-- and thereby transform it from `ContEx` to `DoneEx`
|
|
| 484 | +-- This happens in the `ApplyToVal` case of `mkDupableContWithDmds`
|
|
| 485 | +-- See Note [The argument of ApplyToVal] in GHC.Core.Opt.Simplify.Utils
|
|
| 486 | +data SimplClo
|
|
| 478 | 487 | = DoneEx OutExpr JoinPointHood
|
| 479 | 488 | -- If x :-> DoneEx e ja is in the SimplIdSubst
|
| 480 | 489 | -- then replace occurrences of x by e
|
| 481 | 490 | -- and ja = Just a <=> x is a join-point of arity a
|
| 482 | 491 | -- See Note [Join arity in SimplIdSubst]
|
| 483 | 492 | |
| 484 | - |
|
| 485 | 493 | | DoneId OutId
|
| 486 | 494 | -- If x :-> DoneId v is in the SimplIdSubst
|
| 487 | 495 | -- then replace occurrences of x by v
|
| ... | ... | @@ -490,11 +498,11 @@ data SimplSR |
| 490 | 498 | |
| 491 | 499 | | ContEx SimplEnv
|
| 492 | 500 | InExpr
|
| 493 | - MOutCoercion -- See Note [The sc_cast field of ApplyToVal]
|
|
| 501 | + MOutCoercion -- See Note [The MOutCoercion field of ContEx]
|
|
| 494 | 502 | -- If x :-> ContEx static_env e mco is in the SimplISubst
|
| 495 | 503 | -- then replace occurrences of x by (subst static_env e) |> mco
|
| 496 | 504 | |
| 497 | -instance Outputable SimplSR where
|
|
| 505 | +instance Outputable SimplClo where
|
|
| 498 | 506 | ppr (DoneId v) = text "DoneId" <+> ppr v
|
| 499 | 507 | ppr (DoneEx e mj) = text "DoneEx" <> pp_mj <+> ppr e
|
| 500 | 508 | where
|
| ... | ... | @@ -637,7 +645,7 @@ reSimplifying :: SimplEnv -> Bool |
| 637 | 645 | reSimplifying (SimplEnv { seInlineDepth = n }) = n>0
|
| 638 | 646 | |
| 639 | 647 | ---------------------
|
| 640 | -extendIdSubst :: SimplEnv -> Id -> SimplSR -> SimplEnv
|
|
| 648 | +extendIdSubst :: SimplEnv -> Id -> SimplClo -> SimplEnv
|
|
| 641 | 649 | extendIdSubst env@(SimplEnv {seIdSubst = subst}) var res
|
| 642 | 650 | = assertPpr (isId var && not (isCoVar var)) (ppr var) $
|
| 643 | 651 | env { seIdSubst = extendVarEnv subst var res }
|
| ... | ... | @@ -1032,7 +1040,7 @@ So we want to look up the inner X.g_34 in the substitution, where we'll |
| 1032 | 1040 | find that it has been substituted by b. (Or conceivably cloned.)
|
| 1033 | 1041 | -}
|
| 1034 | 1042 | |
| 1035 | -substId :: SimplEnv -> InId -> SimplSR
|
|
| 1043 | +substId :: SimplEnv -> InId -> SimplClo
|
|
| 1036 | 1044 | -- Returns DoneEx only on a non-Var expression
|
| 1037 | 1045 | substId (SimplEnv { seInScope = in_scope, seIdSubst = ids }) v
|
| 1038 | 1046 | = case lookupVarEnv ids v of -- Note [Global Ids in the substitution]
|
| ... | ... | @@ -1364,15 +1372,18 @@ getTCvSubst (SimplEnv { seInScope = in_scope, seTvSubst = tv_env, seCvSubst = cv |
| 1364 | 1372 | |
| 1365 | 1373 | getFullSubst :: InScopeSet -> SimplEnv -> Subst
|
| 1366 | 1374 | getFullSubst in_scope (SimplEnv { seIdSubst = id_env, seTvSubst = tv_env, seCvSubst = cv_env })
|
| 1367 | - = mkSubst in_scope (mapVarEnv to_expr id_env) tv_env cv_env
|
|
| 1375 | + = mkSubst in_scope (mapVarEnv (simplCloExpr in_scope) id_env) tv_env cv_env
|
|
| 1376 | + |
|
| 1377 | +simplCloExpr :: InScopeSet -> SimplClo -> OutExpr
|
|
| 1378 | +-- Turn a SimplClo into an OutExpr, by applying the substitution
|
|
| 1379 | +-- (if any) and the pending cast (if any)
|
|
| 1380 | +simplCloExpr _ (DoneEx e _) = e
|
|
| 1381 | +simplCloExpr _ (DoneId v) = Var v
|
|
| 1382 | +simplCloExpr in_scope (ContEx env e mco) = mkCastMCo e' mco
|
|
| 1368 | 1383 | where
|
| 1369 | - to_expr :: SimplSR -> CoreExpr
|
|
| 1370 | - -- A tiresome impedence-matcher
|
|
| 1371 | - to_expr (DoneEx e _) = e
|
|
| 1372 | - to_expr (DoneId v) = Var v
|
|
| 1373 | - to_expr (ContEx env e mco) = mkCastMCo e' mco
|
|
| 1374 | - where
|
|
| 1375 | - e' = GHC.Core.Subst.substExprSC (getFullSubst in_scope env) e
|
|
| 1384 | + e' = GHC.Core.Subst.substExpr (getFullSubst in_scope env) e
|
|
| 1385 | + -- NB: we use substExpr, not substExprSC: we want to get the benefit of
|
|
| 1386 | + -- knowing what is evaluated etc, via the in-scope set
|
|
| 1376 | 1387 | |
| 1377 | 1388 | substTy :: HasDebugCallStack => SimplEnv -> Type -> Type
|
| 1378 | 1389 | substTy env ty = Type.substTy (getTCvSubst env) ty
|
| ... | ... | @@ -285,8 +285,8 @@ simplRecOrTopPair :: SimplEnv |
| 285 | 285 | -> SimplM (SimplFloats, SimplEnv)
|
| 286 | 286 | |
| 287 | 287 | simplRecOrTopPair env bind_cxt old_bndr new_bndr rhs
|
| 288 | - | Just env' <- preInlineUnconditionally env (bindContextLevel bind_cxt)
|
|
| 289 | - old_bndr (UnSimplified env) rhs MRefl
|
|
| 288 | + | Just env' <- preInlineLetUnconditionally env (bindContextLevel bind_cxt)
|
|
| 289 | + old_bndr rhs env
|
|
| 290 | 290 | = {-#SCC "simplRecOrTopPair-pre-inline-uncond" #-}
|
| 291 | 291 | simplTrace "SimplBindr:inline-uncond1" (ppr old_bndr) $
|
| 292 | 292 | do { tick (PreInlineUnconditionally old_bndr)
|
| ... | ... | @@ -1226,17 +1226,8 @@ simplExprF1 env (Tick t expr) cont = {-#SCC "simplTick" #-} simplTick env t exp |
| 1226 | 1226 | simplExprF1 env (Coercion co) cont = {-#SCC "simplCoercionF" #-} simplCoercionF env co cont
|
| 1227 | 1227 | |
| 1228 | 1228 | simplExprF1 env (Cast body co) cont
|
| 1229 | - = do { co1 <- {-#SCC "simplCast-simplCoercion" #-}
|
|
| 1230 | - simplCoercion env co
|
|
| 1231 | - |
|
| 1232 | - ; let cont1 = {-#SCC "simplCast-addCoerce" #-}
|
|
| 1233 | - if isReflCo co1
|
|
| 1234 | - then cont -- See Note [Optimising reflexivity]
|
|
| 1235 | - else pushCast env co1 cont
|
|
| 1236 | - -- True <=> co1 is optimised
|
|
| 1237 | - |
|
| 1238 | - ; {-#SCC "simplCast-simplExprF" #-}
|
|
| 1239 | - simplExprF env body cont1 }
|
|
| 1229 | + = do { co1 <- simplCoercion env co
|
|
| 1230 | + ; simplExprF env body (pushCast env co1 cont) }
|
|
| 1240 | 1231 | |
| 1241 | 1232 | simplExprF1 env (App fun arg) cont
|
| 1242 | 1233 | = {-#SCC "simplExprF1-App" #-} case arg of
|
| ... | ... | @@ -1264,9 +1255,9 @@ simplExprF1 env (App fun arg) cont |
| 1264 | 1255 | -- observed the quadratic behavior, so this extra entanglement
|
| 1265 | 1256 | -- seems not worthwhile.
|
| 1266 | 1257 | simplExprF env fun $
|
| 1267 | - ApplyToVal { sc_arg = arg, sc_env = UnSimplified env
|
|
| 1258 | + ApplyToVal { sc_arg = ContEx env arg MRefl, sc_dup = NoDup
|
|
| 1268 | 1259 | , sc_hole_ty = substTy env (exprType fun)
|
| 1269 | - , sc_cast = MRefl, sc_cont = cont }
|
|
| 1260 | + , sc_cont = cont }
|
|
| 1270 | 1261 | |
| 1271 | 1262 | simplExprF1 env expr@(Lam {}) cont
|
| 1272 | 1263 | = {-#SCC "simplExprF1-Lam" #-}
|
| ... | ... | @@ -1301,7 +1292,7 @@ simplExprF1 env (Let (NonRec bndr rhs) body) cont |
| 1301 | 1292 | do { ty' <- simplType env ty
|
| 1302 | 1293 | ; simplExprF (extendTvSubst env bndr ty') body cont }
|
| 1303 | 1294 | |
| 1304 | - | Just env' <- preInlineUnconditionally env NotTopLevel bndr (UnSimplified env) rhs MRefl
|
|
| 1295 | + | Just env' <- preInlineLetUnconditionally env NotTopLevel bndr rhs env
|
|
| 1305 | 1296 | -- Because of the let-can-float invariant, it's ok to
|
| 1306 | 1297 | -- inline freely, or to drop the binding if it is dead.
|
| 1307 | 1298 | = do { simplTrace "SimplBindr:inline-uncond2" (ppr bndr) $
|
| ... | ... | @@ -1637,9 +1628,8 @@ rebuild_go env expr cont |
| 1637 | 1628 | ApplyToTy { sc_arg_ty = ty, sc_cont = cont}
|
| 1638 | 1629 | -> rebuild_go env (App expr (Type ty)) cont
|
| 1639 | 1630 | |
| 1640 | - ApplyToVal { sc_arg = arg, sc_env = arg_se, sc_cast = arg_mco
|
|
| 1641 | - , sc_cont = cont, sc_hole_ty = fun_ty }
|
|
| 1642 | - -> do { arg' <- simplArg env Nothing fun_ty arg_se arg arg_mco
|
|
| 1631 | + ApplyToVal { sc_arg = arg, sc_cont = cont, sc_hole_ty = fun_ty }
|
|
| 1632 | + -> do { arg' <- simplArg env Nothing fun_ty arg
|
|
| 1643 | 1633 | ; rebuild_go env (App expr arg') cont }
|
| 1644 | 1634 | |
| 1645 | 1635 | completeBindX :: SimplEnv
|
| ... | ... | @@ -1687,69 +1677,12 @@ completeBindX env from_what bndr rhs body cont |
| 1687 | 1677 | ; let all_floats = rhs_floats `addFloats` bind_float `addFloats` body_floats
|
| 1688 | 1678 | ; return ( all_floats, body' ) }
|
| 1689 | 1679 | |
| 1690 | -{-
|
|
| 1691 | -************************************************************************
|
|
| 1680 | + |
|
| 1681 | +{- *********************************************************************
|
|
| 1692 | 1682 | * *
|
| 1693 | -\subsection{Lambdas}
|
|
| 1683 | + Arguments
|
|
| 1694 | 1684 | * *
|
| 1695 | -************************************************************************
|
|
| 1696 | --}
|
|
| 1697 | - |
|
| 1698 | -{- Note [Optimising reflexivity]
|
|
| 1699 | -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 1700 | -It's important (for compiler performance) to get rid of reflexivity as soon
|
|
| 1701 | -as it appears. See #11735, #14737, and #15019.
|
|
| 1702 | - |
|
| 1703 | -In particular, we want to behave well on
|
|
| 1704 | - |
|
| 1705 | - * e |> co1 |> co2
|
|
| 1706 | - where the two happen to cancel out entirely. That is quite common;
|
|
| 1707 | - e.g. a newtype wrapping and unwrapping cancel.
|
|
| 1708 | - |
|
| 1709 | - |
|
| 1710 | - * (f |> co) @t1 @t2 ... @tn x1 .. xm
|
|
| 1711 | - Here we will use pushCoTyArg and pushCoValArg successively, which
|
|
| 1712 | - build up SelCo stacks. Silly to do that if co is reflexive.
|
|
| 1713 | - |
|
| 1714 | -However, we don't want to call isReflexiveCo too much, because it uses
|
|
| 1715 | -type equality which is expensive on big types (#14737 comment:7).
|
|
| 1716 | - |
|
| 1717 | -A good compromise (determined experimentally) seems to be to call
|
|
| 1718 | -isReflexiveCo
|
|
| 1719 | - * when composing casts, and
|
|
| 1720 | - * at the end
|
|
| 1721 | - |
|
| 1722 | -In investigating this I saw missed opportunities for on-the-fly
|
|
| 1723 | -coercion shrinkage. See #15090.
|
|
| 1724 | - |
|
| 1725 | -Note [Avoid re-simplifying coercions]
|
|
| 1726 | -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 1727 | -In some benchmarks (with deeply nested cases) we successively push
|
|
| 1728 | -casts onto the SimplCont. We don't want to call the coercion optimiser
|
|
| 1729 | -on each successive composition -- that's at least quadratic. So:
|
|
| 1730 | - |
|
| 1731 | -* The CastIt constructor in SimplCont has a `sc_opt :: Bool` flag to
|
|
| 1732 | - record whether the coercion optimiser has been applied to the coercion.
|
|
| 1733 | - |
|
| 1734 | -* In `simplCast`, when we see (Cast e co), we simplify `co` to get
|
|
| 1735 | - an OutCoercion, and built a CastIt with sc_opt=True.
|
|
| 1736 | - |
|
| 1737 | - Actually not quite: if we are simplifying the result of inlining an
|
|
| 1738 | - unfolding (seInlineDepth > 0), then instead of /optimising/ it again,
|
|
| 1739 | - just /substitute/ which is cheaper. See `simplCoercion`.
|
|
| 1740 | - |
|
| 1741 | -* In `pushCast` if we combine this new coercion with an existing one,
|
|
| 1742 | - we build a CastIt for (co1 ; co2) with sc_opt=False.
|
|
| 1743 | - |
|
| 1744 | -* When unpacking a CastIt, in `rebuild`, we optimise the (presumably
|
|
| 1745 | - composed) coercion if sc_opt=False; this is done by `optOutCoercion`.
|
|
| 1746 | - |
|
| 1747 | -* When duplicating a continuation in `mkDupableContWithDmds`, before
|
|
| 1748 | - duplicating a CastIt, optimise the coercion. Otherwise we'll end up
|
|
| 1749 | - optimising it separately in the duplicate copies.
|
|
| 1750 | - |
|
| 1751 | -* See also Note [The sc_cast field of ApplyToVal] in GHC.Core.Opt.Simplify.Utils.
|
|
| 1752 | --}
|
|
| 1685 | +********************************************************************* -}
|
|
| 1753 | 1686 | |
| 1754 | 1687 | simplArg :: SimplEnvIS -- ^ Used only for its InScopeSet
|
| 1755 | 1688 | -> Maybe ArgInfo -- ^ Just <=> This arg `ai` occurs in an app
|
| ... | ... | @@ -1757,15 +1690,12 @@ simplArg :: SimplEnvIS -- ^ Used only for its InScopeSet |
| 1757 | 1690 | -- how `f` uses `ai`, affecting the Stop
|
| 1758 | 1691 | -- continuation passed to 'simplExprC'
|
| 1759 | 1692 | -> OutType -- ^ Type of the function applied to this arg
|
| 1760 | - -> StaticEnv -> CoreExpr -- ^ Expression with its static envt
|
|
| 1761 | - -> MOutCoercion -- Wrap this around the result
|
|
| 1693 | + -> SimplClo -- ^ The argument
|
|
| 1762 | 1694 | -> SimplM OutExpr
|
| 1763 | -simplArg _ _ _ (Simplified {}) arg mco
|
|
| 1764 | - = -- See Note [Avoid repeated simplification]
|
|
| 1765 | - case mco of
|
|
| 1766 | - MRefl -> return arg -- Vastly common case
|
|
| 1767 | - MCo co -> return (mkCast arg co)
|
|
| 1768 | -simplArg env mb_arg_info fun_ty (UnSimplified arg_se) arg mco
|
|
| 1695 | +-- See Note [The argument of ApplyToVal] in GHC.Core.Opt.Simplify.Utils
|
|
| 1696 | +simplArg _ _ _ (DoneEx arg _) = return arg -- See Note [Avoid repeated simplification]
|
|
| 1697 | +simplArg _ _ _ (DoneId v) = return (Var v)
|
|
| 1698 | +simplArg env mb_arg_info fun_ty (ContEx arg_se arg mco)
|
|
| 1769 | 1699 | = do { let arg_env' = arg_se `setInScopeFromE` env
|
| 1770 | 1700 | arg_ty = funArgTy fun_ty
|
| 1771 | 1701 | stop = case mb_arg_info of
|
| ... | ... | @@ -1774,13 +1704,12 @@ simplArg env mb_arg_info fun_ty (UnSimplified arg_se) arg mco |
| 1774 | 1704 | cont = addCastMCo mco stop
|
| 1775 | 1705 | ; simplExprC arg_env' arg cont }
|
| 1776 | 1706 | |
| 1777 | -{-
|
|
| 1778 | -************************************************************************
|
|
| 1707 | + |
|
| 1708 | +{- *********************************************************************
|
|
| 1779 | 1709 | * *
|
| 1780 | -\subsection{Lambdas}
|
|
| 1710 | + Lambdas
|
|
| 1781 | 1711 | * *
|
| 1782 | -************************************************************************
|
|
| 1783 | --}
|
|
| 1712 | +********************************************************************* -}
|
|
| 1784 | 1713 | |
| 1785 | 1714 | simplNonRecBody :: SimplEnv -> FromWhat
|
| 1786 | 1715 | -> InExpr -> SimplCont
|
| ... | ... | @@ -1807,13 +1736,9 @@ simpl_lam env bndr body (ApplyToTy { sc_arg_ty = arg_ty, sc_cont = cont }) |
| 1807 | 1736 | |
| 1808 | 1737 | -- Value beta-reduction
|
| 1809 | 1738 | -- This works for /coercion/ lambdas too
|
| 1810 | -simpl_lam env bndr body (ApplyToVal { sc_arg = arg, sc_env = arg_se
|
|
| 1811 | - , sc_cast = arg_mco, sc_cont = cont
|
|
| 1739 | +simpl_lam env bndr body (ApplyToVal { sc_arg = arg, sc_cont = cont
|
|
| 1812 | 1740 | , sc_hole_ty = fun_ty})
|
| 1813 | - | Just env' <- preInlineUnconditionally env NotTopLevel bndr arg_se arg arg_mco
|
|
| 1814 | - , not (needsCaseBindingL arg_levity arg)
|
|
| 1815 | - -- Ok to test arg::InExpr in needsCaseBinding because
|
|
| 1816 | - -- exprOkForSpeculation is stable under simplification
|
|
| 1741 | + | Just env' <- preInlineBetaUnconditionally env arg_levity bndr arg
|
|
| 1817 | 1742 | = simplTrace "SimplBindr:inline-uncond3"
|
| 1818 | 1743 | (ppr bndr <+> text ":=" <+> ppr arg) $
|
| 1819 | 1744 | do { tick (BetaReduction bndr)
|
| ... | ... | @@ -1824,14 +1749,14 @@ simpl_lam env bndr body (ApplyToVal { sc_arg = arg, sc_env = arg_se |
| 1824 | 1749 | = do { tick (BetaReduction bndr)
|
| 1825 | 1750 | ; let from_what = FromBeta arg_levity
|
| 1826 | 1751 | |
| 1827 | - ; case (arg_se, arg_mco) of
|
|
| 1828 | - (UnSimplified arg_env, MRefl)
|
|
| 1752 | + ; case arg of
|
|
| 1753 | + ContEx arg_env arg_expr MRefl
|
|
| 1829 | 1754 | -- Optimisation: when there is no wrapping coercion use simplNonRecE
|
| 1830 | 1755 | -- so that the clever floating out of let-bindings can take place
|
| 1831 | 1756 | -- in this round of the Simplifier
|
| 1832 | - -> simplNonRecE env from_what bndr (arg, arg_env) body cont
|
|
| 1757 | + -> simplNonRecE env from_what bndr (arg_expr, arg_env) body cont
|
|
| 1833 | 1758 | |
| 1834 | - _ -> do { arg' <- simplArg env Nothing fun_ty arg_se arg arg_mco
|
|
| 1759 | + _ -> do { arg' <- simplArg env Nothing fun_ty arg
|
|
| 1835 | 1760 | ; completeBindX env from_what bndr arg' body cont }
|
| 1836 | 1761 | }
|
| 1837 | 1762 | where
|
| ... | ... | @@ -2295,11 +2220,14 @@ simplOutId env fun cont |
| 2295 | 2220 | | fun `hasKey` runRWKey
|
| 2296 | 2221 | , ApplyToTy { sc_cont = cont1 } <- cont
|
| 2297 | 2222 | , ApplyToTy { sc_cont = cont2, sc_arg_ty = hole_ty } <- cont1
|
| 2298 | - , ApplyToVal { sc_cont = cont3, sc_arg = arg, sc_cast = arg_mco
|
|
| 2299 | - , sc_env = arg_se, sc_hole_ty = fun_ty } <- cont2
|
|
| 2223 | + , ApplyToVal { sc_cont = cont3, sc_arg = arg_clo, sc_hole_ty = fun_ty } <- cont2
|
|
| 2300 | 2224 | -- Do this even if (contIsStop cont), or if seCaseCase is off.
|
| 2301 | 2225 | -- See Note [No eta-expansion in runRW#]
|
| 2302 | - = do { let (_saf,arg_env) = mkAltEnv env arg_se
|
|
| 2226 | + = do { let (arg_env, arg, arg_mco)
|
|
| 2227 | + = case arg_clo of
|
|
| 2228 | + ContEx se e mco -> (se `setInScopeFromE` env, e, mco)
|
|
| 2229 | + DoneEx e _ -> (env, e, MRefl)
|
|
| 2230 | + DoneId v -> (env, Var v, MRefl)
|
|
| 2303 | 2231 | |
| 2304 | 2232 | overall_res_ty = contResultType cont3
|
| 2305 | 2233 | -- hole_ty is the type of the current runRW# application
|
| ... | ... | @@ -2314,23 +2242,32 @@ simplOutId env fun cont |
| 2314 | 2242 | -- * If we don't do this we get a beta-redex every time, so the
|
| 2315 | 2243 | -- simplifier keeps doing more iterations.
|
| 2316 | 2244 | -- * Even more important: see Note [No eta-expansion in runRW#]
|
| 2245 | + --
|
|
| 2246 | + -- But only if there is no pending cast on the argument (#27962);
|
|
| 2247 | + -- if there is, the general case below deals with it properly
|
|
| 2317 | 2248 | ; arg' <- case arg of
|
| 2318 | - Lam s body -> do { (env', s') <- simplBinder arg_env s
|
|
| 2319 | - ; body' <- simplExprC env' body inner_cont
|
|
| 2320 | - ; return (Lam s' body') }
|
|
| 2321 | - -- Important: do not try to eta-expand this lambda
|
|
| 2322 | - -- See Note [No eta-expansion in runRW#]
|
|
| 2249 | + Lam s body
|
|
| 2250 | + | MRefl <- arg_mco
|
|
| 2251 | + -> do { (env', s') <- simplBinder arg_env s
|
|
| 2252 | + ; body' <- simplExprC env' body inner_cont
|
|
| 2253 | + ; return (Lam s' body') }
|
|
| 2254 | + -- Important: do not try to eta-expand this lambda
|
|
| 2255 | + -- See Note [No eta-expansion in runRW#]
|
|
| 2323 | 2256 | |
| 2324 | 2257 | _ -> do { s' <- newId (fsLit "s") ManyTy realWorldStatePrimTy
|
| 2325 | 2258 | ; let (m,_,_) = splitFunTy fun_ty
|
| 2326 | 2259 | env' = arg_env `addNewInScopeIds` [s']
|
| 2327 | 2260 | hole_ty = mkVisFunTy m realWorldStatePrimTy new_runrw_res_ty
|
| 2328 | 2261 | cont' = pushCastMCo env' arg_mco $
|
| 2329 | - ApplyToVal { sc_arg = Var s', sc_cast = MRefl
|
|
| 2330 | - , sc_env = Simplified OkDup
|
|
| 2262 | + ApplyToVal { sc_arg = DoneId s'
|
|
| 2263 | + , sc_dup = dup
|
|
| 2331 | 2264 | , sc_cont = inner_cont
|
| 2332 | 2265 | , sc_hole_ty = hole_ty }
|
| 2333 | 2266 | -- cont' applies to s', then K
|
| 2267 | + -- The argument (Var s') is small, but inner_cont may not be
|
|
| 2268 | + -- dupable; see (AV2) in Note [The argument of ApplyToVal]
|
|
| 2269 | + dup | contIsDupable inner_cont = OkDup
|
|
| 2270 | + | otherwise = NoDup
|
|
| 2334 | 2271 | ; body' <- simplExprC env' arg cont'
|
| 2335 | 2272 | ; return (Lam s' body') }
|
| 2336 | 2273 | |
| ... | ... | @@ -2397,13 +2334,12 @@ rebuildCall env info (ApplyToTy { sc_arg_ty = arg_ty, sc_hole_ty = hole_ty, sc_c |
| 2397 | 2334 | |
| 2398 | 2335 | ---------- Simplify value arguments --------------------
|
| 2399 | 2336 | rebuildCall env fun_info
|
| 2400 | - (ApplyToVal { sc_arg = arg, sc_env = arg_se, sc_cast = arg_mco
|
|
| 2401 | - , sc_hole_ty = fun_ty, sc_cont = cont })
|
|
| 2402 | - | UnSimplified arg_env <- arg_se
|
|
| 2337 | + (ApplyToVal { sc_arg = arg, sc_hole_ty = fun_ty, sc_cont = cont })
|
|
| 2338 | + | ContEx arg_env arg_expr arg_mco <- arg
|
|
| 2403 | 2339 | , isStrictArgInfo fun_info -- Strict arguments
|
| 2404 | 2340 | , seCaseCase env -- But only when case-of-case is on.
|
| 2405 | 2341 | -- See Note [Case-of-case and full laziness]
|
| 2406 | - = simplExprF (arg_env `setInScopeFromE` env) arg $
|
|
| 2342 | + = simplExprF (arg_env `setInScopeFromE` env) arg_expr $
|
|
| 2407 | 2343 | addCastMCo arg_mco $
|
| 2408 | 2344 | StrictArg { sc_fun = fun_info, sc_fun_ty = fun_ty
|
| 2409 | 2345 | , sc_dup = NoDup
|
| ... | ... | @@ -2415,7 +2351,7 @@ rebuildCall env fun_info |
| 2415 | 2351 | -- There is no benefit (unlike in a let-binding), and we'd
|
| 2416 | 2352 | -- have to be very careful about bogus strictness through
|
| 2417 | 2353 | -- floating a demanded let.
|
| 2418 | - = do { arg' <- simplArg env (Just fun_info) fun_ty arg_se arg arg_mco
|
|
| 2354 | + = do { arg' <- simplArg env (Just fun_info) fun_ty arg
|
|
| 2419 | 2355 | ; rebuildCall env (addValArgTo fun_info arg' fun_ty) cont }
|
| 2420 | 2356 | |
| 2421 | 2357 | ---------- No further useful info, revert to generic rebuild ------------
|
| ... | ... | @@ -2563,11 +2499,11 @@ Then |
| 2563 | 2499 | * We simplify BIG before trying the rule; but the rule does not fire
|
| 2564 | 2500 | (forcing this simplification is why we have the RULE in this example)
|
| 2565 | 2501 | * We inline f = \x. g x, in `simpl_lam`
|
| 2566 | - * So if `simpl_lam` did preInlineUnconditionally we get (g BIG)
|
|
| 2502 | + * So if `simpl_lam` did preInlineBetaUnconditionally we get (g BIG)
|
|
| 2567 | 2503 | * Now if g has a RULE we'll simplify BIG again, and this whole thing can
|
| 2568 | 2504 | iterate.
|
| 2569 | 2505 | * However, if `f` did not have a RULE, so that BIG has /not/ already been
|
| 2570 | - simplified, we /want/ to do preInlineUnconditionally in simpl_lam.
|
|
| 2506 | + simplified, we /want/ to do preInlineBetaUnconditionally in simpl_lam.
|
|
| 2571 | 2507 | |
| 2572 | 2508 | So we go to some effort to avoid repeatedly simplifying the same thing.
|
| 2573 | 2509 | Suppose we are in the Plan (AFTER) case of rule application, for a rule
|
| ... | ... | @@ -2576,8 +2512,9 @@ and a call |
| 2576 | 2512 | f (e1,e2)
|
| 2577 | 2513 | Then:
|
| 2578 | 2514 | |
| 2579 | -* The (sc_env :: StaticEnv) field of ApplyToVal records if the argument
|
|
| 2580 | - has been evaluated. See Note [StaticEnv] in GHC.Core.Opt.Simplify.Utils.
|
|
| 2515 | +* The (sc_arg :: SimplClo) field of ApplyToVal records if the argument
|
|
| 2516 | + has been simplified: it is a DoneEx if so, and a ContEx if not.
|
|
| 2517 | + See Note [The argument of ApplyToVal] in GHC.Core.Opt.Simplify.Utils.
|
|
| 2581 | 2518 | |
| 2582 | 2519 | * The rule fires returning
|
| 2583 | 2520 | RuleMatch { rm_rhs = (\x y. ...x..), rm_args = [ e1, e2 ] }
|
| ... | ... | @@ -2590,17 +2527,17 @@ Then: |
| 2590 | 2527 | is the reason that `RuleMatch` separates the two.
|
| 2591 | 2528 | |
| 2592 | 2529 | * `fireRuleAFTER` uses `pushOutArgs` to push [ e1, e2 ] back onto the
|
| 2593 | - continuation with sc_env=Simplified NoDup.
|
|
| 2530 | + continuation as (DoneEx e1) and (DoneEx e2).
|
|
| 2594 | 2531 | |
| 2595 | 2532 | * Beta-reduction fires (in `simpl_lam`) for the \x.
|
| 2596 | 2533 | |
| 2597 | 2534 | * Suppose `x` occurs many times. Then we want to behave like `let x=e1 in ..`,
|
| 2598 | - but we use `simplArg`, which in turn uses that `sc_env` flag to avoid
|
|
| 2535 | + but we use `simplArg`, which in turn uses the DoneEx to avoid
|
|
| 2599 | 2536 | re-simplifying `e1`.
|
| 2600 | 2537 | |
| 2601 | 2538 | * Suppose `x` occurs just once or not at all; then the call to
|
| 2602 | - `preInlineUnconditionally` in `simpl_lam` will fire, and extend the
|
|
| 2603 | - substitution with
|
|
| 2539 | + `preInlineBetaUnconditionally` in `simpl_lam` will fire, and extend the
|
|
| 2540 | + substitution with the very same SimplClo
|
|
| 2604 | 2541 | x :-> DoneEx e1
|
| 2605 | 2542 | |
| 2606 | 2543 | * At the /occurrence/ of `x`, the function `simplInId` calls `simplOutExpr`
|
| ... | ... | @@ -2807,8 +2744,7 @@ trySeqRules in_env scrut rhs cont |
| 2807 | 2744 | , ValArg { as_arg = no_cast_scrut
|
| 2808 | 2745 | , as_dmd = seqDmd
|
| 2809 | 2746 | , as_hole_ty = res3_ty } ]
|
| 2810 | - rule_cont = ApplyToVal { sc_env = UnSimplified in_env, sc_arg = rhs
|
|
| 2811 | - , sc_cast = MRefl
|
|
| 2747 | + rule_cont = ApplyToVal { sc_arg = ContEx in_env rhs MRefl, sc_dup = NoDup
|
|
| 2812 | 2748 | , sc_cont = cont, sc_hole_ty = res4_ty }
|
| 2813 | 2749 | |
| 2814 | 2750 | out_args = [Type rhs_rep, Type scrut_ty, Type rhs_ty, no_cast_scrut]
|
| ... | ... | @@ -3913,8 +3849,8 @@ knownCon env scrut dc dc_args case_bndr alt_bndrs rhs cont |
| 3913 | 3849 | return ( emptyFloats env
|
| 3914 | 3850 | , extendIdSubst env case_bndr (DoneEx scrut NotJoinPoint))
|
| 3915 | 3851 | |
| 3916 | - | Just env' <- preInlineUnconditionally env NotTopLevel case_bndr
|
|
| 3917 | - (UnSimplified env) con_app MRefl
|
|
| 3852 | + | Just env' <- preInlineLetUnconditionally env NotTopLevel case_bndr
|
|
| 3853 | + con_app env
|
|
| 3918 | 3854 | = return (emptyFloats env', env')
|
| 3919 | 3855 | |
| 3920 | 3856 | | otherwise
|
| ... | ... | @@ -4148,8 +4084,7 @@ mkDupableContWithDmds env dmds |
| 4148 | 4084 | , sc_arg_ty = arg_ty, sc_hole_ty = hole_ty }) }
|
| 4149 | 4085 | |
| 4150 | 4086 | mkDupableContWithDmds env dmds
|
| 4151 | - (ApplyToVal { sc_arg = arg, sc_env = se, sc_cast = arg_mco
|
|
| 4152 | - , sc_cont = cont, sc_hole_ty = hole_ty })
|
|
| 4087 | + (ApplyToVal { sc_arg = arg, sc_cont = cont, sc_hole_ty = hole_ty })
|
|
| 4153 | 4088 | = -- e.g. [...hole...] (...arg...)
|
| 4154 | 4089 | -- ==>
|
| 4155 | 4090 | -- let a = ...arg...
|
| ... | ... | @@ -4161,12 +4096,12 @@ mkDupableContWithDmds env dmds |
| 4161 | 4096 | expectNonEmpty dmds
|
| 4162 | 4097 | ; (floats1, cont') <- mkDupableContWithDmds env cont_dmds cont
|
| 4163 | 4098 | ; let env' = env `setInScopeFromF` floats1
|
| 4164 | - ; arg' <- simplArg env' Nothing hole_ty se arg arg_mco
|
|
| 4099 | + ; arg' <- simplArg env' Nothing hole_ty arg
|
|
| 4165 | 4100 | ; (let_floats2, triv_arg) <- makeTrivial env' NotTopLevel dmd (fsLit "karg") arg'
|
| 4166 | 4101 | ; let all_floats = floats1 `addLetFloats` let_floats2
|
| 4167 | 4102 | ; return ( all_floats
|
| 4168 | - , ApplyToVal { sc_arg = triv_arg, sc_env = Simplified OkDup
|
|
| 4169 | - , sc_cast = MRefl, sc_cont = cont', sc_hole_ty = hole_ty }) }
|
|
| 4103 | + , ApplyToVal { sc_arg = DoneEx triv_arg NotJoinPoint, sc_dup = OkDup
|
|
| 4104 | + , sc_cont = cont', sc_hole_ty = hole_ty }) }
|
|
| 4170 | 4105 | |
| 4171 | 4106 | mkDupableContWithDmds env _
|
| 4172 | 4107 | (Select { sc_bndr = case_bndr, sc_alts = alts, sc_env = se, sc_cont = cont })
|
| ... | ... | @@ -12,7 +12,8 @@ module GHC.Core.Opt.Simplify.Utils ( |
| 12 | 12 | tryEtaExpandRhs, wantEtaExpansion,
|
| 13 | 13 | |
| 14 | 14 | -- Inlining,
|
| 15 | - preInlineUnconditionally, postInlineUnconditionally,
|
|
| 15 | + preInlineLetUnconditionally, preInlineBetaUnconditionally,
|
|
| 16 | + postInlineUnconditionally,
|
|
| 16 | 17 | activeRule,
|
| 17 | 18 | getUnfoldingInRuleMatch,
|
| 18 | 19 | updModeForStableUnfoldings, updModeForRuleLHS, updModeForRuleRHS,
|
| ... | ... | @@ -174,16 +175,14 @@ data SimplCont |
| 174 | 175 | -- Invariant: never an identity coercion
|
| 175 | 176 | , sc_opt :: Bool -- True <=> sc_co has had optCoercion applied to it
|
| 176 | 177 | -- See Note [Avoid re-simplifying coercions]
|
| 177 | - -- in GHC.Core.Opt.Simplify.Iteration
|
|
| 178 | 178 | , sc_cont :: SimplCont }
|
| 179 | 179 | |
| 180 | 180 | | ApplyToVal -- (ApplyToVal arg K)[e] = K[ e arg ]
|
| 181 | 181 | { sc_hole_ty :: OutType -- Type of the function, presumably (forall a. blah)
|
| 182 | 182 | -- See Note [The hole type in ApplyToTy]
|
| 183 | - , sc_env :: StaticEnv -- See Note [StaticEnv]
|
|
| 184 | - , sc_arg :: CoreExpr -- The argument
|
|
| 185 | - , sc_cast :: MOutCoercion -- Wrap this OutCoercion around the (simplified) argument
|
|
| 186 | - -- See Note [The sc_cast field of ApplyToVal]
|
|
| 183 | + , sc_dup :: DupFlag -- See Note [The argument of ApplyToVal]
|
|
| 184 | + , sc_arg :: SimplClo -- The argument
|
|
| 185 | + -- Invariant: if sc_arg is a ContEx, then sc_dup = NoDup
|
|
| 187 | 186 | , sc_cont :: SimplCont }
|
| 188 | 187 | |
| 189 | 188 | | ApplyToTy -- (ApplyToTy ty K)[e] = K[ e ty ]
|
| ... | ... | @@ -253,49 +252,85 @@ perhapsSubstTy (UnSimplified env) ty = substTy env ty |
| 253 | 252 | |
| 254 | 253 | {- Note [StaticEnv]
|
| 255 | 254 | ~~~~~~~~~~~~~~~~~~~
|
| 256 | -Consider ApplyToVal, which has
|
|
| 257 | - sc_env :: StaticEnv
|
|
| 258 | - sc_arg :: CoreExpr
|
|
| 259 | -Initially, `sc_arg` is an un-simplified InExpr, and `sc_env` is (UnSimplified env),
|
|
| 260 | -where `env` gives meaning to the free variables of `sc_arg`; in particular, `env`
|
|
| 261 | -may have substitutions that must apply to the argument.
|
|
| 262 | - |
|
| 263 | -But sometimes we simplify the argument, and in that case, `sc_arg` is an OutExpr,
|
|
| 264 | -the simplified argument, and `sc_env` is (Simplified NoDup) or (Simplified OkDup).
|
|
| 265 | -The former is the safe, conservative option, but in `mkDupableCont` we want to make
|
|
| 266 | -the continuation duplicable, so we make the argument small and tag it with
|
|
| 267 | -(Simplified OkDup).
|
|
| 268 | - |
|
| 269 | -We later simplify the argument, e.g. in `simplArg`. Then
|
|
| 270 | - * If sc_env is Simplified, it's a no-op
|
|
| 271 | - * If sc_env is (UnSimplified arg_env) we simplify `sc_arg` with
|
|
| 272 | - - Its captured envt `arg_env`
|
|
| 255 | +Consider Select, which has
|
|
| 256 | + sc_env :: StaticEnv
|
|
| 257 | + sc_bndr :: Id
|
|
| 258 | + sc_alts :: [CoreAlt]
|
|
| 259 | +Initially, `sc_bndr` and `sc_alts` are un-simplified InThings, and `sc_env` is
|
|
| 260 | +(UnSimplified env), where `env` gives meaning to their free variables; in
|
|
| 261 | +particular, `env` may have substitutions that must apply to them.
|
|
| 262 | + |
|
| 263 | +But sometimes (notably in `mkDupableCont`) we simplify them, and in that case
|
|
| 264 | +`sc_bndr` and `sc_alts` are OutThings, and `sc_env` is (Simplified dup).
|
|
| 265 | + |
|
| 266 | +When we finally simplify the alternatives (see `mkAltEnv`)
|
|
| 267 | + * If sc_env is Simplified, there is nothing to do
|
|
| 268 | + * If sc_env is (UnSimplified alt_env) we simplify them with
|
|
| 269 | + - Their captured envt `alt_env`
|
|
| 273 | 270 | - but overriding its InScopeSet with the larger one at the
|
| 274 | 271 | simplification point.
|
| 275 | 272 | Why override the InScopeSet? Example:
|
| 276 | - (let y = ey in f) ex
|
|
| 277 | - By the time we simplify ex, 'y' will be in scope.
|
|
| 278 | - All this is done in `simplArg`.
|
|
| 273 | + case (let y = ey in f) of alts
|
|
| 274 | + By the time we simplify alts, 'y' will be in scope.
|
|
| 279 | 275 | |
| 280 | 276 | Note that:
|
| 281 | 277 | |
| 282 | 278 | * In the Simplified case there is no environment, because the substitution has
|
| 283 | 279 | already been applied.
|
| 284 | 280 | |
| 285 | -* We say sc_arg :: CoreExpr, rather than sc_arg :: InExpr or sc_arg :: OutExpr,
|
|
| 286 | - because whether it is InExpr or OutExpr depends on `sc_env`
|
|
| 281 | +* We say sc_alts :: [CoreAlt], rather than [InAlt] or [OutAlt],
|
|
| 282 | + because whether they are InAlts or OutAlts depends on `sc_env`
|
|
| 283 | + |
|
| 284 | +* Same deal for StrictBind, where the StaticEnv scopes over sc_bndr and sc_body.
|
|
| 287 | 285 | |
| 288 | -* Same deal for Select, and StrictBind, but the StaticEnv scopes over
|
|
| 289 | - * sc_bndr and sc_alts (for Select)
|
|
| 290 | - * sc_bndr and sc_body (for StrictBind)
|
|
| 286 | +* ApplyToVal is similar, but simpler: it uses a SimplClo instead.
|
|
| 287 | + See Note [The argument of ApplyToVal].
|
|
| 291 | 288 | |
| 292 | -* Even though the InScopeSet of an (UnSimplified se) is overridden in `simplArg`,
|
|
| 293 | - that InScopeSet is not irrelevant: it should include all the free vars of
|
|
| 294 | - applying the substitution to the InExpr. Reason: contHoleType uses perhapsSubstTy
|
|
| 295 | - to apply the substitution to the expression, and that (rightly) gives ASSERT
|
|
| 296 | - failures if the InScopeSet isn't big enough.
|
|
| 289 | +* Even though the InScopeSet of an (UnSimplified se) is overridden, that
|
|
| 290 | + InScopeSet is not irrelevant: it should include all the free vars of
|
|
| 291 | + applying the substitution to the InThing. Reason: contHoleType uses
|
|
| 292 | + perhapsSubstTy to apply the substitution to the binder, and that
|
|
| 293 | + (rightly) gives ASSERT failures if the InScopeSet isn't big enough.
|
|
| 297 | 294 | |
| 298 | 295 | (SE1) If dup = OkToDup, then continuation k is also ok-to-dup
|
| 296 | + |
|
| 297 | +Note [The argument of ApplyToVal]
|
|
| 298 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 299 | +ApplyToVal has
|
|
| 300 | + sc_dup :: DupFlag
|
|
| 301 | + sc_arg :: SimplClo
|
|
| 302 | + |
|
| 303 | +Initially, `sc_arg` is (ContEx env arg MRefl), where `arg` is the un-simplified
|
|
| 304 | +InExpr, and `env` gives meaning to the free variables of `arg`; in particular,
|
|
| 305 | +`env` may have substitutions that must apply to the argument.
|
|
| 306 | + |
|
| 307 | +But sometimes we simplify the argument, and in that case, `sc_arg` is
|
|
| 308 | +(DoneEx arg' NotJoinPoint), where `arg'` is the simplified argument.
|
|
| 309 | + |
|
| 310 | +The `sc_dup` flag says whether the continuation can be duplicated. NoDup is
|
|
| 311 | +the safe, conservative option, but in `mkDupableCont` we want to make the
|
|
| 312 | +continuation duplicable, so we make the argument small (and hence a DoneEx)
|
|
| 313 | +and set `sc_dup` to OkDup. Invariants:
|
|
| 314 | + |
|
| 315 | + (AV1) If `sc_arg` is a ContEx, then `sc_dup` is NoDup.
|
|
| 316 | + |
|
| 317 | + (AV2) If `sc_dup` is OkDup then `sc_cont` is also ok-to-dup; see (SE1)
|
|
| 318 | + in Note [StaticEnv].
|
|
| 319 | + |
|
| 320 | +We later simplify the argument, in `simplArg`. Then
|
|
| 321 | + * If `sc_arg` is a DoneEx (or DoneId), it's a no-op
|
|
| 322 | + * If `sc_arg` is (ContEx arg_env arg mco) we simplify `arg` with
|
|
| 323 | + - Its captured envt `arg_env`
|
|
| 324 | + - but overriding its InScopeSet with the larger one at the
|
|
| 325 | + simplification point.
|
|
| 326 | + Why override the InScopeSet? Example:
|
|
| 327 | + (let y = ey in f) ex
|
|
| 328 | + By the time we simplify ex, 'y' will be in scope.
|
|
| 329 | + Then we wrap the cast `mco` around the result.
|
|
| 330 | + See Note [The MOutCoercion field of ContEx].
|
|
| 331 | + |
|
| 332 | +Using a SimplClo for the argument means that it can be substituted for a lambda
|
|
| 333 | +binder, in `preInlineBetaUnconditionally`, without further ado.
|
|
| 299 | 334 | -}
|
| 300 | 335 | |
| 301 | 336 | instance Outputable DupFlag where
|
| ... | ... | @@ -313,9 +348,9 @@ instance Outputable SimplCont where |
| 313 | 348 | = (text "TickIt" <+> ppr t) $$ ppr cont
|
| 314 | 349 | ppr (ApplyToTy { sc_arg_ty = ty, sc_cont = cont })
|
| 315 | 350 | = (text "ApplyToTy" <+> pprParendType ty) $$ ppr cont
|
| 316 | - ppr (ApplyToVal { sc_arg = arg, sc_env = env, sc_cont = cont, sc_hole_ty = hole_ty })
|
|
| 317 | - = (hang (text "ApplyToVal" <> braces (ppr env) <+> text "hole-ty:" <+> pprParendType hole_ty)
|
|
| 318 | - 2 (pprParendExpr arg))
|
|
| 351 | + ppr (ApplyToVal { sc_arg = arg, sc_dup = dup, sc_cont = cont, sc_hole_ty = hole_ty })
|
|
| 352 | + = (hang (text "ApplyToVal" <> braces (ppr dup) <+> text "hole-ty:" <+> pprParendType hole_ty)
|
|
| 353 | + 2 (ppr arg))
|
|
| 319 | 354 | $$ ppr cont
|
| 320 | 355 | ppr (StrictBind { sc_bndr = b, sc_cont = cont })
|
| 321 | 356 | = (text "StrictBind" <+> ppr b) $$ ppr cont
|
| ... | ... | @@ -434,8 +469,8 @@ pushOutArgs fun_ty (arg:args) cont |
| 434 | 469 | = ApplyToTy { sc_hole_ty = fun_ty, sc_arg_ty = ty
|
| 435 | 470 | , sc_cont = pushOutArgs (piResultTy fun_ty ty) args cont }
|
| 436 | 471 | | otherwise
|
| 437 | - = ApplyToVal { sc_hole_ty = fun_ty, sc_cast = MRefl
|
|
| 438 | - , sc_arg = arg, sc_env = Simplified NoDup
|
|
| 472 | + = ApplyToVal { sc_hole_ty = fun_ty
|
|
| 473 | + , sc_arg = DoneEx arg NotJoinPoint, sc_dup = NoDup
|
|
| 439 | 474 | , sc_cont = pushOutArgs (funResultTy fun_ty) args cont}
|
| 440 | 475 | |
| 441 | 476 | pushArgSpecs :: [ArgSpec] -- In normal, forward order
|
| ... | ... | @@ -446,7 +481,7 @@ pushArgSpec :: ArgSpec -> SimplCont -> SimplCont |
| 446 | 481 | pushArgSpec (TyArg { as_arg_ty = arg_ty, as_hole_ty = hole_ty }) cont
|
| 447 | 482 | = ApplyToTy { sc_arg_ty = arg_ty, sc_hole_ty = hole_ty, sc_cont = cont }
|
| 448 | 483 | pushArgSpec (ValArg { as_arg = arg, as_hole_ty = hole_ty }) cont
|
| 449 | - = ApplyToVal { sc_arg = arg, sc_env = Simplified NoDup, sc_cast = MRefl
|
|
| 484 | + = ApplyToVal { sc_arg = DoneEx arg NotJoinPoint, sc_dup = NoDup
|
|
| 450 | 485 | , sc_hole_ty = hole_ty, sc_cont = cont }
|
| 451 | 486 | |
| 452 | 487 | argSpecArg :: ArgSpec -> OutExpr
|
| ... | ... | @@ -497,7 +532,12 @@ contIsStop _ = False |
| 497 | 532 | contIsDupable :: SimplCont -> Bool
|
| 498 | 533 | contIsDupable (Stop {}) = True
|
| 499 | 534 | contIsDupable (ApplyToTy { sc_cont = k }) = contIsDupable k
|
| 500 | -contIsDupable (ApplyToVal { sc_env = se }) = okToDupSE se -- See (SE1) in Note [StaticEnv]
|
|
| 535 | +contIsDupable (ApplyToVal { sc_dup = dup, sc_arg = arg })
|
|
| 536 | + = assertPpr (not (okToDup dup && is_cont_ex arg)) (ppr arg) $
|
|
| 537 | + okToDup dup -- See (AV1,AV2) in Note [The argument of ApplyToVal]
|
|
| 538 | + where
|
|
| 539 | + is_cont_ex (ContEx {}) = True
|
|
| 540 | + is_cont_ex _ = False
|
|
| 501 | 541 | contIsDupable (Select { sc_env = se }) = okToDupSE se -- ...ditto...
|
| 502 | 542 | contIsDupable (StrictBind { sc_env = se }) = okToDupSE se -- ...ditto...
|
| 503 | 543 | contIsDupable (StrictArg { sc_dup = dup }) = okToDup dup -- ...ditto...
|
| ... | ... | @@ -606,8 +646,8 @@ contArgs env cont |
| 606 | 646 | lone (CastIt {}) = False -- stops it being "lone"
|
| 607 | 647 | lone _ = True
|
| 608 | 648 | |
| 609 | - go args (ApplyToVal { sc_arg = arg, sc_env = se, sc_cont = k })
|
|
| 610 | - = go (interestingArg env se arg : args) k
|
|
| 649 | + go args (ApplyToVal { sc_arg = arg, sc_cont = k })
|
|
| 650 | + = go (interestingArg env arg : args) k
|
|
| 611 | 651 | go args (ApplyToTy { sc_cont = k }) = go args k
|
| 612 | 652 | go args (CastIt { sc_cont = k }) = go args k
|
| 613 | 653 | go args k = (False, reverse args, k)
|
| ... | ... | @@ -622,15 +662,13 @@ contOutArgs env cont |
| 622 | 662 | go (ApplyToTy { sc_arg_ty = ty, sc_cont = cont })
|
| 623 | 663 | = Type ty : go cont
|
| 624 | 664 | |
| 625 | - go (ApplyToVal { sc_arg = arg, sc_env = se, sc_cont = cont })
|
|
| 626 | - = case se of
|
|
| 627 | - Simplified {} -> arg : go cont
|
|
| 628 | - UnSimplified env -> GHC.Core.Subst.substExpr (getFullSubst in_scope env) arg : go cont
|
|
| 629 | - -- Make sure we apply the static environment `sc_env` as a substitution
|
|
| 665 | + go (ApplyToVal { sc_arg = arg, sc_cont = cont })
|
|
| 666 | + = simplCloExpr in_scope arg : go cont
|
|
| 667 | + -- Make sure we apply the static environment of a ContEx as a substitution
|
|
| 630 | 668 | -- to get an OutExpr. See (BF1) in Note [tryRules: plan (BEFORE)]
|
| 631 | 669 | -- in GHC.Core.Opt.Simplify.Iteration
|
| 632 | - -- NB: we use substExpr, not substExprSC: we want to get the benefit of
|
|
| 633 | - -- knowing what is evaluated etc, via the in-scope set
|
|
| 670 | + -- simplCloExpr also wraps the pending cast (if any) around the argument;
|
|
| 671 | + -- omitting it can make a rule fail to match (#27963)
|
|
| 634 | 672 | |
| 635 | 673 | -- No more arguments
|
| 636 | 674 | go _ = []
|
| ... | ... | @@ -827,23 +865,86 @@ make use of the strictness info for the function. |
| 827 | 865 | * *
|
| 828 | 866 | ********************************************************************* -}
|
| 829 | 867 | |
| 830 | -{- Note [The sc_cast field of ApplyToVal]
|
|
| 831 | -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 868 | +{- Note [The MOutCoercion field of ContEx]
|
|
| 869 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 832 | 870 | In the `ApplyToVal` case of `pushCast` we want to push an `OutCoercion` into the
|
| 833 | -argument. But the argument may or may not have been simplified (see Note [StaticEnv])
|
|
| 834 | -so we definitely can't just wrap the cast around the argument. Instead we store it
|
|
| 835 | -in the `sc_cast` field -- this is the whole reason for the `sc_cast` field.
|
|
| 871 | +argument. But the argument may or may not have been simplified (see Note [The
|
|
| 872 | +argument of ApplyToVal]):
|
|
| 873 | + |
|
| 874 | +* If it has been simplified, it is (DoneEx arg' _), and we can simply wrap the
|
|
| 875 | + cast around `arg'`.
|
|
| 876 | + |
|
| 877 | +* If it has not, it is (ContEx env arg mco), and we definitely can't just wrap
|
|
| 878 | + the (Out)cast around the InExpr `arg`. Instead we compose it with `mco` --
|
|
| 879 | + this is the whole reason for the MOutCoercion field of `ContEx`.
|
|
| 880 | + |
|
| 881 | +All this is done by `mkCloCastMCo`.
|
|
| 836 | 882 | |
| 837 | 883 | We deal with that pending cast mainly in `simplArg`.
|
| 838 | 884 | |
| 839 | 885 | But we /also/ sometimes want to substitute that argument for a lambda-binder, in
|
| 840 | -`preInlineUnconditionally`. So we also have to put that pending cast into
|
|
| 841 | -`ContEx`; hence the MOutCoercion field of `ContEx`. Then we deal with it
|
|
| 842 | -appropriately when /applying/ the substitution in `simplInId`.
|
|
| 886 | +`preInlineBetaUnconditionally`; and since the substitution maps an Id to a SimplClo,
|
|
| 887 | +the pending cast comes along for free. Then we deal with it appropriately when
|
|
| 888 | +/applying/ the substitution in `simplInId`.
|
|
| 889 | + |
|
| 890 | +Note [Optimising reflexivity]
|
|
| 891 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 892 | +It's important (for compiler performance) to get rid of reflexivity as soon
|
|
| 893 | +as it appears. See #11735, #14737, and #15019.
|
|
| 894 | + |
|
| 895 | +In particular, we want to behave well on
|
|
| 896 | + |
|
| 897 | + * e |> co1 |> co2
|
|
| 898 | + where the two happen to cancel out entirely. That is quite common;
|
|
| 899 | + e.g. a newtype wrapping and unwrapping cancel.
|
|
| 900 | + |
|
| 901 | + |
|
| 902 | + * (f |> co) @t1 @t2 ... @tn x1 .. xm
|
|
| 903 | + Here we will use pushCoTyArg and pushCoValArg successively, which
|
|
| 904 | + build up SelCo stacks. Silly to do that if co is reflexive.
|
|
| 905 | + |
|
| 906 | +However, we don't want to call isReflexiveCo too much, because it uses
|
|
| 907 | +type equality which is expensive on big types (#14737 comment:7).
|
|
| 908 | + |
|
| 909 | +A good compromise (determined experimentally) seems to be to call
|
|
| 910 | +isReflexiveCo
|
|
| 911 | + * when composing casts, and
|
|
| 912 | + * at the end
|
|
| 913 | + |
|
| 914 | +In investigating this I saw missed opportunities for on-the-fly
|
|
| 915 | +coercion shrinkage. See #15090.
|
|
| 916 | + |
|
| 917 | +Note [Avoid re-simplifying coercions]
|
|
| 918 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 919 | +In some benchmarks (with deeply nested cases) we successively push
|
|
| 920 | +casts onto the SimplCont. We don't want to call the coercion optimiser
|
|
| 921 | +on each successive composition -- that's at least quadratic. So:
|
|
| 922 | + |
|
| 923 | +* The CastIt constructor in SimplCont has a `sc_opt :: Bool` flag to
|
|
| 924 | + record whether the coercion optimiser has been applied to the coercion.
|
|
| 925 | + |
|
| 926 | +* In `simplCast`, when we see (Cast e co), we simplify `co` to get
|
|
| 927 | + an OutCoercion, and built a CastIt with sc_opt=True.
|
|
| 928 | + |
|
| 929 | + Actually not quite: if we are simplifying the result of inlining an
|
|
| 930 | + unfolding (seInlineDepth > 0), then instead of /optimising/ it again,
|
|
| 931 | + just /substitute/ which is cheaper. See `simplCoercion`.
|
|
| 932 | + |
|
| 933 | +* In `pushCast` if we combine this new coercion with an existing one,
|
|
| 934 | + we build a CastIt for (co1 ; co2) with sc_opt=False.
|
|
| 935 | + |
|
| 936 | +* When unpacking a CastIt, in `rebuild`, we optimise the (presumably
|
|
| 937 | + composed) coercion if sc_opt=False; this is done by `optOutCoercion`.
|
|
| 938 | + |
|
| 939 | +* When duplicating a continuation in `mkDupableContWithDmds`, before
|
|
| 940 | + duplicating a CastIt, optimise the coercion. Otherwise we'll end up
|
|
| 941 | + optimising it separately in the duplicate copies.
|
|
| 942 | + |
|
| 943 | +* See also Note [The MOutCoercion field of ContEx]
|
|
| 843 | 944 | -}
|
| 844 | 945 | |
| 845 | 946 | optOutCoercion :: SimplEnvIS -> OutCoercion -> Bool -> OutCoercion
|
| 846 | --- See Note [Avoid re-simplifying coercions] in GHC.Core.Opt.Simplify.Iteration
|
|
| 947 | +-- See Note [Avoid re-simplifying coercions]
|
|
| 847 | 948 | optOutCoercion env co already_optimised
|
| 848 | 949 | | already_optimised = co -- Ditto
|
| 849 | 950 | | otherwise = optCoercion opts empty_subst co
|
| ... | ... | @@ -863,7 +964,8 @@ pushCastMCo env (MCo co) cont = pushCast env co cont |
| 863 | 964 | |
| 864 | 965 | pushCast :: SimplEnv -> OutCoercion -> SimplCont -> SimplCont
|
| 865 | 966 | pushCast env co cont
|
| 866 | - = go co True cont
|
|
| 967 | + | isReflCo co = cont -- See Note [Optimising reflexivity]
|
|
| 968 | + | otherwise = go co True cont
|
|
| 867 | 969 | where
|
| 868 | 970 | |
| 869 | 971 | -- ToDo: pushCast Refl (ApplylToVal arg1 (ApplyToVal arg2 ...))
|
| ... | ... | @@ -872,7 +974,7 @@ pushCast env co cont |
| 872 | 974 | go co1 _ (CastIt { sc_co = co2, sc_cont = cont }) -- See Note [Optimising reflexivity] in GHC.Core.Opt.Simplify.Iteration
|
| 873 | 975 | = go (mkTransCo co1 co2) False cont
|
| 874 | 976 | -- False: (mkTransCo co1 co2) is not fully optimised
|
| 875 | - -- See Note [Avoid re-simplifying coercions] in GHC.Core.Opt.Simplify.Iteration
|
|
| 977 | + -- See Note [Avoid re-simplifying coercions]
|
|
| 876 | 978 | |
| 877 | 979 | go co co_is_opt (ApplyToTy { sc_arg_ty = arg_ty, sc_cont = tail })
|
| 878 | 980 | | Just (tyL, arg_ty', m_co') <- pushCoTyArg co arg_ty
|
| ... | ... | @@ -887,21 +989,17 @@ pushCast env co cont |
| 887 | 989 | -- where co :: (s1->s2) ~ (t1->t2)
|
| 888 | 990 | -- co1 :: t1 ~ s1
|
| 889 | 991 | -- co2 :: s2 ~ t2
|
| 890 | - go co co_is_opt cont@(ApplyToVal { sc_arg = arg, sc_env = arg_se
|
|
| 891 | - , sc_cast = arg_mco, sc_cont = tail })
|
|
| 992 | + go co co_is_opt cont@(ApplyToVal { sc_arg = arg, sc_cont = tail })
|
|
| 892 | 993 | | not co_is_opt
|
| 893 | 994 | = -- pushCoValArg duplicates the coercion, so optimise first
|
| 894 | 995 | go (optOutCoercion (zapSubstEnv env) co co_is_opt) True cont
|
| 895 | 996 | |
| 896 | 997 | | Just (tyL, m_co1, m_co2) <- pushCoValArg co
|
| 897 | 998 | = {-#SCC "addCoerce-pushCoValArg" #-}
|
| 898 | - -- Dealing with m_co1 is the entire reason for the sc_cast field
|
|
| 899 | - -- of ApplyToVal; see Note [The sc_cast field of ApplyToVal]
|
|
| 900 | - ApplyToVal { sc_arg = arg
|
|
| 901 | - , sc_env = arg_se
|
|
| 902 | - , sc_cast = arg_mco `mkTransMCo` m_co1
|
|
| 903 | - , sc_cont = go_mco m_co2 True tail
|
|
| 904 | - , sc_hole_ty = tyL }
|
|
| 999 | + -- See Note [The MOutCoercion field of ContEx]
|
|
| 1000 | + cont { sc_arg = mkCloCastMCo arg m_co1
|
|
| 1001 | + , sc_cont = go_mco m_co2 True tail
|
|
| 1002 | + , sc_hole_ty = tyL }
|
|
| 905 | 1003 | |
| 906 | 1004 | go co co_is_opt cont
|
| 907 | 1005 | | isReflCo co = cont -- Having this at the end makes a huge
|
| ... | ... | @@ -915,6 +1013,13 @@ pushCast env co cont |
| 915 | 1013 | go_mco MRefl _ cont = cont
|
| 916 | 1014 | go_mco (MCo co) opt cont = go co opt cont
|
| 917 | 1015 | |
| 1016 | +mkCloCastMCo :: SimplClo -> MOutCoercion -> SimplClo
|
|
| 1017 | +-- See Note [The MOutCoercion field of ContEx]
|
|
| 1018 | +mkCloCastMCo clo MRefl = clo
|
|
| 1019 | +mkCloCastMCo (DoneId v) (MCo co) = DoneEx (mkCast (Var v) co) NotJoinPoint
|
|
| 1020 | +mkCloCastMCo (DoneEx e _) (MCo co) = DoneEx (mkCast e co) NotJoinPoint
|
|
| 1021 | +mkCloCastMCo (ContEx se e mco) (MCo co) = ContEx se e (mco `mkTransMCo` MCo co)
|
|
| 1022 | + |
|
| 918 | 1023 | |
| 919 | 1024 | {-
|
| 920 | 1025 | ************************************************************************
|
| ... | ... | @@ -1228,19 +1333,17 @@ Wrinkles: |
| 1228 | 1333 | |
| 1229 | 1334 | -}
|
| 1230 | 1335 | |
| 1231 | -interestingArg :: SimplEnv -> StaticEnv -> CoreExpr -> ArgSummary
|
|
| 1336 | +interestingArg :: SimplEnv -> SimplClo -> ArgSummary
|
|
| 1232 | 1337 | -- See Note [Interesting arguments]
|
| 1233 | -interestingArg env se e
|
|
| 1234 | - = case se of
|
|
| 1235 | - Simplified {} -> go env 0 e
|
|
| 1236 | - UnSimplified static_env -> go (static_env `setInScopeFromE` env) 0 e
|
|
| 1338 | +interestingArg env clo
|
|
| 1339 | + = go_clo env 0 clo
|
|
| 1237 | 1340 | where
|
| 1341 | + go_clo _ n (DoneId v) = go_var n v
|
|
| 1342 | + go_clo env n (DoneEx e _) = go (zapSubstEnv env) n e
|
|
| 1343 | + go_clo env n (ContEx se e _) = go (se `setInScopeFromE` env) n e
|
|
| 1344 | + |
|
| 1238 | 1345 | -- n is # value args to which the expression is applied
|
| 1239 | - go env n (Var v)
|
|
| 1240 | - = case substId env v of
|
|
| 1241 | - DoneId v' -> go_var n v'
|
|
| 1242 | - DoneEx e _ -> go (zapSubstEnv env) n e
|
|
| 1243 | - ContEx se e _mco -> go (se `setInScopeFromE` env) n e
|
|
| 1346 | + go env n (Var v) = go_clo env n (substId env v)
|
|
| 1244 | 1347 | |
| 1245 | 1348 | go _ _ (Lit l)
|
| 1246 | 1349 | | isLitRubbish l = NonTrivArg -- See (IA3) in Note [Interesting arguments]
|
| ... | ... | @@ -1739,15 +1842,15 @@ is a term (not a coercion) so we can't necessarily inline the latter in |
| 1739 | 1842 | the former.
|
| 1740 | 1843 | -}
|
| 1741 | 1844 | |
| 1742 | -preInlineUnconditionally
|
|
| 1845 | +preInlineLetUnconditionally
|
|
| 1743 | 1846 | :: SimplEnv -> TopLevelFlag -> InId
|
| 1744 | - -> StaticEnv -> CoreExpr -> MOutCoercion -- The argument
|
|
| 1847 | + -> InExpr -> SimplEnv -- These two go together
|
|
| 1745 | 1848 | -> Maybe SimplEnv -- Returned env has extended substitution
|
| 1746 | 1849 | -- Precondition: rhs satisfies the let-can-float invariant
|
| 1747 | 1850 | -- See Note [Core let-can-float invariant] in GHC.Core
|
| 1748 | 1851 | -- Reason: we don't want to inline single uses, or discard dead bindings,
|
| 1749 | 1852 | -- for unlifted, side-effect-ful bindings
|
| 1750 | -preInlineUnconditionally env top_lvl bndr rhs_se rhs rhs_mco
|
|
| 1853 | +preInlineLetUnconditionally env top_lvl bndr rhs rhs_env
|
|
| 1751 | 1854 | | not pre_inline_unconditionally = Nothing
|
| 1752 | 1855 | | not active = Nothing
|
| 1753 | 1856 | | isTopLevel top_lvl && isDeadEndId bndr = Nothing -- Note [Top-level bottoming Ids]
|
| ... | ... | @@ -1759,26 +1862,15 @@ preInlineUnconditionally env top_lvl bndr rhs_se rhs rhs_mco |
| 1759 | 1862 | |
| 1760 | 1863 | -- See Note [Stable unfoldings and preInlineUnconditionally]
|
| 1761 | 1864 | | not (isInlinePragma inline_prag)
|
| 1762 | - , Just inl <- maybeUnfoldingTemplate unf = assertPpr (isReflMCo rhs_mco) (ppr bndr) $
|
|
| 1763 | - Just $! (extend_subst_with inl)
|
|
| 1865 | + , Just inl <- maybeUnfoldingTemplate unf = Just $! (extend_subst_with inl)
|
|
| 1764 | 1866 | |
| 1765 | 1867 | | otherwise = Nothing
|
| 1766 | 1868 | where
|
| 1767 | 1869 | unf = idUnfolding bndr
|
| 1768 | 1870 | |
| 1769 | - -- If the rhs is /not/ already simplified, extend the envt with ContEx, which captures
|
|
| 1770 | - -- the the lexical environment for us to restore in `simplInId`.
|
|
| 1771 | - -- If the rhs /is/ already simplified, then extend the envt with DoneEx;
|
|
| 1772 | - -- That makes `simplInId` call `simplOutExpr` which avoids re-simplifying
|
|
| 1773 | - -- the RHS.
|
|
| 1774 | - -- See Note [Avoid repeated simplification] in GHC.Core.Opt.Simplify.Iteration
|
|
| 1775 | - extend_subst_with inl_rhs
|
|
| 1776 | - = extendIdSubst env bndr $!
|
|
| 1777 | - case rhs_se of
|
|
| 1778 | - Simplified _ -> case rhs_mco of
|
|
| 1779 | - MRefl -> DoneEx inl_rhs NotJoinPoint -- Common case
|
|
| 1780 | - MCo co -> DoneEx (mkCast inl_rhs co) NotJoinPoint
|
|
| 1781 | - UnSimplified rhs_env -> ContEx rhs_env inl_rhs rhs_mco
|
|
| 1871 | + -- The rhs is not yet simplified, so extend the envt with ContEx, which
|
|
| 1872 | + -- captures the lexical environment for us to restore in `simplInId`.
|
|
| 1873 | + extend_subst_with inl_rhs = extendIdSubst env bndr $! ContEx rhs_env inl_rhs MRefl
|
|
| 1782 | 1874 | |
| 1783 | 1875 | one_occ IAmDead = True -- Happens in ((\x.1) v)
|
| 1784 | 1876 | one_occ OneOcc{ occ_n_br = 1
|
| ... | ... | @@ -1794,39 +1886,6 @@ preInlineUnconditionally env top_lvl bndr rhs_se rhs rhs_mco |
| 1794 | 1886 | -- See Note [pre/postInlineUnconditionally in gentle mode]
|
| 1795 | 1887 | inline_prag = idInlinePragma bndr
|
| 1796 | 1888 | |
| 1797 | --- Be very careful before inlining inside a lambda, because (a) we must not
|
|
| 1798 | --- invalidate occurrence information, and (b) we want to avoid pushing a
|
|
| 1799 | --- single allocation (here) into multiple allocations (inside lambda).
|
|
| 1800 | --- Inlining a *function* with a single *saturated* call would be ok, mind you.
|
|
| 1801 | --- || (if is_cheap && not (canInlineInLam rhs) then pprTrace "preinline" (ppr bndr <+> ppr rhs) ok else ok)
|
|
| 1802 | --- where
|
|
| 1803 | --- is_cheap = exprIsCheap rhs
|
|
| 1804 | --- ok = is_cheap && int_cxt
|
|
| 1805 | - |
|
| 1806 | - -- int_cxt The context isn't totally boring
|
|
| 1807 | - -- E.g. let f = \ab.BIG in \y. map f xs
|
|
| 1808 | - -- Don't want to substitute for f, because then we allocate
|
|
| 1809 | - -- its closure every time the \y is called
|
|
| 1810 | - -- But: let f = \ab.BIG in \y. map (f y) xs
|
|
| 1811 | - -- Now we do want to substitute for f, even though it's not
|
|
| 1812 | - -- saturated, because we're going to allocate a closure for
|
|
| 1813 | - -- (f y) every time round the loop anyhow.
|
|
| 1814 | - |
|
| 1815 | - -- canInlineInLam => free vars of rhs are (Once in_lam) or Many,
|
|
| 1816 | - -- so substituting rhs inside a lambda doesn't change the occ info.
|
|
| 1817 | - -- Sadly, not quite the same as exprIsHNF.
|
|
| 1818 | - canInlineInLam (Lit _) = True
|
|
| 1819 | - canInlineInLam (Cast e _) = canInlineInLam e
|
|
| 1820 | - canInlineInLam (Lam b e) = isRuntimeVar b || canInlineInLam e
|
|
| 1821 | - canInlineInLam (Tick t e) = not (tickishIsCode t) && canInlineInLam e
|
|
| 1822 | - canInlineInLam (Var v) = case idOccInfo v of
|
|
| 1823 | - OneOcc { occ_in_lam = IsInsideLam } -> True
|
|
| 1824 | - ManyOccs {} -> True
|
|
| 1825 | - _ -> False
|
|
| 1826 | - canInlineInLam _ = False
|
|
| 1827 | - -- not ticks. Counting ticks cannot be duplicated, and non-counting
|
|
| 1828 | - -- ticks around a Lam will disappear anyway.
|
|
| 1829 | - |
|
| 1830 | 1889 | early_phase =
|
| 1831 | 1890 | case sePhase env of
|
| 1832 | 1891 | SimplPhase p -> p /= FinalPhase
|
| ... | ... | @@ -1857,6 +1916,97 @@ preInlineUnconditionally env top_lvl bndr rhs_se rhs rhs_mco |
| 1857 | 1916 | -- (Nor can we check for `exprIsExpandable rhs`, because that needs to look
|
| 1858 | 1917 | -- at the non-existent unfolding for the `I# 2#` which is also floated out.)
|
| 1859 | 1918 | |
| 1919 | +preInlineBetaUnconditionally
|
|
| 1920 | + :: SimplEnv -> Levity -> InId
|
|
| 1921 | + -> SimplClo -- The argument
|
|
| 1922 | + -> Maybe SimplEnv -- Returned env has extended substitution
|
|
| 1923 | +-- Like preInlineLetUnconditionally, but for a beta-redex (\bndr. body) arg
|
|
| 1924 | +-- The argument may or may not be already simplified (DoneEx or ContEx)
|
|
| 1925 | +--
|
|
| 1926 | +-- The binder is a lambda binder and so lacks most IdInfo;
|
|
| 1927 | +-- see Note [Core binder IdInfo invariants]
|
|
| 1928 | +--
|
|
| 1929 | +-- Compared to preInlineLetUnconditionally we omit some checks:
|
|
| 1930 | +-- * The binder is never top-level, so no need for the
|
|
| 1931 | +-- Note [Top-level bottoming Ids] check, nor for `early_phase`
|
|
| 1932 | +-- * A lambda binder is never a join point, so no need for the
|
|
| 1933 | +-- Note [Do not inline exit join points] check
|
|
| 1934 | +-- * A lambda binder never has an inline pragma, a stable unfolding, or RULES
|
|
| 1935 | +-- (see `lamBndrInfoOK`), so no need to check activation, nor for
|
|
| 1936 | +-- Note [Stable unfoldings and preInlineUnconditionally]
|
|
| 1937 | +--
|
|
| 1938 | +-- And we add one extra one
|
|
| 1939 | +-- * Check the let-can-float invariant, and don't inline if not
|
|
| 1940 | + |
|
| 1941 | +preInlineBetaUnconditionally env levity bndr arg
|
|
| 1942 | + | assertPpr (lamBndrInfoOK bndr) (ppr bndr) $
|
|
| 1943 | + not pre_inline_unconditionally = Nothing
|
|
| 1944 | + | isCoVar bndr = Nothing -- Note [Do not inline CoVars unconditionally]
|
|
| 1945 | + | not (one_occ (idOccInfo bndr)) = Nothing
|
|
| 1946 | + | needs_case_binding = Nothing -- Enforce the let-can-float invariant
|
|
| 1947 | + | otherwise = Just $! extendIdSubst env bndr arg
|
|
| 1948 | + -- If the arg is not yet simplified, it is a ContEx, which captures the
|
|
| 1949 | + -- lexical environment for us to restore in `simplInId`.
|
|
| 1950 | + -- If the arg is already simplified, then it is a DoneEx;
|
|
| 1951 | + -- That makes `simplInId` call `simplOutExpr` which avoids re-simplifying
|
|
| 1952 | + -- the arg.
|
|
| 1953 | + -- See Note [Avoid repeated simplification] in GHC.Core.Opt.Simplify.Iteration
|
|
| 1954 | + where
|
|
| 1955 | + pre_inline_unconditionally = sePreInline env
|
|
| 1956 | + |
|
| 1957 | + needs_case_binding = needsCaseBindingL levity rhs
|
|
| 1958 | + -- Ok to test an InExpr or OutExpr in needsCaseBindingL because
|
|
| 1959 | + -- exprOkForSpeculation is stable under simplification
|
|
| 1960 | + |
|
| 1961 | + rhs :: CoreExpr -- Can be InExpr or OutExpr, but here it does not matter
|
|
| 1962 | + rhs = case arg of
|
|
| 1963 | + ContEx _ e _ -> e -- InExpr
|
|
| 1964 | + DoneEx e _ -> e -- OutExpr
|
|
| 1965 | + DoneId v -> Var v -- OutExpr
|
|
| 1966 | + |
|
| 1967 | + one_occ IAmDead = True -- Happens in ((\x.1) v)
|
|
| 1968 | + one_occ OneOcc{ occ_n_br = 1
|
|
| 1969 | + , occ_in_lam = NotInsideLam } = True
|
|
| 1970 | + one_occ OneOcc{ occ_n_br = 1
|
|
| 1971 | + , occ_in_lam = IsInsideLam
|
|
| 1972 | + , occ_int_cxt = IsInteresting } = canInlineInLam rhs
|
|
| 1973 | + one_occ _ = False
|
|
| 1974 | + |
|
| 1975 | + |
|
| 1976 | +canInlineInLam :: CoreExpr -> Bool
|
|
| 1977 | +-- Be very careful before inlining inside a lambda, because (a) we must not
|
|
| 1978 | +-- invalidate occurrence information, and (b) we want to avoid pushing a
|
|
| 1979 | +-- single allocation (here) into multiple allocations (inside lambda).
|
|
| 1980 | +-- Inlining a *function* with a single *saturated* call would be ok, mind you.
|
|
| 1981 | +-- || (if is_cheap && not (canInlineInLam rhs) then pprTrace "preinline" (ppr bndr <+> ppr rhs) ok else ok)
|
|
| 1982 | +-- where
|
|
| 1983 | +-- is_cheap = exprIsCheap rhs
|
|
| 1984 | +-- ok = is_cheap && int_cxt
|
|
| 1985 | + |
|
| 1986 | + -- int_cxt The context isn't totally boring
|
|
| 1987 | + -- E.g. let f = \ab.BIG in \y. map f xs
|
|
| 1988 | + -- Don't want to substitute for f, because then we allocate
|
|
| 1989 | + -- its closure every time the \y is called
|
|
| 1990 | + -- But: let f = \ab.BIG in \y. map (f y) xs
|
|
| 1991 | + -- Now we do want to substitute for f, even though it's not
|
|
| 1992 | + -- saturated, because we're going to allocate a closure for
|
|
| 1993 | + -- (f y) every time round the loop anyhow.
|
|
| 1994 | + |
|
| 1995 | + -- canInlineInLam => free vars of rhs are (Once in_lam) or Many,
|
|
| 1996 | + -- so substituting rhs inside a lambda doesn't change the occ info.
|
|
| 1997 | + -- Sadly, not quite the same as exprIsHNF.
|
|
| 1998 | +canInlineInLam (Lit _) = True
|
|
| 1999 | +canInlineInLam (Cast e _) = canInlineInLam e
|
|
| 2000 | +canInlineInLam (Lam b e) = isRuntimeVar b || canInlineInLam e
|
|
| 2001 | +canInlineInLam (Tick t e) = not (tickishIsCode t) && canInlineInLam e
|
|
| 2002 | +canInlineInLam (Var v) = case idOccInfo v of
|
|
| 2003 | + OneOcc { occ_in_lam = IsInsideLam } -> True
|
|
| 2004 | + ManyOccs {} -> True
|
|
| 2005 | + _ -> False
|
|
| 2006 | +canInlineInLam _ = False
|
|
| 2007 | + -- not ticks. Counting ticks cannot be duplicated, and non-counting
|
|
| 2008 | + -- ticks around a Lam will disappear anyway.
|
|
| 2009 | + |
|
| 1860 | 2010 | {-
|
| 1861 | 2011 | ************************************************************************
|
| 1862 | 2012 | * *
|
| ... | ... | @@ -4,6 +4,7 @@ Rule fired: Int# -> Integer -> Int# (GHC.Internal.Bignum.Integer) |
| 4 | 4 | Rule fired: USPEC plusTwoRec @Int (T15445a)
|
| 5 | 5 | Rule fired: USPEC $fShowList @Int (GHC.Internal.Show)
|
| 6 | 6 | Rule fired: Class op >> (BUILTIN)
|
| 7 | +Rule fired: Class op show (BUILTIN)
|
|
| 7 | 8 | Rule fired: USPEC plusTwoRec @Int (T15445a)
|
| 8 | 9 | Rule fired: Class op enumFromTo (BUILTIN)
|
| 9 | 10 | Rule fired: eftInt (GHC.Internal.Enum)
|
| ... | ... | @@ -11,7 +12,6 @@ Rule fired: Class op show (BUILTIN) |
| 11 | 12 | Rule fired: USPEC plusTwoRec @Int (T15445a)
|
| 12 | 13 | Rule fired: Class op enumFromTo (BUILTIN)
|
| 13 | 14 | Rule fired: eftInt (GHC.Internal.Enum)
|
| 14 | -Rule fired: Class op show (BUILTIN)
|
|
| 15 | 15 | Rule fired: eftIntList (GHC.Internal.Enum)
|
| 16 | 16 | Rule fired: ># (BUILTIN)
|
| 17 | 17 | Rule fired: ==# (BUILTIN)
|
| 1 | +{-# LANGUAGE MagicHash #-}
|
|
| 2 | + |
|
| 3 | +module T27962 where
|
|
| 4 | + |
|
| 5 | +import GHC.Exts
|
|
| 6 | + |
|
| 7 | +newtype N = MkN Int
|
|
| 8 | + |
|
| 9 | +-- After inlining myRun, we get (runRW# @LiftedRep @Int |> co) (\_ -> ...)
|
|
| 10 | +-- and the simplifier pushes co into the argument of runRW#.
|
|
| 11 | +-- #27962: the simplifier dropped that cast, yielding ill-typed Core
|
|
| 12 | +myRun :: (State# RealWorld -> N) -> N
|
|
| 13 | +myRun = coerce (runRW# :: (State# RealWorld -> Int) -> Int)
|
|
| 14 | +{-# INLINE myRun #-}
|
|
| 15 | + |
|
| 16 | +foo x = myRun (\_ -> MkN (x+1)) |
| 1 | +{-# OPTIONS_GHC -Wno-inline-rule-shadowing #-}
|
|
| 2 | + |
|
| 3 | +module T27963 where
|
|
| 4 | + |
|
| 5 | +-- This test is pretty delicate because it relies on a function
|
|
| 6 | +-- that has BOTH a rule AND an INLINE pragma
|
|
| 7 | +--
|
|
| 8 | +-- See #27963 for some discussion
|
|
| 9 | +--
|
|
| 10 | +-- Still, it should (just) work
|
|
| 11 | + |
|
| 12 | +import GHC.Exts
|
|
| 13 | + |
|
| 14 | +newtype N = MkN Int
|
|
| 15 | + |
|
| 16 | +f x = x
|
|
| 17 | +{-# INLINE f #-}
|
|
| 18 | +{-# RULES "f/42" forall x. f x = 42::Int #-}
|
|
| 19 | + |
|
| 20 | +-- The rule "f/42" should fire in both h1 and h2
|
|
| 21 | +h1,h2 :: N -> Int
|
|
| 22 | +h1 n = f (coerce n) + 0
|
|
| 23 | +h2 n = (coerce f) n + 0 -- #27963: the rule did not fire here |
| 1 | +Rule fired: f/42 (T27963)
|
|
| 2 | +Rule fired: f/42 (T27963) |
| ... | ... | @@ -633,4 +633,3 @@ test('T27962', normal, compile, ['']) |
| 633 | 633 | test('T27962a', [extra_files(['T27962.hs'])], multimod_compile, ['T27962', '-v0 -O'])
|
| 634 | 634 | test('T27963', [grep_errmsg(r'f/42')], compile, ['-O -ddump-rule-firings'])
|
| 635 | 635 | test('T27965', normal, compile, ['']) |
| 636 | - |