feat: gc (does compile, but WIP)
This commit is contained in:
@@ -6,6 +6,7 @@ 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};
|
||||
@@ -22,6 +23,8 @@ pub struct Helpers<'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>,
|
||||
@@ -48,6 +51,12 @@ impl<'ctx> Helpers<'ctx> {
|
||||
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(), ptr_type.into()], false);
|
||||
|
||||
let new_thunk = module.add_function(
|
||||
"new_thunk",
|
||||
value_type.fn_type(&[ptr_type.into(), ptr_type.into()], false),
|
||||
None,
|
||||
);
|
||||
let debug = module.add_function(
|
||||
"debug",
|
||||
context.void_type().fn_type(&[value_type.into()], false),
|
||||
@@ -55,9 +64,10 @@ impl<'ctx> Helpers<'ctx> {
|
||||
);
|
||||
let capture_env = module.add_function(
|
||||
"capture_env",
|
||||
context
|
||||
.void_type()
|
||||
.fn_type(&[value_type.into(), ptr_type.into(), ptr_type.into()], false),
|
||||
context.void_type().fn_type(
|
||||
&[value_type.into(), ptr_type.into(), ptr_type.into()],
|
||||
false,
|
||||
),
|
||||
None,
|
||||
);
|
||||
let neg = module.add_function(
|
||||
@@ -109,6 +119,7 @@ impl<'ctx> Helpers<'ctx> {
|
||||
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 _);
|
||||
@@ -130,6 +141,8 @@ impl<'ctx> Helpers<'ctx> {
|
||||
value_type,
|
||||
func_type,
|
||||
|
||||
new_thunk,
|
||||
|
||||
debug,
|
||||
capture_env,
|
||||
neg,
|
||||
@@ -188,25 +201,20 @@ impl<'ctx> Helpers<'ctx> {
|
||||
])
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn new_thunk(&self, thunk: *const Thunk) -> BasicValueEnum<'ctx> {
|
||||
self.value_type
|
||||
.const_named_struct(&[
|
||||
self.int_type.const_int(ValueTag::Thunk as _, false).into(),
|
||||
self.ptr_int_type.const_int(thunk 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>, mc: *const Mutation<'gc>) {
|
||||
extern "C" fn helper_capture_env<'gc>(
|
||||
thunk: JITValue,
|
||||
env: *const VmEnv<'gc>,
|
||||
mc: *const Mutation<'gc>,
|
||||
) {
|
||||
let thunk = unsafe { (thunk.data.ptr as *const Thunk).as_ref().unwrap() };
|
||||
let env = unsafe { Gc::from_ptr(env) };
|
||||
thunk.capture(env, unsafe { mc.as_ref() }.unwrap());
|
||||
thunk.capture_env(env, unsafe { mc.as_ref() }.unwrap());
|
||||
}
|
||||
|
||||
extern "C" fn helper_neg(rhs: JITValue, _env: *const VmEnv) -> JITValue {
|
||||
@@ -309,13 +317,22 @@ extern "C" fn helper_or(lhs: JITValue, rhs: JITValue) -> JITValue {
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn helper_call(func: JITValue, arg: JITValue, vm: *const VM, mc: *const Mutation) -> JITValue {
|
||||
extern "C" fn helper_call(
|
||||
func: JITValue,
|
||||
arg: JITValue,
|
||||
vm: *const VM,
|
||||
mc: *const Mutation,
|
||||
) -> JITValue {
|
||||
use ValueTag::*;
|
||||
match func.tag {
|
||||
Function => {
|
||||
let mut func: Value = func.into();
|
||||
func.call(arg.into(), unsafe { vm.as_ref() }.unwrap(), unsafe { mc.as_ref() }.unwrap())
|
||||
.unwrap();
|
||||
func.call(
|
||||
arg.into(),
|
||||
unsafe { vm.as_ref() }.unwrap(),
|
||||
unsafe { mc.as_ref() }.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
func.into()
|
||||
}
|
||||
_ => todo!(),
|
||||
@@ -329,7 +346,20 @@ extern "C" fn helper_lookup(sym: usize, env: *const VmEnv) -> JITValue {
|
||||
}
|
||||
|
||||
extern "C" fn helper_force(thunk: JITValue, vm: *const VM, mc: *const Mutation) -> JITValue {
|
||||
let mut val = Value::from(thunk);
|
||||
val.force(unsafe { vm.as_ref() }.unwrap(), unsafe { mc.as_ref() }.unwrap()).unwrap();
|
||||
val.into()
|
||||
todo!()
|
||||
/* let mut val = Value::from(thunk);
|
||||
val.force(
|
||||
unsafe { vm.as_ref() }.unwrap(),
|
||||
unsafe { mc.as_ref() }.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
val.into() */
|
||||
}
|
||||
|
||||
extern "C" fn helper_new_thunk(opcodes: *const OpCodes, mc: *const Mutation) -> JITValue {
|
||||
Value::Thunk(Thunk::new(
|
||||
unsafe { opcodes.as_ref() }.unwrap(),
|
||||
unsafe { mc.as_ref() }.unwrap(),
|
||||
))
|
||||
.into()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::ops::Deref;
|
||||
|
||||
use gc_arena::{Collect, Gc};
|
||||
use gc_arena::{Collect, Gc, Mutation};
|
||||
use inkwell::OptimizationLevel;
|
||||
use inkwell::builder::Builder;
|
||||
use inkwell::context::Context;
|
||||
@@ -60,10 +60,12 @@ impl<'gc> From<JITValue> for Value<'gc> {
|
||||
fn from(value: JITValue) -> Self {
|
||||
use ValueTag::*;
|
||||
match value.tag {
|
||||
Int => Value::Const(Const::Int(unsafe { value.data.int })),
|
||||
Null => Value::Const(Const::Null),
|
||||
Int => Value::Int(unsafe { value.data.int }),
|
||||
Null => Value::Null,
|
||||
Function => Value::Func(unsafe { Gc::from_ptr(value.data.ptr as *const _) }),
|
||||
Thunk => Value::Thunk(self::Thunk { thunk: unsafe { Gc::from_ptr(value.data.ptr as *const _) } }),
|
||||
Thunk => Value::Thunk(self::Thunk {
|
||||
thunk: unsafe { Gc::from_ptr(value.data.ptr as *const _) },
|
||||
}),
|
||||
_ => todo!("not implemented for {:?}", value.tag),
|
||||
}
|
||||
}
|
||||
@@ -72,7 +74,7 @@ impl<'gc> From<JITValue> for Value<'gc> {
|
||||
impl From<&Value<'_>> for JITValue {
|
||||
fn from(value: &Value<'_>) -> Self {
|
||||
match *value {
|
||||
Value::Const(Const::Int(int)) => JITValue {
|
||||
Value::Int(int) => JITValue {
|
||||
tag: ValueTag::Int,
|
||||
data: JITValueData { int },
|
||||
},
|
||||
@@ -96,7 +98,7 @@ impl From<&Value<'_>> for JITValue {
|
||||
impl From<Value<'_>> for JITValue {
|
||||
fn from(value: Value) -> Self {
|
||||
match value {
|
||||
Value::Const(Const::Int(int)) => JITValue {
|
||||
Value::Int(int) => JITValue {
|
||||
tag: ValueTag::Int,
|
||||
data: JITValueData { int },
|
||||
},
|
||||
@@ -117,18 +119,33 @@ impl From<Value<'_>> for JITValue {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Collect)]
|
||||
#[collect(require_static)]
|
||||
pub struct JITFunc<'gc>(JitFunction<'gc, unsafe extern "C" fn(*const VM<'gc>, *const VmEnv<'gc>) -> JITValue>);
|
||||
pub struct JITFunc<'gc>(
|
||||
JitFunction<'gc, unsafe extern "C" fn(*const VmEnv<'gc>, *const Mutation<'gc>) -> JITValue>,
|
||||
);
|
||||
|
||||
impl<'gc> From<JitFunction<'gc, unsafe extern "C" fn(*const VM<'gc>, *const VmEnv<'gc>) -> JITValue>> for JITFunc<'gc> {
|
||||
fn from(value: JitFunction<'gc, unsafe extern "C" fn(*const VM<'gc>, *const VmEnv<'gc>) -> JITValue>) -> Self {
|
||||
unsafe impl<'gc> Collect<'gc> for JITFunc<'gc> {
|
||||
fn trace<T: gc_arena::collect::Trace<'gc>>(&self, _: &mut T) {}
|
||||
const NEEDS_TRACE: bool = false;
|
||||
}
|
||||
|
||||
impl<'gc>
|
||||
From<
|
||||
JitFunction<'gc, unsafe extern "C" fn(*const VmEnv<'gc>, *const Mutation<'gc>) -> JITValue>,
|
||||
> for JITFunc<'gc>
|
||||
{
|
||||
fn from(
|
||||
value: JitFunction<
|
||||
'gc,
|
||||
unsafe extern "C" fn(*const VmEnv<'gc>, *const Mutation<'gc>) -> JITValue,
|
||||
>,
|
||||
) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> Deref for JITFunc<'gc> {
|
||||
type Target = JitFunction<'gc, unsafe extern "C" fn(*const VM<'gc>, *const VmEnv<'gc>) -> JITValue>;
|
||||
type Target =
|
||||
JitFunction<'gc, unsafe extern "C" fn(*const VmEnv<'gc>, *const Mutation<'gc>) -> JITValue>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
@@ -143,15 +160,10 @@ pub struct JITContext<'gc> {
|
||||
helpers: Helpers<'gc>,
|
||||
}
|
||||
|
||||
unsafe impl<'gc> Collect for JITContext<'gc> {
|
||||
fn trace(&self, _cc: &gc_arena::Collection) {}
|
||||
fn needs_trace() -> bool
|
||||
where
|
||||
Self: Sized, {
|
||||
false
|
||||
}
|
||||
unsafe impl<'gc> Collect<'gc> for JITContext<'gc> {
|
||||
fn trace<T: gc_arena::collect::Trace<'gc>>(&self, _: &mut T) {}
|
||||
const NEEDS_TRACE: bool = false;
|
||||
}
|
||||
|
||||
impl<'gc> JITContext<'gc> {
|
||||
pub fn new(context: &'gc Context) -> Self {
|
||||
// force linker to link JIT engine
|
||||
@@ -184,20 +196,25 @@ impl<'gc> JITContext<'gc> {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn compile_function(
|
||||
&self,
|
||||
func: &'gc Func,
|
||||
vm: &'gc VM<'gc>,
|
||||
) -> Result<JITFunc<'gc>> {
|
||||
pub fn compile_function(&self, func: &'gc Func, vm: &'gc VM<'gc>) -> Result<JITFunc<'gc>> {
|
||||
let mut stack = Stack::<_, STACK_SIZE>::new();
|
||||
let mut iter = func.opcodes.iter().copied();
|
||||
let func_ = self
|
||||
.module
|
||||
.add_function("nixjit_function", self.helpers.func_type, None);
|
||||
let env = func_.get_nth_param(1).unwrap().into_pointer_value();
|
||||
let env = func_.get_nth_param(0).unwrap().into_pointer_value();
|
||||
let mc = func_.get_nth_param(1).unwrap().into_pointer_value();
|
||||
let entry = self.context.append_basic_block(func_, "entry");
|
||||
self.builder.position_at_end(entry);
|
||||
self.build_expr(&mut iter, vm, env, &mut stack, func_, func.opcodes.len())?;
|
||||
self.build_expr(
|
||||
&mut iter,
|
||||
vm,
|
||||
env,
|
||||
mc,
|
||||
&mut stack,
|
||||
func_,
|
||||
func.opcodes.len(),
|
||||
)?;
|
||||
|
||||
assert_eq!(stack.len(), 1);
|
||||
let value = stack.pop();
|
||||
@@ -221,13 +238,14 @@ impl<'gc> JITContext<'gc> {
|
||||
iter: &mut impl Iterator<Item = OpCode>,
|
||||
vm: &'gc VM<'gc>,
|
||||
env: PointerValue<'gc>,
|
||||
mc: 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)?;
|
||||
let br = self.single_op(opcode, vm, env, mc, stack)?;
|
||||
length -= 1;
|
||||
if br > 0 {
|
||||
let consq = self.context.append_basic_block(func, "consq");
|
||||
@@ -259,13 +277,13 @@ impl<'gc> JITContext<'gc> {
|
||||
|
||||
length -= br;
|
||||
self.builder.position_at_end(consq);
|
||||
let br = self.build_expr(iter, vm, env, stack, func, br)?;
|
||||
let br = self.build_expr(iter, vm, env, mc, 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.build_expr(iter, vm, env, mc, stack, func, br)?;
|
||||
self.builder.build_store(result, stack.pop())?;
|
||||
self.builder.build_unconditional_branch(cont)?;
|
||||
|
||||
@@ -278,7 +296,7 @@ impl<'gc> JITContext<'gc> {
|
||||
}
|
||||
}
|
||||
if length > 0 {
|
||||
self.single_op(iter.next().unwrap(), vm, env, stack)
|
||||
self.single_op(iter.next().unwrap(), vm, env, mc, stack)
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
@@ -290,6 +308,7 @@ impl<'gc> JITContext<'gc> {
|
||||
opcode: OpCode,
|
||||
vm: &'gc VM<'_>,
|
||||
env: PointerValue<'gc>,
|
||||
mc: PointerValue<'gc>,
|
||||
stack: &mut Stack<BasicValueEnum<'gc>, CAP>,
|
||||
) -> Result<usize> {
|
||||
match opcode {
|
||||
@@ -304,8 +323,18 @@ impl<'gc> JITContext<'gc> {
|
||||
}
|
||||
}
|
||||
OpCode::LoadThunk { idx } => stack.push(
|
||||
self.helpers
|
||||
.new_thunk(Thunk::new(vm.get_thunk(idx))),
|
||||
self.builder
|
||||
.build_direct_call(
|
||||
self.helpers.new_thunk,
|
||||
&[
|
||||
self.new_ptr(vm.get_thunk(idx) as *const _).into(),
|
||||
mc.into(),
|
||||
],
|
||||
"call_capture_env",
|
||||
)?
|
||||
.try_as_basic_value()
|
||||
.unwrap_left()
|
||||
.into(),
|
||||
)?,
|
||||
OpCode::CaptureEnv => {
|
||||
let thunk = *stack.tos();
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
extern crate test;
|
||||
|
||||
use bumpalo::Bump;
|
||||
use gc_arena::Arena;
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
|
||||
@@ -17,29 +16,14 @@ use crate::ir::downgrade;
|
||||
use crate::jit::JITContext;
|
||||
use crate::ty::common::Const;
|
||||
use crate::ty::public::*;
|
||||
use crate::vm::VM;
|
||||
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);
|
||||
let ctx = Context::create();
|
||||
let jit = JITContext::new(&ctx);
|
||||
let vm = VM::new(
|
||||
prog.thunks,
|
||||
prog.funcs,
|
||||
prog.symbols.into(),
|
||||
prog.symmap.into(),
|
||||
prog.consts,
|
||||
jit,
|
||||
);
|
||||
let env = env(&vm);
|
||||
let value = vm
|
||||
.eval(prog.top_level.into_iter(), vm.bump.alloc(env))
|
||||
.unwrap()
|
||||
.to_public(&vm, &mut HashSet::new());
|
||||
assert_eq!(value, expected);
|
||||
assert_eq!(run(prog).unwrap(), expected);
|
||||
}
|
||||
|
||||
macro_rules! map {
|
||||
|
||||
Reference in New Issue
Block a user