1
1
mirror of https://github.com/anoma/juvix.git synced 2024-07-07 04:36:19 +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:
Łukasz Czajka 2024-05-22 12:26:51 +02:00 committed by GitHub
parent 60bffcfeb8
commit 7e737d7037
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
77 changed files with 810 additions and 81 deletions

View File

@ -164,6 +164,11 @@ jobs:
cd main
just ${{ env.JUST_ARGS }} build runtime
- name: Test Rust runtime
run: |
cd main/runtime/rust
cargo test --release
# We use the options:
# - -fhide-source-paths
# - -fwrite-ide-info -hiedir=.hie

2
.gitignore vendored
View File

@ -56,7 +56,7 @@ TAGS
# other
.DS_Store
/runtime/include/
/runtime/c/include/
_build/
_build.*/
*.agdai

View File

@ -211,7 +211,7 @@ fast-build: submodules runtime
.PHONY: runtime
runtime:
cd runtime && make -j 4 -s
cd runtime && make
# -- Install

View File

@ -52,10 +52,10 @@ nativeHelperOptions opts =
| otherwise = nativeReleaseRuntime
where
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 = $(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 inputFile baseOutputFile =

View File

@ -29,4 +29,4 @@ writeRuntime runtime = do
mapM_ (uncurry writeHeader) headersDir
where
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)

View File

@ -52,10 +52,10 @@ wasiHelperOptions opts =
| otherwise = wasiReleaseRuntime
where
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 = $(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 inputFile baseOutputFile =

View File

@ -58,16 +58,16 @@ prepareRuntime buildDir o = do
AppTargetCairo -> return ()
where
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 = $(FE.makeRelativeToProject "runtime/_build.native64/libjuvix.a" >>= FE.embedFile)
nativeReleaseRuntime = $(FE.makeRelativeToProject "runtime/c/_build.native64/libjuvix.a" >>= FE.embedFile)
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 = $(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 =
@ -75,7 +75,7 @@ prepareRuntime buildDir o = do
. BS.writeFile (toFilePath (buildDir <//> $(mkRelFile "libjuvix.a")))
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 = juvixIncludeDir buildDir

View File

@ -8,12 +8,13 @@ function count_ext () {
find $2 -name $1 -print | xargs sed '/^[[:space:]]*$/d' | wc -l | tr -d ' '
}
RUNTIME_C=$(count runtime/src/juvix)
RUNTIME_VAMPIR=$(count_ext '*.pir' runtime/src/vampir)
RUNTIME_JVT=$(count_ext '*.jvt' runtime/src/tree)
RUNTIME_CASM=$(count_ext '*.casm' runtime/src/casm)
RUNTIME_C=$(count runtime/c/src/juvix)
RUNTIME_RUST=$(count runtime/rust/src)
RUNTIME_VAMPIR=$(count_ext '*.pir' runtime/vampir)
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/)
CAIRO=$(count src/Juvix/Compiler/Backend/Cairo/)
@ -62,6 +63,7 @@ echo " JuvixTree: $TREE LOC"
echo " JuvixCore: $CORE LOC"
echo "Runtime: $RUNTIME LOC"
echo " C runtime: $RUNTIME_C LOC"
echo " Rust runtime: $RUNTIME_RUST LOC"
echo " JuvixTree runtime: $RUNTIME_JVT LOC"
echo " Cairo assembly runtime: $RUNTIME_CASM LOC"
echo " VampIR runtime: $RUNTIME_VAMPIR LOC"
@ -74,4 +76,4 @@ echo " Data: $DATA LOC"
echo " Prelude: $PRELUDE LOC"
echo "Tests: $TESTS LOC"
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"

View File

@ -104,7 +104,7 @@ run-profile +cmd:
# Build the juvix runtime
_buildRuntime:
cd runtime && make {{ runtimeArgs }} -j 4 -s
cd runtime && make {{ runtimeArgs }}
# Build the project. `build runtime` builds only the runtime.
[no-exit-message]

View File

@ -36,12 +36,12 @@ extra-source-files:
- juvix-stdlib/**/*.juvix
- include/package/**/*.juvix
- include/package-base/**/*.juvix
- runtime/include/**/*.h
- runtime/**/*.a
- runtime/src/tree/*.jvt
- runtime/src/vampir/*.pir
- runtime/src/casm/*.casm
- runtime/src/nockma/*.nockma
- runtime/c/include/**/*.h
- runtime/c/**/*.a
- runtime/tree/*.jvt
- runtime/vampir/*.pir
- runtime/casm/*.casm
- runtime/nockma/*.nockma
dependencies:
- aeson-better-errors == 0.9.*

View File

@ -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)
$(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:
@clang-format -i `find src -name '*.c' -or -name '*.h'`
.PHONY: juvix_rust
juvix_rust:
cd rust && cargo build --release
.PHONY: clean
clean:
@-rm -rf _build*
@-rm -rf include
cd c && $(MAKE) clean
.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
View 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
View File

@ -0,0 +1,3 @@
/target
Cargo.lock
*~

8
runtime/rust/Cargo.toml Normal file
View 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
View 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
View 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);
}
}
}
}
}

View 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
View 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
}
}

View 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
}

View 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
View 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);
}
}

View 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);
}
}
}

View File

@ -103,9 +103,9 @@ vampIRDefs bits unsafe =
<> ";\n\n"
<> if
| 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 ->
UTF8.toString $(FE.makeRelativeToProject ("runtime/src/vampir/stdlib" <> vampIRFileExt) >>= FE.embedFile)
UTF8.toString $(FE.makeRelativeToProject ("runtime/vampir/stdlib" <> vampIRFileExt) >>= FE.embedFile)
--------------------------------------------------------------------------------
-- helper functions

View File

@ -30,7 +30,7 @@ addStdlibBuiltins addr = do
instrs <-
fmap (fromRight impossible) $
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"
_stdlibCallClosureName :: Text = "juvix_call_closure"
_stdlibExtendClosureName :: Text = "juvix_extend_closure"

View File

@ -8,4 +8,4 @@ stdlib :: Term Natural
stdlib =
fromRight impossible $
parseText $
decodeUtf8 $(FE.makeRelativeToProject "runtime/src/nockma/stdlib.nockma" >>= FE.embedFile)
decodeUtf8 $(FE.makeRelativeToProject "runtime/nockma/stdlib.nockma" >>= FE.embedFile)

View File

@ -27,7 +27,7 @@ addApplyBuiltins tab = (blts, bs' ^. stateInfoTable)
bs' =
fromRight impossible $
parseText' bs $
decodeUtf8 $(FE.makeRelativeToProject "runtime/src/tree/apply.jvt" >>= FE.embedFile)
decodeUtf8 $(FE.makeRelativeToProject "runtime/tree/apply.jvt" >>= FE.embedFile)
blts :: ApplyBuiltins
blts =

View File

@ -99,7 +99,7 @@ commonArgs outputFile =
]
where
runtimeInclude :: FilePath
runtimeInclude = $(makeRelativeToProject "runtime/include" >>= strToExp)
runtimeInclude = $(makeRelativeToProject "runtime/c/include" >>= strToExp)
native64Args :: Int -> Path Abs File -> Path Abs File -> [String]
native64Args optLevel outputFile inputFile =
@ -115,7 +115,7 @@ native64Args optLevel outputFile inputFile =
]
where
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 optLevel sysrootPath outputFile inputFile =
@ -134,4 +134,4 @@ wasiArgs optLevel sysrootPath outputFile inputFile =
]
where
juvixLibraryDir :: Path Abs Dir
juvixLibraryDir = absDir $(makeRelativeToProject "runtime/_build.wasm32-wasi-debug" >>= strToExp)
juvixLibraryDir = absDir $(makeRelativeToProject "runtime/c/_build.wasm32-wasi-debug" >>= strToExp)