feat: TODO
This commit is contained in:
@@ -45,7 +45,7 @@ impl<Ctx: JITContext> JITCompile<Ctx> for AttrSet {
|
||||
/// This creates a new attribute set and compiles all static attributes into it.
|
||||
fn compile(&self, ctx: &mut Context<Ctx>, rt_ctx: ir::Value) -> StackSlot {
|
||||
let attrs = ctx.create_attrs();
|
||||
for (k, v) in self.stcs.iter() {
|
||||
for (&k, v) in self.stcs.iter() {
|
||||
let v = v.compile(ctx, rt_ctx);
|
||||
ctx.push_attr(attrs, k, v);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ use hashbrown::HashMap;
|
||||
|
||||
use nixjit_eval::{AttrSet, EvalContext, List, Value};
|
||||
use nixjit_ir::ExprId;
|
||||
use nixjit_ir::StackIdx;
|
||||
use nixjit_ir::SymId;
|
||||
|
||||
use super::JITContext;
|
||||
|
||||
@@ -23,20 +23,12 @@ pub extern "C" fn helper_call<Ctx: JITContext>(
|
||||
arg: NonNull<Value>,
|
||||
ctx: &mut Ctx,
|
||||
) {
|
||||
// SAFETY: The `arg` pointer is guaranteed to be valid and non-null by the JIT compiler,
|
||||
// which allocates it on the stack. The JIT code ensures that the pointer points to a
|
||||
// valid `Value` and that its lifetime is managed correctly within the compiled function.
|
||||
func.call(unsafe { arg.read() }, ctx).unwrap();
|
||||
}
|
||||
|
||||
/// Helper function to look up a value in the evaluation stack.
|
||||
///
|
||||
/// This function is called from JIT-compiled code to access values in the evaluation stack.
|
||||
pub extern "C" fn helper_lookup_stack<Ctx: JITContext + EvalContext>(
|
||||
ctx: &Ctx,
|
||||
idx: StackIdx,
|
||||
ret: &mut MaybeUninit<Value>,
|
||||
) {
|
||||
ret.write(ctx.lookup_stack(idx).clone());
|
||||
}
|
||||
|
||||
/// Helper function to look up a function argument.
|
||||
///
|
||||
/// This function is called from JIT-compiled code to access function arguments.
|
||||
@@ -56,6 +48,9 @@ pub extern "C" fn helper_lookup<Ctx: JITContext>(
|
||||
ret: &mut MaybeUninit<Value>,
|
||||
) {
|
||||
// TODO: Error Handling
|
||||
// SAFETY: The `sym_ptr` and `sym_len` are provided by the JIT compiler and are
|
||||
// guaranteed to form a valid UTF-8 string slice. The string data is embedded
|
||||
// in the compiled code and has a static lifetime, ensuring the pointer is always valid.
|
||||
unsafe {
|
||||
ret.write(
|
||||
ctx.lookup_with(str::from_utf8_unchecked(slice::from_raw_parts(
|
||||
@@ -78,6 +73,9 @@ pub extern "C" fn helper_select<Ctx: JITContext>(
|
||||
ctx: &mut Ctx,
|
||||
) {
|
||||
let path = core::ptr::slice_from_raw_parts_mut(path_ptr, path_len);
|
||||
// SAFETY: The `path_ptr` is allocated by the JIT compiler using `helper_alloc_array`
|
||||
// and is guaranteed to be valid for the given length. The `Box::from_raw` call
|
||||
// correctly takes ownership of the allocated slice, ensuring it is properly deallocated.
|
||||
let path = unsafe { Box::from_raw(path) };
|
||||
for attr in path {
|
||||
val.select(&attr.force_string_no_ctx().unwrap(), ctx)
|
||||
@@ -97,6 +95,9 @@ pub extern "C" fn helper_select_with_default<Ctx: JITContext>(
|
||||
ctx: &mut Ctx,
|
||||
) {
|
||||
let path = core::ptr::slice_from_raw_parts_mut(path_ptr, path_len);
|
||||
// SAFETY: The `path_ptr` is allocated by the JIT compiler using `helper_alloc_array`
|
||||
// and is guaranteed to be valid for the given length. The `Box::from_raw` call
|
||||
// correctly takes ownership of the allocated slice, ensuring it is properly deallocated.
|
||||
let path = unsafe { Box::from_raw(path) };
|
||||
for attr in path {
|
||||
val.select_or(&attr.force_string_no_ctx().unwrap(), default, ctx)
|
||||
@@ -107,7 +108,10 @@ pub extern "C" fn helper_select_with_default<Ctx: JITContext>(
|
||||
/// Helper function to check equality between two values.
|
||||
///
|
||||
/// This function is called from JIT-compiled code to perform equality comparisons.
|
||||
pub extern "C" fn helper_eq<Ctx: JITContext>(lhs: &mut Value, rhs: NonNull<Value>) {
|
||||
pub extern "C" fn helper_eq(lhs: &mut Value, rhs: NonNull<Value>) {
|
||||
// SAFETY: The `rhs` pointer is guaranteed to be valid and non-null by the JIT compiler,
|
||||
// which allocates it on the stack. The JIT code ensures that the pointer points to a
|
||||
// valid `Value` and that its lifetime is managed correctly within the compiled function.
|
||||
lhs.eq(unsafe { rhs.read() });
|
||||
}
|
||||
|
||||
@@ -115,11 +119,14 @@ pub extern "C" fn helper_eq<Ctx: JITContext>(lhs: &mut Value, rhs: NonNull<Value
|
||||
///
|
||||
/// This function is called from JIT-compiled code to create string values
|
||||
/// from raw byte arrays.
|
||||
pub unsafe extern "C" fn helper_create_string<Ctx: JITContext>(
|
||||
pub unsafe extern "C" fn helper_create_string(
|
||||
ptr: *const u8,
|
||||
len: usize,
|
||||
ret: &mut MaybeUninit<Value>,
|
||||
) {
|
||||
// SAFETY: The `ptr` and `len` are provided by the JIT compiler and are guaranteed
|
||||
// to form a valid UTF-8 string slice. The string data is embedded in the compiled
|
||||
// code and has a static lifetime, ensuring the pointer is always valid.
|
||||
unsafe {
|
||||
ret.write(Value::String(
|
||||
str::from_utf8_unchecked(slice::from_raw_parts(ptr, len)).to_owned(),
|
||||
@@ -131,11 +138,14 @@ pub unsafe extern "C" fn helper_create_string<Ctx: JITContext>(
|
||||
///
|
||||
/// This function is called from JIT-compiled code to create list values
|
||||
/// from arrays of values.
|
||||
pub unsafe extern "C" fn helper_create_list<Ctx: JITContext>(
|
||||
pub unsafe extern "C" fn helper_create_list(
|
||||
ptr: *mut Value,
|
||||
len: usize,
|
||||
ret: &mut MaybeUninit<Value>,
|
||||
) {
|
||||
// SAFETY: The `ptr` is allocated by the JIT compiler using `helper_alloc_array` and
|
||||
// is guaranteed to be valid for `len` elements. The `Vec::from_raw_parts` call
|
||||
// correctly takes ownership of the allocated memory, ensuring it is properly managed.
|
||||
unsafe {
|
||||
ret.write(Value::List(
|
||||
List::from(Vec::from_raw_parts(ptr, len, len)).into(),
|
||||
@@ -146,7 +156,7 @@ pub unsafe extern "C" fn helper_create_list<Ctx: JITContext>(
|
||||
/// Helper function to create an attribute set.
|
||||
///
|
||||
/// This function is called from JIT-compiled code to create a new, empty attribute set.
|
||||
pub unsafe extern "C" fn helper_create_attrs<Ctx: JITContext>(
|
||||
pub unsafe extern "C" fn helper_create_attrs(
|
||||
ret: &mut MaybeUninit<HashMap<String, Value>>,
|
||||
) {
|
||||
ret.write(HashMap::new());
|
||||
@@ -156,15 +166,17 @@ pub unsafe extern "C" fn helper_create_attrs<Ctx: JITContext>(
|
||||
///
|
||||
/// This function is called from JIT-compiled code to insert a key-value pair
|
||||
/// into an attribute set.
|
||||
pub unsafe extern "C" fn helper_push_attr<Ctx: JITContext>(
|
||||
attrs: &mut HashMap<String, Value>,
|
||||
sym_ptr: *const u8,
|
||||
sym_len: usize,
|
||||
pub unsafe extern "C" fn helper_push_attr(
|
||||
attrs: &mut HashMap<SymId, Value>,
|
||||
sym: SymId,
|
||||
val: NonNull<Value>,
|
||||
) {
|
||||
// SAFETY: The `sym_ptr` and `sym_len` are provided by the JIT compiler and are
|
||||
// guaranteed to form a valid UTF-8 string slice. The `val` pointer is also
|
||||
// guaranteed to be valid and non-null by the JIT compiler.
|
||||
unsafe {
|
||||
attrs.insert(
|
||||
str::from_utf8_unchecked(slice::from_raw_parts(sym_ptr, sym_len)).to_owned(),
|
||||
sym,
|
||||
val.read(),
|
||||
);
|
||||
}
|
||||
@@ -174,10 +186,13 @@ pub unsafe extern "C" fn helper_push_attr<Ctx: JITContext>(
|
||||
///
|
||||
/// This function is called from JIT-compiled code to convert a HashMap into
|
||||
/// a proper attribute set value.
|
||||
pub unsafe extern "C" fn helper_finalize_attrs<Ctx: JITContext>(
|
||||
pub unsafe extern "C" fn helper_finalize_attrs(
|
||||
attrs: NonNull<HashMap<String, Value>>,
|
||||
ret: &mut MaybeUninit<Value>,
|
||||
) {
|
||||
// SAFETY: The `attrs` pointer is guaranteed to be valid and non-null by the JIT
|
||||
// compiler, which allocates it on the stack. The `read` operation correctly
|
||||
// takes ownership of the HashMap.
|
||||
ret.write(Value::AttrSet(
|
||||
AttrSet::from(unsafe { attrs.read() }).into(),
|
||||
));
|
||||
@@ -191,6 +206,8 @@ pub unsafe extern "C" fn helper_enter_with<Ctx: JITContext>(
|
||||
ctx: &mut Ctx,
|
||||
namespace: NonNull<Value>,
|
||||
) {
|
||||
// SAFETY: The `namespace` pointer is guaranteed to be valid and non-null by the JIT
|
||||
// compiler. The `read` operation correctly takes ownership of the `Value`.
|
||||
ctx.enter_with(unsafe { namespace.read() }.unwrap_attr_set().into_inner());
|
||||
}
|
||||
|
||||
@@ -205,13 +222,16 @@ pub unsafe extern "C" fn helper_exit_with<Ctx: JITContext>(ctx: &mut Ctx) {
|
||||
///
|
||||
/// This function is called from JIT-compiled code to allocate memory for
|
||||
/// arrays of values, such as function arguments or list elements.
|
||||
pub unsafe extern "C" fn helper_alloc_array<Ctx: JITContext>(len: usize) -> *mut u8 {
|
||||
pub unsafe extern "C" fn helper_alloc_array(len: usize) -> *mut u8 {
|
||||
// SAFETY: The `Layout` is guaranteed to be valid for non-zero `len`. The caller
|
||||
// is responsible for deallocating the memory, which is typically done by
|
||||
// `Vec::from_raw_parts` or `Box::from_raw` in other helpers.
|
||||
unsafe { alloc(Layout::array::<Value>(len).unwrap()) }
|
||||
}
|
||||
|
||||
/// Helper function for debugging.
|
||||
///
|
||||
/// This function is called from JIT-compiled code to print a value for debugging purposes.
|
||||
pub extern "C" fn helper_dbg<Ctx: JITContext>(value: &Value) {
|
||||
pub extern "C" fn helper_dbg(value: &Value) {
|
||||
println!("{value:?}")
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ use cranelift_module::{FuncId, Linkage, Module};
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
|
||||
use nixjit_eval::{EvalContext, Value};
|
||||
use nixjit_ir::SymId;
|
||||
use nixjit_lir::Lir;
|
||||
|
||||
mod compile;
|
||||
@@ -174,25 +175,20 @@ impl<'comp, 'ctx, Ctx: JITContext> Context<'comp, 'ctx, Ctx> {
|
||||
slot
|
||||
}
|
||||
|
||||
fn push_attr(&mut self, attrs: StackSlot, sym: &str, val: StackSlot) {
|
||||
fn push_attr(&mut self, attrs: StackSlot, sym: SymId, val: StackSlot) {
|
||||
self.free_slot(attrs);
|
||||
self.free_slot(val);
|
||||
let attrs = self.builder.ins().stack_addr(types::I64, attrs, 0);
|
||||
let val = self.builder.ins().stack_addr(types::I64, val, 0);
|
||||
let sym = self.strings.get_or_insert_with(sym, |_| sym.to_owned());
|
||||
let ptr = self
|
||||
let sym = self
|
||||
.builder
|
||||
.ins()
|
||||
.iconst(self.compiler.ptr_type, sym.as_ptr() as i64);
|
||||
let len = self
|
||||
.builder
|
||||
.ins()
|
||||
.iconst(self.compiler.ptr_type, sym.len() as i64);
|
||||
.iconst(self.compiler.ptr_type, unsafe { sym.raw() } as i64);
|
||||
let push_attr = self
|
||||
.compiler
|
||||
.module
|
||||
.declare_func_in_func(self.compiler.push_attr, self.builder.func);
|
||||
self.builder.ins().call(push_attr, &[attrs, ptr, len, val]);
|
||||
self.builder.ins().call(push_attr, &[attrs, sym, val]);
|
||||
}
|
||||
|
||||
fn finalize_attrs(&mut self, attrs: StackSlot) -> StackSlot {
|
||||
@@ -281,24 +277,6 @@ impl<'comp, 'ctx, Ctx: JITContext> Context<'comp, 'ctx, Ctx> {
|
||||
slot
|
||||
}
|
||||
|
||||
fn lookup_stack(&mut self, ctx: ir::Value, idx: usize) -> StackSlot {
|
||||
let slot = self.alloca();
|
||||
let lookup_stack = self
|
||||
.compiler
|
||||
.module
|
||||
.declare_func_in_func(self.compiler.lookup_stack, self.builder.func);
|
||||
let idx = self
|
||||
.builder
|
||||
.ins()
|
||||
.iconst(self.compiler.ptr_type, idx as i64);
|
||||
let ptr = self
|
||||
.builder
|
||||
.ins()
|
||||
.stack_addr(self.compiler.ptr_type, slot, 0);
|
||||
self.builder.ins().call(lookup_stack, &[ctx, idx, ptr]);
|
||||
slot
|
||||
}
|
||||
|
||||
fn lookup_arg(&mut self, ctx: ir::Value, idx: usize) -> StackSlot {
|
||||
let slot = self.alloca();
|
||||
let lookup_arg = self
|
||||
@@ -406,7 +384,6 @@ pub struct JITCompiler<Ctx: JITContext> {
|
||||
func_sig: Signature,
|
||||
|
||||
call: FuncId,
|
||||
lookup_stack: FuncId,
|
||||
lookup_arg: FuncId,
|
||||
lookup: FuncId,
|
||||
select: FuncId,
|
||||
@@ -445,7 +422,6 @@ impl<Ctx: JITContext> JITCompiler<Ctx> {
|
||||
let mut builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names());
|
||||
|
||||
builder.symbol("helper_call", helper_call::<Ctx> as _);
|
||||
builder.symbol("helper_lookup_stack", helper_lookup_stack::<Ctx> as _);
|
||||
builder.symbol("helper_lookup_arg", helper_lookup_arg::<Ctx> as _);
|
||||
builder.symbol("helper_lookup", helper_lookup::<Ctx> as _);
|
||||
builder.symbol("helper_select", helper_select::<Ctx> as _);
|
||||
@@ -453,17 +429,17 @@ impl<Ctx: JITContext> JITCompiler<Ctx> {
|
||||
"helper_select_with_default",
|
||||
helper_select_with_default::<Ctx> as _,
|
||||
);
|
||||
builder.symbol("helper_eq", helper_eq::<Ctx> as _);
|
||||
builder.symbol("helper_eq", helper_eq as _);
|
||||
|
||||
builder.symbol("helper_alloc_array", helper_alloc_array::<Ctx> as _);
|
||||
builder.symbol("helper_create_string", helper_create_string::<Ctx> as _);
|
||||
builder.symbol("helper_create_list", helper_create_list::<Ctx> as _);
|
||||
builder.symbol("helper_create_attrs", helper_create_attrs::<Ctx> as _);
|
||||
builder.symbol("helper_push_attr", helper_push_attr::<Ctx> as _);
|
||||
builder.symbol("helper_finalize_attrs", helper_finalize_attrs::<Ctx> as _);
|
||||
builder.symbol("helper_alloc_array", helper_alloc_array as _);
|
||||
builder.symbol("helper_create_string", helper_create_string as _);
|
||||
builder.symbol("helper_create_list", helper_create_list as _);
|
||||
builder.symbol("helper_create_attrs", helper_create_attrs as _);
|
||||
builder.symbol("helper_push_attr", helper_push_attr as _);
|
||||
builder.symbol("helper_finalize_attrs", helper_finalize_attrs as _);
|
||||
builder.symbol("helper_enter_with", helper_enter_with::<Ctx> as _);
|
||||
builder.symbol("helper_exit_with", helper_exit_with::<Ctx> as _);
|
||||
builder.symbol("helper_dbg", helper_dbg::<Ctx> as _);
|
||||
builder.symbol("helper_dbg", helper_dbg as _);
|
||||
|
||||
let mut module = JITModule::new(builder);
|
||||
let ctx = module.make_context();
|
||||
@@ -495,18 +471,6 @@ impl<Ctx: JITContext> JITCompiler<Ctx> {
|
||||
.declare_function("helper_call", Linkage::Import, &call_sig)
|
||||
.unwrap();
|
||||
|
||||
let mut lookup_stack_sig = module.make_signature();
|
||||
lookup_stack_sig.params.extend(
|
||||
[AbiParam {
|
||||
value_type: ptr_type,
|
||||
purpose: ArgumentPurpose::Normal,
|
||||
extension: ArgumentExtension::None,
|
||||
}; 3],
|
||||
);
|
||||
let lookup_stack = module
|
||||
.declare_function("helper_lookup_stack", Linkage::Import, &lookup_stack_sig)
|
||||
.unwrap();
|
||||
|
||||
let mut lookup_arg_sig = module.make_signature();
|
||||
lookup_arg_sig.params.extend(
|
||||
[AbiParam {
|
||||
@@ -626,7 +590,7 @@ impl<Ctx: JITContext> JITCompiler<Ctx> {
|
||||
value_type: ptr_type,
|
||||
purpose: ArgumentPurpose::Normal,
|
||||
extension: ArgumentExtension::None,
|
||||
}; 4],
|
||||
}; 3],
|
||||
);
|
||||
let push_attr = module
|
||||
.declare_function("helper_push_attr", Linkage::Import, &push_attr_sig)
|
||||
@@ -694,7 +658,6 @@ impl<Ctx: JITContext> JITCompiler<Ctx> {
|
||||
func_sig,
|
||||
|
||||
call,
|
||||
lookup_stack,
|
||||
lookup_arg,
|
||||
lookup,
|
||||
select,
|
||||
@@ -760,6 +723,11 @@ impl<Ctx: JITContext> JITCompiler<Ctx> {
|
||||
self.ctx.clear();
|
||||
|
||||
let _ = self.builder_ctx.insert(builder_ctx);
|
||||
// SAFETY: The `get_finalized_function` method returns a raw pointer to the
|
||||
// compiled machine code. We transmute it to the correct function pointer type `F<Ctx>`.
|
||||
// This is safe because the function was compiled with the signature defined in `self.func_sig`,
|
||||
// which matches the signature of `F<Ctx>`. The lifetime of the compiled code is managed
|
||||
// by the `JITModule`, ensuring the pointer remains valid.
|
||||
unsafe {
|
||||
JITFunc {
|
||||
func: std::mem::transmute::<*const u8, F<Ctx>>(
|
||||
|
||||
Reference in New Issue
Block a user