Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make forcing builtin functions pure and work-free #5627

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions plutus-core/untyped-plutus-core/src/UntypedPlutusCore/Core.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module UntypedPlutusCore.Core (
module Export,
splitParams,
splitApplication,
splitForces,
) where

import UntypedPlutusCore.Core.Instance as Export
Expand All @@ -25,3 +26,11 @@ splitApplication = go []
go acc = \case
Apply ann fun arg -> go ((ann, arg) : acc) fun
t -> (t, acc)

-- | Extract the term inside a number of nested `Force` nodes.
--
-- This also returns the annotations on the `Force` noes, in the order of outer to inner.
splitForces :: Term name uni fun a -> (Term name uni fun a, [a])
splitForces = \case
Force ann body -> second (ann:) (splitForces body)
t -> (t, [])
18 changes: 13 additions & 5 deletions plutus-core/untyped-plutus-core/src/UntypedPlutusCore/Purity.hs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ module UntypedPlutusCore.Purity
) where

import PlutusCore.Pretty
import UntypedPlutusCore.Core.Type
import UntypedPlutusCore.Core

import Data.DList qualified as DList
import Prettyprinter
Expand Down Expand Up @@ -108,15 +108,23 @@ termEvaluationOrder = goTerm
t@(Force _ dterm) ->
-- first delayed term
goTerm dterm
-- then the whole term, which will mean forcing, so work
<> evalThis (EvalTerm Pure MaybeWork t)
<> evalThis (EvalTerm Pure workFreedom t)
<> dest
where
workFreedom = case dtermInner of
-- Forcing a builtin is workfree
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

Builtin{} -> WorkFree
-- Forcing a delayed term may not be workfree
_ -> MaybeWork
dest = case dterm of
-- known delayed term
(Delay _ body) -> goTerm body
-- unknown delayed term
_ -> evalThis Unknown
_ -> case dtermInner of
-- known builtin. It is already accounted for in `goTerm dterm`, hence `mempty`.
Builtin{} -> mempty
-- unknown delayed term
_ -> evalThis Unknown
dtermInner = fst (splitForces dterm)

t@(Constr _ _ ts) ->
-- first the arguments, in left-to-right order
Expand Down
20 changes: 20 additions & 0 deletions plutus-core/untyped-plutus-core/test/Transform/Simplify.hs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,24 @@ inlineImpure4 =
mkInlinePurityTest $
Force () . Force () . Force () . Delay () . Delay () . Var () <$> freshName "a"

{- | @(\x -> error x) (force ifThenElse)@.
@x@ should be inlined since @force ifThenElse@ is pure.
-}
inline1 :: Term Name PLC.DefaultUni PLC.DefaultFun ()
inline1 = runQuote $ do
x <- freshName "x"
let xRhs = Force () (Builtin () PLC.IfThenElse)
pure $ Apply () (LamAbs () x (Apply () (Error ()) (Var () x))) xRhs

{- | @(\x -> error x) (force (force fstPair))@.
@x@ should be inlined since @force (force fstPair)@ is pure.
-}
inline2 :: Term Name PLC.DefaultUni PLC.DefaultFun ()
inline2 = runQuote $ do
x <- freshName "x"
let xRhs = Force () (Force () (Builtin () PLC.FstPair))
pure $ Apply () (LamAbs () x (Apply () (Error ()) (Var () x))) xRhs

{- | @(\a -> f (a 0 1) (a 2)) (\x y -> g x y)@

The first occurrence of `a` should be inlined because doing so does not increase
Expand Down Expand Up @@ -264,5 +282,7 @@ test_simplify =
, goldenVsSimplified "inlineImpure2" inlineImpure2
, goldenVsSimplified "inlineImpure3" inlineImpure3
, goldenVsSimplified "inlineImpure4" inlineImpure4
, goldenVsSimplified "inline1" inline1
, goldenVsSimplified "inline2" inline2
, goldenVsSimplified "multiApp" multiApp
]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(error (force ifThenElse))
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(error (force (force fstPair)))