feat: TODO

This commit is contained in:
2025-08-28 18:18:35 +08:00
parent 2fbd2a26a9
commit f7131079e5
26 changed files with 580 additions and 580 deletions

View File

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

View File

@@ -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:?}")
}

View File

@@ -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>>(