Simon Peyton Jones pushed to branch wip/T26989 at Glasgow Haskell Compiler / GHC
Commits:
-
39d59acd
by Simon Peyton Jones at 2026-03-17T10:26:25+00:00
4 changed files:
- compiler/GHC/Core/Opt/Simplify/Iteration.hs
- compiler/GHC/Core/Opt/Simplify/Utils.hs
- compiler/GHC/Core/Opt/Specialise.hs
- compiler/GHC/Core/Rules.hs
Changes:
| ... | ... | @@ -40,7 +40,7 @@ import GHC.Core.Opt.Arity ( ArityType, exprArity, arityTypeBotSigs_maybe |
| 40 | 40 | , typeArity, arityTypeArity, etaExpandAT )
|
| 41 | 41 | import GHC.Core.SimpleOpt ( exprIsConApp_maybe, joinPointBinding_maybe, joinPointBindings_maybe )
|
| 42 | 42 | import GHC.Core.FVs ( mkRuleInfo {- exprsFreeIds -} )
|
| 43 | -import GHC.Core.Rules ( lookupRule, getRules )
|
|
| 43 | +import GHC.Core.Rules ( lookupRule, getRules, RuleMatch(..) )
|
|
| 44 | 44 | import GHC.Core.Multiplicity
|
| 45 | 45 | |
| 46 | 46 | import GHC.Hs.Extension
|
| ... | ... | @@ -413,9 +413,9 @@ simplAuxBind _str env bndr new_rhs |
| 413 | 413 | -- and more direct to focus on the "hot" cases.
|
| 414 | 414 | -- e.g. auxiliary bindings have no NOLINE pragmas, RULEs, or stable unfoldings
|
| 415 | 415 | | exprIsTrivial new_rhs -- Short-cut for let x = y in ...
|
| 416 | - || case (idOccInfo bndr) of
|
|
| 417 | - OneOcc{ occ_n_br = 1, occ_in_lam = NotInsideLam } -> True
|
|
| 418 | - _ -> False
|
|
| 416 | +-- || case (idOccInfo bndr) of
|
|
| 417 | +-- OneOcc{ occ_n_br = 1, occ_in_lam = NotInsideLam } -> True
|
|
| 418 | +-- _ -> False
|
|
| 419 | 419 | = return ( emptyFloats env
|
| 420 | 420 | , extendCvIdSubst env bndr new_rhs ) -- bndr can be a CoVar
|
| 421 | 421 | |
| ... | ... | @@ -2361,8 +2361,9 @@ simplOutId env fun cont |
| 2361 | 2361 | out_args = contOutArgs env cont :: [OutExpr]
|
| 2362 | 2362 | ; mb_match <- if not (null rules_for_me) &&
|
| 2363 | 2363 | (isClassOpId fun || activeUnfolding (seMode env) fun)
|
| 2364 | - then tryRules env rules_for_me fun out_args
|
|
| 2364 | + then tryRules False env rules_for_me fun out_args
|
|
| 2365 | 2365 | else return Nothing
|
| 2366 | + |
|
| 2366 | 2367 | ; case mb_match of {
|
| 2367 | 2368 | Just (rule_arity, rhs) -> simplExprF env rhs $
|
| 2368 | 2369 | dropContArgs rule_arity cont ;
|
| ... | ... | @@ -2455,7 +2456,7 @@ rebuildCall env (ArgInfo { ai_fun = fun, ai_args = rev_args, ai_rules = rules }) |
| 2455 | 2456 | = rebuild env (argInfoExpr fun rev_args) cont
|
| 2456 | 2457 | | otherwise -- Try rules again: Plan (AFTER) in Note [When to apply rewrite rules]
|
| 2457 | 2458 | = do { let args = reverse rev_args
|
| 2458 | - ; mb_match <- tryRules env rules fun (map argSpecArg args)
|
|
| 2459 | + ; mb_match <- tryRules True env rules fun (map argSpecArg args)
|
|
| 2459 | 2460 | ; case mb_match of
|
| 2460 | 2461 | Just (rule_arity, rhs) -> simplExprF env rhs $
|
| 2461 | 2462 | pushSimplifiedArgs env (drop rule_arity args) cont
|
| ... | ... | @@ -2641,17 +2642,20 @@ See Note [No free join points in arityType] in GHC.Core.Opt.Arity |
| 2641 | 2642 | ************************************************************************
|
| 2642 | 2643 | -}
|
| 2643 | 2644 | |
| 2644 | -tryRules :: SimplEnv -> [CoreRule]
|
|
| 2645 | - -> OutId -> [OutExpr]
|
|
| 2645 | +tryRules :: Bool -- True <=> args are already simplified
|
|
| 2646 | + -> SimplEnv -> [CoreRule]
|
|
| 2647 | + -> OutId -> [CoreExpr]
|
|
| 2646 | 2648 | -> SimplM (Maybe (FullArgCount, CoreExpr))
|
| 2647 | 2649 | |
| 2648 | -tryRules env rules fn args
|
|
| 2649 | - | Just (rule, rule_rhs) <- lookupRule ropts in_scope_env
|
|
| 2650 | - act_fun fn args rules
|
|
| 2650 | +tryRules args_are_simplified env rules fn args
|
|
| 2651 | + | Just (RM { rm_rule = rule, rm_rhs = rule_rhs })
|
|
| 2652 | + <- -- pprTrace ("tryRules "++show args_are_simplified) (ppr fn) $
|
|
| 2653 | + lookupRule ropts in_scope_env act_fun fn args rules
|
|
| 2651 | 2654 | -- Fire a rule for the function
|
| 2652 | 2655 | = do { logger <- getLogger
|
| 2653 | 2656 | ; checkedTick (RuleFired (ruleName rule))
|
| 2654 | - ; let occ_anald_rhs = occurAnalyseExpr rule_rhs
|
|
| 2657 | + ; let occ_anald_rhs | args_are_simplified = rule_rhs
|
|
| 2658 | + | otherwise = occurAnalyseExpr rule_rhs
|
|
| 2655 | 2659 | -- See Note [Occurrence-analyse after rule firing]
|
| 2656 | 2660 | ; dump logger rule rule_rhs
|
| 2657 | 2661 | ; return (Just (ruleArity rule, occ_anald_rhs)) }
|
| ... | ... | @@ -2718,7 +2722,7 @@ trySeqRules :: SimplEnv |
| 2718 | 2722 | trySeqRules in_env scrut rhs cont
|
| 2719 | 2723 | = do { rule_base <- getSimplRules
|
| 2720 | 2724 | ; let seq_rules = getRules rule_base seqId
|
| 2721 | - ; mb_match <- tryRules in_env seq_rules seqId out_args
|
|
| 2725 | + ; mb_match <- tryRules True in_env seq_rules seqId out_args
|
|
| 2722 | 2726 | ; case mb_match of
|
| 2723 | 2727 | Nothing -> return Nothing
|
| 2724 | 2728 | Just (rule_arity, rhs) -> return (Just (rhs, cont'))
|
| ... | ... | @@ -1715,6 +1715,7 @@ postInlineUnconditionally env bind_cxt old_bndr bndr rhs |
| 1715 | 1715 | | n_br >= 100 -> False -- See #23627
|
| 1716 | 1716 | |
| 1717 | 1717 | | n_br == 1, NotInsideLam <- in_lam -- One syntactic occurrence
|
| 1718 | + , unf_is_small
|
|
| 1718 | 1719 | -> True -- See Note [Post-inline for single-use things]
|
| 1719 | 1720 | |
| 1720 | 1721 | -- | is_unlifted -- Unlifted binding, hence ok-for-spec
|
| ... | ... | @@ -1728,7 +1729,7 @@ postInlineUnconditionally env bind_cxt old_bndr bndr rhs |
| 1728 | 1729 | -- so inlining duplicates code but nothing more
|
| 1729 | 1730 | |
| 1730 | 1731 | | otherwise
|
| 1731 | - -> work_ok in_lam int_cxt && (n_br == 1 || smallEnoughToInline uf_opts unfolding)
|
|
| 1732 | + -> work_ok in_lam int_cxt && ({- n_br == 1 || -} unf_is_small)
|
|
| 1732 | 1733 | -- Multiple syntactic occurences; but lazy, and small enough to dup
|
| 1733 | 1734 | -- ToDo: consider discount on smallEnoughToInline if int_cxt is true
|
| 1734 | 1735 | |
| ... | ... | @@ -1740,6 +1741,8 @@ postInlineUnconditionally env bind_cxt old_bndr bndr rhs |
| 1740 | 1741 | _ -> False
|
| 1741 | 1742 | |
| 1742 | 1743 | where
|
| 1744 | + unf_is_small = smallEnoughToInline uf_opts unfolding
|
|
| 1745 | + |
|
| 1743 | 1746 | work_ok NotInsideLam _ = True
|
| 1744 | 1747 | work_ok IsInsideLam IsInteresting = isCheapUnfolding unfolding
|
| 1745 | 1748 | work_ok IsInsideLam NotInteresting = False
|
| ... | ... | @@ -1266,7 +1266,7 @@ fireRewriteRules :: SpecEnv -- Substitution is already zapped |
| 1266 | 1266 | -> OutExpr -> [OutExpr] -> (OutExpr, [OutExpr])
|
| 1267 | 1267 | fireRewriteRules env (Var f) args
|
| 1268 | 1268 | | let rules = getRules (se_rules env) f
|
| 1269 | - , Just (rule, expr) <- specLookupRule env f args activeInInitialPhase rules
|
|
| 1269 | + , Just (RM { rm_rule = rule, rm_rhs = expr }) <- specLookupRule env f args activeInInitialPhase rules
|
|
| 1270 | 1270 | , let rest_args = drop (ruleArity rule) args -- See Note [Extra args in the target]
|
| 1271 | 1271 | zapped_subst = se_subst env -- Just needed for the InScopeSet
|
| 1272 | 1272 | expr' = simpleOptExprWith defaultSimpleOpts zapped_subst (mkApps expr rest_args)
|
| ... | ... | @@ -1801,7 +1801,7 @@ alreadyCovered :: SpecEnv |
| 1801 | 1801 | alreadyCovered env bndrs fn args is_active rules
|
| 1802 | 1802 | = case specLookupRule env fn args is_active rules of
|
| 1803 | 1803 | Nothing -> False
|
| 1804 | - Just (rule, _)
|
|
| 1804 | + Just (RM { rm_rule = rule })
|
|
| 1805 | 1805 | | isAutoRule rule -> -- Discard identical rules
|
| 1806 | 1806 | -- We know that (fn args) is an instance of RULE
|
| 1807 | 1807 | -- Check if RULE is an instance of (fn args)
|
| ... | ... | @@ -1815,7 +1815,7 @@ alreadyCovered env bndrs fn args is_active rules |
| 1815 | 1815 | specLookupRule :: HasDebugCallStack
|
| 1816 | 1816 | => SpecEnv -> Id -> [CoreExpr]
|
| 1817 | 1817 | -> (ActivationGhc -> Bool) -- Which rules are active
|
| 1818 | - -> [CoreRule] -> Maybe (CoreRule, CoreExpr)
|
|
| 1818 | + -> [CoreRule] -> Maybe RuleMatch
|
|
| 1819 | 1819 | specLookupRule env fn args is_active rules
|
| 1820 | 1820 | | null rules
|
| 1821 | 1821 | = Nothing -- Saves building a few thunks in the common case
|
| ... | ... | @@ -10,6 +10,7 @@ |
| 10 | 10 | module GHC.Core.Rules (
|
| 11 | 11 | -- ** Looking up rules
|
| 12 | 12 | lookupRule, matchExprs, ruleLhsIsMoreSpecific,
|
| 13 | + RuleMatch(..),
|
|
| 13 | 14 | |
| 14 | 15 | -- ** RuleBase, RuleEnv
|
| 15 | 16 | RuleBase, RuleEnv(..), mkRuleEnv, emptyRuleEnv,
|
| ... | ... | @@ -542,6 +543,14 @@ map. |
| 542 | 543 | ************************************************************************
|
| 543 | 544 | -}
|
| 544 | 545 | |
| 546 | +data RuleMatch = RM { rm_rule :: CoreRule
|
|
| 547 | + , rm_rhs :: CoreExpr }
|
|
| 548 | + -- Rule `rm_rule` matches, and the result is `rm_rhs`
|
|
| 549 | + --
|
|
| 550 | + -- Example: RULE forall x y. f (g y x) = h [x,y]
|
|
| 551 | + -- If the target is (f (g e1 e2)), the RuleMatch has
|
|
| 552 | + -- rm_rhs = let {x = e1; y = e2 } in h [x,y]
|
|
| 553 | + |
|
| 545 | 554 | -- | The main rule matching function. Attempts to apply all (active)
|
| 546 | 555 | -- supplied rules to this instance of an application in a given
|
| 547 | 556 | -- context, returning the rule applied and the resulting expression if
|
| ... | ... | @@ -552,7 +561,7 @@ lookupRule :: HasDebugCallStack |
| 552 | 561 | -> Id -- Function head
|
| 553 | 562 | -> [CoreExpr] -- Args
|
| 554 | 563 | -> [CoreRule] -- Rules
|
| 555 | - -> Maybe (CoreRule, CoreExpr)
|
|
| 564 | + -> Maybe RuleMatch
|
|
| 556 | 565 | |
| 557 | 566 | -- See Note [Extra args in the target]
|
| 558 | 567 | -- See comments on matchRule
|
| ... | ... | @@ -570,11 +579,11 @@ lookupRule opts rule_env@(ISE in_scope _) is_active fn args rules |
| 570 | 579 | args' = map (stripTicksTopE tickishFloatable) args
|
| 571 | 580 | ticks = concatMap (stripTicksTopT tickishFloatable) args
|
| 572 | 581 | |
| 573 | - go :: [(CoreRule,CoreExpr)] -> [CoreRule] -> [(CoreRule,CoreExpr)]
|
|
| 582 | + go :: [RuleMatch] -> [CoreRule] -> [RuleMatch]
|
|
| 574 | 583 | go ms [] = ms
|
| 575 | 584 | go ms (r:rs)
|
| 576 | - | Just e <- matchRule opts rule_env is_active fn args' rough_args r
|
|
| 577 | - = go ((r,mkTicks ticks e):ms) rs
|
|
| 585 | + | Just rhs <- matchRule opts rule_env is_active fn args' rough_args r
|
|
| 586 | + = go (RM { rm_rule = r, rm_rhs = mkTicks ticks rhs } : ms) rs
|
|
| 578 | 587 | | otherwise
|
| 579 | 588 | = -- pprTrace "match failed" (ppr r $$ ppr args $$
|
| 580 | 589 | -- ppr [ (arg_id, maybeUnfoldingTemplate unf)
|
| ... | ... | @@ -584,15 +593,17 @@ lookupRule opts rule_env@(ISE in_scope _) is_active fn args rules |
| 584 | 593 | go ms rs
|
| 585 | 594 | |
| 586 | 595 | findBest :: InScopeSet -> (Id, [CoreExpr])
|
| 587 | - -> (CoreRule,CoreExpr) -> [(CoreRule,CoreExpr)] -> (CoreRule,CoreExpr)
|
|
| 596 | + -> RuleMatch -> [RuleMatch] -> RuleMatch
|
|
| 588 | 597 | -- All these pairs matched the expression
|
| 589 | 598 | -- Return the pair the most specific rule
|
| 590 | 599 | -- The (fn,args) is just for overlap reporting
|
| 591 | 600 | |
| 592 | -findBest _ _ (rule,ans) [] = (rule,ans)
|
|
| 593 | -findBest in_scope target (rule1,ans1) ((rule2,ans2):prs)
|
|
| 594 | - | ruleIsMoreSpecific in_scope rule1 rule2 = findBest in_scope target (rule1,ans1) prs
|
|
| 595 | - | ruleIsMoreSpecific in_scope rule2 rule1 = findBest in_scope target (rule2,ans2) prs
|
|
| 601 | +findBest _ _ rm [] = rm
|
|
| 602 | +findBest in_scope target@(fn,args)
|
|
| 603 | + rm1@(RM { rm_rule = rule1 })
|
|
| 604 | + (rm2@(RM { rm_rule = rule2 }) : prs)
|
|
| 605 | + | ruleIsMoreSpecific in_scope rule1 rule2 = findBest in_scope target rm1 prs
|
|
| 606 | + | ruleIsMoreSpecific in_scope rule2 rule1 = findBest in_scope target rm2 prs
|
|
| 596 | 607 | | debugIsOn = let pp_rule rule
|
| 597 | 608 | = ifPprDebug (ppr rule)
|
| 598 | 609 | (doubleQuotes (ftext (ruleName rule)))
|
| ... | ... | @@ -602,10 +613,8 @@ findBest in_scope target (rule1,ans1) ((rule2,ans2):prs) |
| 602 | 613 | <+> sep (map ppr args)
|
| 603 | 614 | , text "Rule 1:" <+> pp_rule rule1
|
| 604 | 615 | , text "Rule 2:" <+> pp_rule rule2]) $
|
| 605 | - findBest in_scope target (rule1,ans1) prs
|
|
| 606 | - | otherwise = findBest in_scope target (rule1,ans1) prs
|
|
| 607 | - where
|
|
| 608 | - (fn,args) = target
|
|
| 616 | + findBest in_scope target rm1 prs
|
|
| 617 | + | otherwise = findBest in_scope target rm1 prs
|
|
| 609 | 618 | |
| 610 | 619 | ruleIsMoreSpecific :: InScopeSet -> CoreRule -> CoreRule -> Bool
|
| 611 | 620 | -- The call (rule1 `ruleIsMoreSpecific` rule2)
|
| ... | ... | @@ -684,7 +693,7 @@ matchRule :: HasDebugCallStack |
| 684 | 693 | -> Id -> [CoreExpr] -> [Maybe Name]
|
| 685 | 694 | -> CoreRule -> Maybe CoreExpr
|
| 686 | 695 | |
| 687 | --- If (matchRule rule args) returns Just (name,rhs)
|
|
| 696 | +-- If (matchRule rule args) returns Just rhs
|
|
| 688 | 697 | -- then (f args) matches the rule, and the corresponding
|
| 689 | 698 | -- rewritten RHS is rhs
|
| 690 | 699 | --
|
| ... | ... | @@ -709,25 +718,31 @@ matchRule :: HasDebugCallStack |
| 709 | 718 | -- NB: The 'surplus' argument e4 in the input is simply dropped.
|
| 710 | 719 | -- See Note [Extra args in the target]
|
| 711 | 720 | |
| 712 | -matchRule opts rule_env _is_active fn args _rough_args
|
|
| 721 | +matchRule opts ise _is_active fn args _rough_args
|
|
| 713 | 722 | (BuiltinRule { ru_try = match_fn })
|
| 714 | - | not (roBuiltinRules opts) = Nothing
|
|
| 715 | - | otherwise = match_fn opts rule_env fn args
|
|
| 716 | 723 | |
| 717 | -matchRule _ rule_env is_active _ args rough_args
|
|
| 718 | - (Rule { ru_name = rule_name, ru_act = act, ru_rough = tpl_tops
|
|
| 719 | - , ru_bndrs = tpl_vars, ru_args = tpl_args, ru_rhs = rhs })
|
|
| 720 | - | not (is_active act) = Nothing
|
|
| 721 | - | ruleCantMatch tpl_tops rough_args = Nothing
|
|
| 722 | - | otherwise = matchN rule_env rule_name tpl_vars tpl_args args rhs
|
|
| 724 | + | roBuiltinRules opts = match_fn opts ise fn args
|
|
| 725 | + | otherwise = Nothing
|
|
| 723 | 726 | |
| 727 | +matchRule _ ise is_active _ target_args rough_args
|
|
| 728 | + (Rule { ru_act = act, ru_rough = tpl_tops
|
|
| 729 | + , ru_bndrs = tpl_vars, ru_args = tpl_args, ru_rhs = rhs })
|
|
| 730 | + | is_active act
|
|
| 731 | + , not (ruleCantMatch tpl_tops rough_args)
|
|
| 732 | + , Just (bind_wrapper, matched_es) <- matchExprs ise tpl_vars tpl_args target_args
|
|
| 733 | + = Just (bind_wrapper $
|
|
| 734 | + mkLams tpl_vars rhs `mkApps` matched_es )
|
|
| 735 | + -- NB: We cannot say:
|
|
| 736 | + -- mkLets [NonRec b e | (b,e) <- zip tpl_vars matched_es] rhs
|
|
| 737 | + -- because that brings tpl_vars into scope in a way that
|
|
| 738 | + -- shadows the matched_es. Bad bad.
|
|
| 739 | + | otherwise
|
|
| 740 | + = Nothing
|
|
| 724 | 741 | |
| 725 | 742 | ---------------------------------------
|
| 726 | -matchN :: HasDebugCallStack
|
|
| 727 | - => InScopeEnv
|
|
| 728 | - -> RuleName -> [Var] -> [CoreExpr]
|
|
| 729 | - -> [CoreExpr] -> CoreExpr -- ^ Target; can have more elements than the template
|
|
| 730 | - -> Maybe CoreExpr
|
|
| 743 | +matchExprs :: HasDebugCallStack
|
|
| 744 | + => InScopeEnv -> [Var] -> [CoreExpr] -> [CoreExpr]
|
|
| 745 | + -> Maybe (BindWrapper, [CoreExpr]) -- 1-1 with the [Var]
|
|
| 731 | 746 | -- For a given match template and context, find bindings to wrap around
|
| 732 | 747 | -- the entire result and what should be substituted for each template variable.
|
| 733 | 748 | --
|
| ... | ... | @@ -737,15 +752,6 @@ matchN :: HasDebugCallStack |
| 737 | 752 | -- If there are too /many/ actual arguments, we simply ignore the
|
| 738 | 753 | -- trailing ones, returning the result of applying the rule to a prefix
|
| 739 | 754 | -- of the actual arguments.
|
| 740 | - |
|
| 741 | -matchN ise _rule_name tmpl_vars tmpl_es target_es rhs
|
|
| 742 | - = do { (bind_wrapper, matched_es) <- matchExprs ise tmpl_vars tmpl_es target_es
|
|
| 743 | - ; return (bind_wrapper $
|
|
| 744 | - mkLams tmpl_vars rhs `mkApps` matched_es) }
|
|
| 745 | - |
|
| 746 | -matchExprs :: HasDebugCallStack
|
|
| 747 | - => InScopeEnv -> [Var] -> [CoreExpr] -> [CoreExpr]
|
|
| 748 | - -> Maybe (BindWrapper, [CoreExpr]) -- 1-1 with the [Var]
|
|
| 749 | 755 | matchExprs (ISE in_scope id_unf) tmpl_vars tmpl_es target_es
|
| 750 | 756 | = do { rule_subst <- match_exprs init_menv emptyRuleSubst tmpl_es target_es
|
| 751 | 757 | ; let (_, matched_es) = mapAccumL (lookup_tmpl rule_subst)
|
| ... | ... | @@ -806,7 +812,7 @@ match_exprs :: HasDebugCallStack |
| 806 | 812 | -> [CoreExpr] -- Targets
|
| 807 | 813 | -> Maybe RuleSubst
|
| 808 | 814 | -- If the targets are longer than templates, succeed, simply ignoring
|
| 809 | --- the leftover targets. This matters in the call in matchN.
|
|
| 815 | +-- the leftover targets. This matters in the call in matchExprs.
|
|
| 810 | 816 | --
|
| 811 | 817 | -- Precondition: corresponding elements of es1 and es2 have the same
|
| 812 | 818 | -- type, assuming earlier elements match.
|