mirror of
https://github.com/anoma/juvix.git
synced 2024-09-11 08:15:41 +03:00
Rust runtime (#2782)
* Closes #2781 * This PR only implements the Rust runtime. The Rust backend / code generation need to be implemented in a separate PR. * The tests are unit tests for different modules and tests with "manually" compiled Juvix programs. * Adds building & testing of the Rust runtime to the CI.
This commit is contained in:
parent
60bffcfeb8
commit
7e737d7037
5
.github/workflows/ci.yml
vendored
5
.github/workflows/ci.yml
vendored
@ -164,6 +164,11 @@ jobs:
|
|||||||
cd main
|
cd main
|
||||||
just ${{ env.JUST_ARGS }} build runtime
|
just ${{ env.JUST_ARGS }} build runtime
|
||||||
|
|
||||||
|
- name: Test Rust runtime
|
||||||
|
run: |
|
||||||
|
cd main/runtime/rust
|
||||||
|
cargo test --release
|
||||||
|
|
||||||
# We use the options:
|
# We use the options:
|
||||||
# - -fhide-source-paths
|
# - -fhide-source-paths
|
||||||
# - -fwrite-ide-info -hiedir=.hie
|
# - -fwrite-ide-info -hiedir=.hie
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -56,7 +56,7 @@ TAGS
|
|||||||
# other
|
# other
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
/runtime/include/
|
/runtime/c/include/
|
||||||
_build/
|
_build/
|
||||||
_build.*/
|
_build.*/
|
||||||
*.agdai
|
*.agdai
|
||||||
|
2
Makefile
2
Makefile
@ -211,7 +211,7 @@ fast-build: submodules runtime
|
|||||||
|
|
||||||
.PHONY: runtime
|
.PHONY: runtime
|
||||||
runtime:
|
runtime:
|
||||||
cd runtime && make -j 4 -s
|
cd runtime && make
|
||||||
|
|
||||||
# -- Install
|
# -- Install
|
||||||
|
|
||||||
|
@ -52,10 +52,10 @@ nativeHelperOptions opts =
|
|||||||
| otherwise = nativeReleaseRuntime
|
| otherwise = nativeReleaseRuntime
|
||||||
where
|
where
|
||||||
nativeReleaseRuntime :: BS.ByteString
|
nativeReleaseRuntime :: BS.ByteString
|
||||||
nativeReleaseRuntime = $(FE.makeRelativeToProject "runtime/_build.native64/libjuvix.a" >>= FE.embedFile)
|
nativeReleaseRuntime = $(FE.makeRelativeToProject "runtime/c/_build.native64/libjuvix.a" >>= FE.embedFile)
|
||||||
|
|
||||||
nativeDebugRuntime :: BS.ByteString
|
nativeDebugRuntime :: BS.ByteString
|
||||||
nativeDebugRuntime = $(FE.makeRelativeToProject "runtime/_build.native64-debug/libjuvix.a" >>= FE.embedFile)
|
nativeDebugRuntime = $(FE.makeRelativeToProject "runtime/c/_build.native64-debug/libjuvix.a" >>= FE.embedFile)
|
||||||
|
|
||||||
nativeDefaultOutputFile :: Path Abs File -> Path Abs File -> Path Abs File
|
nativeDefaultOutputFile :: Path Abs File -> Path Abs File -> Path Abs File
|
||||||
nativeDefaultOutputFile inputFile baseOutputFile =
|
nativeDefaultOutputFile inputFile baseOutputFile =
|
||||||
|
@ -29,4 +29,4 @@ writeRuntime runtime = do
|
|||||||
mapM_ (uncurry writeHeader) headersDir
|
mapM_ (uncurry writeHeader) headersDir
|
||||||
where
|
where
|
||||||
headersDir :: [(Path Rel File, BS.ByteString)]
|
headersDir :: [(Path Rel File, BS.ByteString)]
|
||||||
headersDir = map (first relFile) $(FE.makeRelativeToProject "runtime/include" >>= FE.embedDir)
|
headersDir = map (first relFile) $(FE.makeRelativeToProject "runtime/c/include" >>= FE.embedDir)
|
||||||
|
@ -52,10 +52,10 @@ wasiHelperOptions opts =
|
|||||||
| otherwise = wasiReleaseRuntime
|
| otherwise = wasiReleaseRuntime
|
||||||
where
|
where
|
||||||
wasiReleaseRuntime :: BS.ByteString
|
wasiReleaseRuntime :: BS.ByteString
|
||||||
wasiReleaseRuntime = $(FE.makeRelativeToProject "runtime/_build.wasm32-wasi/libjuvix.a" >>= FE.embedFile)
|
wasiReleaseRuntime = $(FE.makeRelativeToProject "runtime/c/_build.wasm32-wasi/libjuvix.a" >>= FE.embedFile)
|
||||||
|
|
||||||
wasiDebugRuntime :: BS.ByteString
|
wasiDebugRuntime :: BS.ByteString
|
||||||
wasiDebugRuntime = $(FE.makeRelativeToProject "runtime/_build.wasm32-wasi-debug/libjuvix.a" >>= FE.embedFile)
|
wasiDebugRuntime = $(FE.makeRelativeToProject "runtime/c/_build.wasm32-wasi-debug/libjuvix.a" >>= FE.embedFile)
|
||||||
|
|
||||||
wasiDefaultOutputFile :: Path Abs File -> Path Abs File -> Path Abs File
|
wasiDefaultOutputFile :: Path Abs File -> Path Abs File -> Path Abs File
|
||||||
wasiDefaultOutputFile inputFile baseOutputFile =
|
wasiDefaultOutputFile inputFile baseOutputFile =
|
||||||
|
@ -58,16 +58,16 @@ prepareRuntime buildDir o = do
|
|||||||
AppTargetCairo -> return ()
|
AppTargetCairo -> return ()
|
||||||
where
|
where
|
||||||
wasiReleaseRuntime :: BS.ByteString
|
wasiReleaseRuntime :: BS.ByteString
|
||||||
wasiReleaseRuntime = $(FE.makeRelativeToProject "runtime/_build.wasm32-wasi/libjuvix.a" >>= FE.embedFile)
|
wasiReleaseRuntime = $(FE.makeRelativeToProject "runtime/c/_build.wasm32-wasi/libjuvix.a" >>= FE.embedFile)
|
||||||
|
|
||||||
nativeReleaseRuntime :: BS.ByteString
|
nativeReleaseRuntime :: BS.ByteString
|
||||||
nativeReleaseRuntime = $(FE.makeRelativeToProject "runtime/_build.native64/libjuvix.a" >>= FE.embedFile)
|
nativeReleaseRuntime = $(FE.makeRelativeToProject "runtime/c/_build.native64/libjuvix.a" >>= FE.embedFile)
|
||||||
|
|
||||||
wasiDebugRuntime :: BS.ByteString
|
wasiDebugRuntime :: BS.ByteString
|
||||||
wasiDebugRuntime = $(FE.makeRelativeToProject "runtime/_build.wasm32-wasi-debug/libjuvix.a" >>= FE.embedFile)
|
wasiDebugRuntime = $(FE.makeRelativeToProject "runtime/c/_build.wasm32-wasi-debug/libjuvix.a" >>= FE.embedFile)
|
||||||
|
|
||||||
nativeDebugRuntime :: BS.ByteString
|
nativeDebugRuntime :: BS.ByteString
|
||||||
nativeDebugRuntime = $(FE.makeRelativeToProject "runtime/_build.native64-debug/libjuvix.a" >>= FE.embedFile)
|
nativeDebugRuntime = $(FE.makeRelativeToProject "runtime/c/_build.native64-debug/libjuvix.a" >>= FE.embedFile)
|
||||||
|
|
||||||
writeRuntime :: BS.ByteString -> Sem r ()
|
writeRuntime :: BS.ByteString -> Sem r ()
|
||||||
writeRuntime =
|
writeRuntime =
|
||||||
@ -75,7 +75,7 @@ prepareRuntime buildDir o = do
|
|||||||
. BS.writeFile (toFilePath (buildDir <//> $(mkRelFile "libjuvix.a")))
|
. BS.writeFile (toFilePath (buildDir <//> $(mkRelFile "libjuvix.a")))
|
||||||
|
|
||||||
headersDir :: [(Path Rel File, BS.ByteString)]
|
headersDir :: [(Path Rel File, BS.ByteString)]
|
||||||
headersDir = map (first relFile) $(FE.makeRelativeToProject "runtime/include" >>= FE.embedDir)
|
headersDir = map (first relFile) $(FE.makeRelativeToProject "runtime/c/include" >>= FE.embedDir)
|
||||||
|
|
||||||
includeDir :: Path Abs Dir
|
includeDir :: Path Abs Dir
|
||||||
includeDir = juvixIncludeDir buildDir
|
includeDir = juvixIncludeDir buildDir
|
||||||
|
14
cntlines.sh
14
cntlines.sh
@ -8,12 +8,13 @@ function count_ext () {
|
|||||||
find $2 -name $1 -print | xargs sed '/^[[:space:]]*$/d' | wc -l | tr -d ' '
|
find $2 -name $1 -print | xargs sed '/^[[:space:]]*$/d' | wc -l | tr -d ' '
|
||||||
}
|
}
|
||||||
|
|
||||||
RUNTIME_C=$(count runtime/src/juvix)
|
RUNTIME_C=$(count runtime/c/src/juvix)
|
||||||
RUNTIME_VAMPIR=$(count_ext '*.pir' runtime/src/vampir)
|
RUNTIME_RUST=$(count runtime/rust/src)
|
||||||
RUNTIME_JVT=$(count_ext '*.jvt' runtime/src/tree)
|
RUNTIME_VAMPIR=$(count_ext '*.pir' runtime/vampir)
|
||||||
RUNTIME_CASM=$(count_ext '*.casm' runtime/src/casm)
|
RUNTIME_JVT=$(count_ext '*.jvt' runtime/tree)
|
||||||
|
RUNTIME_CASM=$(count_ext '*.casm' runtime/casm)
|
||||||
|
|
||||||
RUNTIME=$((RUNTIME_C+RUNTIME_VAMPIR+RUNTIME_JVT+RUNTIME_CASM))
|
RUNTIME=$((RUNTIME_C+RUNTIME_RUST+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 " JuvixTree: $TREE LOC"
|
|||||||
echo " JuvixCore: $CORE LOC"
|
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 " Rust runtime: $RUNTIME_RUST LOC"
|
||||||
echo " JuvixTree runtime: $RUNTIME_JVT LOC"
|
echo " JuvixTree runtime: $RUNTIME_JVT LOC"
|
||||||
echo " Cairo assembly runtime: $RUNTIME_CASM LOC"
|
echo " Cairo assembly runtime: $RUNTIME_CASM LOC"
|
||||||
echo " VampIR runtime: $RUNTIME_VAMPIR LOC"
|
echo " VampIR runtime: $RUNTIME_VAMPIR LOC"
|
||||||
@ -74,4 +76,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_CASM CASM LOC + $RUNTIME_VAMPIR VampIR LOC"
|
echo "Total: $TOTAL Haskell LOC + $RUNTIME_C C LOC + $RUNTIME_RUST Rust LOC + $RUNTIME_JVT JuvixTree LOC + $RUNTIME_CASM CASM LOC + $RUNTIME_VAMPIR VampIR LOC"
|
||||||
|
2
justfile
2
justfile
@ -104,7 +104,7 @@ run-profile +cmd:
|
|||||||
|
|
||||||
# Build the juvix runtime
|
# Build the juvix runtime
|
||||||
_buildRuntime:
|
_buildRuntime:
|
||||||
cd runtime && make {{ runtimeArgs }} -j 4 -s
|
cd runtime && make {{ runtimeArgs }}
|
||||||
|
|
||||||
# Build the project. `build runtime` builds only the runtime.
|
# Build the project. `build runtime` builds only the runtime.
|
||||||
[no-exit-message]
|
[no-exit-message]
|
||||||
|
12
package.yaml
12
package.yaml
@ -36,12 +36,12 @@ extra-source-files:
|
|||||||
- juvix-stdlib/**/*.juvix
|
- juvix-stdlib/**/*.juvix
|
||||||
- include/package/**/*.juvix
|
- include/package/**/*.juvix
|
||||||
- include/package-base/**/*.juvix
|
- include/package-base/**/*.juvix
|
||||||
- runtime/include/**/*.h
|
- runtime/c/include/**/*.h
|
||||||
- runtime/**/*.a
|
- runtime/c/**/*.a
|
||||||
- runtime/src/tree/*.jvt
|
- runtime/tree/*.jvt
|
||||||
- runtime/src/vampir/*.pir
|
- runtime/vampir/*.pir
|
||||||
- runtime/src/casm/*.casm
|
- runtime/casm/*.casm
|
||||||
- runtime/src/nockma/*.nockma
|
- runtime/nockma/*.nockma
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
- aeson-better-errors == 0.9.*
|
- aeson-better-errors == 0.9.*
|
||||||
|
@ -1,55 +1,21 @@
|
|||||||
all: release debug includes
|
|
||||||
|
|
||||||
release: wasm32-wasi native64
|
export
|
||||||
|
|
||||||
debug: wasm32-wasi-debug native64-debug
|
.PHONY: all
|
||||||
|
all: juvix_c juvix_rust
|
||||||
|
|
||||||
HEADERS := $(patsubst src/%,include/%,$(shell find src -name '*.h'))
|
.PHONY: juvix_c
|
||||||
|
juvix_c:
|
||||||
|
cd c && $(MAKE) -j 4 -s
|
||||||
|
|
||||||
includes: $(HEADERS)
|
.PHONY: juvix_rust
|
||||||
|
juvix_rust:
|
||||||
$(HEADERS) : include/%.h : src/%.h
|
cd rust && cargo build --release
|
||||||
@mkdir -p `dirname $@`
|
|
||||||
@cp $< $@
|
|
||||||
|
|
||||||
wasm32:
|
|
||||||
$(MAKE) -f Makefile.generic CONFIG=WASM32+RELEASE
|
|
||||||
|
|
||||||
wasm32-wasi:
|
|
||||||
$(MAKE) -f Makefile.generic CONFIG=WASM32-WASI+RELEASE
|
|
||||||
|
|
||||||
native32:
|
|
||||||
$(MAKE) -f Makefile.generic CONFIG=NATIVE32+RELEASE
|
|
||||||
|
|
||||||
native64:
|
|
||||||
$(MAKE) -f Makefile.generic CONFIG=NATIVE64+RELEASE
|
|
||||||
|
|
||||||
x86_32:
|
|
||||||
$(MAKE) -f Makefile.generic CONFIG=X86_32+RELEASE
|
|
||||||
|
|
||||||
wasm32-debug:
|
|
||||||
$(MAKE) -f Makefile.generic CONFIG=WASM32+DEBUG
|
|
||||||
|
|
||||||
wasm32-wasi-debug:
|
|
||||||
$(MAKE) -f Makefile.generic CONFIG=WASM32-WASI+DEBUG
|
|
||||||
|
|
||||||
native32-debug:
|
|
||||||
$(MAKE) -f Makefile.generic CONFIG=NATIVE32+DEBUG
|
|
||||||
|
|
||||||
native64-debug:
|
|
||||||
$(MAKE) -f Makefile.generic CONFIG=NATIVE64+DEBUG
|
|
||||||
|
|
||||||
x86_32-debug:
|
|
||||||
$(MAKE) -f Makefile.generic CONFIG=X86_32+DEBUG
|
|
||||||
|
|
||||||
asm:
|
|
||||||
$(MAKE) -f Makefile.generic CONFIG=ASM+DEBUG
|
|
||||||
|
|
||||||
format:
|
|
||||||
@clang-format -i `find src -name '*.c' -or -name '*.h'`
|
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
@-rm -rf _build*
|
cd c && $(MAKE) clean
|
||||||
@-rm -rf include
|
|
||||||
|
|
||||||
.PHONY: release debug includes wasm32 wasm32-wasi native32 native64 x86_32 wasm32-debug wasm32-wasi-debug native32-debug native64-debug x86_32-debug asm format clean
|
.PHONY: format
|
||||||
|
format:
|
||||||
|
cd c && $(MAKE) format
|
||||||
|
56
runtime/c/Makefile
Normal file
56
runtime/c/Makefile
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
all: release debug includes
|
||||||
|
|
||||||
|
release: wasm32-wasi native64
|
||||||
|
|
||||||
|
debug: wasm32-wasi-debug native64-debug
|
||||||
|
|
||||||
|
HEADERS := $(patsubst src/%,include/%,$(shell find src -name '*.h'))
|
||||||
|
CFORMAT := clang-format
|
||||||
|
|
||||||
|
includes: $(HEADERS)
|
||||||
|
|
||||||
|
$(HEADERS) : include/%.h : src/%.h
|
||||||
|
@mkdir -p `dirname $@`
|
||||||
|
@cp $< $@
|
||||||
|
|
||||||
|
wasm32:
|
||||||
|
$(MAKE) -f Makefile.generic CONFIG=WASM32+RELEASE
|
||||||
|
|
||||||
|
wasm32-wasi:
|
||||||
|
$(MAKE) -f Makefile.generic CONFIG=WASM32-WASI+RELEASE
|
||||||
|
|
||||||
|
native32:
|
||||||
|
$(MAKE) -f Makefile.generic CONFIG=NATIVE32+RELEASE
|
||||||
|
|
||||||
|
native64:
|
||||||
|
$(MAKE) -f Makefile.generic CONFIG=NATIVE64+RELEASE
|
||||||
|
|
||||||
|
x86_32:
|
||||||
|
$(MAKE) -f Makefile.generic CONFIG=X86_32+RELEASE
|
||||||
|
|
||||||
|
wasm32-debug:
|
||||||
|
$(MAKE) -f Makefile.generic CONFIG=WASM32+DEBUG
|
||||||
|
|
||||||
|
wasm32-wasi-debug:
|
||||||
|
$(MAKE) -f Makefile.generic CONFIG=WASM32-WASI+DEBUG
|
||||||
|
|
||||||
|
native32-debug:
|
||||||
|
$(MAKE) -f Makefile.generic CONFIG=NATIVE32+DEBUG
|
||||||
|
|
||||||
|
native64-debug:
|
||||||
|
$(MAKE) -f Makefile.generic CONFIG=NATIVE64+DEBUG
|
||||||
|
|
||||||
|
x86_32-debug:
|
||||||
|
$(MAKE) -f Makefile.generic CONFIG=X86_32+DEBUG
|
||||||
|
|
||||||
|
asm:
|
||||||
|
$(MAKE) -f Makefile.generic CONFIG=ASM+DEBUG
|
||||||
|
|
||||||
|
format:
|
||||||
|
@$(CFORMAT) -i `find src -name '*.c' -or -name '*.h'`
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@-rm -rf _build*
|
||||||
|
@-rm -rf include
|
||||||
|
|
||||||
|
.PHONY: release debug includes wasm32 wasm32-wasi native32 native64 x86_32 wasm32-debug wasm32-wasi-debug native32-debug native64-debug x86_32-debug asm format clean
|
3
runtime/rust/.gitignore
vendored
Normal file
3
runtime/rust/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/target
|
||||||
|
Cargo.lock
|
||||||
|
*~
|
8
runtime/rust/Cargo.toml
Normal file
8
runtime/rust/Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "juvix"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
70
runtime/rust/src/apply.rs
Normal file
70
runtime/rust/src/apply.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// Closure application
|
||||||
|
|
||||||
|
use super::defs::*;
|
||||||
|
use super::memory::*;
|
||||||
|
|
||||||
|
pub enum AppResult {
|
||||||
|
// Call(fid)
|
||||||
|
Call(Word, Vec<Word>),
|
||||||
|
// Return(result_closure)
|
||||||
|
Return(Word),
|
||||||
|
// Continue(fid, args_left)
|
||||||
|
Continue(Word, Vec<Word>, Vec<Word>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Memory {
|
||||||
|
pub fn apply(self: &mut Memory, cl: Word, cargs: &[Word]) -> AppResult {
|
||||||
|
let argsnum = self.get_closure_largs(cl);
|
||||||
|
if argsnum == cargs.len() {
|
||||||
|
let (fid, args) = self.call_closure(cl, cargs);
|
||||||
|
AppResult::Call(fid, args)
|
||||||
|
} else if argsnum > cargs.len() {
|
||||||
|
AppResult::Return(self.extend_closure(cl, cargs))
|
||||||
|
} else {
|
||||||
|
let (fid, args) = self.call_closure(cl, &cargs[0..argsnum]);
|
||||||
|
AppResult::Continue(fid, args, Vec::from(&cargs[argsnum..cargs.len()]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! tapply {
|
||||||
|
($lab:lifetime, $program:ident, $mem:ident, $fid:ident, $args:ident, $cl0:expr, $cargs0:expr) => {
|
||||||
|
let mut cl = $cl0;
|
||||||
|
let mut cargs = $cargs0;
|
||||||
|
loop {
|
||||||
|
match $mem.apply( cl, &cargs) {
|
||||||
|
apply::AppResult::Call(fid1, args1) => {
|
||||||
|
$fid = fid1;
|
||||||
|
$args = args1;
|
||||||
|
continue $lab;
|
||||||
|
}
|
||||||
|
apply::AppResult::Return(r) => break $lab r,
|
||||||
|
apply::AppResult::Continue(fid1, args1, cargs1) => {
|
||||||
|
cl = $program($mem, fid1, args1);
|
||||||
|
cargs = cargs1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! apply {
|
||||||
|
($program:ident, $mem:ident, $cl0:expr, $cargs0:expr) => {{
|
||||||
|
let mut cl = $cl0;
|
||||||
|
let mut cargs = $cargs0;
|
||||||
|
loop {
|
||||||
|
match $mem.apply(cl, &cargs) {
|
||||||
|
apply::AppResult::Call(fid, args) => {
|
||||||
|
break $program($mem, fid, args);
|
||||||
|
}
|
||||||
|
apply::AppResult::Return(r) => break r,
|
||||||
|
apply::AppResult::Continue(fid1, args1, cargs1) => {
|
||||||
|
cl = $program($mem, fid1, args1);
|
||||||
|
cargs = cargs1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
130
runtime/rust/src/closure.rs
Normal file
130
runtime/rust/src/closure.rs
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
// Closure manipulation
|
||||||
|
|
||||||
|
use super::defs::*;
|
||||||
|
use super::memory::*;
|
||||||
|
|
||||||
|
pub const CLOSURE_HEADER_SIZE: usize = 3;
|
||||||
|
|
||||||
|
impl Memory {
|
||||||
|
pub fn alloc_closure(self: &mut Memory, fid: Word, args: &[Word], largs: usize) -> Pointer {
|
||||||
|
let p = self.alloc(args.len() + CLOSURE_HEADER_SIZE);
|
||||||
|
self.set_closure_fid(p, fid);
|
||||||
|
self.set_closure_nargs(p, args.len());
|
||||||
|
self.set_closure_largs(p, largs);
|
||||||
|
self.set_closure_args(p, args);
|
||||||
|
p
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extend_closure(self: &mut Memory, ptr: Pointer, args: &[Word]) -> Pointer {
|
||||||
|
let nargs = self.get_closure_nargs(ptr);
|
||||||
|
let p = self.alloc(nargs + args.len() + CLOSURE_HEADER_SIZE);
|
||||||
|
self.set_closure_fid(p, self.get_closure_fid(ptr));
|
||||||
|
self.set_closure_nargs(p, self.get_closure_nargs(ptr) + args.len());
|
||||||
|
self.set_closure_largs(p, self.get_closure_largs(ptr) - args.len());
|
||||||
|
let i1 = word_to_usize(ptr) + CLOSURE_HEADER_SIZE;
|
||||||
|
let i2 = word_to_usize(p) + CLOSURE_HEADER_SIZE;
|
||||||
|
for i in 0..nargs {
|
||||||
|
self[i2 + i] = self[i1 + i]
|
||||||
|
}
|
||||||
|
self[i2 + nargs..i2 + nargs + args.len()].copy_from_slice(args);
|
||||||
|
p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assigns stored closure args plus the provided closure args (`cargs`) to the
|
||||||
|
// argument space (`args`). Returns the function id to call.
|
||||||
|
pub fn call_closure(self: &Memory, cl: Word, cargs: &[Word]) -> (Word, Vec<Word>) {
|
||||||
|
let mut args = Vec::from(self.get_closure_args(cl));
|
||||||
|
args.extend(cargs);
|
||||||
|
return (self.get_closure_fid(cl), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function id
|
||||||
|
pub fn get_closure_fid(self: &Memory, ptr: Pointer) -> Word {
|
||||||
|
self[word_to_usize(ptr)]
|
||||||
|
}
|
||||||
|
|
||||||
|
// The number of arguments stored in the closure
|
||||||
|
pub fn get_closure_nargs(self: &Memory, ptr: Pointer) -> usize {
|
||||||
|
word_to_usize(self[word_to_usize(ptr) + 1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// The number of arguments remaining for a call to the function
|
||||||
|
pub fn get_closure_largs(self: &Memory, ptr: Pointer) -> usize {
|
||||||
|
word_to_usize(self[word_to_usize(ptr) + 2])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_closure_arg(self: &Memory, ptr: Pointer, idx: Word) -> Word {
|
||||||
|
self[word_to_usize(ptr) + CLOSURE_HEADER_SIZE + word_to_usize(idx)]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_closure_args(self: &Memory, ptr: Pointer) -> &[Word] {
|
||||||
|
let i = word_to_usize(ptr) + CLOSURE_HEADER_SIZE;
|
||||||
|
let nargs = self.get_closure_nargs(ptr);
|
||||||
|
&self[i..i + nargs]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mut_closure_args(self: &mut Memory, ptr: Pointer) -> &mut [Word] {
|
||||||
|
let i = word_to_usize(ptr) + CLOSURE_HEADER_SIZE;
|
||||||
|
let nargs = self.get_closure_nargs(ptr);
|
||||||
|
&mut self[i..i + nargs]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_closure_fid(self: &mut Memory, ptr: Pointer, uid: Word) {
|
||||||
|
self[word_to_usize(ptr)] = uid
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_closure_nargs(self: &mut Memory, ptr: Pointer, nargs: usize) {
|
||||||
|
self[word_to_usize(ptr) + 1] = usize_to_word(nargs)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_closure_largs(self: &mut Memory, ptr: Pointer, largs: usize) {
|
||||||
|
self[word_to_usize(ptr) + 2] = usize_to_word(largs)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_closure_arg(self: &mut Memory, ptr: Pointer, idx: Word, x: Word) {
|
||||||
|
self[word_to_usize(ptr) + CLOSURE_HEADER_SIZE + word_to_usize(idx)] = x
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_closure_args(self: &mut Memory, ptr: Pointer, args: &[Word]) {
|
||||||
|
self.mut_closure_args(ptr).copy_from_slice(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_closures() {
|
||||||
|
let mut mem = Memory::new();
|
||||||
|
for fid in 0..10 {
|
||||||
|
for k in 0..100 {
|
||||||
|
let args: Vec<Word> = (0..k).collect();
|
||||||
|
let largs = (k / 2) as usize;
|
||||||
|
let cl = mem.alloc_closure(fid, &args, largs);
|
||||||
|
assert_eq!(mem.get_closure_fid(cl), fid);
|
||||||
|
assert_eq!(mem.get_closure_nargs(cl), args.len());
|
||||||
|
assert_eq!(mem.get_closure_largs(cl), largs);
|
||||||
|
assert_eq!(mem.get_closure_args(cl), args);
|
||||||
|
mem.set_closure_fid(cl, fid + 1);
|
||||||
|
assert_eq!(mem.get_closure_fid(cl), fid + 1);
|
||||||
|
if k > 0 {
|
||||||
|
mem.set_closure_arg(cl, k / 2, 789);
|
||||||
|
assert_eq!(mem.get_closure_arg(cl, k / 2), 789);
|
||||||
|
}
|
||||||
|
if largs > 1 {
|
||||||
|
let n: Word = k + largs as Word - 1;
|
||||||
|
let args1: Vec<Word> = (k..n).collect();
|
||||||
|
let cl1 = mem.extend_closure(cl, &args1);
|
||||||
|
let mut args2: Vec<Word> = (0..n).collect();
|
||||||
|
if k > 0 {
|
||||||
|
args2[(k / 2) as usize] = 789;
|
||||||
|
}
|
||||||
|
assert_eq!(mem.get_closure_args(cl1), args2);
|
||||||
|
assert_eq!(mem.get_closure_nargs(cl1), args2.len());
|
||||||
|
assert_eq!(mem.get_closure_largs(cl1), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
80
runtime/rust/src/constr.rs
Normal file
80
runtime/rust/src/constr.rs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
// Constructor access and manipulation
|
||||||
|
|
||||||
|
use super::defs::*;
|
||||||
|
use super::memory::*;
|
||||||
|
|
||||||
|
pub const CONSTR_HEADER_SIZE: usize = 2;
|
||||||
|
|
||||||
|
impl Memory {
|
||||||
|
pub fn alloc_constr(self: &mut Memory, tag: Word, args: &[Word]) -> Pointer {
|
||||||
|
let p = self.alloc(args.len() + CONSTR_HEADER_SIZE);
|
||||||
|
self.set_constr_tag(p, tag);
|
||||||
|
self.set_constr_nargs(p, args.len());
|
||||||
|
self.set_constr_args(p, args);
|
||||||
|
p
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_constr_tag(self: &Memory, ptr: Pointer) -> Word {
|
||||||
|
self[word_to_usize(ptr)]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_constr_nargs(self: &Memory, ptr: Pointer) -> usize {
|
||||||
|
word_to_usize(self[word_to_usize(ptr) + 1])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_constr_arg(self: &Memory, ptr: Pointer, idx: Word) -> Word {
|
||||||
|
self[word_to_usize(ptr) + CONSTR_HEADER_SIZE + word_to_usize(idx)]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_constr_args(self: &Memory, ptr: Pointer) -> &[Word] {
|
||||||
|
let i = word_to_usize(ptr) + CONSTR_HEADER_SIZE;
|
||||||
|
let nargs = self.get_constr_nargs(ptr);
|
||||||
|
&self[i..i + nargs]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mut_constr_args(self: &mut Memory, ptr: Pointer) -> &mut [Word] {
|
||||||
|
let i = word_to_usize(ptr) + CONSTR_HEADER_SIZE;
|
||||||
|
let nargs = self.get_constr_nargs(ptr);
|
||||||
|
&mut self[i..i + nargs]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_constr_tag(self: &mut Memory, ptr: Pointer, tag: Word) {
|
||||||
|
self[word_to_usize(ptr)] = tag
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_constr_nargs(self: &mut Memory, ptr: Pointer, nfields: usize) {
|
||||||
|
self[word_to_usize(ptr) + 1] = usize_to_word(nfields)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_constr_arg(self: &mut Memory, ptr: Pointer, idx: Word, x: Word) {
|
||||||
|
self[word_to_usize(ptr) + CONSTR_HEADER_SIZE + word_to_usize(idx)] = x
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_constr_args(self: &mut Memory, ptr: Pointer, args: &[Word]) {
|
||||||
|
self.mut_constr_args(ptr).copy_from_slice(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_constrs() {
|
||||||
|
let mut mem = Memory::new();
|
||||||
|
for tag in 0..10 {
|
||||||
|
for k in 0..100 {
|
||||||
|
let args: Vec<Word> = (0..k).collect();
|
||||||
|
let ctr = mem.alloc_constr(tag, &args);
|
||||||
|
assert_eq!(mem.get_constr_tag(ctr), tag);
|
||||||
|
assert_eq!(mem.get_constr_args(ctr), args);
|
||||||
|
mem.set_constr_tag(ctr, tag + 1);
|
||||||
|
assert_eq!(mem.get_constr_tag(ctr), tag + 1);
|
||||||
|
if k > 0 {
|
||||||
|
mem.set_constr_arg(ctr, k / 2, 789);
|
||||||
|
assert_eq!(mem.get_constr_arg(ctr, k / 2), 789);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
runtime/rust/src/defs.rs
Normal file
33
runtime/rust/src/defs.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// Runtime definitions
|
||||||
|
|
||||||
|
// Type of words
|
||||||
|
pub type Word = u32;
|
||||||
|
|
||||||
|
// Type of pointers
|
||||||
|
pub type Pointer = Word;
|
||||||
|
|
||||||
|
// Type of signed small integers that fit into a single word
|
||||||
|
pub type SmallInt = i32;
|
||||||
|
|
||||||
|
pub const INITIAL_STACK_CAPACITY: usize = 10 * 1024;
|
||||||
|
pub const INITIAL_MEMORY_CAPACITY: usize = 10 * 1024;
|
||||||
|
|
||||||
|
pub fn word_to_usize(s: Word) -> usize {
|
||||||
|
s as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn usize_to_word(s: usize) -> Word {
|
||||||
|
s as Word
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bool_to_word(x: bool) -> Word {
|
||||||
|
x as Word
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn word_to_bool(x: Word) -> bool {
|
||||||
|
if x == 0 {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
9
runtime/rust/src/equality.rs
Normal file
9
runtime/rust/src/equality.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// Equality
|
||||||
|
|
||||||
|
use super::defs::*;
|
||||||
|
|
||||||
|
// Check for equality. The implementation is incorrect for complex objects, but
|
||||||
|
// complex objects are not compared in Juvix with built-in equality for now.
|
||||||
|
pub fn juvix_equal(x: Word, y: Word) -> bool {
|
||||||
|
x == y
|
||||||
|
}
|
68
runtime/rust/src/integer.rs
Normal file
68
runtime/rust/src/integer.rs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
// Integers
|
||||||
|
|
||||||
|
use super::defs::*;
|
||||||
|
|
||||||
|
pub fn make_smallint(x: SmallInt) -> Word {
|
||||||
|
x as Word
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn smallint_value(x: Word) -> SmallInt {
|
||||||
|
x as SmallInt
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn smallint_add(x: Word, y: Word) -> Word {
|
||||||
|
x + y
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn smallint_sub(x: Word, y: Word) -> Word {
|
||||||
|
make_smallint(smallint_value(x) - smallint_value(y))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn smallint_mul(x: Word, y: Word) -> Word {
|
||||||
|
make_smallint(smallint_value(x) * smallint_value(y))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn smallint_div(x: Word, y: Word) -> Word {
|
||||||
|
make_smallint(smallint_value(x) / smallint_value(y))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn smallint_mod(x: Word, y: Word) -> Word {
|
||||||
|
make_smallint(smallint_value(x) % smallint_value(y))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn smallint_lt(x: Word, y: Word) -> Word {
|
||||||
|
bool_to_word(smallint_value(x) < smallint_value(y))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn smallint_le(x: Word, y: Word) -> Word {
|
||||||
|
bool_to_word(smallint_value(x) <= smallint_value(y))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_integer_conversion() {
|
||||||
|
for x in 0..100 {
|
||||||
|
assert_eq!(smallint_value(make_smallint(x)), x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_integer_arithmetic() {
|
||||||
|
for x in 1..100 {
|
||||||
|
let y = smallint_div(
|
||||||
|
smallint_mul(
|
||||||
|
smallint_sub(
|
||||||
|
smallint_add(make_smallint(x), make_smallint(4)),
|
||||||
|
make_smallint(4),
|
||||||
|
),
|
||||||
|
make_smallint(7),
|
||||||
|
),
|
||||||
|
make_smallint(7),
|
||||||
|
);
|
||||||
|
assert_eq!(make_smallint(x), y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
238
runtime/rust/src/lib.rs
Normal file
238
runtime/rust/src/lib.rs
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
#[macro_use]
|
||||||
|
pub mod apply;
|
||||||
|
pub mod closure;
|
||||||
|
pub mod constr;
|
||||||
|
pub mod defs;
|
||||||
|
pub mod equality;
|
||||||
|
pub mod integer;
|
||||||
|
pub mod memory;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::apply;
|
||||||
|
use super::defs::*;
|
||||||
|
use super::equality::*;
|
||||||
|
use super::integer::*;
|
||||||
|
use super::memory::*;
|
||||||
|
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
fn program_fib(mut fid: Word, args: Vec<Word>) -> Word {
|
||||||
|
const FUN_FIB: Word = 0;
|
||||||
|
loop {
|
||||||
|
match fid {
|
||||||
|
FUN_FIB => {
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
let mut tmp1: Word;
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
let mut tmp2: Word;
|
||||||
|
if word_to_bool(smallint_le(args[0], make_smallint(1))) {
|
||||||
|
break args[0];
|
||||||
|
} else {
|
||||||
|
tmp1 = program_fib(FUN_FIB, vec![smallint_sub(args[0], make_smallint(1))]);
|
||||||
|
tmp2 = program_fib(FUN_FIB, vec![smallint_sub(args[0], make_smallint(2))]);
|
||||||
|
break (smallint_add(tmp1, tmp2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => panic!("unknown function id"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn program_itfib(mut fid: Word, mut args: Vec<Word>) -> Word {
|
||||||
|
const FUN_ITFIB: Word = 0;
|
||||||
|
const FUN_ITFIB_GO: Word = 1;
|
||||||
|
loop {
|
||||||
|
match fid {
|
||||||
|
FUN_ITFIB => {
|
||||||
|
args = vec![args[0], 0, 1];
|
||||||
|
fid = FUN_ITFIB_GO;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
FUN_ITFIB_GO => {
|
||||||
|
if juvix_equal(args[0], make_smallint(0)) {
|
||||||
|
break args[1];
|
||||||
|
} else {
|
||||||
|
args = vec![
|
||||||
|
smallint_sub(args[0], make_smallint(1)),
|
||||||
|
args[2],
|
||||||
|
smallint_add(args[1], args[2]),
|
||||||
|
];
|
||||||
|
fid = FUN_ITFIB_GO;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => panic!("unknown function id"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn program_closure_call(mem: &mut Memory, mut fid: Word, mut args: Vec<Word>) -> Word {
|
||||||
|
const FUN_MAIN: Word = 0;
|
||||||
|
const FUN_CALCULATE: Word = 1;
|
||||||
|
const FUN_APPLY_1: Word = 2;
|
||||||
|
loop {
|
||||||
|
match fid {
|
||||||
|
FUN_MAIN => {
|
||||||
|
args[0] =
|
||||||
|
mem.alloc_closure(FUN_CALCULATE, &[make_smallint(5), make_smallint(3)], 1);
|
||||||
|
fid = FUN_APPLY_1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
FUN_CALCULATE => {
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
let mut tmp1: Word;
|
||||||
|
tmp1 = smallint_mul(args[2], args[1]);
|
||||||
|
tmp1 = smallint_add(args[0], tmp1);
|
||||||
|
return tmp1;
|
||||||
|
}
|
||||||
|
FUN_APPLY_1 => {
|
||||||
|
(fid, args) = mem.call_closure(args[0], &[make_smallint(2)]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_ => panic!("unknown function id"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn program_sk(mem: &mut Memory, mut fid: Word, mut args: Vec<Word>) -> Word {
|
||||||
|
const FUN_MAIN: Word = 0;
|
||||||
|
const FUN_S: Word = 1;
|
||||||
|
const FUN_K: Word = 2;
|
||||||
|
const FUN_I: Word = 3;
|
||||||
|
'program: loop {
|
||||||
|
match fid {
|
||||||
|
FUN_MAIN => {
|
||||||
|
let id = program_sk(mem, FUN_I, vec![]);
|
||||||
|
let x = apply!(
|
||||||
|
program_sk,
|
||||||
|
mem,
|
||||||
|
id,
|
||||||
|
vec![id, id, id, id, id, id, make_smallint(1)]
|
||||||
|
);
|
||||||
|
let y = apply!(
|
||||||
|
program_sk,
|
||||||
|
mem,
|
||||||
|
id,
|
||||||
|
vec![id, id, id, id, id, id, id, id, id, make_smallint(1)]
|
||||||
|
);
|
||||||
|
let z = apply!(
|
||||||
|
program_sk,
|
||||||
|
mem,
|
||||||
|
id,
|
||||||
|
vec![id, id, id, id, id, id, id, id, id, id, id, make_smallint(1)]
|
||||||
|
);
|
||||||
|
let tmp1 = smallint_add(x, y);
|
||||||
|
break smallint_add(tmp1, z);
|
||||||
|
}
|
||||||
|
FUN_S => {
|
||||||
|
let xz = apply!(program_sk, mem, args[0], vec![args[2]]);
|
||||||
|
let yz = apply!(program_sk, mem, args[1], vec![args[2]]);
|
||||||
|
tapply!('program, program_sk, mem, fid, args, xz, vec![yz]);
|
||||||
|
}
|
||||||
|
FUN_K => {
|
||||||
|
break args[0];
|
||||||
|
}
|
||||||
|
FUN_I => {
|
||||||
|
let k = mem.alloc_closure(FUN_K, &[], 2);
|
||||||
|
break mem.alloc_closure(FUN_S, &[k, k], 1);
|
||||||
|
}
|
||||||
|
_ => panic!("unknown function id"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
fn program_lists(mem: &mut Memory, mut fid: Word, mut args: Vec<Word>) -> Word {
|
||||||
|
const FUN_MAIN: Word = 0;
|
||||||
|
const FUN_MAP: Word = 1;
|
||||||
|
const FUN_ADD_ONE: Word = 2;
|
||||||
|
const FUN_SUM: Word = 3;
|
||||||
|
const FUN_GEN: Word = 4;
|
||||||
|
const TAG_NIL: Word = 0;
|
||||||
|
const TAG_CONS: Word = 1;
|
||||||
|
loop {
|
||||||
|
match fid {
|
||||||
|
FUN_MAIN => {
|
||||||
|
let lst1 = program_lists(mem, FUN_GEN, vec![make_smallint(1000)]);
|
||||||
|
let inc = mem.alloc_closure(FUN_ADD_ONE, &[], 1);
|
||||||
|
let lst2 = program_lists(mem, FUN_MAP, vec![inc, lst1]);
|
||||||
|
return program_lists(mem, FUN_SUM, vec![lst2]);
|
||||||
|
}
|
||||||
|
FUN_MAP => match mem.get_constr_tag(args[1]) {
|
||||||
|
TAG_NIL => {
|
||||||
|
return args[1];
|
||||||
|
}
|
||||||
|
TAG_CONS => {
|
||||||
|
let h = apply!(
|
||||||
|
program_lists,
|
||||||
|
mem,
|
||||||
|
args[0],
|
||||||
|
vec![mem.get_constr_arg(args[1], 0)]
|
||||||
|
);
|
||||||
|
let t = program_lists(
|
||||||
|
mem,
|
||||||
|
FUN_MAP,
|
||||||
|
vec![args[0], mem.get_constr_arg(args[1], 1)],
|
||||||
|
);
|
||||||
|
return mem.alloc_constr(TAG_CONS, &[h, t]);
|
||||||
|
}
|
||||||
|
_ => panic!("unknown constructor tag"),
|
||||||
|
},
|
||||||
|
FUN_ADD_ONE => {
|
||||||
|
return smallint_add(args[0], make_smallint(1));
|
||||||
|
}
|
||||||
|
FUN_SUM => match mem.get_constr_tag(args[0]) {
|
||||||
|
TAG_NIL => return make_smallint(0),
|
||||||
|
TAG_CONS => {
|
||||||
|
let s = program_lists(mem, FUN_SUM, vec![mem.get_constr_arg(args[0], 1)]);
|
||||||
|
return smallint_add(s, mem.get_constr_arg(args[0], 0));
|
||||||
|
}
|
||||||
|
_ => panic!("unknown constructor tag"),
|
||||||
|
},
|
||||||
|
FUN_GEN => {
|
||||||
|
if args[0] == make_smallint(0) {
|
||||||
|
return mem.alloc_constr(TAG_NIL, &[]);
|
||||||
|
} else {
|
||||||
|
let t = program_lists(
|
||||||
|
mem,
|
||||||
|
FUN_GEN,
|
||||||
|
vec![smallint_sub(args[0], make_smallint(1))],
|
||||||
|
);
|
||||||
|
return mem.alloc_constr(TAG_CONS, &[args[0], t]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => panic!("unknown function id"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fib() {
|
||||||
|
let result = program_fib(0, vec![11]);
|
||||||
|
assert_eq!(result, 89);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_itfib() {
|
||||||
|
let result = program_itfib(0, vec![11, 0, 0]);
|
||||||
|
assert_eq!(result, 89);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_closure_call() {
|
||||||
|
let result = program_closure_call(&mut Memory::new(), 0, vec![0, 0, 0]);
|
||||||
|
assert_eq!(result, 11);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sk() {
|
||||||
|
let result = program_sk(&mut Memory::new(), 0, vec![]);
|
||||||
|
assert_eq!(result, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lists() {
|
||||||
|
let result = program_lists(&mut Memory::new(), 0, vec![]);
|
||||||
|
assert_eq!(result, 501500);
|
||||||
|
}
|
||||||
|
}
|
61
runtime/rust/src/memory.rs
Normal file
61
runtime/rust/src/memory.rs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// Heap memory management
|
||||||
|
|
||||||
|
use super::defs::*;
|
||||||
|
|
||||||
|
pub struct Memory {
|
||||||
|
memory: Vec<Word>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Memory {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
memory: Vec::with_capacity(INITIAL_MEMORY_CAPACITY),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn alloc(&mut self, size: usize) -> Pointer {
|
||||||
|
let idx = self.memory.len();
|
||||||
|
self.memory.resize(idx + size, 0);
|
||||||
|
idx as Pointer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Idx> std::ops::Index<Idx> for Memory
|
||||||
|
where
|
||||||
|
Idx: std::slice::SliceIndex<[Word]>,
|
||||||
|
{
|
||||||
|
type Output = Idx::Output;
|
||||||
|
|
||||||
|
fn index(&self, index: Idx) -> &Self::Output {
|
||||||
|
&self.memory[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Idx> std::ops::IndexMut<Idx> for Memory
|
||||||
|
where
|
||||||
|
Idx: std::slice::SliceIndex<[Word]>,
|
||||||
|
{
|
||||||
|
fn index_mut(&mut self, index: Idx) -> &mut Self::Output {
|
||||||
|
&mut self.memory[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_memory() {
|
||||||
|
let mut mem = Memory::new();
|
||||||
|
for k in 0..100 {
|
||||||
|
let p = mem.alloc(1000);
|
||||||
|
for j in 1..1001 {
|
||||||
|
mem[(p + j - 1) as usize] = k * 1000 + j;
|
||||||
|
assert_eq!(mem[(p + j - 1) as usize], k * 1000 + j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i in 0..100_000 {
|
||||||
|
assert_eq!(mem[i as usize], i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -103,9 +103,9 @@ vampIRDefs bits unsafe =
|
|||||||
<> ";\n\n"
|
<> ";\n\n"
|
||||||
<> if
|
<> if
|
||||||
| unsafe ->
|
| unsafe ->
|
||||||
UTF8.toString $(FE.makeRelativeToProject ("runtime/src/vampir/stdlib_unsafe" <> vampIRFileExt) >>= FE.embedFile)
|
UTF8.toString $(FE.makeRelativeToProject ("runtime/vampir/stdlib_unsafe" <> vampIRFileExt) >>= FE.embedFile)
|
||||||
| otherwise ->
|
| otherwise ->
|
||||||
UTF8.toString $(FE.makeRelativeToProject ("runtime/src/vampir/stdlib" <> vampIRFileExt) >>= FE.embedFile)
|
UTF8.toString $(FE.makeRelativeToProject ("runtime/vampir/stdlib" <> vampIRFileExt) >>= FE.embedFile)
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
-- helper functions
|
-- helper functions
|
||||||
|
@ -30,7 +30,7 @@ addStdlibBuiltins addr = do
|
|||||||
instrs <-
|
instrs <-
|
||||||
fmap (fromRight impossible) $
|
fmap (fromRight impossible) $
|
||||||
runParser' addr "stdlib.casm" $
|
runParser' addr "stdlib.casm" $
|
||||||
decodeUtf8 $(FE.makeRelativeToProject "runtime/src/casm/stdlib.casm" >>= FE.embedFile)
|
decodeUtf8 $(FE.makeRelativeToProject "runtime/casm/stdlib.casm" >>= FE.embedFile)
|
||||||
let _stdlibGetRegsName :: Text = "juvix_get_regs"
|
let _stdlibGetRegsName :: Text = "juvix_get_regs"
|
||||||
_stdlibCallClosureName :: Text = "juvix_call_closure"
|
_stdlibCallClosureName :: Text = "juvix_call_closure"
|
||||||
_stdlibExtendClosureName :: Text = "juvix_extend_closure"
|
_stdlibExtendClosureName :: Text = "juvix_extend_closure"
|
||||||
|
@ -8,4 +8,4 @@ stdlib :: Term Natural
|
|||||||
stdlib =
|
stdlib =
|
||||||
fromRight impossible $
|
fromRight impossible $
|
||||||
parseText $
|
parseText $
|
||||||
decodeUtf8 $(FE.makeRelativeToProject "runtime/src/nockma/stdlib.nockma" >>= FE.embedFile)
|
decodeUtf8 $(FE.makeRelativeToProject "runtime/nockma/stdlib.nockma" >>= FE.embedFile)
|
||||||
|
@ -27,7 +27,7 @@ addApplyBuiltins tab = (blts, bs' ^. stateInfoTable)
|
|||||||
bs' =
|
bs' =
|
||||||
fromRight impossible $
|
fromRight impossible $
|
||||||
parseText' bs $
|
parseText' bs $
|
||||||
decodeUtf8 $(FE.makeRelativeToProject "runtime/src/tree/apply.jvt" >>= FE.embedFile)
|
decodeUtf8 $(FE.makeRelativeToProject "runtime/tree/apply.jvt" >>= FE.embedFile)
|
||||||
|
|
||||||
blts :: ApplyBuiltins
|
blts :: ApplyBuiltins
|
||||||
blts =
|
blts =
|
||||||
|
@ -99,7 +99,7 @@ commonArgs outputFile =
|
|||||||
]
|
]
|
||||||
where
|
where
|
||||||
runtimeInclude :: FilePath
|
runtimeInclude :: FilePath
|
||||||
runtimeInclude = $(makeRelativeToProject "runtime/include" >>= strToExp)
|
runtimeInclude = $(makeRelativeToProject "runtime/c/include" >>= strToExp)
|
||||||
|
|
||||||
native64Args :: Int -> Path Abs File -> Path Abs File -> [String]
|
native64Args :: Int -> Path Abs File -> Path Abs File -> [String]
|
||||||
native64Args optLevel outputFile inputFile =
|
native64Args optLevel outputFile inputFile =
|
||||||
@ -115,7 +115,7 @@ native64Args optLevel outputFile inputFile =
|
|||||||
]
|
]
|
||||||
where
|
where
|
||||||
juvixLibraryDir :: FilePath
|
juvixLibraryDir :: FilePath
|
||||||
juvixLibraryDir = $(makeRelativeToProject "runtime/_build.native64-debug" >>= strToExp)
|
juvixLibraryDir = $(makeRelativeToProject "runtime/c/_build.native64-debug" >>= strToExp)
|
||||||
|
|
||||||
wasiArgs :: Int -> Path Abs Dir -> Path Abs File -> Path Abs File -> [String]
|
wasiArgs :: Int -> Path Abs Dir -> Path Abs File -> Path Abs File -> [String]
|
||||||
wasiArgs optLevel sysrootPath outputFile inputFile =
|
wasiArgs optLevel sysrootPath outputFile inputFile =
|
||||||
@ -134,4 +134,4 @@ wasiArgs optLevel sysrootPath outputFile inputFile =
|
|||||||
]
|
]
|
||||||
where
|
where
|
||||||
juvixLibraryDir :: Path Abs Dir
|
juvixLibraryDir :: Path Abs Dir
|
||||||
juvixLibraryDir = absDir $(makeRelativeToProject "runtime/_build.wasm32-wasi-debug" >>= strToExp)
|
juvixLibraryDir = absDir $(makeRelativeToProject "runtime/c/_build.wasm32-wasi-debug" >>= strToExp)
|
||||||
|
Loading…
Reference in New Issue
Block a user