1
1
mirror of https://github.com/anoma/juvix.git synced 2024-08-18 04:30:28 +03:00

JuvixReg to CASM translation (#2671)

* Closes #2562 

Checklist
---------

- [x] Translation from JuvixReg to CASM
- [x] CASM runtime
- [x] Juvix to CASM pipeline: combine the right transformations and
check prerequisites
- [x] CLI commands: add target `casm` to the `compile` commands
- [x] Tests:
   - [x] Test the translation from JuvixReg to CASM
   - [x] Test the entire pipeline from Juvix to CASM
This commit is contained in:
Łukasz Czajka 2024-03-20 12:14:12 +01:00 committed by GitHub
parent b615fde186
commit 0f713c7c84
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
254 changed files with 6140 additions and 160 deletions

View File

@ -29,6 +29,7 @@ runCommand opts@CompileOptions {..} = do
TargetReg -> Compile.runRegPipeline arg TargetReg -> Compile.runRegPipeline arg
TargetNockma -> Compile.runNockmaPipeline arg TargetNockma -> Compile.runNockmaPipeline arg
TargetAnoma -> Compile.runAnomaPipeline arg TargetAnoma -> Compile.runAnomaPipeline arg
TargetCasm -> Compile.runCasmPipeline arg
writeCoreFile :: (Members '[EmbedIO, App, TaggedLock] r) => Compile.PipelineArg -> Sem r () writeCoreFile :: (Members '[EmbedIO, App, TaggedLock] r) => Compile.PipelineArg -> Sem r ()
writeCoreFile pa@Compile.PipelineArg {..} = do writeCoreFile pa@Compile.PipelineArg {..} = do

View File

@ -6,7 +6,10 @@ import Commands.Extra.Compile qualified as Compile
import Juvix.Compiler.Asm.Translation.FromSource qualified as Asm import Juvix.Compiler.Asm.Translation.FromSource qualified as Asm
import Juvix.Compiler.Backend qualified as Backend import Juvix.Compiler.Backend qualified as Backend
import Juvix.Compiler.Backend.C qualified as C import Juvix.Compiler.Backend.C qualified as C
import Juvix.Compiler.Casm.Data.Result qualified as Casm
import Juvix.Compiler.Casm.Pretty qualified as Casm
import Juvix.Compiler.Reg.Pretty qualified as Reg import Juvix.Compiler.Reg.Pretty qualified as Reg
import Juvix.Prelude.Pretty
runCommand :: forall r. (Members '[EmbedIO, App, TaggedLock] r) => AsmCompileOptions -> Sem r () runCommand :: forall r. (Members '[EmbedIO, App, TaggedLock] r) => AsmCompileOptions -> Sem r ()
runCommand opts = do runCommand opts = do
@ -34,6 +37,15 @@ runCommand opts = do
tab' <- getRight r tab' <- getRight r
let code = Reg.ppPrint tab' tab' let code = Reg.ppPrint tab' tab'
writeFileEnsureLn regFile code writeFileEnsureLn regFile code
TargetCasm -> do
casmFile <- Compile.outputFile opts file
r <-
runReader entryPoint
. runError @JuvixError
. asmToCasm
$ tab
Casm.Result {..} <- getRight r
writeFileEnsureLn casmFile (toPlainText $ Casm.ppProgram _resultCode)
_ -> _ ->
case run $ runReader entryPoint $ runError $ asmToMiniC tab of case run $ runReader entryPoint $ runError $ asmToMiniC tab of
Left err -> exitJuvixError err Left err -> exitJuvixError err
@ -57,6 +69,7 @@ runCommand opts = do
TargetWasm32Wasi -> return Backend.TargetCWasm32Wasi TargetWasm32Wasi -> return Backend.TargetCWasm32Wasi
TargetNative64 -> return Backend.TargetCNative64 TargetNative64 -> return Backend.TargetCNative64
TargetReg -> return Backend.TargetReg TargetReg -> return Backend.TargetReg
TargetCasm -> return Backend.TargetCairo
TargetNockma -> err "Nockma" TargetNockma -> err "Nockma"
TargetAnoma -> err "Anoma" TargetAnoma -> err "Anoma"
TargetTree -> err "JuvixTree" TargetTree -> err "JuvixTree"

View File

@ -15,7 +15,8 @@ asmSupportedTargets =
NonEmpty.fromList NonEmpty.fromList
[ TargetWasm32Wasi, [ TargetWasm32Wasi,
TargetNative64, TargetNative64,
TargetReg TargetReg,
TargetCasm
] ]
parseAsmCompileOptions :: Parser AsmCompileOptions parseAsmCompileOptions :: Parser AsmCompileOptions

View File

@ -23,6 +23,7 @@ runCommand opts = do
TargetTree -> runTreePipeline arg TargetTree -> runTreePipeline arg
TargetNockma -> runNockmaPipeline arg TargetNockma -> runNockmaPipeline arg
TargetAnoma -> runAnomaPipeline arg TargetAnoma -> runAnomaPipeline arg
TargetCasm -> runCasmPipeline arg
where where
getFile :: Sem r (Path Abs File) getFile :: Sem r (Path Abs File)
getFile = getMainFile (opts ^. compileInputFile) getFile = getMainFile (opts ^. compileInputFile)

View File

@ -8,11 +8,14 @@ import Juvix.Compiler.Backend qualified as Backend
import Juvix.Compiler.Backend.C qualified as C import Juvix.Compiler.Backend.C qualified as C
import Juvix.Compiler.Backend.Geb qualified as Geb import Juvix.Compiler.Backend.Geb qualified as Geb
import Juvix.Compiler.Backend.VampIR.Translation qualified as VampIR import Juvix.Compiler.Backend.VampIR.Translation qualified as VampIR
import Juvix.Compiler.Casm.Data.Result qualified as Casm
import Juvix.Compiler.Casm.Pretty qualified as Casm
import Juvix.Compiler.Core.Data.Module qualified as Core import Juvix.Compiler.Core.Data.Module qualified as Core
import Juvix.Compiler.Core.Data.TransformationId qualified as Core import Juvix.Compiler.Core.Data.TransformationId qualified as Core
import Juvix.Compiler.Nockma.Pretty qualified as Nockma import Juvix.Compiler.Nockma.Pretty qualified as Nockma
import Juvix.Compiler.Reg.Pretty qualified as Reg import Juvix.Compiler.Reg.Pretty qualified as Reg
import Juvix.Compiler.Tree.Pretty qualified as Tree import Juvix.Compiler.Tree.Pretty qualified as Tree
import Juvix.Prelude.Pretty
import System.FilePath (takeBaseName) import System.FilePath (takeBaseName)
data PipelineArg = PipelineArg data PipelineArg = PipelineArg
@ -45,6 +48,7 @@ getEntry PipelineArg {..} = do
TargetTree -> Backend.TargetTree TargetTree -> Backend.TargetTree
TargetNockma -> Backend.TargetNockma TargetNockma -> Backend.TargetNockma
TargetAnoma -> Backend.TargetAnoma TargetAnoma -> Backend.TargetAnoma
TargetCasm -> Backend.TargetCairo
defaultOptLevel :: Int defaultOptLevel :: Int
defaultOptLevel defaultOptLevel
@ -168,3 +172,15 @@ runAnomaPipeline pa@PipelineArg {..} = do
tab' <- getRight r tab' <- getRight r
let code = Nockma.ppSerialize tab' let code = Nockma.ppSerialize tab'
writeFileEnsureLn nockmaFile code writeFileEnsureLn nockmaFile code
runCasmPipeline :: (Members '[Embed IO, App, TaggedLock] r) => PipelineArg -> Sem r ()
runCasmPipeline pa@PipelineArg {..} = do
entryPoint <- getEntry pa
casmFile <- Compile.outputFile _pipelineArgOptions _pipelineArgFile
r <-
runReader entryPoint
. runError @JuvixError
. coreToCasm
$ _pipelineArgModule
Casm.Result {..} <- getRight r
writeFileEnsureLn casmFile (toPlainText $ Casm.ppProgram _resultCode)

View File

@ -19,7 +19,8 @@ coreSupportedTargets =
TargetVampIR, TargetVampIR,
TargetTree, TargetTree,
TargetAsm, TargetAsm,
TargetReg TargetReg,
TargetCasm
] ]
parseCoreCompileOptions :: Parser CoreCompileOptions parseCoreCompileOptions :: Parser CoreCompileOptions

View File

@ -1,11 +1,13 @@
module Commands.Dev.Reg where module Commands.Dev.Reg where
import Commands.Base import Commands.Base
import Commands.Dev.Reg.Compile as Compile
import Commands.Dev.Reg.Options import Commands.Dev.Reg.Options
import Commands.Dev.Reg.Read as Read import Commands.Dev.Reg.Read as Read
import Commands.Dev.Reg.Run as Run import Commands.Dev.Reg.Run as Run
runCommand :: forall r. (Members '[EmbedIO, App, TaggedLock] r) => RegCommand -> Sem r () runCommand :: forall r. (Members '[EmbedIO, App, TaggedLock] r) => RegCommand -> Sem r ()
runCommand = \case runCommand = \case
Compile opts -> Compile.runCommand opts
Run opts -> Run.runCommand opts Run opts -> Run.runCommand opts
Read opts -> Read.runCommand opts Read opts -> Read.runCommand opts

View File

@ -0,0 +1,79 @@
module Commands.Dev.Reg.Compile where
import Commands.Base
import Commands.Dev.Reg.Compile.Options
import Commands.Extra.Compile qualified as Compile
import Juvix.Compiler.Backend qualified as Backend
import Juvix.Compiler.Backend.C qualified as C
import Juvix.Compiler.Casm.Data.Result qualified as Casm
import Juvix.Compiler.Casm.Pretty qualified as Casm
import Juvix.Compiler.Reg.Translation.FromSource qualified as Reg
import Juvix.Prelude.Pretty
runCommand :: forall r. (Members '[EmbedIO, App, TaggedLock] r) => CompileOptions -> Sem r ()
runCommand opts = do
file <- getFile
s <- readFile file
case Reg.runParser file s of
Left err -> exitJuvixError (JuvixError err)
Right tab -> do
ep <- getEntryPoint (AppPath (preFileFromAbs file) True)
tgt <- getTarget (opts ^. compileTarget)
let entryPoint :: EntryPoint
entryPoint =
ep
{ _entryPointTarget = tgt,
_entryPointDebug = opts ^. compileDebug
}
case opts ^. compileTarget of
TargetCasm -> do
casmFile <- Compile.outputFile opts file
r <-
runReader entryPoint
. runError @JuvixError
. regToCasm
$ tab
Casm.Result {..} <- getRight r
writeFileEnsureLn casmFile (toPlainText $ Casm.ppProgram _resultCode)
_ ->
case run $ runReader entryPoint $ runError $ regToMiniC tab of
Left err -> exitJuvixError err
Right C.MiniCResult {..} -> do
buildDir <- askBuildDir
ensureDir buildDir
cFile <- inputCFile file
writeFileEnsureLn cFile _resultCCode
outfile <- Compile.outputFile opts file
Compile.runCommand
opts
{ _compileInputFile = Just (AppPath (preFileFromAbs cFile) False),
_compileOutputFile = Just (AppPath (preFileFromAbs outfile) False)
}
where
getFile :: Sem r (Path Abs File)
getFile = getMainFile (opts ^. compileInputFile)
getTarget :: CompileTarget -> Sem r Backend.Target
getTarget = \case
TargetWasm32Wasi -> return Backend.TargetCWasm32Wasi
TargetNative64 -> return Backend.TargetCNative64
TargetCasm -> return Backend.TargetCairo
TargetReg -> err "JuvixReg"
TargetNockma -> err "Nockma"
TargetAnoma -> err "Anoma"
TargetTree -> err "JuvixTree"
TargetGeb -> err "GEB"
TargetVampIR -> err "VampIR"
TargetCore -> err "JuvixCore"
TargetAsm -> err "JuvixAsm"
where
err :: Text -> Sem r a
err tgt = exitMsg (ExitFailure 1) ("error: " <> tgt <> " target not supported for JuvixReg")
inputCFile :: (Members '[App] r) => Path Abs File -> Sem r (Path Abs File)
inputCFile inputFileCompile = do
buildDir <- askBuildDir
return (buildDir <//> outputMiniCFile)
where
outputMiniCFile :: Path Rel File
outputMiniCFile = replaceExtension' ".c" (filename inputFileCompile)

View File

@ -0,0 +1,23 @@
module Commands.Dev.Reg.Compile.Options
( module Commands.Dev.Reg.Compile.Options,
module Commands.Extra.Compile.Options,
)
where
import Commands.Extra.Compile.Options
import CommonOptions
import Data.List.NonEmpty qualified as NonEmpty
regSupportedTargets :: NonEmpty CompileTarget
regSupportedTargets =
NonEmpty.fromList
[ TargetWasm32Wasi,
TargetNative64,
TargetCasm
]
parseRegCompileOptions :: Parser CompileOptions
parseRegCompileOptions =
parseCompileOptions
regSupportedTargets
(parseInputFile FileExtJuvixReg)

View File

@ -1,11 +1,13 @@
module Commands.Dev.Reg.Options where module Commands.Dev.Reg.Options where
import Commands.Dev.Reg.Compile.Options
import Commands.Dev.Reg.Read.Options import Commands.Dev.Reg.Read.Options
import Commands.Dev.Reg.Run.Options import Commands.Dev.Reg.Run.Options
import CommonOptions import CommonOptions
data RegCommand data RegCommand
= Run RegRunOptions = Compile CompileOptions
| Run RegRunOptions
| Read RegReadOptions | Read RegReadOptions
deriving stock (Data) deriving stock (Data)
@ -13,16 +15,26 @@ parseRegCommand :: Parser RegCommand
parseRegCommand = parseRegCommand =
hsubparser $ hsubparser $
mconcat mconcat
[ commandRun, [ commandCompile,
commandRun,
commandRead commandRead
] ]
where where
commandCompile :: Mod CommandFields RegCommand
commandCompile = command "compile" compileInfo
commandRun :: Mod CommandFields RegCommand commandRun :: Mod CommandFields RegCommand
commandRun = command "run" runInfo commandRun = command "run" runInfo
commandRead :: Mod CommandFields RegCommand commandRead :: Mod CommandFields RegCommand
commandRead = command "read" readInfo commandRead = command "read" readInfo
compileInfo :: ParserInfo RegCommand
compileInfo =
info
(Compile <$> parseRegCompileOptions)
(progDesc "Compile a JuvixReg file")
runInfo :: ParserInfo RegCommand runInfo :: ParserInfo RegCommand
runInfo = runInfo =
info info

View File

@ -22,6 +22,7 @@ runCommand opts = do
TargetTree -> return () TargetTree -> return ()
TargetNockma -> runNockmaPipeline arg TargetNockma -> runNockmaPipeline arg
TargetAnoma -> runAnomaPipeline arg TargetAnoma -> runAnomaPipeline arg
TargetCasm -> runCasmPipeline arg
where where
getFile :: Sem r (Path Abs File) getFile :: Sem r (Path Abs File)
getFile = getMainFile (opts ^. compileInputFile) getFile = getMainFile (opts ^. compileInputFile)

View File

@ -6,9 +6,12 @@ import Commands.Extra.Compile qualified as Compile
import Juvix.Compiler.Asm.Pretty qualified as Asm import Juvix.Compiler.Asm.Pretty qualified as Asm
import Juvix.Compiler.Backend qualified as Backend import Juvix.Compiler.Backend qualified as Backend
import Juvix.Compiler.Backend.C qualified as C import Juvix.Compiler.Backend.C qualified as C
import Juvix.Compiler.Casm.Data.Result qualified as Casm
import Juvix.Compiler.Casm.Pretty qualified as Casm
import Juvix.Compiler.Nockma.Pretty qualified as Nockma import Juvix.Compiler.Nockma.Pretty qualified as Nockma
import Juvix.Compiler.Reg.Pretty qualified as Reg import Juvix.Compiler.Reg.Pretty qualified as Reg
import Juvix.Compiler.Tree.Data.InfoTable qualified as Tree import Juvix.Compiler.Tree.Data.InfoTable qualified as Tree
import Juvix.Prelude.Pretty
data PipelineArg = PipelineArg data PipelineArg = PipelineArg
{ _pipelineArgOptions :: CompileOptions, { _pipelineArgOptions :: CompileOptions,
@ -40,6 +43,7 @@ getEntry PipelineArg {..} = do
TargetTree -> Backend.TargetTree TargetTree -> Backend.TargetTree
TargetNockma -> Backend.TargetNockma TargetNockma -> Backend.TargetNockma
TargetAnoma -> Backend.TargetAnoma TargetAnoma -> Backend.TargetAnoma
TargetCasm -> Backend.TargetCairo
defaultOptLevel :: Int defaultOptLevel :: Int
defaultOptLevel defaultOptLevel
@ -125,3 +129,15 @@ runAnomaPipeline pa@PipelineArg {..} = do
tab' <- getRight r tab' <- getRight r
let code = Nockma.ppSerialize tab' let code = Nockma.ppSerialize tab'
writeFileEnsureLn nockmaFile code writeFileEnsureLn nockmaFile code
runCasmPipeline :: (Members '[Embed IO, App, TaggedLock] r) => PipelineArg -> Sem r ()
runCasmPipeline pa@PipelineArg {..} = do
entryPoint <- getEntry pa
casmFile <- Compile.outputFile _pipelineArgOptions _pipelineArgFile
r <-
runReader entryPoint
. runError @JuvixError
. treeToCasm
$ _pipelineArgTable
Casm.Result {..} <- getRight r
writeFileEnsureLn casmFile (toPlainText $ Casm.ppProgram _resultCode)

View File

@ -14,7 +14,8 @@ treeSupportedTargets =
TargetNative64, TargetNative64,
TargetAsm, TargetAsm,
TargetReg, TargetReg,
TargetNockma TargetNockma,
TargetCasm
] ]
parseTreeCompileOptions :: Parser CompileOptions parseTreeCompileOptions :: Parser CompileOptions

View File

@ -37,6 +37,7 @@ runCompile inputFile o = do
TargetTree -> return (Right ()) TargetTree -> return (Right ())
TargetNockma -> return (Right ()) TargetNockma -> return (Right ())
TargetAnoma -> return (Right ()) TargetAnoma -> return (Right ())
TargetCasm -> return (Right ())
prepareRuntime :: forall r. (Members '[App, EmbedIO] r) => Path Abs Dir -> CompileOptions -> Sem r () prepareRuntime :: forall r. (Members '[App, EmbedIO] r) => Path Abs Dir -> CompileOptions -> Sem r ()
prepareRuntime buildDir o = do prepareRuntime buildDir o = do
@ -56,6 +57,7 @@ prepareRuntime buildDir o = do
TargetTree -> return () TargetTree -> return ()
TargetNockma -> return () TargetNockma -> return ()
TargetAnoma -> return () TargetAnoma -> return ()
TargetCasm -> return ()
where where
wasiReleaseRuntime :: BS.ByteString wasiReleaseRuntime :: BS.ByteString
wasiReleaseRuntime = $(FE.makeRelativeToProject "runtime/_build.wasm32-wasi/libjuvix.a" >>= FE.embedFile) wasiReleaseRuntime = $(FE.makeRelativeToProject "runtime/_build.wasm32-wasi/libjuvix.a" >>= FE.embedFile)
@ -121,6 +123,8 @@ outputFile opts inputFile =
replaceExtension' nockmaFileExt baseOutputFile replaceExtension' nockmaFileExt baseOutputFile
TargetAnoma -> TargetAnoma ->
replaceExtension' nockmaFileExt baseOutputFile replaceExtension' nockmaFileExt baseOutputFile
TargetCasm ->
replaceExtension' casmFileExt baseOutputFile
clangNativeCompile :: clangNativeCompile ::
forall r. forall r.

View File

@ -15,6 +15,7 @@ data CompileTarget
| TargetTree | TargetTree
| TargetNockma | TargetNockma
| TargetAnoma | TargetAnoma
| TargetCasm
deriving stock (Eq, Data, Bounded, Enum) deriving stock (Eq, Data, Bounded, Enum)
instance Show CompileTarget where instance Show CompileTarget where
@ -29,6 +30,7 @@ instance Show CompileTarget where
TargetTree -> "tree" TargetTree -> "tree"
TargetNockma -> "nockma" TargetNockma -> "nockma"
TargetAnoma -> "anoma" TargetAnoma -> "anoma"
TargetCasm -> "casm"
data CompileOptions = CompileOptions data CompileOptions = CompileOptions
{ _compileDebug :: Bool, { _compileDebug :: Bool,

View File

@ -11,8 +11,9 @@ function count_ext () {
RUNTIME_C=$(count runtime/src/juvix) RUNTIME_C=$(count runtime/src/juvix)
RUNTIME_VAMPIR=$(count_ext '*.pir' runtime/src/vampir) RUNTIME_VAMPIR=$(count_ext '*.pir' runtime/src/vampir)
RUNTIME_JVT=$(count_ext '*.jvt' runtime/src/tree) RUNTIME_JVT=$(count_ext '*.jvt' runtime/src/tree)
RUNTIME_CASM=$(count_ext '*.casm' runtime/src/casm)
RUNTIME=$((RUNTIME_C+RUNTIME_VAMPIR+RUNTIME_JVT)) RUNTIME=$((RUNTIME_C+RUNTIME_VAMPIR+RUNTIME_JVT+RUNTIME_CASM))
BACKENDC=$(count src/Juvix/Compiler/Backend/C/) BACKENDC=$(count src/Juvix/Compiler/Backend/C/)
CAIRO=$(count src/Juvix/Compiler/Backend/Cairo/) CAIRO=$(count src/Juvix/Compiler/Backend/Cairo/)
@ -62,6 +63,7 @@ echo " JuvixCore: $CORE LOC"
echo "Runtime: $RUNTIME LOC" echo "Runtime: $RUNTIME LOC"
echo " C runtime: $RUNTIME_C LOC" echo " C runtime: $RUNTIME_C LOC"
echo " JuvixTree runtime: $RUNTIME_JVT LOC" echo " JuvixTree runtime: $RUNTIME_JVT LOC"
echo " Cairo assembly runtime: $RUNTIME_CASM LOC"
echo " VampIR runtime: $RUNTIME_VAMPIR LOC" echo " VampIR runtime: $RUNTIME_VAMPIR LOC"
echo "Other: $OTHER LOC" echo "Other: $OTHER LOC"
echo " Application: $APP LOC" echo " Application: $APP LOC"
@ -72,4 +74,4 @@ echo " Data: $DATA LOC"
echo " Prelude: $PRELUDE LOC" echo " Prelude: $PRELUDE LOC"
echo "Tests: $TESTS LOC" echo "Tests: $TESTS LOC"
echo "" echo ""
echo "Total: $TOTAL Haskell LOC + $RUNTIME_C C LOC + $RUNTIME_JVT JuvixTree LOC + $RUNTIME_VAMPIR VampIR LOC" echo "Total: $TOTAL Haskell LOC + $RUNTIME_C C LOC + $RUNTIME_JVT JuvixTree LOC + $RUNTIME_CASM CASM LOC + $RUNTIME_VAMPIR VampIR LOC"

View File

@ -40,6 +40,7 @@ extra-source-files:
- runtime/**/*.a - runtime/**/*.a
- runtime/src/tree/*.jvt - runtime/src/tree/*.jvt
- runtime/src/vampir/*.pir - runtime/src/vampir/*.pir
- runtime/src/casm/*.casm
dependencies: dependencies:
- aeson-better-errors == 0.9.* - aeson-better-errors == 0.9.*

View File

@ -0,0 +1,99 @@
-- Juvix runtime for Cairo assembly
-- Closure layout: [ addr | 9 - sargs | 9 - argsnum | arguments... ]
-- sargs -- number of arguments stored in the closure
-- argsnum -- number of arguments left
-- sargs + argsnum = total numer of arguments for the function
-- Maximum number of function arguments: 8
-- after calling juvix_get_regs:
-- [ap - 4] == fp
-- [ap - 3] == pc
-- [ap - 2] == ap - 2
juvix_get_regs:
call juvix_get_ap_reg
ret
juvix_get_ap_reg:
ret
-- [fp - 3]: closure; [fp - 3 - k]: argument k to closure call
juvix_call_closure:
ap += 1
-- closure addr
[fp] = [[fp - 3]]
-- jmp rel (9 - argsnum)
jmp rel [[fp - 3] + 2]
[ap] = [fp - 11]; ap++
[ap] = [fp - 10]; ap++
[ap] = [fp - 9]; ap++
[ap] = [fp - 8]; ap++
[ap] = [fp - 7]; ap++
[ap] = [fp - 6]; ap++
[ap] = [fp - 5]; ap++
[ap] = [fp - 4]; ap++
-- jmp rel (9 - sargs)
jmp rel [[fp - 3] + 1]
[ap] = [[fp - 3] + 10]; ap++
[ap] = [[fp - 3] + 9]; ap++
[ap] = [[fp - 3] + 8]; ap++
[ap] = [[fp - 3] + 7]; ap++
[ap] = [[fp - 3] + 6]; ap++
[ap] = [[fp - 3] + 5]; ap++
[ap] = [[fp - 3] + 4]; ap++
[ap] = [[fp - 3] + 3]; ap++
call [fp]
ret
-- [fp - 3]: closure
-- [fp - 4]: n = the number of arguments to extend with
-- [fp - 4 - k]: argument n - k - 1 (reverse order!)
juvix_extend_closure:
ap += 6
-- 9 - sargs
[fp + 1] = [[fp - 3] + 1]
-- 9 - argsnum (expected)
[fp + 2] = [[fp - 3] + 2]
[fp + 3] = 9
-- [fp + 4] = sargs
[fp + 4] = [fp + 3] - [fp + 1]
-- [fp + 5] = 9 - n
[fp + 5] = [fp + 3] - [fp - 4]
-- copy stored args reversing them
jmp rel [fp + 1]
[ap] = [[fp - 3] + 10]; ap++
[ap] = [[fp - 3] + 9]; ap++
[ap] = [[fp - 3] + 8]; ap++
[ap] = [[fp - 3] + 7]; ap++
[ap] = [[fp - 3] + 6]; ap++
[ap] = [[fp - 3] + 5]; ap++
[ap] = [[fp - 3] + 4]; ap++
[ap] = [[fp - 3] + 3]; ap++
call juvix_get_regs
[fp] = [ap - 2] + 2
-- closure header
[ap] = [[fp - 3]]; ap++
[ap] = [fp + 1] - [fp - 4]; ap++
[ap] = [fp + 2] + [fp - 4]; ap++
-- copy stored args: jmp rel (9 - sargs)
jmp rel [fp + 1]
[ap] = [fp + 13]; ap++
[ap] = [fp + 12]; ap++
[ap] = [fp + 11]; ap++
[ap] = [fp + 10]; ap++
[ap] = [fp + 9]; ap++
[ap] = [fp + 8]; ap++
[ap] = [fp + 7]; ap++
[ap] = [fp + 6]; ap++
-- copy extra args: jmp rel (9 - extra args num)
jmp rel [fp + 5]
[ap] = [fp - 12]; ap++
[ap] = [fp - 11]; ap++
[ap] = [fp - 10]; ap++
[ap] = [fp - 9]; ap++
[ap] = [fp - 8]; ap++
[ap] = [fp - 7]; ap++
[ap] = [fp - 6]; ap++
[ap] = [fp - 5]; ap++
-- return value
[ap] = [fp]; ap++
ret

View File

@ -156,18 +156,9 @@ runCodeR infoTable funInfo = goCode (funInfo ^. functionCode) >> popLastValueSta
goUnop :: (Member Runtime r) => (Val -> Either Text Val) -> Sem r () goUnop :: (Member Runtime r) => (Val -> Either Text Val) -> Sem r ()
goUnop op = goUnop' (eitherToError . op) goUnop op = goUnop' (eitherToError . op)
getConstantVal :: Constant -> Val
getConstantVal = \case
ConstInt i -> ValInteger i
ConstField f -> ValField f
ConstBool b -> ValBool b
ConstString s -> ValString s
ConstUnit -> ValUnit
ConstVoid -> ValVoid
getVal :: (Member Runtime r) => Value -> Sem r Val getVal :: (Member Runtime r) => Value -> Sem r Val
getVal = \case getVal = \case
Constant c -> return (getConstantVal c) Constant c -> return (constantToValue c)
Ref r -> getMemVal r Ref r -> getMemVal r
getMemVal :: forall r. (Member Runtime r) => MemRef -> Sem r Val getMemVal :: forall r. (Member Runtime r) => MemRef -> Sem r Val

View File

@ -14,6 +14,7 @@ data Target
| TargetTree | TargetTree
| TargetNockma | TargetNockma
| TargetAnoma | TargetAnoma
| TargetCairo
deriving stock (Data, Eq, Show) deriving stock (Data, Eq, Show)
data Limits = Limits data Limits = Limits
@ -97,6 +98,21 @@ getLimits tgt debug = case tgt of
defaultLimits defaultLimits
TargetAnoma -> TargetAnoma ->
defaultLimits defaultLimits
TargetCairo ->
Limits
{ _limitsMaxConstrs = 1048568,
_limitsMaxConstrArgs = 255,
_limitsMaxFunctionArgs = 8,
_limitsMaxLocalVars = 2048,
_limitsMaxClosureSize = 8 + 3,
_limitsClosureHeadSize = 3,
_limitsMaxStringSize = 0,
_limitsMaxStackDelta = 0, -- irrelevant
_limitsMaxFunctionAlloc = 0, -- irrelevant
_limitsDispatchStackSize = 0, -- irrelevant
_limitsBuiltinUIDsNum = 8,
_limitsSpecialisedApply = 3
}
defaultLimits :: Limits defaultLimits :: Limits
defaultLimits = defaultLimits =

View File

@ -29,6 +29,11 @@ emptyBuilderState =
_stateIdents = mempty _stateIdents = mempty
} }
runLabelInfoBuilderWithNextId :: Word -> Sem (LabelInfoBuilder ': r) a -> Sem r (LabelInfo, a)
runLabelInfoBuilderWithNextId nextSymId =
fmap (first (^. stateLabelInfo))
. runLabelInfoBuilder' emptyBuilderState {_stateNextSymbolId = nextSymId}
runLabelInfoBuilder :: Sem (LabelInfoBuilder ': r) a -> Sem r (LabelInfo, a) runLabelInfoBuilder :: Sem (LabelInfoBuilder ': r) a -> Sem r (LabelInfo, a)
runLabelInfoBuilder = fmap (first (^. stateLabelInfo)) . runLabelInfoBuilder' emptyBuilderState runLabelInfoBuilder = fmap (first (^. stateLabelInfo)) . runLabelInfoBuilder' emptyBuilderState

View File

@ -0,0 +1,16 @@
module Juvix.Compiler.Casm.Data.Limits where
import Juvix.Compiler.Backend
import Juvix.Compiler.Casm.Language
casmMaxFunctionArgs :: Int
casmMaxFunctionArgs = getLimits TargetCairo False ^. limitsMaxFunctionArgs
casmClosureAddrOffset :: Offset
casmClosureAddrOffset = 0
casmClosureStoredArgsOffset :: Offset
casmClosureStoredArgsOffset = 1
casmClosureArgsNumOffset :: Offset
casmClosureArgsNumOffset = 2

View File

@ -0,0 +1,11 @@
module Juvix.Compiler.Casm.Data.Result where
import Juvix.Compiler.Casm.Data.LabelInfo
import Juvix.Compiler.Casm.Language
data Result = Result
{ _resultLabelInfo :: LabelInfo,
_resultCode :: [Instruction]
}
makeLenses ''Result

View File

@ -0,0 +1,107 @@
module Juvix.Compiler.Casm.Extra.Base where
import Juvix.Compiler.Casm.Data.Limits
import Juvix.Compiler.Casm.Language
toOffset :: (Show a, Integral a) => a -> Offset
toOffset x
| fromIntegral v == x = v
| otherwise = error ("invalid offset: " <> show x)
where
v = fromIntegral x
adjustAp :: Int16 -> MemRef -> MemRef
adjustAp idx mr@MemRef {..} = case _memRefReg of
Ap -> MemRef Ap (_memRefOff - idx)
Fp -> mr
mkExtraBinop :: ExtraOpcode -> MemRef -> MemRef -> Value -> Instruction
mkExtraBinop op res arg1 arg2 =
ExtraBinop
InstrExtraBinop
{ _instrExtraBinopOpcode = op,
_instrExtraBinopResult = res,
_instrExtraBinopArg1 = arg1,
_instrExtraBinopArg2 = arg2,
_instrExtraBinopIncAp = False
}
mkNativeBinop :: Opcode -> MemRef -> MemRef -> Value -> Instruction
mkNativeBinop op res arg1 arg2 =
Assign
InstrAssign
{ _instrAssignResult = res,
_instrAssignValue =
Binop
BinopValue
{ _binopValueOpcode = op,
_binopValueArg1 = arg1,
_binopValueArg2 = arg2
},
_instrAssignIncAp = False
}
mkEq :: MemRef -> MemRef -> Value -> Instruction
mkEq res arg1 arg2 = mkExtraBinop FieldSub res arg1 arg2
mkIntLe :: MemRef -> MemRef -> Value -> [Instruction]
mkIntLe res arg1 arg2 = case arg2 of
Imm v ->
[mkExtraBinop IntLt res arg1 (Imm (v + 1))]
Ref mref ->
[inc, mkExtraBinop IntLt res (adjustAp 1 arg1) (Ref $ MemRef Ap (-1))]
where
inc =
Assign
InstrAssign
{ _instrAssignResult = MemRef Ap 0,
_instrAssignValue =
Binop
BinopValue
{ _binopValueArg1 = mref,
_binopValueArg2 = Imm 1,
_binopValueOpcode = FieldAdd
},
_instrAssignIncAp = True
}
Lab {} -> impossible
mkAssign :: MemRef -> RValue -> Instruction
mkAssign mr rv =
Assign
InstrAssign
{ _instrAssignResult = mr,
_instrAssignValue = rv,
_instrAssignIncAp = False
}
mkAssignAp :: RValue -> Instruction
mkAssignAp v =
Assign
InstrAssign
{ _instrAssignResult = MemRef Ap 0,
_instrAssignValue = v,
_instrAssignIncAp = True
}
mkOpArgsNum :: MemRef -> MemRef -> [Instruction]
mkOpArgsNum res v =
[ mkAssignAp (Val $ Imm $ fromIntegral casmMaxFunctionArgs + 1),
mkAssignAp (Load $ LoadValue (adjustAp 1 v) casmClosureArgsNumOffset),
mkExtraBinop FieldSub res (MemRef Ap (-2)) (Ref $ MemRef Ap (-1))
]
mkCall :: Value -> Instruction
mkCall = Call . InstrCall
mkJump :: RValue -> Instruction
mkJump tgt = Jump (InstrJump tgt False False)
mkJumpIf :: Value -> MemRef -> Instruction
mkJumpIf tgt v = JumpIf (InstrJumpIf tgt v False False)
mkJumpRel :: RValue -> Instruction
mkJumpRel tgt = Jump (InstrJump tgt True False)
mkJumpRelIf :: Value -> MemRef -> Instruction
mkJumpRelIf tgt v = JumpIf (InstrJumpIf tgt v True False)

View File

@ -0,0 +1,32 @@
module Juvix.Compiler.Casm.Extra.Stdlib where
import Data.FileEmbed qualified as FE
import Data.Text.Encoding
import Juvix.Compiler.Casm.Data.LabelInfoBuilder
import Juvix.Compiler.Casm.Language
import Juvix.Compiler.Casm.Translation.FromSource
data StdlibBuiltins = StdlibBuiltins
{ _stdlibGetRegs :: Symbol,
_stdlibCallClosure :: Symbol,
_stdlibExtendClosure :: Symbol,
_stdlibGetRegsName :: Text,
_stdlibCallClosureName :: Text,
_stdlibExtendClosureName :: Text
}
makeLenses ''StdlibBuiltins
addStdlibBuiltins :: (Member LabelInfoBuilder r) => Address -> Sem r (StdlibBuiltins, [Instruction])
addStdlibBuiltins addr = do
instrs <-
fmap (fromRight impossible) $
runParser' addr "stdlib.casm" $
decodeUtf8 $(FE.makeRelativeToProject "runtime/src/casm/stdlib.casm" >>= FE.embedFile)
let _stdlibGetRegsName :: Text = "juvix_get_regs"
_stdlibCallClosureName :: Text = "juvix_call_closure"
_stdlibExtendClosureName :: Text = "juvix_extend_closure"
_stdlibGetRegs <- fromJust <$> getIdent _stdlibGetRegsName
_stdlibCallClosure <- fromJust <$> getIdent _stdlibCallClosureName
_stdlibExtendClosure <- fromJust <$> getIdent _stdlibExtendClosureName
return (StdlibBuiltins {..}, instrs)

View File

@ -19,9 +19,12 @@ import Juvix.Data.Field
type Memory s = MV.MVector s (Maybe FField) type Memory s = MV.MVector s (Maybe FField)
-- | Runs Cairo Assembly. Returns the value of `[ap - 1]` at program exit.
runCode :: LabelInfo -> [Instruction] -> FField runCode :: LabelInfo -> [Instruction] -> FField
runCode (LabelInfo labelInfo) instrs0 = runST goCode runCode = hRunCode stderr
-- | Runs Cairo Assembly. Returns the value of `[ap - 1]` at program exit.
hRunCode :: Handle -> LabelInfo -> [Instruction] -> FField
hRunCode hout (LabelInfo labelInfo) instrs0 = runST goCode
where where
instrs :: Vec.Vector Instruction instrs :: Vec.Vector Instruction
instrs = Vec.fromList instrs0 instrs = Vec.fromList instrs0
@ -48,6 +51,8 @@ runCode (LabelInfo labelInfo) instrs0 = runST goCode
when (Vec.length instrs < pc) $ when (Vec.length instrs < pc) $
throwRunError ("invalid program counter: " <> show pc) throwRunError ("invalid program counter: " <> show pc)
checkGaps mem checkGaps mem
when (ap == 0) $
throwRunError "nothing to return"
readMem mem (ap - 1) readMem mem (ap - 1)
| otherwise = | otherwise =
case instrs Vec.! pc of case instrs Vec.! pc of
@ -55,7 +60,6 @@ runCode (LabelInfo labelInfo) instrs0 = runST goCode
ExtraBinop x -> goExtraBinop x pc ap fp mem ExtraBinop x -> goExtraBinop x pc ap fp mem
Jump x -> goJump x pc ap fp mem Jump x -> goJump x pc ap fp mem
JumpIf x -> goJumpIf x pc ap fp mem JumpIf x -> goJumpIf x pc ap fp mem
JumpRel x -> goJumpRel x pc ap fp mem
Call x -> goCall x pc ap fp mem Call x -> goCall x pc ap fp mem
Return -> goReturn pc ap fp mem Return -> goReturn pc ap fp mem
Alloc x -> goAlloc x pc ap fp mem Alloc x -> goAlloc x pc ap fp mem
@ -182,7 +186,7 @@ runCode (LabelInfo labelInfo) instrs0 = runST goCode
IntMod -> fieldFromInteger fsize (fieldToInt x `rem` fieldToInt y) IntMod -> fieldFromInteger fsize (fieldToInt x `rem` fieldToInt y)
IntLt -> IntLt ->
fieldFromInteger fsize $ fieldFromInteger fsize $
if fieldToInt x < fieldToInt y then 1 else 0 if fieldToInt x < fieldToInt y then 0 else 1
fieldToInt :: FField -> Integer fieldToInt :: FField -> Integer
fieldToInt f fieldToInt f
@ -192,20 +196,17 @@ runCode (LabelInfo labelInfo) instrs0 = runST goCode
v = fieldToInteger f v = fieldToInteger f
goJump :: InstrJump -> Address -> Address -> Address -> Memory s -> ST s FField goJump :: InstrJump -> Address -> Address -> Address -> Memory s -> ST s FField
goJump InstrJump {..} _ ap fp mem = do goJump InstrJump {..} pc ap fp mem = do
tgt <- readValue ap fp mem _instrJumpTarget tgt <- readRValue ap fp mem _instrJumpTarget
go (fromInteger (fieldToInteger tgt)) (ap + fromEnum _instrJumpIncAp) fp mem let off = if _instrJumpRel then pc else 0
go (off + fromInteger (fieldToInteger tgt)) (ap + fromEnum _instrJumpIncAp) fp mem
goJumpIf :: InstrJumpIf -> Address -> Address -> Address -> Memory s -> ST s FField goJumpIf :: InstrJumpIf -> Address -> Address -> Address -> Memory s -> ST s FField
goJumpIf InstrJumpIf {..} pc ap fp mem = do goJumpIf InstrJumpIf {..} pc ap fp mem = do
tgt <- readValue ap fp mem _instrJumpIfTarget tgt <- readValue ap fp mem _instrJumpIfTarget
v <- readMemRef ap fp mem _instrJumpIfValue v <- readMemRef ap fp mem _instrJumpIfValue
go (if fieldToInteger v /= 0 then fromInteger (fieldToInteger tgt) else pc + 1) (ap + fromEnum _instrJumpIfIncAp) fp mem let off = if _instrJumpIfRel then pc else 0
go (if fieldToInteger v /= 0 then off + fromInteger (fieldToInteger tgt) else pc + 1) (ap + fromEnum _instrJumpIfIncAp) fp mem
goJumpRel :: InstrJumpRel -> Address -> Address -> Address -> Memory s -> ST s FField
goJumpRel InstrJumpRel {..} pc ap fp mem = do
tgt <- readRValue ap fp mem _instrJumpRelTarget
go (pc + fromInteger (fieldToInteger tgt)) (ap + fromEnum _instrJumpRelIncAp) fp mem
goCall :: InstrCall -> Address -> Address -> Address -> Memory s -> ST s FField goCall :: InstrCall -> Address -> Address -> Address -> Memory s -> ST s FField
goCall InstrCall {..} pc ap fp mem = do goCall InstrCall {..} pc ap fp mem = do
@ -228,7 +229,7 @@ runCode (LabelInfo labelInfo) instrs0 = runST goCode
goTrace :: InstrTrace -> Address -> Address -> Address -> Memory s -> ST s FField goTrace :: InstrTrace -> Address -> Address -> Address -> Memory s -> ST s FField
goTrace InstrTrace {..} pc ap fp mem = do goTrace InstrTrace {..} pc ap fp mem = do
v <- readRValue ap fp mem _instrTraceValue v <- readRValue ap fp mem _instrTraceValue
GHC.unsafePerformIO (print v >> return (pure ())) GHC.unsafePerformIO (hPrint hout v >> return (pure ()))
go (pc + 1) ap fp mem go (pc + 1) ap fp mem
catchRunErrorIO :: a -> IO (Either CasmError a) catchRunErrorIO :: a -> IO (Either CasmError a)

View File

@ -7,10 +7,11 @@ where
{- {-
This module defines data structures for an extended subset of the Cairo Assembly This module defines data structures for an extended subset of the Cairo Assembly
language, following Section 5 of [1]. Except the `ExtraBinop` instruction, all language, following Section 5 of [1]. Except for the `ExtraBinop` instruction
instructions correspond to the instructions from [1]. The parser and pretty and absolute conditional jumps, all instructions correspond to the instructions
printer implemented in `Juvix.Compiler.Casm.Translation.FromSource` and from [1]. The parser and pretty printer implemented in
`Juvix.Compiler.Casm.Pretty` follow the syntax of [1, Section 5]. `Juvix.Compiler.Casm.Translation.FromSource` and `Juvix.Compiler.Casm.Pretty`
follow the syntax of [1, Section 5].
[1] Goldberg, Papini, Riabzev: "Cairo a Turing-complete STARK-friendly CPU [1] Goldberg, Papini, Riabzev: "Cairo a Turing-complete STARK-friendly CPU
architecture" (https://ia.cr/2021/1063) architecture" (https://ia.cr/2021/1063)
@ -78,7 +79,7 @@ data ExtraOpcode
| IntMul | IntMul
| IntDiv | IntDiv
| IntMod | IntMod
| -- | Sets the result to 1 if arg1 < arg2, or to 0 otherwise | -- | Sets the result to zero if arg1 < arg2, or to non-zero otherwise
IntLt IntLt
data Instruction data Instruction
@ -88,7 +89,6 @@ data Instruction
ExtraBinop InstrExtraBinop ExtraBinop InstrExtraBinop
| Jump InstrJump | Jump InstrJump
| JumpIf InstrJumpIf | JumpIf InstrJumpIf
| JumpRel InstrJumpRel
| Call InstrCall | Call InstrCall
| Return | Return
| Alloc InstrAlloc | Alloc InstrAlloc
@ -110,22 +110,20 @@ data InstrExtraBinop = InstrExtraBinop
} }
data InstrJump = InstrJump data InstrJump = InstrJump
{ _instrJumpTarget :: Value, { _instrJumpTarget :: RValue,
_instrJumpRel :: Bool,
_instrJumpIncAp :: Bool _instrJumpIncAp :: Bool
} }
-- | Jump if value is nonzero -- | Jump if value is nonzero. Boolean true is translated to zero, false to
-- non-zero.
data InstrJumpIf = InstrJumpIf data InstrJumpIf = InstrJumpIf
{ _instrJumpIfTarget :: Value, { _instrJumpIfTarget :: Value,
_instrJumpIfValue :: MemRef, _instrJumpIfValue :: MemRef,
_instrJumpIfRel :: Bool,
_instrJumpIfIncAp :: Bool _instrJumpIfIncAp :: Bool
} }
data InstrJumpRel = InstrJumpRel
{ _instrJumpRelTarget :: RValue,
_instrJumpRelIncAp :: Bool
}
newtype InstrCall = InstrCall newtype InstrCall = InstrCall
{ _instrCallTarget :: Value { _instrCallTarget :: Value
} }
@ -148,7 +146,6 @@ makeLenses ''InstrAssign
makeLenses ''InstrExtraBinop makeLenses ''InstrExtraBinop
makeLenses ''InstrJump makeLenses ''InstrJump
makeLenses ''InstrJumpIf makeLenses ''InstrJumpIf
makeLenses ''InstrJumpRel
makeLenses ''InstrCall makeLenses ''InstrCall
makeLenses ''InstrAlloc makeLenses ''InstrAlloc
makeLenses ''InstrTrace makeLenses ''InstrTrace

View File

@ -116,20 +116,16 @@ instance PrettyCode InstrJump where
ppCode InstrJump {..} = do ppCode InstrJump {..} = do
tgt <- ppCode _instrJumpTarget tgt <- ppCode _instrJumpTarget
incAp <- ppIncAp _instrJumpIncAp incAp <- ppIncAp _instrJumpIncAp
return $ Str.jmp <+> tgt <> incAp let rel = if _instrJumpRel then Str.rel <> space else mempty
return $ Str.jmp <+> rel <> tgt <> incAp
instance PrettyCode InstrJumpIf where instance PrettyCode InstrJumpIf where
ppCode InstrJumpIf {..} = do ppCode InstrJumpIf {..} = do
tgt <- ppCode _instrJumpIfTarget tgt <- ppCode _instrJumpIfTarget
v <- ppCode _instrJumpIfValue v <- ppCode _instrJumpIfValue
incAp <- ppIncAp _instrJumpIfIncAp incAp <- ppIncAp _instrJumpIfIncAp
return $ Str.jmp <+> tgt <+> Str.if_ <+> v <+> Str.notequal <+> annotate AnnLiteralInteger "0" <> incAp let rel = if _instrJumpIfRel then Str.rel <> space else mempty
return $ Str.jmp <+> rel <> tgt <+> Str.if_ <+> v <+> Str.notequal <+> annotate AnnLiteralInteger "0" <> incAp
instance PrettyCode InstrJumpRel where
ppCode InstrJumpRel {..} = do
tgt <- ppCode _instrJumpRelTarget
incAp <- ppIncAp _instrJumpRelIncAp
return $ Str.jmp <+> Str.rel <+> tgt <> incAp
instance PrettyCode InstrCall where instance PrettyCode InstrCall where
ppCode InstrCall {..} = do ppCode InstrCall {..} = do
@ -152,7 +148,6 @@ instance PrettyCode Instruction where
ExtraBinop x -> ppCode x ExtraBinop x -> ppCode x
Jump x -> ppCode x Jump x -> ppCode x
JumpIf x -> ppCode x JumpIf x -> ppCode x
JumpRel x -> ppCode x
Call x -> ppCode x Call x -> ppCode x
Return -> return Str.ret Return -> return Str.ret
Alloc x -> ppCode x Alloc x -> ppCode x

View File

@ -1 +1,377 @@
module Juvix.Compiler.Casm.Translation.FromReg where module Juvix.Compiler.Casm.Translation.FromReg where
import Data.HashMap.Strict qualified as HashMap
import Data.HashSet qualified as HashSet
import Juvix.Compiler.Backend
import Juvix.Compiler.Casm.Data.LabelInfoBuilder
import Juvix.Compiler.Casm.Data.Limits
import Juvix.Compiler.Casm.Data.Result
import Juvix.Compiler.Casm.Extra.Base
import Juvix.Compiler.Casm.Extra.Stdlib
import Juvix.Compiler.Casm.Language
import Juvix.Compiler.Reg.Data.InfoTable qualified as Reg
import Juvix.Compiler.Reg.Extra.Info qualified as Reg
import Juvix.Compiler.Reg.Language qualified as Reg
import Juvix.Compiler.Tree.Evaluator.Builtins qualified as Reg
import Juvix.Data.Field
fromReg :: Reg.InfoTable -> Result
fromReg tab = uncurry Result $ run $ runLabelInfoBuilderWithNextId (Reg.getNextSymbolId tab) $ do
let initialOffset :: Int = 2
(blts, binstrs) <- addStdlibBuiltins initialOffset
endSym <- freshSymbol
let endName :: Text = "__juvix_end"
endLab = LabelRef endSym (Just endName)
(addr, instrs) <- second (concat . reverse) <$> foldM (goFun blts endLab) (initialOffset + length binstrs, []) (tab ^. Reg.infoFunctions)
eassert (addr == length instrs + length binstrs + initialOffset)
registerLabelName endSym endName
registerLabelAddress endSym addr
let mainSym = fromJust $ tab ^. Reg.infoMainFunction
mainName = fromJust (HashMap.lookup mainSym (tab ^. Reg.infoFunctions)) ^. Reg.functionName
callInstr = Call $ InstrCall $ Lab $ LabelRef mainSym (Just mainName)
jmpInstr = mkJump (Val $ Lab endLab)
return $ callInstr : jmpInstr : binstrs ++ instrs ++ [Label endLab]
where
info :: Reg.ExtraInfo
info = Reg.computeExtraInfo (getLimits TargetCairo False) tab
getTagId :: Tag -> Int
getTagId tag =
1 + fromJust (HashMap.lookup tag (info ^. Reg.extraInfoCIDs))
goFun :: forall r. (Member LabelInfoBuilder r) => StdlibBuiltins -> LabelRef -> (Address, [[Instruction]]) -> Reg.FunctionInfo -> Sem r (Address, [[Instruction]])
goFun blts failLab (addr0, acc) funInfo = do
let sym = funInfo ^. Reg.functionSymbol
funName = funInfo ^. Reg.functionName
registerLabelName sym funName
registerLabelAddress sym addr0
let lab = Label $ LabelRef sym (Just funName)
code = funInfo ^. Reg.functionCode
n = fromJust $ HashMap.lookup (funInfo ^. Reg.functionSymbol) (info ^. Reg.extraInfoLocalVarsNum)
i1 = Alloc $ InstrAlloc $ Val $ Imm $ fromIntegral n
pre = [lab, i1]
addr1 = addr0 + length pre
instrs <- goCode addr1 code
return (addr1 + length instrs, (pre ++ instrs) : acc)
where
unsupported :: Text -> a
unsupported what = error ("Cairo backend: unsupported: " <> what)
goCode :: Address -> Reg.Code -> Sem r [Instruction]
goCode addr code = concat . reverse . snd <$> foldM go' (addr, []) code
where
go' :: (Address, [[Instruction]]) -> Reg.Instruction -> Sem r (Address, [[Instruction]])
go' (addr', acc') i = do
is <- goInstr addr' i
return (addr' + length is, is : acc')
goInstr :: Address -> Reg.Instruction -> Sem r [Instruction]
goInstr addr = \case
Reg.Binop x -> goBinop addr x
Reg.Unop x -> goUnop addr x
Reg.Assign x -> goAssign addr x
Reg.Alloc x -> goAlloc addr x
Reg.AllocClosure x -> goAllocClosure addr x
Reg.ExtendClosure x -> goExtendClosure addr x
Reg.Call x -> goCall addr x
Reg.TailCall x -> goTailCall addr x
Reg.CallClosures {} -> impossible
Reg.TailCallClosures {} -> impossible
Reg.Return x -> goReturn addr x
Reg.Branch x -> goBranch addr x
Reg.Case x -> goCase addr x
Reg.Trace x -> goTrace addr x
Reg.Dump -> unsupported "dump"
Reg.Failure x -> goFail addr x
Reg.Prealloc {} -> return []
Reg.Nop -> return []
Reg.Block x -> goBlock addr x
goConst :: Reg.Constant -> Integer
goConst = \case
Reg.ConstInt x -> x
Reg.ConstBool True -> 0
Reg.ConstBool False -> 1
Reg.ConstField f -> fieldToInteger f
Reg.ConstUnit -> 0
Reg.ConstVoid -> 0
Reg.ConstString {} -> unsupported "strings"
goConstrField :: Reg.ConstrField -> RValue
goConstrField Reg.ConstrField {..} =
Load $ LoadValue (goVarRef _constrFieldRef) (toOffset _constrFieldIndex + 1)
goVarRef :: Reg.VarRef -> MemRef
goVarRef Reg.VarRef {..} = case _varRefGroup of
Reg.VarGroupArgs ->
MemRef Fp (-3 - toOffset _varRefIndex)
Reg.VarGroupLocal ->
MemRef Fp (toOffset _varRefIndex)
goValue :: Reg.Value -> ([Instruction], Value)
goValue = \case
Reg.Const c -> ([], Imm $ goConst c)
Reg.CRef x -> ([mkAssignAp (goConstrField x)], Ref $ MemRef Ap (-1))
Reg.VRef x -> ([], Ref $ goVarRef x)
goRValue :: Reg.Value -> RValue
goRValue = \case
Reg.Const c -> Val $ Imm $ goConst c
Reg.CRef x -> goConstrField x
Reg.VRef x -> Val $ Ref $ goVarRef x
goLoad :: Reg.Value -> Offset -> ([Instruction], RValue)
goLoad v off = case goRValue v of
Val (Ref r) -> ([], Load $ LoadValue r off)
v' -> ([mkAssignAp v'], Load $ LoadValue (MemRef Ap (-1)) off)
goAssignValue :: MemRef -> Reg.Value -> Instruction
goAssignValue res = mkAssign res . goRValue
goAssignApValue :: Reg.Value -> Instruction
goAssignApValue = mkAssignAp . goRValue
mkBinop :: Reg.BinaryOp -> MemRef -> MemRef -> Value -> [Instruction]
mkBinop op res arg1 arg2 = case op of
Reg.OpIntAdd ->
[mkExtraBinop IntAdd res arg1 arg2]
Reg.OpIntSub ->
[mkExtraBinop IntSub res arg1 arg2]
Reg.OpIntMul ->
[mkExtraBinop IntMul res arg1 arg2]
Reg.OpIntDiv ->
[mkExtraBinop IntDiv res arg1 arg2]
Reg.OpIntMod ->
[mkExtraBinop IntMod res arg1 arg2]
Reg.OpIntLt ->
[mkExtraBinop IntLt res arg1 arg2]
Reg.OpIntLe ->
mkIntLe res arg1 arg2
Reg.OpFieldAdd ->
[mkNativeBinop FieldAdd res arg1 arg2]
Reg.OpFieldSub ->
[mkExtraBinop FieldSub res arg1 arg2]
Reg.OpFieldMul ->
[mkNativeBinop FieldMul res arg1 arg2]
Reg.OpFieldDiv ->
[mkExtraBinop FieldDiv res arg1 arg2]
Reg.OpEq ->
[mkEq res arg1 arg2]
Reg.OpStrConcat ->
unsupported "strings"
goBinop :: Address -> Reg.InstrBinop -> Sem r [Instruction]
goBinop addr x@Reg.InstrBinop {..} = case _instrBinopArg1 of
Reg.Const c1 -> case _instrBinopArg2 of
Reg.Const c2 -> case Reg.evalBinop' _instrBinopOpcode c1 c2 of
Left err -> error err
Right c ->
return [mkAssign res (Val $ Imm $ goConst c)]
_ ->
goBinop
addr
x
{ Reg._instrBinopArg1 = _instrBinopArg2,
Reg._instrBinopArg2 = _instrBinopArg1
}
Reg.CRef ctr1 ->
case _instrBinopArg2 of
Reg.CRef {} ->
return $ i : is2 ++ mkBinop _instrBinopOpcode res (MemRef Ap (-2)) (Ref $ MemRef Ap (-1))
_ -> do
eassert (null is2)
return $ i : mkBinop _instrBinopOpcode res (MemRef Ap (-1)) v2
where
i = mkAssignAp (goConstrField ctr1)
Reg.VRef var1 ->
return $ is2 ++ mkBinop _instrBinopOpcode res (goVarRef var1) v2
where
res = goVarRef _instrBinopResult
(is2, v2) = goValue _instrBinopArg2
goUnop :: Address -> Reg.InstrUnop -> Sem r [Instruction]
goUnop _ Reg.InstrUnop {..} = case _instrUnopOpcode of
Reg.OpShow -> unsupported "strings"
Reg.OpStrToInt -> unsupported "strings"
Reg.OpFieldToInt -> return [goAssignValue res _instrUnopArg]
Reg.OpIntToField -> return [goAssignValue res _instrUnopArg]
Reg.OpArgsNum -> case v of
Ref mr ->
return $ is ++ mkOpArgsNum res mr
Imm {} -> impossible
Lab {} -> impossible
where
res = goVarRef _instrUnopResult
(is, v) = goValue _instrUnopArg
goAssign :: Address -> Reg.InstrAssign -> Sem r [Instruction]
goAssign _ Reg.InstrAssign {..} =
return [goAssignValue res _instrAssignValue]
where
res = goVarRef _instrAssignResult
mkAllocCall :: MemRef -> [Instruction]
mkAllocCall res =
[ mkCall $ Lab $ LabelRef (blts ^. stdlibGetRegs) (Just (blts ^. stdlibGetRegsName)),
mkNativeBinop FieldAdd res (MemRef Ap (-2)) (Imm 2)
]
goAlloc :: Address -> Reg.InstrAlloc -> Sem r [Instruction]
goAlloc _ Reg.InstrAlloc {..} =
return $
mkAllocCall res
++ [ mkAssignAp (Val $ Imm $ fromIntegral tagId)
]
++ map goAssignApValue _instrAllocArgs
where
res = goVarRef _instrAllocResult
tagId = getTagId _instrAllocTag
goAllocClosure :: Address -> Reg.InstrAllocClosure -> Sem r [Instruction]
goAllocClosure _ Reg.InstrAllocClosure {..} =
return $
mkAllocCall res
++ [ mkAssignAp (Val $ Lab $ LabelRef _instrAllocClosureSymbol (Just funName)),
mkAssignAp (Val $ Imm $ fromIntegral $ casmMaxFunctionArgs + 1 - storedArgsNum),
mkAssignAp (Val $ Imm $ fromIntegral $ casmMaxFunctionArgs + 1 - leftArgsNum)
]
++ map goAssignApValue _instrAllocClosureArgs
where
res = goVarRef _instrAllocClosureResult
funName = Reg.lookupFunInfo tab _instrAllocClosureSymbol ^. Reg.functionName
storedArgsNum = length _instrAllocClosureArgs
leftArgsNum = _instrAllocClosureExpectedArgsNum - storedArgsNum
goExtendClosure :: Address -> Reg.InstrExtendClosure -> Sem r [Instruction]
goExtendClosure _ Reg.InstrExtendClosure {..} =
return $
map goAssignApValue _instrExtendClosureArgs
++ [ mkAssignAp (Val $ Imm $ fromIntegral $ length _instrExtendClosureArgs),
mkAssignAp (Val $ Ref val),
mkCall $ Lab $ LabelRef (blts ^. stdlibExtendClosure) (Just (blts ^. stdlibExtendClosureName)),
mkAssign res (Val $ Ref $ MemRef Ap (-1))
]
where
res = goVarRef _instrExtendClosureResult
val = goVarRef _instrExtendClosureValue
goCall' :: Instruction -> Reg.CallType -> [Reg.Value] -> [Instruction]
goCall' saveOrRet ct args = case ct of
Reg.CallFun sym ->
args'
++ [ mkCall $ Lab $ LabelRef sym (Just funName),
saveOrRet
]
where
funName = Reg.lookupFunInfo tab sym ^. Reg.functionName
Reg.CallClosure cl ->
args'
++ [ mkAssignAp (Val $ Ref $ goVarRef cl),
mkCall $ Lab $ LabelRef (blts ^. stdlibCallClosure) (Just (blts ^. stdlibCallClosureName)),
saveOrRet
]
where
args' = map goAssignApValue (reverse args)
goCall :: Address -> Reg.InstrCall -> Sem r [Instruction]
goCall _ Reg.InstrCall {..} =
return $
goCall' (mkAssign res (Val $ Ref $ MemRef Ap (-1))) _instrCallType _instrCallArgs
where
res = goVarRef _instrCallResult
-- There is no way to make "proper" tail calls in Cairo, because
-- the only way to set the `fp` register is via the `call` instruction.
-- So we just translate tail calls into `call` followed by `ret`.
goTailCall :: Address -> Reg.InstrTailCall -> Sem r [Instruction]
goTailCall _ Reg.InstrTailCall {..} =
return $
goCall' Return _instrTailCallType _instrTailCallArgs
goReturn :: Address -> Reg.InstrReturn -> Sem r [Instruction]
goReturn _ Reg.InstrReturn {..} =
return $
[ goAssignApValue _instrReturnValue,
Return
]
goBranch :: Address -> Reg.InstrBranch -> Sem r [Instruction]
goBranch addr Reg.InstrBranch {..} = case v of
Imm c
| c == 0 -> goCode addr _instrBranchTrue
| otherwise -> goCode addr _instrBranchFalse
Ref r -> do
symFalse <- freshSymbol
symEnd <- freshSymbol
let labFalse = LabelRef symFalse Nothing
labEnd = LabelRef symEnd Nothing
addr1 = addr + length is + 1
codeTrue <- goCode addr1 _instrBranchTrue
let addr2 = addr1 + length codeTrue + 1
registerLabelAddress symFalse addr2
codeFalse <- goCode (addr2 + 1) _instrBranchFalse
registerLabelAddress symEnd (addr2 + 1 + length codeFalse)
return $
is
++ [mkJumpIf (Lab labFalse) r]
++ codeTrue
++ [ mkJump (Val $ Lab labEnd),
Label labFalse
]
++ codeFalse
++ [Label labEnd]
Lab {} -> impossible
where
(is, v) = goValue _instrBranchValue
goCase :: Address -> Reg.InstrCase -> Sem r [Instruction]
goCase addr Reg.InstrCase {..} = do
syms <- replicateM (length tags) freshSymbol
symEnd <- freshSymbol
let symMap = HashMap.fromList $ zip tags syms
labs = map (flip LabelRef Nothing) syms
labEnd = LabelRef symEnd Nothing
jmps = map (mkJump . Val . Lab) labs
addr1 = addr + length is + 1 + length jmps
(addr2, instrs) <- second (concat . reverse) <$> foldM (goCaseBranch symMap labEnd) (addr1, []) _instrCaseBranches
(addr3, instrs') <- second reverse <$> foldM (goDefaultLabel symMap) (addr2, []) defaultTags
instrs'' <- maybe (return []) (goCode addr3) _instrCaseDefault
let addr4 = addr3 + length instrs''
registerLabelAddress symEnd addr4
return $ is ++ mkJumpRel v : jmps ++ instrs ++ instrs' ++ instrs'' ++ [Label labEnd]
where
(is, v) = goLoad _instrCaseValue 0
tags = Reg.lookupInductiveInfo tab _instrCaseInductive ^. Reg.inductiveConstructors
ctrTags = HashSet.fromList $ map (^. Reg.caseBranchTag) _instrCaseBranches
defaultTags = filter (not . flip HashSet.member ctrTags) tags
goCaseBranch :: HashMap Tag Symbol -> LabelRef -> (Address, [[Instruction]]) -> Reg.CaseBranch -> Sem r (Address, [[Instruction]])
goCaseBranch symMap labEnd (addr', acc') Reg.CaseBranch {..} = do
let sym = fromJust $ HashMap.lookup _caseBranchTag symMap
lab = LabelRef sym Nothing
registerLabelAddress sym addr'
instrs <- goCode (addr' + 1) _caseBranchCode
let instrs' = Label lab : instrs ++ [mkJump (Val $ Lab labEnd)]
return (addr' + length instrs', instrs' : acc')
goDefaultLabel :: HashMap Tag Symbol -> (Address, [Instruction]) -> Reg.Tag -> Sem r (Address, [Instruction])
goDefaultLabel symMap (addr', acc') tag = do
let sym = fromJust $ HashMap.lookup tag symMap
lab = LabelRef sym Nothing
registerLabelAddress sym addr'
return (addr' + 1, Label lab : acc')
goTrace :: Address -> Reg.InstrTrace -> Sem r [Instruction]
goTrace _ Reg.InstrTrace {..} =
return [Trace (InstrTrace (goRValue _instrTraceValue))]
goFail :: Address -> Reg.InstrFailure -> Sem r [Instruction]
goFail _ Reg.InstrFailure {..} =
return
[ Trace (InstrTrace (goRValue _instrFailureValue)),
mkJump (Val $ Lab failLab)
]
goBlock :: Address -> Reg.InstrBlock -> Sem r [Instruction]
goBlock addr Reg.InstrBlock {..} =
goCode addr _instrBlockCode

View File

@ -14,13 +14,20 @@ parseText = runParser noFile
runParser :: Path Abs File -> Text -> Either MegaparsecError (LabelInfo, [Instruction]) runParser :: Path Abs File -> Text -> Either MegaparsecError (LabelInfo, [Instruction])
runParser fileName input_ = runParser fileName input_ =
case run . runLabelInfoBuilder $ P.runParserT parseToplevel (toFilePath fileName) input_ of case run . runLabelInfoBuilder $ runParser' 0 (toFilePath fileName) input_ of
(_, Left err) -> Left (MegaparsecError err) (_, Left err) -> Left err
(li, Right instrs) -> Right (li, instrs) (li, Right instrs) -> Right (li, instrs)
parseToplevel :: (Member LabelInfoBuilder r) => ParsecS r [Instruction] runParser' :: (Member LabelInfoBuilder r) => Address -> FilePath -> Text -> Sem r (Either MegaparsecError [Instruction])
parseToplevel = do runParser' addr fileName input_ = do
instrs <- statements 0 e <- P.runParserT (parseToplevel addr) fileName input_
return $ case e of
Left err -> Left (MegaparsecError err)
Right instrs -> Right instrs
parseToplevel :: (Member LabelInfoBuilder r) => Address -> ParsecS r [Instruction]
parseToplevel addr = do
instrs <- statements addr
P.eof P.eof
return instrs return instrs
@ -160,44 +167,38 @@ parseIncAp = (kw delimSemicolon >> kw kwApPlusPlus >> return True) <|> return Fa
parseJump :: forall r. (Member LabelInfoBuilder r) => ParsecS r Instruction parseJump :: forall r. (Member LabelInfoBuilder r) => ParsecS r Instruction
parseJump = do parseJump = do
kw kwJmp kw kwJmp
relJmp <|> jmp P.try jmpIf <|> jmp
where where
relJmp :: ParsecS r Instruction jmpIf :: ParsecS r Instruction
relJmp = do jmpIf = do
kw kwRel isRel <- isJust <$> optional (kw kwRel)
tgt <- parseRValue tgt <- parseValue
kw kwIf
v <- parseMemRef
kw kwNotEq
symbol "0"
incAp <- parseIncAp incAp <- parseIncAp
return $ return $
JumpRel $ JumpIf $
InstrJumpRel InstrJumpIf
{ _instrJumpRelTarget = tgt, { _instrJumpIfTarget = tgt,
_instrJumpRelIncAp = incAp _instrJumpIfValue = v,
_instrJumpIfRel = isRel,
_instrJumpIfIncAp = incAp
} }
jmp :: ParsecS r Instruction jmp :: ParsecS r Instruction
jmp = do jmp = do
tgt <- parseValue isRel <- isJust <$> optional (kw kwRel)
mv <- optional if_ tgt <- parseRValue
incAp <- parseIncAp incAp <- parseIncAp
case mv of return $
Nothing -> Jump $
return $ Jump $ InstrJump {_instrJumpTarget = tgt, _instrJumpIncAp = incAp} InstrJump
Just v -> { _instrJumpTarget = tgt,
return $ _instrJumpRel = isRel,
JumpIf $ _instrJumpIncAp = incAp
InstrJumpIf }
{ _instrJumpIfTarget = tgt,
_instrJumpIfValue = v,
_instrJumpIfIncAp = incAp
}
where
if_ :: ParsecS r MemRef
if_ = do
kw kwIf
v <- parseMemRef
kw kwNotEq
symbol "0"
return v
parseCall :: (Member LabelInfoBuilder r) => ParsecS r Instruction parseCall :: (Member LabelInfoBuilder r) => ParsecS r Instruction
parseCall = do parseCall = do

View File

@ -15,7 +15,6 @@ validate labi instrs = mapM_ go instrs
ExtraBinop x -> goExtraBinop x ExtraBinop x -> goExtraBinop x
Jump x -> goJump x Jump x -> goJump x
JumpIf x -> goJumpIf x JumpIf x -> goJumpIf x
JumpRel x -> goJumpRel x
Call x -> goCall x Call x -> goCall x
Return -> return () Return -> return ()
Alloc x -> goAlloc x Alloc x -> goAlloc x
@ -52,14 +51,11 @@ validate labi instrs = mapM_ go instrs
goExtraBinop InstrExtraBinop {..} = goValue _instrExtraBinopArg2 goExtraBinop InstrExtraBinop {..} = goValue _instrExtraBinopArg2
goJump :: InstrJump -> Either CasmError () goJump :: InstrJump -> Either CasmError ()
goJump InstrJump {..} = goValue _instrJumpTarget goJump InstrJump {..} = goRValue _instrJumpTarget
goJumpIf :: InstrJumpIf -> Either CasmError () goJumpIf :: InstrJumpIf -> Either CasmError ()
goJumpIf InstrJumpIf {..} = goValue _instrJumpIfTarget goJumpIf InstrJumpIf {..} = goValue _instrJumpIfTarget
goJumpRel :: InstrJumpRel -> Either CasmError ()
goJumpRel InstrJumpRel {..} = goRValue _instrJumpRelTarget
goCall :: InstrCall -> Either CasmError () goCall :: InstrCall -> Either CasmError ()
goCall InstrCall {..} = goValue _instrCallTarget goCall InstrCall {..} = goValue _instrCallTarget

View File

@ -25,6 +25,7 @@ data TransformationId
| CheckExec | CheckExec
| CheckVampIR | CheckVampIR
| CheckAnoma | CheckAnoma
| CheckCairo
| Normalize | Normalize
| LetFolding | LetFolding
| LambdaFolding | LambdaFolding
@ -99,6 +100,7 @@ instance TransformationId' TransformationId where
CheckExec -> strCheckExec CheckExec -> strCheckExec
CheckVampIR -> strCheckVampIR CheckVampIR -> strCheckVampIR
CheckAnoma -> strCheckAnoma CheckAnoma -> strCheckAnoma
CheckCairo -> strCheckCairo
Normalize -> strNormalize Normalize -> strNormalize
LetFolding -> strLetFolding LetFolding -> strLetFolding
LambdaFolding -> strLambdaFolding LambdaFolding -> strLambdaFolding

View File

@ -83,6 +83,9 @@ strCheckVampIR = "check-vampir"
strCheckAnoma :: Text strCheckAnoma :: Text
strCheckAnoma = "check-anoma" strCheckAnoma = "check-anoma"
strCheckCairo :: Text
strCheckCairo = "check-cairo"
strNormalize :: Text strNormalize :: Text
strNormalize = "normalize" strNormalize = "normalize"

View File

@ -14,6 +14,7 @@ import Juvix.Compiler.Core.Error
import Juvix.Compiler.Core.Options import Juvix.Compiler.Core.Options
import Juvix.Compiler.Core.Transformation.Base import Juvix.Compiler.Core.Transformation.Base
import Juvix.Compiler.Core.Transformation.Check.Anoma import Juvix.Compiler.Core.Transformation.Check.Anoma
import Juvix.Compiler.Core.Transformation.Check.Cairo
import Juvix.Compiler.Core.Transformation.Check.Exec import Juvix.Compiler.Core.Transformation.Check.Exec
import Juvix.Compiler.Core.Transformation.Check.Geb import Juvix.Compiler.Core.Transformation.Check.Geb
import Juvix.Compiler.Core.Transformation.Check.VampIR import Juvix.Compiler.Core.Transformation.Check.VampIR
@ -77,6 +78,7 @@ applyTransformations ts tbl = foldM (flip appTrans) tbl ts
CheckExec -> mapError (JuvixError @CoreError) . checkExec CheckExec -> mapError (JuvixError @CoreError) . checkExec
CheckVampIR -> mapError (JuvixError @CoreError) . checkVampIR CheckVampIR -> mapError (JuvixError @CoreError) . checkVampIR
CheckAnoma -> mapError (JuvixError @CoreError) . checkAnoma CheckAnoma -> mapError (JuvixError @CoreError) . checkAnoma
CheckCairo -> mapError (JuvixError @CoreError) . checkCairo
Normalize -> normalize Normalize -> normalize
LetFolding -> return . letFolding LetFolding -> return . letFolding
LambdaFolding -> return . lambdaFolding LambdaFolding -> return . lambdaFolding

View File

@ -0,0 +1,13 @@
module Juvix.Compiler.Core.Transformation.Check.Cairo where
import Juvix.Compiler.Core.Error
import Juvix.Compiler.Core.Extra
import Juvix.Compiler.Core.Transformation.Base
import Juvix.Compiler.Core.Transformation.Check.Base
checkCairo :: forall r. (Member (Error CoreError) r) => Module -> Sem r Module
checkCairo md = do
checkMainExists md
checkNoAxioms md
mapAllNodesM checkNoIO md
mapAllNodesM (checkBuiltins' [OpStrConcat, OpStrToInt, OpShow] [PrimString]) md

View File

@ -16,6 +16,8 @@ import Juvix.Compiler.Backend qualified as Backend
import Juvix.Compiler.Backend.C qualified as C import Juvix.Compiler.Backend.C qualified as C
import Juvix.Compiler.Backend.Geb qualified as Geb import Juvix.Compiler.Backend.Geb qualified as Geb
import Juvix.Compiler.Backend.VampIR.Translation qualified as VampIR import Juvix.Compiler.Backend.VampIR.Translation qualified as VampIR
import Juvix.Compiler.Casm.Data.Result 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
import Juvix.Compiler.Concrete.Translation.FromParsed qualified as Scoper import Juvix.Compiler.Concrete.Translation.FromParsed qualified as Scoper
@ -42,6 +44,7 @@ import Juvix.Compiler.Tree qualified as Tree
import Juvix.Data.Effect.Git import Juvix.Data.Effect.Git
import Juvix.Data.Effect.Process import Juvix.Data.Effect.Process
import Juvix.Data.Effect.TaggedLock import Juvix.Data.Effect.TaggedLock
import Juvix.Data.Field
type PipelineAppEffects = '[TaggedLock, EmbedIO, Resource, Final IO] type PipelineAppEffects = '[TaggedLock, EmbedIO, Resource, Final IO]
@ -101,6 +104,12 @@ upToAsm ::
upToAsm = upToAsm =
upToStoredCore >>= \Core.CoreResult {..} -> storedCoreToAsm _coreResultModule upToStoredCore >>= \Core.CoreResult {..} -> storedCoreToAsm _coreResultModule
upToCasm ::
(Members '[HighlightBuilder, Reader Parser.ParserResult, Reader EntryPoint, Reader Store.ModuleTable, Files, NameIdGen, Error JuvixError, GitClone, PathResolver] r) =>
Sem r Casm.Result
upToCasm =
upToStoredCore >>= \Core.CoreResult {..} -> storedCoreToCasm _coreResultModule
upToMiniC :: upToMiniC ::
(Members '[HighlightBuilder, Reader Parser.ParserResult, Reader EntryPoint, Reader Store.ModuleTable, Files, NameIdGen, Error JuvixError, GitClone, PathResolver] r) => (Members '[HighlightBuilder, Reader Parser.ParserResult, Reader EntryPoint, Reader Store.ModuleTable, Files, NameIdGen, Error JuvixError, GitClone, PathResolver] r) =>
Sem r C.MiniCResult Sem r C.MiniCResult
@ -153,6 +162,9 @@ storedCoreToReg = storedCoreToAsm >=> asmToReg
storedCoreToMiniC :: (Members '[Error JuvixError, Reader EntryPoint] r) => Core.Module -> Sem r C.MiniCResult storedCoreToMiniC :: (Members '[Error JuvixError, Reader EntryPoint] r) => Core.Module -> Sem r C.MiniCResult
storedCoreToMiniC = storedCoreToAsm >=> asmToMiniC storedCoreToMiniC = storedCoreToAsm >=> asmToMiniC
storedCoreToCasm :: (Members '[Error JuvixError, Reader EntryPoint] r) => Core.Module -> Sem r Casm.Result
storedCoreToCasm = local (set entryPointFieldSize cairoFieldSize) . storedCoreToTree Core.CheckCairo >=> treeToCasm
storedCoreToGeb :: (Members '[Error JuvixError, Reader EntryPoint] r) => Geb.ResultSpec -> Core.Module -> Sem r Geb.Result storedCoreToGeb :: (Members '[Error JuvixError, Reader EntryPoint] r) => Geb.ResultSpec -> Core.Module -> Sem r Geb.Result
storedCoreToGeb spec = Core.toGeb >=> return . uncurry (Geb.toResult spec) . Geb.fromCore . Core.computeCombinedInfoTable storedCoreToGeb spec = Core.toGeb >=> return . uncurry (Geb.toResult spec) . Geb.fromCore . Core.computeCombinedInfoTable
@ -175,6 +187,9 @@ coreToAsm = Core.toStored >=> storedCoreToAsm
coreToReg :: (Members '[Error JuvixError, Reader EntryPoint] r) => Core.Module -> Sem r Reg.InfoTable coreToReg :: (Members '[Error JuvixError, Reader EntryPoint] r) => Core.Module -> Sem r Reg.InfoTable
coreToReg = Core.toStored >=> storedCoreToReg coreToReg = Core.toStored >=> storedCoreToReg
coreToCasm :: (Members '[Error JuvixError, Reader EntryPoint] r) => Core.Module -> Sem r Casm.Result
coreToCasm = Core.toStored >=> storedCoreToCasm
coreToNockma :: (Members '[Error JuvixError, Reader EntryPoint] r) => Core.Module -> Sem r (Nockma.Cell Natural) coreToNockma :: (Members '[Error JuvixError, Reader EntryPoint] r) => Core.Module -> Sem r (Nockma.Cell Natural)
coreToNockma = coreToTree Core.CheckAnoma >=> treeToNockma coreToNockma = coreToTree Core.CheckAnoma >=> treeToNockma
@ -200,6 +215,9 @@ coreToVampIR' = Core.toStored' >=> storedCoreToVampIR'
treeToAsm :: (Member (Error JuvixError) r) => Tree.InfoTable -> Sem r Asm.InfoTable treeToAsm :: (Member (Error JuvixError) r) => Tree.InfoTable -> Sem r Asm.InfoTable
treeToAsm = Tree.toAsm >=> return . Asm.fromTree treeToAsm = Tree.toAsm >=> return . Asm.fromTree
treeToCairoAsm :: (Member (Error JuvixError) r) => Tree.InfoTable -> Sem r Asm.InfoTable
treeToCairoAsm = Tree.toCairoAsm >=> return . Asm.fromTree
treeToReg :: (Members '[Error JuvixError, Reader EntryPoint] r) => Tree.InfoTable -> Sem r Reg.InfoTable treeToReg :: (Members '[Error JuvixError, Reader EntryPoint] r) => Tree.InfoTable -> Sem r Reg.InfoTable
treeToReg = treeToAsm >=> asmToReg treeToReg = treeToAsm >=> asmToReg
@ -212,9 +230,15 @@ treeToAnoma = Tree.toNockma >=> mapReader NockmaTree.fromEntryPoint . NockmaTree
treeToMiniC :: (Members '[Error JuvixError, Reader EntryPoint] r) => Tree.InfoTable -> Sem r C.MiniCResult treeToMiniC :: (Members '[Error JuvixError, Reader EntryPoint] r) => Tree.InfoTable -> Sem r C.MiniCResult
treeToMiniC = treeToAsm >=> asmToMiniC treeToMiniC = treeToAsm >=> asmToMiniC
treeToCasm :: (Members '[Error JuvixError, Reader EntryPoint] r) => Tree.InfoTable -> Sem r Casm.Result
treeToCasm = treeToCairoAsm >=> asmToCasm
asmToReg :: (Members '[Error JuvixError, Reader EntryPoint] r) => Asm.InfoTable -> Sem r Reg.InfoTable asmToReg :: (Members '[Error JuvixError, Reader EntryPoint] r) => Asm.InfoTable -> Sem r Reg.InfoTable
asmToReg = Asm.toReg >=> return . Reg.fromAsm asmToReg = Asm.toReg >=> return . Reg.fromAsm
asmToCasm :: (Members '[Error JuvixError, Reader EntryPoint] r) => Asm.InfoTable -> Sem r Casm.Result
asmToCasm = asmToReg >=> regToCasm
asmToMiniC :: (Members '[Error JuvixError, Reader EntryPoint] r) => Asm.InfoTable -> Sem r C.MiniCResult asmToMiniC :: (Members '[Error JuvixError, Reader EntryPoint] r) => Asm.InfoTable -> Sem r C.MiniCResult
asmToMiniC = asmToReg >=> regToMiniC asmToMiniC = asmToReg >=> regToMiniC
@ -224,6 +248,9 @@ regToMiniC tab = do
e <- ask e <- ask
return $ C.fromReg (Backend.getLimits (e ^. entryPointTarget) (e ^. entryPointDebug)) tab' return $ C.fromReg (Backend.getLimits (e ^. entryPointTarget) (e ^. entryPointDebug)) tab'
regToCasm :: Reg.InfoTable -> Sem r Casm.Result
regToCasm = Reg.toCasm >=> return . Casm.fromReg
treeToNockma' :: (Members '[Error JuvixError, Reader NockmaTree.CompilerOptions] r) => Tree.InfoTable -> Sem r (Nockma.Cell Natural) treeToNockma' :: (Members '[Error JuvixError, Reader NockmaTree.CompilerOptions] r) => Tree.InfoTable -> Sem r (Nockma.Cell Natural)
treeToNockma' = Tree.toNockma >=> NockmaTree.fromTreeTable NockmaTree.ProgramCallingConventionJuvix treeToNockma' = Tree.toNockma >=> NockmaTree.fromTreeTable NockmaTree.ProgramCallingConventionJuvix

View File

@ -12,7 +12,7 @@ data TransformationId
deriving stock (Data, Bounded, Enum, Show) deriving stock (Data, Bounded, Enum, Show)
data PipelineId data PipelineId
= PipelineCairo = PipelineCasm
| PipelineC | PipelineC
deriving stock (Data, Bounded, Enum) deriving stock (Data, Bounded, Enum)
@ -21,8 +21,8 @@ type TransformationLikeId = TransformationLikeId' TransformationId PipelineId
toCTransformations :: [TransformationId] toCTransformations :: [TransformationId]
toCTransformations = [Cleanup] toCTransformations = [Cleanup]
toCairoTransformations :: [TransformationId] toCasmTransformations :: [TransformationId]
toCairoTransformations = [Cleanup, SSA, InitBranchVars] toCasmTransformations = [Cleanup, SSA, InitBranchVars]
instance TransformationId' TransformationId where instance TransformationId' TransformationId where
transformationText :: TransformationId -> Text transformationText :: TransformationId -> Text
@ -36,9 +36,9 @@ instance PipelineId' TransformationId PipelineId where
pipelineText :: PipelineId -> Text pipelineText :: PipelineId -> Text
pipelineText = \case pipelineText = \case
PipelineC -> strCPipeline PipelineC -> strCPipeline
PipelineCairo -> strCairoPipeline PipelineCasm -> strCasmPipeline
pipeline :: PipelineId -> [TransformationId] pipeline :: PipelineId -> [TransformationId]
pipeline = \case pipeline = \case
PipelineC -> toCTransformations PipelineC -> toCTransformations
PipelineCairo -> toCairoTransformations PipelineCasm -> toCasmTransformations

View File

@ -5,8 +5,8 @@ import Juvix.Prelude
strCPipeline :: Text strCPipeline :: Text
strCPipeline = "pipeline-c" strCPipeline = "pipeline-c"
strCairoPipeline :: Text strCasmPipeline :: Text
strCairoPipeline = "pipeline-cairo" strCasmPipeline = "pipeline-casm"
strIdentity :: Text strIdentity :: Text
strIdentity = "identity" strIdentity = "identity"

View File

@ -196,8 +196,13 @@ computeLocalVarsNum = maximum . map go
data ExtraInfo = ExtraInfo data ExtraInfo = ExtraInfo
{ _extraInfoTable :: InfoTable, { _extraInfoTable :: InfoTable,
-- | Globally unique IDs for constructor tags
_extraInfoUIDs :: HashMap Tag Int, _extraInfoUIDs :: HashMap Tag Int,
-- | Globally unique IDs for function symbols
_extraInfoFUIDs :: HashMap Symbol Int, _extraInfoFUIDs :: HashMap Symbol Int,
-- | IDs for constructor tags, consecutive starting from 0 for each
-- inductive type separately
_extraInfoCIDs :: HashMap Tag Int,
_extraInfoStringMap :: HashMap Text Int, _extraInfoStringMap :: HashMap Text Int,
_extraInfoMaxStackHeight :: HashMap Symbol Int, _extraInfoMaxStackHeight :: HashMap Symbol Int,
_extraInfoLocalVarsNum :: HashMap Symbol Int, _extraInfoLocalVarsNum :: HashMap Symbol Int,
@ -226,12 +231,19 @@ computeFUIDs tab =
(HashMap.elems (tab ^. infoFunctions)) (HashMap.elems (tab ^. infoFunctions))
[0 ..] [0 ..]
computeCIDs :: InfoTable -> HashMap Tag Int
computeCIDs tab = HashMap.fromList $ concatMap go (tab ^. infoInductives)
where
go :: InductiveInfo -> [(Tag, Int)]
go InductiveInfo {..} = zip _inductiveConstructors [0 ..]
computeExtraInfo :: Limits -> InfoTable -> ExtraInfo computeExtraInfo :: Limits -> InfoTable -> ExtraInfo
computeExtraInfo lims tab = computeExtraInfo lims tab =
ExtraInfo ExtraInfo
{ _extraInfoTable = tab, { _extraInfoTable = tab,
_extraInfoUIDs = computeUIDs lims tab, _extraInfoUIDs = computeUIDs lims tab,
_extraInfoFUIDs = computeFUIDs tab, _extraInfoFUIDs = computeFUIDs tab,
_extraInfoCIDs = computeCIDs tab,
_extraInfoStringMap = _extraInfoStringMap =
foldr foldr
(\fi mp -> computeStringMap mp (fi ^. functionCode)) (\fi mp -> computeStringMap mp (fi ^. functionCode))

View File

@ -65,15 +65,6 @@ runFunction hout infoTable args0 info0 = do
[] -> [] ->
return $ ValVoid return $ ValVoid
readConst :: Constant -> Val
readConst = \case
ConstInt i -> ValInteger i
ConstField f -> ValField f
ConstBool b -> ValBool b
ConstString s -> ValString s
ConstUnit -> ValUnit
ConstVoid -> ValVoid
readVarRef :: Args -> Vars s -> VarRef -> ST s Val readVarRef :: Args -> Vars s -> VarRef -> ST s Val
readVarRef args tmps VarRef {..} = case _varRefGroup of readVarRef args tmps VarRef {..} = case _varRefGroup of
VarGroupArgs -> return $ args Vec.! _varRefIndex VarGroupArgs -> return $ args Vec.! _varRefIndex
@ -97,7 +88,7 @@ runFunction hout infoTable args0 info0 = do
readValue :: Args -> Vars s -> Value -> ST s Val readValue :: Args -> Vars s -> Value -> ST s Val
readValue args tmps = \case readValue args tmps = \case
Const c -> return $ readConst c Const c -> return $ constantToValue c
CRef r -> readConstrRef args tmps r CRef r -> readConstrRef args tmps r
VRef r -> readVarRef args tmps r VRef r -> readVarRef args tmps r

View File

@ -19,6 +19,7 @@ data ConstrField = ConstrField
_constrFieldMemRep :: MemRep, _constrFieldMemRep :: MemRep,
-- | Location where the data is stored. -- | Location where the data is stored.
_constrFieldRef :: VarRef, _constrFieldRef :: VarRef,
-- | Index of the constructor argument being referenced.
_constrFieldIndex :: Index _constrFieldIndex :: Index
} }

View File

@ -11,6 +11,7 @@ import Juvix.Compiler.Reg.Transformation
toC :: InfoTable -> Sem r InfoTable toC :: InfoTable -> Sem r InfoTable
toC = applyTransformations toCTransformations toC = applyTransformations toCTransformations
-- | Perform transformations on JuvixReg necessary before the translation to Cairo -- | Perform transformations on JuvixReg necessary before the translation to
toCairo :: InfoTable -> Sem r InfoTable -- Cairo assembly
toCairo = applyTransformations toCairoTransformations toCasm :: InfoTable -> Sem r InfoTable
toCasm = applyTransformations toCasmTransformations

View File

@ -17,6 +17,7 @@ data TransformationId
data PipelineId data PipelineId
= PipelineNockma = PipelineNockma
| PipelineAsm | PipelineAsm
| PipelineCairoAsm
deriving stock (Data, Bounded, Enum) deriving stock (Data, Bounded, Enum)
type TransformationLikeId = TransformationLikeId' TransformationId PipelineId type TransformationLikeId = TransformationLikeId' TransformationId PipelineId
@ -27,6 +28,9 @@ toNockmaTransformations = [Validate, Apply, FilterUnreachable, TempHeight]
toAsmTransformations :: [TransformationId] toAsmTransformations :: [TransformationId]
toAsmTransformations = [Validate] toAsmTransformations = [Validate]
toCairoAsmTransformations :: [TransformationId]
toCairoAsmTransformations = [Validate, Apply, FilterUnreachable]
instance TransformationId' TransformationId where instance TransformationId' TransformationId where
transformationText :: TransformationId -> Text transformationText :: TransformationId -> Text
transformationText = \case transformationText = \case
@ -43,8 +47,10 @@ instance PipelineId' TransformationId PipelineId where
pipelineText = \case pipelineText = \case
PipelineNockma -> strNockmaPipeline PipelineNockma -> strNockmaPipeline
PipelineAsm -> strAsmPipeline PipelineAsm -> strAsmPipeline
PipelineCairoAsm -> strCairoAsmPipeline
pipeline :: PipelineId -> [TransformationId] pipeline :: PipelineId -> [TransformationId]
pipeline = \case pipeline = \case
PipelineNockma -> toNockmaTransformations PipelineNockma -> toNockmaTransformations
PipelineAsm -> toAsmTransformations PipelineAsm -> toAsmTransformations
PipelineCairoAsm -> toCairoAsmTransformations

View File

@ -8,6 +8,9 @@ strNockmaPipeline = "pipeline-nockma"
strAsmPipeline :: Text strAsmPipeline :: Text
strAsmPipeline = "pipeline-asm" strAsmPipeline = "pipeline-asm"
strCairoAsmPipeline :: Text
strCairoAsmPipeline = "pipeline-cairo-asm"
strIdentity :: Text strIdentity :: Text
strIdentity = "identity" strIdentity = "identity"

View File

@ -81,13 +81,7 @@ hEval hout tab = eval' [] mempty
goTrace v = unsafePerformIO (hPutStrLn hout (printValue tab v) >> return v) goTrace v = unsafePerformIO (hPutStrLn hout (printValue tab v) >> return v)
goConstant :: NodeConstant -> Value goConstant :: NodeConstant -> Value
goConstant NodeConstant {..} = case _nodeConstant of goConstant NodeConstant {..} = constantToValue _nodeConstant
ConstInt i -> ValInteger i
ConstField f -> ValField f
ConstBool b -> ValBool b
ConstString s -> ValString s
ConstUnit -> ValUnit
ConstVoid -> ValVoid
goMemRef :: NodeMemRef -> Value goMemRef :: NodeMemRef -> Value
goMemRef NodeMemRef {..} = case _nodeMemRef of goMemRef NodeMemRef {..} = case _nodeMemRef of

View File

@ -1,12 +1,12 @@
module Juvix.Compiler.Tree.Evaluator.Builtins where module Juvix.Compiler.Tree.Evaluator.Builtins where
import Juvix.Compiler.Tree.Data.InfoTable.Base import Juvix.Compiler.Tree.Data.InfoTable.Base
import Juvix.Compiler.Tree.Language.Base
import Juvix.Compiler.Tree.Language.Builtins import Juvix.Compiler.Tree.Language.Builtins
import Juvix.Compiler.Tree.Language.Value import Juvix.Compiler.Tree.Language.Value
import Juvix.Compiler.Tree.Pretty.Base import Juvix.Compiler.Tree.Pretty.Base
import Juvix.Data.Field import Juvix.Data.Field
import Juvix.Data.PPOutput import Juvix.Data.PPOutput
import Juvix.Prelude
import Text.Read qualified as T import Text.Read qualified as T
type ErrorMsg = Text type ErrorMsg = Text
@ -104,3 +104,32 @@ printValue :: InfoTable' t e -> Value -> Text
printValue tab = \case printValue tab = \case
ValString s -> s ValString s -> s
v -> toPlainText . mkAnsiText . PPOutput . doc (defaultOptions tab) $ v v -> toPlainText . mkAnsiText . PPOutput . doc (defaultOptions tab) $ v
constantToValue :: Constant -> Value
constantToValue = \case
ConstInt i -> ValInteger i
ConstField f -> ValField f
ConstBool b -> ValBool b
ConstString s -> ValString s
ConstUnit -> ValUnit
ConstVoid -> ValVoid
valueToConstant :: Value -> Constant
valueToConstant = \case
ValInteger i -> ConstInt i
ValField f -> ConstField f
ValBool b -> ConstBool b
ValString s -> ConstString s
ValUnit -> ConstUnit
ValVoid -> ConstVoid
_ -> impossible
evalBinop' :: BinaryOp -> Constant -> Constant -> Either ErrorMsg Constant
evalBinop' op arg1 arg2 =
mapRight valueToConstant $
evalBinop op (constantToValue arg1) (constantToValue arg2)
evalUnop' :: InfoTable' t e -> UnaryOp -> Constant -> Either ErrorMsg Constant
evalUnop' tab op v =
mapRight valueToConstant $
evalUnop tab op (constantToValue v)

View File

@ -77,13 +77,7 @@ eval tab = E.runReader emptyEvalCtx . eval'
goTrace v = E.output v $> v goTrace v = E.output v $> v
goConstant :: NodeConstant -> Value goConstant :: NodeConstant -> Value
goConstant NodeConstant {..} = case _nodeConstant of goConstant NodeConstant {..} = constantToValue _nodeConstant
ConstInt i -> ValInteger i
ConstField f -> ValField f
ConstBool b -> ValBool b
ConstString s -> ValString s
ConstUnit -> ValUnit
ConstVoid -> ValVoid
askTemp :: Eff r' (BL.BinderList Value) askTemp :: Eff r' (BL.BinderList Value)
askTemp = E.asks (^. evalCtxTemp) askTemp = E.asks (^. evalCtxTemp)

View File

@ -75,13 +75,7 @@ eval tab = runReader emptyEvalCtx . eval'
goTrace v = output v $> v goTrace v = output v $> v
goConstant :: NodeConstant -> Value goConstant :: NodeConstant -> Value
goConstant NodeConstant {..} = case _nodeConstant of goConstant NodeConstant {..} = constantToValue _nodeConstant
ConstInt i -> ValInteger i
ConstField f -> ValField f
ConstBool b -> ValBool b
ConstString s -> ValString s
ConstUnit -> ValUnit
ConstVoid -> ValVoid
askTemp :: Sem r' (BL.BinderList Value) askTemp :: Sem r' (BL.BinderList Value)
askTemp = asks (^. evalCtxTemp) askTemp = asks (^. evalCtxTemp)

View File

@ -12,3 +12,6 @@ toNockma = applyTransformations toNockmaTransformations
toAsm :: (Member (Error JuvixError) r) => InfoTable -> Sem r InfoTable toAsm :: (Member (Error JuvixError) r) => InfoTable -> Sem r InfoTable
toAsm = applyTransformations toAsmTransformations toAsm = applyTransformations toAsmTransformations
toCairoAsm :: (Member (Error JuvixError) r) => InfoTable -> Sem r InfoTable
toCairoAsm = applyTransformations toCairoAsmTransformations

View File

@ -1,7 +1,9 @@
module Casm where module Casm where
import Base import Base
import Casm.Compilation qualified as Compile
import Casm.Reg qualified as Reg
import Casm.Run qualified as Run import Casm.Run qualified as Run
allTests :: TestTree allTests :: TestTree
allTests = testGroup "CASM tests" [Run.allTests] allTests = testGroup "CASM tests" [Run.allTests, Reg.allTests, Compile.allTests]

7
test/Casm/Compilation.hs Normal file
View File

@ -0,0 +1,7 @@
module Casm.Compilation where
import Base
import Casm.Compilation.Positive qualified as P
allTests :: TestTree
allTests = testGroup "Juvix to CASM compilation" [P.allTests, P.allTestsNoOptimize]

View File

@ -0,0 +1,52 @@
module Casm.Compilation.Base where
import Base
import Juvix.Compiler.Casm.Data.Result
import Juvix.Compiler.Casm.Interpreter
import Juvix.Compiler.Casm.Pretty
import Juvix.Compiler.Core qualified as Core
import Juvix.Data.Field
import Juvix.Data.PPOutput
compileAssertion ::
Path Abs Dir ->
Int ->
Path Abs File ->
Path Abs File ->
(String -> IO ()) ->
Assertion
compileAssertion = compileAssertionEntry (\e -> e {_entryPointFieldSize = cairoFieldSize})
compileAssertionEntry ::
(EntryPoint -> EntryPoint) ->
Path Abs Dir ->
Int ->
Path Abs File ->
Path Abs File ->
(String -> IO ()) ->
Assertion
compileAssertionEntry adjustEntry root' optLevel mainFile expectedFile step = do
step "Translate to JuvixCore"
entryPoint <- adjustEntry <$> testDefaultEntryPointIO root' mainFile
PipelineResult {..} <- snd <$> testRunIO entryPoint upToStoredCore
step "Translate to CASM"
let entryPoint' = entryPoint {_entryPointOptimizationLevel = optLevel}
case run $ runError @JuvixError $ runReader entryPoint' $ storedCoreToCasm (_pipelineResult ^. Core.coreResultModule) of
Left err -> assertFailure (show (pretty (fromJuvixError @GenericError err)))
Right Result {..} -> do
withTempDir'
( \dirPath -> do
let outputFile = dirPath <//> $(mkRelFile "out.out")
let tmpFile = dirPath <//> $(mkRelFile "tmp.out")
step "Serialize"
writeFileEnsureLn tmpFile (toPlainText $ ppProgram _resultCode)
hout <- openFile (toFilePath outputFile) WriteMode
step "Interpret"
let v = hRunCode hout _resultLabelInfo _resultCode
hPutStrLn hout (show v)
hClose hout
actualOutput <- readFile outputFile
step "Compare expected and actual program output"
expected <- readFile expectedFile
assertEqDiffText ("Check: RUN output = " <> toFilePath expectedFile) actualOutput expected
)

View File

@ -0,0 +1,353 @@
module Casm.Compilation.Positive where
import Base
import Casm.Compilation.Base
import Data.HashSet qualified as HashSet
data PosTest = PosTest
{ _name :: String,
_dir :: Path Abs Dir,
_file :: Path Abs File,
_expectedFile :: Path Abs File
}
makeLenses ''PosTest
root :: Path Abs Dir
root = relToProject $(mkRelDir "tests/Casm/Compilation/positive/")
toTestDescr :: Int -> PosTest -> TestDescr
toTestDescr optLevel PosTest {..} =
let tRoot = _dir
file' = _file
expected' = _expectedFile
in TestDescr
{ _testName = _name,
_testRoot = tRoot,
_testAssertion = Steps $ compileAssertion _dir optLevel file' expected'
}
allTests :: TestTree
allTests =
testGroup
"Juvix to CASM positive tests"
(map (mkTest . toTestDescr 3) tests)
allTestsNoOptimize :: TestTree
allTestsNoOptimize =
testGroup
"Juvix to CASM positive tests (no optimization)"
(map (mkTest . toTestDescr 0) tests)
posTest :: String -> Path Rel Dir -> Path Rel File -> Path Rel File -> PosTest
posTest _name rdir rfile routfile =
let _dir = root <//> rdir
_file = _dir <//> rfile
_expectedFile = root <//> routfile
in PosTest {..}
isIgnored :: PosTest -> Bool
isIgnored t = HashSet.member (t ^. name) ignored
ignored :: HashSet String
ignored =
HashSet.fromList
[ -- strings not supported (Partial trait)
"Test026: Functional queues"
]
tests :: [PosTest]
tests =
filter
(not . isIgnored)
[ posTest
"Test001: Arithmetic operators"
$(mkRelDir ".")
$(mkRelFile "test001.juvix")
$(mkRelFile "out/test001.out"),
posTest
"Test002: Arithmetic operators inside lambdas"
$(mkRelDir ".")
$(mkRelFile "test002.juvix")
$(mkRelFile "out/test002.out"),
posTest
"Test003: Integer arithmetic"
$(mkRelDir ".")
$(mkRelFile "test003.juvix")
$(mkRelFile "out/test003.out"),
posTest
"Test005: Higher-order functions"
$(mkRelDir ".")
$(mkRelFile "test005.juvix")
$(mkRelFile "out/test005.out"),
posTest
"Test006: If-then-else and lazy boolean operators"
$(mkRelDir ".")
$(mkRelFile "test006.juvix")
$(mkRelFile "out/test006.out"),
posTest
"Test007: Pattern matching and lambda-case"
$(mkRelDir ".")
$(mkRelFile "test007.juvix")
$(mkRelFile "out/test007.out"),
posTest
"Test008: Recursion"
$(mkRelDir ".")
$(mkRelFile "test008.juvix")
$(mkRelFile "out/test008.out"),
posTest
"Test009: Tail recursion"
$(mkRelDir ".")
$(mkRelFile "test009.juvix")
$(mkRelFile "out/test009.out"),
posTest
"Test010: Let"
$(mkRelDir ".")
$(mkRelFile "test010.juvix")
$(mkRelFile "out/test010.out"),
posTest
"Test013: Functions returning functions with variable capture"
$(mkRelDir ".")
$(mkRelFile "test013.juvix")
$(mkRelFile "out/test013.out"),
posTest
"Test014: Arithmetic"
$(mkRelDir ".")
$(mkRelFile "test014.juvix")
$(mkRelFile "out/test014.out"),
posTest
"Test015: Local functions with free variables"
$(mkRelDir ".")
$(mkRelFile "test015.juvix")
$(mkRelFile "out/test015.out"),
posTest
"Test016: Recursion through higher-order functions"
$(mkRelDir ".")
$(mkRelFile "test016.juvix")
$(mkRelFile "out/test016.out"),
posTest
"Test017: Tail recursion through higher-order functions"
$(mkRelDir ".")
$(mkRelFile "test017.juvix")
$(mkRelFile "out/test017.out"),
posTest
"Test018: Higher-order functions and recursion"
$(mkRelDir ".")
$(mkRelFile "test018.juvix")
$(mkRelFile "out/test018.out"),
posTest
"Test019: Self-application"
$(mkRelDir ".")
$(mkRelFile "test019.juvix")
$(mkRelFile "out/test019.out"),
posTest
"Test020: Recursive functions: McCarthy's 91 function, subtraction by increments"
$(mkRelDir ".")
$(mkRelFile "test020.juvix")
$(mkRelFile "out/test020.out"),
posTest
"Test021: Fast exponentiation"
$(mkRelDir ".")
$(mkRelFile "test021.juvix")
$(mkRelFile "out/test021.out"),
posTest
"Test022: Lists"
$(mkRelDir ".")
$(mkRelFile "test022.juvix")
$(mkRelFile "out/test022.out"),
posTest
"Test023: Mutual recursion"
$(mkRelDir ".")
$(mkRelFile "test023.juvix")
$(mkRelFile "out/test023.out"),
posTest
"Test024: Nested binders with variable capture"
$(mkRelDir ".")
$(mkRelFile "test024.juvix")
$(mkRelFile "out/test024.out"),
posTest
"Test025: Euclid's algorithm"
$(mkRelDir ".")
$(mkRelFile "test025.juvix")
$(mkRelFile "out/test025.out"),
posTest
"Test026: Functional queues"
$(mkRelDir ".")
$(mkRelFile "test026.juvix")
$(mkRelFile "out/test026.out"),
posTest
"Test028: Streams without memoization"
$(mkRelDir ".")
$(mkRelFile "test028.juvix")
$(mkRelFile "out/test028.out"),
posTest
"Test029: Ackermann function"
$(mkRelDir ".")
$(mkRelFile "test029.juvix")
$(mkRelFile "out/test029.out"),
posTest
"Test030: Ackermann function (higher-order definition)"
$(mkRelDir ".")
$(mkRelFile "test030.juvix")
$(mkRelFile "out/test030.out"),
posTest
"Test032: Merge sort"
$(mkRelDir ".")
$(mkRelFile "test032.juvix")
$(mkRelFile "out/test032.out"),
posTest
"Test033: Eta-expansion of builtins and constructors"
$(mkRelDir ".")
$(mkRelFile "test033.juvix")
$(mkRelFile "out/test033.out"),
posTest
"Test034: Recursive let"
$(mkRelDir ".")
$(mkRelFile "test034.juvix")
$(mkRelFile "out/test034.out"),
posTest
"Test035: Pattern matching"
$(mkRelDir ".")
$(mkRelFile "test035.juvix")
$(mkRelFile "out/test035.out"),
posTest
"Test036: Eta-expansion"
$(mkRelDir ".")
$(mkRelFile "test036.juvix")
$(mkRelFile "out/test036.out"),
posTest
"Test037: Applications with lets and cases in function position"
$(mkRelDir ".")
$(mkRelFile "test037.juvix")
$(mkRelFile "out/test037.out"),
posTest
"Test038: Simple case expression"
$(mkRelDir ".")
$(mkRelFile "test038.juvix")
$(mkRelFile "out/test038.out"),
posTest
"Test039: Mutually recursive let expression"
$(mkRelDir ".")
$(mkRelFile "test039.juvix")
$(mkRelFile "out/test039.out"),
posTest
"Test040: Pattern matching nullary constructor"
$(mkRelDir ".")
$(mkRelFile "test040.juvix")
$(mkRelFile "out/test040.out"),
posTest
"Test045: Implicit builtin bool"
$(mkRelDir ".")
$(mkRelFile "test045.juvix")
$(mkRelFile "out/test045.out"),
posTest
"Test046: Polymorphic type arguments"
$(mkRelDir ".")
$(mkRelFile "test046.juvix")
$(mkRelFile "out/test046.out"),
posTest
"Test047: Local Modules"
$(mkRelDir ".")
$(mkRelFile "test047.juvix")
$(mkRelFile "out/test047.out"),
posTest
"Test050: Pattern matching with integers"
$(mkRelDir ".")
$(mkRelFile "test050.juvix")
$(mkRelFile "out/test050.out"),
posTest
"Test053: Inlining"
$(mkRelDir ".")
$(mkRelFile "test053.juvix")
$(mkRelFile "out/test053.out"),
posTest
"Test054: Iterators"
$(mkRelDir ".")
$(mkRelFile "test054.juvix")
$(mkRelFile "out/test054.out"),
posTest
"Test056: Argument specialization"
$(mkRelDir ".")
$(mkRelFile "test056.juvix")
$(mkRelFile "out/test056.out"),
posTest
"Test057: Case folding"
$(mkRelDir ".")
$(mkRelFile "test057.juvix")
$(mkRelFile "out/test057.out"),
posTest
"Test058: Ranges"
$(mkRelDir ".")
$(mkRelFile "test058.juvix")
$(mkRelFile "out/test058.out"),
posTest
"Test059: Builtin list"
$(mkRelDir ".")
$(mkRelFile "test059.juvix")
$(mkRelFile "out/test059.out"),
posTest
"Test060: Record update"
$(mkRelDir ".")
$(mkRelFile "test060.juvix")
$(mkRelFile "out/test060.out"),
posTest
"Test062: Overapplication"
$(mkRelDir ".")
$(mkRelFile "test062.juvix")
$(mkRelFile "out/test062.out"),
posTest
"Test064: Constant folding"
$(mkRelDir ".")
$(mkRelFile "test064.juvix")
$(mkRelFile "out/test064.out"),
posTest
"Test065: Arithmetic simplification"
$(mkRelDir ".")
$(mkRelFile "test065.juvix")
$(mkRelFile "out/test065.out"),
posTest
"Test066: Import function with a function call in default argument"
$(mkRelDir "test066")
$(mkRelFile "M.juvix")
$(mkRelFile "out/test066.out"),
posTest
"Test067: Dependent default values inserted during translation FromConcrete"
$(mkRelDir ".")
$(mkRelFile "test067.juvix")
$(mkRelFile "out/test067.out"),
posTest
"Test068: Dependent default values inserted in the arity checker"
$(mkRelDir ".")
$(mkRelFile "test068.juvix")
$(mkRelFile "out/test068.out"),
posTest
"Test069: Dependent default values for Ord trait"
$(mkRelDir ".")
$(mkRelFile "test069.juvix")
$(mkRelFile "out/test069.out"),
posTest
"Test070: Nested default values and named arguments"
$(mkRelDir ".")
$(mkRelFile "test070.juvix")
$(mkRelFile "out/test070.out"),
posTest
"Test071: Named application (Ord instance with default cmp)"
$(mkRelDir ".")
$(mkRelFile "test071.juvix")
$(mkRelFile "out/test071.out"),
posTest
"Test072: Monad transformers (ReaderT + StateT + Identity)"
$(mkRelDir "test072")
$(mkRelFile "ReaderT.juvix")
$(mkRelFile "out/test072.out"),
posTest
"Test073: Import and use a syntax alias"
$(mkRelDir "test073")
$(mkRelFile "test073.juvix")
$(mkRelFile "out/test073.out"),
posTest
"Test074: Fields"
$(mkRelDir ".")
$(mkRelFile "test074.juvix")
$(mkRelFile "out/test074.out")
]

7
test/Casm/Reg.hs Normal file
View File

@ -0,0 +1,7 @@
module Casm.Reg where
import Base
import Casm.Reg.Positive qualified as P
allTests :: TestTree
allTests = testGroup "CASM from JuvixReg translation" [P.allTests]

20
test/Casm/Reg/Base.hs Normal file
View File

@ -0,0 +1,20 @@
module Casm.Reg.Base where
import Base
import Juvix.Compiler.Casm.Data.Result
import Juvix.Compiler.Casm.Interpreter
import Juvix.Compiler.Reg.Data.InfoTable qualified as Reg
import Juvix.Data.PPOutput
import Reg.Run.Base qualified as Reg
compileAssertion' :: Handle -> Symbol -> Reg.InfoTable -> IO ()
compileAssertion' hout _ tab = do
case run $ runError @JuvixError $ regToCasm tab of
Left err -> assertFailure (show (pretty (fromJuvixError @GenericError err)))
Right Result {..} -> do
let v = hRunCode hout _resultLabelInfo _resultCode
hPutStrLn hout (show v)
regToCasmAssertion :: Path Abs File -> Path Abs File -> (String -> IO ()) -> Assertion
regToCasmAssertion mainFile expectedFile step =
Reg.regRunAssertionParam compileAssertion' mainFile expectedFile [] (const (return ())) step

183
test/Casm/Reg/Positive.hs Normal file
View File

@ -0,0 +1,183 @@
module Casm.Reg.Positive where
import Base
import Casm.Reg.Base
data PosTest = PosTest
{ _name :: String,
_relDir :: Path Rel Dir,
_file :: Path Rel File,
_expectedFile :: Path Rel File
}
root :: Path Abs Dir
root = relToProject $(mkRelDir "tests/Casm/Reg/positive")
testDescr :: PosTest -> TestDescr
testDescr PosTest {..} =
let tRoot = root <//> _relDir
file' = tRoot <//> _file
expected' = tRoot <//> _expectedFile
in TestDescr
{ _testName = _name,
_testRoot = tRoot,
_testAssertion = Steps $ regToCasmAssertion file' expected'
}
filterTests :: [String] -> [PosTest] -> [PosTest]
filterTests incl = filter (\PosTest {..} -> _name `elem` incl)
allTests :: TestTree
allTests =
testGroup
"CASM from JuvixReg translation positive tests"
(map (mkTest . testDescr) tests)
tests :: [PosTest]
tests =
[ PosTest
"Test001: Arithmetic opcodes"
$(mkRelDir ".")
$(mkRelFile "test001.jvr")
$(mkRelFile "out/test001.out"),
PosTest
"Test002: Direct call"
$(mkRelDir ".")
$(mkRelFile "test002.jvr")
$(mkRelFile "out/test002.out"),
PosTest
"Test003: Indirect call"
$(mkRelDir ".")
$(mkRelFile "test003.jvr")
$(mkRelFile "out/test003.out"),
PosTest
"Test004: Tail calls"
$(mkRelDir ".")
$(mkRelFile "test004.jvr")
$(mkRelFile "out/test004.out"),
PosTest
"Test008: Branch"
$(mkRelDir ".")
$(mkRelFile "test008.jvr")
$(mkRelFile "out/test008.out"),
PosTest
"Test009: Case"
$(mkRelDir ".")
$(mkRelFile "test009.jvr")
$(mkRelFile "out/test009.out"),
PosTest
"Test010: Recursion"
$(mkRelDir ".")
$(mkRelFile "test010.jvr")
$(mkRelFile "out/test010.out"),
PosTest
"Test011: Tail recursion"
$(mkRelDir ".")
$(mkRelFile "test011.jvr")
$(mkRelFile "out/test011.out"),
PosTest
"Test012: Temporary stack"
$(mkRelDir ".")
$(mkRelFile "test012.jvr")
$(mkRelFile "out/test012.out"),
PosTest
"Test013: Fibonacci numbers in linear time"
$(mkRelDir ".")
$(mkRelFile "test013.jvr")
$(mkRelFile "out/test013.out"),
PosTest
"Test014: Trees"
$(mkRelDir ".")
$(mkRelFile "test014.jvr")
$(mkRelFile "out/test014.out"),
PosTest
"Test015: Functions returning functions"
$(mkRelDir ".")
$(mkRelFile "test015.jvr")
$(mkRelFile "out/test015.out"),
PosTest
"Test016: Arithmetic"
$(mkRelDir ".")
$(mkRelFile "test016.jvr")
$(mkRelFile "out/test016.out"),
PosTest
"Test017: Closures as arguments"
$(mkRelDir ".")
$(mkRelFile "test017.jvr")
$(mkRelFile "out/test017.out"),
PosTest
"Test018: Closure extension"
$(mkRelDir ".")
$(mkRelFile "test018.jvr")
$(mkRelFile "out/test018.out"),
PosTest
"Test019: Recursion through higher-order functions"
$(mkRelDir ".")
$(mkRelFile "test019.jvr")
$(mkRelFile "out/test019.out"),
PosTest
"Test020: Tail recursion through higher-order functions"
$(mkRelDir ".")
$(mkRelFile "test020.jvr")
$(mkRelFile "out/test020.out"),
PosTest
"Test021: Higher-order functions and recursion"
$(mkRelDir ".")
$(mkRelFile "test021.jvr")
$(mkRelFile "out/test021.out"),
PosTest
"Test023: McCarthy's 91 function"
$(mkRelDir ".")
$(mkRelFile "test023.jvr")
$(mkRelFile "out/test023.out"),
PosTest
"Test024: Higher-order recursive functions"
$(mkRelDir ".")
$(mkRelFile "test024.jvr")
$(mkRelFile "out/test024.out"),
PosTest
"Test026: Currying & uncurrying"
$(mkRelDir ".")
$(mkRelFile "test026.jvr")
$(mkRelFile "out/test026.out"),
PosTest
"Test027: Fast exponentiation"
$(mkRelDir ".")
$(mkRelFile "test027.jvr")
$(mkRelFile "out/test027.out"),
PosTest
"Test028: Lists"
$(mkRelDir ".")
$(mkRelFile "test028.jvr")
$(mkRelFile "out/test028.out"),
PosTest
"Test030: Mutual recursion"
$(mkRelDir ".")
$(mkRelFile "test030.jvr")
$(mkRelFile "out/test030.out"),
PosTest
"Test031: Temporary stack with branching"
$(mkRelDir ".")
$(mkRelFile "test031.jvr")
$(mkRelFile "out/test031.out"),
PosTest
"Test033: Ackermann function"
$(mkRelDir ".")
$(mkRelFile "test033.jvr")
$(mkRelFile "out/test033.out"),
PosTest
"Test034: Higher-order function composition"
$(mkRelDir ".")
$(mkRelFile "test034.jvr")
$(mkRelFile "out/test034.out"),
PosTest
"Test036: Streams without memoization"
$(mkRelDir ".")
$(mkRelFile "test036.jvr")
$(mkRelFile "out/test036.out"),
PosTest
"Test038: Apply & argsnum"
$(mkRelDir ".")
$(mkRelFile "test038.jvr")
$(mkRelFile "out/test038.out")
]

View File

@ -1,7 +1,5 @@
module Casm.Run where module Casm.Run where
-- import Casm.Run.Negative qualified as RunN
import Base import Base
import Casm.Run.Negative qualified as RunN import Casm.Run.Negative qualified as RunN
import Casm.Run.Positive qualified as RunP import Casm.Run.Positive qualified as RunP

View File

@ -1,3 +1,4 @@
11 11
9 9
10 10
11

View File

@ -40,6 +40,17 @@ function mult(integer, integer) : integer {
ret; ret;
} }
function g(integer, integer, integer, integer) : integer {
push arg[1];
push arg[0];
sub;
push arg[2];
add;
push arg[3];
mul;
ret;
}
function main() { function main() {
calloc plus 0; calloc plus 0;
call f; call f;
@ -53,6 +64,12 @@ function main() {
call f; call f;
trace; -- 10 trace; -- 10
pop; pop;
push 2;
push 3;
calloc g 2;
call f;
trace; -- 11
pop;
push void; push void;
ret; ret;
} }

View File

@ -0,0 +1 @@
11

View File

@ -0,0 +1 @@
11

View File

@ -0,0 +1 @@
11

View File

@ -0,0 +1 @@
6

View File

@ -0,0 +1 @@
3

View File

@ -0,0 +1 @@
6

View File

@ -0,0 +1 @@
50005000

View File

@ -0,0 +1 @@
532635520

View File

@ -0,0 +1 @@
32

View File

@ -0,0 +1 @@
8

View File

@ -0,0 +1 @@
92

View File

@ -0,0 +1 @@
771

View File

@ -0,0 +1 @@
55

View File

@ -0,0 +1 @@
50005000

View File

@ -0,0 +1 @@
11

View File

@ -0,0 +1 @@
7

View File

@ -0,0 +1 @@
4876

View File

@ -0,0 +1 @@
48830320

View File

@ -0,0 +1 @@
100010000

View File

@ -0,0 +1 @@
6386010

View File

@ -0,0 +1 @@
6688

View File

@ -0,0 +1 @@
87

View File

@ -0,0 +1 @@
5050

View File

@ -0,0 +1 @@
2040

View File

@ -0,0 +1 @@
78

View File

@ -0,0 +1 @@
2247

View File

@ -0,0 +1 @@
195

View File

@ -0,0 +1 @@
31

View File

@ -0,0 +1 @@
50006027

View File

@ -0,0 +1 @@
34422

View File

@ -0,0 +1 @@
18

View File

@ -0,0 +1 @@
9

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1 @@
4

View File

@ -0,0 +1 @@
7

View File

@ -0,0 +1 @@
660

View File

@ -0,0 +1 @@
11

View File

@ -0,0 +1 @@
21

View File

@ -0,0 +1 @@
189

Some files were not shown because too many files have changed in this diff Show More