40 Commits
master ... jit

Author SHA1 Message Date
0fd846e844 feat: builtins env (WIP) 2025-06-06 09:48:03 +08:00
484cfa4610 feat: get rid of gc and cyclic thunk 2025-06-05 16:46:43 +08:00
51f8df9cca feat: less gc (WIP) 2025-06-02 14:19:06 +08:00
d3442e87e7 feat(gc): WIP 2025-06-02 12:00:38 +08:00
20b2b6f1ef feat: lookup at downgrade time
works, but leaks memory
2025-06-01 09:20:04 +08:00
7d6168fdae feat: ir env (WIP) 2025-05-30 18:29:09 +08:00
c548c4c6ac feat: ignore flamegraph and perf.data 2025-05-29 07:52:19 +08:00
541db02361 fix: repl 2025-05-28 23:01:59 +08:00
c8276c1729 chore: cargo clippy 2025-05-28 22:47:35 +08:00
f3bf44ab97 chore: cargo fmt 2025-05-28 21:59:45 +08:00
99dce2e778 feat: gc-arena
finally...
2025-05-28 21:52:13 +08:00
c3ace28af1 feat: gc (does compile, but WIP) 2025-05-27 21:08:59 +08:00
319c12c1f4 fix(vm): lifetime (still does not compile) 2025-05-25 17:28:33 +08:00
cc06369c5e feat: gc-arena (WIP, does not compile) 2025-05-25 17:18:54 +08:00
b41fd38bcc feat(env): move env out of vm, 2025-05-24 09:28:59 +08:00
5291e49313 fix(jit): should not be unreachable 2025-05-23 19:16:49 +08:00
a47a08b051 feat: bumpalo 2025-05-23 12:09:53 +08:00
53cbb37b00 optimize: make all call single arg
to allow more aggressive optimization
2025-05-23 09:21:40 +08:00
f380e5fd70 optimize(value): less clone 2025-05-22 21:24:19 +08:00
b0b73439fd optimize: enable lto 2025-05-22 19:49:14 +08:00
6bb86ca2cf chore: cargo clippy 2025-05-22 19:22:38 +08:00
c898b577b0 feat: less env clone 2025-05-22 19:21:14 +08:00
dcd22ad1f3 feat: add compile cli 2025-05-21 21:28:56 +08:00
2a19ddb279 feat: no clone in JIT
IMPORTANT: should not drop or create values in JIT anymore
2025-05-21 20:48:56 +08:00
177acfabcf feat: generalize env 2025-05-21 09:33:43 +08:00
36f29a9cac feat: eval cli 2025-05-20 21:45:13 +08:00
9b3c3d6fe9 chore: cargo clippy 2025-05-20 18:39:09 +08:00
736402dc53 chore: cargo fmt 2025-05-20 18:30:24 +08:00
b4249ccd11 feat(jit): support multiple arg function call
note: performance regression?
2025-05-20 18:22:06 +08:00
96fb6033a4 fix(builtins): should not appear in public value 2025-05-20 10:00:50 +08:00
d0298ce2a6 optimize(env): single arg 2025-05-20 09:47:30 +08:00
b4db46d48a chore: cargo fmt 2025-05-19 19:40:26 +08:00
9e172bf013 feat(jit): fib! 2025-05-19 19:29:25 +08:00
6d26716412 chore: cargo fmt 2025-05-19 11:33:18 +08:00
e17c48f2d9 fix: builtins impl 2025-05-19 08:37:40 +08:00
4124156d52 feat(jit): lookup 2025-05-18 21:52:36 +08:00
af5a312e1e feat(jit): fix segfault 2025-05-18 17:07:49 +08:00
f98d623c13 feat: JIT (unusable, segfault) 2025-05-18 15:02:02 +08:00
29e959894d feat: JIT (WIP) 2025-05-17 22:38:05 +08:00
95ebddf272 feat: JIT (WIP) 2025-05-17 20:54:36 +08:00
31 changed files with 2759 additions and 1347 deletions

4
.gitignore vendored
View File

@@ -1,3 +1,7 @@
target/ target/
/.direnv/ /.direnv/
.env
/flamegraph.svg
/perf.data*

39
Cargo.lock generated
View File

@@ -23,16 +23,6 @@ version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
[[package]]
name = "archery"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8967cd1cc9e9e1954f644e14fbd6042fe9a37da96c52a67e44a2ac18261f8561"
dependencies = [
"static_assertions",
"triomphe",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.1.0"
@@ -313,8 +303,8 @@ dependencies = [
"inkwell", "inkwell",
"itertools", "itertools",
"regex", "regex",
"replace_with",
"rnix", "rnix",
"rpds",
"rustyline", "rustyline",
"thiserror 2.0.12", "thiserror 2.0.12",
] ]
@@ -388,6 +378,12 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "replace_with"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51743d3e274e2b18df81c4dc6caf8a5b8e15dbe799e0dca05c7617380094e884"
[[package]] [[package]]
name = "rnix" name = "rnix"
version = "0.12.0" version = "0.12.0"
@@ -410,15 +406,6 @@ dependencies = [
"text-size", "text-size",
] ]
[[package]]
name = "rpds"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0e15515d3ce3313324d842629ea4905c25a13f81953eadb88f85516f59290a4"
dependencies = [
"archery",
]
[[package]] [[package]]
name = "rustc-hash" name = "rustc-hash"
version = "1.1.0" version = "1.1.0"
@@ -478,12 +465,6 @@ version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.101" version = "2.0.101"
@@ -541,12 +522,6 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "triomphe"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.18" version = "1.0.18"

View File

@@ -11,19 +11,23 @@ name = "repl"
required-features = ["repl"] required-features = ["repl"]
[profile.perf] [profile.perf]
debug = 1 debug = 2
strip = false
inherits = "release" inherits = "release"
[profile.release]
lto = true
strip = true
[dependencies] [dependencies]
rnix = "0.12" rnix = "0.12"
thiserror = "2.0" thiserror = "2.0"
itertools = "0.14" itertools = "0.14"
rpds = "1.1"
derive_more = { version = "2.0", features = ["full"] } derive_more = { version = "2.0", features = ["full"] }
ecow = "0.2" ecow = "0.2"
regex = "1.11" regex = "1.11"
hashbrown = "0.15" hashbrown = "0.15"
replace_with = "0.1"
inkwell = { version = "0.6.0", features = ["llvm18-1"] } inkwell = { version = "0.6.0", features = ["llvm18-1"] }
rustyline = { version = "15.0", optional = true } rustyline = { version = "15.0", optional = true }

30
src/bin/compile.rs Normal file
View File

@@ -0,0 +1,30 @@
use std::process::exit;
use itertools::Itertools;
use nixjit::compile::compile;
use nixjit::error::Error;
use nixjit::error::Result;
use nixjit::ir::downgrade;
fn main() -> Result<()> {
let mut args = std::env::args();
if args.len() != 2 {
eprintln!("Usage: {} expr", args.next().unwrap());
exit(1);
}
args.next();
let expr = args.next().unwrap();
let root = rnix::Root::parse(&expr);
if !root.errors().is_empty() {
return Err(Error::ParseError(
root.errors().iter().map(|err| err.to_string()).join(";"),
));
}
let expr = root.tree().expr().unwrap();
let downgraded = downgrade(expr)?;
let prog = compile(downgraded);
println!("{prog:?}");
Ok(())
}

31
src/bin/eval.rs Normal file
View File

@@ -0,0 +1,31 @@
use std::process::exit;
use itertools::Itertools;
use nixjit::compile::compile;
use nixjit::error::Error;
use nixjit::error::Result;
use nixjit::ir::downgrade;
use nixjit::vm::run;
fn main() -> Result<()> {
let mut args = std::env::args();
if args.len() != 2 {
eprintln!("Usage: {} expr", args.next().unwrap());
exit(1);
}
args.next();
let expr = args.next().unwrap();
let root = rnix::Root::parse(&expr);
if !root.errors().is_empty() {
return Err(Error::ParseError(
root.errors().iter().map(|err| err.to_string()).join(";"),
));
}
let expr = root.tree().expr().unwrap();
let downgraded = downgrade(expr)?;
let prog = compile(downgraded);
println!("{}", run(prog)?);
Ok(())
}

View File

@@ -1,4 +1,3 @@
use inkwell::context::Context;
use itertools::Itertools; use itertools::Itertools;
use rustyline::error::ReadlineError; use rustyline::error::ReadlineError;
use rustyline::{DefaultEditor, Result}; use rustyline::{DefaultEditor, Result};
@@ -6,7 +5,7 @@ use rustyline::{DefaultEditor, Result};
use nixjit::compile::compile; use nixjit::compile::compile;
use nixjit::error::Error; use nixjit::error::Error;
use nixjit::ir::downgrade; use nixjit::ir::downgrade;
use nixjit::vm::{JITContext, run}; use nixjit::vm::run;
macro_rules! unwrap { macro_rules! unwrap {
($e:expr) => { ($e:expr) => {
@@ -43,9 +42,7 @@ fn main() -> Result<()> {
let expr = root.tree().expr().unwrap(); let expr = root.tree().expr().unwrap();
let downgraded = unwrap!(downgrade(expr)); let downgraded = unwrap!(downgrade(expr));
let prog = compile(downgraded); let prog = compile(downgraded);
let ctx = Context::create(); println!("{}", unwrap!(run(prog)));
let jit = JITContext::new(&ctx);
println!("{}", unwrap!(run(prog, jit)));
} }
Err(ReadlineError::Interrupted) => { Err(ReadlineError::Interrupted) => {
println!("CTRL-C"); println!("CTRL-C");

View File

@@ -1,60 +1,12 @@
use hashbrown::HashMap; use hashbrown::HashMap;
use std::rc::Rc;
use crate::ty::internal::{AttrSet, Const, PrimOp, Value}; use crate::ty::common::Const;
use crate::vm::{Env, VM}; use crate::ir::{DowngradeContext, Ir};
pub fn env<'vm>(vm: &'vm VM) -> Env<'vm> {
let mut env = Env::empty();
env.insert(vm.new_sym("true"), Value::Const(Const::Bool(true)));
env.insert(vm.new_sym("false"), Value::Const(Const::Bool(false)));
let primops = [
PrimOp::new("add", 2, |_, args| {
let [first, second]: [Value; 2] = args.try_into().unwrap();
first.add(second).ok()
}),
PrimOp::new("sub", 2, |_, args| {
let [first, second]: [Value; 2] = args.try_into().unwrap();
first.add(second.neg()).ok()
}),
PrimOp::new("mul", 2, |_, args| {
let [first, second]: [Value; 2] = args.try_into().unwrap();
first.mul(second).ok()
}),
PrimOp::new("div", 2, |_, args| {
let [first, second]: [Value; 2] = args.try_into().unwrap();
first.div(second)
}),
PrimOp::new("lessThan", 2, |_, args| {
let [first, second]: [Value; 2] = args.try_into().unwrap();
first.lt(second).ok()
}),
PrimOp::new("seq", 2, |vm, args| {
let [mut first, second]: [Value; 2] = args.try_into().unwrap();
first.force(vm).unwrap();
second.ok()
}),
PrimOp::new("deepSeq", 2, |vm, args| {
let [mut first, second]: [Value; 2] = args.try_into().unwrap();
first.force_deep(vm).unwrap();
second.ok()
}),
];
pub fn ir_env(ctx: &mut DowngradeContext) -> HashMap<usize, Ir> {
let mut map = HashMap::new(); let mut map = HashMap::new();
for primop in primops { map.insert(ctx.new_sym("true"), ctx.new_const(Const::Bool(true)).ir());
let primop = Rc::new(primop); map.insert(ctx.new_sym("false"), ctx.new_const(Const::Bool(false)).ir());
env.insert(
vm.new_sym(format!("__{}", primop.name)),
Value::PrimOp(primop.clone()),
);
map.insert(vm.new_sym(primop.name), Value::PrimOp(primop));
}
let attrs: Rc<_> = AttrSet::from_inner(map).into();
let mut builtins = Value::AttrSet(attrs);
builtins.push_attr(vm.new_sym("builtins"), Value::Builtins);
env.insert(vm.new_sym("builtins"), builtins); map
env
} }

View File

@@ -1,8 +1,8 @@
use ecow::EcoString;
use hashbrown::HashMap; use hashbrown::HashMap;
use ecow::EcoString; use crate::ty::common::Const;
use crate::ty::internal::Param;
use crate::ty::internal::{Const, Param};
type Slice<T> = Box<[T]>; type Slice<T> = Box<[T]>;
@@ -14,40 +14,45 @@ pub enum OpCode {
Const { idx: usize }, Const { idx: usize },
/// load a dynamic var onto stack /// load a dynamic var onto stack
LookUp { sym: usize }, LookUp { sym: usize },
/// load a var from let binding onto stack
LookUpLet { level: usize, idx: usize },
/// load a thunk lazily onto stack /// load a thunk lazily onto stack
LoadThunk { idx: usize }, LoadThunk { idx: usize },
/// load a thunk value onto stack
LoadValue { idx: usize },
/// let TOS capture current environment /// let TOS capture current environment
CaptureEnv, CaptureEnv,
/// force TOS to value /// force TOS to value
ForceValue, ForceValue,
/// TODO:
InsertValue,
/// [ .. func args @ .. ] consume (`arity` + 1) elements, call `func` with args` of length `arity` /// [ .. func arg ] consume 2 elements, call `func` with arg
/// Example: __add 1 2 => [ LookUp("__add") Const(1) Const(2) Call(2) ] Call,
Call { arity: usize },
/// make a function /// make a function
Func { idx: usize }, Func { idx: usize },
/// load a function argument
Arg { level: usize },
/// consume 1 element, assert TOS is true /// consume 1 element, assert TOS is true
Assert, Assert,
/// jump forward /// jump forward
Jmp { step: usize }, Jmp { step: usize },
/// [ .. cond ] consume 1 element, if `cond`` is true, then jump forward
JmpIfTrue { step: usize },
/// [ .. cond ] consume 1 element, if `cond` is false, then jump forward /// [ .. cond ] consume 1 element, if `cond` is false, then jump forward
JmpIfFalse { step: usize }, JmpIfFalse { step: usize },
/// push an empty attribute set onto stack /// push an empty attribute set onto stack
AttrSet { cap: usize }, AttrSet { cap: usize },
/// finalize the recursive attribute set at TOS /// finalize the recursive attribute set at TOS
FinalizeRec, FinalizeLet,
/// [ .. set value ] consume 1 element, push a static kv pair (`name`, `value`) into `set` /// [ .. set value ] consume 1 element, push a static kv pair (`name`, `value`) into `set`
PushStaticAttr { name: usize }, PushStaticAttr { name: usize },
/// [ .. set name value ] consume 2 elements, push a dynamic kv pair (`name`, `value`) in to `set` /// [ .. set name value ] consume 2 elements, push a dynamic kv pair (`name`, `value`) in to `set`
PushDynamicAttr, PushDynamicAttr,
/// push an empty list onto stack /// push an empty list onto stack
List, List { cap: usize },
/// [ .. list elem ] consume 1 element, push `elem` into `list` /// [ .. list elem ] consume 1 element, push `elem` into `list`
PushElem, PushElem,
@@ -72,10 +77,12 @@ pub enum OpCode {
SelectDynamic, SelectDynamic,
/// [ .. set sym default ] select `sym` from `set` or `default` /// [ .. set sym default ] select `sym` from `set` or `default`
SelectDynamicOrDefault, SelectDynamicOrDefault,
/// enter the environment of the attribute set at TOS /// enter the with environment of the attribute set at TOS
EnterEnv, EnterWithEnv,
/// exit current envrironment /// exit current envrironment
LeaveEnv, LeaveEnv,
/// TODO:
PopEnv,
/// illegal operation, used as termporary placeholder /// illegal operation, used as termporary placeholder
Illegal, Illegal,

View File

@@ -35,7 +35,7 @@ impl Compiler {
} }
fn compile(mut self, ir: ir::Ir) -> OpCodes { fn compile(mut self, ir: ir::Ir) -> OpCodes {
ir.compile(&mut self); ir.compile_force(&mut self);
self.opcodes() self.opcodes()
} }
@@ -60,8 +60,12 @@ impl Compiler {
} }
} }
pub trait Compile { pub trait Compile: Sized {
fn compile(self, comp: &mut Compiler); fn compile(self, comp: &mut Compiler);
fn compile_force(self, comp: &mut Compiler) {
self.compile(comp);
comp.push(OpCode::ForceValue);
}
} }
pub trait CompileWithLength { pub trait CompileWithLength {
@@ -81,6 +85,9 @@ impl Compile for ir::Const {
fn compile(self, comp: &mut Compiler) { fn compile(self, comp: &mut Compiler) {
comp.push(OpCode::Const { idx: self.idx }); comp.push(OpCode::Const { idx: self.idx });
} }
fn compile_force(self, comp: &mut Compiler) {
self.compile(comp);
}
} }
impl Compile for ir::Var { impl Compile for ir::Var {
@@ -89,10 +96,28 @@ impl Compile for ir::Var {
} }
} }
impl Compile for ir::Arg {
fn compile(self, comp: &mut Compiler) {
comp.push(OpCode::Arg { level: self.level })
}
}
impl Compile for ir::LetVar {
fn compile(self, comp: &mut Compiler) {
comp.push(OpCode::LookUpLet {
level: self.level,
idx: self.idx,
});
}
}
impl Compile for ir::Thunk { impl Compile for ir::Thunk {
fn compile(self, comp: &mut Compiler) { fn compile(self, comp: &mut Compiler) {
comp.push(OpCode::LoadThunk { idx: self.idx }); comp.push(OpCode::LoadThunk { idx: self.idx });
} }
fn compile_force(self, comp: &mut Compiler) {
comp.push(OpCode::LoadValue { idx: self.idx });
}
} }
impl Compile for ir::Attrs { impl Compile for ir::Attrs {
@@ -101,29 +126,30 @@ impl Compile for ir::Attrs {
cap: self.stcs.len() + self.dyns.len(), cap: self.stcs.len() + self.dyns.len(),
}); });
for stc in self.stcs { for stc in self.stcs {
let thunk = stc.1.is_thunk();
stc.1.compile(comp); stc.1.compile(comp);
if !self.rec { if thunk {
comp.push(OpCode::CaptureEnv); comp.push(OpCode::CaptureEnv);
} }
comp.push(OpCode::PushStaticAttr { name: stc.0 }); comp.push(OpCode::PushStaticAttr { name: stc.0 });
} }
for dynamic in self.dyns { for dynamic in self.dyns {
dynamic.0.compile(comp); let thunk = dynamic.1.is_thunk();
dynamic.0.compile_force(comp);
dynamic.1.compile(comp); dynamic.1.compile(comp);
if !self.rec { if thunk {
comp.push(OpCode::CaptureEnv); comp.push(OpCode::CaptureEnv);
} }
comp.push(OpCode::PushDynamicAttr) comp.push(OpCode::PushDynamicAttr)
} }
if self.rec {
comp.push(OpCode::FinalizeRec);
}
} }
} }
impl Compile for ir::List { impl Compile for ir::List {
fn compile(self, comp: &mut Compiler) { fn compile(self, comp: &mut Compiler) {
comp.push(OpCode::List); comp.push(OpCode::List {
cap: self.items.len(),
});
for item in self.items { for item in self.items {
item.compile(comp); item.compile(comp);
comp.push(OpCode::PushElem); comp.push(OpCode::PushElem);
@@ -136,11 +162,11 @@ impl Compile for ir::UnOp {
use ir::UnOpKind::*; use ir::UnOpKind::*;
match self.kind { match self.kind {
Neg => { Neg => {
self.rhs.compile(comp); self.rhs.compile_force(comp);
comp.push(OpCode::UnOp { op: UnOp::Neg }); comp.push(OpCode::UnOp { op: UnOp::Neg });
} }
Not => { Not => {
self.rhs.compile(comp); self.rhs.compile_force(comp);
comp.push(OpCode::UnOp { op: UnOp::Not }); comp.push(OpCode::UnOp { op: UnOp::Not });
} }
} }
@@ -152,95 +178,95 @@ impl Compile for ir::BinOp {
use ir::BinOpKind::*; use ir::BinOpKind::*;
match self.kind { match self.kind {
Add => { Add => {
self.lhs.compile(comp); self.lhs.compile_force(comp);
self.rhs.compile(comp); self.rhs.compile_force(comp);
comp.push(OpCode::BinOp { op: BinOp::Add }); comp.push(OpCode::BinOp { op: BinOp::Add });
} }
Mul => { Mul => {
self.lhs.compile(comp); self.lhs.compile_force(comp);
self.rhs.compile(comp); self.rhs.compile_force(comp);
comp.push(OpCode::BinOp { op: BinOp::Mul }); comp.push(OpCode::BinOp { op: BinOp::Mul });
} }
Div => { Div => {
self.lhs.compile(comp); self.lhs.compile_force(comp);
self.rhs.compile(comp); self.rhs.compile_force(comp);
comp.push(OpCode::BinOp { op: BinOp::Div }); comp.push(OpCode::BinOp { op: BinOp::Div });
} }
And => { And => {
self.lhs.compile(comp); self.lhs.compile_force(comp);
self.rhs.compile(comp); self.rhs.compile_force(comp);
comp.push(OpCode::BinOp { op: BinOp::And }); comp.push(OpCode::BinOp { op: BinOp::And });
} }
Or => { Or => {
self.lhs.compile(comp); self.lhs.compile_force(comp);
self.rhs.compile(comp); self.rhs.compile_force(comp);
comp.push(OpCode::BinOp { op: BinOp::Or }); comp.push(OpCode::BinOp { op: BinOp::Or });
} }
Eq => { Eq => {
self.lhs.compile(comp); self.lhs.compile_force(comp);
self.rhs.compile(comp); self.rhs.compile_force(comp);
comp.push(OpCode::BinOp { op: BinOp::Eq }); comp.push(OpCode::BinOp { op: BinOp::Eq });
} }
Lt => { Lt => {
self.lhs.compile(comp); self.lhs.compile_force(comp);
self.rhs.compile(comp); self.rhs.compile_force(comp);
comp.push(OpCode::BinOp { op: BinOp::Lt }); comp.push(OpCode::BinOp { op: BinOp::Lt });
} }
Con => { Con => {
self.lhs.compile(comp); self.lhs.compile_force(comp);
self.rhs.compile(comp); self.rhs.compile_force(comp);
comp.push(OpCode::BinOp { op: BinOp::Con }); comp.push(OpCode::BinOp { op: BinOp::Con });
} }
Upd => { Upd => {
self.lhs.compile(comp); self.lhs.compile_force(comp);
self.rhs.compile(comp); self.rhs.compile_force(comp);
comp.push(OpCode::BinOp { op: BinOp::Upd }); comp.push(OpCode::BinOp { op: BinOp::Upd });
} }
Sub => { Sub => {
self.lhs.compile(comp); self.lhs.compile_force(comp);
self.rhs.compile(comp); self.rhs.compile_force(comp);
comp.push(OpCode::BinOp { op: BinOp::Sub }); comp.push(OpCode::BinOp { op: BinOp::Sub });
} }
Impl => { Impl => {
self.lhs.compile(comp); self.lhs.compile_force(comp);
comp.push(OpCode::UnOp { op: UnOp::Not }); comp.push(OpCode::UnOp { op: UnOp::Not });
self.rhs.compile(comp); self.rhs.compile_force(comp);
comp.push(OpCode::BinOp { op: BinOp::Or }); comp.push(OpCode::BinOp { op: BinOp::Or });
} }
Neq => { Neq => {
self.lhs.compile(comp); self.lhs.compile_force(comp);
self.rhs.compile(comp); self.rhs.compile_force(comp);
comp.push(OpCode::BinOp { op: BinOp::Eq }); comp.push(OpCode::BinOp { op: BinOp::Eq });
comp.push(OpCode::UnOp { op: UnOp::Not }); comp.push(OpCode::UnOp { op: UnOp::Not });
} }
Gt => { Gt => {
self.rhs.compile(comp); self.rhs.compile_force(comp);
self.lhs.compile(comp); self.lhs.compile_force(comp);
comp.push(OpCode::BinOp { op: BinOp::Lt }); comp.push(OpCode::BinOp { op: BinOp::Lt });
} }
Leq => { Leq => {
self.rhs.compile(comp); self.rhs.compile_force(comp);
self.lhs.compile(comp); self.lhs.compile_force(comp);
comp.push(OpCode::BinOp { op: BinOp::Lt }); comp.push(OpCode::BinOp { op: BinOp::Lt });
comp.push(OpCode::UnOp { op: UnOp::Not }); comp.push(OpCode::UnOp { op: UnOp::Not });
} }
Geq => { Geq => {
self.lhs.compile(comp); self.lhs.compile_force(comp);
self.rhs.compile(comp); self.rhs.compile_force(comp);
comp.push(OpCode::BinOp { op: BinOp::Lt }); comp.push(OpCode::BinOp { op: BinOp::Lt });
comp.push(OpCode::UnOp { op: UnOp::Not }); comp.push(OpCode::UnOp { op: UnOp::Not });
} }
PipeL => { PipeL => {
self.lhs.compile(comp); self.lhs.compile_force(comp);
self.rhs.compile(comp); self.rhs.compile_force(comp);
comp.push(OpCode::Call { arity: 1 }); comp.push(OpCode::Call);
} }
PipeR => { PipeR => {
self.rhs.compile(comp); self.rhs.compile_force(comp);
self.lhs.compile(comp); self.lhs.compile_force(comp);
comp.push(OpCode::Call { arity: 1 }); comp.push(OpCode::Call);
} }
} }
} }
@@ -250,13 +276,14 @@ impl Compile for ir::HasAttr {
fn compile(self, comp: &mut Compiler) { fn compile(self, comp: &mut Compiler) {
self.lhs.compile(comp); self.lhs.compile(comp);
for attr in self.rhs { for attr in self.rhs {
comp.push(OpCode::ForceValue);
match attr { match attr {
ir::Attr::Str(sym) => { ir::Attr::Str(sym) => {
comp.push(OpCode::AttrSet { cap: 0 }); comp.push(OpCode::AttrSet { cap: 0 });
comp.push(OpCode::SelectOrDefault { sym }) comp.push(OpCode::SelectOrDefault { sym })
} }
ir::Attr::Dynamic(dynamic) => { ir::Attr::Dynamic(dynamic) => {
dynamic.compile(comp); dynamic.compile_force(comp);
comp.push(OpCode::AttrSet { cap: 0 }); comp.push(OpCode::AttrSet { cap: 0 });
comp.push(OpCode::SelectDynamicOrDefault); comp.push(OpCode::SelectDynamicOrDefault);
} }
@@ -281,6 +308,7 @@ impl Compile for ir::Select {
fn compile(self, comp: &mut Compiler) { fn compile(self, comp: &mut Compiler) {
self.expr.compile(comp); self.expr.compile(comp);
for attr in self.attrpath { for attr in self.attrpath {
comp.push(OpCode::ForceValue);
match attr { match attr {
ir::Attr::Str(sym) => { ir::Attr::Str(sym) => {
comp.push(OpCode::AttrSet { cap: 0 }); comp.push(OpCode::AttrSet { cap: 0 });
@@ -303,11 +331,7 @@ impl Compile for ir::Select {
let last = comp.pop().unwrap(); let last = comp.pop().unwrap();
let _ = comp.pop(); let _ = comp.pop();
default.compile(comp); default.compile(comp);
match last { comp.push(last);
OpCode::SelectOrDefault { sym } => comp.push(OpCode::SelectOrDefault { sym }),
OpCode::SelectDynamicOrDefault => comp.push(OpCode::SelectDynamicOrDefault),
_ => unreachable!(),
}
} }
None => { None => {
let last = comp.pop().unwrap(); let last = comp.pop().unwrap();
@@ -320,14 +344,18 @@ impl Compile for ir::Select {
} }
} }
} }
fn compile_force(self, comp: &mut Compiler) {
self.compile(comp);
comp.push(OpCode::ForceValue);
}
} }
impl Compile for ir::ConcatStrings { impl Compile for ir::ConcatStrings {
fn compile(self, comp: &mut Compiler) { fn compile(self, comp: &mut Compiler) {
let mut iter = self.parts.into_iter(); let mut iter = self.parts.into_iter();
iter.next().unwrap().compile(comp); iter.next().unwrap().compile_force(comp);
for item in iter { for item in iter {
item.compile(comp); item.compile_force(comp);
comp.push(OpCode::ConcatString); comp.push(OpCode::ConcatString);
} }
} }
@@ -361,9 +389,19 @@ impl Compile for ir::If {
impl Compile for ir::Let { impl Compile for ir::Let {
fn compile(self, comp: &mut Compiler) { fn compile(self, comp: &mut Compiler) {
self.attrs.compile(comp); comp.push(OpCode::List {
comp.push(OpCode::EnterEnv); cap: self.bindings.len(),
});
for (_, val) in self.bindings {
val.compile(comp);
comp.push(OpCode::PushElem);
}
comp.push(OpCode::FinalizeLet);
let thunk = self.expr.is_thunk();
self.expr.compile(comp); self.expr.compile(comp);
if thunk {
comp.push(OpCode::CaptureEnv);
}
comp.push(OpCode::LeaveEnv); comp.push(OpCode::LeaveEnv);
} }
} }
@@ -371,7 +409,7 @@ impl Compile for ir::Let {
impl Compile for ir::With { impl Compile for ir::With {
fn compile(self, comp: &mut Compiler) { fn compile(self, comp: &mut Compiler) {
self.namespace.compile(comp); self.namespace.compile(comp);
comp.push(OpCode::EnterEnv); comp.push(OpCode::EnterWithEnv);
self.expr.compile(comp); self.expr.compile(comp);
comp.push(OpCode::LeaveEnv); comp.push(OpCode::LeaveEnv);
} }
@@ -393,12 +431,11 @@ impl Compile for ir::LoadFunc {
impl Compile for ir::Call { impl Compile for ir::Call {
fn compile(self, comp: &mut Compiler) { fn compile(self, comp: &mut Compiler) {
let arity = self.args.len(); self.func.compile_force(comp);
self.func.compile(comp);
self.args.into_iter().for_each(|arg| { self.args.into_iter().for_each(|arg| {
arg.compile(comp); arg.compile(comp);
comp.push(OpCode::Call);
}); });
comp.push(OpCode::Call { arity });
} }
} }

142
src/env.rs Normal file
View File

@@ -0,0 +1,142 @@
use std::hash::Hash;
use std::rc::Rc;
use hashbrown::HashMap;
use crate::{ir::Ir, ty::internal::Value};
pub struct Env<K: Hash + Eq, V> {
let_: Rc<LetEnv<V>>,
with: Rc<With<K, V>>,
args: Rc<Vec<V>>,
last: Option<Rc<Env<K, V>>>,
}
pub struct LetEnv<V> {
map: Vec<V>,
last: Option<Rc<LetEnv<V>>>,
}
pub type VmEnv<'gc> = Env<usize, Value<'gc>>;
pub type IrEnv<'gc> = Env<usize, Ir>;
#[derive(Default, Clone)]
pub struct With<K: Hash + Eq, V> {
map: Option<Rc<HashMap<K, V>>>,
last: Option<Rc<With<K, V>>>,
}
enum LetNode<K: Hash + Eq, V> {
Let(Rc<HashMap<K, V>>),
MultiArg(Rc<HashMap<K, V>>),
}
#[derive(Clone, Copy)]
pub enum Type {
Arg,
Let,
With,
}
impl<K: Hash + Eq + Clone, V: Clone> Env<K, V> {
pub fn new(map: Vec<V>) -> Rc<Self> {
Rc::new(Self {
let_: LetEnv::new(map),
with: With {
map: None,
last: None,
}.into(),
args: Vec::new().into(),
last: None,
})
}
pub fn lookup_arg(&self, level: usize) -> &V {
&self.args[self.args.len() - level - 1]
}
pub fn lookup_let(&self, level: usize, idx: usize) -> &V {
self.let_.lookup(level, idx)
}
pub fn lookup_with(&self, symbol: &K) -> Option<&V> {
self.with.lookup(symbol)
}
pub fn has_with(&self) -> bool {
self.with.map.is_some()
}
#[must_use]
pub fn enter_arg(self: Rc<Self>, val: V) -> Rc<Self> {
let mut args = self.args.clone();
Rc::make_mut(&mut args).push(val);
Rc::new(Self {
let_: self.let_.clone(),
with: self.with.clone(),
last: Some(self),
args,
})
}
#[must_use]
pub fn enter_let(self: Rc<Self>, map: Vec<V>) -> Rc<Self> {
Rc::new(Self {
let_: self.let_.clone().enter_let(map),
with: self.with.clone(),
args: self.args.clone(),
last: Some(self),
})
}
#[must_use]
pub fn enter_with(self: Rc<Self>, map: Rc<HashMap<K, V>>) -> Rc<Self> {
Rc::new(Self {
let_: self.let_.clone(),
with: self.with.clone().enter(map),
args: self.args.clone(),
last: Some(self),
})
}
pub fn leave(&self) -> Rc<Self> {
self.last.clone().unwrap()
}
}
impl<V: Clone> LetEnv<V> {
pub fn new(map: Vec<V>) -> Rc<Self> {
Rc::new(Self { map, last: None })
}
pub fn lookup(&self, level: usize, idx: usize) -> &V {
let mut cur = self;
for _ in 0..level {
cur = cur.last.as_ref().unwrap();
}
&cur.map[idx]
}
pub fn enter_let(self: Rc<Self>, map: Vec<V>) -> Rc<Self> {
Rc::new(Self {
map,
last: Some(self),
})
}
}
impl<K: Hash + Eq + Clone, V: Clone> With<K, V> {
pub fn lookup(&self, symbol: &K) -> Option<&V> {
if let Some(val) = self.map.as_ref()?.get(symbol) {
return Some(val);
}
self.last.as_ref().and_then(|env| env.lookup(symbol))
}
pub fn enter(self: Rc<Self>, map: Rc<HashMap<K, V>>) -> Rc<Self> {
Rc::new(Self {
map: Some(map),
last: Some(self),
})
}
}

View File

@@ -10,6 +10,8 @@ pub enum Error {
DowngradeError(String), DowngradeError(String),
#[error("error occurred during evaluation stage: {0}")] #[error("error occurred during evaluation stage: {0}")]
EvalError(String), EvalError(String),
#[error("error occurred during JIT compile stage: {0}")]
CompileError(#[from] inkwell::builder::BuilderError),
#[error("unknown error")] #[error("unknown error")]
Unknown, Unknown,
} }

676
src/ir.rs
View File

@@ -1,15 +1,22 @@
use derive_more::{IsVariant, TryUnwrap, Unwrap};
use hashbrown::HashMap; use hashbrown::HashMap;
use ecow::EcoString; use ecow::EcoString;
use itertools::Itertools;
use rnix::ast::HasEntry;
use rnix::ast::{self, Expr}; use rnix::ast::{self, Expr};
use crate::builtins::ir_env;
use crate::compile::*; use crate::compile::*;
use crate::error::*; use crate::error::*;
use crate::ty::internal as i; use crate::ty::common as c;
pub fn downgrade(expr: Expr) -> Result<Downgraded> { pub fn downgrade(expr: Expr) -> Result<Downgraded> {
let mut ctx = DowngradeContext::new(); let mut ctx = DowngradeContext::new();
let builtins = ir_env(&mut ctx);
let env = Env::new(&builtins);
let ir = expr.downgrade(&mut ctx)?; let ir = expr.downgrade(&mut ctx)?;
let ir = ir.resolve(&mut ctx, &env)?;
Ok(Downgraded { Ok(Downgraded {
top_level: ir, top_level: ir,
consts: ctx.consts.into(), consts: ctx.consts.into(),
@@ -20,14 +27,6 @@ pub fn downgrade(expr: Expr) -> Result<Downgraded> {
}) })
} }
trait Downcast<T: Sized>
where
Self: Sized,
{
fn downcast_ref(&self) -> Option<&T>;
fn downcast_mut(&mut self) -> Option<&mut T>;
}
macro_rules! ir { macro_rules! ir {
( (
$( $(
@@ -38,22 +37,59 @@ macro_rules! ir {
) )
,*$(,)? ,*$(,)?
) => { ) => {
#[derive(Clone, Debug)] #[derive(Clone, Debug, IsVariant, Unwrap)]
pub enum Ir { pub enum Ir {
$( $(
$ty($ty), $ty($ty),
)* )*
} }
#[derive(Debug, IsVariant, TryUnwrap)]
pub enum IrRef<'a> {
$(
$ty(&'a $ty),
)*
}
#[derive(Debug, IsVariant, TryUnwrap)]
pub enum IrMut<'a> {
$(
$ty(&'a mut $ty),
)*
}
impl Ir { impl Ir {
#[inline]
fn boxed(self) -> Box<Self> { fn boxed(self) -> Box<Self> {
Box::new(self) Box::new(self)
} }
#[inline]
fn ok(self) -> Result<Self> { fn ok(self) -> Result<Self> {
Ok(self) Ok(self)
} }
#[inline]
fn as_ref(&self) -> IrRef {
match self {
$(Ir::$ty(ir) => IrRef::$ty(ir),)*
}
} }
#[inline]
fn as_mut(&mut self) -> IrMut {
match self {
$(Ir::$ty(ir) => IrMut::$ty(ir),)*
}
}
#[inline]
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
match self {
$(Ir::$ty(ir) => ir.resolve(ctx, env),)*
}
}
}
impl Compile for Ir { impl Compile for Ir {
fn compile(self, ctx: &mut Compiler) { fn compile(self, ctx: &mut Compiler) {
match self { match self {
@@ -78,27 +114,12 @@ macro_rules! ir {
Ir::$ty(self) Ir::$ty(self)
} }
} }
impl Downcast<$ty> for Ir {
fn downcast_ref(&self) -> Option<&$ty> {
match self {
Ir::$ty(value) => Some(value),
_ => None,
}
}
fn downcast_mut(&mut self) -> Option<&mut $ty> {
match self {
Ir::$ty(value) => Some(value),
_ => None,
}
}
}
)* )*
} }
} }
ir! { ir! {
Attrs => { stcs: HashMap<usize, Ir>, dyns: Vec<DynamicAttrPair>, rec: bool }, Attrs => { stcs: HashMap<usize, Ir>, dyns: Vec<DynamicAttrPair> },
List => { items: Vec<Ir> }, List => { items: Vec<Ir> },
HasAttr => { lhs: Box<Ir>, rhs: Vec<Attr> }, HasAttr => { lhs: Box<Ir>, rhs: Vec<Attr> },
BinOp => { lhs: Box<Ir>, rhs: Box<Ir>, kind: BinOpKind }, BinOp => { lhs: Box<Ir>, rhs: Box<Ir>, kind: BinOpKind },
@@ -108,13 +129,19 @@ ir! {
LoadFunc => { idx: usize }, LoadFunc => { idx: usize },
Call => { func: Box<Ir>, args: Vec<Ir> }, Call => { func: Box<Ir>, args: Vec<Ir> },
Let => { attrs: Attrs, expr: Box<Ir> }, Let => { bindings: Vec<(usize, Ir)>, expr: Box<Ir> },
With => { namespace: Box<Ir>, expr: Box<Ir> }, With => { namespace: Box<Ir>, expr: Box<Ir> },
Assert => { assertion: Box<Ir>, expr: Box<Ir> }, Assert => { assertion: Box<Ir>, expr: Box<Ir> },
ConcatStrings => { parts: Vec<Ir> }, ConcatStrings => { parts: Vec<Ir> },
#[derive(Copy)]
Const => { idx: usize }, Const => { idx: usize },
#[derive(Copy)]
Var => { sym: usize }, Var => { sym: usize },
#[derive(Copy)] #[derive(Copy)]
Arg => { level: usize },
#[derive(Copy)]
LetVar => { level: usize, idx: usize },
#[derive(Copy)]
Thunk => { idx: usize }, Thunk => { idx: usize },
Path => { expr: Box<Ir> }, Path => { expr: Box<Ir> },
} }
@@ -122,19 +149,148 @@ ir! {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct DynamicAttrPair(pub Ir, pub Ir); pub struct DynamicAttrPair(pub Ir, pub Ir);
#[derive(Default)]
pub struct DowngradeContext { pub struct DowngradeContext {
thunks: Vec<Ir>, thunks: Vec<Ir>,
funcs: Vec<Func>, funcs: Vec<Func>,
consts: Vec<i::Const>, consts: Vec<c::Const>,
constmap: HashMap<i::Const, usize>, constmap: HashMap<c::Const, usize>,
symbols: Vec<EcoString>, symbols: Vec<EcoString>,
symmap: HashMap<EcoString, usize>, symmap: HashMap<EcoString, usize>,
} }
struct Env<'a, 'env> {
env: EnvNode<'a>,
prev: Option<&'env Env<'a, 'env>>,
}
enum EnvNode<'a> {
Builtins(&'a HashMap<usize, Ir>),
Let(&'a Vec<usize>),
SingleArg(usize),
MultiArg(HashMap<usize, Option<Ir>>, Option<usize>),
With,
}
enum LookupResult {
Builtin(Ir),
Let { level: usize, idx: usize },
SingleArg { level: usize },
MultiArg { level: usize, default: Option<Ir> },
With,
}
impl<'a, 'env> Env<'a, 'env> {
fn new(base: &'a HashMap<usize, Ir>) -> Self {
Self {
env: EnvNode::Builtins(base),
prev: None,
}
}
fn enter_let(&'env self, map: &'a Vec<usize>) -> Self {
Self {
env: EnvNode::Let(map),
prev: Some(self),
}
}
fn enter_single_arg(&'env self, ident: usize) -> Self {
Self {
env: EnvNode::SingleArg(ident),
prev: Some(self),
}
}
fn enter_multi_arg(&'env self, map: HashMap<usize, Option<Ir>>, alias: Option<usize>) -> Self {
Self {
env: EnvNode::MultiArg(map, alias),
prev: Some(self),
}
}
fn enter_with(&'env self) -> Self {
Self {
env: EnvNode::With,
prev: Some(self),
}
}
fn _lookup(
&self,
ident: usize,
mut arg_level: usize,
mut let_level: usize,
has_with: bool,
) -> core::result::Result<LookupResult, ()> {
use EnvNode::*;
let mut has_with = has_with;
match &self.env {
Builtins(map) => {
return if let Some(ir) = map.get(&ident) {
Ok(LookupResult::Builtin(ir.clone()))
} else if has_with {
Ok(LookupResult::With)
} else {
Err(())
};
}
Let(map) => {
if let Ok(idx) = map.binary_search(&ident) {
return Ok(LookupResult::Let {
level: let_level,
idx,
});
} else {
let_level += 1;
}
}
SingleArg(arg) => {
if *arg == ident {
return Ok(LookupResult::SingleArg { level: arg_level });
} else {
arg_level += 1;
}
}
MultiArg(set, alias) => {
if let Some(default) = set.get(&ident) {
return Ok(LookupResult::MultiArg {
level: arg_level,
default: default.clone(),
});
} else if *alias == Some(ident) {
return Ok(LookupResult::SingleArg { level: arg_level });
} else {
arg_level += 1;
}
}
With => has_with = true,
}
self.prev
.map(|prev| prev._lookup(ident, arg_level, let_level, has_with))
.map_or_else(|| unreachable!(), |x| x)
}
fn lookup(&self, ident: usize) -> core::result::Result<LookupResult, ()> {
self._lookup(ident, 0, 0, false)
}
}
impl DowngradeContext {
fn new() -> Self {
DowngradeContext {
thunks: Vec::new(),
funcs: Vec::new(),
consts: Vec::new(),
constmap: HashMap::new(),
symbols: Vec::new(),
symmap: HashMap::new(),
}
}
}
pub struct Downgraded { pub struct Downgraded {
pub top_level: Ir, pub top_level: Ir,
pub consts: Box<[i::Const]>, pub consts: Box<[c::Const]>,
pub symbols: Vec<EcoString>, pub symbols: Vec<EcoString>,
pub symmap: HashMap<EcoString, usize>, pub symmap: HashMap<EcoString, usize>,
pub thunks: Box<[Ir]>, pub thunks: Box<[Ir]>,
@@ -142,10 +298,6 @@ pub struct Downgraded {
} }
impl DowngradeContext { impl DowngradeContext {
fn new() -> DowngradeContext {
DowngradeContext::default()
}
fn new_thunk(&mut self, thunk: Ir) -> Thunk { fn new_thunk(&mut self, thunk: Ir) -> Thunk {
let idx = self.thunks.len(); let idx = self.thunks.len();
self.thunks.push(thunk); self.thunks.push(thunk);
@@ -158,7 +310,7 @@ impl DowngradeContext {
LoadFunc { idx } LoadFunc { idx }
} }
fn new_const(&mut self, cnst: i::Const) -> Const { pub fn new_const(&mut self, cnst: c::Const) -> Const {
if let Some(&idx) = self.constmap.get(&cnst) { if let Some(&idx) = self.constmap.get(&cnst) {
Const { idx } Const { idx }
} else { } else {
@@ -170,7 +322,7 @@ impl DowngradeContext {
} }
} }
fn new_sym(&mut self, sym: impl Into<EcoString>) -> usize { pub fn new_sym(&mut self, sym: impl Into<EcoString>) -> usize {
let sym = sym.into(); let sym = sym.into();
if let Some(&idx) = self.symmap.get(&sym) { if let Some(&idx) = self.symmap.get(&sym) {
idx idx
@@ -180,10 +332,48 @@ impl DowngradeContext {
self.symbols.len() - 1 self.symbols.len() - 1
} }
} }
fn get_sym(&self, idx: usize) -> &EcoString {
&self.symbols[idx]
}
fn resolve_func(&mut self, idx: usize, env: &Env) -> Result<()> {
let self_ptr = self as *mut Self;
self.funcs.get_mut(idx).map_or_else(
|| unreachable!(),
|func| {
unsafe {
let old = std::ptr::read(func);
std::ptr::write(func, old.resolve(self_ptr.as_mut().unwrap(), env)?);
}
Ok(())
},
)
}
fn resolve_thunk(&mut self, idx: usize, env: &Env) -> Result<()> {
let self_ptr = self as *mut Self;
self.thunks.get_mut(idx).map_or_else(
|| unreachable!(),
|thunk| {
unsafe {
let old = std::ptr::read(thunk);
std::ptr::write(thunk, old.resolve(self_ptr.as_mut().unwrap(), env)?);
}
Ok(())
},
)
}
} }
impl Attrs { impl Attrs {
fn _insert(&mut self, mut path: std::vec::IntoIter<Attr>, name: Attr, value: Ir) -> Result<()> { fn _insert(
&mut self,
mut path: std::vec::IntoIter<Attr>,
name: Attr,
value: Ir,
ctx: &mut DowngradeContext,
) -> Result<()> {
if let Some(attr) = path.next() { if let Some(attr) = path.next() {
match attr { match attr {
Attr::Str(ident) => { Attr::Str(ident) => {
@@ -191,41 +381,37 @@ impl Attrs {
self.stcs self.stcs
.get_mut(&ident) .get_mut(&ident)
.unwrap() .unwrap()
.downcast_mut() .as_mut()
.ok_or_else(|| { .try_unwrap_attrs().map_err(|_| Error::DowngradeError(format!(
Error::DowngradeError(format!( r#"attribute '{}' already defined"#,
r#""{ident}" already exsists in this set"# ctx.get_sym(ident)
)) )))
}) .and_then(|attrs: &mut Attrs| attrs._insert(path, name, value, ctx))
.and_then(|attrs: &mut Attrs| attrs._insert(path, name, value))
} else { } else {
let mut attrs = Attrs { let mut attrs = Attrs {
rec: false,
stcs: HashMap::new(), stcs: HashMap::new(),
dyns: Vec::new(), dyns: Vec::new(),
}; };
attrs._insert(path, name, value)?; attrs._insert(path, name, value, ctx)?;
assert!(self.stcs.insert(ident, attrs.ir()).is_none()); assert!(self.stcs.insert(ident, attrs.ir()).is_none());
Ok(()) Ok(())
} }
} }
Attr::Strs(string) => { Attr::Strs(string) => {
let mut attrs = Attrs { let mut attrs = Attrs {
rec: false,
stcs: HashMap::new(), stcs: HashMap::new(),
dyns: Vec::new(), dyns: Vec::new(),
}; };
attrs._insert(path, name, value)?; attrs._insert(path, name, value, ctx)?;
self.dyns.push(DynamicAttrPair(string.ir(), attrs.ir())); self.dyns.push(DynamicAttrPair(string.ir(), attrs.ir()));
Ok(()) Ok(())
} }
Attr::Dynamic(dynamic) => { Attr::Dynamic(dynamic) => {
let mut attrs = Attrs { let mut attrs = Attrs {
rec: false,
stcs: HashMap::new(), stcs: HashMap::new(),
dyns: Vec::new(), dyns: Vec::new(),
}; };
attrs._insert(path, name, value)?; attrs._insert(path, name, value, ctx)?;
self.dyns.push(DynamicAttrPair(dynamic, attrs.ir())); self.dyns.push(DynamicAttrPair(dynamic, attrs.ir()));
Ok(()) Ok(())
} }
@@ -235,7 +421,8 @@ impl Attrs {
Attr::Str(ident) => { Attr::Str(ident) => {
if self.stcs.get(&ident).is_some() { if self.stcs.get(&ident).is_some() {
return Err(Error::DowngradeError(format!( return Err(Error::DowngradeError(format!(
r#""{ident}" already exsists in this set"# r#"attribute '{}' already defined"#,
ctx.get_sym(ident)
))); )));
} }
self.stcs.insert(ident, value); self.stcs.insert(ident, value);
@@ -251,10 +438,10 @@ impl Attrs {
} }
} }
pub fn insert(&mut self, path: Vec<Attr>, value: Ir) -> Result<()> { pub fn insert(&mut self, path: Vec<Attr>, value: Ir, ctx: &mut DowngradeContext) -> Result<()> {
let mut path = path.into_iter(); let mut path = path.into_iter();
let name = path.next_back().unwrap(); let name = path.next_back().unwrap();
self._insert(path, name, value) self._insert(path, name, value, ctx)
} }
fn _has_attr(&self, mut path: std::slice::Iter<Attr>, name: Attr) -> Option<bool> { fn _has_attr(&self, mut path: std::slice::Iter<Attr>, name: Attr) -> Option<bool> {
@@ -262,7 +449,7 @@ impl Attrs {
Some(Attr::Str(ident)) => self Some(Attr::Str(ident)) => self
.stcs .stcs
.get(ident) .get(ident)
.and_then(|attrs| attrs.downcast_ref()) .and_then(|attrs| attrs.as_ref().try_unwrap_attrs().ok())
.map_or(Some(false), |attrs: &Attrs| attrs._has_attr(path, name)), .map_or(Some(false), |attrs: &Attrs| attrs._has_attr(path, name)),
None => match name { None => match name {
Attr::Str(ident) => Some(self.stcs.get(&ident).is_some()), Attr::Str(ident) => Some(self.stcs.get(&ident).is_some()),
@@ -279,13 +466,30 @@ impl Attrs {
} }
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, TryUnwrap)]
pub enum Attr { pub enum Attr {
Dynamic(Ir), Dynamic(Ir),
Strs(ConcatStrings), Strs(ConcatStrings),
Str(usize), Str(usize),
} }
impl Attr {
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Attr> {
use Attr::*;
Ok(match self {
Dynamic(ir) => Dynamic(ir.resolve(ctx, env)?),
other => other,
})
}
}
impl Thunk {
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
ctx.resolve_thunk(self.idx, env)?;
self.ir().ok()
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum BinOpKind { pub enum BinOpKind {
Add, Add,
@@ -359,12 +563,20 @@ pub struct Func {
pub enum Param { pub enum Param {
Ident(usize), Ident(usize),
Formals { Formals {
// formals: Vec<(usize, Option<Ir>)>,
formals: Vec<(usize, Option<Thunk>)>, formals: Vec<(usize, Option<Thunk>)>,
ellipsis: bool, ellipsis: bool,
alias: Option<usize>, alias: Option<usize>,
}, },
} }
impl LoadFunc {
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
ctx.resolve_func(self.idx, env)?;
self.ir().ok()
}
}
trait Downgrade trait Downgrade
where where
Self: Sized, Self: Sized,
@@ -410,6 +622,17 @@ impl Downgrade for ast::Assert {
} }
} }
impl Assert {
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
Self {
assertion: self.assertion.resolve(ctx, env)?.boxed(),
expr: self.expr.resolve(ctx, env)?.boxed(),
}
.ir()
.ok()
}
}
impl Downgrade for ast::IfElse { impl Downgrade for ast::IfElse {
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> { fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
If { If {
@@ -422,6 +645,18 @@ impl Downgrade for ast::IfElse {
} }
} }
impl If {
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
If {
cond: self.cond.resolve(ctx, env)?.boxed(),
consq: self.consq.resolve(ctx, env)?.boxed(),
alter: self.alter.resolve(ctx, env)?.boxed(),
}
.ir()
.ok()
}
}
impl Downgrade for ast::Path { impl Downgrade for ast::Path {
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> { fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
let parts = self let parts = self
@@ -447,6 +682,16 @@ impl Downgrade for ast::Path {
} }
} }
impl Path {
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
Self {
expr: self.expr.resolve(ctx, env)?.boxed(),
}
.ir()
.ok()
}
}
impl Downgrade for ast::Str { impl Downgrade for ast::Str {
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> { fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
let parts = self let parts = self
@@ -467,6 +712,20 @@ impl Downgrade for ast::Str {
} }
} }
impl ConcatStrings {
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
Self {
parts: self
.parts
.into_iter()
.map(|ir| ir.resolve(ctx, env))
.collect::<Result<Vec<_>>>()?,
}
.ir()
.ok()
}
}
impl Downgrade for ast::Literal { impl Downgrade for ast::Literal {
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> { fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
match self.kind() { match self.kind() {
@@ -479,20 +738,97 @@ impl Downgrade for ast::Literal {
} }
} }
impl Const {
fn resolve<'a, 'env>(self, _: &mut DowngradeContext, _: &Env<'a, 'env>) -> Result<Ir> {
self.ir().ok()
}
}
impl Downgrade for ast::Ident { impl Downgrade for ast::Ident {
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> { fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
Var { let sym = ctx.new_sym(self.ident_token().unwrap().text());
sym: ctx.new_sym(self.to_string()), Var { sym }.ir().ok()
}
}
impl Var {
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
use LookupResult::*;
match env
.lookup(self.sym)
.map_err(|_| Error::DowngradeError(format!("{} not found", ctx.get_sym(self.sym))))?
{
Builtin(ir) => ir,
Let { level, idx } => LetVar { level, idx }.ir(),
SingleArg { level } => Arg { level }.ir(),
MultiArg { level, default } => Select {
expr: Arg { level }.ir().boxed(),
attrpath: vec![Attr::Str(self.sym)],
default: default.map(Box::new),
}
.ir(),
With => self.ir(),
} }
.ir()
.ok() .ok()
} }
} }
impl Arg {
fn resolve<'a, 'env>(self, _: &mut DowngradeContext, _: &Env<'a, 'env>) -> Result<Ir> {
unreachable!()
}
}
impl LetVar {
fn resolve<'a, 'env>(self, _: &mut DowngradeContext, _: &Env<'a, 'env>) -> Result<Ir> {
unreachable!()
}
}
impl Downgrade for ast::AttrSet { impl Downgrade for ast::AttrSet {
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> { fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
let rec = self.rec_token().is_some(); let rec = self.rec_token().is_some();
downgrade_has_entry(self, rec, ctx).map(|attrs| attrs.ir()) let attrs = downgrade_attrs(self, ctx)?;
if rec {
let bindings = attrs
.stcs
.into_iter()
.sorted_by_key(|(k, _)| *k)
.collect::<Vec<_>>();
let stcs = bindings
.iter()
.map(|&(sym, _)| (sym, Var { sym }.ir()))
.collect();
Let {
bindings,
expr: Attrs { stcs, ..attrs }.ir().boxed(),
}
.ir()
} else {
attrs.ir()
}
.ok()
}
}
impl Attrs {
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
Self {
stcs: self
.stcs
.into_iter()
.map(|(k, v)| Ok((k, v.resolve(ctx, env)?)))
.collect::<Result<_>>()?,
dyns: self
.dyns
.into_iter()
.map(|DynamicAttrPair(k, v)| {
Ok(DynamicAttrPair(k.resolve(ctx, env)?, v.resolve(ctx, env)?))
})
.collect::<Result<_>>()?,
}
.ir()
.ok()
} }
} }
@@ -506,6 +842,20 @@ impl Downgrade for ast::List {
} }
} }
impl List {
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
Self {
items: self
.items
.into_iter()
.map(|item| item.resolve(ctx, env))
.collect::<Result<_>>()?,
}
.ir()
.ok()
}
}
impl Downgrade for ast::BinOp { impl Downgrade for ast::BinOp {
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> { fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
BinOp { BinOp {
@@ -518,6 +868,18 @@ impl Downgrade for ast::BinOp {
} }
} }
impl BinOp {
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
Self {
lhs: self.lhs.resolve(ctx, env)?.boxed(),
rhs: self.rhs.resolve(ctx, env)?.boxed(),
..self
}
.ir()
.ok()
}
}
impl Downgrade for ast::HasAttr { impl Downgrade for ast::HasAttr {
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> { fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
let attrs = self.expr().unwrap().downgrade(ctx)?; let attrs = self.expr().unwrap().downgrade(ctx)?;
@@ -531,6 +893,21 @@ impl Downgrade for ast::HasAttr {
} }
} }
impl HasAttr {
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
Self {
lhs: self.lhs.resolve(ctx, env)?.boxed(),
rhs: self
.rhs
.into_iter()
.map(|attr| attr.resolve(ctx, env))
.collect::<Result<_>>()?,
}
.ir()
.ok()
}
}
impl Downgrade for ast::UnaryOp { impl Downgrade for ast::UnaryOp {
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> { fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
UnOp { UnOp {
@@ -542,6 +919,17 @@ impl Downgrade for ast::UnaryOp {
} }
} }
impl UnOp {
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
Self {
rhs: self.rhs.resolve(ctx, env)?.boxed(),
..self
}
.ir()
.ok()
}
}
impl Downgrade for ast::Select { impl Downgrade for ast::Select {
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> { fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
Select { Select {
@@ -557,12 +945,33 @@ impl Downgrade for ast::Select {
} }
} }
impl Select {
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
let default = if let Some(default) = self.default {
Some(default.resolve(ctx, env)?.boxed())
} else {
None
};
Self {
expr: self.expr.resolve(ctx, env)?.boxed(),
attrpath: self
.attrpath
.into_iter()
.map(|attr| attr.resolve(ctx, env))
.collect::<Result<_>>()?,
default,
}
.ir()
.ok()
}
}
impl Downgrade for ast::LegacyLet { impl Downgrade for ast::LegacyLet {
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> { fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
let attrs = downgrade_has_entry(self, true, ctx)?; let attrs = downgrade_attrs(self, ctx)?;
Select { Select {
expr: attrs.ir().boxed(), expr: attrs.ir().boxed(),
attrpath: vec![Attr::Str(ctx.new_sym("body".to_string()))], attrpath: vec![Attr::Str(ctx.new_sym("body"))],
default: None, default: None,
} }
.ir() .ir()
@@ -572,25 +981,103 @@ impl Downgrade for ast::LegacyLet {
impl Downgrade for ast::LetIn { impl Downgrade for ast::LetIn {
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> { fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
let body = self.body().unwrap(); let expr = self.body().unwrap().downgrade(ctx)?.boxed();
let attrs = downgrade_has_entry(self, true, ctx)?; let mut bindings = HashMap::new();
let expr = body.downgrade(ctx)?.boxed(); for entry in self.entries() {
Let { attrs, expr }.ir().ok() match entry {
ast::Entry::Inherit(inherit) => downgrade_inherit(inherit, &mut bindings, ctx)?,
ast::Entry::AttrpathValue(value) => {
let mut path = downgrade_attrpath(value.attrpath().unwrap(), ctx)?.into_iter();
let value = value.value().unwrap().downgrade(ctx)?;
let unwrap_ident = |ident: Attr| {
ident.try_unwrap_str().map_err(|_| {
Error::DowngradeError("dynamic attributes not allowed in let".into())
})
};
let ident = unwrap_ident(path.next().unwrap())?;
if path.len() > 1 {
let mut attrs = bindings
.entry(ident)
.or_insert_with(|| {
Attrs {
stcs: HashMap::new(),
dyns: Vec::new(),
}
.ir()
})
.as_mut()
.try_unwrap_attrs()
.map_err(|_| {
Error::DowngradeError(format!(
r#"attribute '{}' already defined"#,
ctx.get_sym(ident)
))
})?;
while path.len() > 1 {
attrs = attrs
.stcs
.entry(unwrap_ident(path.next().unwrap())?)
.or_insert_with(|| {
Attrs {
stcs: HashMap::new(),
dyns: Vec::new(),
}
.ir()
})
.as_mut()
.try_unwrap_attrs()
.unwrap();
}
attrs
.stcs
.insert(unwrap_ident(path.next().unwrap())?, value);
} else {
bindings.insert(ident, value);
}
}
}
}
let bindings = bindings.into_iter().sorted_by_key(|(k, _)| *k).collect();
Let { bindings, expr }.ir().ok()
}
}
impl Let {
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
let map = self.bindings.iter().map(|(sym, _)| *sym).sorted().collect();
let env = env.enter_let(&map);
let bindings = self
.bindings
.into_iter()
.map(|(k, v)| {
Ok((
k,
match v.resolve(ctx, &env)? {
ir @ Ir::Const(_) => ir,
ir => ctx.new_thunk(ir).ir(),
},
))
})
.collect::<Result<_>>()?;
let expr = self.expr.resolve(ctx, &env)?.boxed();
Self { bindings, expr }.ir().ok()
} }
} }
impl Downgrade for ast::With { impl Downgrade for ast::With {
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> { fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
let namespace = self.namespace().unwrap().downgrade(ctx)?; let namespace = self.namespace().unwrap().downgrade(ctx)?.boxed();
if let Ir::Attrs(attrs) = namespace {
let expr = self.body().unwrap().downgrade(ctx)?.boxed();
Let { attrs, expr }.ir().ok()
} else {
let namespace = namespace.boxed();
let expr = self.body().unwrap().downgrade(ctx)?.boxed(); let expr = self.body().unwrap().downgrade(ctx)?.boxed();
With { namespace, expr }.ir().ok() With { namespace, expr }.ir().ok()
} }
} }
impl With {
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
let namespace = self.namespace.resolve(ctx, env)?.boxed();
let expr = self.expr.resolve(ctx, &env.enter_with())?.boxed();
Self { namespace, expr }.ir().ok()
}
} }
impl Downgrade for ast::Lambda { impl Downgrade for ast::Lambda {
@@ -602,6 +1089,24 @@ impl Downgrade for ast::Lambda {
} }
} }
impl Func {
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Func> {
let env = match &self.param {
Param::Ident(ident) => env.enter_single_arg(*ident),
Param::Formals { formals, alias, .. } => env.enter_multi_arg(
formals
.iter()
.cloned()
.map(|(ident, default)| (ident, default.map(Ir::Thunk)))
.collect(),
*alias,
),
};
let body = self.body.resolve(ctx, &env)?.boxed();
Ok(Self { body, ..self })
}
}
impl Downgrade for ast::Apply { impl Downgrade for ast::Apply {
fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> { fn downgrade(self, ctx: &mut DowngradeContext) -> Result<Ir> {
let mut args = vec![self.argument().unwrap().downgrade(ctx)?]; let mut args = vec![self.argument().unwrap().downgrade(ctx)?];
@@ -616,6 +1121,21 @@ impl Downgrade for ast::Apply {
} }
} }
impl Call {
fn resolve<'a, 'env>(self, ctx: &mut DowngradeContext, env: &Env<'a, 'env>) -> Result<Ir> {
Self {
func: self.func.resolve(ctx, env)?.boxed(),
args: self
.args
.into_iter()
.map(|arg| arg.resolve(ctx, env))
.collect::<Result<_>>()?,
}
.ir()
.ok()
}
}
fn downgrade_param(param: ast::Param, ctx: &mut DowngradeContext) -> Result<Param> { fn downgrade_param(param: ast::Param, ctx: &mut DowngradeContext) -> Result<Param> {
match param { match param {
ast::Param::IdentParam(ident) => Ok(Param::Ident(ctx.new_sym(ident.to_string()))), ast::Param::IdentParam(ident) => Ok(Param::Ident(ctx.new_sym(ident.to_string()))),
@@ -636,6 +1156,7 @@ fn downgrade_pattern(pattern: ast::Pattern, ctx: &mut DowngradeContext) -> Resul
.unwrap() .unwrap()
.downgrade(ctx) .downgrade(ctx)
.map(|ok| (ident, Some(ctx.new_thunk(ok)))) .map(|ok| (ident, Some(ctx.new_thunk(ok))))
// .map(|ok| (ident, Some(ok)))
} }
}) })
.collect::<Result<Vec<_>>>()?; .collect::<Result<Vec<_>>>()?;
@@ -650,14 +1171,9 @@ fn downgrade_pattern(pattern: ast::Pattern, ctx: &mut DowngradeContext) -> Resul
}) })
} }
fn downgrade_has_entry( fn downgrade_attrs(has_entry: impl ast::HasEntry, ctx: &mut DowngradeContext) -> Result<Attrs> {
has_entry: impl ast::HasEntry,
rec: bool,
ctx: &mut DowngradeContext,
) -> Result<Attrs> {
let entires = has_entry.entries(); let entires = has_entry.entries();
let mut attrs = Attrs { let mut attrs = Attrs {
rec,
stcs: HashMap::new(), stcs: HashMap::new(),
dyns: Vec::new(), dyns: Vec::new(),
}; };
@@ -715,7 +1231,7 @@ fn downgrade_attr(attr: ast::Attr, ctx: &mut DowngradeContext) -> Result<Attr> {
Ident(ident) => Ok(Attr::Str(ctx.new_sym(ident.to_string()))), Ident(ident) => Ok(Attr::Str(ctx.new_sym(ident.to_string()))),
Str(string) => { Str(string) => {
let parts = string.normalized_parts(); let parts = string.normalized_parts();
if parts.len() == 0 { if parts.is_empty() {
Ok(Attr::Str(ctx.new_sym(""))) Ok(Attr::Str(ctx.new_sym("")))
} else if parts.len() == 1 { } else if parts.len() == 1 {
match parts.into_iter().next().unwrap() { match parts.into_iter().next().unwrap() {
@@ -757,5 +1273,5 @@ fn downgrade_attrpathvalue(
x @ Ir::Const(_) => x, x @ Ir::Const(_) => x,
x => ctx.new_thunk(x).ir(), x => ctx.new_thunk(x).ir(),
}; };
attrs.insert(path, value) attrs.insert(path, value, ctx)
} }

396
src/jit/helpers.rs Normal file
View File

@@ -0,0 +1,396 @@
use std::ptr::NonNull;
use std::rc::Rc;
use inkwell::AddressSpace;
use inkwell::context::Context;
use inkwell::execution_engine::ExecutionEngine;
use inkwell::module::Module;
use inkwell::types::{FloatType, FunctionType, IntType, PointerType, StructType};
use inkwell::values::{BasicValueEnum, FunctionValue};
use crate::bytecode::OpCodes;
use crate::env::VmEnv;
use crate::jit::JITValueData;
use crate::ty::internal::{Thunk, Value};
use crate::vm::VM;
use super::{JITContext, JITValue, ValueTag};
pub struct Helpers<'ctx> {
pub int_type: IntType<'ctx>,
pub float_type: FloatType<'ctx>,
pub bool_type: IntType<'ctx>,
pub ptr_int_type: IntType<'ctx>,
pub ptr_type: PointerType<'ctx>,
pub value_type: StructType<'ctx>,
pub func_type: FunctionType<'ctx>,
pub new_thunk: FunctionValue<'ctx>,
pub debug: FunctionValue<'ctx>,
pub capture_env: FunctionValue<'ctx>,
pub neg: FunctionValue<'ctx>,
pub not: FunctionValue<'ctx>,
pub add: FunctionValue<'ctx>,
pub sub: FunctionValue<'ctx>,
pub eq: FunctionValue<'ctx>,
pub or: FunctionValue<'ctx>,
pub call: FunctionValue<'ctx>,
pub arg: FunctionValue<'ctx>,
pub lookup_let: FunctionValue<'ctx>,
pub lookup: FunctionValue<'ctx>,
pub force: FunctionValue<'ctx>,
}
impl<'ctx> Helpers<'ctx> {
pub fn new(
context: &'ctx Context,
module: &Module<'ctx>,
execution_engine: &ExecutionEngine<'ctx>,
) -> Self {
let int_type = context.i64_type();
let float_type = context.f64_type();
let bool_type = context.bool_type();
let ptr_int_type = context.ptr_sized_int_type(execution_engine.get_target_data(), None);
let ptr_type = context.ptr_type(AddressSpace::default());
let value_type = context.struct_type(&[int_type.into(), int_type.into()], false);
let func_type = value_type.fn_type(&[ptr_type.into()], false);
let new_thunk = module.add_function(
"new_thunk",
value_type.fn_type(&[ptr_type.into()], false),
None,
);
let debug = module.add_function(
"debug",
context.void_type().fn_type(&[value_type.into()], false),
None,
);
let capture_env = module.add_function(
"capture_env",
context
.void_type()
.fn_type(&[value_type.into(), ptr_type.into()], false),
None,
);
let neg = module.add_function(
"neg",
value_type.fn_type(&[value_type.into(), ptr_type.into()], false),
None,
);
let not = module.add_function(
"not",
value_type.fn_type(&[value_type.into(), ptr_type.into()], false),
None,
);
let add = module.add_function(
"add",
value_type.fn_type(&[value_type.into(), value_type.into()], false),
None,
);
let sub = module.add_function(
"sub",
value_type.fn_type(&[value_type.into(), value_type.into()], false),
None,
);
let eq = module.add_function(
"eq",
value_type.fn_type(&[value_type.into(), value_type.into()], false),
None,
);
let or = module.add_function(
"or",
value_type.fn_type(&[value_type.into(), value_type.into()], false),
None,
);
let call = module.add_function(
"call",
value_type.fn_type(
&[value_type.into(), value_type.into(), ptr_type.into()],
false,
),
None,
);
let arg = module.add_function(
"arg",
value_type.fn_type(&[ptr_int_type.into(), ptr_type.into()], false),
None,
);
let lookup_let = module.add_function(
"lookup_let",
value_type.fn_type(
&[ptr_int_type.into(), ptr_int_type.into(), ptr_type.into()],
false,
),
None,
);
let lookup = module.add_function(
"lookup",
value_type.fn_type(&[ptr_int_type.into(), ptr_type.into()], false),
None,
);
let force = module.add_function(
"force",
value_type.fn_type(
&[value_type.into(), ptr_type.into(), ptr_type.into()],
false,
),
None,
);
execution_engine.add_global_mapping(&new_thunk, helper_new_thunk as _);
execution_engine.add_global_mapping(&debug, helper_debug as _);
execution_engine.add_global_mapping(&capture_env, helper_capture_env as _);
execution_engine.add_global_mapping(&neg, helper_neg as _);
execution_engine.add_global_mapping(&not, helper_not as _);
execution_engine.add_global_mapping(&add, helper_add as _);
execution_engine.add_global_mapping(&sub, helper_sub as _);
execution_engine.add_global_mapping(&eq, helper_eq as _);
execution_engine.add_global_mapping(&or, helper_or as _);
execution_engine.add_global_mapping(&call, helper_call as _);
execution_engine.add_global_mapping(&arg, helper_arg as _);
execution_engine.add_global_mapping(&lookup_let, helper_lookup_let as _);
execution_engine.add_global_mapping(&lookup, helper_lookup as _);
execution_engine.add_global_mapping(&force, helper_force as _);
Helpers {
int_type,
float_type,
bool_type,
ptr_int_type,
ptr_type,
value_type,
func_type,
new_thunk,
debug,
capture_env,
neg,
not,
add,
sub,
eq,
or,
call,
arg,
lookup_let,
lookup,
force,
}
}
pub fn new_int(&self, int: i64) -> BasicValueEnum<'ctx> {
self.value_type
.const_named_struct(&[
self.int_type.const_int(ValueTag::Int as _, false).into(),
self.int_type.const_int(int as _, false).into(),
])
.into()
}
pub fn new_float(&self, float: f64) -> BasicValueEnum<'ctx> {
self.value_type
.const_named_struct(&[
self.int_type.const_int(ValueTag::Float as _, false).into(),
self.float_type.const_float(float).into(),
])
.into()
}
pub fn new_bool(&self, bool: bool) -> BasicValueEnum<'ctx> {
self.value_type
.const_named_struct(&[
self.int_type.const_int(ValueTag::Bool as _, false).into(),
self.bool_type.const_int(bool as _, false).into(),
])
.into()
}
pub fn new_null(&self) -> BasicValueEnum<'ctx> {
self.value_type
.const_named_struct(&[
self.int_type.const_int(ValueTag::Null as _, false).into(),
self.int_type.const_zero().into(),
])
.into()
}
pub fn const_string(&self, string: *const u8) -> BasicValueEnum<'ctx> {
self.value_type
.const_named_struct(&[
self.int_type.const_int(ValueTag::String as _, false).into(),
self.ptr_int_type.const_int(string as _, false).into(),
])
.into()
}
}
extern "C" fn helper_debug(value: JITValue) {
dbg!(value.tag);
}
extern "C" fn helper_capture_env<'gc>(thunk: JITValue, env: *const VmEnv<'gc>) {
let thunk = unsafe { (thunk.data.ptr as *const Thunk).as_ref().unwrap() };
let env = unsafe { Rc::from_raw(env) };
thunk.capture_env(env.clone());
std::mem::forget(env);
}
extern "C" fn helper_neg(rhs: JITValue, _env: *const VmEnv) -> JITValue {
use ValueTag::*;
match rhs.tag {
Int => JITValue {
tag: Int,
data: JITValueData {
int: -unsafe { rhs.data.int },
},
},
Float => JITValue {
tag: Float,
data: JITValueData {
float: -unsafe { rhs.data.float },
},
},
_ => todo!(),
}
}
extern "C" fn helper_not(rhs: JITValue, _env: *const VmEnv) -> JITValue {
use ValueTag::*;
match rhs.tag {
Bool => JITValue {
tag: Bool,
data: JITValueData {
bool: !unsafe { rhs.data.bool },
},
},
_ => todo!(),
}
}
extern "C" fn helper_add(lhs: JITValue, rhs: JITValue) -> JITValue {
use ValueTag::*;
match (lhs.tag, rhs.tag) {
(Int, Int) => JITValue {
tag: Int,
data: JITValueData {
int: unsafe { lhs.data.int + rhs.data.int },
},
},
_ => todo!(
"Addition not implemented for {:?} and {:?}",
lhs.tag,
rhs.tag
),
}
}
extern "C" fn helper_sub(lhs: JITValue, rhs: JITValue) -> JITValue {
use ValueTag::*;
match (lhs.tag, rhs.tag) {
(Int, Int) => JITValue {
tag: Int,
data: JITValueData {
int: unsafe { lhs.data.int - rhs.data.int },
},
},
_ => todo!(
"Substruction not implemented for {:?} and {:?}",
lhs.tag,
rhs.tag
),
}
}
extern "C" fn helper_eq(lhs: JITValue, rhs: JITValue) -> JITValue {
use ValueTag::*;
match (lhs.tag, rhs.tag) {
(Int, Int) => JITValue {
tag: Bool,
data: JITValueData {
bool: unsafe { lhs.data.int == rhs.data.int },
},
},
_ => todo!(
"Equation not implemented for {:?} and {:?}",
lhs.tag,
rhs.tag
),
}
}
extern "C" fn helper_or(lhs: JITValue, rhs: JITValue) -> JITValue {
use ValueTag::*;
match (lhs.tag, rhs.tag) {
(Bool, Bool) => JITValue {
tag: Bool,
data: JITValueData {
bool: unsafe { lhs.data.bool || rhs.data.bool },
},
},
_ => todo!(
"Substraction not implemented for {:?} and {:?}",
lhs.tag,
rhs.tag
),
}
}
extern "C" fn helper_call<'gc>(func: JITValue, arg: JITValue, vm: *const VM<'gc>) -> JITValue {
let vm = unsafe { vm.as_ref() }.unwrap();
let arg = Value::from(arg);
match func.tag {
ValueTag::Function => {
let func = Value::from(func).unwrap_func();
func.call_compile(arg, vm).unwrap().clone().into()
}
_ => todo!(),
}
}
extern "C" fn helper_arg(idx: usize, env: *const VmEnv) -> JITValue {
let env = unsafe { env.as_ref() }.unwrap();
let val: JITValue = env.lookup_arg(idx).clone().into();
val
}
extern "C" fn helper_lookup_let(level: usize, idx: usize, env: *const VmEnv) -> JITValue {
let env = unsafe { env.as_ref() }.unwrap();
let val: JITValue = env.lookup_let(level, idx).clone().into();
val
}
extern "C" fn helper_lookup(sym: usize, env: *const VmEnv) -> JITValue {
let env = unsafe { env.as_ref() }.unwrap();
let val: JITValue = env.lookup_with(&sym).unwrap().clone().into();
val
}
extern "C" fn helper_force<'gc>(
thunk: JITValue,
vm: NonNull<VM<'gc>>,
jit: *const JITContext<'gc>,
) -> JITValue {
if !matches!(thunk.tag, ValueTag::Thunk) {
return thunk;
}
let vm = unsafe { vm.as_ref() };
let thunk = Value::from(thunk).unwrap_thunk();
if let Some(val) = thunk.get_value() {
return val.clone().into();
}
let (opcodes, env) = thunk.suspend().unwrap();
let func = unsafe { jit.as_ref() }
.unwrap()
.compile_seq(opcodes.iter().copied().rev(), vm)
.unwrap();
let val = unsafe { func(env.as_ref() as *const _) };
thunk.insert_value(val.into());
val
}
extern "C" fn helper_new_thunk(opcodes: *const OpCodes) -> JITValue {
Value::Thunk(Thunk::new(unsafe { opcodes.as_ref() }.unwrap())).into()
}

468
src/jit/mod.rs Normal file
View File

@@ -0,0 +1,468 @@
use std::marker::PhantomData;
use std::ops::Deref;
use std::rc::Rc;
use inkwell::OptimizationLevel;
use inkwell::builder::Builder;
use inkwell::context::Context;
use inkwell::execution_engine::ExecutionEngine;
use inkwell::module::Module;
use inkwell::values::{BasicValueEnum, FunctionValue, PointerValue};
use crate::bytecode::{OpCode, UnOp};
use crate::env::VmEnv;
use crate::error::*;
use crate::stack::Stack;
use crate::ty::common::Const;
use crate::ty::internal::{Thunk, Value};
use crate::vm::VM;
mod helpers;
use helpers::Helpers;
#[cfg(test)]
mod test;
const STACK_SIZE: usize = 8 * 1024 / size_of::<Value>();
#[repr(u64)]
#[derive(Debug, Clone, Copy)]
pub enum ValueTag {
Null,
Int,
Float,
String,
Bool,
AttrSet,
List,
Function,
Thunk,
Path,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct JITValue {
tag: ValueTag,
data: JITValueData,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub union JITValueData {
int: i64,
float: f64,
bool: bool,
ptr: *const (),
}
impl<'gc> From<JITValue> for Value<'gc> {
fn from(value: JITValue) -> Self {
use ValueTag::*;
match value.tag {
Int => Value::Int(unsafe { value.data.int }),
Null => Value::Null,
Function => Value::Func(unsafe { Rc::from_raw(value.data.ptr as *const _) }),
Thunk => Value::Thunk(self::Thunk {
thunk: unsafe { Rc::from_raw(value.data.ptr as *const _) },
}),
_ => todo!("not implemented for {:?}", value.tag),
}
}
}
impl From<Value<'_>> for JITValue {
fn from(value: Value) -> Self {
match value {
Value::Int(int) => JITValue {
tag: ValueTag::Int,
data: JITValueData { int },
},
Value::Func(func) => JITValue {
tag: ValueTag::Function,
data: JITValueData {
ptr: Rc::into_raw(func) as *const _,
},
},
Value::Thunk(thunk) => JITValue {
tag: ValueTag::Thunk,
data: JITValueData {
ptr: Rc::into_raw(thunk.thunk) as *const _,
},
},
_ => todo!(),
}
}
}
pub struct JITFunc<'gc>(F<'gc>, PhantomData<&'gc mut ()>);
type F<'gc> = unsafe extern "C" fn(*const VmEnv<'gc>) -> JITValue;
impl<'gc> From<F<'gc>> for JITFunc<'gc> {
fn from(value: F<'gc>) -> Self {
Self(value, PhantomData)
}
}
impl<'gc> Deref for JITFunc<'gc> {
type Target = F<'gc>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub struct JITContext<'gc> {
context: &'gc Context,
module: Module<'gc>,
builder: Builder<'gc>,
execution_engine: ExecutionEngine<'gc>,
helpers: Helpers<'gc>,
}
impl<'gc> JITContext<'gc> {
pub fn new(context: &'gc Context) -> Self {
// force linker to link JIT engine
unsafe {
inkwell::llvm_sys::execution_engine::LLVMLinkInMCJIT();
}
let module = context.create_module("nixjit");
let execution_engine = module
.create_jit_execution_engine(OptimizationLevel::Aggressive)
.unwrap();
let helpers = Helpers::new(context, &module, &execution_engine);
JITContext {
execution_engine,
builder: context.create_builder(),
context,
module,
helpers,
}
}
pub fn new_ptr<T>(&self, ptr: *const T) -> PointerValue<'gc> {
self.builder
.build_int_to_ptr(
self.helpers.int_type.const_int(ptr as _, false),
self.helpers.ptr_type,
"ptrconv",
)
.unwrap()
}
pub fn compile_seq(
&self,
mut opcodes: impl ExactSizeIterator<Item = OpCode>,
vm: &VM<'gc>,
) -> Result<JITFunc<'gc>> {
let mut stack = Stack::<_, STACK_SIZE>::new();
let func_ = self
.module
.add_function("nixjit_function", self.helpers.func_type, None);
let env = func_.get_nth_param(0).unwrap().into_pointer_value();
let entry = self.context.append_basic_block(func_, "entry");
self.builder.position_at_end(entry);
let len = opcodes.len();
self.build_expr(&mut opcodes, vm, env, &mut stack, func_, len)?;
assert_eq!(stack.len(), 1);
let value = stack.pop();
let exit = self.context.append_basic_block(func_, "exit");
self.builder.build_unconditional_branch(exit)?;
self.builder.position_at_end(exit);
self.builder.build_return(Some(&value))?;
if func_.verify(true) {
unsafe {
let name = func_.get_name().to_str().unwrap();
let func = self.execution_engine.get_function(name).unwrap();
Ok(JITFunc(func.as_raw(), PhantomData))
}
} else {
todo!()
}
}
fn build_expr<const CAP: usize>(
&self,
iter: &mut impl Iterator<Item = OpCode>,
vm: &VM<'gc>,
env: PointerValue<'gc>,
stack: &mut Stack<BasicValueEnum<'gc>, CAP>,
func: FunctionValue<'gc>,
mut length: usize,
) -> Result<usize> {
while length > 1 {
let opcode = iter.next().unwrap();
let br = self.single_op(opcode, vm, env, stack)?;
length -= 1;
if br > 0 {
let consq = self.context.append_basic_block(func, "consq");
let alter = self.context.append_basic_block(func, "alter");
let cont = self.context.append_basic_block(func, "cont");
let cond = self
.builder
.build_alloca(self.helpers.value_type, "cond_alloca")?;
let result = self
.builder
.build_alloca(self.helpers.value_type, "result_alloca")?;
self.builder.build_store(cond, stack.pop())?;
self.builder.build_conditional_branch(
self.builder
.build_load(
self.context.bool_type(),
self.builder.build_struct_gep(
self.helpers.value_type,
cond,
1,
"gep_cond",
)?,
"load_cond",
)?
.into_int_value(),
consq,
alter,
)?;
length -= br;
self.builder.position_at_end(consq);
let br = self.build_expr(iter, vm, env, stack, func, br)?;
self.builder.build_store(result, stack.pop())?;
self.builder.build_unconditional_branch(cont)?;
length -= br;
self.builder.position_at_end(alter);
self.build_expr(iter, vm, env, stack, func, br)?;
self.builder.build_store(result, stack.pop())?;
self.builder.build_unconditional_branch(cont)?;
self.builder.position_at_end(cont);
stack.push(self.builder.build_load(
self.helpers.value_type,
result,
"load_result",
)?)?;
}
}
if length > 0 {
self.single_op(iter.next().unwrap(), vm, env, stack)
} else {
Ok(0)
}
}
#[inline(always)]
fn single_op<const CAP: usize>(
&self,
opcode: OpCode,
vm: &VM<'_>,
env: PointerValue<'gc>,
stack: &mut Stack<BasicValueEnum<'gc>, CAP>,
) -> Result<usize> {
match opcode {
OpCode::Const { idx } => {
use Const::*;
match vm.get_const(idx) {
Int(int) => stack.push(self.helpers.new_int(int))?,
Float(float) => stack.push(self.helpers.new_float(float))?,
Bool(bool) => stack.push(self.helpers.new_bool(bool))?,
String(string) => stack.push(self.helpers.const_string(string.as_ptr()))?,
Null => stack.push(self.helpers.new_null())?,
}
}
OpCode::LoadThunk { idx } => stack.push(
self.builder
.build_direct_call(
self.helpers.new_thunk,
&[self.new_ptr(vm.get_thunk(idx) as *const _).into()],
"call_capture_env",
)?
.try_as_basic_value()
.unwrap_left(),
)?,
OpCode::ForceValue => {
let thunk = stack.pop();
let _ = stack.push(
self.builder
.build_direct_call(
self.helpers.force,
&[
thunk.into(),
self.new_ptr(vm as *const _).into(),
self.new_ptr(self as *const _).into(),
],
"call_force",
)?
.try_as_basic_value()
.left()
.unwrap(),
);
}
OpCode::CaptureEnv => {
let thunk = *stack.tos();
self.builder.build_direct_call(
self.helpers.capture_env,
&[thunk.into(), env.into()],
"call_capture_env",
)?;
}
OpCode::UnOp { op } => {
use UnOp::*;
let rhs = stack.pop();
stack.push(match op {
Neg => self
.builder
.build_direct_call(self.helpers.neg, &[rhs.into(), env.into()], "call_neg")?
.try_as_basic_value()
.left()
.unwrap(),
Not => self
.builder
.build_direct_call(self.helpers.not, &[rhs.into(), env.into()], "call_neg")?
.try_as_basic_value()
.left()
.unwrap(),
})?
}
OpCode::BinOp { op } => {
use crate::bytecode::BinOp;
let rhs = stack.pop();
let lhs = stack.pop();
match op {
BinOp::Add => {
let result = self
.builder
.build_direct_call(
self.helpers.add,
&[lhs.into(), rhs.into()],
"call_add",
)?
.try_as_basic_value()
.left()
.unwrap();
stack.push(result)?;
}
BinOp::Sub => {
let result = self
.builder
.build_direct_call(
self.helpers.sub,
&[lhs.into(), rhs.into()],
"call_add",
)?
.try_as_basic_value()
.left()
.unwrap();
stack.push(result)?;
}
BinOp::Eq => {
let result = self
.builder
.build_direct_call(
self.helpers.eq,
&[lhs.into(), rhs.into()],
"call_eq",
)?
.try_as_basic_value()
.left()
.unwrap();
stack.push(result)?;
}
BinOp::Or => {
let result = self
.builder
.build_direct_call(
self.helpers.or,
&[lhs.into(), rhs.into()],
"call_or",
)?
.try_as_basic_value()
.left()
.unwrap();
stack.push(result)?;
}
_ => todo!("BinOp::{:?} not implemented in JIT", op),
}
}
OpCode::Arg { level } => stack.push(
self.builder
.build_direct_call(
self.helpers.arg,
&[
self.helpers
.ptr_int_type
.const_int(level as u64, false)
.into(),
env.into(),
],
"call_arg",
)
.unwrap()
.try_as_basic_value()
.left()
.unwrap(),
)?,
OpCode::LookUpLet { level, idx } => stack.push(
self.builder
.build_direct_call(
self.helpers.lookup_let,
&[
self.helpers
.ptr_int_type
.const_int(level as u64, false)
.into(),
self.helpers
.ptr_int_type
.const_int(idx as u64, false)
.into(),
env.into(),
],
"call_lookup_let",
)
.unwrap()
.try_as_basic_value()
.left()
.unwrap(),
)?,
OpCode::LookUp { sym } => stack.push(
self.builder
.build_direct_call(
self.helpers.lookup,
&[
self.helpers
.ptr_int_type
.const_int(sym as u64, false)
.into(),
env.into(),
],
"call_lookup",
)
.unwrap()
.try_as_basic_value()
.left()
.unwrap(),
)?,
OpCode::Call => {
let arg = stack.pop();
let func = stack.pop();
let ret = self
.builder
.build_direct_call(
self.helpers.call,
&[func.into(), arg.into(), self.new_ptr(vm).into()],
"call",
)?
.try_as_basic_value()
.left()
.unwrap();
stack.push(ret)?;
}
OpCode::JmpIfFalse { step } => return Ok(step),
OpCode::Jmp { step } => return Ok(step),
_ => todo!("{opcode:?} not implemented"),
}
Ok(0)
}
}

96
src/jit/test.rs Normal file
View File

@@ -0,0 +1,96 @@
#![allow(unused)]
extern crate test;
use hashbrown::{HashMap, HashSet};
use inkwell::context::Context;
use ecow::EcoString;
use crate::compile::compile;
use crate::ir::downgrade;
use crate::jit::JITContext;
use crate::ty::common::Const;
use crate::ty::public::*;
use crate::vm::run;
#[inline]
fn test_expr(expr: &str, expected: Value) {
let downgraded = downgrade(rnix::Root::parse(expr).tree().expr().unwrap()).unwrap();
let prog = compile(downgraded);
dbg!(&prog);
assert_eq!(run(prog).unwrap(), expected);
}
macro_rules! map {
($($k:expr => $v:expr),*) => {
{
#[allow(unused_mut)]
let mut m = HashMap::new();
$(
m.insert($k, $v);
)*
m
}
};
}
macro_rules! thunk {
() => {
Value::Thunk
};
}
macro_rules! int {
($e:expr) => {
Value::Const(Const::Int($e))
};
}
macro_rules! float {
($e:expr) => {
Value::Const(Const::Float($e as f64))
};
}
macro_rules! boolean {
($e:expr) => {
Value::Const(Const::Bool($e))
};
}
macro_rules! string {
($e:expr) => {
Value::Const(Const::String(EcoString::from($e)))
};
}
macro_rules! symbol {
($e:expr) => {
Symbol::from($e.to_string())
};
}
macro_rules! list {
($($x:tt)*) => (
Value::List(List::new(vec![$($x)*]))
);
}
macro_rules! attrs {
($($x:tt)*) => (
Value::AttrSet(AttrSet::new(map!{$($x)*}))
)
}
#[test]
fn test_jit_const() {
// test_expr("let f = _: 1; in (f 1) + (f 1)", int!(2));
test_expr("let f = _: 1; in (f 1) == (f 1)", boolean!(true));
}
#[test]
fn test_arith() {
test_expr("let f = _: -(-1); in (f 1) + (f 1)", int!(2));
}

View File

@@ -1,14 +1,18 @@
#![cfg_attr(test, feature(test))] #![cfg_attr(test, feature(test))]
#![allow(dead_code)] #![allow(dead_code)]
#![feature(iter_collect_into)]
#![feature(arbitrary_self_types)]
mod builtins; mod builtins;
mod bytecode; mod bytecode;
mod env;
mod stack; mod stack;
mod ty; mod ty;
pub mod compile; pub mod compile;
pub mod error; pub mod error;
pub mod ir; pub mod ir;
pub mod jit;
pub mod vm; pub mod vm;
pub use ty::public::Value; pub use ty::public::Value;

View File

@@ -3,11 +3,6 @@ use std::ops::Deref;
use crate::error::*; use crate::error::*;
pub struct Stack<T, const CAP: usize> {
items: [MaybeUninit<T>; CAP],
top: usize,
}
macro_rules! into { macro_rules! into {
($e:expr) => { ($e:expr) => {
// SAFETY: This macro is used to transmute `MaybeUninit<Value<'vm>>` to `Value<'vm>` // SAFETY: This macro is used to transmute `MaybeUninit<Value<'vm>>` to `Value<'vm>`
@@ -18,6 +13,18 @@ macro_rules! into {
}; };
} }
pub struct Stack<T, const CAP: usize> {
items: [MaybeUninit<T>; CAP],
top: usize,
}
impl<T, const CAP: usize> Default for Stack<T, CAP> {
fn default() -> Self {
Self::new()
}
}
impl<T, const CAP: usize> Stack<T, CAP> { impl<T, const CAP: usize> Stack<T, CAP> {
pub fn new() -> Self { pub fn new() -> Self {
Stack { Stack {
@@ -29,18 +36,15 @@ impl<T, const CAP: usize> Stack<T, CAP> {
pub fn push(&mut self, item: T) -> Result<()> { pub fn push(&mut self, item: T) -> Result<()> {
self.items self.items
.get_mut(self.top) .get_mut(self.top)
.map_or_else( .map_or_else(|| Err(Error::EvalError("stack overflow".to_string())), Ok)?
|| Err(Error::EvalError("stack overflow".to_string())),
|ok| Ok(ok),
)?
.write(item); .write(item);
self.top += 1; self.top += 1;
Ok(()) Ok(())
} }
pub fn pop(&mut self) -> T { pub fn pop(&mut self) -> T {
let item = self.items.get_mut(self.top - 1).unwrap();
self.top -= 1; self.top -= 1;
let item = self.items.get_mut(self.top).unwrap();
// SAFETY: `item` at `self.top` was previously written and is initialized. // SAFETY: `item` at `self.top` was previously written and is initialized.
// We replace it with `MaybeUninit::uninit()` and then `assume_init` // We replace it with `MaybeUninit::uninit()` and then `assume_init`
@@ -48,20 +52,12 @@ impl<T, const CAP: usize> Stack<T, CAP> {
unsafe { replace(item, MaybeUninit::uninit()).assume_init() } unsafe { replace(item, MaybeUninit::uninit()).assume_init() }
} }
pub fn tos(&self) -> Result<&T> { pub fn tos(&self) -> &T {
if self.top == 0 { into!(&self.items[self.top - 1])
panic!("stack empty")
} else {
Ok(into!(&self.items[self.top - 1]))
}
} }
pub fn tos_mut(&mut self) -> Result<&mut T> { pub fn tos_mut(&mut self) -> &mut T {
if self.top == 0 { into!(&mut self.items[self.top - 1])
panic!("stack empty")
} else {
Ok(into!(&mut self.items[self.top - 1]))
}
} }
} }

View File

@@ -1,14 +1,116 @@
use std::fmt::{Display, Formatter, Result as FmtResult}; use std::fmt::{Display, Formatter, Result as FmtResult};
use std::hash::Hash;
use derive_more::Constructor; use derive_more::{Constructor, IsVariant, Unwrap};
use ecow::EcoString;
#[derive(Clone, Debug, PartialEq, Constructor, Hash)] #[derive(Clone, Debug, PartialEq, Constructor, Hash)]
pub struct Catchable { pub struct Catchable {
msg: String, msg: String,
} }
impl From<String> for Catchable {
fn from(value: String) -> Self {
Catchable { msg: value }
}
}
impl Display for Catchable { impl Display for Catchable {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "<error: {}>", self.msg) write!(f, "<error: {}>", self.msg)
} }
} }
#[derive(Debug, Clone, IsVariant, Unwrap)]
pub enum Const {
Bool(bool),
Int(i64),
Float(f64),
String(EcoString),
Null,
}
impl Hash for Const {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
use Const::*;
std::mem::discriminant(self).hash(state);
match self {
Int(x) => x.hash(state),
Float(x) => x.to_bits().hash(state),
Bool(x) => x.hash(state),
String(x) => x.hash(state),
Null => (),
}
}
}
impl Display for Const {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
use Const::*;
match self {
Int(x) => write!(f, "{x}"),
Float(x) => write!(f, "{x}"),
Bool(x) => write!(f, "{x}"),
String(x) => write!(f, "{x:?}"),
Null => write!(f, "null"),
}
}
}
impl From<bool> for Const {
fn from(value: bool) -> Self {
Const::Bool(value)
}
}
impl From<i64> for Const {
fn from(value: i64) -> Self {
Const::Int(value)
}
}
impl From<f64> for Const {
fn from(value: f64) -> Self {
Const::Float(value)
}
}
impl From<EcoString> for Const {
fn from(value: EcoString) -> Self {
Const::String(value)
}
}
impl From<String> for Const {
fn from(value: String) -> Self {
Const::String(value.into())
}
}
impl From<&str> for Const {
fn from(value: &str) -> Self {
Const::String(value.into())
}
}
impl PartialEq for Const {
fn eq(&self, other: &Self) -> bool {
use Const::*;
match (self, other) {
(Bool(a), Bool(b)) => a == b,
(Int(a), Int(b)) => a == b,
(Float(a), Float(b)) => a == b,
(Int(a), Float(b)) => *a as f64 == *b,
(Float(a), Int(b)) => *b as f64 == *a,
(String(a), String(b)) => a == b,
_ => false,
}
}
}
impl Eq for Const {}
pub enum MaybeThunk {
Thunk(usize),
Const(Const),
}

View File

@@ -1,45 +1,53 @@
use hashbrown::{HashMap, HashSet}; use std::ops::Deref;
use std::rc::Rc;
use derive_more::Constructor; use derive_more::Constructor;
use hashbrown::{HashMap, HashSet};
use itertools::Itertools; use itertools::Itertools;
use crate::error::Result; use crate::vm::VM;
use crate::vm::{Env, VM};
use super::super::public as p; use super::super::public as p;
use super::Value; use super::Value;
#[repr(C)] #[repr(transparent)]
#[derive(Debug, Constructor, Clone, PartialEq)] #[derive(Constructor, Clone, PartialEq)]
pub struct AttrSet<'vm> { pub struct AttrSet<'gc> {
data: HashMap<usize, Value<'vm>>, data: HashMap<usize, Value<'gc>>,
} }
impl<'vm> AttrSet<'vm> { impl<'gc> From<HashMap<usize, Value<'gc>>> for AttrSet<'gc> {
pub fn empty() -> Self { fn from(data: HashMap<usize, Value<'gc>>) -> Self {
AttrSet { Self { data }
data: HashMap::new(),
} }
} }
impl<'gc> Deref for AttrSet<'gc> {
type Target = HashMap<usize, Value<'gc>>;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<'gc> AttrSet<'gc> {
pub fn with_capacity(cap: usize) -> Self { pub fn with_capacity(cap: usize) -> Self {
AttrSet { AttrSet {
data: HashMap::with_capacity(cap), data: HashMap::with_capacity(cap),
} }
} }
pub fn push_attr_force(&mut self, sym: usize, val: Value<'vm>) { pub fn push_attr_force(&mut self, sym: usize, val: Value<'gc>) {
self.data.insert(sym, val); self.data.insert(sym, val);
} }
pub fn push_attr(&mut self, sym: usize, val: Value<'vm>) { pub fn push_attr(&mut self, sym: usize, val: Value<'gc>) {
if self.data.get(&sym).is_some() { if self.data.get(&sym).is_some() {
todo!() todo!()
} }
self.data.insert(sym, val); self.data.insert(sym, val);
} }
pub fn select(&self, sym: usize) -> Option<Value<'vm>> { pub fn select(&self, sym: usize) -> Option<Value<'gc>> {
self.data.get(&sym).cloned() self.data.get(&sym).cloned()
} }
@@ -47,52 +55,34 @@ impl<'vm> AttrSet<'vm> {
self.data.get(&sym).is_some() self.data.get(&sym).is_some()
} }
pub fn capture(&mut self, env: &Env<'vm>) { pub fn update(&mut self, other: &AttrSet<'gc>) {
self.data.iter().for_each(|(_, v)| match v.clone() {
Value::Thunk(ref thunk) => {
thunk.capture(env.clone());
}
_ => (),
})
}
pub fn update(&mut self, other: &AttrSet<'vm>) {
for (k, v) in other.data.iter() { for (k, v) in other.data.iter() {
self.push_attr_force(k.clone(), v.clone()) self.push_attr_force(*k, v.clone())
} }
} }
pub fn as_inner(&self) -> &HashMap<usize, Value<'vm>> { pub fn as_inner(&self) -> &HashMap<usize, Value<'gc>> {
&self.data &self.data
} }
pub fn from_inner(data: HashMap<usize, Value<'vm>>) -> Self { pub fn into_inner(self: Rc<Self>) -> Rc<HashMap<usize, Value<'gc>>> {
unsafe { std::mem::transmute(self) }
}
pub fn from_inner(data: HashMap<usize, Value<'gc>>) -> Self {
Self { data } Self { data }
} }
pub fn force_deep(&mut self, vm: &'vm VM<'_>) -> Result<()> { pub fn eq_impl(&self, other: &AttrSet<'gc>) -> bool {
let mut map: Vec<_> = self
.data
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect();
for (_, v) in map.iter_mut() {
v.force_deep(vm)?;
}
self.data = map.into_iter().collect();
Ok(())
}
pub fn eq_impl(&self, other: &AttrSet<'vm>, vm: &'vm VM<'_>) -> bool {
self.data.iter().len() == other.data.iter().len() self.data.iter().len() == other.data.iter().len()
&& std::iter::zip( && std::iter::zip(
self.data.iter().sorted_by_key(|(k, _)| **k), self.data.iter().sorted_by_key(|(k, _)| **k),
self.data.iter().sorted_by_key(|(k, _)| **k), self.data.iter().sorted_by_key(|(k, _)| **k),
) )
.all(|((_, v1), (_, v2))| v1.eq_impl(v2, vm)) .all(|((_, v1), (_, v2))| v1.eq_impl(v2))
} }
pub fn to_public(&self, vm: &'vm VM, seen: &mut HashSet<Value<'vm>>) -> p::Value { pub fn to_public(&self, vm: &VM<'gc>, seen: &mut HashSet<Value<'gc>>) -> p::Value {
p::Value::AttrSet(p::AttrSet::new( p::Value::AttrSet(p::AttrSet::new(
self.data self.data
.iter() .iter()

View File

@@ -1,79 +0,0 @@
use std::hash::Hash;
use derive_more::{IsVariant, Unwrap};
use ecow::EcoString;
#[derive(Debug, Clone, IsVariant, Unwrap)]
pub enum Const {
Bool(bool),
Int(i64),
Float(f64),
String(EcoString),
Null,
}
impl Hash for Const {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
use Const::*;
match self {
Int(x) => x.hash(state),
Float(x) => x.to_bits().hash(state),
Bool(x) => x.hash(state),
String(x) => x.hash(state),
x @ Null => x.hash(state),
}
}
}
impl From<bool> for Const {
fn from(value: bool) -> Self {
Const::Bool(value)
}
}
impl From<i64> for Const {
fn from(value: i64) -> Self {
Const::Int(value)
}
}
impl From<f64> for Const {
fn from(value: f64) -> Self {
Const::Float(value)
}
}
impl From<EcoString> for Const {
fn from(value: EcoString) -> Self {
Const::String(value)
}
}
impl From<String> for Const {
fn from(value: String) -> Self {
Const::String(value.into())
}
}
impl From<&str> for Const {
fn from(value: &str) -> Self {
Const::String(value.into())
}
}
impl PartialEq for Const {
fn eq(&self, other: &Self) -> bool {
use Const::*;
match (self, other) {
(Bool(a), Bool(b)) => a == b,
(Int(a), Int(b)) => a == b,
(Float(a), Float(b)) => a == b,
(Int(a), Float(b)) => *a as f64 == *b,
(Float(a), Int(b)) => *b as f64 == *a,
(String(a), String(b)) => a == b,
_ => false,
}
}
}
impl Eq for Const {}

View File

@@ -1,11 +1,13 @@
use derive_more::Constructor; use std::cell::{Cell, OnceCell};
use itertools::Itertools; use std::rc::Rc;
use crate::bytecode::Func as BFunc; use crate::bytecode::Func as BFunc;
use crate::env::VmEnv;
use crate::error::Result; use crate::error::Result;
use crate::ir; use crate::ir;
use crate::ty::internal::{Thunk, Value}; use crate::jit::JITFunc;
use crate::vm::{Env, VM}; use crate::ty::internal::Value;
use crate::vm::VM;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Param { pub enum Param {
@@ -37,59 +39,28 @@ impl From<ir::Param> for Param {
} }
} }
pub type JITFunc<'vm> = pub struct Func<'gc> {
unsafe extern "C" fn(vm: *mut VM<'_>, *mut Env<'vm>, *mut Value<'vm>) -> Value<'vm>; pub func: &'gc BFunc,
pub env: Rc<VmEnv<'gc>>,
#[derive(Debug, Clone, Constructor)] pub compiled: OnceCell<JITFunc<'gc>>,
pub struct Func<'vm> { pub count: Cell<usize>,
pub func: &'vm BFunc,
pub env: Env<'vm>,
pub compiled: Option<JITFunc<'vm>>,
} }
impl<'vm> Func<'vm> { impl<'gc> Func<'gc> {
pub fn call(&self, vm: &'vm VM<'_>, arg: Value<'vm>) -> Result<Value<'vm>> { pub fn new(func: &'gc BFunc, env: Rc<VmEnv<'gc>>) -> Self {
use Param::*; Self {
func,
let mut env = self.env.clone(); env,
compiled: OnceCell::new(),
match self.func.param.clone() { count: Cell::new(0),
Ident(ident) => env = env.enter([(ident.into(), arg)].into_iter()),
Formals {
formals,
ellipsis,
alias,
} => {
let arg = arg.unwrap_attr_set();
let mut new = Vec::with_capacity(formals.len() + alias.iter().len());
if !ellipsis
&& arg
.as_inner()
.iter()
.map(|(k, _)| k)
.sorted()
.ne(formals.iter().map(|(k, _)| k).sorted())
{
todo!()
}
for (formal, default) in formals {
let formal = formal.clone().into();
let arg = arg
.select(formal)
.or_else(|| {
default.map(|idx| Value::Thunk(Thunk::new(vm.get_thunk(idx)).into()))
})
.unwrap();
new.push((formal, arg));
}
if let Some(alias) = alias {
new.push((alias.clone().into(), Value::AttrSet(arg)));
}
env = env.enter(new.into_iter());
} }
} }
vm.eval(self.func.opcodes.iter().copied(), env) pub fn call_compile(&self, arg: Value<'gc>, vm: &'gc VM<'gc>) -> Result<Value<'gc>> {
let env = self.env.clone().enter_arg(arg);
let compiled = self.compiled.get_or_init(|| vm.compile_func(self.func));
let ret = unsafe { compiled(env.as_ref() as *const VmEnv) };
Ok(ret.into())
} }
} }

View File

@@ -1,46 +1,58 @@
use std::rc::Weak;
use hashbrown::HashSet; use hashbrown::HashSet;
use derive_more::Constructor; use crate::env::VmEnv;
use rpds::Vector;
use crate::error::Result;
use crate::ty::public as p; use crate::ty::public as p;
use crate::vm::VM; use crate::vm::VM;
use super::Value; use super::Value;
#[derive(Debug, Constructor, Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub struct List<'vm> { pub struct List<'gc> {
data: Vector<Value<'vm>>, data: Vec<Value<'gc>>,
} }
impl<'vm> List<'vm> { impl<'gc> Default for List<'gc> {
pub fn empty() -> Self { fn default() -> Self {
Self::new()
}
}
impl<'gc> List<'gc> {
pub fn new() -> Self {
List { data: Vec::new() }
}
pub fn with_capacity(cap: usize) -> Self {
List { List {
data: Vector::new(), data: Vec::with_capacity(cap),
} }
} }
pub fn push(&mut self, elem: Value<'vm>) { pub fn push(&mut self, elem: Value<'gc>) {
self.data.push_back_mut(elem); self.data.push(elem);
} }
pub fn concat(&mut self, other: &List<'vm>) { pub fn concat(&mut self, other: &List<'gc>) {
for elem in other.data.iter() { for elem in other.data.iter() {
self.data.push_back_mut(elem.clone()); self.data.push(elem.clone());
} }
} }
pub fn force_deep(&mut self, vm: &'vm VM<'_>) -> Result<()> { pub fn capture(&mut self, env: &Weak<VmEnv<'gc>>) {
let mut vec: Vec<_> = self.data.iter().cloned().collect(); self.data.iter().for_each(|v| {
for v in vec.iter_mut() { if let Value::Thunk(ref thunk) = v.clone() {
v.force_deep(vm)?; thunk.capture_env_weak(env.clone());
} }
self.data = vec.into_iter().collect(); })
Ok(())
} }
pub fn to_public(&self, vm: &'vm VM, seen: &mut HashSet<Value<'vm>>) -> p::Value { pub fn into_inner(self) -> Vec<Value<'gc>> {
self.data
}
pub fn to_public(&self, vm: &VM<'gc>, seen: &mut HashSet<Value<'gc>>) -> p::Value {
p::Value::List(p::List::new( p::Value::List(p::List::new(
self.data self.data
.iter() .iter()

View File

@@ -1,43 +1,44 @@
use hashbrown::HashSet;
use std::cell::OnceCell;
use std::cell::RefCell; use std::cell::RefCell;
use std::hash::Hash; use std::hash::Hash;
use std::rc::Rc; use std::rc::{Rc, Weak};
use derive_more::{IsVariant, Unwrap}; use derive_more::{IsVariant, Unwrap};
use hashbrown::HashSet;
use replace_with::replace_with_or_abort;
use super::common as c; use super::common::*;
use super::public as p; use super::public as p;
use crate::bytecode::OpCodes; use crate::bytecode::OpCodes;
use crate::env::VmEnv;
use crate::error::*; use crate::error::*;
use crate::vm::{Env, VM}; use crate::vm::VM;
mod attrset; mod attrset;
mod cnst;
mod func; mod func;
mod list; mod list;
mod primop; mod primop;
mod string; mod string;
pub use attrset::*; pub use attrset::*;
pub use cnst::Const;
pub use func::*; pub use func::*;
pub use list::List; pub use list::List;
pub use primop::*; pub use primop::*;
#[derive(Debug, IsVariant, Unwrap, Clone, PartialEq)] #[derive(IsVariant, Unwrap, Clone)]
pub enum Value<'vm> { pub enum Value<'gc> {
Const(Const), Int(i64),
Thunk(Rc<Thunk<'vm>>), Float(f64),
ThunkRef(&'vm Thunk<'vm>), Bool(bool),
AttrSet(Rc<AttrSet<'vm>>), String(Rc<String>),
List(Rc<List<'vm>>), Null,
Catchable(c::Catchable), Thunk(Thunk<'gc>),
PrimOp(Rc<PrimOp<'vm>>), AttrSet(Rc<AttrSet<'gc>>),
PartialPrimOp(Rc<PartialPrimOp<'vm>>), List(Rc<List<'gc>>),
Func(Rc<Func<'vm>>), Catchable(Rc<String>),
Builtins, PrimOp(Rc<PrimOp>),
PartialPrimOp(Rc<PartialPrimOp<'gc>>),
Func(Rc<Func<'gc>>),
} }
impl Hash for Value<'_> { impl Hash for Value<'_> {
@@ -45,29 +46,37 @@ impl Hash for Value<'_> {
use Value::*; use Value::*;
std::mem::discriminant(self).hash(state); std::mem::discriminant(self).hash(state);
match self { match self {
Const(x) => x.hash(state), AttrSet(x) => Rc::as_ptr(x).hash(state),
Thunk(x) => (x.as_ref() as *const self::Thunk).hash(state), List(x) => Rc::as_ptr(x).hash(state),
ThunkRef(x) => (*x as *const self::Thunk).hash(state), _ => 0.hash(state),
AttrSet(x) => (x.as_ref() as *const self::AttrSet).hash(state),
List(x) => (x.as_ref() as *const self::List).hash(state),
Catchable(x) => x.hash(state),
PrimOp(x) => (x.as_ref() as *const self::PrimOp).hash(state),
PartialPrimOp(x) => (x.as_ref() as *const self::PartialPrimOp).hash(state),
Func(x) => (x.as_ref() as *const self::Func).hash(state),
Builtins => (),
} }
} }
} }
impl<'vm> Value<'vm> { impl<'gc> Value<'gc> {
fn eq_impl(&self, other: &Self, vm: &'vm VM<'_>) -> bool { fn eq_impl(&self, other: &Self) -> bool {
use Value::*; use Value::*;
match (self, other) { match (self, other) {
(Const(a), Const(b)) => a.eq(b), (Bool(a), Bool(b)) => a == b,
(AttrSet(a), AttrSet(b)) => a.eq_impl(b, vm), (Int(a), Int(b)) => a == b,
(Float(a), Float(b)) => a == b,
(Int(a), Float(b)) => *a as f64 == *b,
(Float(a), Int(b)) => *b as f64 == *a,
(String(a), String(b)) => a.as_str().eq(b.as_str()),
(Null, Null) => true,
(AttrSet(a), AttrSet(b)) => a.eq_impl(b),
(List(a), List(b)) => a.eq(b), (List(a), List(b)) => a.eq(b),
(Builtins, AttrSet(attrs)) => attrs.has_attr(vm.new_sym("builtins")), _ => false,
(AttrSet(attrs), Builtins) => attrs.has_attr(vm.new_sym("builtins")), }
}
}
impl<'gc> PartialEq for Value<'gc> {
fn eq(&self, other: &Self) -> bool {
use Value::*;
match (self, other) {
(AttrSet(a), AttrSet(b)) => Rc::as_ptr(a).eq(&Rc::as_ptr(b)),
(List(a), List(b)) => Rc::as_ptr(a).eq(&Rc::as_ptr(b)),
_ => false, _ => false,
} }
} }
@@ -75,68 +84,43 @@ impl<'vm> Value<'vm> {
impl Eq for Value<'_> {} impl Eq for Value<'_> {}
#[derive(Debug, IsVariant, Unwrap, Clone, PartialEq)] #[derive(IsVariant, Unwrap, Clone)]
pub enum ValueAsRef<'v, 'vm: 'v> { pub enum ValueAsRef<'v, 'gc> {
Const(&'v Const), Int(i64),
Thunk(&'v Thunk<'vm>), Float(f64),
AttrSet(&'v AttrSet<'vm>), Bool(bool),
List(&'v List<'vm>), String(&'v str),
Catchable(&'v c::Catchable), Null,
PrimOp(&'v PrimOp<'vm>), Thunk(&'v Thunk<'gc>),
PartialPrimOp(&'v PartialPrimOp<'vm>), AttrSet(&'v AttrSet<'gc>),
Func(&'v Func<'vm>), List(&'v List<'gc>),
Catchable(&'v str),
PrimOp(&'v PrimOp),
PartialPrimOp(&'v PartialPrimOp<'gc>),
Func(&'v Func<'gc>),
} }
#[derive(Debug, IsVariant, Unwrap, PartialEq)] impl<'gc, 'v> Value<'gc> {
pub enum ValueAsMut<'v, 'vm: 'v> { pub fn as_ref(&'v self) -> ValueAsRef<'v, 'gc> {
Const(&'v mut Const),
Thunk(&'v Thunk<'vm>),
AttrSet(&'v mut AttrSet<'vm>),
List(&'v mut List<'vm>),
Catchable(&'v mut c::Catchable),
PrimOp(&'v mut PrimOp<'vm>),
PartialPrimOp(&'v mut PartialPrimOp<'vm>),
Func(&'v Func<'vm>),
}
impl<'v, 'vm: 'v> Value<'vm> {
pub fn as_ref(&'v self) -> ValueAsRef<'v, 'vm> {
use Value::*; use Value::*;
use ValueAsRef as R; use ValueAsRef as R;
match self { match self {
Const(x) => R::Const(x), Int(x) => R::Int(*x),
Float(x) => R::Float(*x),
Bool(x) => R::Bool(*x),
String(x) => R::String(x),
Null => R::Null,
Thunk(x) => R::Thunk(x), Thunk(x) => R::Thunk(x),
ThunkRef(x) => R::Thunk(x),
AttrSet(x) => R::AttrSet(x), AttrSet(x) => R::AttrSet(x),
List(x) => R::List(x), List(x) => R::List(x),
Catchable(x) => R::Catchable(x), Catchable(x) => R::Catchable(x),
PrimOp(x) => R::PrimOp(x), PrimOp(x) => R::PrimOp(x),
PartialPrimOp(x) => R::PartialPrimOp(x), PartialPrimOp(x) => R::PartialPrimOp(x),
Func(x) => R::Func(x), Func(x) => R::Func(x),
Builtins => unreachable!(),
}
}
pub fn as_mut(&'v mut self) -> ValueAsMut<'v, 'vm> {
use Value::*;
use ValueAsMut as M;
match self {
Const(x) => M::Const(x),
Thunk(x) => M::Thunk(x),
ThunkRef(x) => M::Thunk(x),
AttrSet(x) => M::AttrSet(Rc::make_mut(x)),
List(x) => M::List(Rc::make_mut(x)),
Catchable(x) => M::Catchable(x),
PrimOp(x) => M::PrimOp(Rc::make_mut(x)),
PartialPrimOp(x) => M::PartialPrimOp(Rc::make_mut(x)),
Func(x) => M::Func(x),
Builtins => unreachable!(),
} }
} }
} }
impl<'gc> Value<'gc> {
use Value::Const as VmConst;
impl<'vm> Value<'vm> {
pub fn ok(self) -> Result<Self> { pub fn ok(self) -> Result<Self> {
Ok(self) Ok(self)
} }
@@ -144,16 +128,18 @@ impl<'vm> Value<'vm> {
pub fn typename(&self) -> &'static str { pub fn typename(&self) -> &'static str {
use Value::*; use Value::*;
match self { match self {
Const(_) => unreachable!(), Int(_) => "int",
Float(_) => "float",
Bool(_) => "bool",
String(_) => "string",
Null => "null",
Thunk(_) => "thunk", Thunk(_) => "thunk",
ThunkRef(_) => "thunk",
AttrSet(_) => "set", AttrSet(_) => "set",
List(_) => "list", List(_) => "list",
Catchable(_) => unreachable!(), Catchable(_) => unreachable!(),
PrimOp(_) => "lambda", PrimOp(_) => "lambda",
PartialPrimOp(_) => "lambda", PartialPrimOp(_) => "lambda",
Func(_) => "lambda", Func(_) => "lambda",
Builtins => "set",
} }
} }
@@ -165,139 +151,139 @@ impl<'vm> Value<'vm> {
} }
} }
pub fn call(&self, vm: &'vm VM<'_>, args: Vec<Self>) -> Result<Self> { pub fn call(&mut self, arg: Self, vm: &VM) -> Result<()> {
use Value::*; use Value::*;
match self { if matches!(arg, Value::Catchable(_)) {
PrimOp(func) => func.call(vm, args), *self = arg;
PartialPrimOp(func) => func.call(vm, args), return Ok(());
func @ Value::Func(_) => {
let mut iter = args.into_iter();
let mut func = func.clone();
while let Some(arg) = iter.next() {
func = match func {
PrimOp(func) => {
return func.call(vm, [arg].into_iter().chain(iter).collect());
} }
PartialPrimOp(func) => { *self = match self {
return func.call(vm, [arg].into_iter().chain(iter).collect()); PrimOp(func) => func.call(arg, vm),
} PartialPrimOp(func) => func.call(arg, vm),
Func(func) => func.call(vm, arg)?, Catchable(_) => return Ok(()),
_ => todo!(), _ => todo!(),
}?;
Ok(())
} }
}
func.ok() pub fn not(&mut self) {
} use Value::*;
x @ Catchable(_) => x.clone().ok(), *self = match &*self {
Bool(bool) => Bool(!bool),
Value::Catchable(_) => return,
_ => todo!(), _ => todo!(),
} }
} }
pub fn not(self) -> Self { pub fn and(&mut self, other: Self) {
use Const::*; use Value::*;
match self { *self = match (&*self, other) {
VmConst(Bool(bool)) => VmConst(Bool(!bool)), (Bool(a), Bool(b)) => Bool(*a && b),
x @ Value::Catchable(_) => x, (Value::Catchable(_), _) => return,
(_, x @ Value::Catchable(_)) => x,
_ => todo!(), _ => todo!(),
} }
} }
pub fn and(self, other: Self) -> Self { pub fn or(&mut self, other: Self) {
use Const::*; use Value::*;
match (self, other) { *self = match (&*self, other) {
(VmConst(Bool(a)), VmConst(Bool(b))) => VmConst(Bool(a && b)), (Bool(a), Bool(b)) => Bool(*a || b),
(x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x, (Value::Catchable(_), _) => return,
(_, x @ Value::Catchable(_)) => x,
_ => todo!(), _ => todo!(),
} }
} }
pub fn or(self, other: Self) -> Self { pub fn eq(&mut self, other: Self) {
use Const::*; use Value::Bool;
match (self, other) { *self = match (&*self, other) {
(VmConst(Bool(a)), VmConst(Bool(b))) => VmConst(Bool(a || b)), (Value::Catchable(_), _) => return,
(x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x, (_, x @ Value::Catchable(_)) => x,
_ => todo!(), (s, other) => Bool(s.eq_impl(&other)),
} };
} }
pub fn eq(self, other: Self, vm: &'vm VM<'_>) -> Self { pub fn lt(&mut self, other: Self) {
use Const::Bool; use Value::*;
match (self, other) { *self = Bool(match (&*self, other) {
(x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x, (Int(a), Int(b)) => *a < b,
(s, other) => VmConst(Bool(s.eq_impl(&other, vm))), (Int(a), Float(b)) => (*a as f64) < b,
(Float(a), Int(b)) => *a < b as f64,
(Float(a), Float(b)) => *a < b,
(String(a), String(b)) => a.as_str() < b.as_str(),
(Value::Catchable(_), _) => return,
(_, x @ Value::Catchable(_)) => {
*self = x;
return;
} }
}
pub fn lt(self, other: Self) -> Self {
use Const::*;
VmConst(Bool(match (self, other) {
(VmConst(Int(a)), VmConst(Int(b))) => a < b,
(VmConst(Int(a)), VmConst(Float(b))) => (a as f64) < b,
(VmConst(Float(a)), VmConst(Int(b))) => a < b as f64,
(VmConst(Float(a)), VmConst(Float(b))) => a < b,
(VmConst(String(a)), VmConst(String(b))) => a < b,
(x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => return x,
_ => todo!(),
}))
}
pub fn neg(self) -> Self {
use Const::*;
match self {
VmConst(Int(int)) => VmConst(Int(-int)),
VmConst(Float(float)) => VmConst(Float(-float)),
x @ Value::Catchable(_) => x,
_ => todo!(),
}
}
pub fn add(self, other: Self) -> Self {
use Const::*;
match (self, other) {
(VmConst(Int(a)), VmConst(Int(b))) => VmConst(Int(a + b)),
(VmConst(Int(a)), VmConst(Float(b))) => VmConst(Float(a as f64 + b)),
(VmConst(Float(a)), VmConst(Int(b))) => VmConst(Float(a + b as f64)),
(VmConst(Float(a)), VmConst(Float(b))) => VmConst(Float(a + b)),
(VmConst(String(a)), VmConst(String(b))) => {
let mut string = a.clone();
string.push_str(b.as_str());
VmConst(String(string))
}
(x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x,
_ => todo!(),
}
}
pub fn mul(self, other: Self) -> Self {
use Const::*;
match (self, other) {
(VmConst(Int(a)), VmConst(Int(b))) => VmConst(Int(a * b)),
(VmConst(Int(a)), VmConst(Float(b))) => VmConst(Float(a as f64 * b)),
(VmConst(Float(a)), VmConst(Int(b))) => VmConst(Float(a * b as f64)),
(VmConst(Float(a)), VmConst(Float(b))) => VmConst(Float(a * b)),
(x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x,
_ => todo!(),
}
}
pub fn div(self, other: Self) -> Result<Self> {
use Const::*;
Ok(match (self, other) {
(_, VmConst(Int(0))) => return Err(Error::EvalError("division by zero".to_string())),
(_, VmConst(Float(0.))) => {
return Err(Error::EvalError("division by zero".to_string()));
}
(VmConst(Int(a)), VmConst(Int(b))) => VmConst(Int(a / b)),
(VmConst(Int(a)), VmConst(Float(b))) => VmConst(Float(a as f64 / b)),
(VmConst(Float(a)), VmConst(Int(b))) => VmConst(Float(a / b as f64)),
(VmConst(Float(a)), VmConst(Float(b))) => VmConst(Float(a / b)),
(x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x,
_ => todo!(), _ => todo!(),
}) })
} }
pub fn neg(&mut self) {
use Value::*;
*self = match &*self {
Int(int) => Int(-int),
Float(float) => Float(-float),
Value::Catchable(_) => return,
_ => todo!(),
}
}
pub fn add(&mut self, other: Self) {
use Value::*;
replace_with_or_abort(self, |a| match (a, other) {
(Int(a), Int(b)) => Int(a + b),
(Int(a), Float(b)) => Float(a as f64 + b),
(Float(a), Int(b)) => Float(a + b as f64),
(Float(a), Float(b)) => Float(a + b),
(String(mut a), String(b)) => {
Rc::make_mut(&mut a).push_str(&b);
String(a)
}
(a @ Value::Catchable(_), _) => a,
(_, x @ Value::Catchable(_)) => x,
_ => todo!(),
});
}
pub fn mul(&mut self, other: Self) {
use Value::*;
*self = match (&*self, other) {
(Int(a), Int(b)) => Int(a * b),
(Int(a), Float(b)) => Float(*a as f64 * b),
(Float(a), Int(b)) => Float(a * b as f64),
(Float(a), Float(b)) => Float(a * b),
(Value::Catchable(_), _) => return,
(_, x @ Value::Catchable(_)) => x,
_ => todo!(),
}
}
pub fn div(&mut self, other: Self) -> Result<()> {
use Value::*;
*self = match (&*self, other) {
(_, Int(0)) => return Err(Error::EvalError("division by zero".to_string())),
(_, Float(0.)) => {
return Err(Error::EvalError("division by zero".to_string()));
}
(Int(a), Int(b)) => Int(a / b),
(Int(a), Float(b)) => Float(*a as f64 / b),
(Float(a), Int(b)) => Float(a / b as f64),
(Float(a), Float(b)) => Float(a / b),
(Value::Catchable(_), _) => return Ok(()),
(_, x @ Value::Catchable(_)) => x,
_ => todo!(),
};
Ok(())
}
pub fn concat_string(&mut self, mut other: Self) -> &mut Self { pub fn concat_string(&mut self, mut other: Self) -> &mut Self {
match (self.coerce_to_string(), other.coerce_to_string()) { match (self.coerce_to_string(), other.coerce_to_string()) {
(VmConst(Const::String(a)), VmConst(Const::String(b))) => a.push_str(b.as_str()), (Value::String(a), Value::String(b)) => {
Rc::make_mut(a).push_str(b.as_str());
}
(_, Value::Catchable(_)) => *self = other, (_, Value::Catchable(_)) => *self = other,
(Value::Catchable(_), _) => (), (Value::Catchable(_), _) => (),
_ => todo!(), _ => todo!(),
@@ -317,20 +303,23 @@ impl<'vm> Value<'vm> {
self self
} }
pub fn concat(self, other: Self) -> Self { pub fn concat(&mut self, other: Self) {
match (self, other) { if let x @ Value::Catchable(_) = other {
(Value::List(mut a), Value::List(b)) => { *self = x;
Rc::make_mut(&mut a).concat(b.as_ref()); return;
Value::List(a)
} }
(x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x, match (self, other) {
(Value::List(a), Value::List(b)) => {
Rc::make_mut(a).concat(&b);
}
(Value::Catchable(_), _) => (),
_ => todo!(), _ => todo!(),
} }
} }
pub fn push_attr(&mut self, sym: usize, val: Self) -> &mut Self { pub fn push_attr(&mut self, sym: usize, val: Self) -> &mut Self {
if let Value::AttrSet(attrs) = self { if let Value::AttrSet(attrs) = self {
Rc::make_mut(attrs).push_attr(sym, val) Rc::make_mut(attrs).push_attr(sym, val);
} else if let Value::Catchable(_) = self { } else if let Value::Catchable(_) = self {
} else if let Value::Catchable(_) = val { } else if let Value::Catchable(_) = val {
*self = val *self = val
@@ -340,18 +329,21 @@ impl<'vm> Value<'vm> {
self self
} }
pub fn update(self, other: Self) -> Self { pub fn update(&mut self, other: Self) {
match (self, other) { if let x @ Value::Catchable(_) = other {
(Value::AttrSet(mut a), Value::AttrSet(b)) => { *self = x;
Rc::make_mut(&mut a).update(b.as_ref()); return;
Value::AttrSet(a)
} }
(x @ Value::Catchable(_), _) | (_, x @ Value::Catchable(_)) => x, match (self, other) {
(Value::AttrSet(a), Value::AttrSet(b)) => {
Rc::make_mut(a).update(&b);
}
(Value::Catchable(_), _) => (),
_ => todo!(), _ => todo!(),
} }
} }
pub fn select(&mut self, sym: usize, vm: &'vm VM<'_>) -> Result<&mut Self> { pub fn select(&mut self, sym: usize, vm: &VM<'gc>) -> Result<&mut Self> {
let val = match self { let val = match self {
Value::AttrSet(attrs) => attrs Value::AttrSet(attrs) => attrs
.select(sym) .select(sym)
@@ -362,10 +354,7 @@ impl<'vm> Value<'vm> {
self.typename() self.typename()
))), ))),
}?; }?;
if let Value::Builtins = val {
} else {
*self = val; *self = val;
}
Ok(self) Ok(self)
} }
@@ -380,26 +369,23 @@ impl<'vm> Value<'vm> {
))); )));
} }
}; };
if let Value::Builtins = val {
} else {
*self = val; *self = val;
}
Ok(self) Ok(self)
} }
pub fn has_attr(&mut self, sym: usize) -> &mut Self { pub fn has_attr(&mut self, sym: usize) -> &mut Self {
if let Value::AttrSet(attrs) = self { if let Value::AttrSet(attrs) = self {
let val = VmConst(Const::Bool(attrs.has_attr(sym))); let val = Value::Bool(attrs.has_attr(sym));
*self = val; *self = val;
} else if let Value::Catchable(_) = self { } else if let Value::Catchable(_) = self {
} else { } else {
*self = VmConst(Const::Bool(false)); *self = Value::Bool(false);
} }
self self
} }
pub fn coerce_to_string(&mut self) -> &mut Self { pub fn coerce_to_string(&mut self) -> &mut Self {
if let VmConst(Const::String(_)) = self { if let Value::String(_) = self {
} else if let Value::Catchable(_) = self { } else if let Value::Catchable(_) = self {
} else { } else {
todo!() todo!()
@@ -407,111 +393,110 @@ impl<'vm> Value<'vm> {
self self
} }
pub fn force(&mut self, vm: &'vm VM<'_>) -> Result<&mut Self> { pub fn to_public(&self, vm: &VM<'gc>, seen: &mut HashSet<Value<'gc>>) -> p::Value {
if let Value::Thunk(thunk) = self {
let value = thunk.force(vm)?;
*self = value
} else if let Value::ThunkRef(thunk) = self {
let value = thunk.force(vm)?;
*self = value
}
Ok(self)
}
pub fn force_deep(&mut self, vm: &'vm VM<'_>) -> Result<&mut Self> {
match self {
Value::Thunk(thunk) => {
let mut value = thunk.force(vm)?;
let _ = value.force_deep(vm)?;
*self = value;
}
Value::ThunkRef(thunk) => {
let mut value = thunk.force(vm)?;
let _ = value.force_deep(vm)?;
*self = value;
}
Value::List(list) => Rc::make_mut(list).force_deep(vm)?,
Value::AttrSet(attrs) => Rc::make_mut(attrs).force_deep(vm)?,
_ => (),
}
Ok(self)
}
pub fn to_public(&self, vm: &'vm VM, seen: &mut HashSet<Value<'vm>>) -> p::Value {
use self::Value::*; use self::Value::*;
use p::Value; use p::Value;
if seen.contains(self) { if seen.contains(self) {
return Value::Repeated; return Value::Repeated;
} }
seen.insert(self.clone());
match self { match self {
AttrSet(attrs) => attrs.to_public(vm, seen), AttrSet(attrs) => {
List(list) => list.to_public(vm, seen), seen.insert(self.clone());
Catchable(catchable) => Value::Catchable(catchable.clone()), attrs.to_public(vm, seen)
Const(cnst) => Value::Const(cnst.clone().into()), }
List(list) => {
seen.insert(self.clone());
list.to_public(vm, seen)
}
Catchable(catchable) => Value::Catchable(catchable.as_ref().clone().into()),
Int(x) => Value::Const(Const::Int(*x)),
Float(x) => Value::Const(Const::Float(*x)),
Bool(x) => Value::Const(Const::Bool(*x)),
String(x) => Value::Const(Const::String(x.as_str().into())),
Null => Value::Const(Const::Null),
Thunk(_) => Value::Thunk, Thunk(_) => Value::Thunk,
ThunkRef(_) => Value::Thunk,
PrimOp(primop) => Value::PrimOp(primop.name), PrimOp(primop) => Value::PrimOp(primop.name),
PartialPrimOp(primop) => Value::PartialPrimOp(primop.name), PartialPrimOp(primop) => Value::PartialPrimOp(primop.name),
Func(_) => Value::Func, Func(_) => Value::Func,
Builtins => Value::Repeated,
} }
} }
} }
#[derive(Debug, Clone)] #[repr(transparent)]
pub struct Thunk<'vm> { #[derive(Clone)]
pub thunk: RefCell<_Thunk<'vm>>, pub struct Thunk<'gc> {
pub thunk: Rc<RefCell<_Thunk<'gc>>>,
} }
#[derive(Debug, IsVariant, Unwrap, Clone)] #[derive(IsVariant, Unwrap)]
pub enum _Thunk<'vm> { pub enum _Thunk<'gc> {
Code(&'vm OpCodes, OnceCell<Env<'vm>>), Code(&'gc OpCodes, Option<Env<'gc>>),
SuspendedFrom(*const Thunk<'vm>), Suspended,
Value(Value<'vm>), Value(Value<'gc>),
} }
impl<'vm> Thunk<'vm> { #[derive(Unwrap)]
pub fn new(opcodes: &'vm OpCodes) -> Self { pub enum Env<'gc> {
Strong(Rc<VmEnv<'gc>>),
Weak(Weak<VmEnv<'gc>>),
}
impl Env<'_> {
fn upgrade(self) -> Self {
if let Self::Weak(weak) = self {
Env::Strong(weak.upgrade().unwrap())
} else {
self
}
}
}
impl<'gc> Thunk<'gc> {
pub fn new(opcodes: &'gc OpCodes) -> Self {
Thunk { Thunk {
thunk: RefCell::new(_Thunk::Code(opcodes, OnceCell::new())), thunk: RefCell::new(_Thunk::Code(opcodes, None)).into(),
} }
} }
pub fn capture(&self, env: Env<'vm>) { pub fn capture_env(&self, env: Rc<VmEnv<'gc>>) {
if let _Thunk::Code(_, envcell) = &*self.thunk.borrow() { if let _Thunk::Code(_, envcell) = &mut *self.thunk.borrow_mut() {
envcell.get_or_init(|| env); let _ = envcell.insert(Env::Strong(env));
} }
} }
pub fn force(&self, vm: &'vm VM<'_>) -> Result<Value<'vm>> { pub fn capture_env_weak(&self, env: Weak<VmEnv<'gc>>) {
match &*self.thunk.borrow() { if let _Thunk::Code(_, envcell) = &mut *self.thunk.borrow_mut() {
_Thunk::Value(value) => return Ok(value.clone()), let _ = envcell.insert(Env::Weak(env));
_Thunk::SuspendedFrom(from) => {
return Err(Error::EvalError(format!(
"thunk {:p} already suspended from {from:p} (infinite recursion encountered)",
self as *const Thunk
)));
} }
_Thunk::Code(..) => (),
}
let (opcodes, env) = std::mem::replace(
&mut *self.thunk.borrow_mut(),
_Thunk::SuspendedFrom(self as *const Thunk),
)
.unwrap_code();
let value = vm.eval(opcodes.iter().copied(), env.get().unwrap().clone())?;
let _ = std::mem::replace(
&mut *self.thunk.borrow_mut(),
_Thunk::Value(value.clone().into()),
);
Ok(value)
} }
pub fn value(&'vm self) -> Option<Value<'vm>> { pub fn upgrade(&self) {
match &*self.thunk.borrow() { replace_with_or_abort(&mut *self.thunk.borrow_mut(), |this| {
_Thunk::Value(value) => Some(value.clone()), if let _Thunk::Code(opcodes, envcell) = this {
_ => None, _Thunk::Code(opcodes, envcell.map(Env::upgrade))
} else {
this
}
});
}
pub fn suspend(&self) -> Result<(&'gc OpCodes, Rc<VmEnv<'gc>>)> {
use _Thunk::*;
match std::mem::replace(&mut *self.thunk.borrow_mut(), _Thunk::Suspended) {
Code(opcodes, env) => Ok((opcodes, env.unwrap().upgrade().unwrap_strong())),
_ => Err(Error::EvalError("infinite recursion encountered".into())),
}
}
pub fn insert_value(&self, value: Value<'gc>) {
*self.thunk.borrow_mut() = _Thunk::Value(value);
}
pub fn get_value(&self) -> Option<&Value<'gc>> {
if let _Thunk::Value(val) = unsafe { &*self.thunk.as_ptr() } {
Some(val)
} else {
None
} }
} }
} }

View File

@@ -8,45 +8,42 @@ use crate::vm::VM;
use super::Value; use super::Value;
#[derive(Debug, Clone, Constructor)] #[derive(Debug, Clone, Constructor)]
pub struct PrimOp<'vm> { pub struct PrimOp {
pub name: &'static str, pub name: &'static str,
arity: usize, arity: usize,
func: fn(&'vm VM<'_>, Vec<Value<'vm>>) -> Result<Value<'vm>>, func: for<'gc> fn(Vec<Value<'gc>>, &VM) -> Result<Value<'gc>>,
} }
impl PartialEq for PrimOp<'_> { impl PartialEq for PrimOp {
fn eq(&self, _: &Self) -> bool { fn eq(&self, _: &Self) -> bool {
false false
} }
} }
impl<'vm> PrimOp<'vm> { impl PrimOp {
pub fn call(&self, vm: &'vm VM<'_>, args: Vec<Value<'vm>>) -> Result<Value<'vm>> { pub fn call<'gc>(&self, arg: Value<'gc>, vm: &VM) -> Result<Value<'gc>> {
if (args.len()) < self.arity { let mut args = Vec::with_capacity(self.arity);
Value::PartialPrimOp( args.push(arg);
PartialPrimOp { if self.arity > 1 {
Value::PartialPrimOp(Rc::new(PartialPrimOp {
name: self.name, name: self.name,
arity: self.arity - args.len(), arity: self.arity - 1,
args, args,
func: self.func, func: self.func,
} }))
.into(),
)
.ok() .ok()
} else if args.len() == self.arity {
(self.func)(vm, args)
} else { } else {
unimplemented!() (self.func)(args, vm)
} }
} }
} }
#[derive(Debug, Clone)] #[derive(Clone)]
pub struct PartialPrimOp<'vm> { pub struct PartialPrimOp<'gc> {
pub name: &'static str, pub name: &'static str,
arity: usize, arity: usize,
args: Vec<Value<'vm>>, args: Vec<Value<'gc>>,
func: fn(&'vm VM<'_>, Vec<Value<'vm>>) -> Result<Value<'vm>>, func: fn(Vec<Value<'gc>>, &VM) -> Result<Value<'gc>>,
} }
impl PartialEq for PartialPrimOp<'_> { impl PartialEq for PartialPrimOp<'_> {
@@ -55,20 +52,21 @@ impl PartialEq for PartialPrimOp<'_> {
} }
} }
impl<'vm> PartialPrimOp<'vm> { impl<'gc> PartialPrimOp<'gc> {
pub fn call(self: &Rc<Self>, vm: &'vm VM<'_>, args: Vec<Value<'vm>>) -> Result<Value<'vm>> { pub fn call(self: &mut Rc<Self>, arg: Value<'gc>, vm: &VM) -> Result<Value<'gc>> {
let len = args.len(); let func = self.func;
let mut self_clone = self.clone(); let Some(ret) = ({
let self_mut = Rc::make_mut(&mut self_clone); let self_mut = Rc::make_mut(self);
self_mut.args.extend(args); self_mut.args.push(arg);
self_mut.arity -= len; self_mut.arity -= 1;
if self_mut.arity > 0 { if self_mut.arity == 0 {
Value::PartialPrimOp(self_clone).ok() Some(func(std::mem::take(&mut self_mut.args), vm))
} else if self_mut.arity == 0 {
let args = std::mem::replace(&mut self_mut.args, Vec::new());
(self.func)(vm, args)
} else { } else {
unimplemented!() None
} }
}) else {
return Value::PartialPrimOp(self.clone()).ok();
};
ret
} }
} }

View File

@@ -1,16 +1,15 @@
// TODO: Contextful String // TODO: Contextful String
use ecow::EcoString; use ecow::EcoString;
use rpds::List;
pub struct StringContext { pub struct StringContext {
context: List<()>, context: Vec<()>,
} }
impl StringContext { impl StringContext {
pub fn new() -> StringContext { pub fn new() -> StringContext {
StringContext { StringContext {
context: List::new(), context: Vec::new(),
} }
} }
} }

View File

@@ -6,14 +6,9 @@ use std::sync::LazyLock;
use derive_more::{Constructor, IsVariant, Unwrap}; use derive_more::{Constructor, IsVariant, Unwrap};
use ecow::EcoString; use ecow::EcoString;
use regex::Regex; use regex::Regex;
use rpds::VectorSync;
use super::common::*; use super::common::*;
mod cnst;
pub use cnst::Const;
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Constructor)] #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Constructor)]
pub struct Symbol(EcoString); pub struct Symbol(EcoString);
@@ -80,9 +75,16 @@ impl Debug for AttrSet {
impl Display for AttrSet { impl Display for AttrSet {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
use Value::*;
write!(f, "{{ ")?; write!(f, "{{ ")?;
for (k, v) in self.data.iter() { for (k, v) in self.data.iter() {
write!(f, "{k} = {v}; ")?; write!(f, "{k} = ")?;
match v {
AttrSet(_) => write!(f, "{{ ... }}"),
List(_) => write!(f, "[ ... ]"),
v => write!(f, "{v}"),
}?;
write!(f, "; ")?;
} }
write!(f, "}}") write!(f, "}}")
} }
@@ -90,7 +92,7 @@ impl Display for AttrSet {
#[derive(Constructor, Clone, Debug, PartialEq)] #[derive(Constructor, Clone, Debug, PartialEq)]
pub struct List { pub struct List {
data: VectorSync<Value>, data: Vec<Value>,
} }
impl Display for List { impl Display for List {

View File

@@ -1,137 +0,0 @@
use std::fmt::{Display, Formatter, Result as FmtResult};
use derive_more::{IsVariant, Unwrap};
use ecow::EcoString;
use crate::error::Error;
use super::super::internal as i;
#[derive(Debug, Clone, IsVariant, Unwrap)]
pub enum Const {
Bool(bool),
Int(i64),
Float(f64),
String(EcoString),
Null,
}
impl Display for Const {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
use Const::*;
match self {
Bool(b) => write!(f, "{b}"),
Int(i) => write!(f, "{i}"),
Float(float) => write!(f, "{float}"),
String(s) => write!(f, "{s}"),
Null => write!(f, "null"),
}
}
}
impl From<i::Const> for Const {
fn from(value: i::Const) -> Self {
use i::Const::*;
match value {
Bool(bool) => Const::Bool(bool),
Int(int) => Const::Int(int),
Float(float) => Const::Float(float),
String(string) => Const::String(string),
Null => Const::Null,
}
}
}
impl From<bool> for Const {
fn from(value: bool) -> Self {
Const::Bool(value)
}
}
impl From<i64> for Const {
fn from(value: i64) -> Self {
Const::Int(value)
}
}
impl From<f64> for Const {
fn from(value: f64) -> Self {
Const::Float(value)
}
}
impl From<EcoString> for Const {
fn from(value: EcoString) -> Self {
Const::String(value)
}
}
impl From<String> for Const {
fn from(value: String) -> Self {
Const::String(value.into())
}
}
impl From<&str> for Const {
fn from(value: &str) -> Self {
Const::String(value.into())
}
}
impl<'a> TryFrom<&'a Const> for &'a bool {
type Error = Error;
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
match value {
Const::Bool(b) => Ok(b),
_ => panic!(),
}
}
}
impl<'a> TryFrom<&'a Const> for &'a i64 {
type Error = Error;
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
match value {
Const::Int(int) => Ok(int),
_ => panic!(),
}
}
}
impl<'a> TryFrom<&'a Const> for &'a f64 {
type Error = Error;
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
match value {
Const::Float(float) => Ok(float),
_ => panic!(),
}
}
}
impl<'a> TryFrom<&'a Const> for &'a str {
type Error = Error;
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
match value {
Const::String(string) => Ok(string),
_ => panic!(),
}
}
}
impl PartialEq for Const {
fn eq(&self, other: &Self) -> bool {
use Const::*;
match (self, other) {
(Bool(a), Bool(b)) => a == b,
(Int(a), Int(b)) => a == b,
(Float(a), Float(b)) => a == b,
(String(a), String(b)) => a == b,
_ => false,
}
}
}
impl Eq for Const {}

View File

@@ -1,69 +0,0 @@
use hashbrown::HashMap;
use std::rc::Rc;
use crate::ty::internal::{AttrSet, Value};
#[derive(Debug, Default, Clone)]
pub struct Env<'vm> {
last: Option<Rc<Env<'vm>>>,
map: Rc<HashMap<usize, Value<'vm>>>,
}
impl<'vm> Env<'vm> {
pub fn empty() -> Self {
Env::default()
}
pub fn lookup(&self, symbol: usize) -> Option<Value<'vm>> {
if let Some(val) = self.map.get(&symbol).cloned() {
return Some(val);
}
self.last.as_ref().map(|env| env.lookup(symbol)).flatten()
}
pub fn insert(&mut self, symbol: usize, value: Value<'vm>) {
Rc::make_mut(&mut self.map).insert(symbol, value);
}
pub fn enter(self, new: impl Iterator<Item = (usize, Value<'vm>)>) -> Self {
let map = Rc::new(new.collect());
let last = Some(
Env {
last: self.last,
map: self.map,
}
.into(),
);
Env { last, map }
}
pub fn enter_with(self, new: Rc<AttrSet<'vm>>) -> Self {
let map = Rc::new(
new.as_inner()
.iter()
.map(|(&k, v)| {
(
k,
if let Value::Builtins = v {
Value::AttrSet(new.clone())
} else {
v.clone()
},
)
})
.collect(),
);
let last = Some(
Env {
last: self.last.clone(),
map: self.map.clone(),
}
.into(),
);
Env { last, map }
}
pub fn leave(self) -> Self {
self.last.unwrap().as_ref().clone()
}
}

View File

@@ -1,78 +0,0 @@
use std::pin::Pin;
use inkwell::builder::Builder;
use inkwell::context::Context;
use inkwell::execution_engine::ExecutionEngine;
use inkwell::module::Module;
use inkwell::types::{BasicType, BasicTypeEnum, FunctionType, StructType};
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue};
use inkwell::{AddressSpace, OptimizationLevel};
use crate::stack::Stack;
use super::STACK_SIZE;
#[repr(usize)]
pub enum ValueTag {
Int,
String,
Bool,
AttrSet,
List,
Function,
Thunk,
Path,
}
#[repr(C)]
pub struct JITValue {
tag: ValueTag,
data: u64,
}
pub struct JITContext<'ctx> {
context: &'ctx Context,
module: Module<'ctx>,
builder: Builder<'ctx>,
execution_engine: ExecutionEngine<'ctx>,
stack: Stack<BasicValueEnum<'ctx>, STACK_SIZE>,
cur_func: Option<FunctionValue<'ctx>>,
value_type: StructType<'ctx>,
func_type: FunctionType<'ctx>,
}
impl<'ctx> JITContext<'ctx> {
pub fn new(context: &'ctx Context) -> Pin<Box<Self>> {
let module = context.create_module("nixjit");
let stack = Stack::new();
let int_type = context.i64_type();
let pointer_type = context.ptr_type(AddressSpace::default());
let value_type = context.struct_type(&[int_type.into(), int_type.into()], false);
let func_type = value_type.fn_type(
&[pointer_type.into(), pointer_type.into(), value_type.into()],
false,
);
Pin::new(Box::new(JITContext {
execution_engine: module
.create_jit_execution_engine(OptimizationLevel::Default)
.unwrap(),
builder: context.create_builder(),
context,
module,
stack,
cur_func: None,
value_type,
func_type,
}))
}
fn new_int(&self, int: i64) -> IntValue {
self.context.i64_type().const_int(int as u64, false)
}
pub fn start_trace(&mut self) {}
}

View File

@@ -1,69 +1,309 @@
use hashbrown::{HashMap, HashSet};
use std::cell::RefCell; use std::cell::RefCell;
use std::pin::Pin; use std::rc::Rc;
use hashbrown::{HashMap, HashSet};
use inkwell::context::Context;
use crate::builtins::env;
use crate::bytecode::{BinOp, Func as F, OpCode, OpCodes, Program, UnOp}; use crate::bytecode::{BinOp, Func as F, OpCode, OpCodes, Program, UnOp};
use crate::env::VmEnv;
use crate::error::*; use crate::error::*;
use crate::jit::{JITContext, JITFunc};
use crate::stack::Stack;
use crate::ty::common::Const;
use crate::ty::internal::*; use crate::ty::internal::*;
use crate::ty::public::{self as p, Symbol}; use crate::ty::public::{self as p, Symbol};
use crate::stack::Stack;
use derive_more::Constructor; use derive_more::Constructor;
use ecow::EcoString; use ecow::EcoString;
pub use env::Env;
pub use jit::JITContext;
mod env;
mod jit;
#[cfg(test)] #[cfg(test)]
mod test; mod test;
pub const STACK_SIZE: usize = 8 * 1024 / size_of::<Value>(); const STACK_SIZE: usize = 8 * 1024 / size_of::<Value>();
const COLLECTOR_GRANULARITY: f64 = 1024.0;
pub fn run(prog: Program, jit: Pin<Box<JITContext<'_>>>) -> Result<p::Value> { struct ContextWrapper(Context);
let vm = VM::new(
prog.thunks, pub fn run(mut prog: Program) -> Result<p::Value> {
prog.funcs, let jit = Context::create();
RefCell::new(prog.symbols), prog.thunks.iter_mut().for_each(|code| code.reverse());
RefCell::new(prog.symmap), prog.funcs
prog.consts, .iter_mut()
jit, .for_each(|F { opcodes, .. }| opcodes.reverse());
); let vm = VM {
let env = env(&vm); thunks: prog.thunks,
let mut seen = HashSet::new(); funcs: prog.funcs,
let value = vm consts: prog.consts,
.eval(prog.top_level.into_iter(), env)? symbols: RefCell::new(prog.symbols),
.to_public(&vm, &mut seen); symmap: RefCell::new(prog.symmap),
Ok(value) jit: JITContext::new(&jit),
};
prog.top_level.reverse();
Ok(eval(prog.top_level, &vm, VmEnv::new(vec![]))?.to_public(&vm, &mut HashSet::new()))
}
pub fn eval<'gc>(opcodes: Box<[OpCode]>, vm: &VM<'gc>, env: Rc<VmEnv<'gc>>) -> Result<Value<'gc>> {
let mut opcodes = opcodes.into_vec();
let mut envs = vec![env];
let mut stack = Stack::<_, STACK_SIZE>::new();
while let Some(opcode) = opcodes.pop() {
let consq = single_op(vm, opcode, &mut stack, envs.last_mut().unwrap())?;
match consq {
Consq::NoOp => (),
Consq::Jmp(step) => opcodes.resize_with(opcodes.len() - step, || unreachable!()),
Consq::Force => {
let thunk = stack.tos().as_ref().unwrap_thunk();
let (code, env) = thunk.suspend()?;
opcodes.push(OpCode::InsertValue);
opcodes.push(OpCode::PopEnv);
opcodes.extend(code);
envs.push(env);
}
Consq::PopEnv => {
let _ = envs.pop().unwrap();
}
Consq::Call => {
let arg = stack.pop();
let func = stack.pop().unwrap_func();
let env = func.env.clone().enter_arg(arg);
let count = func.count.get();
func.count.set(count + 1);
if count > 1 {
let compiled = func.compiled.get_or_init(|| vm.compile_func(func.func));
let ret = unsafe { compiled(env.as_ref() as *const VmEnv) };
stack.push(ret.into())?;
} else {
envs.push(env);
opcodes.push(OpCode::PopEnv);
opcodes.extend(&func.func.opcodes);
}
}
}
}
stack.pop().ok()
}
enum Consq {
Jmp(usize),
Call,
Force,
PopEnv,
NoOp,
}
#[inline(always)]
fn single_op<'gc, const CAP: usize>(
vm: &VM<'gc>,
opcode: OpCode,
stack: &mut Stack<Value<'gc>, CAP>,
env: &mut Rc<VmEnv<'gc>>,
) -> Result<Consq> {
match opcode {
OpCode::Illegal => panic!("illegal opcode"),
OpCode::Const { idx } => stack.push(match vm.get_const(idx) {
Const::Int(x) => Value::Int(x),
Const::Float(x) => Value::Float(x),
Const::Bool(x) => Value::Bool(x),
Const::String(x) => Value::String(Rc::new(x.into())),
Const::Null => Value::Null,
})?,
OpCode::LoadThunk { idx } => stack.push(Value::Thunk(Thunk::new(vm.get_thunk(idx))))?,
OpCode::LoadValue { idx } => {
stack.push(Value::Thunk(Thunk::new(vm.get_thunk(idx))))?;
stack.tos().as_ref().unwrap_thunk().capture_env(env.clone());
return Ok(Consq::Force);
}
OpCode::CaptureEnv => stack.tos().as_ref().unwrap_thunk().capture_env(env.clone()),
OpCode::ForceValue => {
if !stack.tos().is_thunk() {
return Ok(Consq::NoOp);
}
let Some(val) = stack.tos().as_ref().unwrap_thunk().get_value() else {
return Ok(Consq::Force);
};
*stack.tos_mut() = val.clone();
}
OpCode::InsertValue => {
let val = stack.pop();
stack
.tos()
.as_ref()
.unwrap_thunk()
.insert_value(val.clone());
*stack.tos_mut() = val;
}
OpCode::Jmp { step } => return Ok(Consq::Jmp(step)),
OpCode::JmpIfFalse { step } => {
if let Value::Bool(false) = stack.pop() {
return Ok(Consq::Jmp(step));
}
}
OpCode::Call => {
let arg = stack.pop();
let func = stack.tos_mut();
if func.is_func() {
let _ = stack.push(arg);
return Ok(Consq::Call);
}
func.call(arg, vm)?;
}
OpCode::Func { idx } => {
let func = vm.get_func(idx);
stack.push(Value::Func(Rc::new(Func::new(func, env.clone()))))?;
}
OpCode::Arg { level } => {
stack.push(env.lookup_arg(level).clone())?;
}
OpCode::UnOp { op } => {
use UnOp::*;
let value = stack.tos_mut();
match op {
Neg => value.neg(),
Not => value.not(),
}
}
OpCode::BinOp { op } => {
use BinOp::*;
let mut rhs = stack.pop();
let lhs = stack.tos_mut();
match op {
Add => lhs.add(rhs),
Sub => {
rhs.neg();
lhs.add(rhs);
}
Mul => lhs.mul(rhs),
Div => lhs.div(rhs)?,
And => lhs.and(rhs),
Or => lhs.or(rhs),
Eq => Value::eq(lhs, rhs),
Lt => lhs.lt(rhs),
Con => lhs.concat(rhs),
Upd => lhs.update(rhs),
}
}
OpCode::ConcatString => {
let rhs = stack.pop();
stack.tos_mut().concat_string(rhs);
}
OpCode::Path => {
todo!()
}
OpCode::List { cap } => {
stack.push(Value::List(Rc::new(List::with_capacity(cap))))?;
}
OpCode::PushElem => {
let elem = stack.pop();
stack.tos_mut().push(elem);
}
OpCode::AttrSet { cap } => {
stack.push(Value::AttrSet(Rc::new(AttrSet::with_capacity(cap))))?;
}
OpCode::FinalizeLet => {
let mut list = stack.pop().unwrap_list();
let map = list.as_ref().clone().into_inner();
*env = env.clone().enter_let(map);
Rc::make_mut(&mut list).capture(&Rc::downgrade(env));
}
OpCode::PushStaticAttr { name } => {
let val = stack.pop();
stack.tos_mut().push_attr(name, val);
}
OpCode::PushDynamicAttr => {
let val = stack.pop();
let sym = stack.pop();
let sym = vm.new_sym::<&str>(&sym.unwrap_string());
stack.tos_mut().push_attr(sym, val);
}
OpCode::Select { sym } => {
stack.tos_mut().select(sym, vm)?;
}
OpCode::SelectOrDefault { sym } => {
let default = stack.pop();
stack.tos_mut().select_with_default(sym, default)?;
}
OpCode::SelectDynamic => {
let mut val = stack.pop();
val.coerce_to_string();
let sym = vm.new_sym::<&str>(&val.unwrap_string());
stack.tos_mut().select(sym, vm)?;
}
OpCode::SelectDynamicOrDefault => {
let default = stack.pop();
let mut val = stack.pop();
val.coerce_to_string();
let sym = vm.new_sym::<&str>(&val.unwrap_string());
stack.tos_mut().select_with_default(sym, default)?;
}
OpCode::HasAttr { sym } => {
stack.tos_mut().has_attr(sym);
}
OpCode::HasDynamicAttr => {
let mut val = stack.pop();
val.coerce_to_string();
let sym = vm.new_sym::<&str>(&val.unwrap_string());
stack.tos_mut().has_attr(sym);
}
OpCode::LookUp { sym } => {
stack.push(
env.lookup_with(&sym)
.ok_or_else(|| Error::EvalError(format!("{} not found", vm.get_sym(sym))))?
.clone(),
)?;
}
OpCode::LookUpLet { level, idx } => {
let val = env.lookup_let(level, idx);
if let Value::Thunk(thunk) = val {
thunk.upgrade();
}
stack.push(val.clone())?;
}
OpCode::LeaveEnv => *env = env.leave(),
OpCode::EnterWithEnv => {
let mut new = HashMap::new();
stack
.pop()
.unwrap_attr_set()
.as_inner()
.iter()
.map(|(&k, v)| (k, v.clone()))
.collect_into(&mut new);
*env = env.clone().enter_with(new.into());
}
OpCode::PopEnv => return Ok(Consq::PopEnv),
OpCode::Assert => {
if !stack.pop().unwrap_bool() {
todo!()
}
}
}
Ok(Consq::NoOp)
} }
#[derive(Constructor)] #[derive(Constructor)]
pub struct VM<'jit> { pub struct VM<'gc> {
thunks: Box<[OpCodes]>, thunks: Box<[OpCodes]>,
funcs: Box<[F]>, funcs: Box<[F]>,
symbols: RefCell<Vec<EcoString>>, symbols: RefCell<Vec<EcoString>>,
symmap: RefCell<HashMap<EcoString, usize>>, symmap: RefCell<HashMap<EcoString, usize>>,
consts: Box<[Const]>, consts: Box<[Const]>,
jit: Pin<Box<JITContext<'jit>>>, jit: JITContext<'gc>,
} }
impl<'vm, 'jit: 'vm> VM<'jit> { impl<'gc> VM<'gc> {
pub fn get_thunk(&self, idx: usize) -> &OpCodes { pub fn get_thunk(&self, idx: usize) -> &'gc OpCodes {
&self.thunks[idx] unsafe { &*(&self.thunks[idx] as *const _) }
} }
pub fn get_func(&self, idx: usize) -> &F { pub fn get_func(&self, idx: usize) -> &'gc F {
&self.funcs[idx] unsafe { &*(&self.funcs[idx] as *const _) }
} }
pub fn get_sym(&self, idx: usize) -> Symbol { pub fn get_sym(&self, idx: usize) -> Symbol {
self.symbols.borrow()[idx].clone().into() self.symbols.borrow()[idx].clone().into()
} }
pub fn new_sym(&self, sym: impl Into<EcoString>) -> usize { pub fn new_sym<T: Into<EcoString>>(&self, sym: T) -> usize {
let sym = sym.into(); let sym = sym.into();
if let Some(&idx) = self.symmap.borrow().get(&sym) { if let Some(&idx) = self.symmap.borrow().get(&sym) {
idx idx
@@ -76,192 +316,13 @@ impl<'vm, 'jit: 'vm> VM<'jit> {
} }
} }
pub fn eval( pub fn get_const(&self, idx: usize) -> Const {
&'vm self, self.consts[idx].clone()
opcodes: impl Iterator<Item = OpCode>,
mut env: Env<'vm>,
) -> Result<Value<'vm>> {
let mut stack = Stack::<_, STACK_SIZE>::new();
let mut iter = opcodes.into_iter();
while let Some(opcode) = iter.next() {
let jmp = self.single_op(opcode, &mut stack, &mut env)?;
for _ in 0..jmp {
iter.next().unwrap();
}
}
assert_eq!(stack.len(), 1);
let mut ret = stack.pop();
ret.force(self)?;
Ok(ret)
} }
#[inline(always)] pub fn compile_func(&self, func: &'gc F) -> JITFunc<'gc> {
fn single_op<'s, const CAP: usize>( self.jit
&'vm self, .compile_seq(func.opcodes.iter().copied().rev(), self)
opcode: OpCode, .unwrap()
stack: &'s mut Stack<Value<'vm>, CAP>,
env: &mut Env<'vm>,
) -> Result<usize> {
match opcode {
OpCode::Illegal => panic!("illegal opcode"),
OpCode::Const { idx } => stack.push(Value::Const(self.consts[idx].clone()))?,
OpCode::LoadThunk { idx } => {
stack.push(Value::Thunk(Thunk::new(self.get_thunk(idx)).into()))?
}
OpCode::CaptureEnv => match stack.tos()? {
Value::Thunk(thunk) => thunk.capture(env.clone()),
_ => (),
},
OpCode::ForceValue => {
stack.tos_mut()?.force(self)?;
}
OpCode::Jmp { step } => return Ok(step),
OpCode::JmpIfTrue { step } => {
if let Value::Const(Const::Bool(true)) = stack.pop() {
return Ok(step);
}
}
OpCode::JmpIfFalse { step } => {
if let Value::Const(Const::Bool(false)) = stack.pop() {
return Ok(step);
}
}
OpCode::Call { arity } => {
let mut args = Vec::with_capacity(arity);
for _ in 0..arity {
args.insert(0, stack.pop());
}
let mut func = stack.pop();
func.force(self)?;
stack.push(func.call(self, args)?)?;
}
OpCode::Func { idx } => {
let func = self.get_func(idx);
stack.push(Value::Func(Func::new(func, env.clone(), None).into()))?;
}
OpCode::UnOp { op } => {
use UnOp::*;
let mut value = stack.pop();
value.force(self)?;
stack.push(match op {
Neg => value.neg(),
Not => value.not(),
})?;
}
OpCode::BinOp { op } => {
use BinOp::*;
let mut rhs = stack.pop();
let mut lhs = stack.pop();
lhs.force(self)?;
rhs.force(self)?;
stack.push(match op {
Add => lhs.add(rhs),
Sub => lhs.add(rhs.neg()),
Mul => lhs.mul(rhs),
Div => lhs.div(rhs)?,
And => lhs.and(rhs),
Or => lhs.or(rhs),
Eq => lhs.eq(rhs, self),
Lt => lhs.lt(rhs),
Con => lhs.concat(rhs),
Upd => lhs.update(rhs),
})?;
}
OpCode::ConcatString => {
let mut rhs = stack.pop();
rhs.force(self)?;
stack.tos_mut()?.concat_string(rhs);
}
OpCode::Path => {
todo!()
}
OpCode::List => {
stack.push(Value::List(List::empty().into()))?;
}
OpCode::PushElem => {
let elem = stack.pop();
stack.tos_mut()?.push(elem);
}
OpCode::AttrSet { cap } => {
stack.push(Value::AttrSet(AttrSet::with_capacity(cap).into()))?;
}
OpCode::FinalizeRec => {
let env = env.clone().enter(
stack
.tos()?
.clone()
.unwrap_attr_set()
.as_inner()
.iter()
.map(|(k, v)| (k.clone(), v.clone())),
);
stack.tos_mut()?.as_mut().unwrap_attr_set().capture(&env);
}
OpCode::PushStaticAttr { name } => {
let val = stack.pop();
stack.tos_mut()?.push_attr(name, val);
}
OpCode::PushDynamicAttr => {
let val = stack.pop();
let mut sym = stack.pop();
sym.force(self)?.coerce_to_string();
let sym = self.new_sym(sym.unwrap_const().unwrap_string());
stack.tos_mut()?.push_attr(sym, val);
}
OpCode::Select { sym } => {
stack.tos_mut()?.force(self)?.select(sym, self)?;
}
OpCode::SelectOrDefault { sym } => {
let default = stack.pop();
stack
.tos_mut()?
.force(self)?
.select_with_default(sym, default)?;
}
OpCode::SelectDynamic => {
let mut val = stack.pop();
val.force(self)?;
val.coerce_to_string();
let sym = self.new_sym(val.unwrap_const().unwrap_string());
stack.tos_mut()?.force(self)?.select(sym, self)?;
}
OpCode::SelectDynamicOrDefault => {
let default = stack.pop();
let mut val = stack.pop();
val.force(self)?;
val.coerce_to_string();
let sym = self.new_sym(val.unwrap_const().unwrap_string());
stack
.tos_mut()?
.force(self)?
.select_with_default(sym, default)?;
}
OpCode::HasAttr { sym } => {
stack.tos_mut()?.force(self)?.has_attr(sym);
}
OpCode::HasDynamicAttr => {
let mut val = stack.pop();
val.coerce_to_string();
let sym = self.new_sym(val.unwrap_const().unwrap_string());
stack.tos_mut()?.force(self)?.has_attr(sym);
}
OpCode::LookUp { sym } => {
stack.push(env.lookup(sym).ok_or_else(|| {
Error::EvalError(format!("{} not found", self.get_sym(sym)))
})?)?;
}
OpCode::EnterEnv => match stack.pop() {
Value::AttrSet(attrs) => *env = env.clone().enter_with(attrs),
_ => unreachable!(),
},
OpCode::LeaveEnv => *env = env.clone().leave(),
OpCode::Assert => {
if !stack.pop().unwrap_const().unwrap_bool() {
todo!()
}
}
}
Ok(0)
} }
} }

View File

@@ -1,18 +1,17 @@
#![allow(unused_macros)]
extern crate test; extern crate test;
use hashbrown::HashMap; use hashbrown::HashMap;
use inkwell::context::Context;
use test::{Bencher, black_box}; use test::{Bencher, black_box};
use ecow::EcoString; use ecow::EcoString;
use rpds::vector_sync;
use crate::compile::compile; use crate::compile::compile;
use crate::ir::downgrade; use crate::ir::downgrade;
use crate::ty::public::Symbol; use crate::ty::common::Const;
use crate::ty::public::*; use crate::ty::public::*;
use crate::vm::JITContext;
use super::run; use super::run;
@@ -21,9 +20,7 @@ fn test_expr(expr: &str, expected: Value) {
let downgraded = downgrade(rnix::Root::parse(expr).tree().expr().unwrap()).unwrap(); let downgraded = downgrade(rnix::Root::parse(expr).tree().expr().unwrap()).unwrap();
let prog = compile(downgraded); let prog = compile(downgraded);
dbg!(&prog); dbg!(&prog);
let ctx = Context::create(); assert_eq!(run(prog).unwrap(), expected);
let jit = JITContext::new(&ctx);
assert_eq!(run(prog, jit).unwrap(), expected);
} }
macro_rules! map { macro_rules! map {
@@ -77,7 +74,7 @@ macro_rules! symbol {
macro_rules! list { macro_rules! list {
($($x:tt)*) => ( ($($x:tt)*) => (
Value::List(List::new(vector_sync![$($x)*])) Value::List(List::new(vec![$($x)*]))
); );
} }
@@ -225,9 +222,10 @@ fn test_fib() {
#[bench] #[bench]
fn bench_fib(b: &mut Bencher) { fn bench_fib(b: &mut Bencher) {
b.iter(|| { b.iter(|| {
black_box(test_expr( test_expr(
"let fib = n: if n == 1 || n == 2 then 1 else (fib (n - 1)) + (fib (n - 2)); in fib 20", "let fib = n: if n == 1 || n == 2 then 1 else (fib (n - 1)) + (fib (n - 2)); in fib 30",
int!(6765), int!(832040),
)) );
black_box(())
}) })
} }