Skip to content

Commit

Permalink
Pass abstraction (#5665)
Browse files Browse the repository at this point in the history
* Pass abstraction

* Sanitise property test counts

* Tidy up simplifier and runIfOpts

* Bit more on typechecking

* Fixes

* More fixes

* More

* Fix beta needing global uniqueness

* Comments

* WIP

* WIP

* WIP

* Fixes

* fix

* Tweak
  • Loading branch information
michaelpj authored Dec 12, 2023
1 parent 67b7d90 commit f94330a
Show file tree
Hide file tree
Showing 80 changed files with 1,225 additions and 725 deletions.
2 changes: 1 addition & 1 deletion plutus-core/executables/pir/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ doOptimisations :: PirTerm PLC.SrcSpan -> Either (PirError UnitProvenance) (PirT
doOptimisations term = do
plcTcConfig <- PLC.getDefTypeCheckConfig PIR.noProvenance
let ctx = getCtx plcTcConfig
runExcept $ flip runReaderT ctx $ runQuoteT $ PIR.simplifyTerm =<< PLC.rename (PIR.Original () <$ term)
runExcept $ flip runReaderT ctx $ runQuoteT $ PIR.runCompilerPass PIR.simplifier (PIR.Original () <$ term)
where
getCtx
:: PLC.TypeCheckConfig PLC.DefaultUni PLC.DefaultFun
Expand Down
4 changes: 3 additions & 1 deletion plutus-core/plutus-core.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,7 @@ library plutus-ir
PlutusIR.Mark
PlutusIR.MkPir
PlutusIR.Parser
PlutusIR.Pass
PlutusIR.Purity
PlutusIR.Subst
PlutusIR.Transform.Beta
Expand Down Expand Up @@ -537,6 +538,7 @@ library plutus-ir
PlutusIR.Compiler.Recursion
PlutusIR.Normalize
PlutusIR.Transform.RewriteRules.Common
PlutusIR.Transform.RewriteRules.Rules
PlutusIR.Transform.RewriteRules.UnConstrConstrData

build-depends:
Expand Down Expand Up @@ -712,7 +714,7 @@ library plutus-core-testlib
PlutusIR.Generators.QuickCheck.Common
PlutusIR.Generators.QuickCheck.GenerateTerms
PlutusIR.Generators.QuickCheck.ShrinkTerms
PlutusIR.Properties.Typecheck
PlutusIR.Pass.Test
PlutusIR.Test
Test.Tasty.Extras
UntypedPlutusCore.Test.DeBruijn.Bad
Expand Down
295 changes: 114 additions & 181 deletions plutus-core/plutus-ir/src/PlutusIR/Compiler.hs

Large diffs are not rendered by default.

27 changes: 25 additions & 2 deletions plutus-core/plutus-ir/src/PlutusIR/Compiler/Let.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
-- | Functions for compiling PIR let terms.
module PlutusIR.Compiler.Let (compileLets, LetKind(..)) where
module PlutusIR.Compiler.Let (compileLets, LetKind(..), compileLetsPass, compileLetsPassSC) where

import PlutusIR
import PlutusIR.Compiler.Datatype
Expand All @@ -24,6 +24,8 @@ import Data.Foldable
import Data.List.NonEmpty hiding (partition, reverse)
import Data.List.NonEmpty qualified as NE
import PlutusCore.Pretty (display)
import PlutusIR.Pass
import PlutusIR.TypeCheck qualified as TC

{- Note [Extra definitions while compiling let-bindings]
The let-compiling passes can generate some additional definitions, so we use the
Expand All @@ -37,6 +39,27 @@ Also we should pull out more stuff (e.g. see 'NonStrict' which uses unit).
-}

data LetKind = RecTerms | NonRecTerms | Types | DataTypes
deriving stock (Show, Eq, Ord)

compileLetsPassSC
:: Compiling m e uni fun a
=> TC.PirTCConfig uni fun
-> LetKind
-> Pass m TyName Name uni fun (Provenance a)
compileLetsPassSC tcconfig letKind =
renamePass <> compileLetsPass tcconfig letKind

compileLetsPass
:: Compiling m e uni fun a
=> TC.PirTCConfig uni fun
-> LetKind
-> Pass m TyName Name uni fun (Provenance a)
compileLetsPass tcconfig letKind =
NamedPass "compile lets" $
Pass
(compileLets letKind)
[Typechecks tcconfig, GloballyUniqueNames]
[ConstCondition (Typechecks tcconfig)]

-- | Compile the let terms out of a 'Term'. Note: the result does *not* have globally unique names.
compileLets :: Compiling m e uni fun a => LetKind -> PIRTerm uni fun a -> m (PIRTerm uni fun a)
Expand All @@ -63,7 +86,7 @@ compileRecBindings kind body bs =
singleGroup :| [] ->
case NE.head singleGroup of
TermBind {} -> compileRecTermBindings kind body singleGroup
DatatypeBind {} -> lift $ compileRecDataBindings kind body singleGroup
DatatypeBind {} -> lift $ compileRecDataBindings kind body singleGroup
tb@TypeBind {} ->
lift $ getEnclosing >>= \p -> throwing _Error $
CompilationError p
Expand Down
8 changes: 5 additions & 3 deletions plutus-core/plutus-ir/src/PlutusIR/Compiler/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import PlutusCore.Quote
import PlutusCore.StdLib.Type qualified as Types
import PlutusCore.TypeCheck.Internal qualified as PLC
import PlutusCore.Version qualified as PLC
import PlutusIR.Transform.RewriteRules
import PlutusIR.Transform.RewriteRules.Rules
import PlutusPrelude

import Control.Monad.Error.Lens (throwing)
Expand Down Expand Up @@ -76,6 +76,7 @@ defaultDatatypeCompilationOpts = DatatypeCompilationOpts SumsOfProducts

data CompilationOpts a = CompilationOpts {
_coOptimize :: Bool
, _coTypecheck :: Bool
, _coPedantic :: Bool
, _coVerbose :: Bool
, _coDebug :: Bool
Expand Down Expand Up @@ -105,6 +106,7 @@ makeLenses ''CompilationOpts
defaultCompilationOpts :: CompilationOpts a
defaultCompilationOpts = CompilationOpts
{ _coOptimize = True -- synonymous with max-simplifier-iterations=0
, _coTypecheck = True
, _coPedantic = False
, _coVerbose = False
, _coDebug = False
Expand All @@ -130,7 +132,7 @@ data CompilationCtx uni fun a = CompilationCtx {
_ccOpts :: CompilationOpts a
, _ccEnclosing :: Provenance a
-- | Decide to either typecheck (passing a specific tcconfig) or not by passing 'Nothing'.
, _ccTypeCheckConfig :: Maybe (PirTCConfig uni fun)
, _ccTypeCheckConfig :: PirTCConfig uni fun
, _ccBuiltinsInfo :: BuiltinsInfo uni fun
, _ccBuiltinCostModel :: PLC.CostingPart uni fun
, _ccRewriteRules :: RewriteRules uni fun
Expand All @@ -145,7 +147,7 @@ toDefaultCompilationCtx
toDefaultCompilationCtx configPlc = CompilationCtx
{ _ccOpts = defaultCompilationOpts
, _ccEnclosing = noProvenance
, _ccTypeCheckConfig = Just $ PirTCConfig configPlc YesEscape
, _ccTypeCheckConfig = PirTCConfig configPlc YesEscape
, _ccBuiltinsInfo = def
, _ccBuiltinCostModel = def
, _ccRewriteRules = def
Expand Down
3 changes: 3 additions & 0 deletions plutus-core/plutus-ir/src/PlutusIR/Error.hs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ instance AsTypeErrorExt (Error uni fun a) uni a where
instance PLC.AsFreeVariableError (Error uni fun a) where
_FreeVariableError = _PLCError . PLC._FreeVariableError

instance PLC.AsUniqueError (Error uni fun a) a where
_UniqueError = _PLCError . PLC._UniqueError

-- Pretty-printing
------------------

Expand Down
153 changes: 153 additions & 0 deletions plutus-core/plutus-ir/src/PlutusIR/Pass.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RankNTypes #-}

module PlutusIR.Pass where

import PlutusIR.Check.Uniques qualified as Uniques
import PlutusIR.Core.Type
import PlutusIR.Error
import PlutusIR.TypeCheck qualified as TC

import PlutusCore qualified as PLC
import PlutusCore.Name

import Control.Monad (when)
import Control.Monad.Except
import Control.Monad.Trans.Class (lift)
import Data.Foldable
import Data.Text (Text)
import PlutusCore.Quote
import PlutusPrelude

-- | A condition on a 'Term'.
data Condition tyname name uni fun a where
-- | The 'Term' typechecks.
Typechecks :: (PLC.Typecheckable uni fun, PLC.GEq uni)
=> TC.PirTCConfig uni fun -> Condition TyName Name uni fun a
-- | The 'Term' has globally unique names.
GloballyUniqueNames :: (HasUnique tyname TypeUnique, HasUnique name TermUnique, Ord a)
=> Condition tyname name uni fun a
-- | A custom condition. 'Nothing' indicates success, 'Just' indicates a failure at
-- a location with a message.
Custom :: (Term tyname name uni fun a -> Maybe (a, Text))
-> Condition tyname name uni fun a

-- | A condition on a pair of 'Term's.
data BiCondition tyname name uni fun a where
-- | A condition on the second 'Term'.
ConstCondition :: Condition tyname name uni fun a -> BiCondition tyname name uni fun a
-- | A custom condition. 'Nothing' indicates success, 'Just' indicates a failure at
-- a location with a message.
CustomBi :: (Term tyname name uni fun a -> Term tyname name uni fun a -> Maybe (a, Text))
-> BiCondition tyname name uni fun a

checkCondition
:: MonadError (Error uni fun a) m
=> Condition tyname name uni fun a
-> Term tyname name uni fun a
-> m ()
checkCondition c t = case c of
Typechecks tcconfig -> void $ runQuoteT $ do
-- Typechecking requires globally unique names
renamed <- PLC.rename t
TC.inferType tcconfig renamed
GloballyUniqueNames -> void $ Uniques.checkTerm (const True) t
Custom f -> case f t of
Just (a, e) -> throwError $ CompilationError a e
Nothing -> pure ()

checkBiCondition
:: MonadError (Error uni fun a) m
=> BiCondition tyname name uni fun a
-> Term tyname name uni fun a
-> Term tyname name uni fun a
-> m ()
checkBiCondition c t1 t2 = case c of
ConstCondition c' -> checkCondition c' t2
CustomBi f -> case f t1 t2 of
Just (a, e) -> throwError $ CompilationError a e
Nothing -> pure ()

-- | A pass over a term, with pre- and post-conditions.
data Pass m tyname name uni fun a =
-- | A basic pass. Has a function, which is the pass itself, a set of pre-conditions
-- which are run on the input term, and a set of post-conditions which are run on the
-- input and output terms (so can compare them).
Pass
(Term tyname name uni fun a -> m (Term tyname name uni fun a))
[Condition tyname name uni fun a]
[BiCondition tyname name uni fun a]
| CompoundPass (Pass m tyname name uni fun a) (Pass m tyname name uni fun a)
| NoOpPass
| NamedPass String (Pass m tyname name uni fun a)

instance Semigroup (Pass m tyname name uni fun a) where
p1 <> p2 = CompoundPass p1 p2

instance Monoid (Pass m tyname name uni fun a) where
mempty = NoOpPass

hoistPass :: (forall v . m v -> n v) -> Pass m tyname name uni fun a -> Pass n tyname name uni fun a
hoistPass f p = case p of
Pass mainPass pre post -> Pass (f . mainPass) pre post
CompoundPass p1 p2 -> CompoundPass (hoistPass f p1) (hoistPass f p2)
NamedPass n pass -> NamedPass n (hoistPass f pass)
NoOpPass -> NoOpPass

runPass
:: Monad m
=> (String -> m ())
-> Bool
-> Pass m tyname name uni fun a
-> Term tyname name uni fun a
-> ExceptT (Error uni fun a) m (Term tyname name uni fun a)
runPass logger checkConditions (Pass mainPass pre post) t = do
when checkConditions $ do
lift $ logger "checking preconditions"
for_ pre $ \c -> checkCondition c t
t' <- lift $ mainPass t
when checkConditions $ do
lift $ logger "checking postconditions"
for_ post $ \c -> checkBiCondition c t t'
pure t'
runPass logger checkConditions (CompoundPass p1 p2) t = do
t' <- runPass logger checkConditions p1 t
runPass logger checkConditions p2 t'
runPass logger checkConditions (NamedPass n pass) t = do
lift $ logger $ n ++ ": running pass"
t' <- runPass logger checkConditions pass t
lift $ logger $ n ++ ": finished pass"
pure t'
runPass _ _ NoOpPass t = pure t

-- | A simple, non-monadic pass that should typecheck.
simplePass
:: (PLC.Typecheckable uni fun, PLC.GEq uni, Applicative m)
=> String
-> TC.PirTCConfig uni fun
-> (Term TyName Name uni fun a -> Term TyName Name uni fun a)
-> Pass m TyName Name uni fun a
simplePass name tcConfig f =
NamedPass name $
Pass (pure . f) [Typechecks tcConfig] [ConstCondition (Typechecks tcConfig)]

-- | A pass that does renaming.
renamePass
:: (HasUnique name TermUnique, HasUnique tyname TypeUnique, MonadQuote m, Ord a)
=> Pass m tyname name uni fun a
renamePass =
NamedPass "renaming" $
Pass PLC.rename [] [ConstCondition GloballyUniqueNames]

-- | A pass that does typechecking, useful when you want to do it explicitly
-- and not as part of a precondition check.
typecheckPass
:: (TC.MonadTypeCheckPir err uni fun a m, Ord a)
=> TC.PirTCConfig uni fun
-> Pass m TyName Name uni fun a
typecheckPass tcconfig = NamedPass "typechecking" $ Pass run [GloballyUniqueNames] []
where
run t = do
_ <- TC.inferType tcconfig t
pure t
31 changes: 28 additions & 3 deletions plutus-core/plutus-ir/src/PlutusIR/Transform/Beta.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@
A simple beta-reduction pass.
-}
module PlutusIR.Transform.Beta (
beta
beta,
betaPass,
betaPassSC
) where

import PlutusIR

import Control.Lens (over)
import Data.List.NonEmpty qualified as NE
import PlutusCore qualified as PLC
import PlutusIR
import PlutusIR.Pass
import PlutusIR.TypeCheck qualified as TC

{- Note [Multi-beta]
Consider two examples where applying beta should be helpful.
Expand Down Expand Up @@ -58,6 +62,10 @@ applications.
That does mean that we need to do a manual traversal rather than doing standard bottom-up
processing.
Note that multi-beta requires globally unique names. In the example above, we end up with
the binding for `x` outside `b`, which means it could shadow an existing `x` binding in the
environment.
Note that multi-beta cannot be used on TypeBinds. For instance, it is unsound to turn
(/\a \(b : a). t) {x} (y : x)
Expand Down Expand Up @@ -128,3 +136,20 @@ beta = over termSubterms beta . localTransform
let b = TypeBind a (TyVarDecl a n k) tyArg
in Let (termAnn body) NonRec (pure b) body
t -> t

betaPassSC
:: (PLC.Typecheckable uni fun, PLC.GEq uni, PLC.MonadQuote m, Ord a)
=> TC.PirTCConfig uni fun
-> Pass m TyName Name uni fun a
betaPassSC tcconfig = renamePass <> betaPass tcconfig

betaPass
:: (PLC.Typecheckable uni fun, PLC.GEq uni, Applicative m, Ord a)
=> TC.PirTCConfig uni fun
-> Pass m TyName Name uni fun a
betaPass tcconfig =
NamedPass "beta" $
Pass
(pure . beta)
[Typechecks tcconfig, GloballyUniqueNames]
[ConstCondition (Typechecks tcconfig)]
Loading

1 comment on commit f94330a

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'Plutus Benchmarks'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.05.

Benchmark suite Current: f94330a Previous: 67b7d90 Ratio
validation-decode-escrow-refund-1 311.4 μs 296.3 μs 1.05
validation-decode-token-account-2 209.5 μs 199.1 μs 1.05
marlowe-semantics/ccab11ce1a8774135d0e3c9e635631b68af9e276b5dabc66ff669d5650d0be1c 991.7 μs 940.7 μs 1.05
marlowe-semantics/2797d7ac77c1b6aff8e42cf9a47fa86b1e60f22719a996871ad412cbe4de78b5 1813 μs 1719 μs 1.05
marlowe-semantics/1f0f02191604101e1f201016171604060d010d1d1c150e110a110e1006160a0d 997 μs 948.2 μs 1.05

This comment was automatically generated by workflow using github-action-benchmark.

CC: @input-output-hk/plutus-core

Please sign in to comment.