summaryrefslogtreecommitdiff
path: root/compiler/GHC/Core/Unfold.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/GHC/Core/Unfold.hs')
-rw-r--r--compiler/GHC/Core/Unfold.hs302
1 files changed, 194 insertions, 108 deletions
diff --git a/compiler/GHC/Core/Unfold.hs b/compiler/GHC/Core/Unfold.hs
index 08c5a10b30..c7914c31e1 100644
--- a/compiler/GHC/Core/Unfold.hs
+++ b/compiler/GHC/Core/Unfold.hs
@@ -51,13 +51,15 @@ import GHC.Types.Basic ( Arity )
import GHC.Core.Type
import GHC.Builtin.Names
import GHC.Builtin.Types.Prim ( realWorldStatePrimTy )
-import GHC.Data.Bag
import GHC.Utils.Logger
import GHC.Utils.Misc
import GHC.Utils.Outputable
import GHC.Types.ForeignCall
import GHC.Types.Name
import GHC.Types.Tickish
+import GHC.Types.Var.Env
+import GHC.Types.Var.Set
+import GHC.Types.Unique.FM( nonDetStrictFoldUFM_Directly )
import qualified Data.ByteString as BS
import Data.List (isPrefixOf)
@@ -237,7 +239,7 @@ calcUnfoldingGuidance opts is_top_bottoming (Tick t expr)
calcUnfoldingGuidance opts is_top_bottoming expr
= case sizeExpr opts bOMB_OUT_SIZE val_bndrs body of
TooBig -> UnfNever
- SizeIs size cased_bndrs scrut_discount
+ SizeIs size id_discounts scrut_discount
| uncondInline expr n_val_bndrs size
-> UnfWhen { ug_unsat_ok = unSaturatedOk
, ug_boring_ok = boringCxtOk
@@ -247,10 +249,11 @@ calcUnfoldingGuidance opts is_top_bottoming expr
-> UnfNever -- See Note [Do not inline top-level bottoming functions]
| otherwise
- -> UnfIfGoodArgs { ug_args = map (mk_discount cased_bndrs) val_bndrs
+ -> UnfIfGoodArgs { ug_args = map (lookupDiscount id_discounts) val_bndrs
+ , ug_fvs = mapVarEnv getDiscount $
+ id_discounts `delVarEnvList` val_bndrs
, ug_size = size
, ug_res = scrut_discount }
-
where
(bndrs, body) = collectBinders expr
bOMB_OUT_SIZE = unfoldingCreationThreshold opts
@@ -258,17 +261,7 @@ calcUnfoldingGuidance opts is_top_bottoming expr
val_bndrs = filter isId bndrs
n_val_bndrs = length val_bndrs
- mk_discount :: Bag (Id,Int) -> Id -> Int
- mk_discount cbs bndr = foldl' combine 0 cbs
- where
- combine acc (bndr', disc)
- | bndr == bndr' = acc `plus_disc` disc
- | otherwise = acc
- plus_disc :: Int -> Int -> Int
- plus_disc | isFunTy (idType bndr) = max
- | otherwise = (+)
- -- See Note [Function and non-function discounts]
{- Note [Inline unsafeCoerce]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -424,46 +417,58 @@ sizeExpr :: UnfoldingOpts
-- Forcing bOMB_OUT_SIZE early prevents repeated
-- unboxing of the Int argument.
sizeExpr opts !bOMB_OUT_SIZE top_args expr
- = size_up expr
+ = size_up (mkVarSet top_args, emptyVarSet) expr
where
- size_up (Cast e _) = size_up e
- size_up (Tick _ e) = size_up e
- size_up (Type _) = sizeZero -- Types cost nothing
- size_up (Coercion _) = sizeZero
- size_up (Lit lit) = sizeN (litSize lit)
- size_up (Var f) | isRealWorldId f = sizeZero
+ size_up :: IgnoreSet -> CoreExpr -> ExprSize
+ -- The IdSet are the Ids that we *don't* want to collect
+ -- discount information for; namely, the Ids bound locally in
+ -- the expression
+
+ size_up ig (Cast e _) = size_up ig e
+ size_up ig (Tick _ e) = size_up ig e
+ size_up _ (Type _) = sizeZero -- Types cost nothing
+ size_up _ (Coercion _) = sizeZero
+ size_up _ (Lit lit) = sizeN (litSize lit)
+ size_up ig (Var f) | isRealWorldId f = sizeZero
-- Make sure we get constructor discounts even
-- on nullary constructors
- | otherwise = size_up_call f [] 0
+ | otherwise = size_up_call ig f [] 0
- size_up (App fun arg)
- | isTyCoArg arg = size_up fun
- | otherwise = size_up arg `addSizeNSD`
- size_up_app fun [arg] (if isRealWorldExpr arg then 1 else 0)
+ size_up ig (App fun arg)
+ | isTyCoArg arg = size_up ig fun
+ | otherwise = size_up ig arg `addSizeNSD`
+ size_up_app ig fun [arg] (if isRealWorldExpr arg then 1 else 0)
- size_up (Lam b e)
- | isId b && not (isRealWorldId b) = lamScrutDiscount opts (size_up e `addSizeN` 10)
- | otherwise = size_up e
+ size_up ig (Lam b e)
+ | isId b && not (isRealWorldId b) = lamScrutDiscount opts (size_up ig' e `addSizeN` 10)
+ | otherwise = size_up ig' e
+ where
+ ig' = ig `extendIgnore` b
- size_up (Let (NonRec binder rhs) body)
- = size_up_rhs (binder, rhs) `addSizeNSD`
- size_up body `addSizeN`
+ size_up ig (Let (NonRec binder rhs) body)
+ = size_up_rhs ig (binder, rhs) `addSizeNSD`
+ size_up ig' body `addSizeN`
size_up_alloc binder
+ where
+ ig' = ig `extendIgnore` binder
- size_up (Let (Rec pairs) body)
- = foldr (addSizeNSD . size_up_rhs)
- (size_up body `addSizeN` sum (map (size_up_alloc . fst) pairs))
+ size_up ig (Let (Rec pairs) body)
+ = foldr (addSizeNSD . size_up_rhs ig')
+ (size_up ig' body `addSizeN` sum (map (size_up_alloc . fst) pairs))
pairs
+ where
+ ig' = ig `extendIgnoreList` map fst pairs
- size_up (Case e _ _ alts)
+ size_up ig (Case e bndr _ alts)
| null alts
- = size_up e -- case e of {} never returns, so take size of scrutinee
+ = size_up (ig `extendIgnore` bndr) e
+ -- case e of {} never returns, so take size of scrutinee
- size_up (Case e _ _ alts)
+ size_up ig (Case e bndr _ alts)
-- Now alts is non-empty
- | Just v <- is_top_arg e -- We are scrutinising an argument variable
+ | Just v <- is_var e -- We are scrutinising a variable
= let
- alt_sizes = map size_up_alt alts
+ alt_sizes = map (size_up_alt ig') alts
-- alts_size tries to compute a good discount for
-- the case when we are scrutinising an argument variable
@@ -471,17 +476,17 @@ sizeExpr opts !bOMB_OUT_SIZE top_args expr
-- Size of all alternatives
(SizeIs max _ _)
-- Size of biggest alternative
- = SizeIs tot (unitBag (v, 20 + tot - max)
- `unionBags` tot_disc) tot_scrut
+ = SizeIs tot (unitDisc ig v (CaseDisc (20 + tot - max))
+ `addDiscs` tot_disc) tot_scrut
-- If the variable is known, we produce a
-- discount that will take us back to 'max',
-- the size of the largest alternative The
- -- 1+ is a little discount for reduced
+ -- 20+ is a little discount for reduced
-- allocation in the caller
--
-- Notice though, that we return tot_disc,
- -- the total discount from all branches. I
- -- think that's right.
+ -- the total discount from all branches.
+ -- I think that's right.
alts_size tot_size _ = tot_size
in
@@ -491,14 +496,18 @@ sizeExpr opts !bOMB_OUT_SIZE top_args expr
-- that may eliminate allocation in the caller
-- And it eliminates the case itself
where
- is_top_arg (Var v) | v `elem` top_args = Just v
- is_top_arg (Cast e _) = is_top_arg e
- is_top_arg _ = Nothing
+ ig' = ig `extendIgnore` bndr
+ is_var (Var v) = Just v
+ is_var (Cast e _) = is_var e
+ is_var _ = Nothing
- size_up (Case e _ _ alts) = size_up e `addSizeNSD`
- foldr (addAltSize . size_up_alt) case_size alts
+
+ size_up ig (Case e bndr _ alts) = size_up ig' e `addSizeNSD`
+ foldr (addAltSize . size_up_alt ig') case_size alts
where
+ ig' = ig `extendIgnore` bndr
+
case_size
| is_inline_scrut e, lengthAtMost alts 1 = sizeN (-10)
| otherwise = sizeZero
@@ -532,42 +541,44 @@ sizeExpr opts !bOMB_OUT_SIZE top_args expr
| otherwise
= False
- size_up_rhs (bndr, rhs)
+ size_up_rhs ig (bndr, rhs)
| Just join_arity <- isJoinId_maybe bndr
-- Skip arguments to join point
- , (_bndrs, body) <- collectNBinders join_arity rhs
- = size_up body
+ , (bndrs, body) <- collectNBinders join_arity rhs
+ = size_up (ig `extendIgnoreList` bndrs) body
| otherwise
- = size_up rhs
+ = size_up ig rhs
------------
-- size_up_app is used when there's ONE OR MORE value args
- size_up_app (App fun arg) args voids
- | isTyCoArg arg = size_up_app fun args voids
- | isRealWorldExpr arg = size_up_app fun (arg:args) (voids + 1)
- | otherwise = size_up arg `addSizeNSD`
- size_up_app fun (arg:args) voids
- size_up_app (Var fun) args voids = size_up_call fun args voids
- size_up_app (Tick _ expr) args voids = size_up_app expr args voids
- size_up_app (Cast expr _) args voids = size_up_app expr args voids
- size_up_app other args voids = size_up other `addSizeN`
- callSize (length args) voids
+ size_up_app ig (App fun arg) args voids
+ | isTyCoArg arg = size_up_app ig fun args voids
+ | isRealWorldExpr arg = size_up_app ig fun (arg:args) (voids + 1)
+ | otherwise = size_up ig arg `addSizeNSD`
+ size_up_app ig fun (arg:args) voids
+ size_up_app ig (Var fun) args voids = size_up_call ig fun args voids
+ size_up_app ig (Tick _ expr) args voids = size_up_app ig expr args voids
+ size_up_app ig (Cast expr _) args voids = size_up_app ig expr args voids
+ size_up_app ig other args voids = size_up ig other `addSizeN`
+ callSize (length args) voids
-- if the lhs is not an App or a Var, or an invisible thing like a
-- Tick or Cast, then we should charge for a complete call plus the
-- size of the lhs itself.
------------
- size_up_call :: Id -> [CoreExpr] -> Int -> ExprSize
- size_up_call fun val_args voids
+ size_up_call :: IgnoreSet -> Id -> [CoreExpr] -> Int -> ExprSize
+ size_up_call ig fun val_args voids
= case idDetails fun of
FCallId _ -> sizeN (callSize (length val_args) voids)
DataConWorkId dc -> conSize dc (length val_args)
PrimOpId op -> primOpSize op (length val_args)
- ClassOpId _ -> classOpSize opts top_args val_args
- _ -> funSize opts top_args fun (length val_args) voids
+ ClassOpId _ -> classOpSize opts ig val_args
+ _ -> funSize opts ig fun (length val_args) voids
------------
- size_up_alt (Alt _con _bndrs rhs) = size_up rhs `addSizeN` 10
+ size_up_alt ig (Alt _con bndrs rhs) = size_up ig' rhs `addSizeN` 10
+ where
+ ig' = ig `extendIgnoreList` bndrs
-- Don't charge for args, so that wrappers look cheap
-- (See comments about wrappers with Case)
--
@@ -591,26 +602,28 @@ sizeExpr opts !bOMB_OUT_SIZE top_args expr
addSizeN TooBig _ = TooBig
addSizeN (SizeIs n xs d) m = mkSizeIs bOMB_OUT_SIZE (n + m) xs d
- -- addAltSize is used to add the sizes of case alternatives
- addAltSize TooBig _ = TooBig
+ -- addAltSize is used to add the sizes of case alternatives
+ -- The /second/ argument is expected to be the bigger one; force it first
addAltSize _ TooBig = TooBig
+ addAltSize TooBig _ = TooBig
addAltSize (SizeIs n1 xs d1) (SizeIs n2 ys d2)
= mkSizeIs bOMB_OUT_SIZE (n1 + n2)
- (xs `unionBags` ys)
+ (xs `addDiscs` ys)
(d1 + d2) -- Note [addAltSize result discounts]
- -- This variant ignores the result discount from its LEFT argument
- -- It's used when the second argument isn't part of the result
- addSizeNSD TooBig _ = TooBig
+ -- This variant ignores the result discount from its FIRST argument
+ -- It's used when the first argument isn't part of the result
+ -- The second argument is also expected to be bigger: force it first
addSizeNSD _ TooBig = TooBig
+ addSizeNSD TooBig _ = TooBig
addSizeNSD (SizeIs n1 xs _) (SizeIs n2 ys d2)
= mkSizeIs bOMB_OUT_SIZE (n1 + n2)
- (xs `unionBags` ys)
+ (xs `addDiscs` ys)
d2 -- Ignore d1
isRealWorldId id = idType id `eqType` realWorldStatePrimTy
- -- an expression of type State# RealWorld must be a variable
+ -- An expression of type State# RealWorld must be a variable
isRealWorldExpr (Var id) = isRealWorldId id
isRealWorldExpr (Tick _ e) = isRealWorldExpr e
isRealWorldExpr _ = False
@@ -627,11 +640,11 @@ litSize _other = 0 -- Must match size of nullary constructors
-- Key point: if x |-> 4, then x must inline unconditionally
-- (eg via case binding)
-classOpSize :: UnfoldingOpts -> [Id] -> [CoreExpr] -> ExprSize
+classOpSize :: UnfoldingOpts -> IgnoreSet -> [CoreExpr] -> ExprSize
-- See Note [Conlike is interesting]
classOpSize _ _ []
= sizeZero
-classOpSize opts top_args (arg1 : other_args)
+classOpSize opts ig (arg1 : other_args)
= SizeIs size arg_discount 0
where
size = 20 + (10 * length other_args)
@@ -639,9 +652,8 @@ classOpSize opts top_args (arg1 : other_args)
-- give it a discount, to encourage the inlining of this function
-- The actual discount is rather arbitrarily chosen
arg_discount = case arg1 of
- Var dict | dict `elem` top_args
- -> unitBag (dict, unfoldingDictDiscount opts)
- _other -> emptyBag
+ Var dict -> unitDisc ig dict (CaseDisc (unfoldingDictDiscount opts))
+ _other -> emptyIdDiscounts
-- | The size of a function call
callSize
@@ -664,10 +676,10 @@ jumpSize n_val_args voids = 2 * (1 + n_val_args - voids)
-- spectral/puzzle. TODO Perhaps adjusting the default threshold would be a
-- better solution?
-funSize :: UnfoldingOpts -> [Id] -> Id -> Int -> Int -> ExprSize
+funSize :: UnfoldingOpts -> IgnoreSet -> Id -> Int -> Int -> ExprSize
-- Size for functions that are not constructors or primops
-- Note [Function applications]
-funSize opts top_args fun n_val_args voids
+funSize opts ig fun n_val_args voids
| fun `hasKey` buildIdKey = buildSize
| fun `hasKey` augmentIdKey = augmentSize
| otherwise = SizeIs size arg_discount res_discount
@@ -681,9 +693,9 @@ funSize opts top_args fun n_val_args voids
-- DISCOUNTS
-- See Note [Function and non-function discounts]
- arg_discount | some_val_args && fun `elem` top_args
- = unitBag (fun, unfoldingFunAppDiscount opts)
- | otherwise = emptyBag
+ arg_discount | some_val_args
+ = unitDisc ig fun (AppDisc (unfoldingFunAppDiscount opts))
+ | otherwise = emptyIdDiscounts
-- If the function is an argument and is applied
-- to some values, give it an arg-discount
@@ -694,13 +706,13 @@ funSize opts top_args fun n_val_args voids
conSize :: DataCon -> Int -> ExprSize
conSize dc n_val_args
- | n_val_args == 0 = SizeIs 0 emptyBag 10 -- Like variables
+ | n_val_args == 0 = SizeIs 0 emptyIdDiscounts 10 -- Like variables
-- See Note [Unboxed tuple size and result discount]
- | isUnboxedTupleDataCon dc = SizeIs 0 emptyBag 10
+ | isUnboxedTupleDataCon dc = SizeIs 0 emptyIdDiscounts 10
-- See Note [Constructor size and result discount]
- | otherwise = SizeIs 10 emptyBag 10
+ | otherwise = SizeIs 10 emptyIdDiscounts 10
{- Note [Constructor size and result discount]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -778,8 +790,8 @@ the function to a constructor application, so we *want* a big discount
if the argument is scrutinised by many case expressions.
Conclusion:
- - For functions, take the max of the discounts
- - For data values, take the sum of the discounts
+ - For functions, take the max of the discounts (AppDisc)
+ - For data values, take the sum of the discounts (CaseDisc)
Note [Literal integer size]
@@ -804,7 +816,7 @@ primOpSize op n_val_args
buildSize :: ExprSize
-buildSize = SizeIs 0 emptyBag 40
+buildSize = SizeIs 0 emptyIdDiscounts 40
-- We really want to inline applications of build
-- build t (\cn -> e) should cost only the cost of e (because build will be inlined later)
-- Indeed, we should add a result_discount because build is
@@ -813,14 +825,14 @@ buildSize = SizeIs 0 emptyBag 40
-- The "4" is rather arbitrary.
augmentSize :: ExprSize
-augmentSize = SizeIs 0 emptyBag 40
+augmentSize = SizeIs 0 emptyIdDiscounts 40
-- Ditto (augment t (\cn -> e) ys) should cost only the cost of
-- e plus ys. The -2 accounts for the \cn
-- When we return a lambda, give a discount if it's used (applied)
lamScrutDiscount :: UnfoldingOpts -> ExprSize -> ExprSize
lamScrutDiscount opts (SizeIs n vs _) = SizeIs n vs (unfoldingFunAppDiscount opts)
-lamScrutDiscount _ TooBig = TooBig
+lamScrutDiscount _ TooBig = TooBig
{-
Note [addAltSize result discounts]
@@ -895,13 +907,71 @@ Code for manipulating sizes
data ExprSize
= TooBig
| SizeIs { _es_size_is :: {-# UNPACK #-} !Int -- ^ Size found
- , _es_args :: !(Bag (Id,Int))
+
+ , _es_args :: !IdDiscounts
-- ^ Arguments cased herein, and discount for each such
+
, _es_discount :: {-# UNPACK #-} !Int
-- ^ Size to subtract if result is scrutinised by a case
-- expression
}
+type IgnoreSet
+ = ( IdSet -- Lambda-bound binders for this unfolding
+ , IdSet ) -- Locally-bound binders, within this unfolding
+
+type IdDiscounts = IdEnv IdDiscount
+
+
+lookupDiscount :: IdDiscounts -> Id -> Int
+lookupDiscount discounts bndr
+ = case lookupVarEnv discounts bndr of
+ Just d -> getDiscount d
+ Nothing -> 0
+
+emptyIdDiscounts :: IdDiscounts
+emptyIdDiscounts = emptyVarEnv
+
+extendIgnore :: IgnoreSet -> Id -> IgnoreSet
+extendIgnore (tops,locals) v = (tops, locals `extendVarSet` v)
+
+extendIgnoreList :: IgnoreSet -> [Id] -> IgnoreSet
+extendIgnoreList (tops,locals) vs = (tops, locals `extendVarSetList` vs)
+
+unitDisc :: IgnoreSet -> Id -> IdDiscount -> IdDiscounts
+-- Record a discount for the use of an Id
+-- But not if it is
+-- (a) a GlobalId
+-- (b) bound locally within the function body we are analysing
+-- (c) an AppDisc for a free variable
+-- (d) has no unfolding
+unitDisc (top_args, ignore_these) v disc
+ | isLocalId v
+ , not (v `elemVarSet` ignore_these)
+ , case disc of { AppDisc _ -> v `elemVarSet` top_args
+ ; CaseDisc {} -> True }
+ , not (hasCoreUnfolding (idUnfolding v))
+ = unitVarEnv v disc
+ | otherwise
+ = emptyIdDiscounts
+
+addDiscs :: IdDiscounts -> IdDiscounts -> IdDiscounts
+addDiscs = plusVarEnv_C addIdDiscount
+
+data IdDiscount
+ = CaseDisc {-# UNPACK #-} !Int
+ | AppDisc {-# UNPACK #-} !Int -- See Note [Function and non-function discounts]
+
+getDiscount :: IdDiscount -> Int
+getDiscount (CaseDisc n) = n
+getDiscount (AppDisc n) = n
+
+addIdDiscount :: IdDiscount -> IdDiscount -> IdDiscount
+addIdDiscount (CaseDisc n1) (CaseDisc n2) = CaseDisc (n1+n2)
+addIdDiscount (CaseDisc n1) (AppDisc n2) = AppDisc (n1 `max` n2)
+addIdDiscount (AppDisc n1) (CaseDisc n2) = AppDisc (n1 `max` n2)
+addIdDiscount (AppDisc n1) (AppDisc n2) = AppDisc (n1 `max` n2)
+
instance Outputable ExprSize where
ppr TooBig = text "TooBig"
ppr (SizeIs a _ c) = brackets (int a <+> int c)
@@ -911,7 +981,7 @@ instance Outputable ExprSize where
-- tup = (a_1, ..., a_99)
-- x = case tup of ...
--
-mkSizeIs :: Int -> Int -> Bag (Id, Int) -> Int -> ExprSize
+mkSizeIs :: Int -> Int -> IdDiscounts -> Int -> ExprSize
mkSizeIs max n xs d | (n - d) > max = TooBig
| otherwise = SizeIs n xs d
@@ -924,8 +994,8 @@ maxSize s1@(SizeIs n1 _ _) s2@(SizeIs n2 _ _) | n1 > n2 = s1
sizeZero :: ExprSize
sizeN :: Int -> ExprSize
-sizeZero = SizeIs 0 emptyBag 0
-sizeN n = SizeIs n emptyBag 0
+sizeZero = SizeIs 0 emptyIdDiscounts 0
+sizeN n = SizeIs n emptyIdDiscounts 0
{-
************************************************************************
@@ -1019,6 +1089,7 @@ instance Outputable CallCtxt where
callSiteInline :: Logger
-> UnfoldingOpts
+ -> InScopeSet
-> Int -- Case depth
-> Id -- The Id
-> Bool -- True <=> unfolding is active
@@ -1026,7 +1097,8 @@ callSiteInline :: Logger
-> [ArgSummary] -- One for each value arg; True if it is interesting
-> CallCtxt -- True <=> continuation is interesting
-> Maybe CoreExpr -- Unfolding, if any
-callSiteInline logger opts !case_depth id active_unfolding lone_variable arg_infos cont_info
+callSiteInline logger opts in_scope !case_depth id
+ active_unfolding lone_variable arg_infos cont_info
= case idUnfolding id of
-- idUnfolding checks for loop-breakers, returning NoUnfolding
-- Things with an INLINE pragma may have an unfolding *and*
@@ -1034,7 +1106,7 @@ callSiteInline logger opts !case_depth id active_unfolding lone_variable arg_inf
CoreUnfolding { uf_tmpl = unf_template
, uf_is_work_free = is_wf
, uf_guidance = guidance, uf_expandable = is_exp }
- | active_unfolding -> tryUnfolding logger opts case_depth id lone_variable
+ | active_unfolding -> tryUnfolding logger opts in_scope case_depth id lone_variable
arg_infos cont_info unf_template
is_wf is_exp guidance
| otherwise -> traceInline logger opts id "Inactive unfolding:" (ppr id) Nothing
@@ -1157,10 +1229,11 @@ needed on a per-module basis.
-}
-tryUnfolding :: Logger -> UnfoldingOpts -> Int -> Id -> Bool -> [ArgSummary] -> CallCtxt
+tryUnfolding :: Logger -> UnfoldingOpts -> InScopeSet -> Int -> Id
+ -> Bool -> [ArgSummary] -> CallCtxt
-> CoreExpr -> Bool -> Bool -> UnfoldingGuidance
-> Maybe CoreExpr
-tryUnfolding logger opts !case_depth id lone_variable
+tryUnfolding logger opts in_scope !case_depth id lone_variable
arg_infos cont_info unf_template
is_wf is_exp guidance
= case guidance of
@@ -1176,7 +1249,8 @@ tryUnfolding logger opts !case_depth id lone_variable
some_benefit = calc_some_benefit uf_arity
enough_args = (n_val_args >= uf_arity) || (unsat_ok && n_val_args > 0)
- UnfIfGoodArgs { ug_args = arg_discounts, ug_res = res_discount, ug_size = size }
+ UnfIfGoodArgs { ug_args = arg_discounts, ug_fvs = fv_discounts
+ , ug_res = res_discount, ug_size = size }
| unfoldingVeryAggressive opts
-> traceInline logger opts id str (mk_doc some_benefit extra_doc True) (Just unf_template)
| is_wf && some_benefit && small_enough
@@ -1195,7 +1269,8 @@ tryUnfolding logger opts !case_depth id lone_variable
| otherwise = (size * (case_depth - depth_treshold)) `div` depth_scaling
adjusted_size = size + depth_penalty - discount
small_enough = adjusted_size <= unfoldingUseThreshold opts
- discount = computeDiscount arg_discounts res_discount arg_infos cont_info
+ discount = computeDiscount in_scope arg_discounts fv_discounts res_discount
+ arg_infos cont_info
where
mk_doc some_benefit extra_doc yes_or_no
@@ -1441,9 +1516,13 @@ which Roman did.
-}
-computeDiscount :: [Int] -> Int -> [ArgSummary] -> CallCtxt
+computeDiscount :: InScopeSet
+ -> [Int] -- Argument discounts
+ -> VarEnv Int -- Free-variable discounts
+ -> Int -> [ArgSummary] -> CallCtxt
-> Int
-computeDiscount arg_discounts res_discount arg_infos cont_info
+computeDiscount in_scope arg_discounts fv_discounts res_discount
+ arg_infos cont_info
= 10 -- Discount of 10 because the result replaces the call
-- so we count 10 for the function itself
@@ -1452,10 +1531,17 @@ computeDiscount arg_discounts res_discount arg_infos cont_info
-- Discount of 10 for each arg supplied,
-- because the result replaces the call
- + total_arg_discount + res_discount'
+ + total_arg_discount + fv_discount + res_discount'
where
actual_arg_discounts = zipWith mk_arg_discount arg_discounts arg_infos
total_arg_discount = sum actual_arg_discounts
+ fv_discount = nonDetStrictFoldUFM_Directly add_fv 0 fv_discounts
+ add_fv uniq disc tot_disc
+ | Just v <- lookupInScope_Directly in_scope uniq
+ , hasCoreUnfolding (idUnfolding v)
+ = disc + tot_disc
+ | otherwise
+ = disc
mk_arg_discount _ TrivArg = 0
mk_arg_discount _ NonTrivArg = 10