morley-1.19.0: src/Morley/Michelson/Optimizer.hs
-- SPDX-FileCopyrightText: 2021 Oxhead Alpha
-- SPDX-License-Identifier: LicenseRef-MIT-OA
-- NOTE this pragmas.
-- We disable some wargnings for the sake of speed up.
-- Write code with care.
{-# OPTIONS_GHC -Wno-incomplete-patterns #-}
{-# OPTIONS_GHC -Wno-overlapping-patterns #-}
-- | Optimizer for typed instructions.
--
-- It's quite experimental and incomplete.
module Morley.Michelson.Optimizer
( optimize
, optimizeWithConf
, defaultOptimizerConf
, defaultRules
, defaultRulesAndPushPack
, orRule
, orSimpleRule
, Rule (..)
, OptimizerConf (..)
, ocGotoValuesL
) where
import Prelude hiding (EQ, GT, LT)
import Control.Lens (makeLensesFor)
import Data.Constraint (Dict(..))
import Data.Default (Default(def))
import Data.Singletons (sing)
import Data.Type.Equality ((:~:)(Refl))
import Morley.Michelson.Interpret.Pack (packValue')
import Morley.Michelson.Typed.Aliases (Value)
import Morley.Michelson.Typed.Arith
import Morley.Michelson.Typed.Instr
import Morley.Michelson.Typed.Scope (ConstantScope, PackedValScope, checkScope)
import Morley.Michelson.Typed.Sing
import Morley.Michelson.Typed.T
import Morley.Michelson.Typed.Util (DfsSettings(..), dfsModifyInstr)
import Morley.Michelson.Typed.Value
import Morley.Util.PeanoNatural
----------------------------------------------------------------------------
-- High level
----------------------------------------------------------------------------
data OptimizerConf = OptimizerConf
{ ocGotoValues :: Bool
, ocRuleset :: [Rule]
}
-- | Default config - all commonly useful rules will be applied to all the code.
defaultOptimizerConf :: OptimizerConf
defaultOptimizerConf = OptimizerConf
{ ocGotoValues = True
, ocRuleset = defaultRules
}
instance Default OptimizerConf where
def = defaultOptimizerConf
-- | Optimize a typed instruction by replacing some sequences of
-- instructions with smaller equivalent sequences.
-- Applies default set of rewrite rules.
optimize :: Instr inp out -> Instr inp out
optimize = optimizeWithConf def
-- | Optimize a typed instruction using a custom set of rules.
-- The set is divided into several stages, as applying
-- some rules can prevent others to be performed.
optimizeWithConf :: OptimizerConf -> Instr inp out -> Instr inp out
optimizeWithConf (OptimizerConf ocGotoValues rules) instr = foldl (flip performOneStage) instrRHS rules
where
dfsSettings = def{ dsGoToValues = ocGotoValues }
performOneStage stageRules =
dfsModifyInstr dfsSettings $ applyOnce stageRules
instrRHS = applyOnce (fixpoint flattenSeqLHS) instr
----------------------------------------------------------------------------
-- Rewrite rules
----------------------------------------------------------------------------
-- Type of a single rewrite rule, wrapped in `newtype`. It takes an instruction
-- and tries to optimize its head (first few instructions). If optimization
-- succeeds, it returns `Just` the optimized instruction, otherwise it returns `Nothing`.
newtype Rule = Rule {unRule :: forall inp out. Instr inp out -> Maybe (Instr inp out)}
defaultRules :: [Rule]
defaultRules = map fixpoint
[ mainStageRules
, glueAdjacentDropsStageRules
]
where
glueAdjacentDropsStageRules = flattenSeqLHS `orSimpleRule` adjacentDrops
-- | We do not enable 'pushPack' rule by default because it is
-- potentially dangerous.
-- There are various code processing functions that may depend on constants,
-- e. g. string transformations.
-- need helper
defaultRulesAndPushPack :: [Rule]
defaultRulesAndPushPack =
case defaultRules of
-- Perhaps one day main rules will not be performed on the first stage.
-- We need another way to add `pushPack` to `mainStageRules` in that case.
_ : otherStagesRules -> do
let mainStageRulesAndPushPack = fixpoint $ mainStageRules `orSimpleRule` pushPack
mainStageRulesAndPushPack : otherStagesRules
_ -> error "`defaultRules` should not be empty"
mainStageRules :: Rule -> Rule
mainStageRules =
flattenSeqLHS
`orSimpleRule` removeNesting
`orSimpleRule` removeExtStackType
`orSimpleRule` dipDrop2swapDrop
`orSimpleRule` ifNopNop2Drop
`orSimpleRule` nopIsNeutralForSeq
`orSimpleRule` variousNops
`orSimpleRule` dupSwap2dup
`orSimpleRule` noDipNeeded
`orSimpleRule` branchShortCut
`orSimpleRule` compareWithZero
`orSimpleRule` simpleDrops
`orSimpleRule` internalNop
`orSimpleRule` simpleDips
`orSimpleRule` simpleDups
`orSimpleRule` adjacentDips
`orSimpleRule` isSomeOnIf
`orSimpleRule` redundantIf
`orSimpleRule` emptyDip
`orSimpleRule` digDug
`orSimpleRule` specificPush
`orSimpleRule` pairUnpair
`orSimpleRule` pairMisc
`orSimpleRule` unpairMisc
`orSimpleRule` swapBeforeCommutative
`orSimpleRule` justDrops
`orSimpleRule` justDoubleDrops
flattenSeqLHS :: Rule -> Rule
flattenSeqLHS toplevel = Rule $ \case
it@(Seq (Seq _ _) _) -> Just $ linearizeAndReapply toplevel it
_ -> Nothing
removeNesting :: Rule
removeNesting = Rule $ \case
Nested i -> Just i
_ -> Nothing
-- | STACKTYPE is currently a Nop and may safely be removed.
removeExtStackType :: Rule
removeExtStackType = Rule $ \case
Ext (STACKTYPE{}) -> Just Nop
_ -> Nothing
dipDrop2swapDrop :: Rule
dipDrop2swapDrop = Rule $ \case
DIP DROP -> Just $ SWAP :# DROP
_ -> Nothing
ifNopNop2Drop :: Rule
ifNopNop2Drop = Rule $ \case
IF Nop Nop -> Just DROP
_ -> Nothing
nopIsNeutralForSeq :: Rule
nopIsNeutralForSeq = Rule $ \case
Nop :# i -> Just i
i :# Nop -> Just i
_ -> Nothing
variousNops :: Rule
variousNops = Rule $ \case
DUP :# DROP :# c -> Just c
DUPN _ :# DROP :# c -> Just c
SWAP :# SWAP :# c -> Just c
PUSH _ :# DROP :# c -> Just c
NONE :# DROP :# c -> Just c
UNIT :# DROP :# c -> Just c
NIL :# DROP :# c -> Just c
EMPTY_SET :# DROP :# c -> Just c
EMPTY_MAP :# DROP :# c -> Just c
EMPTY_BIG_MAP :# DROP :# c -> Just c
LAMBDA _ :# DROP :# c -> Just c
SELF _ :# DROP :# c -> Just c
NOW :# DROP :# c -> Just c
AMOUNT :# DROP :# c -> Just c
BALANCE :# DROP :# c -> Just c
TOTAL_VOTING_POWER :# DROP :# c -> Just c
SOURCE :# DROP :# c -> Just c
SENDER :# DROP :# c -> Just c
CHAIN_ID :# DROP :# c -> Just c
LEVEL :# DROP :# c -> Just c
SELF_ADDRESS :# DROP :# c -> Just c
READ_TICKET :# DROP :# c -> Just c
DUP :# DROP -> Just Nop
DUPN _ :# DROP -> Just Nop
SWAP :# SWAP -> Just Nop
PUSH _ :# DROP -> Just Nop
NONE :# DROP -> Just Nop
UNIT :# DROP -> Just Nop
NIL :# DROP -> Just Nop
EMPTY_SET :# DROP -> Just Nop
EMPTY_MAP :# DROP -> Just Nop
EMPTY_BIG_MAP :# DROP -> Just Nop
LAMBDA _ :# DROP -> Just Nop
SELF _ :# DROP -> Just Nop
NOW :# DROP -> Just Nop
AMOUNT :# DROP -> Just Nop
BALANCE :# DROP -> Just Nop
TOTAL_VOTING_POWER :# DROP -> Just Nop
SOURCE :# DROP -> Just Nop
SENDER :# DROP -> Just Nop
CHAIN_ID :# DROP -> Just Nop
LEVEL :# DROP -> Just Nop
SELF_ADDRESS :# DROP -> Just Nop
READ_TICKET :# DROP -> Just Nop
_ -> Nothing
dupSwap2dup :: Rule
dupSwap2dup = Rule $ \case
DUP :# SWAP :# c -> Just (DUP :# c)
DUP :# SWAP -> Just DUP
_ -> Nothing
noDipNeeded :: Rule
noDipNeeded = Rule $ \case
-- If we put a constant value on stack and then do something under it,
-- we can do this "something" on original stack and then put that constant.
PUSH x :# DIP f :# c -> Just $ f :# PUSH x :# c
PUSH x :# DIP f -> Just $ f :# PUSH x
UNIT :# DIP f :# c -> Just $ f :# UNIT :# c
UNIT :# DIP f -> Just $ f :# UNIT
NOW :# DIP f :# c -> Just $ f :# NOW :# c
NOW :# DIP f -> Just $ f :# NOW
SENDER :# DIP f :# c -> Just $ f :# SENDER :# c
SENDER :# DIP f -> Just $ f :# SENDER
EMPTY_MAP :# DIP f :# c -> Just $ f :# EMPTY_MAP :# c
EMPTY_MAP :# DIP f -> Just $ f :# EMPTY_MAP
EMPTY_SET :# DIP f :# c -> Just $ f :# EMPTY_SET :# c
EMPTY_SET :# DIP f -> Just $ f :# EMPTY_SET
-- If we do something ignoring top of the stack and then immediately
-- drop top of the stack, we can drop that item in advance and
-- not use 'DIP' at all.
DIP f :# DROP :# c -> Just $ DROP :# f :# c
DIP f :# DROP -> Just $ DROP :# f
_ -> Nothing
branchShortCut :: Rule
branchShortCut = Rule $ \case
LEFT :# IF_LEFT f _ :# c -> Just (f :# c)
RIGHT :# IF_LEFT _ f :# c -> Just (f :# c)
CONS :# IF_CONS f _ :# c -> Just (f :# c)
NIL :# IF_CONS _ f :# c -> Just (f :# c)
NONE :# IF_NONE f _ :# c -> Just (f :# c)
SOME :# IF_NONE _ f :# c -> Just (f :# c)
PUSH vOr@(VOr eitherVal) :# IF_LEFT f g :# c -> case vOr of
(_ :: Value ('TOr l r)) -> case eitherVal of
Left val -> case checkScope @(ConstantScope l) of
Right Dict -> Just (PUSH val :# f :# c)
_ -> Nothing
Right val -> case checkScope @(ConstantScope r) of
Right Dict -> Just (PUSH val :# g :# c)
_ -> Nothing
PUSH (VList (x : xs)) :# IF_CONS f _ :# c -> Just (PUSH (VList xs) :# PUSH x :# f :# c)
PUSH (VList _) :# IF_CONS _ f :# c -> Just (f :# c)
PUSH (VOption Nothing) :# IF_NONE f _ :# c -> Just (f :# c)
PUSH (VOption (Just val)) :# IF_NONE _ f :# c -> Just (PUSH val :# f :# c)
PUSH (VBool True) :# IF f _ :# c -> Just (f :# c)
PUSH (VBool False) :# IF _ f :# c -> Just (f :# c)
LEFT :# IF_LEFT f _ -> Just f
RIGHT :# IF_LEFT _ f -> Just f
CONS :# IF_CONS f _ -> Just f
NIL :# IF_CONS _ f -> Just f
NONE :# IF_NONE f _ -> Just f
SOME :# IF_NONE _ f -> Just f
PUSH vOr@(VOr eitherVal) :# IF_LEFT f g -> case vOr of
(_ :: Value ('TOr l r)) -> case eitherVal of
Left val -> case checkScope @(ConstantScope l) of
Right Dict -> Just (PUSH val :# f)
_ -> Nothing
Right val -> case checkScope @(ConstantScope r) of
Right Dict -> Just (PUSH val :# g)
_ -> Nothing
PUSH (VList (x : xs)) :# IF_CONS f _ -> Just (PUSH (VList xs) :# PUSH x :# f)
PUSH (VList _) :# IF_CONS _ f -> Just f
PUSH (VOption Nothing) :# IF_NONE f _ -> Just f
PUSH (VOption (Just val)) :# IF_NONE _ f -> Just (PUSH val :# f)
PUSH (VBool True) :# IF f _ -> Just f
PUSH (VBool False) :# IF _ f -> Just f
_ -> Nothing
compareWithZero :: Rule
compareWithZero = Rule $ \case
PUSH (VInt 0) :# COMPARE :# EQ :# c -> Just $ EQ :# c
PUSH (VNat 0) :# COMPARE :# EQ :# c -> Just $ INT :# EQ :# c
PUSH (VInt 0) :# COMPARE :# EQ -> Just $ EQ
PUSH (VNat 0) :# COMPARE :# EQ -> Just $ INT :# EQ
_ -> Nothing
simpleDrops :: Rule
simpleDrops = Rule $ \case
-- DROP 0 is Nop
DROPN Zero :# c -> Just c
DROPN Zero -> Just Nop
-- DROP 1 is DROP.
-- @gromak: DROP seems to be cheaper (in my experiments it consumed 3 less gas).
-- It is packed more efficiently.
-- Unfortunately I do not know how to convince GHC that types match here.
-- Specifically, it can not deduce that `inp` is not empty
-- (`DROP` expects non-empty input).
-- We have `LongerOrSameLength inp (S Z)` here, but that is not enough to
-- convince GHC.
-- I will leave this note and rule here in hope that someone will manage to
-- deal with this problem one day.
-- DROPN One :# c -> Just (DROP :# c)
-- DROPN One -> Just DROP
_ -> Nothing
-- If an instruction takes another instruction as an argument and that
-- internal instruction is 'Nop', sometimes the whole instruction is
-- 'Nop'.
-- For now we do it only for 'DIP', but ideally we should do it for
-- 'MAP' as well (which is harder).
internalNop :: Rule
internalNop = Rule $ \case
DIP Nop -> Just Nop
DIP Nop :# c -> Just c
_ -> Nothing
simpleDips :: Rule
simpleDips = Rule $ \case
-- DIP 0 is redundant
DIPN Zero i :# c -> Just (i :# c)
DIPN Zero i -> Just i
-- @gromak: same situation as with `DROP 1` (see above).
-- DIPN One i :# c -> Just (DIP i :# c)
-- DIPN One i -> Just (DIP i)
_ -> Nothing
simpleDups :: Rule
simpleDups = Rule $ \case
-- DUP 1 is just DUP
DUPN One :# xs -> Just $ DUP :# xs
DUPN One -> Just $ DUP
_ -> Nothing
adjacentDips :: Rule
adjacentDips = Rule $ \case
DIP f :# DIP g -> Just (DIP (f :# g))
DIP f :# DIP g :# c -> Just (DIP (f :# g) :# c)
_ -> Nothing
redundantIf :: Rule
redundantIf = Rule \case
IF x y :# xs
| x == y
-> Just $ DROP :# x :# xs
IF x y
| x == y
-> Just $ DROP :# x
_ -> Nothing
emptyDip :: Rule
emptyDip = Rule \case
DIP Nop :# xs -> Just xs
DIP Nop -> Just Nop
DIPN _ Nop :# xs -> Just xs
DIPN _ Nop -> Just Nop
_ -> Nothing
digDug :: Rule
digDug = Rule \case
(DIG (x :: PeanoNatural n) :: Instr inp t) :# (DUG y :: Instr t out) :# xs
| Just Refl <- eqPeanoNat x y
-> Just xs
(DIG (x :: PeanoNatural n) :: Instr inp t) :# (DUG y :: Instr t out)
| Just Refl <- eqPeanoNat x y
-> Just Nop
_ -> Nothing
-- TODO [#299]: optimize sequences of more than 2 DROPs.
-- | Sequences of @DROP@s can be turned into single @DROP n@.
-- When @n@ is greater than 2 it saves size and gas.
-- When @n@ is 2 it saves gas only.
adjacentDrops :: Rule
adjacentDrops = Rule $ \case
DROP :# DROP -> Just (DROPN Two)
DROP :# DROP :# c -> Just (DROPN Two :# c)
-- Does not compile, need to do something smart
-- DROPN Two :# DROP -> Just (DROPN (Succ Two))
_ -> Nothing
specificPush :: Rule
specificPush = Rule $ \case
push@PUSH{} -> optimizePush push
push@PUSH{} :# c -> (:# c) <$> optimizePush push
_ -> Nothing
where
optimizePush :: Instr inp out -> Maybe (Instr inp out)
optimizePush = \case
PUSH v | _ :: Value v <- v -> case v of
VUnit -> Just UNIT
VMap m
| null m -> case sing @v of STMap{} -> Just EMPTY_MAP
VSet m
| null m -> case sing @v of STSet{} -> Just EMPTY_SET
_ -> Nothing
_ -> Nothing
isSomeOnIf :: Rule
isSomeOnIf = Rule $ \case
IF (PUSH (VOption Just{})) (PUSH (VOption Nothing)) :# c -> case c of
IF_NONE (PUSH (VBool False)) (DROP :# PUSH (VBool True)) :# s -> Just s
IF_NONE (PUSH (VBool False)) (DROP :# PUSH (VBool True)) -> Just Nop
_ -> Nothing
_ -> Nothing
pairUnpair :: Rule
pairUnpair = Rule $ \case
PAIR :# UNPAIR :# c -> Just c
PAIR :# UNPAIR -> Just Nop
UNPAIR :# PAIR :# c -> Just c
UNPAIR :# PAIR -> Just Nop
_ -> Nothing
pairMisc :: Rule
pairMisc = Rule $ \case
PAIR :# CDR :# c -> Just $ DROP :# c
PAIR :# CDR -> Just DROP
PAIR :# CAR :# c -> Just $ (DIP DROP) :# c
PAIR :# CAR -> Just $ DIP DROP
_ -> Nothing
unpairMisc :: Rule
unpairMisc = Rule $ \case
DUP :# CAR :# DIP CDR -> Just $ UNPAIR
DUP :# CAR :# DIP CDR :# c -> Just $ UNPAIR :# c
DUP :# CDR :# DIP CAR -> Just $ UNPAIR :# SWAP
DUP :# CDR :# DIP CAR :# c -> Just $ UNPAIR :# SWAP :# c
UNPAIR :# DROP -> Just CDR
UNPAIR :# DROP :# c -> Just $ CDR :# c
_ -> Nothing
commuteArith ::
forall n m s out. Instr (n ': m ': s) out -> Maybe (Instr (m ': n ': s) out)
commuteArith = \case
ADD -> do Dict <- commutativityProof @Add @n @m; Just ADD
MUL -> do Dict <- commutativityProof @Mul @n @m; Just MUL
OR -> do Dict <- commutativityProof @Or @n @m; Just OR
AND -> do Dict <- commutativityProof @And @n @m; Just AND
XOR -> do Dict <- commutativityProof @Xor @n @m; Just XOR
_ -> Nothing
swapBeforeCommutative :: Rule
swapBeforeCommutative = Rule $ \case
SWAP :# i :# c -> (:# c) <$> commuteArith i
SWAP :# i -> commuteArith i
_ -> Nothing
pushPack :: Rule
pushPack = Rule $ \case
PUSH x :# PACK -> Just (pushPacked x)
PUSH x :# PACK :# c -> Just (pushPacked x :# c)
_ -> Nothing
where
pushPacked :: PackedValScope t => Value t -> Instr s ('TBytes ': s)
pushPacked = PUSH . VBytes . packValue'
justDrops :: Rule
justDrops = Rule $ \case
CAR :# DROP :# c -> Just $ DROP :# c
CDR :# DROP :# c -> Just $ DROP :# c
SOME :# DROP :# c -> Just $ DROP :# c
LEFT :# DROP :# c -> Just $ DROP :# c
RIGHT :# DROP :# c -> Just $ DROP :# c
SIZE :# DROP :# c -> Just $ DROP :# c
GETN _ :# DROP :# c -> Just $ DROP :# c
CAST :# DROP :# c -> Just $ DROP :# c
RENAME :# DROP :# c -> Just $ DROP :# c
PACK :# DROP :# c -> Just $ DROP :# c
UNPACK :# DROP :# c -> Just $ DROP :# c
CONCAT' :# DROP :# c -> Just $ DROP :# c
ISNAT :# DROP :# c -> Just $ DROP :# c
ABS :# DROP :# c -> Just $ DROP :# c
NEG :# DROP :# c -> Just $ DROP :# c
NOT :# DROP :# c -> Just $ DROP :# c
EQ :# DROP :# c -> Just $ DROP :# c
NEQ :# DROP :# c -> Just $ DROP :# c
LT :# DROP :# c -> Just $ DROP :# c
GT :# DROP :# c -> Just $ DROP :# c
LE :# DROP :# c -> Just $ DROP :# c
GE :# DROP :# c -> Just $ DROP :# c
INT :# DROP :# c -> Just $ DROP :# c
CONTRACT _ :# DROP :# c -> Just $ DROP :# c
SET_DELEGATE :# DROP :# c -> Just $ DROP :# c
IMPLICIT_ACCOUNT :# DROP :# c -> Just $ DROP :# c
VOTING_POWER :# DROP :# c -> Just $ DROP :# c
SHA256 :# DROP :# c -> Just $ DROP :# c
SHA512 :# DROP :# c -> Just $ DROP :# c
BLAKE2B :# DROP :# c -> Just $ DROP :# c
SHA3 :# DROP :# c -> Just $ DROP :# c
KECCAK :# DROP :# c -> Just $ DROP :# c
HASH_KEY :# DROP :# c -> Just $ DROP :# c
PAIRING_CHECK :# DROP :# c -> Just $ DROP :# c
ADDRESS :# DROP :# c -> Just $ DROP :# c
JOIN_TICKETS :# DROP :# c -> Just $ DROP :# c
CAR :# DROP -> Just DROP
CDR :# DROP -> Just DROP
SOME :# DROP -> Just DROP
LEFT :# DROP -> Just DROP
RIGHT :# DROP -> Just DROP
SIZE :# DROP -> Just DROP
GETN _ :# DROP -> Just DROP
CAST :# DROP -> Just DROP
RENAME :# DROP -> Just DROP
PACK :# DROP -> Just DROP
UNPACK :# DROP -> Just DROP
CONCAT' :# DROP -> Just DROP
ISNAT :# DROP -> Just DROP
ABS :# DROP -> Just DROP
NEG :# DROP -> Just DROP
NOT :# DROP -> Just DROP
EQ :# DROP -> Just DROP
NEQ :# DROP -> Just DROP
LT :# DROP -> Just DROP
GT :# DROP -> Just DROP
LE :# DROP -> Just DROP
GE :# DROP -> Just DROP
INT :# DROP -> Just DROP
CONTRACT _ :# DROP -> Just DROP
SET_DELEGATE :# DROP -> Just DROP
IMPLICIT_ACCOUNT :# DROP -> Just DROP
VOTING_POWER :# DROP -> Just DROP
SHA256 :# DROP -> Just DROP
SHA512 :# DROP -> Just DROP
BLAKE2B :# DROP -> Just DROP
SHA3 :# DROP -> Just DROP
KECCAK :# DROP -> Just DROP
HASH_KEY :# DROP -> Just DROP
PAIRING_CHECK :# DROP -> Just DROP
ADDRESS :# DROP -> Just DROP
JOIN_TICKETS :# DROP -> Just DROP
_ -> Nothing
justDoubleDrops :: Rule
justDoubleDrops = Rule $ \case
PAIR :# DROP :# c -> Just $ DROP :# DROP :# c
MEM :# DROP :# c -> Just $ DROP :# DROP :# c
GET :# DROP :# c -> Just $ DROP :# DROP :# c
APPLY :# DROP :# c -> Just $ DROP :# DROP :# c
CONCAT :# DROP :# c -> Just $ DROP :# DROP :# c
ADD :# DROP :# c -> Just $ DROP :# DROP :# c
SUB :# DROP :# c -> Just $ DROP :# DROP :# c
SUB_MUTEZ :# DROP :# c -> Just $ DROP :# DROP :# c
MUL :# DROP :# c -> Just $ DROP :# DROP :# c
EDIV :# DROP :# c -> Just $ DROP :# DROP :# c
OR :# DROP :# c -> Just $ DROP :# DROP :# c
AND :# DROP :# c -> Just $ DROP :# DROP :# c
XOR :# DROP :# c -> Just $ DROP :# DROP :# c
COMPARE :# DROP :# c -> Just $ DROP :# DROP :# c
TICKET :# DROP :# c -> Just $ DROP :# DROP :# c
SPLIT_TICKET :# DROP :# c -> Just $ DROP :# DROP :# c
SWAP :# DROP :# DROP :# c -> Just $ DROP :# DROP :# c
PAIR :# DROP -> Just $ DROP :# DROP
MEM :# DROP -> Just $ DROP :# DROP
GET :# DROP -> Just $ DROP :# DROP
APPLY :# DROP -> Just $ DROP :# DROP
CONCAT :# DROP -> Just $ DROP :# DROP
ADD :# DROP -> Just $ DROP :# DROP
SUB :# DROP -> Just $ DROP :# DROP
SUB_MUTEZ :# DROP -> Just $ DROP :# DROP
MUL :# DROP -> Just $ DROP :# DROP
EDIV :# DROP -> Just $ DROP :# DROP
OR :# DROP -> Just $ DROP :# DROP
AND :# DROP -> Just $ DROP :# DROP
XOR :# DROP -> Just $ DROP :# DROP
COMPARE :# DROP -> Just $ DROP :# DROP
TICKET :# DROP -> Just $ DROP :# DROP
SPLIT_TICKET :# DROP -> Just $ DROP :# DROP
SWAP :# DROP :# DROP -> Just $ DROP :# DROP
_ -> Nothing
-- | Append LHS of v'Seq' to RHS and re-run pointwise ocRuleset at each point.
-- That might cause reinvocation of this function (see 'defaultRules'),
-- but effectively this ensures it will flatten any v'Seq'-tree right-to-left,
-- while evaling no more than once on each node.
--
-- The reason this function invokes ocRuleset is when you append an instr
-- to already-optimised RHS of v'Seq', you might get an optimisable tree.
--
-- The argument is a local, non-structurally-recursive ocRuleset.
linearizeAndReapply :: Rule -> Instr inp out -> Instr inp out
linearizeAndReapply restart = \case
Seq (Seq a b) c ->
applyOnce restart $ Seq a (linearizeAndReapply restart (Seq b c))
other -> applyOnce restart other
----------------------------------------------------------------------------
-- Generic functions working with rules
----------------------------------------------------------------------------
-- | Combine two rule fixpoints.
orRule :: (Rule -> Rule) -> (Rule -> Rule) -> (Rule -> Rule)
orRule l r topl = Rule $ \instr ->
(unRule (l topl) $ instr) <|> (unRule (r topl) $ instr)
-- | Combine a rule fixpoint and a simple rule.
orSimpleRule :: (Rule -> Rule) -> Rule -> (Rule -> Rule)
orSimpleRule l r topl = Rule $ \instr ->
(unRule (l topl) $ instr) <|> (unRule r $ instr)
-- | Turn rule fixpoint into rule.
fixpoint :: (Rule -> Rule) -> Rule
fixpoint r = go
where
go :: Rule
go = whileApplies (r go)
-- | Apply the rule once, if it fails, return the instruction unmodified.
applyOnce :: Rule -> Instr inp out -> Instr inp out
applyOnce r i = maybe i id (unRule r $ i)
-- | Apply a rule to the same code, until it fails.
whileApplies :: Rule -> Rule
whileApplies r = Rule go
where
go :: Instr inp out -> Maybe (Instr inp out)
go i = maybe (Just i) go (unRule r $ i)
----------------------------------------------------------------------------
-- TH
----------------------------------------------------------------------------
makeLensesFor [("ocGotoValues", "ocGotoValuesL")] ''OptimizerConf