mirror of
https://github.com/anoma/juvix.git
synced 2024-08-16 11:40:36 +03:00
Peephole optimization of Cairo assembly (#2858)
* Closes #2703 * Adds [peephole optimization](https://en.wikipedia.org/wiki/Peephole_optimization) of Cairo assembly. * Adds a transformation framework for the CASM IR. * Adds `--transforms`, `--run` and `--no-print` options to the `dev casm read` command.
This commit is contained in:
parent
4dcbb002fe
commit
802d82f22e
@ -2,7 +2,11 @@ module Commands.Dev.Casm.Read where
|
|||||||
|
|
||||||
import Commands.Base
|
import Commands.Base
|
||||||
import Commands.Dev.Casm.Read.Options
|
import Commands.Dev.Casm.Read.Options
|
||||||
import Juvix.Compiler.Casm.Pretty qualified as Casm
|
import Juvix.Compiler.Casm.Data.InputInfo qualified as Casm
|
||||||
|
import Juvix.Compiler.Casm.Extra.LabelInfo qualified as Casm
|
||||||
|
import Juvix.Compiler.Casm.Interpreter qualified as Casm
|
||||||
|
import Juvix.Compiler.Casm.Pretty qualified as Casm.Pretty
|
||||||
|
import Juvix.Compiler.Casm.Transformation qualified as Casm
|
||||||
import Juvix.Compiler.Casm.Translation.FromSource qualified as Casm
|
import Juvix.Compiler.Casm.Translation.FromSource qualified as Casm
|
||||||
import Juvix.Compiler.Casm.Validate qualified as Casm
|
import Juvix.Compiler.Casm.Validate qualified as Casm
|
||||||
|
|
||||||
@ -15,7 +19,28 @@ runCommand opts = do
|
|||||||
Right (labi, code) ->
|
Right (labi, code) ->
|
||||||
case Casm.validate labi code of
|
case Casm.validate labi code of
|
||||||
Left err -> exitJuvixError (JuvixError err)
|
Left err -> exitJuvixError (JuvixError err)
|
||||||
Right () -> renderStdOut (Casm.ppProgram code)
|
Right () -> do
|
||||||
|
r <-
|
||||||
|
runError @JuvixError
|
||||||
|
. runReader Casm.defaultOptions
|
||||||
|
$ (Casm.applyTransformations (project opts ^. casmReadTransformations) code)
|
||||||
|
case r of
|
||||||
|
Left err -> exitJuvixError (JuvixError err)
|
||||||
|
Right code' -> do
|
||||||
|
unless (project opts ^. casmReadNoPrint) $
|
||||||
|
renderStdOut (Casm.Pretty.ppProgram code')
|
||||||
|
doRun code'
|
||||||
where
|
where
|
||||||
file :: AppPath File
|
file :: AppPath File
|
||||||
file = opts ^. casmReadInputFile
|
file = opts ^. casmReadInputFile
|
||||||
|
|
||||||
|
doRun :: Casm.Code -> Sem r ()
|
||||||
|
doRun code'
|
||||||
|
| project opts ^. casmReadRun = do
|
||||||
|
putStrLn "--------------------------------"
|
||||||
|
putStrLn "| Run |"
|
||||||
|
putStrLn "--------------------------------"
|
||||||
|
let labi = Casm.computeLabelInfo code'
|
||||||
|
inputInfo = Casm.InputInfo mempty
|
||||||
|
print (Casm.runCode inputInfo labi code')
|
||||||
|
| otherwise = return ()
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
module Commands.Dev.Casm.Read.Options where
|
module Commands.Dev.Casm.Read.Options where
|
||||||
|
|
||||||
import CommonOptions
|
import CommonOptions
|
||||||
|
import Juvix.Compiler.Casm.Data.TransformationId
|
||||||
|
|
||||||
newtype CasmReadOptions = CasmReadOptions
|
data CasmReadOptions = CasmReadOptions
|
||||||
{ _casmReadInputFile :: AppPath File
|
{ _casmReadTransformations :: [TransformationId],
|
||||||
|
_casmReadRun :: Bool,
|
||||||
|
_casmReadNoPrint :: Bool,
|
||||||
|
_casmReadInputFile :: AppPath File
|
||||||
}
|
}
|
||||||
deriving stock (Data)
|
deriving stock (Data)
|
||||||
|
|
||||||
@ -11,5 +15,8 @@ makeLenses ''CasmReadOptions
|
|||||||
|
|
||||||
parseCasmReadOptions :: Parser CasmReadOptions
|
parseCasmReadOptions :: Parser CasmReadOptions
|
||||||
parseCasmReadOptions = do
|
parseCasmReadOptions = do
|
||||||
|
_casmReadNoPrint <- optReadNoPrint
|
||||||
|
_casmReadRun <- optReadRun
|
||||||
|
_casmReadTransformations <- optCasmTransformationIds
|
||||||
_casmReadInputFile <- parseInputFile FileExtCasm
|
_casmReadInputFile <- parseInputFile FileExtCasm
|
||||||
pure CasmReadOptions {..}
|
pure CasmReadOptions {..}
|
||||||
|
@ -15,16 +15,8 @@ makeLenses ''RegReadOptions
|
|||||||
|
|
||||||
parseRegReadOptions :: Parser RegReadOptions
|
parseRegReadOptions :: Parser RegReadOptions
|
||||||
parseRegReadOptions = do
|
parseRegReadOptions = do
|
||||||
_regReadNoPrint <-
|
_regReadNoPrint <- optReadNoPrint
|
||||||
switch
|
_regReadRun <- optReadRun
|
||||||
( long "no-print"
|
|
||||||
<> help "Do not print the transformed code"
|
|
||||||
)
|
|
||||||
_regReadRun <-
|
|
||||||
switch
|
|
||||||
( long "run"
|
|
||||||
<> help "Run the code after the transformation"
|
|
||||||
)
|
|
||||||
_regReadTransformations <- optRegTransformationIds
|
_regReadTransformations <- optRegTransformationIds
|
||||||
_regReadInputFile <- parseInputFile FileExtJuvixReg
|
_regReadInputFile <- parseInputFile FileExtJuvixReg
|
||||||
pure RegReadOptions {..}
|
pure RegReadOptions {..}
|
||||||
|
@ -10,6 +10,7 @@ where
|
|||||||
import Control.Exception qualified as GHC
|
import Control.Exception qualified as GHC
|
||||||
import Data.List.NonEmpty qualified as NonEmpty
|
import Data.List.NonEmpty qualified as NonEmpty
|
||||||
import GHC.Conc
|
import GHC.Conc
|
||||||
|
import Juvix.Compiler.Casm.Data.TransformationId.Parser qualified as Casm
|
||||||
import Juvix.Compiler.Concrete.Translation.ImportScanner
|
import Juvix.Compiler.Concrete.Translation.ImportScanner
|
||||||
import Juvix.Compiler.Core.Data.TransformationId.Parser qualified as Core
|
import Juvix.Compiler.Core.Data.TransformationId.Parser qualified as Core
|
||||||
import Juvix.Compiler.Pipeline.EntryPoint
|
import Juvix.Compiler.Pipeline.EntryPoint
|
||||||
@ -282,6 +283,20 @@ optNoDisambiguate =
|
|||||||
<> help "Don't disambiguate the names of bound variables"
|
<> help "Don't disambiguate the names of bound variables"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
optReadRun :: Parser Bool
|
||||||
|
optReadRun =
|
||||||
|
switch
|
||||||
|
( long "run"
|
||||||
|
<> help "Run the code after the transformation"
|
||||||
|
)
|
||||||
|
|
||||||
|
optReadNoPrint :: Parser Bool
|
||||||
|
optReadNoPrint =
|
||||||
|
switch
|
||||||
|
( long "no-print"
|
||||||
|
<> help "Do not print the transformed code"
|
||||||
|
)
|
||||||
|
|
||||||
optTransformationIds :: forall a. (Text -> Either Text [a]) -> (String -> [String]) -> Parser [a]
|
optTransformationIds :: forall a. (Text -> Either Text [a]) -> (String -> [String]) -> Parser [a]
|
||||||
optTransformationIds parseIds completions =
|
optTransformationIds parseIds completions =
|
||||||
option
|
option
|
||||||
@ -317,6 +332,9 @@ optTreeTransformationIds = optTransformationIds Tree.parseTransformations Tree.c
|
|||||||
optRegTransformationIds :: Parser [Reg.TransformationId]
|
optRegTransformationIds :: Parser [Reg.TransformationId]
|
||||||
optRegTransformationIds = optTransformationIds Reg.parseTransformations Reg.completionsString
|
optRegTransformationIds = optTransformationIds Reg.parseTransformations Reg.completionsString
|
||||||
|
|
||||||
|
optCasmTransformationIds :: Parser [Casm.TransformationId]
|
||||||
|
optCasmTransformationIds = optTransformationIds Casm.parseTransformations Casm.completionsString
|
||||||
|
|
||||||
class EntryPointOptions a where
|
class EntryPointOptions a where
|
||||||
applyOptions :: a -> EntryPoint -> EntryPoint
|
applyOptions :: a -> EntryPoint -> EntryPoint
|
||||||
|
|
||||||
|
@ -114,6 +114,9 @@ juvix_ec_op:
|
|||||||
-- [fp - 3]: closure
|
-- [fp - 3]: closure
|
||||||
-- [fp - 4]: n = the number of arguments to extend with
|
-- [fp - 4]: n = the number of arguments to extend with
|
||||||
-- [fp - 4 - k]: argument n - k - 1 (reverse order!) (k is 0-based)
|
-- [fp - 4 - k]: argument n - k - 1 (reverse order!) (k is 0-based)
|
||||||
|
-- On return:
|
||||||
|
-- [ap - 1]: new closure
|
||||||
|
-- This procedure doesn't accept or return the builtins pointer.
|
||||||
juvix_extend_closure:
|
juvix_extend_closure:
|
||||||
-- copy stored args reversing them;
|
-- copy stored args reversing them;
|
||||||
-- to copy the stored args to the new closure
|
-- to copy the stored args to the new closure
|
||||||
|
34
src/Juvix/Compiler/Casm/Data/TransformationId.hs
Normal file
34
src/Juvix/Compiler/Casm/Data/TransformationId.hs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
module Juvix.Compiler.Casm.Data.TransformationId where
|
||||||
|
|
||||||
|
import Juvix.Compiler.Casm.Data.TransformationId.Strings
|
||||||
|
import Juvix.Compiler.Core.Data.TransformationId.Base
|
||||||
|
import Juvix.Prelude
|
||||||
|
|
||||||
|
data TransformationId
|
||||||
|
= IdentityTrans
|
||||||
|
| Peephole
|
||||||
|
deriving stock (Data, Bounded, Enum, Show)
|
||||||
|
|
||||||
|
data PipelineId
|
||||||
|
= PipelineCairo
|
||||||
|
deriving stock (Data, Bounded, Enum)
|
||||||
|
|
||||||
|
type TransformationLikeId = TransformationLikeId' TransformationId PipelineId
|
||||||
|
|
||||||
|
toCairoTransformations :: [TransformationId]
|
||||||
|
toCairoTransformations = [Peephole]
|
||||||
|
|
||||||
|
instance TransformationId' TransformationId where
|
||||||
|
transformationText :: TransformationId -> Text
|
||||||
|
transformationText = \case
|
||||||
|
IdentityTrans -> strIdentity
|
||||||
|
Peephole -> strPeephole
|
||||||
|
|
||||||
|
instance PipelineId' TransformationId PipelineId where
|
||||||
|
pipelineText :: PipelineId -> Text
|
||||||
|
pipelineText = \case
|
||||||
|
PipelineCairo -> strCairoPipeline
|
||||||
|
|
||||||
|
pipeline :: PipelineId -> [TransformationId]
|
||||||
|
pipeline = \case
|
||||||
|
PipelineCairo -> toCairoTransformations
|
14
src/Juvix/Compiler/Casm/Data/TransformationId/Parser.hs
Normal file
14
src/Juvix/Compiler/Casm/Data/TransformationId/Parser.hs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
module Juvix.Compiler.Casm.Data.TransformationId.Parser (parseTransformations, TransformationId (..), completions, completionsString) where
|
||||||
|
|
||||||
|
import Juvix.Compiler.Casm.Data.TransformationId
|
||||||
|
import Juvix.Compiler.Core.Data.TransformationId.Parser.Base
|
||||||
|
import Juvix.Prelude
|
||||||
|
|
||||||
|
parseTransformations :: Text -> Either Text [TransformationId]
|
||||||
|
parseTransformations = parseTransformations' @TransformationId @PipelineId
|
||||||
|
|
||||||
|
completionsString :: String -> [String]
|
||||||
|
completionsString = completionsString' @TransformationId @PipelineId
|
||||||
|
|
||||||
|
completions :: Text -> [Text]
|
||||||
|
completions = completions' @TransformationId @PipelineId
|
12
src/Juvix/Compiler/Casm/Data/TransformationId/Strings.hs
Normal file
12
src/Juvix/Compiler/Casm/Data/TransformationId/Strings.hs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
module Juvix.Compiler.Casm.Data.TransformationId.Strings where
|
||||||
|
|
||||||
|
import Juvix.Prelude
|
||||||
|
|
||||||
|
strCairoPipeline :: Text
|
||||||
|
strCairoPipeline = "pipeline-cairo"
|
||||||
|
|
||||||
|
strIdentity :: Text
|
||||||
|
strIdentity = "identity"
|
||||||
|
|
||||||
|
strPeephole :: Text
|
||||||
|
strPeephole = "peephole"
|
17
src/Juvix/Compiler/Casm/Pipeline.hs
Normal file
17
src/Juvix/Compiler/Casm/Pipeline.hs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
module Juvix.Compiler.Casm.Pipeline
|
||||||
|
( module Juvix.Compiler.Casm.Pipeline,
|
||||||
|
Options,
|
||||||
|
Code,
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import Juvix.Compiler.Casm.Transformation
|
||||||
|
import Juvix.Compiler.Pipeline.EntryPoint (EntryPoint)
|
||||||
|
|
||||||
|
-- | Perform transformations on CASM necessary before the translation to Cairo
|
||||||
|
-- bytecode
|
||||||
|
toCairo' :: Code -> Sem r Code
|
||||||
|
toCairo' = applyTransformations toCairoTransformations
|
||||||
|
|
||||||
|
toCairo :: (Member (Reader EntryPoint) r) => Code -> Sem r Code
|
||||||
|
toCairo = mapReader fromEntryPoint . toCairo'
|
18
src/Juvix/Compiler/Casm/Transformation.hs
Normal file
18
src/Juvix/Compiler/Casm/Transformation.hs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
module Juvix.Compiler.Casm.Transformation
|
||||||
|
( module Juvix.Compiler.Casm.Transformation.Base,
|
||||||
|
module Juvix.Compiler.Casm.Transformation,
|
||||||
|
module Juvix.Compiler.Casm.Data.TransformationId,
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import Juvix.Compiler.Casm.Data.TransformationId
|
||||||
|
import Juvix.Compiler.Casm.Transformation.Base
|
||||||
|
import Juvix.Compiler.Casm.Transformation.Optimize.Peephole
|
||||||
|
|
||||||
|
applyTransformations :: forall r. [TransformationId] -> Code -> Sem r Code
|
||||||
|
applyTransformations ts tbl = foldM (flip appTrans) tbl ts
|
||||||
|
where
|
||||||
|
appTrans :: TransformationId -> Code -> Sem r Code
|
||||||
|
appTrans = \case
|
||||||
|
IdentityTrans -> return
|
||||||
|
Peephole -> return . peephole
|
17
src/Juvix/Compiler/Casm/Transformation/Base.hs
Normal file
17
src/Juvix/Compiler/Casm/Transformation/Base.hs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
module Juvix.Compiler.Casm.Transformation.Base
|
||||||
|
( module Juvix.Compiler.Casm.Transformation.Base,
|
||||||
|
module Juvix.Compiler.Casm.Language,
|
||||||
|
module Juvix.Compiler.Tree.Options,
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import Juvix.Compiler.Casm.Language
|
||||||
|
import Juvix.Compiler.Tree.Options
|
||||||
|
|
||||||
|
mapT :: ([Instruction] -> [Instruction]) -> [Instruction] -> [Instruction]
|
||||||
|
mapT f = go
|
||||||
|
where
|
||||||
|
go :: [Instruction] -> [Instruction]
|
||||||
|
go = \case
|
||||||
|
i : is -> f (i : go is)
|
||||||
|
[] -> f []
|
78
src/Juvix/Compiler/Casm/Transformation/Optimize/Peephole.hs
Normal file
78
src/Juvix/Compiler/Casm/Transformation/Optimize/Peephole.hs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
module Juvix.Compiler.Casm.Transformation.Optimize.Peephole where
|
||||||
|
|
||||||
|
import Juvix.Compiler.Casm.Extra.Base
|
||||||
|
import Juvix.Compiler.Casm.Language
|
||||||
|
import Juvix.Compiler.Casm.Transformation.Base
|
||||||
|
|
||||||
|
peephole :: [Instruction] -> [Instruction]
|
||||||
|
peephole = mapT go
|
||||||
|
where
|
||||||
|
go :: [Instruction] -> [Instruction]
|
||||||
|
go = \case
|
||||||
|
Nop : is -> is
|
||||||
|
Jump InstrJump {..} : lab@(Label LabelRef {..}) : is
|
||||||
|
| not _instrJumpIncAp,
|
||||||
|
Val (Lab (LabelRef sym _)) <- _instrJumpTarget,
|
||||||
|
sym == _labelRefSymbol ->
|
||||||
|
lab : is
|
||||||
|
Call InstrCall {..} : Return : Assign a1 : Return : is
|
||||||
|
| _instrCallRel,
|
||||||
|
Imm 3 <- _instrCallTarget,
|
||||||
|
Just k1 <- getAssignApFp a1 ->
|
||||||
|
fixAssignAp $
|
||||||
|
mkAssignAp (Val (Ref (MemRef Ap k1)))
|
||||||
|
: Return
|
||||||
|
: is
|
||||||
|
Call InstrCall {..} : Return : Assign a1 : Assign a2 : Return : is
|
||||||
|
| _instrCallRel,
|
||||||
|
Imm 3 <- _instrCallTarget,
|
||||||
|
Just k1 <- getAssignApFp a1,
|
||||||
|
Just k2 <- getAssignApFp a2 ->
|
||||||
|
fixAssignAp $
|
||||||
|
mkAssignAp (Val (Ref (MemRef Ap k1)))
|
||||||
|
: mkAssignAp (Val (Ref (MemRef Ap (k2 - 1))))
|
||||||
|
: Return
|
||||||
|
: is
|
||||||
|
Call InstrCall {..} : Return : Jump InstrJump {..} : is
|
||||||
|
| _instrCallRel,
|
||||||
|
Imm 3 <- _instrCallTarget,
|
||||||
|
Val tgt@(Lab {}) <- _instrJumpTarget,
|
||||||
|
not _instrJumpIncAp ->
|
||||||
|
let call =
|
||||||
|
InstrCall
|
||||||
|
{ _instrCallTarget = tgt,
|
||||||
|
_instrCallRel = _instrJumpRel
|
||||||
|
}
|
||||||
|
in Call call : Return : is
|
||||||
|
is -> is
|
||||||
|
|
||||||
|
fixAssignAp :: [Instruction] -> [Instruction]
|
||||||
|
fixAssignAp = \case
|
||||||
|
Assign a : Return : is
|
||||||
|
| Just (-1) <- getAssignAp Ap a ->
|
||||||
|
Return : is
|
||||||
|
Assign a1 : Assign a2 : Return : is
|
||||||
|
| Just (-2) <- getAssignAp Ap a1,
|
||||||
|
Just (-2) <- getAssignAp Ap a2 ->
|
||||||
|
Return : is
|
||||||
|
Assign a1 : Assign a2 : Return : is
|
||||||
|
| Just (-1) <- getAssignAp Ap a1,
|
||||||
|
Just (-3) <- getAssignAp Ap a2 ->
|
||||||
|
mkAssignAp (Val (Ref (MemRef Ap (-2)))) : Return : is
|
||||||
|
is -> is
|
||||||
|
|
||||||
|
getAssignAp :: Reg -> InstrAssign -> Maybe Offset
|
||||||
|
getAssignAp reg InstrAssign {..}
|
||||||
|
| MemRef Ap 0 <- _instrAssignResult,
|
||||||
|
Val (Ref (MemRef r k)) <- _instrAssignValue,
|
||||||
|
r == reg,
|
||||||
|
_instrAssignIncAp =
|
||||||
|
Just k
|
||||||
|
| otherwise =
|
||||||
|
Nothing
|
||||||
|
|
||||||
|
getAssignApFp :: InstrAssign -> Maybe Offset
|
||||||
|
getAssignApFp instr = case getAssignAp Fp instr of
|
||||||
|
Just k
|
||||||
|
| k <= -3 -> Just (k + 2)
|
||||||
|
_ -> Nothing
|
@ -164,16 +164,18 @@ fromReg tab = mkResult $ run $ runLabelInfoBuilderWithNextId (Reg.getNextSymbolI
|
|||||||
|
|
||||||
-- To ensure that memory is accessed sequentially at all times, we divide
|
-- To ensure that memory is accessed sequentially at all times, we divide
|
||||||
-- instructions into basic blocks. Within each basic block, the `ap` offset
|
-- instructions into basic blocks. Within each basic block, the `ap` offset
|
||||||
-- is known at each instruction, which allows to statically associate `fp`
|
-- (i.e. how much `ap` increased since the start of the basic block) is
|
||||||
-- offsets to variables while still generating only sequential assignments
|
-- known at each instruction, which allows to statically associate `fp`
|
||||||
-- to `[ap]` with increasing `ap`. When the `ap` offset can no longer be
|
-- offsets (i.e. offsets relative to `fp`) to variables while still
|
||||||
-- statically determined for new variables (e.g. due to an intervening
|
-- generating only sequential assignments to `[ap]` with increasing `ap`.
|
||||||
-- recursive call), we switch to the next basic block by "calling" it with
|
-- When the `ap` offset can no longer be statically determined for new
|
||||||
-- the `call` instruction (see `goCallBlock`). The arguments of the basic
|
-- variables (e.g. due to an intervening recursive call), we switch to the
|
||||||
-- block call are the variables live at the beginning of the block. Note
|
-- next basic block by "calling" it with the `call` instruction (see
|
||||||
-- that the `fp` offsets of "old" variables are still statically determined
|
-- `goCallBlock`). The arguments of the basic block call are the variables
|
||||||
-- even after the current `ap` offset becomes unknown -- the arbitrary
|
-- live at the beginning of the block. Note that the `fp` offsets of "old"
|
||||||
-- increase of `ap` does not influence the previous variable associations.
|
-- variables are still statically determined even after the current `ap`
|
||||||
|
-- offset becomes unknown -- the arbitrary increase of `ap` does not
|
||||||
|
-- influence the previous variable associations.
|
||||||
goBlock :: forall r. (Members '[LabelInfoBuilder, CasmBuilder, Output Instruction] r) => StdlibBuiltins -> LabelRef -> HashSet Reg.VarRef -> Maybe Reg.VarRef -> Reg.Block -> Sem r ()
|
goBlock :: forall r. (Members '[LabelInfoBuilder, CasmBuilder, Output Instruction] r) => StdlibBuiltins -> LabelRef -> HashSet Reg.VarRef -> Maybe Reg.VarRef -> Reg.Block -> Sem r ()
|
||||||
goBlock blts failLab liveVars0 mout Reg.Block {..} = do
|
goBlock blts failLab liveVars0 mout Reg.Block {..} = do
|
||||||
mapM_ goInstr _blockBody
|
mapM_ goInstr _blockBody
|
||||||
@ -645,7 +647,10 @@ fromReg tab = mkResult $ run $ runLabelInfoBuilderWithNextId (Reg.getNextSymbolI
|
|||||||
ap0 <- getAP
|
ap0 <- getAP
|
||||||
vars <- getVars
|
vars <- getVars
|
||||||
bltOff <- getBuiltinOffset
|
bltOff <- getBuiltinOffset
|
||||||
mapM_ (goCaseBranch ap0 vars bltOff symMap labEnd) _instrCaseBranches
|
-- reversing `_instrCaseBranches` typically results in better
|
||||||
|
-- opportunities for peephole optimization (the last jump to branch
|
||||||
|
-- may be removed by the peephole optimizer)
|
||||||
|
mapM_ (goCaseBranch ap0 vars bltOff symMap labEnd) (reverse _instrCaseBranches)
|
||||||
mapM_ (goDefaultLabel symMap) defaultTags
|
mapM_ (goDefaultLabel symMap) defaultTags
|
||||||
whenJust _instrCaseDefault $
|
whenJust _instrCaseDefault $
|
||||||
goLocalBlock ap0 vars bltOff liveVars _instrCaseOutVar
|
goLocalBlock ap0 vars bltOff liveVars _instrCaseOutVar
|
||||||
|
@ -23,6 +23,7 @@ import Juvix.Compiler.Backend.Rust.Translation.FromReg qualified as Rust
|
|||||||
import Juvix.Compiler.Backend.VampIR.Translation qualified as VampIR
|
import Juvix.Compiler.Backend.VampIR.Translation qualified as VampIR
|
||||||
import Juvix.Compiler.Casm.Data.Builtins qualified as Casm
|
import Juvix.Compiler.Casm.Data.Builtins qualified as Casm
|
||||||
import Juvix.Compiler.Casm.Data.Result qualified as Casm
|
import Juvix.Compiler.Casm.Data.Result qualified as Casm
|
||||||
|
import Juvix.Compiler.Casm.Pipeline qualified as Casm
|
||||||
import Juvix.Compiler.Casm.Translation.FromReg qualified as Casm
|
import Juvix.Compiler.Casm.Translation.FromReg qualified as Casm
|
||||||
import Juvix.Compiler.Concrete.Data.Highlight.Input
|
import Juvix.Compiler.Concrete.Data.Highlight.Input
|
||||||
import Juvix.Compiler.Concrete.Language
|
import Juvix.Compiler.Concrete.Language
|
||||||
@ -364,17 +365,25 @@ regToCasm = Reg.toCasm >=> return . Casm.fromReg
|
|||||||
regToCasm' :: (Member (Reader Reg.Options) r) => Reg.InfoTable -> Sem r Casm.Result
|
regToCasm' :: (Member (Reader Reg.Options) r) => Reg.InfoTable -> Sem r Casm.Result
|
||||||
regToCasm' = Reg.toCasm' >=> return . Casm.fromReg
|
regToCasm' = Reg.toCasm' >=> return . Casm.fromReg
|
||||||
|
|
||||||
casmToCairo :: Casm.Result -> Sem r Cairo.Result
|
casmToCairo :: (Member (Reader EntryPoint) r) => Casm.Result -> Sem r Cairo.Result
|
||||||
casmToCairo Casm.Result {..} =
|
casmToCairo Casm.Result {..} = do
|
||||||
|
code' <- Casm.toCairo _resultCode
|
||||||
return
|
return
|
||||||
. Cairo.serialize _resultOutputSize (map Casm.builtinName _resultBuiltins)
|
. Cairo.serialize _resultOutputSize (map Casm.builtinName _resultBuiltins)
|
||||||
$ Cairo.fromCasm _resultCode
|
$ Cairo.fromCasm code'
|
||||||
|
|
||||||
|
casmToCairo' :: Casm.Result -> Sem r Cairo.Result
|
||||||
|
casmToCairo' Casm.Result {..} = do
|
||||||
|
code' <- Casm.toCairo' _resultCode
|
||||||
|
return
|
||||||
|
. Cairo.serialize _resultOutputSize (map Casm.builtinName _resultBuiltins)
|
||||||
|
$ Cairo.fromCasm code'
|
||||||
|
|
||||||
regToCairo :: (Member (Reader EntryPoint) r) => Reg.InfoTable -> Sem r Cairo.Result
|
regToCairo :: (Member (Reader EntryPoint) r) => Reg.InfoTable -> Sem r Cairo.Result
|
||||||
regToCairo = regToCasm >=> casmToCairo
|
regToCairo = regToCasm >=> casmToCairo
|
||||||
|
|
||||||
regToCairo' :: (Member (Reader Reg.Options) r) => Reg.InfoTable -> Sem r Cairo.Result
|
regToCairo' :: (Member (Reader Reg.Options) r) => Reg.InfoTable -> Sem r Cairo.Result
|
||||||
regToCairo' = regToCasm' >=> casmToCairo
|
regToCairo' = regToCasm' >=> casmToCairo'
|
||||||
|
|
||||||
treeToAnoma' :: (Members '[Error JuvixError, Reader NockmaTree.CompilerOptions] r) => Tree.InfoTable -> Sem r NockmaTree.AnomaResult
|
treeToAnoma' :: (Members '[Error JuvixError, Reader NockmaTree.CompilerOptions] r) => Tree.InfoTable -> Sem r NockmaTree.AnomaResult
|
||||||
treeToAnoma' = Tree.toNockma >=> NockmaTree.fromTreeTable
|
treeToAnoma' = Tree.toNockma >=> NockmaTree.fromTreeTable
|
||||||
|
@ -9,6 +9,7 @@ import Juvix.Compiler.Casm.Extra.InputInfo
|
|||||||
import Juvix.Compiler.Casm.Interpreter
|
import Juvix.Compiler.Casm.Interpreter
|
||||||
import Juvix.Compiler.Casm.Translation.FromSource
|
import Juvix.Compiler.Casm.Translation.FromSource
|
||||||
import Juvix.Compiler.Casm.Validate
|
import Juvix.Compiler.Casm.Validate
|
||||||
|
import Juvix.Compiler.Tree.Options qualified as Casm
|
||||||
import Juvix.Data.Field
|
import Juvix.Data.Field
|
||||||
import Juvix.Data.PPOutput
|
import Juvix.Data.PPOutput
|
||||||
import Juvix.Parser.Error
|
import Juvix.Parser.Error
|
||||||
@ -27,14 +28,15 @@ casmRunVM labi instrs blts outputSize inputFile expectedFile step = do
|
|||||||
step "Serialize to Cairo bytecode"
|
step "Serialize to Cairo bytecode"
|
||||||
let res =
|
let res =
|
||||||
run $
|
run $
|
||||||
casmToCairo
|
runReader Casm.defaultOptions $
|
||||||
( Casm.Result
|
casmToCairo'
|
||||||
{ _resultLabelInfo = labi,
|
( Casm.Result
|
||||||
_resultCode = instrs,
|
{ _resultLabelInfo = labi,
|
||||||
_resultBuiltins = blts,
|
_resultCode = instrs,
|
||||||
_resultOutputSize = outputSize
|
_resultBuiltins = blts,
|
||||||
}
|
_resultOutputSize = outputSize
|
||||||
)
|
}
|
||||||
|
)
|
||||||
outputFile = dirPath <//> $(mkRelFile "out.json")
|
outputFile = dirPath <//> $(mkRelFile "out.json")
|
||||||
encodeFile (toFilePath outputFile) res
|
encodeFile (toFilePath outputFile) res
|
||||||
step "Run Cairo VM"
|
step "Run Cairo VM"
|
||||||
|
@ -166,5 +166,13 @@ tests =
|
|||||||
$(mkRelDir ".")
|
$(mkRelDir ".")
|
||||||
$(mkRelFile "test016.casm")
|
$(mkRelFile "test016.casm")
|
||||||
$(mkRelFile "out/test016.out")
|
$(mkRelFile "out/test016.out")
|
||||||
(Just $(mkRelFile "in/test016.json"))
|
(Just $(mkRelFile "in/test016.json")),
|
||||||
|
PosTest
|
||||||
|
"Test017: Peephole optimization"
|
||||||
|
True
|
||||||
|
True
|
||||||
|
$(mkRelDir ".")
|
||||||
|
$(mkRelFile "test017.casm")
|
||||||
|
$(mkRelFile "out/test017.out")
|
||||||
|
Nothing
|
||||||
]
|
]
|
||||||
|
1
tests/Casm/positive/out/test017.out
Normal file
1
tests/Casm/positive/out/test017.out
Normal file
@ -0,0 +1 @@
|
|||||||
|
7
|
29
tests/Casm/positive/test017.casm
Normal file
29
tests/Casm/positive/test017.casm
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
-- peephole optimization
|
||||||
|
|
||||||
|
call main
|
||||||
|
jmp end
|
||||||
|
|
||||||
|
main:
|
||||||
|
jmp lab_1
|
||||||
|
lab_1:
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
jmp lab_2
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
lab_2:
|
||||||
|
call rel 3
|
||||||
|
ret
|
||||||
|
nop
|
||||||
|
jmp lab_3
|
||||||
|
lab_4:
|
||||||
|
[ap] = 7; ap++
|
||||||
|
call rel 3
|
||||||
|
ret
|
||||||
|
nop
|
||||||
|
[ap] = [fp - 3]; ap++
|
||||||
|
ret
|
||||||
|
lab_3:
|
||||||
|
jmp lab_4
|
||||||
|
|
||||||
|
end:
|
Loading…
Reference in New Issue
Block a user