-
Notifications
You must be signed in to change notification settings - Fork 483
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The UnconstrConstrData rewrite rule (#5605)
Co-authored-by: Nikolaos Bezirgiannis <[email protected]>
- Loading branch information
Showing
28 changed files
with
352 additions
and
39 deletions.
There are no files selected for viewing
3 changes: 3 additions & 0 deletions
3
plutus-core/changelog.d/20231101_201807_bezirg_rewrite_more.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
### Added | ||
|
||
- A PIR rewrite rule for optimizing ""(unconstr . constrdata)" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
89 changes: 89 additions & 0 deletions
89
plutus-core/plutus-ir/src/PlutusIR/Transform/RewriteRules/Common.hs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
{-# LANGUAGE GADTs #-} | ||
{-# LANGUAGE PatternSynonyms #-} | ||
{-# LANGUAGE TypeOperators #-} | ||
module PlutusIR.Transform.RewriteRules.Common | ||
( seqA | ||
, seqP | ||
, mkFreshTermLet -- from MkPlc | ||
, pattern A | ||
, pattern B | ||
, pattern I | ||
) where | ||
|
||
import PlutusCore.Builtin | ||
import PlutusCore.Quote | ||
import PlutusIR | ||
import PlutusIR.Analysis.Builtins | ||
import PlutusIR.Analysis.VarInfo | ||
import PlutusIR.MkPir | ||
import PlutusIR.Purity | ||
|
||
{- | A wrapper that can be more easily turned into an infix operator. | ||
e.g. `infixr 5 (***) = seqA binfo vInfo` | ||
-} | ||
seqA :: (MonadQuote m, Monoid a, ToBuiltinMeaning uni fun) | ||
=> BuiltinsInfo uni fun | ||
-> VarsInfo tyname Name uni a | ||
-> (Type tyname uni a, Term tyname Name uni fun a) | ||
-> m (Term tyname Name uni fun a) | ||
-> m (Term tyname Name uni fun a) | ||
seqA binfo vinfo (a,aT) y = seqOpt binfo vinfo a aT <*> y | ||
|
||
|
||
{- | Another "infix" wrapper where second operand is a Haskell pure value. | ||
e.g. `infixr 5 (***) = seqP binfo vInfo` | ||
-} | ||
seqP :: (MonadQuote m, Monoid a, ToBuiltinMeaning uni fun) | ||
=> BuiltinsInfo uni fun | ||
-> VarsInfo tyname Name uni a | ||
-> (Type tyname uni a, Term tyname Name uni fun a) | ||
-> Term tyname Name uni fun a | ||
-> m (Term tyname Name uni fun a) | ||
seqP binfo vinfo p y = seqA binfo vinfo p (pure y) | ||
|
||
-- | An optimized version to omit call to `seq` if left operand `isPure`. | ||
seqOpt :: ( MonadQuote m | ||
, Monoid a | ||
, ToBuiltinMeaning uni fun | ||
, t ~ Term tyname Name uni fun a | ||
) | ||
=> BuiltinsInfo uni fun | ||
-> VarsInfo tyname Name uni a | ||
-> Type tyname uni a -- ^ the type of left operand a | ||
-> t -- ^ left operand a | ||
-> m (t -> t) -- ^ how to modify right operand b | ||
seqOpt binfo vinfo aT a | isPure binfo vinfo a = pure id | ||
| otherwise = seqUnOpt aT a | ||
|
||
{- | Takes as input a term `a` with its type `aT`, | ||
and constructs a function that expects another term `b`. | ||
When the returned function is applied to a term, the execution of the applied term | ||
would strictly evaluate the first term `a` only for its effects (i.e. ignoring its result) | ||
while returning the result of the second term `b`. | ||
The name is intentionally taken from Haskell's `GHC.Prim.seq`. | ||
Currently, the need for this `seq` "combinator" is in `RewriteRules`, | ||
to trying to retain the effects, that would otherwise be lost if that code was instead considered | ||
completely dead. | ||
Unfortunately, unlike Haskell's `seq`, we need the pass the correct `Type` of `a`, | ||
so as to apply this `seq` combinator. This is usually not a problem because we are generating | ||
code and we should have the type of `a` somewhere available. | ||
-} | ||
seqUnOpt :: (MonadQuote m, Monoid a, t ~ Term tyname Name uni fun a) | ||
=> Type tyname uni a -- ^ the type of left operand a | ||
-> t -- ^ left operand a | ||
-> m (t -> t) -- ^ how to modify right operand b | ||
seqUnOpt aT a = snd <$> mkFreshTermLet aT a | ||
|
||
-- Some shorthands for easier pattern-matching when creating rewrite rules | ||
-- TODO: these patterns ignores annotations. Find a better way for easier writing rules that does | ||
-- not ignore annotations e.g. (pattern-PIR-quasiquoters?) | ||
pattern A :: Term tyname name uni fun a -> Term tyname name uni fun a -> Term tyname name uni fun a | ||
pattern A l r <- Apply _ l r | ||
pattern B :: fun -> Term tyname name uni fun a | ||
pattern B b <- Builtin _ b | ||
pattern I :: Term tyname name uni fun a -> Type tyname uni a -> Term tyname name uni fun a | ||
pattern I e t <- TyInst _ e t |
59 changes: 59 additions & 0 deletions
59
plutus-core/plutus-ir/src/PlutusIR/Transform/RewriteRules/UnConstrConstrData.hs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
-- editorconfig-checker-disable-file | ||
{-# LANGUAGE GADTs #-} | ||
{-# LANGUAGE TypeOperators #-} | ||
module PlutusIR.Transform.RewriteRules.UnConstrConstrData | ||
( unConstrConstrData | ||
) where | ||
|
||
import PlutusCore.Default | ||
import PlutusCore.Quote | ||
import PlutusIR | ||
import PlutusIR.Analysis.Builtins | ||
import PlutusIR.Analysis.VarInfo | ||
import PlutusIR.Transform.RewriteRules.Common | ||
|
||
{- | This rule rewrites terms of form `BUILTIN(unConstrData(constrData(x,y)))` | ||
, where builtin stands for `FstPair` or `SndPair`, to "x" or "y" respectively. | ||
This rewrite-rule was originally meant to rewrite `unConstrData(constrData(x,y)) => (x,y)`, | ||
however we do not have a (polymorphic or monomorphic) builtin constructor to create a `BuiltinPair` | ||
"(x,y)". See note [Representable built-in functions over polymorphic built-in types]. | ||
So we adapted the original rewrite rule to try to achieve a similar goal. | ||
Unfortunately, the adapted rule is less applicable and will most likely never fire | ||
(at least for PIR code generated by plutus-tx). | ||
The reason is that the TH code in plutus-tx does not create such "tight" code, but uses | ||
way more lets that get in the way preventing the rule from firing. | ||
Possible solutions: Some more aggressive PIR inlining, rewriting the PlutusTx TH code, or | ||
introducing specialised pattern-matching builtins as last resort. | ||
Plutus Tx TH code responsible: | ||
<https://github.com/input-output-hk/plutus/blob/9364099b38e3aa27fb311af3299d2210e7e33e45/plutus-tx/src/PlutusTx/IsData/TH.hs#L174-L192> | ||
-} | ||
unConstrConstrData :: (MonadQuote m, t ~ Term tyname Name DefaultUni DefaultFun a, Monoid a) | ||
=> BuiltinsInfo DefaultUni DefaultFun | ||
-> VarsInfo tyname Name DefaultUni a | ||
-> t | ||
-> m t | ||
unConstrConstrData binfo vinfo t = case t of | ||
-- This rule might as well have been split into two separate rules, but kept as one | ||
-- so as to reuse most of the matching pattern. | ||
|
||
-- builtin({t1}, {t2}, unConstr(constrData(i, data))) | ||
(A (I (I (B builtin) tyFst) tySnd) | ||
(A (B UnConstrData) (A (A (B ConstrData) arg1) arg2))) -> | ||
case builtin of | ||
-- sndPair({t1}, {t2}, unConstr(constrData(i, data))) = i `seq` data | ||
SndPair -> (tyFst,arg1) `seQ` arg2 | ||
|
||
-- fstPair({t1}, {t2}, unConstr(constrData(i, data))) = let !gen = i in data `seq` gen | ||
FstPair -> do | ||
(genVar, genLetIn) <- mkFreshTermLet tyFst arg1 | ||
genLetIn <$> | ||
(tySnd, arg2) `seQ` genVar | ||
_ -> pure t | ||
_ -> pure t | ||
|
||
where | ||
infixr 5 `seQ` -- 5 so it has more precedence than <$> | ||
seQ = seqP binfo vinfo |
32 changes: 24 additions & 8 deletions
32
plutus-core/plutus-ir/test/PlutusIR/Transform/RewriteRules/Tests.hs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,37 @@ | ||
module PlutusIR.Transform.RewriteRules.Tests where | ||
|
||
import PlutusCore.Quote | ||
import PlutusCore.Test hiding (ppCatch) | ||
import PlutusIR.Compiler qualified as PIR | ||
import PlutusIR.Parser | ||
import PlutusIR.Test | ||
import PlutusIR.Transform.RewriteRules as RewriteRules | ||
import PlutusPrelude | ||
|
||
import Data.Default.Class | ||
import Test.Tasty | ||
import Test.Tasty.Extras | ||
|
||
test_RewriteRules :: TestTree | ||
test_RewriteRules = runTestNestedIn ["plutus-ir/test/PlutusIR/Transform"] $ | ||
testNested "RewriteRules" $ | ||
fmap | ||
(goldenPir (runQuote . RewriteRules.rewriteWith def) pTerm) | ||
[ "equalsInt" -- this tests that the function works on equalInteger | ||
, "divideInt" -- this tests that the function excludes not commutative functions | ||
, "multiplyInt" -- this tests that the function works on multiplyInteger | ||
, "let" -- this tests that it works in the subterms | ||
(fmap | ||
(goldenPirDoc (prettyPlcClassicDebug . runQuote . RewriteRules.rewriteWith def) pTerm) | ||
[ "equalsInt.pir" -- this tests that the function works on equalInteger | ||
, "divideInt.pir" -- this tests that the function excludes not commutative functions | ||
, "multiplyInt.pir" -- this tests that the function works on multiplyInteger | ||
, "let.pir" -- this tests that it works in the subterms | ||
, "unConstrConstrDataFst.pir" | ||
, "unConstrConstrDataSnd.pir" | ||
] | ||
) | ||
++ | ||
(fmap | ||
(goldenPirEvalTrace pTermAsProg) | ||
[ "unConstrConstrDataFst.pir.eval" | ||
] | ||
) | ||
|
||
where | ||
goldenPirEvalTrace = goldenPirM $ \ast -> ppCatch $ do | ||
-- we need traces to remain for checking the evaluation-order | ||
tplc <- asIfThrown $ compileWithOpts ( set (PIR.ccOpts . PIR.coPreserveLogging) True) ast | ||
runUPlcLogs [void tplc] |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
8 changes: 0 additions & 8 deletions
8
plutus-core/plutus-ir/test/PlutusIR/Transform/RewriteRules/let.golden
This file was deleted.
Oops, something went wrong.
File renamed without changes.
8 changes: 8 additions & 0 deletions
8
plutus-core/plutus-ir/test/PlutusIR/Transform/RewriteRules/let.pir.golden
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
(let | ||
(nonrec) | ||
(termbind (strict) (vardecl x_0 (con integer)) (error (con integer))) | ||
[ | ||
[ (builtin equalsInteger) (con integer 5) ] | ||
[ [ (builtin addInteger) (con integer 10) ] x_0 ] | ||
] | ||
) |
File renamed without changes.
File renamed without changes.
Oops, something went wrong.