Skip to content

Commit

Permalink
JuvixCore to JuvixAsm translation (#1665)
Browse files Browse the repository at this point in the history
An implementation of the translation from JuvixCore to JuvixAsm. After
merging this PR, the only remaining step to complete the basic
compilation pipeline (#1556) is the compilation of complex pattern
matching (#1531).

* Fixes several bugs in lambda-lifting.
* Fixes several bugs in the RemoveTypeArgs transformation.
* Fixes several bugs in the TopEtaExpand transformation.
* Adds the ConvertBuiltinTypes transformation which converts the builtin
bool inductive type to Core primitive bool.
* Adds the `juvix dev core strip` command.
* Adds the `juvix dev core asm` command.
* Adds the `juvix dev core compile` command.
* Adds two groups of tests: 
- JuvixCore to JuvixAsm translation: translate JuvixCore tests to
JuvixAsm and run the results with the JuvixAsm interpreter,
- JuvixCore compilation: compile JuvixCore tests to native code and WASM
and execute the results.
* Closes #1520 
* Closes #1549
  • Loading branch information
lukaszcz authored Jan 9, 2023
1 parent f126489 commit f2298bd
Show file tree
Hide file tree
Showing 71 changed files with 1,108 additions and 303 deletions.
37 changes: 37 additions & 0 deletions app/AsmInterpreter.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
module AsmInterpreter where

import App
import CommonOptions
import Juvix.Compiler.Asm.Data.InfoTable qualified as Asm
import Juvix.Compiler.Asm.Extra qualified as Asm
import Juvix.Compiler.Asm.Interpreter qualified as Asm
import Juvix.Compiler.Asm.Pretty qualified as Asm
import Juvix.Compiler.Asm.Transformation.Validate qualified as Asm

runAsm :: forall r. Members '[Embed IO, App] r => Bool -> Asm.InfoTable -> Sem r ()
runAsm bValidate tab =
let v = if bValidate then Asm.validate' tab else Nothing
in case v of
Just err ->
exitJuvixError (JuvixError err)
Nothing ->
case tab ^. Asm.infoMainFunction of
Just sym -> do
r <- doRun tab (Asm.getFunInfo tab sym)
case r of
Left err ->
exitJuvixError (JuvixError err)
Right Asm.ValVoid ->
return ()
Right val -> do
renderStdOut (Asm.ppOut (Asm.defaultOptions tab) val)
embed (putStrLn "")
Nothing ->
exitMsg (ExitFailure 1) "no 'main' function"
where
doRun ::
Asm.InfoTable ->
Asm.FunctionInfo ->
Sem r (Either Asm.AsmError Asm.Val)
doRun tab' funInfo =
embed $ Asm.catchRunErrorIO (Asm.runCodeIO tab' funInfo)
34 changes: 2 additions & 32 deletions app/Commands/Dev/Asm/Run.hs
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
module Commands.Dev.Asm.Run where

import AsmInterpreter
import Commands.Base
import Commands.Dev.Asm.Run.Options
import Juvix.Compiler.Asm.Data.InfoTable qualified as Asm
import Juvix.Compiler.Asm.Error qualified as Asm
import Juvix.Compiler.Asm.Extra qualified as Asm
import Juvix.Compiler.Asm.Interpreter qualified as Asm
import Juvix.Compiler.Asm.Pretty qualified as Asm
import Juvix.Compiler.Asm.Transformation.Validate qualified as Asm
import Juvix.Compiler.Asm.Translation.FromSource qualified as Asm

runCommand :: forall r. Members '[Embed IO, App] r => AsmRunOptions -> Sem r ()
Expand All @@ -16,32 +11,7 @@ runCommand opts = do
s <- embed (readFile (toFilePath afile))
case Asm.runParser (toFilePath afile) s of
Left err -> exitJuvixError (JuvixError err)
Right tab ->
let v = if opts ^. asmRunNoValidate then Nothing else Asm.validate' tab
in case v of
Just err ->
exitJuvixError (JuvixError err)
Nothing ->
case tab ^. Asm.infoMainFunction of
Just sym -> do
r <- doRun tab (Asm.getFunInfo tab sym)
case r of
Left err ->
exitJuvixError (JuvixError err)
Right Asm.ValVoid ->
return ()
Right val -> do
renderStdOut (Asm.ppOut (Asm.defaultOptions tab) val)
embed (putStrLn "")
Nothing ->
exitMsg (ExitFailure 1) "no 'main' function"
Right tab -> runAsm (not (opts ^. asmRunNoValidate)) tab
where
file :: SomeBase File
file = opts ^. asmRunInputFile . pathPath

doRun ::
Asm.InfoTable ->
Asm.FunctionInfo ->
Sem r (Either Asm.AsmError Asm.Val)
doRun tab funInfo =
embed $ Asm.catchRunErrorIO (Asm.runCodeIO tab funInfo)
6 changes: 6 additions & 0 deletions app/Commands/Dev/Core.hs
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
module Commands.Dev.Core where

import Commands.Base
import Commands.Dev.Core.Asm as Asm
import Commands.Dev.Core.Compile as Compile
import Commands.Dev.Core.Eval as Eval
import Commands.Dev.Core.Options
import Commands.Dev.Core.Read as Read
import Commands.Dev.Core.Repl as Repl
import Commands.Dev.Core.Strip as Strip

runCommand :: forall r. Members '[Embed IO, App] r => CoreCommand -> Sem r ()
runCommand = \case
Repl opts -> Repl.runCommand opts
Eval opts -> Eval.runCommand opts
Read opts -> Read.runCommand opts
Strip opts -> Strip.runCommand opts
CoreAsm opts -> Asm.runCommand opts
CoreCompile opts -> Compile.runCommand opts
24 changes: 24 additions & 0 deletions app/Commands/Dev/Core/Asm.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module Commands.Dev.Core.Asm where

import AsmInterpreter
import Commands.Base
import Commands.Dev.Core.Asm.Options
import Juvix.Compiler.Asm.Pretty qualified as Asm
import Juvix.Compiler.Asm.Translation.FromCore qualified as Asm
import Juvix.Compiler.Core.Pipeline qualified as Core
import Juvix.Compiler.Core.Translation.FromSource qualified as Core
import Juvix.Compiler.Core.Translation.Stripped.FromCore qualified as Stripped

runCommand :: forall r a. (Members '[Embed IO, App] r, CanonicalProjection a CoreAsmOptions) => a -> Sem r ()
runCommand opts = do
inputFile :: Path Abs File <- someBaseToAbs' sinputFile
s' <- embed (readFile $ toFilePath inputFile)
tab <- getRight (mapLeft JuvixError (Core.runParserMain (toFilePath inputFile) Core.emptyInfoTable s'))
let tab' = Asm.fromCore $ Stripped.fromCore (Core.toStripped tab)
if
| project opts ^. coreAsmPrint ->
renderStdOut (Asm.ppOutDefault tab' tab')
| otherwise -> runAsm True tab'
where
sinputFile :: SomeBase File
sinputFile = project opts ^. coreAsmInputFile . pathPath
21 changes: 21 additions & 0 deletions app/Commands/Dev/Core/Asm/Options.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module Commands.Dev.Core.Asm.Options where

import CommonOptions

data CoreAsmOptions = CoreAsmOptions
{ _coreAsmPrint :: Bool,
_coreAsmInputFile :: AppPath File
}
deriving stock (Data)

makeLenses ''CoreAsmOptions

parseCoreAsmOptions :: Parser CoreAsmOptions
parseCoreAsmOptions = do
_coreAsmPrint <-
switch
( long "print"
<> help "print the generated JuvixAsm code instead of running it"
)
_coreAsmInputFile <- parseInputJuvixCoreFile
pure CoreAsmOptions {..}
43 changes: 43 additions & 0 deletions app/Commands/Dev/Core/Compile.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
module Commands.Dev.Core.Compile where

import Commands.Base
import Commands.Dev.Core.Compile.Options
import Commands.Extra.Compile qualified as Compile
import Data.Text.IO qualified as TIO
import Juvix.Compiler.Asm.Options qualified as Asm
import Juvix.Compiler.Backend qualified as Backend
import Juvix.Compiler.Backend.C qualified as C
import Juvix.Compiler.Core.Data.InfoTable qualified as Core
import Juvix.Compiler.Core.Translation.FromSource qualified as Core

runCommand :: forall r. Members '[Embed IO, App] r => CoreCompileOptions -> Sem r ()
runCommand opts = do
file <- getFile
s <- embed (readFile (toFilePath file))
tab <- getRight (mapLeft JuvixError (Core.runParserMain (toFilePath file) Core.emptyInfoTable s))
C.MiniCResult {..} <- getRight (run (runError (coreToMiniC asmOpts tab :: Sem '[Error JuvixError] C.MiniCResult)))
buildDir <- askBuildDir
ensureDir buildDir
cFile <- inputCFile file
embed $ TIO.writeFile (toFilePath cFile) _resultCCode
Compile.runCommand opts {_compileInputFile = AppPath (Abs cFile) False}
where
getFile :: Sem r (Path Abs File)
getFile = someBaseToAbs' (opts ^. compileInputFile . pathPath)

asmOpts :: Asm.Options
asmOpts = Asm.makeOptions (asmTarget (opts ^. compileTarget)) (opts ^. compileDebug)

asmTarget :: CompileTarget -> Backend.Target
asmTarget = \case
TargetWasm32Wasi -> Backend.TargetCWasm32Wasi
TargetNative64 -> Backend.TargetCNative64
TargetC -> Backend.TargetCWasm32Wasi

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)
13 changes: 13 additions & 0 deletions app/Commands/Dev/Core/Compile/Options.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Commands.Dev.Core.Compile.Options
( module Commands.Dev.Core.Compile.Options,
module Commands.Extra.Compile.Options,
)
where

import Commands.Extra.Compile.Options
import CommonOptions

type CoreCompileOptions = CompileOptions

parseCoreCompileOptions :: Parser CoreCompileOptions
parseCoreCompileOptions = parseCompileOptions
38 changes: 37 additions & 1 deletion app/Commands/Dev/Core/Options.hs
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
module Commands.Dev.Core.Options where

import Commands.Dev.Core.Asm.Options
import Commands.Dev.Core.Compile.Options
import Commands.Dev.Core.Eval.Options
import Commands.Dev.Core.Read.Options
import Commands.Dev.Core.Repl.Options
import Commands.Dev.Core.Strip.Options
import CommonOptions

data CoreCommand
= Repl CoreReplOptions
| Eval CoreEvalOptions
| Read CoreReadOptions
| Strip CoreStripOptions
| CoreAsm CoreAsmOptions
| CoreCompile CoreCompileOptions
deriving stock (Data)

parseCoreCommand :: Parser CoreCommand
Expand All @@ -17,7 +23,10 @@ parseCoreCommand =
mconcat
[ commandRepl,
commandEval,
commandRead
commandRead,
commandStrip,
commandAsm,
commandCompile
]
where
commandRepl :: Mod CommandFields CoreCommand
Expand All @@ -29,6 +38,15 @@ parseCoreCommand =
commandRead :: Mod CommandFields CoreCommand
commandRead = command "read" readInfo

commandStrip :: Mod CommandFields CoreCommand
commandStrip = command "strip" stripInfo

commandAsm :: Mod CommandFields CoreCommand
commandAsm = command "asm" asmInfo

commandCompile :: Mod CommandFields CoreCommand
commandCompile = command "compile" compileInfo

replInfo :: ParserInfo CoreCommand
replInfo =
info
Expand All @@ -46,3 +64,21 @@ parseCoreCommand =
info
(Read <$> parseCoreReadOptions)
(progDesc "Read a JuvixCore file, transform it, and pretty print it")

stripInfo :: ParserInfo CoreCommand
stripInfo =
info
(Strip <$> parseCoreStripOptions)
(progDesc "Translate a JuvixCore file to Core.Stripped and pretty print the result")

asmInfo :: ParserInfo CoreCommand
asmInfo =
info
(CoreAsm <$> parseCoreAsmOptions)
(progDesc "Translate a JuvixCore file to JuvixAsm and run the result")

compileInfo :: ParserInfo CoreCommand
compileInfo =
info
(CoreCompile <$> parseCoreCompileOptions)
(progDesc "Compile a JuvixCore file to native code or WebAssembly")
9 changes: 3 additions & 6 deletions app/Commands/Dev/Core/Read.hs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ runCommand :: forall r a. (Members '[Embed IO, App] r, CanonicalProjection a Eva
runCommand opts = do
inputFile :: Path Abs File <- someBaseToAbs' sinputFile
s' <- embed . readFile . toFilePath $ inputFile
(tab, mnode) <- getRight (mapLeft JuvixError (Core.runParser (toFilePath inputFile) Core.emptyInfoTable s'))
tab <- getRight (mapLeft JuvixError (Core.runParserMain (toFilePath inputFile) Core.emptyInfoTable s'))
let tab' = Core.applyTransformations (project opts ^. coreReadTransformations) tab
embed (Scoper.scopeTrace tab')
unless (project opts ^. coreReadNoPrint) $ do
renderStdOut (Core.ppOut opts tab')
whenJust mnode $ doEval tab'
whenJust (tab' ^. Core.infoMain) $ \sym -> doEval tab' (fromJust $ tab' ^. Core.identContext . at sym)
where
doEval :: Core.InfoTable -> Core.Node -> Sem r ()
doEval tab' node =
Expand All @@ -27,9 +27,6 @@ runCommand opts = do
embed (putStrLn "| Eval |")
embed (putStrLn "--------------------------------")
Eval.evalAndPrint opts tab' node
| otherwise -> do
embed (putStrLn "-- Node")
renderStdOut (Core.ppOut opts node)
embed (putStrLn "")
| otherwise -> return ()
sinputFile :: SomeBase File
sinputFile = project opts ^. coreReadInputFile . pathPath
20 changes: 20 additions & 0 deletions app/Commands/Dev/Core/Strip.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module Commands.Dev.Core.Strip where

import Commands.Base
import Commands.Dev.Core.Strip.Options
import Juvix.Compiler.Core.Pipeline qualified as Core
import Juvix.Compiler.Core.Pretty qualified as Core
import Juvix.Compiler.Core.Translation.FromSource qualified as Core
import Juvix.Compiler.Core.Translation.Stripped.FromCore qualified as Stripped

runCommand :: forall r a. (Members '[Embed IO, App] r, CanonicalProjection a Core.Options, CanonicalProjection a CoreStripOptions) => a -> Sem r ()
runCommand opts = do
inputFile :: Path Abs File <- someBaseToAbs' sinputFile
s' <- embed (readFile $ toFilePath inputFile)
(tab, _) <- getRight (mapLeft JuvixError (Core.runParser (toFilePath inputFile) Core.emptyInfoTable s'))
let tab' = Stripped.fromCore (Core.toStripped tab)
unless (project opts ^. coreStripNoPrint) $ do
renderStdOut (Core.ppOut opts tab')
where
sinputFile :: SomeBase File
sinputFile = project opts ^. coreStripInputFile . pathPath
30 changes: 30 additions & 0 deletions app/Commands/Dev/Core/Strip/Options.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module Commands.Dev.Core.Strip.Options where

import CommonOptions
import Juvix.Compiler.Core.Pretty.Options qualified as Core

data CoreStripOptions = CoreStripOptions
{ _coreStripShowDeBruijn :: Bool,
_coreStripNoPrint :: Bool,
_coreStripInputFile :: AppPath File
}
deriving stock (Data)

makeLenses ''CoreStripOptions

instance CanonicalProjection CoreStripOptions Core.Options where
project c =
Core.defaultOptions
{ Core._optShowDeBruijnIndices = c ^. coreStripShowDeBruijn
}

parseCoreStripOptions :: Parser CoreStripOptions
parseCoreStripOptions = do
_coreStripShowDeBruijn <- optDeBruijn
_coreStripNoPrint <-
switch
( long "no-print"
<> help "do not print the stripped code"
)
_coreStripInputFile <- parseInputJuvixCoreFile
pure CoreStripOptions {..}
2 changes: 1 addition & 1 deletion app/Commands/Extra/Compile/Options.hs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ parseCompileOptions = do
)
_compileTarget <- optCompileTarget
_compileOutputFile <- optional parseGenericOutputFile
_compileInputFile <- parseInputCFile
_compileInputFile <- parseGenericInputFile
pure CompileOptions {..}

optCompileTarget :: Parser CompileTarget
Expand Down
11 changes: 11 additions & 0 deletions app/CommonOptions.hs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,17 @@ parseInputCFile = do
)
pure AppPath {_pathIsInput = True, ..}

parseGenericInputFile :: Parser (AppPath File)
parseGenericInputFile = do
_pathPath <-
argument
someFileOpt
( metavar "INPUT_FILE"
<> help "Path to input file"
<> action "file"
)
pure AppPath {_pathIsInput = True, ..}

parseGenericOutputFile :: Parser (AppPath File)
parseGenericOutputFile = do
_pathPath <-
Expand Down
2 changes: 1 addition & 1 deletion runtime/src/juvix/api.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
io_print_toplevel(juvix_result);

// Temporary vars
#define DECL_TMP(k) word_t juvix_tmp_##k
#define DECL_TMP(k) UNUSED word_t juvix_tmp_##k
#define TMP(k) juvix_tmp_##k

// Value stack temporary vars
Expand Down
Loading

0 comments on commit f2298bd

Please sign in to comment.