feat(llvm): Complete function call system implementation by ChatGPT5
Major improvements to LLVM backend function call infrastructure: ## Key Changes ### Function Call System Complete - All MIR functions now properly lowered to LLVM (not just entry) - Function parameter binding to LLVM arguments implemented - ny_main() wrapper added for proper entry point handling - Callee resolution from ValueId to function symbols working ### Call Instruction Analysis - MirInstruction::Call was implemented but system was incomplete - Fixed "rhs missing" errors caused by undefined Call return values - Function calls now properly return values through the system ### Code Modularization (Ongoing) - BoxCall → instructions/boxcall.rs ✓ - ExternCall → instructions/externcall.rs ✓ - Call remains in mod.rs (to be refactored) ### Phase 21 Documentation - Added comprehensive AI evaluation from Gemini and Codex - Both AIs confirm academic paper potential for self-parsing AST DB approach - "Code as Database" concept validated as novel contribution Co-authored-by: ChatGPT5 <noreply@openai.com> 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -3,19 +3,92 @@ use std::collections::HashMap;
|
||||
use inkwell::values::BasicValueEnum;
|
||||
|
||||
use crate::backend::llvm::context::CodegenContext;
|
||||
use crate::mir::{CompareOp, ValueId};
|
||||
use crate::mir::{function::MirFunction, CompareOp, ValueId};
|
||||
|
||||
/// Compare lowering: return the resulting BasicValueEnum (i1)
|
||||
pub(in super::super) fn lower_compare<'ctx>(
|
||||
codegen: &CodegenContext<'ctx>,
|
||||
func: &MirFunction,
|
||||
vmap: &HashMap<ValueId, BasicValueEnum<'ctx>>,
|
||||
op: &CompareOp,
|
||||
lhs: &ValueId,
|
||||
rhs: &ValueId,
|
||||
) -> Result<BasicValueEnum<'ctx>, String> {
|
||||
use crate::backend::llvm::compiler::helpers::{as_float, as_int};
|
||||
let lv = *vmap.get(lhs).ok_or("lhs missing")?;
|
||||
let rv = *vmap.get(rhs).ok_or("rhs missing")?;
|
||||
let lv = *vmap
|
||||
.get(lhs)
|
||||
.ok_or_else(|| format!("lhs missing: {}", lhs.as_u32()))?;
|
||||
let rv = *vmap
|
||||
.get(rhs)
|
||||
.ok_or_else(|| format!("rhs missing: {}", rhs.as_u32()))?;
|
||||
// String equality/inequality by content when annotated as String/StringBox
|
||||
if matches!(op, CompareOp::Eq | CompareOp::Ne) {
|
||||
let l_is_str = match func.metadata.value_types.get(lhs) {
|
||||
Some(crate::mir::MirType::String) => true,
|
||||
Some(crate::mir::MirType::Box(b)) if b == "StringBox" => true,
|
||||
_ => false,
|
||||
};
|
||||
let r_is_str = match func.metadata.value_types.get(rhs) {
|
||||
Some(crate::mir::MirType::String) => true,
|
||||
Some(crate::mir::MirType::Box(b)) if b == "StringBox" => true,
|
||||
_ => false,
|
||||
};
|
||||
if l_is_str && r_is_str {
|
||||
let i64t = codegen.context.i64_type();
|
||||
// Convert both sides to handles if needed
|
||||
let to_handle = |v: BasicValueEnum<'ctx>| -> Result<inkwell::values::IntValue<'ctx>, String> {
|
||||
match v {
|
||||
BasicValueEnum::IntValue(iv) => {
|
||||
if iv.get_type() == i64t { Ok(iv) } else { codegen.builder.build_int_s_extend(iv, i64t, "i2i64").map_err(|e| e.to_string()) }
|
||||
}
|
||||
BasicValueEnum::PointerValue(pv) => {
|
||||
let fnty = i64t.fn_type(&[codegen.context.ptr_type(inkwell::AddressSpace::from(0)).into()], false);
|
||||
let callee = codegen
|
||||
.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()], "str_ptr_to_handle_cmp")
|
||||
.map_err(|e| e.to_string())?;
|
||||
let rv = call
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("from_i8_string returned void".to_string())?;
|
||||
Ok(rv.into_int_value())
|
||||
}
|
||||
_ => Err("unsupported value for string compare".to_string()),
|
||||
}
|
||||
};
|
||||
let lh = to_handle(lv)?;
|
||||
let rh = to_handle(rv)?;
|
||||
let fnty = i64t.fn_type(&[i64t.into(), i64t.into()], false);
|
||||
let callee = codegen
|
||||
.module
|
||||
.get_function("nyash.string.eq_hh")
|
||||
.unwrap_or_else(|| codegen.module.add_function("nyash.string.eq_hh", fnty, None));
|
||||
let call = codegen
|
||||
.builder
|
||||
.build_call(callee, &[lh.into(), rh.into()], "str_eq_hh")
|
||||
.map_err(|e| e.to_string())?;
|
||||
let iv = call
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("eq_hh returned void".to_string())?
|
||||
.into_int_value();
|
||||
let zero = i64t.const_zero();
|
||||
let pred = if matches!(op, CompareOp::Eq) {
|
||||
inkwell::IntPredicate::NE
|
||||
} else {
|
||||
inkwell::IntPredicate::EQ
|
||||
};
|
||||
let b = codegen
|
||||
.builder
|
||||
.build_int_compare(pred, iv, zero, "str_eq_to_bool")
|
||||
.map_err(|e| e.to_string())?;
|
||||
return Ok(b.into());
|
||||
}
|
||||
}
|
||||
let out = if let (Some(li), Some(ri)) = (as_int(lv), as_int(rv)) {
|
||||
use CompareOp as C;
|
||||
let pred = match op {
|
||||
|
||||
@ -63,8 +63,12 @@ pub(in super::super) fn lower_binop<'ctx>(
|
||||
use crate::backend::llvm::compiler::helpers::{as_float, as_int};
|
||||
use inkwell::values::BasicValueEnum as BVE;
|
||||
use inkwell::IntPredicate;
|
||||
let lv = *vmap.get(lhs).ok_or("lhs missing")?;
|
||||
let rv = *vmap.get(rhs).ok_or("rhs missing")?;
|
||||
let lv = *vmap
|
||||
.get(lhs)
|
||||
.ok_or_else(|| format!("lhs missing: {}", lhs.as_u32()))?;
|
||||
let rv = *vmap
|
||||
.get(rhs)
|
||||
.ok_or_else(|| format!("rhs missing: {}", rhs.as_u32()))?;
|
||||
let mut handled_concat = false;
|
||||
if let BinaryOp::Add = op {
|
||||
let i8p = codegen.context.ptr_type(AddressSpace::from(0));
|
||||
|
||||
@ -1,465 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use inkwell::AddressSpace;
|
||||
use inkwell::values::BasicValueEnum as BVE;
|
||||
|
||||
use crate::backend::llvm::context::CodegenContext;
|
||||
use crate::mir::{function::MirFunction, ValueId};
|
||||
|
||||
/// Full ExternCall lowering (console/debug, future.spawn_instance, env.local, env.box.new)
|
||||
pub(in super::super) fn lower_externcall<'ctx>(
|
||||
codegen: &CodegenContext<'ctx>,
|
||||
func: &MirFunction,
|
||||
vmap: &mut HashMap<ValueId, inkwell::values::BasicValueEnum<'ctx>>,
|
||||
dst: &Option<ValueId>,
|
||||
iface_name: &str,
|
||||
method_name: &str,
|
||||
args: &[ValueId],
|
||||
) -> Result<(), String> {
|
||||
use crate::backend::llvm::compiler::helpers::{as_float, as_int};
|
||||
|
||||
if (iface_name == "env.console"
|
||||
&& (method_name == "log" || method_name == "warn" || method_name == "error"))
|
||||
|| (iface_name == "env.debug" && method_name == "trace")
|
||||
{
|
||||
if args.len() != 1 {
|
||||
return Err(format!("{}.{} expects 1 arg", iface_name, method_name));
|
||||
}
|
||||
let av = *vmap.get(&args[0]).ok_or("extern arg missing")?;
|
||||
match av {
|
||||
// If argument is i8* (string), call string variant
|
||||
BVE::PointerValue(pv) => {
|
||||
let i8p = codegen.context.ptr_type(AddressSpace::from(0));
|
||||
let fnty = codegen.context.i64_type().fn_type(&[i8p.into()], false);
|
||||
let fname = if iface_name == "env.console" {
|
||||
match method_name {
|
||||
"log" => "nyash.console.log",
|
||||
"warn" => "nyash.console.warn",
|
||||
_ => "nyash.console.error",
|
||||
}
|
||||
} else {
|
||||
"nyash.debug.trace"
|
||||
};
|
||||
let callee = codegen
|
||||
.module
|
||||
.get_function(fname)
|
||||
.unwrap_or_else(|| codegen.module.add_function(fname, fnty, None));
|
||||
let _ = codegen
|
||||
.builder
|
||||
.build_call(callee, &[pv.into()], "console_log_p")
|
||||
.map_err(|e| e.to_string())?;
|
||||
if let Some(d) = dst {
|
||||
vmap.insert(*d, codegen.context.i64_type().const_zero().into());
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
// Otherwise, convert to i64 and call handle variant
|
||||
_ => {
|
||||
let arg_val = match av {
|
||||
BVE::IntValue(iv) => {
|
||||
if iv.get_type() == codegen.context.bool_type() {
|
||||
codegen
|
||||
.builder
|
||||
.build_int_z_extend(iv, codegen.context.i64_type(), "bool2i64")
|
||||
.map_err(|e| e.to_string())?
|
||||
} else if iv.get_type() == codegen.context.i64_type() {
|
||||
iv
|
||||
} else {
|
||||
codegen
|
||||
.builder
|
||||
.build_int_s_extend(iv, codegen.context.i64_type(), "int2i64")
|
||||
.map_err(|e| e.to_string())?
|
||||
}
|
||||
}
|
||||
BVE::PointerValue(_) => unreachable!(),
|
||||
_ => return Err("console.log arg conversion failed".to_string()),
|
||||
};
|
||||
let fnty = codegen
|
||||
.context
|
||||
.i64_type()
|
||||
.fn_type(&[codegen.context.i64_type().into()], false);
|
||||
let fname = if iface_name == "env.console" {
|
||||
match method_name {
|
||||
"log" => "nyash.console.log_handle",
|
||||
"warn" => "nyash.console.warn_handle",
|
||||
_ => "nyash.console.error_handle",
|
||||
}
|
||||
} else {
|
||||
"nyash.debug.trace_handle"
|
||||
};
|
||||
let callee = codegen
|
||||
.module
|
||||
.get_function(fname)
|
||||
.unwrap_or_else(|| codegen.module.add_function(fname, fnty, None));
|
||||
let _ = codegen
|
||||
.builder
|
||||
.build_call(callee, &[arg_val.into()], "console_log_h")
|
||||
.map_err(|e| e.to_string())?;
|
||||
if let Some(d) = dst {
|
||||
vmap.insert(*d, codegen.context.i64_type().const_zero().into());
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if iface_name == "env.console" && method_name == "readLine" {
|
||||
if !args.is_empty() {
|
||||
return Err("console.readLine expects 0 args".to_string());
|
||||
}
|
||||
let i8p = codegen.context.ptr_type(AddressSpace::from(0));
|
||||
let fnty = i8p.fn_type(&[], false);
|
||||
let callee = codegen
|
||||
.module
|
||||
.get_function("nyash.console.readline")
|
||||
.unwrap_or_else(|| codegen.module.add_function("nyash.console.readline", fnty, None));
|
||||
let call = codegen
|
||||
.builder
|
||||
.build_call(callee, &[], "readline")
|
||||
.map_err(|e| e.to_string())?;
|
||||
if let Some(d) = dst {
|
||||
let rv = call
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("readline returned void".to_string())?;
|
||||
vmap.insert(*d, rv);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if iface_name == "env.future" && method_name == "spawn_instance" {
|
||||
if args.len() < 2 {
|
||||
return Err("env.future.spawn_instance expects at least (recv, method_name)".to_string());
|
||||
}
|
||||
let i64t = codegen.context.i64_type();
|
||||
let i8p = codegen.context.ptr_type(AddressSpace::from(0));
|
||||
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")
|
||||
.map_err(|e| e.to_string())?,
|
||||
_ => return Err("spawn_instance recv must be int or ptr".to_string()),
|
||||
};
|
||||
let name_v = *vmap.get(&args[1]).ok_or("method name missing")?;
|
||||
let name_p = match name_v {
|
||||
BVE::PointerValue(pv) => pv,
|
||||
_ => return Err("spawn_instance method name must be i8*".to_string()),
|
||||
};
|
||||
let fnty = i64t.fn_type(&[i64t.into(), i8p.into()], false);
|
||||
let callee = codegen
|
||||
.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")
|
||||
.map_err(|e| e.to_string())?;
|
||||
if let Some(d) = dst {
|
||||
let rv = call
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("spawn_instance returned void".to_string())?;
|
||||
vmap.insert(*d, rv);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if iface_name == "env.local" && method_name == "get" {
|
||||
if args.len() != 1 {
|
||||
return Err("env.local.get expects 1 arg".to_string());
|
||||
}
|
||||
let name_v = *vmap.get(&args[0]).ok_or("local.get name missing")?;
|
||||
let name_p = if let BVE::PointerValue(pv) = name_v {
|
||||
pv
|
||||
} else {
|
||||
return Err("env.local.get name must be i8*".to_string());
|
||||
};
|
||||
let i64t = codegen.context.i64_type();
|
||||
let i8p = codegen.context.ptr_type(AddressSpace::from(0));
|
||||
let fnty = i64t.fn_type(&[i8p.into()], false);
|
||||
let callee = codegen
|
||||
.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")
|
||||
.map_err(|e| e.to_string())?;
|
||||
let rv = call
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("local.get returned void".to_string())?;
|
||||
// Cast handle to pointer for Box-like return types
|
||||
if let Some(d) = dst {
|
||||
if let Some(mt) = func.metadata.value_types.get(d) {
|
||||
match mt {
|
||||
crate::mir::MirType::Integer | crate::mir::MirType::Bool => {
|
||||
vmap.insert(*d, rv);
|
||||
}
|
||||
crate::mir::MirType::String => {
|
||||
// keep as handle (i64)
|
||||
vmap.insert(*d, rv);
|
||||
}
|
||||
crate::mir::MirType::Box(_)
|
||||
| crate::mir::MirType::Array(_)
|
||||
| crate::mir::MirType::Future(_)
|
||||
| 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")
|
||||
.map_err(|e| e.to_string())?;
|
||||
vmap.insert(*d, ptr.into());
|
||||
}
|
||||
_ => {
|
||||
vmap.insert(*d, rv);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
vmap.insert(*d, rv);
|
||||
}
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if iface_name == "env.box" && method_name == "new" {
|
||||
// Two variants: (name) and (argc, arg1, arg2, arg3, arg4) with optional ptr conversion
|
||||
// Prefer the i64 birth when possible; else call env.box.new(name)
|
||||
let i64t = codegen.context.i64_type();
|
||||
let i8p = codegen.context.ptr_type(AddressSpace::from(0));
|
||||
if args.len() == 1 {
|
||||
let name_v = *vmap.get(&args[0]).ok_or("env.box.new name missing")?;
|
||||
let name_p = if let BVE::PointerValue(pv) = name_v {
|
||||
pv
|
||||
} else {
|
||||
return Err("env.box.new name must be i8*".to_string());
|
||||
};
|
||||
let fnty = i64t.fn_type(&[i8p.into()], false);
|
||||
let callee = codegen
|
||||
.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")
|
||||
.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")
|
||||
.map_err(|e| e.to_string())?;
|
||||
if let Some(d) = dst {
|
||||
vmap.insert(*d, out_ptr.into());
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
if !args.is_empty() {
|
||||
// argc + up to 4 i64 payloads: build i64 via conversions
|
||||
let argc_val = i64t.const_int(args.len() as u64, false);
|
||||
let fnty = i64t.fn_type(
|
||||
&[
|
||||
i8p.into(),
|
||||
i64t.into(),
|
||||
i64t.into(),
|
||||
i64t.into(),
|
||||
i64t.into(),
|
||||
i64t.into(),
|
||||
],
|
||||
false,
|
||||
);
|
||||
let callee = codegen
|
||||
.module
|
||||
.get_function("nyash.env.box.new_i64")
|
||||
.unwrap_or_else(|| codegen.module.add_function("nyash.env.box.new_i64", fnty, None));
|
||||
// arg0: type name string pointer
|
||||
if args.is_empty() {
|
||||
return Err("env.box.new_i64 requires at least type name".to_string());
|
||||
}
|
||||
let ty_ptr = match *vmap.get(&args[0]).ok_or("type name missing")? {
|
||||
BVE::PointerValue(pv) => pv,
|
||||
_ => return Err("env.box.new_i64 arg0 must be i8* type name".to_string()),
|
||||
};
|
||||
let mut a1 = i64t.const_zero();
|
||||
if args.len() >= 2 {
|
||||
let bv = *vmap.get(&args[1]).ok_or("arg missing")?;
|
||||
a1 = match bv {
|
||||
BVE::IntValue(iv) => iv,
|
||||
BVE::FloatValue(fv) => {
|
||||
let fnty = i64t.fn_type(&[codegen.context.f64_type().into()], false);
|
||||
let callee = codegen
|
||||
.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")
|
||||
.map_err(|e| e.to_string())?;
|
||||
let rv = call
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("from_f64 returned void".to_string())?;
|
||||
if let BVE::IntValue(h) = rv { h } else { return Err("from_f64 ret expected i64".to_string()); }
|
||||
}
|
||||
BVE::PointerValue(pv) => {
|
||||
let fnty = i64t.fn_type(&[i8p.into()], false);
|
||||
let callee = codegen
|
||||
.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")
|
||||
.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()); }
|
||||
}
|
||||
_ => return Err("unsupported arg value for env.box.new".to_string()),
|
||||
};
|
||||
}
|
||||
let mut a2 = i64t.const_zero();
|
||||
if args.len() >= 3 {
|
||||
let bv = *vmap.get(&args[2]).ok_or("arg missing")?;
|
||||
a2 = match bv {
|
||||
BVE::IntValue(iv) => iv,
|
||||
BVE::FloatValue(fv) => {
|
||||
let fnty = i64t.fn_type(&[codegen.context.f64_type().into()], false);
|
||||
let callee = codegen
|
||||
.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")
|
||||
.map_err(|e| e.to_string())?;
|
||||
let rv = call
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("from_f64 returned void".to_string())?;
|
||||
if let BVE::IntValue(h) = rv { h } else { return Err("from_f64 ret expected i64".to_string()); }
|
||||
}
|
||||
BVE::PointerValue(pv) => {
|
||||
let fnty = i64t.fn_type(&[i8p.into()], false);
|
||||
let callee = codegen
|
||||
.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")
|
||||
.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()); }
|
||||
}
|
||||
_ => return Err("unsupported arg value for env.box.new".to_string()),
|
||||
};
|
||||
}
|
||||
let mut a3 = i64t.const_zero();
|
||||
if args.len() >= 4 {
|
||||
let bv = *vmap.get(&args[3]).ok_or("arg missing")?;
|
||||
a3 = match bv {
|
||||
BVE::IntValue(iv) => iv,
|
||||
BVE::FloatValue(fv) => {
|
||||
let fnty = i64t.fn_type(&[codegen.context.f64_type().into()], false);
|
||||
let callee = codegen
|
||||
.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")
|
||||
.map_err(|e| e.to_string())?;
|
||||
let rv = call
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("from_f64 returned void".to_string())?;
|
||||
if let BVE::IntValue(h) = rv { h } else { return Err("from_f64 ret expected i64".to_string()); }
|
||||
}
|
||||
BVE::PointerValue(pv) => {
|
||||
let fnty = i64t.fn_type(&[i8p.into()], false);
|
||||
let callee = codegen
|
||||
.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")
|
||||
.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()); }
|
||||
}
|
||||
_ => return Err("unsupported arg value for env.box.new".to_string()),
|
||||
};
|
||||
}
|
||||
let mut a4 = i64t.const_zero();
|
||||
if args.len() >= 5 {
|
||||
let bv = *vmap.get(&args[4]).ok_or("arg missing")?;
|
||||
a4 = match bv {
|
||||
BVE::IntValue(iv) => iv,
|
||||
BVE::FloatValue(fv) => {
|
||||
let fnty = i64t.fn_type(&[codegen.context.f64_type().into()], false);
|
||||
let callee = codegen
|
||||
.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")
|
||||
.map_err(|e| e.to_string())?;
|
||||
let rv = call
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("from_f64 returned void".to_string())?;
|
||||
if let BVE::IntValue(h) = rv { h } else { return Err("from_f64 ret expected i64".to_string()); }
|
||||
}
|
||||
BVE::PointerValue(pv) => {
|
||||
let fnty = i64t.fn_type(&[i8p.into()], false);
|
||||
let callee = codegen
|
||||
.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")
|
||||
.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()); }
|
||||
}
|
||||
_ => return Err("unsupported arg value for env.box.new".to_string()),
|
||||
};
|
||||
}
|
||||
let call = codegen
|
||||
.builder
|
||||
.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")
|
||||
.map_err(|e| e.to_string())?;
|
||||
if let Some(d) = dst {
|
||||
vmap.insert(*d, out_ptr.into());
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
Err(format!(
|
||||
"ExternCall lowering unsupported: {}.{} (add a NyRT shim for this interface method)",
|
||||
iface_name, method_name
|
||||
))
|
||||
}
|
||||
@ -0,0 +1,126 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use inkwell::values::BasicValueEnum as BVE;
|
||||
use inkwell::AddressSpace;
|
||||
|
||||
use crate::backend::llvm::context::CodegenContext;
|
||||
use crate::mir::ValueId;
|
||||
|
||||
pub(super) fn lower_log_or_trace<'ctx>(
|
||||
codegen: &CodegenContext<'ctx>,
|
||||
vmap: &mut HashMap<ValueId, BVE<'ctx>>,
|
||||
dst: &Option<ValueId>,
|
||||
iface_name: &str,
|
||||
method_name: &str,
|
||||
args: &[ValueId],
|
||||
) -> Result<(), String> {
|
||||
if args.len() != 1 {
|
||||
return Err(format!("{}.{} expects 1 arg", iface_name, method_name));
|
||||
}
|
||||
let av = *vmap.get(&args[0]).ok_or("extern arg missing")?;
|
||||
match av {
|
||||
// If argument is i8* (string), call string variant
|
||||
BVE::PointerValue(pv) => {
|
||||
let i8p = codegen.context.ptr_type(AddressSpace::from(0));
|
||||
let fnty = codegen.context.i64_type().fn_type(&[i8p.into()], false);
|
||||
let fname = if iface_name == "env.console" {
|
||||
match method_name {
|
||||
"log" => "nyash.console.log",
|
||||
"warn" => "nyash.console.warn",
|
||||
_ => "nyash.console.error",
|
||||
}
|
||||
} else {
|
||||
"nyash.debug.trace"
|
||||
};
|
||||
let callee = codegen
|
||||
.module
|
||||
.get_function(fname)
|
||||
.unwrap_or_else(|| codegen.module.add_function(fname, fnty, None));
|
||||
let _ = codegen
|
||||
.builder
|
||||
.build_call(callee, &[pv.into()], "console_log_p")
|
||||
.map_err(|e| e.to_string())?;
|
||||
if let Some(d) = dst {
|
||||
vmap.insert(*d, codegen.context.i64_type().const_zero().into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
// Otherwise, convert to i64 and call handle variant
|
||||
_ => {
|
||||
let arg_val = match av {
|
||||
BVE::IntValue(iv) => {
|
||||
if iv.get_type() == codegen.context.bool_type() {
|
||||
codegen
|
||||
.builder
|
||||
.build_int_z_extend(iv, codegen.context.i64_type(), "bool2i64")
|
||||
.map_err(|e| e.to_string())?
|
||||
} else if iv.get_type() == codegen.context.i64_type() {
|
||||
iv
|
||||
} else {
|
||||
codegen
|
||||
.builder
|
||||
.build_int_s_extend(iv, codegen.context.i64_type(), "int2i64")
|
||||
.map_err(|e| e.to_string())?
|
||||
}
|
||||
}
|
||||
BVE::PointerValue(_) => unreachable!(),
|
||||
_ => return Err("console.log arg conversion failed".to_string()),
|
||||
};
|
||||
let fnty = codegen
|
||||
.context
|
||||
.i64_type()
|
||||
.fn_type(&[codegen.context.i64_type().into()], false);
|
||||
let fname = if iface_name == "env.console" {
|
||||
match method_name {
|
||||
"log" => "nyash.console.log_handle",
|
||||
"warn" => "nyash.console.warn_handle",
|
||||
_ => "nyash.console.error_handle",
|
||||
}
|
||||
} else {
|
||||
"nyash.debug.trace_handle"
|
||||
};
|
||||
let callee = codegen
|
||||
.module
|
||||
.get_function(fname)
|
||||
.unwrap_or_else(|| codegen.module.add_function(fname, fnty, None));
|
||||
let _ = codegen
|
||||
.builder
|
||||
.build_call(callee, &[arg_val.into()], "console_log_h")
|
||||
.map_err(|e| e.to_string())?;
|
||||
if let Some(d) = dst {
|
||||
vmap.insert(*d, codegen.context.i64_type().const_zero().into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn lower_readline<'ctx>(
|
||||
codegen: &CodegenContext<'ctx>,
|
||||
vmap: &mut HashMap<ValueId, BVE<'ctx>>,
|
||||
dst: &Option<ValueId>,
|
||||
args: &[ValueId],
|
||||
) -> Result<(), String> {
|
||||
if !args.is_empty() {
|
||||
return Err("console.readLine expects 0 args".to_string());
|
||||
}
|
||||
let i8p = codegen.context.ptr_type(AddressSpace::from(0));
|
||||
let fnty = i8p.fn_type(&[], false);
|
||||
let callee = codegen
|
||||
.module
|
||||
.get_function("nyash.console.readline")
|
||||
.unwrap_or_else(|| codegen.module.add_function("nyash.console.readline", fnty, None));
|
||||
let call = codegen
|
||||
.builder
|
||||
.build_call(callee, &[], "readline")
|
||||
.map_err(|e| e.to_string())?;
|
||||
if let Some(d) = dst {
|
||||
let rv = call
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("readline returned void".to_string())?;
|
||||
vmap.insert(*d, rv);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
356
src/backend/llvm/compiler/codegen/instructions/externcall/env.rs
Normal file
356
src/backend/llvm/compiler/codegen/instructions/externcall/env.rs
Normal file
@ -0,0 +1,356 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use inkwell::values::BasicValueEnum as BVE;
|
||||
use inkwell::AddressSpace;
|
||||
|
||||
use crate::backend::llvm::context::CodegenContext;
|
||||
use crate::mir::{function::MirFunction, ValueId};
|
||||
|
||||
pub(super) fn lower_future_spawn_instance<'ctx>(
|
||||
codegen: &CodegenContext<'ctx>,
|
||||
vmap: &mut HashMap<ValueId, BVE<'ctx>>,
|
||||
dst: &Option<ValueId>,
|
||||
args: &[ValueId],
|
||||
) -> Result<(), String> {
|
||||
if args.len() < 2 {
|
||||
return Err("env.future.spawn_instance expects at least (recv, method_name)".to_string());
|
||||
}
|
||||
let i64t = codegen.context.i64_type();
|
||||
let i8p = codegen.context.ptr_type(AddressSpace::from(0));
|
||||
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")
|
||||
.map_err(|e| e.to_string())?,
|
||||
_ => return Err("spawn_instance recv must be int or ptr".to_string()),
|
||||
};
|
||||
let name_v = *vmap.get(&args[1]).ok_or("method name missing")?;
|
||||
let name_p = match name_v {
|
||||
BVE::PointerValue(pv) => pv,
|
||||
_ => return Err("spawn_instance method name must be i8*".to_string()),
|
||||
};
|
||||
let fnty = i64t.fn_type(&[i64t.into(), i8p.into()], false);
|
||||
let callee = codegen
|
||||
.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")
|
||||
.map_err(|e| e.to_string())?;
|
||||
if let Some(d) = dst {
|
||||
let rv = call
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("spawn_instance returned void".to_string())?;
|
||||
vmap.insert(*d, rv);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn lower_local_get<'ctx>(
|
||||
codegen: &CodegenContext<'ctx>,
|
||||
func: &MirFunction,
|
||||
vmap: &mut HashMap<ValueId, BVE<'ctx>>,
|
||||
dst: &Option<ValueId>,
|
||||
args: &[ValueId],
|
||||
) -> Result<(), String> {
|
||||
if args.len() != 1 {
|
||||
return Err("env.local.get expects 1 arg".to_string());
|
||||
}
|
||||
let name_v = *vmap.get(&args[0]).ok_or("local.get name missing")?;
|
||||
let name_p = if let BVE::PointerValue(pv) = name_v {
|
||||
pv
|
||||
} else {
|
||||
return Err("env.local.get name must be i8*".to_string());
|
||||
};
|
||||
let i64t = codegen.context.i64_type();
|
||||
let i8p = codegen.context.ptr_type(AddressSpace::from(0));
|
||||
let fnty = i64t.fn_type(&[i8p.into()], false);
|
||||
let callee = codegen
|
||||
.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")
|
||||
.map_err(|e| e.to_string())?;
|
||||
let rv = call
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("local.get returned void".to_string())?;
|
||||
// Cast handle to pointer for Box-like return types
|
||||
if let Some(d) = dst {
|
||||
if let Some(mt) = func.metadata.value_types.get(d) {
|
||||
match mt {
|
||||
crate::mir::MirType::Integer | crate::mir::MirType::Bool => {
|
||||
vmap.insert(*d, rv);
|
||||
}
|
||||
crate::mir::MirType::String => {
|
||||
// keep as handle (i64)
|
||||
vmap.insert(*d, rv);
|
||||
}
|
||||
crate::mir::MirType::Box(_)
|
||||
| crate::mir::MirType::Array(_)
|
||||
| crate::mir::MirType::Future(_)
|
||||
| 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")
|
||||
.map_err(|e| e.to_string())?;
|
||||
vmap.insert(*d, ptr.into());
|
||||
}
|
||||
_ => {
|
||||
vmap.insert(*d, rv);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
vmap.insert(*d, rv);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn lower_box_new<'ctx>(
|
||||
codegen: &CodegenContext<'ctx>,
|
||||
vmap: &mut HashMap<ValueId, BVE<'ctx>>,
|
||||
dst: &Option<ValueId>,
|
||||
args: &[ValueId],
|
||||
) -> Result<(), String> {
|
||||
// Two variants: (name) and (argc, arg1, arg2, arg3, arg4) with optional ptr conversion
|
||||
// Prefer the i64 birth when possible; else call env.box.new(name)
|
||||
let i64t = codegen.context.i64_type();
|
||||
let i8p = codegen.context.ptr_type(AddressSpace::from(0));
|
||||
if args.len() == 1 {
|
||||
let name_v = *vmap.get(&args[0]).ok_or("env.box.new name missing")?;
|
||||
let name_p = if let BVE::PointerValue(pv) = name_v {
|
||||
pv
|
||||
} else {
|
||||
return Err("env.box.new name must be i8*".to_string());
|
||||
};
|
||||
let fnty = i64t.fn_type(&[i8p.into()], false);
|
||||
let callee = codegen
|
||||
.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")
|
||||
.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")
|
||||
.map_err(|e| e.to_string())?;
|
||||
if let Some(d) = dst {
|
||||
vmap.insert(*d, out_ptr.into());
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
if !args.is_empty() {
|
||||
// argc + up to 4 i64 payloads: build i64 via conversions
|
||||
let argc_val = i64t.const_int(args.len() as u64, false);
|
||||
let fnty = i64t.fn_type(
|
||||
&[
|
||||
i8p.into(),
|
||||
i64t.into(),
|
||||
i64t.into(),
|
||||
i64t.into(),
|
||||
i64t.into(),
|
||||
i64t.into(),
|
||||
],
|
||||
false,
|
||||
);
|
||||
let callee = codegen
|
||||
.module
|
||||
.get_function("nyash.env.box.new_i64")
|
||||
.unwrap_or_else(|| codegen.module.add_function("nyash.env.box.new_i64", fnty, None));
|
||||
// arg0: type name string pointer
|
||||
if args.is_empty() {
|
||||
return Err("env.box.new_i64 requires at least type name".to_string());
|
||||
}
|
||||
let ty_ptr = match *vmap.get(&args[0]).ok_or("type name missing")? {
|
||||
BVE::PointerValue(pv) => pv,
|
||||
_ => return Err("env.box.new_i64 arg0 must be i8* type name".to_string()),
|
||||
};
|
||||
let mut a1 = i64t.const_zero();
|
||||
if args.len() >= 2 {
|
||||
let bv = *vmap.get(&args[1]).ok_or("arg missing")?;
|
||||
a1 = match bv {
|
||||
BVE::IntValue(iv) => iv,
|
||||
BVE::FloatValue(fv) => {
|
||||
let fnty = i64t.fn_type(&[codegen.context.f64_type().into()], false);
|
||||
let callee = codegen
|
||||
.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")
|
||||
.map_err(|e| e.to_string())?;
|
||||
let rv = call
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("from_f64 returned void".to_string())?;
|
||||
if let BVE::IntValue(h) = rv { h } else { return Err("from_f64 ret expected i64".to_string()); }
|
||||
}
|
||||
BVE::PointerValue(pv) => {
|
||||
let fnty = i64t.fn_type(&[i8p.into()], false);
|
||||
let callee = codegen
|
||||
.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")
|
||||
.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()); }
|
||||
}
|
||||
_ => return Err("unsupported arg value for env.box.new".to_string()),
|
||||
};
|
||||
}
|
||||
let mut a2 = i64t.const_zero();
|
||||
if args.len() >= 3 {
|
||||
let bv = *vmap.get(&args[2]).ok_or("arg missing")?;
|
||||
a2 = match bv {
|
||||
BVE::IntValue(iv) => iv,
|
||||
BVE::FloatValue(fv) => {
|
||||
let fnty = i64t.fn_type(&[codegen.context.f64_type().into()], false);
|
||||
let callee = codegen
|
||||
.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")
|
||||
.map_err(|e| e.to_string())?;
|
||||
let rv = call
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("from_f64 returned void".to_string())?;
|
||||
if let BVE::IntValue(h) = rv { h } else { return Err("from_f64 ret expected i64".to_string()); }
|
||||
}
|
||||
BVE::PointerValue(pv) => {
|
||||
let fnty = i64t.fn_type(&[i8p.into()], false);
|
||||
let callee = codegen
|
||||
.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")
|
||||
.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()); }
|
||||
}
|
||||
_ => return Err("unsupported arg value for env.box.new".to_string()),
|
||||
};
|
||||
}
|
||||
let mut a3 = i64t.const_zero();
|
||||
if args.len() >= 4 {
|
||||
let bv = *vmap.get(&args[3]).ok_or("arg missing")?;
|
||||
a3 = match bv {
|
||||
BVE::IntValue(iv) => iv,
|
||||
BVE::FloatValue(fv) => {
|
||||
let fnty = i64t.fn_type(&[codegen.context.f64_type().into()], false);
|
||||
let callee = codegen
|
||||
.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")
|
||||
.map_err(|e| e.to_string())?;
|
||||
let rv = call
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("from_f64 returned void".to_string())?;
|
||||
if let BVE::IntValue(h) = rv { h } else { return Err("from_f64 ret expected i64".to_string()); }
|
||||
}
|
||||
BVE::PointerValue(pv) => {
|
||||
let fnty = i64t.fn_type(&[i8p.into()], false);
|
||||
let callee = codegen
|
||||
.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")
|
||||
.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()); }
|
||||
}
|
||||
_ => return Err("unsupported arg value for env.box.new".to_string()),
|
||||
};
|
||||
}
|
||||
let mut a4 = i64t.const_zero();
|
||||
if args.len() >= 5 {
|
||||
let bv = *vmap.get(&args[4]).ok_or("arg missing")?;
|
||||
a4 = match bv {
|
||||
BVE::IntValue(iv) => iv,
|
||||
BVE::FloatValue(fv) => {
|
||||
let fnty = i64t.fn_type(&[codegen.context.f64_type().into()], false);
|
||||
let callee = codegen
|
||||
.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")
|
||||
.map_err(|e| e.to_string())?;
|
||||
let rv = call
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("from_f64 returned void".to_string())?;
|
||||
if let BVE::IntValue(h) = rv { h } else { return Err("from_f64 ret expected i64".to_string()); }
|
||||
}
|
||||
BVE::PointerValue(pv) => {
|
||||
let fnty = i64t.fn_type(&[i8p.into()], false);
|
||||
let callee = codegen
|
||||
.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")
|
||||
.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()); }
|
||||
}
|
||||
_ => return Err("unsupported arg value for env.box.new".to_string()),
|
||||
};
|
||||
}
|
||||
let call = codegen
|
||||
.builder
|
||||
.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")
|
||||
.map_err(|e| e.to_string())?;
|
||||
if let Some(d) = dst {
|
||||
vmap.insert(*d, out_ptr.into());
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
Err("env.box.new requires at least 1 arg".to_string())
|
||||
}
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
mod console;
|
||||
mod env;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::backend::llvm::context::CodegenContext;
|
||||
use crate::mir::{function::MirFunction, ValueId};
|
||||
use inkwell::values::BasicValueEnum as BVE;
|
||||
|
||||
/// Full ExternCall lowering dispatcher (console/debug/env.*)
|
||||
pub(in super::super) fn lower_externcall<'ctx>(
|
||||
codegen: &CodegenContext<'ctx>,
|
||||
func: &MirFunction,
|
||||
vmap: &mut HashMap<ValueId, BVE<'ctx>>,
|
||||
dst: &Option<ValueId>,
|
||||
iface_name: &str,
|
||||
method_name: &str,
|
||||
args: &[ValueId],
|
||||
) -> Result<(), String> {
|
||||
// console/debug
|
||||
if (iface_name == "env.console"
|
||||
&& matches!(method_name, "log" | "warn" | "error"))
|
||||
|| (iface_name == "env.debug" && method_name == "trace")
|
||||
{
|
||||
return console::lower_log_or_trace(codegen, vmap, dst, iface_name, method_name, args);
|
||||
}
|
||||
if iface_name == "env.console" && method_name == "readLine" {
|
||||
return console::lower_readline(codegen, vmap, dst, args);
|
||||
}
|
||||
|
||||
// env.*
|
||||
if iface_name == "env.future" && method_name == "spawn_instance" {
|
||||
return env::lower_future_spawn_instance(codegen, vmap, dst, args);
|
||||
}
|
||||
if iface_name == "env.local" && method_name == "get" {
|
||||
return env::lower_local_get(codegen, func, vmap, dst, args);
|
||||
}
|
||||
if iface_name == "env.box" && method_name == "new" {
|
||||
return env::lower_box_new(codegen, vmap, dst, args);
|
||||
}
|
||||
|
||||
Err(format!(
|
||||
"ExternCall lowering unsupported: {}.{} (add a NyRT shim for this interface method)",
|
||||
iface_name, method_name
|
||||
))
|
||||
}
|
||||
|
||||
@ -146,6 +146,82 @@ pub(super) fn try_handle_string_method<'ctx>(
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
// substring(start, end) -> i8*
|
||||
if method == "substring" {
|
||||
if args.len() != 2 {
|
||||
return Err("String.substring expects 2 args (start, end)".to_string());
|
||||
}
|
||||
let i64t = codegen.context.i64_type();
|
||||
let i8p = codegen.context.ptr_type(AddressSpace::from(0));
|
||||
// receiver must be i8* for this fast path
|
||||
let recv_p = match recv_v {
|
||||
BVE::PointerValue(p) => p,
|
||||
_ => return Ok(false),
|
||||
};
|
||||
let a0 = *vmap.get(&args[0]).ok_or("substring start arg missing")?;
|
||||
let a1 = *vmap.get(&args[1]).ok_or("substring end arg missing")?;
|
||||
let s = match a0 {
|
||||
BVE::IntValue(iv) => iv,
|
||||
_ => return Err("substring start must be integer".to_string()),
|
||||
};
|
||||
let e = match a1 {
|
||||
BVE::IntValue(iv) => iv,
|
||||
_ => return Err("substring end must be integer".to_string()),
|
||||
};
|
||||
let fnty = i8p.fn_type(&[i8p.into(), i64t.into(), i64t.into()], false);
|
||||
let callee = codegen
|
||||
.module
|
||||
.get_function("nyash.string.substring_sii")
|
||||
.unwrap_or_else(|| codegen.module.add_function("nyash.string.substring_sii", fnty, None));
|
||||
let call = codegen
|
||||
.builder
|
||||
.build_call(callee, &[recv_p.into(), s.into(), e.into()], "substring_call")
|
||||
.map_err(|e| e.to_string())?;
|
||||
if let Some(d) = dst {
|
||||
let rv = call
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("substring returned void".to_string())?;
|
||||
vmap.insert(*d, rv);
|
||||
}
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
// lastIndexOf(needle) -> i64
|
||||
if method == "lastIndexOf" {
|
||||
if args.len() != 1 {
|
||||
return Err("String.lastIndexOf expects 1 arg".to_string());
|
||||
}
|
||||
let i64t = codegen.context.i64_type();
|
||||
let i8p = codegen.context.ptr_type(AddressSpace::from(0));
|
||||
// receiver must be i8* for this fast path
|
||||
let recv_p = match recv_v {
|
||||
BVE::PointerValue(p) => p,
|
||||
_ => return Ok(false),
|
||||
};
|
||||
let a0 = *vmap.get(&args[0]).ok_or("lastIndexOf arg missing")?;
|
||||
let needle_p = match a0 {
|
||||
BVE::PointerValue(p) => p,
|
||||
_ => return Err("lastIndexOf arg must be i8*".to_string()),
|
||||
};
|
||||
let fnty = i64t.fn_type(&[i8p.into(), i8p.into()], false);
|
||||
let callee = codegen
|
||||
.module
|
||||
.get_function("nyash.string.lastIndexOf_ss")
|
||||
.unwrap_or_else(|| codegen.module.add_function("nyash.string.lastIndexOf_ss", fnty, None));
|
||||
let call = codegen
|
||||
.builder
|
||||
.build_call(callee, &[recv_p.into(), needle_p.into()], "lastindexof_call")
|
||||
.map_err(|e| e.to_string())?;
|
||||
if let Some(d) = dst {
|
||||
let rv = call
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("lastIndexOf returned void".to_string())?;
|
||||
vmap.insert(*d, rv);
|
||||
}
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
|
||||
@ -36,122 +36,152 @@ impl LLVMCompiler {
|
||||
}
|
||||
let context = Context::create();
|
||||
let codegen = CodegenContext::new(&context, "nyash_module")?;
|
||||
// Lower only Main.main for now
|
||||
// Load box type-id mapping from nyash_box.toml (central plugin registry)
|
||||
let box_type_ids = crate::backend::llvm::box_types::load_box_type_ids();
|
||||
|
||||
// Utility: sanitize MIR function name to a valid C symbol
|
||||
let sanitize = |name: &str| -> String {
|
||||
name.chars()
|
||||
.map(|c| match c {
|
||||
'.' | '/' | '-' => '_',
|
||||
other => other,
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
||||
// Find entry function
|
||||
let func = if let Some((_n, f)) = mir_module
|
||||
let (entry_name, _entry_func_ref) = if let Some((n, f)) = mir_module
|
||||
.functions
|
||||
.iter()
|
||||
.find(|(_n, f)| f.metadata.is_entry_point)
|
||||
{
|
||||
f
|
||||
(n.clone(), f)
|
||||
} else if let Some(f) = mir_module.functions.get("Main.main") {
|
||||
f
|
||||
("Main.main".to_string(), f)
|
||||
} else if let Some(f) = mir_module.functions.get("main") {
|
||||
f
|
||||
} else if let Some((_n, f)) = mir_module.functions.iter().next() {
|
||||
f
|
||||
("main".to_string(), f)
|
||||
} else if let Some((n, f)) = mir_module.functions.iter().next() {
|
||||
(n.clone(), f)
|
||||
} else {
|
||||
return Err("Main.main function not found in module".to_string());
|
||||
};
|
||||
|
||||
// Map MIR types to LLVM types via helpers
|
||||
|
||||
// Load box type-id mapping from nyash_box.toml (central plugin registry)
|
||||
let box_type_ids = crate::backend::llvm::box_types::load_box_type_ids();
|
||||
|
||||
// Function type
|
||||
let ret_type = match func.signature.return_type {
|
||||
crate::mir::MirType::Void => None,
|
||||
ref t => Some(map_type(codegen.context, t)?),
|
||||
};
|
||||
let fn_type = match ret_type {
|
||||
Some(BasicTypeEnum::IntType(t)) => t.fn_type(&[], false),
|
||||
Some(BasicTypeEnum::FloatType(t)) => t.fn_type(&[], false),
|
||||
Some(BasicTypeEnum::PointerType(t)) => t.fn_type(&[], false),
|
||||
Some(_) => return Err("Unsupported return basic type".to_string()),
|
||||
None => codegen.context.void_type().fn_type(&[], false),
|
||||
};
|
||||
let llvm_func = codegen.module.add_function("ny_main", fn_type, None);
|
||||
|
||||
// Create LLVM basic blocks: ensure entry is created first to be function entry
|
||||
let (mut bb_map, entry_bb) = instructions::create_basic_blocks(&codegen, llvm_func, func);
|
||||
|
||||
// Position at entry
|
||||
codegen.builder.position_at_end(entry_bb);
|
||||
|
||||
// SSA value map
|
||||
let mut vmap: HashMap<ValueId, BasicValueEnum> = HashMap::new();
|
||||
|
||||
// Helper ops are now provided by codegen/types.rs
|
||||
|
||||
// Pre-create allocas for locals on demand (entry-only builder)
|
||||
let mut allocas: HashMap<ValueId, PointerValue> = HashMap::new();
|
||||
let entry_builder = codegen.context.create_builder();
|
||||
entry_builder.position_at_end(entry_bb);
|
||||
|
||||
// Helper: map MirType to LLVM basic type (value type) is provided by types::map_mirtype_to_basic
|
||||
|
||||
// Helper: create (or get) an alloca for a given pointer-typed SSA value id
|
||||
let mut alloca_elem_types: HashMap<ValueId, BasicTypeEnum> = HashMap::new();
|
||||
|
||||
// Pre-create PHI nodes for all blocks (so we can add incoming from predecessors)
|
||||
let mut phis_by_block: HashMap<
|
||||
crate::mir::BasicBlockId,
|
||||
Vec<(ValueId, PhiValue, Vec<(crate::mir::BasicBlockId, ValueId)>)>,
|
||||
> = HashMap::new();
|
||||
for bid in func.block_ids() {
|
||||
let bb = *bb_map.get(&bid).ok_or("missing bb in map")?;
|
||||
// Position at start of the block (no instructions emitted yet)
|
||||
codegen.builder.position_at_end(bb);
|
||||
let block = func.blocks.get(&bid).unwrap();
|
||||
for inst in block
|
||||
.instructions
|
||||
.iter()
|
||||
.take_while(|i| matches!(i, MirInstruction::Phi { .. }))
|
||||
{
|
||||
if let MirInstruction::Phi { dst, inputs } = inst {
|
||||
// Decide PHI type: prefer annotated value type; fallback to first input's annotated type; finally i64
|
||||
let mut phi_ty: Option<BasicTypeEnum> = None;
|
||||
if let Some(mt) = func.metadata.value_types.get(dst) {
|
||||
phi_ty = Some(map_mirtype_to_basic(codegen.context, mt));
|
||||
} else if let Some((_, iv)) = inputs.first() {
|
||||
if let Some(mt) = func.metadata.value_types.get(iv) {
|
||||
phi_ty = Some(map_mirtype_to_basic(codegen.context, mt));
|
||||
}
|
||||
}
|
||||
let phi_ty = phi_ty.unwrap_or_else(|| codegen.context.i64_type().into());
|
||||
let phi = codegen
|
||||
.builder
|
||||
.build_phi(phi_ty, &format!("phi_{}", dst.as_u32()))
|
||||
.map_err(|e| e.to_string())?;
|
||||
vmap.insert(*dst, phi.as_basic_value());
|
||||
phis_by_block
|
||||
.entry(bid)
|
||||
.or_default()
|
||||
.push((*dst, phi, inputs.clone()));
|
||||
}
|
||||
// Predeclare all MIR functions as LLVM functions
|
||||
let mut llvm_funcs: HashMap<String, FunctionValue> = HashMap::new();
|
||||
for (name, f) in &mir_module.functions {
|
||||
let ret_bt = match f.signature.return_type {
|
||||
crate::mir::MirType::Void => codegen.context.i64_type().into(),
|
||||
ref t => map_type(codegen.context, t)?,
|
||||
};
|
||||
let mut params_bt: Vec<BasicTypeEnum> = Vec::new();
|
||||
for pt in &f.signature.params {
|
||||
params_bt.push(map_type(codegen.context, pt)?);
|
||||
}
|
||||
let ll_fn_ty = match ret_bt {
|
||||
BasicTypeEnum::IntType(t) => t.fn_type(¶ms_bt.iter().map(|t| (*t).into()).collect::<Vec<_>>(), false),
|
||||
BasicTypeEnum::FloatType(t) => t.fn_type(¶ms_bt.iter().map(|t| (*t).into()).collect::<Vec<_>>(), false),
|
||||
BasicTypeEnum::PointerType(t) => t.fn_type(¶ms_bt.iter().map(|t| (*t).into()).collect::<Vec<_>>(), false),
|
||||
_ => return Err("Unsupported return basic type".to_string()),
|
||||
};
|
||||
let sym = format!("ny_f_{}", sanitize(name));
|
||||
let lf = codegen.module.add_function(&sym, ll_fn_ty, None);
|
||||
llvm_funcs.insert(name.clone(), lf);
|
||||
}
|
||||
|
||||
// Lower in block order
|
||||
for bid in func.block_ids() {
|
||||
let bb = *bb_map.get(&bid).unwrap();
|
||||
if codegen
|
||||
.builder
|
||||
.get_insert_block()
|
||||
.map(|b| b != bb)
|
||||
.unwrap_or(true)
|
||||
{
|
||||
codegen.builder.position_at_end(bb);
|
||||
}
|
||||
let block = func.blocks.get(&bid).unwrap();
|
||||
for inst in &block.instructions {
|
||||
match inst {
|
||||
MirInstruction::NewBox { dst, box_type, args } => {
|
||||
instructions::lower_newbox(&codegen, &mut vmap, *dst, box_type, args, &box_type_ids)?;
|
||||
// Helper to build a map of ValueId -> const string for each function (to resolve call targets)
|
||||
let build_const_str_map = |f: &crate::mir::function::MirFunction| -> HashMap<ValueId, String> {
|
||||
let mut m = HashMap::new();
|
||||
for bid in f.block_ids() {
|
||||
if let Some(b) = f.blocks.get(&bid) {
|
||||
for inst in &b.instructions {
|
||||
if let MirInstruction::Const { dst, value: ConstValue::String(s) } = inst {
|
||||
m.insert(*dst, s.clone());
|
||||
}
|
||||
}
|
||||
MirInstruction::Const { dst, value } => {
|
||||
let bval = match value {
|
||||
if let Some(MirInstruction::Const { dst, value: ConstValue::String(s) }) = &b.terminator {
|
||||
m.insert(*dst, s.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
m
|
||||
};
|
||||
|
||||
// Lower all functions
|
||||
for (name, func) in &mir_module.functions {
|
||||
let llvm_func = *llvm_funcs.get(name).ok_or("predecl not found")?;
|
||||
// Create basic blocks
|
||||
let (mut bb_map, entry_bb) = instructions::create_basic_blocks(&codegen, llvm_func, func);
|
||||
codegen.builder.position_at_end(entry_bb);
|
||||
let mut vmap: HashMap<ValueId, BasicValueEnum> = HashMap::new();
|
||||
let mut allocas: HashMap<ValueId, PointerValue> = HashMap::new();
|
||||
let entry_builder = codegen.context.create_builder();
|
||||
entry_builder.position_at_end(entry_bb);
|
||||
let mut alloca_elem_types: HashMap<ValueId, BasicTypeEnum> = HashMap::new();
|
||||
let mut phis_by_block: HashMap<
|
||||
crate::mir::BasicBlockId,
|
||||
Vec<(ValueId, PhiValue, Vec<(crate::mir::BasicBlockId, ValueId)>)>,
|
||||
> = HashMap::new();
|
||||
// Bind parameters
|
||||
for (i, pid) in func.params.iter().enumerate() {
|
||||
if let Some(av) = llvm_func.get_nth_param(i as u32) {
|
||||
vmap.insert(*pid, av);
|
||||
}
|
||||
}
|
||||
// Precreate phis
|
||||
for bid in func.block_ids() {
|
||||
let bb = *bb_map.get(&bid).ok_or("missing bb in map")?;
|
||||
codegen.builder.position_at_end(bb);
|
||||
let block = func.blocks.get(&bid).unwrap();
|
||||
for inst in block
|
||||
.instructions
|
||||
.iter()
|
||||
.take_while(|i| matches!(i, MirInstruction::Phi { .. }))
|
||||
{
|
||||
if let MirInstruction::Phi { dst, inputs } = inst {
|
||||
let mut phi_ty: Option<BasicTypeEnum> = None;
|
||||
if let Some(mt) = func.metadata.value_types.get(dst) {
|
||||
phi_ty = Some(map_mirtype_to_basic(codegen.context, mt));
|
||||
} else if let Some((_, iv)) = inputs.first() {
|
||||
if let Some(mt) = func.metadata.value_types.get(iv) {
|
||||
phi_ty = Some(map_mirtype_to_basic(codegen.context, mt));
|
||||
}
|
||||
}
|
||||
let phi_ty = phi_ty.unwrap_or_else(|| codegen.context.i64_type().into());
|
||||
let phi = codegen
|
||||
.builder
|
||||
.build_phi(phi_ty, &format!("phi_{}", dst.as_u32()))
|
||||
.map_err(|e| e.to_string())?;
|
||||
vmap.insert(*dst, phi.as_basic_value());
|
||||
phis_by_block
|
||||
.entry(bid)
|
||||
.or_default()
|
||||
.push((*dst, phi, inputs.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Map of const strings for Call resolution
|
||||
let const_strs = build_const_str_map(func);
|
||||
|
||||
// Lower body
|
||||
for bid in func.block_ids() {
|
||||
let bb = *bb_map.get(&bid).unwrap();
|
||||
if codegen
|
||||
.builder
|
||||
.get_insert_block()
|
||||
.map(|b| b != bb)
|
||||
.unwrap_or(true)
|
||||
{
|
||||
codegen.builder.position_at_end(bb);
|
||||
}
|
||||
let block = func.blocks.get(&bid).unwrap();
|
||||
for inst in &block.instructions {
|
||||
match inst {
|
||||
MirInstruction::NewBox { dst, box_type, args } => {
|
||||
instructions::lower_newbox(&codegen, &mut vmap, *dst, box_type, args, &box_type_ids)?;
|
||||
}
|
||||
MirInstruction::Const { dst, value } => {
|
||||
let bval = match value {
|
||||
ConstValue::Integer(i) => {
|
||||
codegen.context.i64_type().const_int(*i as u64, true).into()
|
||||
}
|
||||
@ -209,16 +239,42 @@ impl LLVMCompiler {
|
||||
.into(),
|
||||
ConstValue::Void => return Err("Const Void unsupported".to_string()),
|
||||
};
|
||||
vmap.insert(*dst, bval);
|
||||
}
|
||||
MirInstruction::BoxCall {
|
||||
dst,
|
||||
box_val,
|
||||
method,
|
||||
method_id,
|
||||
args,
|
||||
effects: _,
|
||||
} => {
|
||||
vmap.insert(*dst, bval);
|
||||
}
|
||||
MirInstruction::Call { dst, func: callee, args, .. } => {
|
||||
// Resolve callee name from const string -> lookup predeclared function
|
||||
let name_s = const_strs.get(callee).ok_or_else(|| format!("call: callee value {} not a const string", callee.as_u32()))?;
|
||||
let sym = format!("ny_f_{}", sanitize(name_s));
|
||||
let target = codegen
|
||||
.module
|
||||
.get_function(&sym)
|
||||
.ok_or_else(|| format!("call: function symbol not found: {}", sym))?;
|
||||
// Collect args
|
||||
let mut avs: Vec<BasicValueEnum> = Vec::new();
|
||||
for a in args {
|
||||
let v = *vmap
|
||||
.get(a)
|
||||
.ok_or_else(|| format!("call arg missing: {}", a.as_u32()))?;
|
||||
avs.push(v);
|
||||
}
|
||||
let call = codegen
|
||||
.builder
|
||||
.build_call(target, &avs.iter().map(|v| (*v).into()).collect::<Vec<_>>(), "call")
|
||||
.map_err(|e| e.to_string())?;
|
||||
if let Some(d) = dst {
|
||||
if let Some(rv) = call.try_as_basic_value().left() {
|
||||
vmap.insert(*d, rv);
|
||||
}
|
||||
}
|
||||
}
|
||||
MirInstruction::BoxCall {
|
||||
dst,
|
||||
box_val,
|
||||
method,
|
||||
method_id,
|
||||
args,
|
||||
effects: _,
|
||||
} => {
|
||||
// Delegate to refactored lowering and skip legacy body
|
||||
instructions::lower_boxcall(
|
||||
&codegen,
|
||||
@ -521,7 +577,7 @@ impl LLVMCompiler {
|
||||
}
|
||||
}
|
||||
MirInstruction::Compare { dst, op, lhs, rhs } => {
|
||||
let out = instructions::lower_compare(&codegen, &vmap, op, lhs, rhs)?;
|
||||
let out = instructions::lower_compare(&codegen, func, &vmap, op, lhs, rhs)?;
|
||||
vmap.insert(*dst, out);
|
||||
}
|
||||
MirInstruction::Store { value, ptr } => {
|
||||
@ -550,11 +606,59 @@ impl LLVMCompiler {
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// Verify per-function
|
||||
if !llvm_func.verify(true) {
|
||||
return Err(format!("Function verification failed: {}", name));
|
||||
}
|
||||
}
|
||||
|
||||
// Verify and emit
|
||||
if !llvm_func.verify(true) {
|
||||
return Err("Function verification failed".to_string());
|
||||
// Build entry wrapper ny_main -> call entry function
|
||||
let i64t = codegen.context.i64_type();
|
||||
let ny_main_ty = i64t.fn_type(&[], false);
|
||||
let ny_main = codegen.module.add_function("ny_main", ny_main_ty, None);
|
||||
let entry_bb = codegen.context.append_basic_block(ny_main, "entry");
|
||||
codegen.builder.position_at_end(entry_bb);
|
||||
let entry_sym = format!("ny_f_{}", sanitize(&entry_name));
|
||||
let entry_fn = codegen
|
||||
.module
|
||||
.get_function(&entry_sym)
|
||||
.ok_or_else(|| format!("entry function symbol not found: {}", entry_sym))?;
|
||||
let call = codegen
|
||||
.builder
|
||||
.build_call(entry_fn, &[], "call_main")
|
||||
.map_err(|e| e.to_string())?;
|
||||
let rv = call.try_as_basic_value().left();
|
||||
// Normalize to i64 return
|
||||
let ret_v = if let Some(v) = rv {
|
||||
match v {
|
||||
BasicValueEnum::IntValue(iv) => {
|
||||
if iv.get_type().get_bit_width() == 64 {
|
||||
iv
|
||||
} else {
|
||||
codegen
|
||||
.builder
|
||||
.build_int_z_extend(iv, i64t, "ret_zext")
|
||||
.map_err(|e| e.to_string())?
|
||||
}
|
||||
}
|
||||
BasicValueEnum::PointerValue(pv) => codegen
|
||||
.builder
|
||||
.build_ptr_to_int(pv, i64t, "ret_p2i")
|
||||
.map_err(|e| e.to_string())?,
|
||||
BasicValueEnum::FloatValue(fv) => codegen
|
||||
.builder
|
||||
.build_float_to_signed_int(fv, i64t, "ret_f2i")
|
||||
.map_err(|e| e.to_string())?,
|
||||
_ => i64t.const_zero(),
|
||||
}
|
||||
} else {
|
||||
i64t.const_zero()
|
||||
};
|
||||
codegen.builder.build_return(Some(&ret_v)).map_err(|e| e.to_string())?;
|
||||
|
||||
// Verify and emit final object
|
||||
if !ny_main.verify(true) {
|
||||
return Err("ny_main verification failed".to_string());
|
||||
}
|
||||
// Try writing via file API first; if it succeeds but file is missing due to env/FS quirks,
|
||||
// also write via memory buffer as a fallback to ensure presence.
|
||||
|
||||
Reference in New Issue
Block a user