docs: Add LLVM Python harness plan to CURRENT_TASK

- Added llvmlite verification harness strategy
- Python as parallel verification path for PHI/SSA issues
- Nyash ABI wrapper for LLVM emit abstraction
- NYASH_LLVM_USE_HARNESS=1 flag for mode switching
- Goal: Rust implementation in 1-2 days, Python for rapid verification

Acknowledging reality: When stuck at minimal viable implementation,
changing implementation language is a practical solution.
'Simple is Best' - the core Nyash philosophy.
This commit is contained in:
Selfhosting Dev
2025-09-12 19:23:16 +09:00
parent da51f0e51b
commit 45f13cf7a8
18 changed files with 599 additions and 490 deletions

View File

@ -4,10 +4,13 @@ use inkwell::values::BasicValueEnum as BVE;
use inkwell::AddressSpace;
use crate::backend::llvm::context::CodegenContext;
use crate::mir::{function::MirFunction, ValueId};
use crate::mir::{function::MirFunction, BasicBlockId, ValueId};
use crate::backend::llvm::compiler::codegen::instructions::builder_cursor::BuilderCursor;
pub(super) fn lower_future_spawn_instance<'ctx>(
pub(super) fn lower_future_spawn_instance<'ctx, 'b>(
codegen: &CodegenContext<'ctx>,
cursor: &mut BuilderCursor<'ctx, 'b>,
cur_bid: BasicBlockId,
vmap: &mut HashMap<ValueId, BVE<'ctx>>,
dst: &Option<ValueId>,
args: &[ValueId],
@ -20,9 +23,8 @@ pub(super) fn lower_future_spawn_instance<'ctx>(
let recv_v = *vmap.get(&args[0]).ok_or("recv missing")?;
let recv_h = match recv_v {
BVE::IntValue(iv) => iv,
BVE::PointerValue(pv) => codegen
.builder
.build_ptr_to_int(pv, i64t, "recv_p2i")
BVE::PointerValue(pv) => cursor
.emit_instr(cur_bid, |b| b.build_ptr_to_int(pv, i64t, "recv_p2i"))
.map_err(|e| e.to_string())?,
_ => return Err("spawn_instance recv must be int or ptr".to_string()),
};
@ -36,9 +38,8 @@ pub(super) fn lower_future_spawn_instance<'ctx>(
.module
.get_function("nyash.future.spawn_instance")
.unwrap_or_else(|| codegen.module.add_function("nyash.future.spawn_instance", fnty, None));
let call = codegen
.builder
.build_call(callee, &[recv_h.into(), name_p.into()], "spawn_instance")
let call = cursor
.emit_instr(cur_bid, |b| b.build_call(callee, &[recv_h.into(), name_p.into()], "spawn_instance"))
.map_err(|e| e.to_string())?;
if let Some(d) = dst {
let rv = call
@ -50,8 +51,10 @@ pub(super) fn lower_future_spawn_instance<'ctx>(
Ok(())
}
pub(super) fn lower_local_get<'ctx>(
pub(super) fn lower_local_get<'ctx, 'b>(
codegen: &CodegenContext<'ctx>,
cursor: &mut BuilderCursor<'ctx, 'b>,
cur_bid: BasicBlockId,
func: &MirFunction,
vmap: &mut HashMap<ValueId, BVE<'ctx>>,
dst: &Option<ValueId>,
@ -73,9 +76,8 @@ pub(super) fn lower_local_get<'ctx>(
.module
.get_function("nyash.env.local.get_h")
.unwrap_or_else(|| codegen.module.add_function("nyash.env.local.get_h", fnty, None));
let call = codegen
.builder
.build_call(callee, &[name_p.into()], "local_get_h")
let call = cursor
.emit_instr(cur_bid, |b| b.build_call(callee, &[name_p.into()], "local_get_h"))
.map_err(|e| e.to_string())?;
let rv = call
.try_as_basic_value()
@ -98,9 +100,8 @@ pub(super) fn lower_local_get<'ctx>(
| crate::mir::MirType::Unknown => {
let h = rv.into_int_value();
let pty = codegen.context.ptr_type(AddressSpace::from(0));
let ptr = codegen
.builder
.build_int_to_ptr(h, pty, "local_get_handle_to_ptr")
let ptr = cursor
.emit_instr(cur_bid, |b| b.build_int_to_ptr(h, pty, "local_get_handle_to_ptr"))
.map_err(|e| e.to_string())?;
vmap.insert(*d, ptr.into());
}
@ -115,8 +116,10 @@ pub(super) fn lower_local_get<'ctx>(
Ok(())
}
pub(super) fn lower_box_new<'ctx>(
pub(super) fn lower_box_new<'ctx, 'b>(
codegen: &CodegenContext<'ctx>,
cursor: &mut BuilderCursor<'ctx, 'b>,
cur_bid: BasicBlockId,
vmap: &mut HashMap<ValueId, BVE<'ctx>>,
dst: &Option<ValueId>,
args: &[ValueId],
@ -137,18 +140,16 @@ pub(super) fn lower_box_new<'ctx>(
.module
.get_function("nyash.env.box.new")
.unwrap_or_else(|| codegen.module.add_function("nyash.env.box.new", fnty, None));
let call = codegen
.builder
.build_call(callee, &[name_p.into()], "env_box_new")
let call = cursor
.emit_instr(cur_bid, |b| b.build_call(callee, &[name_p.into()], "env_box_new"))
.map_err(|e| e.to_string())?;
let h = call
.try_as_basic_value()
.left()
.ok_or("env.box.new returned void".to_string())?
.into_int_value();
let out_ptr = codegen
.builder
.build_int_to_ptr(h, i8p, "box_handle_to_ptr")
let out_ptr = cursor
.emit_instr(cur_bid, |b| b.build_int_to_ptr(h, i8p, "box_handle_to_ptr"))
.map_err(|e| e.to_string())?;
if let Some(d) = dst {
vmap.insert(*d, out_ptr.into());
@ -192,9 +193,8 @@ pub(super) fn lower_box_new<'ctx>(
.module
.get_function("nyash.box.from_f64")
.unwrap_or_else(|| codegen.module.add_function("nyash.box.from_f64", fnty, None));
let call = codegen
.builder
.build_call(callee, &[fv.into()], "arg1_f64_to_box")
let call = cursor
.emit_instr(cur_bid, |b| b.build_call(callee, &[fv.into()], "arg1_f64_to_box"))
.map_err(|e| e.to_string())?;
let rv = call
.try_as_basic_value()
@ -208,9 +208,8 @@ pub(super) fn lower_box_new<'ctx>(
.module
.get_function("nyash.box.from_i8_string")
.unwrap_or_else(|| codegen.module.add_function("nyash.box.from_i8_string", fnty, None));
let call = codegen
.builder
.build_call(callee, &[pv.into()], "arg1_i8_to_box")
let call = cursor
.emit_instr(cur_bid, |b| b.build_call(callee, &[pv.into()], "arg1_i8_to_box"))
.map_err(|e| e.to_string())?;
let rv = call.try_as_basic_value().left().ok_or("from_i8_string returned void".to_string())?;
if let BVE::IntValue(h) = rv { h } else { return Err("from_i8_string ret expected i64".to_string()); }
@ -229,9 +228,8 @@ pub(super) fn lower_box_new<'ctx>(
.module
.get_function("nyash.box.from_f64")
.unwrap_or_else(|| codegen.module.add_function("nyash.box.from_f64", fnty, None));
let call = codegen
.builder
.build_call(callee, &[fv.into()], "arg2_f64_to_box")
let call = cursor
.emit_instr(cur_bid, |b| b.build_call(callee, &[fv.into()], "arg2_f64_to_box"))
.map_err(|e| e.to_string())?;
let rv = call
.try_as_basic_value()
@ -245,9 +243,8 @@ pub(super) fn lower_box_new<'ctx>(
.module
.get_function("nyash.box.from_i8_string")
.unwrap_or_else(|| codegen.module.add_function("nyash.box.from_i8_string", fnty, None));
let call = codegen
.builder
.build_call(callee, &[pv.into()], "arg2_i8_to_box")
let call = cursor
.emit_instr(cur_bid, |b| b.build_call(callee, &[pv.into()], "arg2_i8_to_box"))
.map_err(|e| e.to_string())?;
let rv = call.try_as_basic_value().left().ok_or("from_i8_string returned void".to_string())?;
if let BVE::IntValue(h) = rv { h } else { return Err("from_i8_string ret expected i64".to_string()); }
@ -266,9 +263,8 @@ pub(super) fn lower_box_new<'ctx>(
.module
.get_function("nyash.box.from_f64")
.unwrap_or_else(|| codegen.module.add_function("nyash.box.from_f64", fnty, None));
let call = codegen
.builder
.build_call(callee, &[fv.into()], "arg3_f64_to_box")
let call = cursor
.emit_instr(cur_bid, |b| b.build_call(callee, &[fv.into()], "arg3_f64_to_box"))
.map_err(|e| e.to_string())?;
let rv = call
.try_as_basic_value()
@ -282,9 +278,8 @@ pub(super) fn lower_box_new<'ctx>(
.module
.get_function("nyash.box.from_i8_string")
.unwrap_or_else(|| codegen.module.add_function("nyash.box.from_i8_string", fnty, None));
let call = codegen
.builder
.build_call(callee, &[pv.into()], "arg3_i8_to_box")
let call = cursor
.emit_instr(cur_bid, |b| b.build_call(callee, &[pv.into()], "arg3_i8_to_box"))
.map_err(|e| e.to_string())?;
let rv = call.try_as_basic_value().left().ok_or("from_i8_string returned void".to_string())?;
if let BVE::IntValue(h) = rv { h } else { return Err("from_i8_string ret expected i64".to_string()); }
@ -303,9 +298,8 @@ pub(super) fn lower_box_new<'ctx>(
.module
.get_function("nyash.box.from_f64")
.unwrap_or_else(|| codegen.module.add_function("nyash.box.from_f64", fnty, None));
let call = codegen
.builder
.build_call(callee, &[fv.into()], "arg4_f64_to_box")
let call = cursor
.emit_instr(cur_bid, |b| b.build_call(callee, &[fv.into()], "arg4_f64_to_box"))
.map_err(|e| e.to_string())?;
let rv = call
.try_as_basic_value()
@ -319,9 +313,8 @@ pub(super) fn lower_box_new<'ctx>(
.module
.get_function("nyash.box.from_i8_string")
.unwrap_or_else(|| codegen.module.add_function("nyash.box.from_i8_string", fnty, None));
let call = codegen
.builder
.build_call(callee, &[pv.into()], "arg4_i8_to_box")
let call = cursor
.emit_instr(cur_bid, |b| b.build_call(callee, &[pv.into()], "arg4_i8_to_box"))
.map_err(|e| e.to_string())?;
let rv = call.try_as_basic_value().left().ok_or("from_i8_string returned void".to_string())?;
if let BVE::IntValue(h) = rv { h } else { return Err("from_i8_string ret expected i64".to_string()); }
@ -329,22 +322,20 @@ pub(super) fn lower_box_new<'ctx>(
_ => return Err("unsupported arg value for env.box.new".to_string()),
};
}
let call = codegen
.builder
.build_call(
let call = cursor
.emit_instr(cur_bid, |b| b.build_call(
callee,
&[ty_ptr.into(), argc_val.into(), a1.into(), a2.into(), a3.into(), a4.into()],
"env_box_new_i64x",
)
))
.map_err(|e| e.to_string())?;
let rv = call
.try_as_basic_value()
.left()
.ok_or("env.box.new_i64 returned void".to_string())?;
let i64v = if let BVE::IntValue(iv) = rv { iv } else { return Err("env.box.new_i64 ret expected i64".to_string()); };
let out_ptr = codegen
.builder
.build_int_to_ptr(i64v, i8p, "box_handle_to_ptr")
let out_ptr = cursor
.emit_instr(cur_bid, |b| b.build_int_to_ptr(i64v, i8p, "box_handle_to_ptr"))
.map_err(|e| e.to_string())?;
if let Some(d) = dst {
vmap.insert(*d, out_ptr.into());
@ -353,4 +344,3 @@ pub(super) fn lower_box_new<'ctx>(
}
Err("env.box.new requires at least 1 arg".to_string())
}