Skip to content

Commit

Permalink
Add constant inlining to UPLC (#5790)
Browse files Browse the repository at this point in the history
* Add constant inlining to UPLC

Signed-off-by: Ana Pantilie <[email protected]>

* Regenerate UPLC test output

Signed-off-by: Ana Pantilie <[email protected]>

* Regenerate benchmark test outputs

Signed-off-by: Ana Pantilie <[email protected]>

---------

Signed-off-by: Ana Pantilie <[email protected]>
  • Loading branch information
ana-pantilie authored Feb 20, 2024
1 parent d748384 commit 6d5e14c
Show file tree
Hide file tree
Showing 9 changed files with 48 additions and 26 deletions.
4 changes: 2 additions & 2 deletions plutus-benchmark/nofib/test/9.6/clausify-F5.budget.golden
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
({cpu: 86149266650
| mem: 341062680})
({cpu: 86149243650
| mem: 341062580})
2 changes: 1 addition & 1 deletion plutus-benchmark/nofib/test/9.6/clausify-F5.size.golden
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1633
1627
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module UntypedPlutusCore.Simplify (
soMaxCseIterations,
soInlineHints,
soConservativeOpts,
soInlineConstants,
defaultSimplifyOpts,
InlineHints (..),
) where
Expand All @@ -36,6 +37,7 @@ data SimplifyOpts name a = SimplifyOpts
, _soMaxCseIterations :: Int
, _soConservativeOpts :: Bool
, _soInlineHints :: InlineHints name a
, _soInlineConstants :: Bool
}
deriving stock (Show)

Expand All @@ -48,6 +50,7 @@ defaultSimplifyOpts =
, _soMaxCseIterations = 4
, _soConservativeOpts = False
, _soInlineHints = mempty
, _soInlineConstants = True
}

simplifyProgram ::
Expand Down Expand Up @@ -83,7 +86,7 @@ simplifyTerm opts =
>=> pure . forceDelayCancel
>=> pure . caseOfCase'
>=> pure . caseReduce
>=> inline (_soInlineHints opts)
>=> inline (_soInlineConstants opts) (_soInlineHints opts)

caseOfCase' :: Term name uni fun a -> Term name uni fun a
caseOfCase' = case eqT @fun @DefaultFun of
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,12 @@ type InliningConstraints name uni fun =
)

-- See Note [Differences from PIR inliner] 2
data InlineInfo name a = InlineInfo {_usages :: Usages.Usages, _hints :: InlineHints name a}
data InlineInfo name a = InlineInfo
{ _iiUsages :: Usages.Usages
, _iiHints :: InlineHints name a
, _iiInlineConstants :: Bool
}
makeLenses ''InlineInfo

-- Using a concrete monad makes a very large difference to the performance of this module
-- (determined from profiling)
Expand Down Expand Up @@ -166,13 +171,15 @@ See Note [Inlining and global uniqueness]
inline ::
forall name uni fun m a.
(ExternalConstraints name uni fun m) =>
Bool ->
-- ^ inline constants
InlineHints name a ->
Term name uni fun a ->
m (Term name uni fun a)
inline hints t =
inline inlineConstants hints t =
let
inlineInfo :: InlineInfo name a
inlineInfo = InlineInfo usgs hints
inlineInfo = InlineInfo usgs hints inlineConstants
usgs :: Usages.Usages
usgs = Usages.termUsages t
in
Expand Down Expand Up @@ -274,7 +281,7 @@ maybeAddSubst body a n rhs0 = do
rhs <- processTerm rhs0

-- Check whether we've been told specifically to inline this
hints <- asks _hints
hints <- view iiHints
let hinted = shouldInline hints a n

if hinted -- if we've been told specifically, then do it right away
Expand All @@ -299,7 +306,8 @@ shouldUnconditionallyInline ::
InlineM name uni fun a Bool
shouldUnconditionallyInline n rhs body = do
isTermPure <- checkPurity rhs
preUnconditional isTermPure ||^ postUnconditional isTermPure
inlineConstants <- view iiInlineConstants
preUnconditional isTermPure ||^ postUnconditional inlineConstants isTermPure
where
-- similar to the paper, preUnconditional inlining checks that the binder is 'OnceSafe'.
-- I.e., it's used at most once AND it neither duplicate code or work.
Expand All @@ -310,7 +318,8 @@ shouldUnconditionallyInline n rhs body = do
-- See Note [Inlining approach and 'Secrets of the GHC Inliner'] and [Inlining and
-- purity]. This is the case where we don't know that the number of occurrences is
-- exactly one, so there's no point checking if the term is immediately evaluated.
postUnconditional isTermPure = pure isTermPure &&^ acceptable rhs
postUnconditional inlineConstants isTermPure =
pure isTermPure &&^ acceptable inlineConstants rhs

-- | Check if term is pure. See Note [Inlining and purity]
checkPurity :: Term name uni fun a -> InlineM name uni fun a Bool
Expand All @@ -322,7 +331,7 @@ nameUsedAtMostOnce ::
name ->
InlineM name uni fun a Bool
nameUsedAtMostOnce n = do
usgs <- asks _usages
usgs <- view iiUsages
-- 'inlining' terms used 0 times is a cheap way to remove dead code while we're here
pure $ Usages.getUsageCount n usgs <= 1

Expand Down Expand Up @@ -360,10 +369,14 @@ effectSafe body n purity = do
{- | Should we inline? Should only inline things that won't duplicate work or code.
See Note [Inlining approach and 'Secrets of the GHC Inliner']
-}
acceptable :: Term name uni fun a -> InlineM name uni fun a Bool
acceptable t =
acceptable ::
Bool ->
-- ^ inline constants
Term name uni fun a ->
InlineM name uni fun a Bool
acceptable inlineConstants t =
-- See Note [Inlining criteria]
pure $ costIsAcceptable t && sizeIsAcceptable t
pure $ costIsAcceptable t && sizeIsAcceptable inlineConstants t

{- | Is the cost increase (in terms of evaluation work) of inlining a variable whose RHS is
the given term acceptable?
Expand Down Expand Up @@ -392,8 +405,12 @@ costIsAcceptable = \case
{- | Is the size increase (in the AST) of inlining a variable whose RHS is
the given term acceptable?
-}
sizeIsAcceptable :: Term name uni fun a -> Bool
sizeIsAcceptable = \case
sizeIsAcceptable ::
Bool ->
-- ^ inline constants
Term name uni fun a ->
Bool
sizeIsAcceptable inlineConstants = \case
Builtin{} -> True
Var{} -> True
Error{} -> True
Expand All @@ -402,16 +419,16 @@ sizeIsAcceptable = \case
-- Inlining constructors of size 1 or 0 seems okay
Constr _ _ es -> case es of
[] -> True
[e] -> sizeIsAcceptable e
[e] -> sizeIsAcceptable inlineConstants e
_ -> False
-- Cases are pretty big, due to the case branches
Case{} -> False
-- Constants can be big! We could check the size here and inline if they're
-- small, but probably not worth it
Constant{} -> False
-- Inlining constants is deemed acceptable if the 'inlineConstants'
-- flag is turned on, see Note [Inlining constants].
Constant{} -> inlineConstants
Apply{} -> False
Force _ t -> sizeIsAcceptable t
Delay _ t -> sizeIsAcceptable t
Force _ t -> sizeIsAcceptable inlineConstants t
Delay _ t -> sizeIsAcceptable inlineConstants t

-- | Fully apply and beta reduce.
fullyApplyAndBetaReduce ::
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
((\a_1 -> addInteger a_1 a_1) 1)
(addInteger 1 1)
Original file line number Diff line number Diff line change
@@ -1 +1 @@
((\a_2 -> addInteger (force a_2) (force a_2)) (delay 1))
(addInteger (force (delay 1)) (force (delay 1)))
Original file line number Diff line number Diff line change
@@ -1 +1 @@
(\b_5 -> (\x_8 y_9 -> 1 x_8) 1)
(\b_5 y_8 -> 1 1)
Original file line number Diff line number Diff line change
@@ -1 +1 @@
(force ((\a_1 -> delay (1 a_1)) 1))
(force (delay (1 1)))
2 changes: 2 additions & 0 deletions plutus-tx-plugin/src/PlutusTx/Plugin.hs
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,8 @@ runCompiler moduleName opts expr = do
& set (PLC.coSimplifyOpts . UPLC.soConservativeOpts)
(opts ^. posConservativeOpts)
& set (PLC.coSimplifyOpts . UPLC.soInlineHints) hints
& set (PLC.coSimplifyOpts . UPLC.soInlineConstants)
(opts ^. posInlineConstants)

-- GHC.Core -> Pir translation.
pirT <- original <$> (PIR.runDefT annMayInline $ compileExprWithDefs expr)
Expand Down

0 comments on commit 6d5e14c

Please sign in to comment.