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,11 +4,14 @@ use inkwell::{values::BasicValueEnum, AddressSpace};
use crate::backend::llvm::compiler::codegen::types;
use crate::backend::llvm::context::CodegenContext;
use crate::mir::{function::MirFunction, instruction::UnaryOp, BinaryOp, ValueId};
use crate::mir::{function::MirFunction, instruction::UnaryOp, BasicBlockId, BinaryOp, ValueId};
use super::builder_cursor::BuilderCursor;
/// Lower UnaryOp and store into vmap (0-diff)
pub(in super::super) fn lower_unary<'ctx>(
pub(in super::super) fn lower_unary<'ctx, 'b>(
codegen: &CodegenContext<'ctx>,
cursor: &mut BuilderCursor<'ctx, 'b>,
cur_bid: BasicBlockId,
vmap: &mut HashMap<ValueId, BasicValueEnum<'ctx>>,
dst: ValueId,
op: &UnaryOp,
@ -19,15 +22,15 @@ pub(in super::super) fn lower_unary<'ctx>(
let out = match op {
UnaryOp::Neg => {
if let Some(iv) = as_int(v) {
codegen
.builder
.build_int_neg(iv, "ineg")
cursor
.emit_instr(cur_bid, |b| b
.build_int_neg(iv, "ineg"))
.map_err(|e| e.to_string())?
.into()
} else if let Some(fv) = as_float(v) {
codegen
.builder
.build_float_neg(fv, "fneg")
cursor
.emit_instr(cur_bid, |b| b
.build_float_neg(fv, "fneg"))
.map_err(|e| e.to_string())?
.into()
} else {
@ -36,9 +39,9 @@ pub(in super::super) fn lower_unary<'ctx>(
}
UnaryOp::Not | UnaryOp::BitNot => {
if let Some(iv) = as_int(v) {
codegen
.builder
.build_not(iv, "inot")
cursor
.emit_instr(cur_bid, |b| b
.build_not(iv, "inot"))
.map_err(|e| e.to_string())?
.into()
} else {
@ -51,8 +54,10 @@ pub(in super::super) fn lower_unary<'ctx>(
}
/// Lower BinOp and store into vmap (includes concat fallback)
pub(in super::super) fn lower_binop<'ctx>(
pub(in super::super) fn lower_binop<'ctx, 'b>(
codegen: &CodegenContext<'ctx>,
cursor: &mut BuilderCursor<'ctx, 'b>,
cur_bid: BasicBlockId,
func: &MirFunction,
vmap: &mut HashMap<ValueId, BasicValueEnum<'ctx>>,
dst: ValueId,
@ -96,9 +101,9 @@ pub(in super::super) fn lower_binop<'ctx>(
.module
.get_function("nyash.string.concat_ss")
.unwrap_or_else(|| codegen.module.add_function("nyash.string.concat_ss", fnty, None));
let call = codegen
.builder
.build_call(callee, &[lp.into(), rp.into()], "concat_ss")
let call = cursor
.emit_instr(cur_bid, |b| b
.build_call(callee, &[lp.into(), rp.into()], "concat_ss"))
.map_err(|e| e.to_string())?;
let rv = call
.try_as_basic_value()
@ -115,9 +120,9 @@ pub(in super::super) fn lower_binop<'ctx>(
.module
.get_function("nyash.box.from_i8_string")
.unwrap_or_else(|| codegen.module.add_function("nyash.box.from_i8_string", fnty_conv, None));
let call_c = codegen
.builder
.build_call(conv, &[lp.into()], "lhs_i8_to_handle")
let call_c = cursor
.emit_instr(cur_bid, |b| b
.build_call(conv, &[lp.into()], "lhs_i8_to_handle"))
.map_err(|e| e.to_string())?;
let lh = call_c
.try_as_basic_value()
@ -129,9 +134,9 @@ pub(in super::super) fn lower_binop<'ctx>(
.module
.get_function("nyash.string.concat_hh")
.unwrap_or_else(|| codegen.module.add_function("nyash.string.concat_hh", fnty_hh, None));
let call = codegen
.builder
.build_call(callee, &[lh.into(), ri.into()], "concat_hh")
let call = cursor
.emit_instr(cur_bid, |b| b
.build_call(callee, &[lh.into(), ri.into()], "concat_hh"))
.map_err(|e| e.to_string())?;
let rv = call
.try_as_basic_value()
@ -180,9 +185,9 @@ pub(in super::super) fn lower_binop<'ctx>(
.module
.get_function("nyash.string.concat_hh")
.unwrap_or_else(|| codegen.module.add_function("nyash.string.concat_hh", fnty_hh, None));
let call = codegen
.builder
.build_call(callee, &[li.into(), rh.into()], "concat_hh")
let call = cursor
.emit_instr(cur_bid, |b| b
.build_call(callee, &[li.into(), rh.into()], "concat_hh"))
.map_err(|e| e.to_string())?;
let rv = call
.try_as_basic_value()
@ -197,9 +202,9 @@ pub(in super::super) fn lower_binop<'ctx>(
.module
.get_function("nyash.string.concat_is")
.unwrap_or_else(|| codegen.module.add_function("nyash.string.concat_is", fnty, None));
let call = codegen
.builder
.build_call(callee, &[li.into(), rp.into()], "concat_is")
let call = cursor
.emit_instr(cur_bid, |b| b
.build_call(callee, &[li.into(), rp.into()], "concat_is"))
.map_err(|e| e.to_string())?;
let rv = call
.try_as_basic_value()
@ -219,97 +224,33 @@ pub(in super::super) fn lower_binop<'ctx>(
let out = if let (Some(li), Some(ri)) = (as_int(lv), as_int(rv)) {
use BinaryOp as B;
match op {
B::Add => codegen
.builder
.build_int_add(li, ri, "iadd")
.map_err(|e| e.to_string())?
.into(),
B::Sub => codegen
.builder
.build_int_sub(li, ri, "isub")
.map_err(|e| e.to_string())?
.into(),
B::Mul => codegen
.builder
.build_int_mul(li, ri, "imul")
.map_err(|e| e.to_string())?
.into(),
B::Div => codegen
.builder
.build_int_signed_div(li, ri, "idiv")
.map_err(|e| e.to_string())?
.into(),
B::Mod => codegen
.builder
.build_int_signed_rem(li, ri, "imod")
.map_err(|e| e.to_string())?
.into(),
B::BitAnd => codegen
.builder
.build_and(li, ri, "iand")
.map_err(|e| e.to_string())?
.into(),
B::BitOr => codegen
.builder
.build_or(li, ri, "ior")
.map_err(|e| e.to_string())?
.into(),
B::BitXor => codegen
.builder
.build_xor(li, ri, "ixor")
.map_err(|e| e.to_string())?
.into(),
B::Shl => codegen
.builder
.build_left_shift(li, ri, "ishl")
.map_err(|e| e.to_string())?
.into(),
B::Shr => codegen
.builder
.build_right_shift(li, ri, false, "ishr")
.map_err(|e| e.to_string())?
.into(),
B::Add => cursor.emit_instr(cur_bid, |b| b.build_int_add(li, ri, "iadd")).map_err(|e| e.to_string())?.into(),
B::Sub => cursor.emit_instr(cur_bid, |b| b.build_int_sub(li, ri, "isub")).map_err(|e| e.to_string())?.into(),
B::Mul => cursor.emit_instr(cur_bid, |b| b.build_int_mul(li, ri, "imul")).map_err(|e| e.to_string())?.into(),
B::Div => cursor.emit_instr(cur_bid, |b| b.build_int_signed_div(li, ri, "idiv")).map_err(|e| e.to_string())?.into(),
B::Mod => cursor.emit_instr(cur_bid, |b| b.build_int_signed_rem(li, ri, "imod")).map_err(|e| e.to_string())?.into(),
B::BitAnd => cursor.emit_instr(cur_bid, |b| b.build_and(li, ri, "iand")).map_err(|e| e.to_string())?.into(),
B::BitOr => cursor.emit_instr(cur_bid, |b| b.build_or(li, ri, "ior")).map_err(|e| e.to_string())?.into(),
B::BitXor => cursor.emit_instr(cur_bid, |b| b.build_xor(li, ri, "ixor")).map_err(|e| e.to_string())?.into(),
B::Shl => cursor.emit_instr(cur_bid, |b| b.build_left_shift(li, ri, "ishl")).map_err(|e| e.to_string())?.into(),
B::Shr => cursor.emit_instr(cur_bid, |b| b.build_right_shift(li, ri, false, "ishr")).map_err(|e| e.to_string())?.into(),
B::And | B::Or => {
// Treat as logical on integers: convert to i1 and and/or
let lb = types::to_bool(codegen.context, li.into(), &codegen.builder)?;
let rb = types::to_bool(codegen.context, ri.into(), &codegen.builder)?;
match op {
B::And => codegen
.builder
.build_and(lb, rb, "land")
.map_err(|e| e.to_string())?
.into(),
_ => codegen
.builder
.build_or(lb, rb, "lor")
.map_err(|e| e.to_string())?
.into(),
B::And => cursor.emit_instr(cur_bid, |b| b.build_and(lb, rb, "land")).map_err(|e| e.to_string())?.into(),
_ => cursor.emit_instr(cur_bid, |b| b.build_or(lb, rb, "lor")).map_err(|e| e.to_string())?.into(),
}
}
}
} else if let (Some(lf), Some(rf)) = (as_float(lv), as_float(rv)) {
use BinaryOp as B;
match op {
B::Add => codegen
.builder
.build_float_add(lf, rf, "fadd")
.map_err(|e| e.to_string())?
.into(),
B::Sub => codegen
.builder
.build_float_sub(lf, rf, "fsub")
.map_err(|e| e.to_string())?
.into(),
B::Mul => codegen
.builder
.build_float_mul(lf, rf, "fmul")
.map_err(|e| e.to_string())?
.into(),
B::Div => codegen
.builder
.build_float_div(lf, rf, "fdiv")
.map_err(|e| e.to_string())?
.into(),
B::Add => cursor.emit_instr(cur_bid, |b| b.build_float_add(lf, rf, "fadd")).map_err(|e| e.to_string())?.into(),
B::Sub => cursor.emit_instr(cur_bid, |b| b.build_float_sub(lf, rf, "fsub")).map_err(|e| e.to_string())?.into(),
B::Mul => cursor.emit_instr(cur_bid, |b| b.build_float_mul(lf, rf, "fmul")).map_err(|e| e.to_string())?.into(),
B::Div => cursor.emit_instr(cur_bid, |b| b.build_float_div(lf, rf, "fdiv")).map_err(|e| e.to_string())?.into(),
B::Mod => return Err("fmod not supported yet".to_string()),
_ => return Err("bit/logic ops on float".to_string()),
}