isValArg, isTypeArg, valArgCount, valBndrCount, isRuntimeArg, isRuntimeVar,
-- * Unfolding data types
- Unfolding(..), UnfoldingGuidance(..), InlineRuleInfo(..), InlSatFlag(..),
+ Unfolding(..), UnfoldingGuidance(..), UnfoldingSource(..),
-- Abstract everywhere but in CoreUnfold.lhs
-- ** Constructing 'Unfolding's
noUnfolding, evaldUnfolding, mkOtherCon,
+ unSaturatedOk, needSaturated, boringCxtOk, boringCxtNotOk,
-- ** Predicates and deconstruction on 'Unfolding'
- unfoldingTemplate, setUnfoldingTemplate,
+ unfoldingTemplate, setUnfoldingTemplate, expandUnfolding_maybe,
maybeUnfoldingTemplate, otherCons, unfoldingArity,
isValueUnfolding, isEvaldUnfolding, isCheapUnfolding,
- isExpandableUnfolding, isConLikeUnfolding,
+ isExpandableUnfolding, isConLikeUnfolding, isCompulsoryUnfolding,
isInlineRule, isInlineRule_maybe, isClosedUnfolding, hasSomeUnfolding,
- isStableUnfolding, canUnfold, neverUnfoldGuidance,
+ isStableUnfolding, canUnfold, neverUnfoldGuidance, isInlineRuleSource,
-- * Strictness
seqExpr, seqExprs, seqUnfolding,
-- * Core rule data types
CoreRule(..), -- CoreSubst, CoreTidy, CoreFVs, PprCore only
- RuleName,
+ RuleName, IdUnfoldingFun,
-- ** Operations on 'CoreRule's
seqRules, ruleArity, ruleName, ruleIdName, ruleActivation_maybe,
ru_fn :: Name, -- ^ As above
ru_nargs :: Int, -- ^ Number of arguments that 'ru_try' consumes,
-- if it fires, including type arguments
- ru_try :: [CoreExpr] -> Maybe CoreExpr
+ ru_try :: IdUnfoldingFun -> [CoreExpr] -> Maybe CoreExpr
-- ^ This function does the rewrite. It given too many
-- arguments, it simply discards them; the returned 'CoreExpr'
-- is just the rewrite of 'ru_fn' applied to the first 'ru_nargs' args
}
-- See Note [Extra args in rule matching] in Rules.lhs
+type IdUnfoldingFun = Id -> Unfolding
+-- A function that embodies how to unfold an Id if you need
+-- to do that in the Rule. The reason we need to pass this info in
+-- is that whether an Id is unfoldable depends on the simplifier phase
+
isBuiltinRule :: CoreRule -> Bool
isBuiltinRule (BuiltinRule {}) = True
isBuiltinRule _ = False
| CoreUnfolding { -- An unfolding for an Id with no pragma, or perhaps a NOINLINE pragma
-- (For NOINLINE, the phase, if any, is in the InlinePragInfo for this Id.)
- uf_tmpl :: CoreExpr, -- Template; occurrence info is correct
- uf_arity :: Arity, -- Number of value arguments expected
+ uf_tmpl :: CoreExpr, -- Template; occurrence info is correct
+ uf_src :: UnfoldingSource, -- Where the unfolding came from
uf_is_top :: Bool, -- True <=> top level binding
+ uf_arity :: Arity, -- Number of value arguments expected
uf_is_value :: Bool, -- exprIsHNF template (cached); it is ok to discard a `seq` on
-- this variable
uf_is_conlike :: Bool, -- True <=> application of constructor or CONLIKE function
-- uf_guidance: Tells us about the /size/ of the unfolding template
------------------------------------------------
+data UnfoldingSource
+ = InlineCompulsory -- Something that *has* no binding, so you *must* inline it
+ -- Only a few primop-like things have this property
+ -- (see MkId.lhs, calls to mkCompulsoryUnfolding).
+ -- Inline absolutely always, however boring the context.
+
+ | InlineRule -- From an {-# INLINE #-} pragma; See Note [InlineRules]
+
+ | InlineWrapper Id -- This unfolding is a the wrapper in a
+ -- worker/wrapper split from the strictness analyser
+ -- The Id is the worker-id
+ -- Used to abbreviate the uf_tmpl in interface files
+ -- which don't need to contain the RHS;
+ -- it can be derived from the strictness info
+
+ | InlineRhs -- The current rhs of the function
+
+ -- For InlineRhs, the uf_tmpl is replaced each time around
+ -- For all the others we leave uf_tmpl alone
+
+
-- | 'UnfoldingGuidance' says when unfolding should take place
data UnfoldingGuidance
- = InlineRule { -- Be very keen to inline this; See Note [InlineRules]
- -- The uf_tmpl is the *original* RHS; do *not* replace it on
- -- each simlifier run. Hence, the *actual* RHS of the function
- -- may be different by now, because it may have been optimised.
-
- ir_sat :: InlSatFlag,
- ir_info :: InlineRuleInfo
+ = UnfWhen { -- Inline without thinking about the *size* of the uf_tmpl
+ -- Used (a) for small *and* cheap unfoldings
+ -- (b) for INLINE functions
+ -- See Note [INLINE for small functions] in CoreUnfold
+ ug_unsat_ok :: Bool, -- True <=> ok to inline even if unsaturated
+ ug_boring_ok :: Bool -- True <=> ok to inline even if the context is boring
+ -- So True,True means "always"
}
- | UnfoldIfGoodArgs { -- Arose from a normal Id; the info here is the
+ | UnfIfGoodArgs { -- Arose from a normal Id; the info here is the
-- result of a simple analysis of the RHS
ug_args :: [Int], -- Discount if the argument is evaluated.
} -- a context (case (thing args) of ...),
-- (where there are the right number of arguments.)
- | UnfoldNever -- A variant of UnfoldIfGoodArgs, used for big RHSs
-
-data InlineRuleInfo
- = InlAlways -- Inline absolutely always, however boring the context.
- -- There is /no original definition/. Only a few primop-like things
- -- have this property (see MkId.lhs, calls to mkCompulsoryUnfolding).
-
- | InlSmall -- The RHS is very small (eg no bigger than a call), so inline any
- -- /saturated/ application, regardless of context
- -- See Note [INLINE for small functions] in CoreUnfold
+ | UnfNever -- The RHS is big, so don't inline it
- | InlVanilla
+-- Constants for the UnfWhen constructor
+needSaturated, unSaturatedOk :: Bool
+needSaturated = False
+unSaturatedOk = True
- | InlWrapper Id -- This unfolding is a the wrapper in a
- -- worker/wrapper split from the strictness analyser
- -- Used to abbreviate the uf_tmpl in interface files
- -- which don't need to contain the RHS;
- -- it can be derived from the strictness info
- -- [In principle this is orthogonal to the InlSmall/InVanilla thing,
- -- but it's convenient to have it here.]
-
-data InlSatFlag = InlSat | InlUnSat
- -- Specifies whether to INLINE only if the thing is applied to 'arity' args
+boringCxtNotOk, boringCxtOk :: Bool
+boringCxtOk = True
+boringCxtNotOk = False
------------------------------------------------
noUnfolding :: Unfolding
seqUnfolding _ = ()
seqGuidance :: UnfoldingGuidance -> ()
-seqGuidance (UnfoldIfGoodArgs ns n b) = n `seq` sum ns `seq` b `seq` ()
-seqGuidance _ = ()
+seqGuidance (UnfIfGoodArgs ns n b) = n `seq` sum ns `seq` b `seq` ()
+seqGuidance _ = ()
\end{code}
\begin{code}
+isInlineRuleSource :: UnfoldingSource -> Bool
+isInlineRuleSource InlineCompulsory = True
+isInlineRuleSource InlineRule = True
+isInlineRuleSource (InlineWrapper {}) = True
+isInlineRuleSource InlineRhs = False
+
-- | Retrieves the template of an unfolding: panics if none is known
unfoldingTemplate :: Unfolding -> CoreExpr
unfoldingTemplate = uf_tmpl
isExpandableUnfolding (CoreUnfolding { uf_expandable = is_expable }) = is_expable
isExpandableUnfolding _ = False
-isInlineRule :: Unfolding -> Bool
-isInlineRule (CoreUnfolding { uf_guidance = InlineRule {}}) = True
-isInlineRule _ = False
+expandUnfolding_maybe :: Unfolding -> Maybe CoreExpr
+-- Expand an expandable unfolding; this is used in rule matching
+-- See Note [Expanding variables] in Rules.lhs
+-- The key point here is that CONLIKE things can be expanded
+expandUnfolding_maybe (CoreUnfolding { uf_expandable = True, uf_tmpl = rhs }) = Just rhs
+expandUnfolding_maybe _ = Nothing
-isInlineRule_maybe :: Unfolding -> Maybe (InlineRuleInfo, InlSatFlag)
-isInlineRule_maybe (CoreUnfolding { uf_guidance =
- InlineRule { ir_info = inl, ir_sat = sat } }) = Just (inl,sat)
-isInlineRule_maybe _ = Nothing
+isInlineRule :: Unfolding -> Bool
+isInlineRule (CoreUnfolding { uf_src = src }) = isInlineRuleSource src
+isInlineRule _ = False
+
+isInlineRule_maybe :: Unfolding -> Maybe (UnfoldingSource, Bool)
+isInlineRule_maybe (CoreUnfolding { uf_src = src, uf_guidance = guide })
+ | isInlineRuleSource src
+ = Just (src, unsat_ok)
+ where
+ unsat_ok = case guide of
+ UnfWhen unsat_ok _ -> unsat_ok
+ _ -> needSaturated
+isInlineRule_maybe _ = Nothing
+
+isCompulsoryUnfolding :: Unfolding -> Bool
+isCompulsoryUnfolding (CoreUnfolding { uf_src = InlineCompulsory }) = True
+isCompulsoryUnfolding _ = False
isStableUnfolding :: Unfolding -> Bool
-- True of unfoldings that should not be overwritten
-- by a CoreUnfolding for the RHS of a let-binding
-isStableUnfolding (CoreUnfolding { uf_guidance = InlineRule {} }) = True
-isStableUnfolding (DFunUnfolding {}) = True
-isStableUnfolding _ = False
+isStableUnfolding (CoreUnfolding { uf_src = src }) = isInlineRuleSource src
+isStableUnfolding (DFunUnfolding {}) = True
+isStableUnfolding _ = False
unfoldingArity :: Unfolding -> Arity
unfoldingArity (CoreUnfolding { uf_arity = arity }) = arity
hasSomeUnfolding _ = True
neverUnfoldGuidance :: UnfoldingGuidance -> Bool
-neverUnfoldGuidance UnfoldNever = True
-neverUnfoldGuidance _ = False
+neverUnfoldGuidance UnfNever = True
+neverUnfoldGuidance _ = False
canUnfold :: Unfolding -> Bool
canUnfold (CoreUnfolding { uf_guidance = g }) = not (neverUnfoldGuidance g)
canUnfold _ = False
\end{code}
-Note [InlineRule]
+Note [InlineRules]
~~~~~~~~~~~~~~~~~
When you say
{-# INLINE f #-}
you intend that calls (f e) are replaced by <rhs>[e/x] So we
should capture (\x.<rhs>) in the Unfolding of 'f', and never meddle
with it. Meanwhile, we can optimise <rhs> to our heart's content,
-leaving the original unfolding intact in Unfolding of 'f'.
+leaving the original unfolding intact in Unfolding of 'f'. For example
+ all xs = foldr (&&) True xs
+ any p = all . map p {-# INLINE any #-}
+We optimise any's RHS fully, but leave the InlineRule saying "all . map p",
+which deforests well at the call site.
-So the representation of an Unfolding has changed quite a bit
-(see CoreSyn). An INLINE pragma gives rise to an InlineRule
-unfolding.
+So INLINE pragma gives rise to an InlineRule, which captures the original RHS.
Moreover, it's only used when 'f' is applied to the
specified number of arguments; that is, the number of argument on