Simon Peyton Jones pushed to branch wip/T26615 at Glasgow Haskell Compiler / GHC
Commits:
-
0e70ef52
by Simon Peyton Jones at 2025-11-29T23:21:57+00:00
6 changed files:
- compiler/GHC/Core/Opt/OccurAnal.hs
- compiler/GHC/Core/Opt/Simplify/Iteration.hs
- compiler/GHC/Core/Opt/Simplify/Utils.hs
- compiler/GHC/Core/Opt/SpecConstr.hs
- compiler/GHC/Core/Opt/Specialise.hs
- compiler/GHC/Core/Utils.hs
Changes:
| ... | ... | @@ -28,7 +28,7 @@ core expression with (hopefully) improved usage information. |
| 28 | 28 | module GHC.Core.Opt.OccurAnal (
|
| 29 | 29 | occurAnalysePgm,
|
| 30 | 30 | occurAnalyseExpr,
|
| 31 | - zapLambdaBndrs, BinderSwapDecision(..), scrutOkForBinderSwap
|
|
| 31 | + zapLambdaBndrs
|
|
| 32 | 32 | ) where
|
| 33 | 33 | |
| 34 | 34 | import GHC.Prelude hiding ( head, init, last, tail )
|
| ... | ... | @@ -36,7 +36,7 @@ import GHC.Prelude hiding ( head, init, last, tail ) |
| 36 | 36 | import GHC.Core
|
| 37 | 37 | import GHC.Core.FVs
|
| 38 | 38 | import GHC.Core.Utils ( exprIsTrivial, isDefaultAlt, isExpandableApp,
|
| 39 | - mkCastMCo, mkTicks )
|
|
| 39 | + mkCastMCo, mkTicks, BinderSwapDecision(..), scrutOkForBinderSwap )
|
|
| 40 | 40 | import GHC.Core.Opt.Arity ( joinRhsArity, isOneShotBndr )
|
| 41 | 41 | import GHC.Core.Coercion
|
| 42 | 42 | import GHC.Core.Type
|
| ... | ... | @@ -3446,6 +3446,7 @@ doesn't use it. So this is only to satisfy the perhaps-over-picky Lint. |
| 3446 | 3446 | -}
|
| 3447 | 3447 | |
| 3448 | 3448 | addBndrSwap :: OutExpr -> Id -> OccEnv -> OccEnv
|
| 3449 | +-- See Note [Binder swap]
|
|
| 3449 | 3450 | -- See Note [The binder-swap substitution]
|
| 3450 | 3451 | addBndrSwap scrut case_bndr
|
| 3451 | 3452 | env@(OccEnv { occ_bs_env = swap_env, occ_bs_rng = rng_vars })
|
| ... | ... | @@ -3463,27 +3464,6 @@ addBndrSwap scrut case_bndr |
| 3463 | 3464 | case_bndr' = zapIdOccInfo case_bndr
|
| 3464 | 3465 | -- See Note [Zap case binders in proxy bindings]
|
| 3465 | 3466 | |
| 3466 | --- | See bBinderSwaOk.
|
|
| 3467 | -data BinderSwapDecision
|
|
| 3468 | - = NoBinderSwap
|
|
| 3469 | - | DoBinderSwap OutVar MCoercion
|
|
| 3470 | - |
|
| 3471 | -scrutOkForBinderSwap :: OutExpr -> BinderSwapDecision
|
|
| 3472 | --- If (scrutOkForBinderSwap e = DoBinderSwap v mco, then
|
|
| 3473 | --- v = e |> mco
|
|
| 3474 | --- See Note [Case of cast]
|
|
| 3475 | --- See Historical Note [Care with binder-swap on dictionaries]
|
|
| 3476 | ---
|
|
| 3477 | --- We use this same function in SpecConstr, and Simplify.Iteration,
|
|
| 3478 | --- when something binder-swap-like is happening
|
|
| 3479 | -scrutOkForBinderSwap e
|
|
| 3480 | - = case e of
|
|
| 3481 | - Tick _ e -> scrutOkForBinderSwap e -- Drop ticks
|
|
| 3482 | - Var v -> DoBinderSwap v MRefl
|
|
| 3483 | - Cast (Var v) co -> DoBinderSwap v (MCo (mkSymCo co))
|
|
| 3484 | - -- Cast: see Note [Case of cast]
|
|
| 3485 | - _ -> NoBinderSwap
|
|
| 3486 | - |
|
| 3487 | 3467 | lookupBndrSwap :: OccEnv -> Id -> (CoreExpr, Id)
|
| 3488 | 3468 | -- See Note [The binder-swap substitution]
|
| 3489 | 3469 | -- Returns an expression of the same type as Id
|
| ... | ... | @@ -22,7 +22,7 @@ import GHC.Core.TyCo.Compare( eqType ) |
| 22 | 22 | import GHC.Core.Opt.Simplify.Env
|
| 23 | 23 | import GHC.Core.Opt.Simplify.Inline
|
| 24 | 24 | import GHC.Core.Opt.Simplify.Utils
|
| 25 | -import GHC.Core.Opt.OccurAnal ( occurAnalyseExpr, zapLambdaBndrs, scrutOkForBinderSwap, BinderSwapDecision (..) )
|
|
| 25 | +import GHC.Core.Opt.OccurAnal ( occurAnalyseExpr, zapLambdaBndrs )
|
|
| 26 | 26 | import GHC.Core.Make ( FloatBind, mkImpossibleExpr, castBottomExpr )
|
| 27 | 27 | import qualified GHC.Core.Make
|
| 28 | 28 | import GHC.Core.Coercion hiding ( substCo, substCoVar )
|
| ... | ... | @@ -2693,7 +2693,7 @@ mkCase, mkCase1, mkCase2, mkCase3 |
| 2693 | 2693 | |
| 2694 | 2694 | mkCase mode scrut outer_bndr alts_ty alts
|
| 2695 | 2695 | | sm_case_merge mode
|
| 2696 | - , Just (joins, alts') <- mergeCaseAlts outer_bndr alts
|
|
| 2696 | + , Just (joins, alts') <- mergeCaseAlts scrut outer_bndr alts
|
|
| 2697 | 2697 | = do { tick (CaseMerge outer_bndr)
|
| 2698 | 2698 | ; case_expr <- mkCase1 mode scrut outer_bndr alts_ty alts'
|
| 2699 | 2699 | ; return (mkLets joins case_expr) }
|
| ... | ... | @@ -30,7 +30,6 @@ import GHC.Core.Opt.Simplify.Inline |
| 30 | 30 | import GHC.Core.FVs ( exprsFreeVarsList, exprFreeVars )
|
| 31 | 31 | import GHC.Core.Opt.Monad
|
| 32 | 32 | import GHC.Core.Opt.WorkWrap.Utils
|
| 33 | -import GHC.Core.Opt.OccurAnal( BinderSwapDecision(..), scrutOkForBinderSwap )
|
|
| 34 | 33 | import GHC.Core.DataCon
|
| 35 | 34 | import GHC.Core.Class( classTyVars )
|
| 36 | 35 | import GHC.Core.Coercion hiding( substCo )
|
| ... | ... | @@ -2026,9 +2025,9 @@ spec_one env fn arg_bndrs body (call_pat, rule_number) |
| 2026 | 2025 | -- , text "spec_call_args" <+> ppr spec_call_args
|
| 2027 | 2026 | -- , text "rule_rhs" <+> ppr rule_rhs
|
| 2028 | 2027 | -- , text "adds_void_worker_arg" <+> ppr add_void_arg
|
| 2029 | ----- , text "body" <+> ppr body
|
|
| 2030 | ----- , text "spec_rhs" <+> ppr spec_rhs
|
|
| 2031 | ----- , text "how_bound" <+> ppr (sc_how_bound env) ]
|
|
| 2028 | +-- , text "body" <+> ppr body
|
|
| 2029 | +-- , text "spec_rhs" <+> ppr spec_rhs
|
|
| 2030 | +-- , text "how_bound" <+> ppr (sc_how_bound env) ]
|
|
| 2032 | 2031 | -- ]
|
| 2033 | 2032 | ; return (spec_usg, OS { os_pat = call_pat, os_rule = rule
|
| 2034 | 2033 | , os_id = spec_id
|
| ... | ... | @@ -1798,8 +1798,7 @@ specCalls spec_imp env existing_rules calls_for_me fn rhs |
| 1798 | 1798 | , text "rule_act" <+> ppr rule_act
|
| 1799 | 1799 | ]
|
| 1800 | 1800 | |
| 1801 | - ; pprTrace "spec_call: rule" _rule_trace_doc
|
|
| 1802 | - return ( spec_rule : rules_acc
|
|
| 1801 | + ; return ( spec_rule : rules_acc
|
|
| 1803 | 1802 | , (spec_fn, spec_rhs1) : pairs_acc
|
| 1804 | 1803 | , rhs_uds2 `thenUDs` uds_acc
|
| 1805 | 1804 | ) } }
|
| ... | ... | @@ -19,6 +19,7 @@ module GHC.Core.Utils ( |
| 19 | 19 | mergeAlts, mergeCaseAlts, trimConArgs,
|
| 20 | 20 | filterAlts, combineIdenticalAlts, refineDefaultAlt,
|
| 21 | 21 | scaleAltsBy,
|
| 22 | + BinderSwapDecision(..), scrutOkForBinderSwap,
|
|
| 22 | 23 | |
| 23 | 24 | -- * Properties of expressions
|
| 24 | 25 | exprType, coreAltType, coreAltsType,
|
| ... | ... | @@ -117,7 +118,6 @@ import Data.Function ( on ) |
| 117 | 118 | import Data.List ( sort, sortBy, partition, zipWith4, mapAccumL )
|
| 118 | 119 | import qualified Data.List as Partial ( init, last )
|
| 119 | 120 | import Data.Ord ( comparing )
|
| 120 | -import Control.Monad ( guard )
|
|
| 121 | 121 | import qualified Data.Set as Set
|
| 122 | 122 | |
| 123 | 123 | {-
|
| ... | ... | @@ -591,6 +591,28 @@ The default alternative must be first, if it exists at all. |
| 591 | 591 | This makes it easy to find, though it makes matching marginally harder.
|
| 592 | 592 | -}
|
| 593 | 593 | |
| 594 | +data BinderSwapDecision
|
|
| 595 | + = NoBinderSwap
|
|
| 596 | + | DoBinderSwap OutVar MCoercion
|
|
| 597 | + |
|
| 598 | +scrutOkForBinderSwap :: OutExpr -> BinderSwapDecision
|
|
| 599 | +-- If (scrutOkForBinderSwap e = DoBinderSwap v mco, then
|
|
| 600 | +-- v = e |> mco
|
|
| 601 | +-- See Note [Case of cast]
|
|
| 602 | +-- See Historical Note [Care with binder-swap on dictionaries]
|
|
| 603 | +--
|
|
| 604 | +-- We use this same function in SpecConstr, and Simplify.Iteration,
|
|
| 605 | +-- when something binder-swap-like is happening
|
|
| 606 | +--
|
|
| 607 | +-- See Note [Binder swap] in GHC.Core.Opt.OccurAnal
|
|
| 608 | +scrutOkForBinderSwap e
|
|
| 609 | + = case e of
|
|
| 610 | + Tick _ e -> scrutOkForBinderSwap e -- Drop ticks
|
|
| 611 | + Var v -> DoBinderSwap v MRefl
|
|
| 612 | + Cast (Var v) co -> DoBinderSwap v (MCo (mkSymCo co))
|
|
| 613 | + -- Cast: see Note [Case of cast]
|
|
| 614 | + _ -> NoBinderSwap
|
|
| 615 | + |
|
| 594 | 616 | -- | Extract the default case alternative
|
| 595 | 617 | findDefault :: [Alt b] -> ([Alt b], Maybe (Expr b))
|
| 596 | 618 | findDefault (Alt DEFAULT args rhs : alts) = assert (null args) (alts, Just rhs)
|
| ... | ... | @@ -652,9 +674,9 @@ filters down the matching alternatives in GHC.Core.Opt.Simplify.rebuildCase. |
| 652 | 674 | -}
|
| 653 | 675 | |
| 654 | 676 | ---------------------------------
|
| 655 | -mergeCaseAlts :: Id -> [CoreAlt] -> Maybe ([CoreBind], [CoreAlt])
|
|
| 677 | +mergeCaseAlts :: CoreExpr -> Id -> [CoreAlt] -> Maybe ([CoreBind], [CoreAlt])
|
|
| 656 | 678 | -- See Note [Merge Nested Cases]
|
| 657 | -mergeCaseAlts outer_bndr (Alt DEFAULT _ deflt_rhs : outer_alts)
|
|
| 679 | +mergeCaseAlts scrut outer_bndr (Alt DEFAULT _ deflt_rhs : outer_alts)
|
|
| 658 | 680 | | Just (joins, inner_alts) <- go deflt_rhs
|
| 659 | 681 | = Just (joins, mergeAlts outer_alts inner_alts)
|
| 660 | 682 | -- NB: mergeAlts gives priority to the left
|
| ... | ... | @@ -666,6 +688,9 @@ mergeCaseAlts outer_bndr (Alt DEFAULT _ deflt_rhs : outer_alts) |
| 666 | 688 | -- When we merge, we must ensure that e1 takes
|
| 667 | 689 | -- precedence over e2 as the value for A!
|
| 668 | 690 | where
|
| 691 | + bndr_swap :: BinderSwapDecision
|
|
| 692 | + bndr_swap = scrutOkForBinderSwap scrut
|
|
| 693 | + |
|
| 669 | 694 | go :: CoreExpr -> Maybe ([CoreBind], [CoreAlt])
|
| 670 | 695 | |
| 671 | 696 | -- Whizzo: we can merge!
|
| ... | ... | @@ -704,16 +729,9 @@ mergeCaseAlts outer_bndr (Alt DEFAULT _ deflt_rhs : outer_alts) |
| 704 | 729 | |
| 705 | 730 | -- Check for capture; but only if we could otherwise do a merge
|
| 706 | 731 | -- (i.e. the recursive `go` succeeds)
|
| 707 | - -- "Capture" means
|
|
| 708 | - -- (a) case x of r { DEFAULT -> join r = ... in ...r... }
|
|
| 709 | - -- (b) case x of r { DEFAULT -> join j = ...r.. in ... }
|
|
| 710 | - -- In both cases we can't float the join point out
|
|
| 711 | - -- because r changes its meaning
|
|
| 712 | - ; let capture = outer_bndr `elem` bindersOf bind -- (a)
|
|
| 713 | - || outer_bndr `elemVarSet` bindFreeVars bind -- (b)
|
|
| 714 | - ; guard (not capture)
|
|
| 715 | - |
|
| 716 | - ; return (bind:joins, alts ) }
|
|
| 732 | + ; fix_up_binds <- okToFloatJoin bndr_swap outer_bndr bind
|
|
| 733 | + |
|
| 734 | + ; return (fix_up_binds ++ (bind : joins), alts ) }
|
|
| 717 | 735 | | otherwise
|
| 718 | 736 | = Nothing
|
| 719 | 737 | |
| ... | ... | @@ -725,7 +743,25 @@ mergeCaseAlts outer_bndr (Alt DEFAULT _ deflt_rhs : outer_alts) |
| 725 | 743 | |
| 726 | 744 | go _ = Nothing
|
| 727 | 745 | |
| 728 | -mergeCaseAlts _ _ = Nothing
|
|
| 746 | +mergeCaseAlts _ _ _ = Nothing
|
|
| 747 | + |
|
| 748 | +okToFloatJoin :: BinderSwapDecision -> Id -> CoreBind -> Maybe [CoreBind]
|
|
| 749 | +-- Check a join-point binding to see if it can be floated out of
|
|
| 750 | +-- the DEFAULT branch of a `case`. A Just result means "yes",
|
|
| 751 | +-- and the [CoreBInd] are the extra fix-up bindings to add.
|
|
| 752 | +-- See Note [Floating join points out of DEFAULT alternatives]
|
|
| 753 | +okToFloatJoin bndr_swap outer_bndr bind
|
|
| 754 | + | outer_bndr `elem` bindersOf bind -- (a)
|
|
| 755 | + = Nothing
|
|
| 756 | + | outer_bndr `elemVarSet` bindFreeVars bind -- (b)
|
|
| 757 | + = case bndr_swap of
|
|
| 758 | + DoBinderSwap scrut_var mco
|
|
| 759 | + | scrut_var /= outer_bndr
|
|
| 760 | + -> Just [ NonRec outer_bndr (mkCastMCo (Var scrut_var) mco) ]
|
|
| 761 | + _ -> Nothing
|
|
| 762 | + | otherwise
|
|
| 763 | + = Just []
|
|
| 764 | + |
|
| 729 | 765 | |
| 730 | 766 | ---------------------------------
|
| 731 | 767 | mergeAlts :: [Alt a] -> [Alt a] -> [Alt a]
|
| ... | ... | @@ -934,10 +970,43 @@ Wrinkles |
| 934 | 970 | non-join-points unless the /outer/ case has just one alternative; doing
|
| 935 | 971 | so would risk more allocation
|
| 936 | 972 | |
| 973 | + Floating out join points isn't entirely straightforward.
|
|
| 974 | + See Note [Floating join points out of DEFAULT alternatives]
|
|
| 975 | + |
|
| 937 | 976 | (MC5) See Note [Cascading case merge]
|
| 938 | 977 | |
| 939 | 978 | See also Note [Example of case-merging and caseRules] in GHC.Core.Opt.Simplify.Utils
|
| 940 | 979 | |
| 980 | +Note [Floating join points out of DEFAULT alternatives]
|
|
| 981 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 982 | +Consider this, from (MC4) of Note [Merge Nested Cases]
|
|
| 983 | + case x of r
|
|
| 984 | + DEFAULT -> join j = rhs in case r of ...
|
|
| 985 | + alts
|
|
| 986 | + |
|
| 987 | +We want to float that join point out to give this
|
|
| 988 | + join j = rhs
|
|
| 989 | + case x of r
|
|
| 990 | + DEFAULT -> case r of ...
|
|
| 991 | + alts
|
|
| 992 | + |
|
| 993 | +But doing so is flat-out wrong if the scoping gets messed up:
|
|
| 994 | + (a) case x of r { DEFAULT -> join r = ... in ...r... }
|
|
| 995 | + (b) case x of r { DEFAULT -> join j = ...r.. in ... }
|
|
| 996 | +In both cases we can't float the join point out because r changes its meaning.
|
|
| 997 | + |
|
| 998 | +BUT we can fix up case (b) by adding an extra binding, like this
|
|
| 999 | + let r = x in
|
|
| 1000 | + join j = rhs[r]
|
|
| 1001 | + case x of r
|
|
| 1002 | + DEFAULT -> ...r...
|
|
| 1003 | + ...other alts...
|
|
| 1004 | + |
|
| 1005 | +This extra binding is figured out by `okToFloatJoin`.
|
|
| 1006 | + |
|
| 1007 | +Note that the cases that still don't work (e.g. (a)) will probably work fine the
|
|
| 1008 | +next iteration of the Simplifier, because they involve shadowing, and the Simplifier
|
|
| 1009 | +generally eliminates shadowing.
|
|
| 941 | 1010 | |
| 942 | 1011 | Note [Cascading case merge]
|
| 943 | 1012 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
|