smokes: add curated LLVM runner; archive legacy smokes; PHI-off unified across Bridge/Builder; LLVM resolver tracing; minimal Throw lowering; config env getters; dev profile and root cleaner; docs updated; CI workflow runs curated LLVM (PHI-on/off)

This commit is contained in:
Selfhosting Dev
2025-09-16 23:49:36 +09:00
parent 97a76c0571
commit 5c9213cd03
104 changed files with 8094 additions and 2930 deletions

View File

@ -9,6 +9,8 @@ use super::super::types::{to_bool, map_mirtype_to_basic};
use super::builder_cursor::BuilderCursor;
use super::Resolver;
fn phi_trace_on() -> bool { std::env::var("NYASH_LLVM_TRACE_PHI").ok().as_deref() == Some("1") }
pub(in super::super) fn emit_return<'ctx, 'b>(
codegen: &CodegenContext<'ctx>,
cursor: &mut BuilderCursor<'ctx, 'b>,
@ -82,6 +84,9 @@ pub(in super::super) fn emit_jump<'ctx, 'b>(
cursor.emit_term(bid, |b| {
b.build_unconditional_branch(tbb).map_err(|e| e.to_string()).unwrap();
});
if phi_trace_on() {
eprintln!("[PHI:jump] pred={} -> succ={}", bid.as_u32(), target.as_u32());
}
Ok(())
}
@ -335,10 +340,10 @@ pub(in super::super) fn finalize_phis<'ctx, 'b>(
});
}
let pred_bb = *bb_map.get(pred).ok_or("pred bb missing")?;
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
if phi_trace_on() {
eprintln!(
"[PHI] finalize add pred_bb={} val={} ty={}",
pred.as_u32(), in_vid.as_u32(),
"[PHI:finalize] succ={} pred={} vid={} ty={}",
succ_bb.as_u32(), pred.as_u32(), in_vid.as_u32(),
phi.as_basic_value().get_type().print_to_string().to_string()
);
}
@ -359,10 +364,10 @@ pub(in super::super) fn finalize_phis<'ctx, 'b>(
BT::PointerType(pt) => pt.const_zero().into(),
_ => return Err("unsupported phi type for zero synth (finalize)".to_string()),
};
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
if phi_trace_on() {
eprintln!(
"[PHI] finalize add (synth) pred_bb={} zero-ty={}",
pred.as_u32(), bt.print_to_string().to_string()
"[PHI:finalize] succ={} pred={} vid=? ty={} src=synth_zero",
succ_bb.as_u32(), pred.as_u32(), bt.print_to_string().to_string()
);
}
match z {
@ -431,6 +436,12 @@ pub(in super::super) fn localize_to_i64<'ctx, 'b>(
};
});
phi.add_incoming(&[(&iv_out, pred_bb)]);
if phi_trace_on() {
eprintln!(
"[PHI:resolve] cur={} pred={} vid={} ty=i64",
cur_bid.as_u32(), p.as_u32(), vid.as_u32()
);
}
}
// Restore insertion point
if let Some(bb) = saved_ip { codegen.builder.position_at_end(bb); }

View File

@ -134,3 +134,49 @@ pub(in super::super) fn lower_load<'ctx, 'b>(
vmap.insert(*dst, lv);
Ok(())
}
// Lower Copy: define dst in the current block by localizing src via Resolver
pub(in super::super) fn lower_copy<'ctx, 'b>(
codegen: &CodegenContext<'ctx>,
cursor: &mut BuilderCursor<'ctx, 'b>,
resolver: &mut super::Resolver<'ctx>,
cur_bid: BasicBlockId,
func: &crate::mir::function::MirFunction,
vmap: &mut HashMap<ValueId, BasicValueEnum<'ctx>>,
dst: &ValueId,
src: &ValueId,
bb_map: &std::collections::HashMap<crate::mir::BasicBlockId, inkwell::basic_block::BasicBlock<'ctx>>,
preds: &std::collections::HashMap<crate::mir::BasicBlockId, Vec<crate::mir::BasicBlockId>>,
block_end_values: &std::collections::HashMap<crate::mir::BasicBlockId, std::collections::HashMap<ValueId, BasicValueEnum<'ctx>>>,
) -> Result<(), String> {
// Choose resolution kind based on metadata type preference
use inkwell::types::BasicTypeEnum as BT;
let expected_bt: Option<BT> = func
.metadata
.value_types
.get(dst)
.or_else(|| func.metadata.value_types.get(src))
.map(|mt| super::super::types::map_mirtype_to_basic(codegen.context, mt));
let out: BasicValueEnum<'ctx> = match expected_bt {
Some(BT::IntType(_)) | None => {
// Prefer i64 for unknown
let iv = resolver.resolve_i64(codegen, cursor, cur_bid, *src, bb_map, preds, block_end_values, vmap)?;
iv.into()
}
Some(BT::PointerType(_)) => {
let pv = resolver.resolve_ptr(codegen, cursor, cur_bid, *src, bb_map, preds, block_end_values, vmap)?;
pv.into()
}
Some(BT::FloatType(_)) => {
let fv = resolver.resolve_f64(codegen, cursor, cur_bid, *src, bb_map, preds, block_end_values, vmap)?;
fv.into()
}
_ => {
// Fallback i64
let iv = resolver.resolve_i64(codegen, cursor, cur_bid, *src, bb_map, preds, block_end_values, vmap)?;
iv.into()
}
};
vmap.insert(*dst, out);
Ok(())
}

View File

@ -24,6 +24,7 @@ pub(super) use newbox::lower_newbox;
pub(super) use boxcall::{lower_boxcall, lower_boxcall_boxed, lower_boxcall_via_ctx};
pub(super) use arith::lower_compare;
pub(super) use mem::{lower_load, lower_store};
pub(super) use mem::lower_copy;
pub(super) use consts::lower_const;
pub(super) use arith_ops::{lower_binop, lower_unary};
pub(super) use call::lower_call;

View File

@ -767,6 +767,22 @@ impl LLVMCompiler {
instructions::lower_load(&codegen, &mut cursor, *bid, &mut vmap, &mut allocas, &mut alloca_elem_types, dst, ptr)?;
defined_in_block.insert(*dst);
},
MirInstruction::Copy { dst, src } => {
instructions::lower_copy(
&codegen,
&mut cursor,
&mut resolver,
*bid,
func,
&mut vmap,
dst,
src,
&bb_map,
&preds,
&block_end_values,
)?;
defined_in_block.insert(*dst);
},
MirInstruction::Phi { .. } => {
// Already created in pre-pass; nothing to do here.
}
@ -799,6 +815,23 @@ impl LLVMCompiler {
&block_end_values,
)?;
}
MirInstruction::Throw { .. } => {
// Minimal lowering: call llvm.trap (optional) then unreachable
let use_trap = std::env::var("NYASH_LLVM_TRAP_ON_THROW").ok().as_deref() != Some("0");
if use_trap {
let fn_ty = codegen.context.void_type().fn_type(&[], false);
let trap = codegen
.module
.get_function("llvm.trap")
.unwrap_or_else(|| codegen.module.add_function("llvm.trap", fn_ty, None));
cursor.emit_term(*bid, |b| {
let _ = b.build_call(trap, &[], "trap");
});
}
// Ensure we end the block
cursor.at_end(*bid, bb);
let _ = codegen.builder.build_unreachable();
}
MirInstruction::Jump { target } => {
// LoopForm simple body→dispatch wiring: if this block is a loop body
// and jumps back to its header, redirect to dispatch and add PHI incoming

View File

@ -24,28 +24,47 @@ fn main() {
// Read stdin to tmp/ny_mir_builder_input.json for re-use
let mut buf = Vec::new();
io::stdin().read_to_end(&mut buf).expect("read stdin");
if buf.is_empty() { eprintln!("error: no input on stdin"); std::process::exit(2); }
let cwd_tmp = Path::new("tmp"); let _ = fs::create_dir_all(cwd_tmp);
if buf.is_empty() {
eprintln!("error: no input on stdin");
std::process::exit(2);
}
let cwd_tmp = Path::new("tmp");
let _ = fs::create_dir_all(cwd_tmp);
let cwd_path = cwd_tmp.join("ny_mir_builder_input.json");
fs::write(&cwd_path, &buf).expect("write cwd tmp json");
cwd_path
} else {
let p = PathBuf::from(matches.get_one::<String>("in").unwrap());
if !p.exists() { eprintln!("error: input not found: {}", p.display()); std::process::exit(2); }
if !p.exists() {
eprintln!("error: input not found: {}", p.display());
std::process::exit(2);
}
p
};
let emit = matches.get_one::<String>("emit").unwrap().as_str();
let out_path = matches.get_one::<String>("out").map(|s| s.to_string()).unwrap_or_else(|| match emit {
"obj" => format!("{}/target/aot_objects/a.o", std::env::current_dir().unwrap().display()),
"ll" => format!("{}/target/aot_objects/a.ll", std::env::current_dir().unwrap().display()),
"exe" => "a.out".to_string(),
"json" => "/dev/stdout".to_string(),
_ => unreachable!(),
});
let out_path = matches
.get_one::<String>("out")
.map(|s| s.to_string())
.unwrap_or_else(|| match emit {
"obj" => format!(
"{}/target/aot_objects/a.o",
std::env::current_dir().unwrap().display()
),
"ll" => format!(
"{}/target/aot_objects/a.ll",
std::env::current_dir().unwrap().display()
),
"exe" => "a.out".to_string(),
"json" => "/dev/stdout".to_string(),
_ => unreachable!(),
});
let verify = matches.get_flag("verify_llvm");
let quiet = matches.get_flag("quiet");
let nyrt_dir = matches.get_one::<String>("nyrt").map(|s| s.to_string()).unwrap_or("crates/nyrt".to_string());
let nyrt_dir = matches
.get_one::<String>("nyrt")
.map(|s| s.to_string())
.unwrap_or("crates/nyrt".to_string());
// Determine sibling nyash binary path (target dir)
let nyash_bin = current_dir_bin("nyash");
@ -56,47 +75,74 @@ fn main() {
} else {
fs::copy(&in_file, &out_path).expect("copy json");
}
if !quiet { println!("OK json:{}", out_path); }
if !quiet {
println!("OK json:{}", out_path);
}
return;
}
// Ensure build dirs
let aot_dir = Path::new("target/aot_objects"); let _ = fs::create_dir_all(aot_dir);
let aot_dir = Path::new("target/aot_objects");
let _ = fs::create_dir_all(aot_dir);
match emit {
"ll" => {
std::env::set_var("NYASH_LLVM_DUMP_LL", "1");
std::env::set_var("NYASH_LLVM_LL_OUT", &out_path);
if verify { std::env::set_var("NYASH_LLVM_VERIFY", "1"); }
if verify {
std::env::set_var("NYASH_LLVM_VERIFY", "1");
}
std::env::set_var("NYASH_LLVM_USE_HARNESS", "1");
run_nyash_pipe(&nyash_bin, &in_file);
if !Path::new(&out_path).exists() { eprintln!("error: failed to produce {}", out_path); std::process::exit(4); }
if !quiet { println!("OK ll:{}", out_path); }
if !Path::new(&out_path).exists() {
eprintln!("error: failed to produce {}", out_path);
std::process::exit(4);
}
if !quiet {
println!("OK ll:{}", out_path);
}
}
"obj" => {
std::env::set_var("NYASH_LLVM_OBJ_OUT", &out_path);
if verify { std::env::set_var("NYASH_LLVM_VERIFY", "1"); }
if verify {
std::env::set_var("NYASH_LLVM_VERIFY", "1");
}
std::env::set_var("NYASH_LLVM_USE_HARNESS", "1");
// remove stale
let _ = fs::remove_file(&out_path);
run_nyash_pipe(&nyash_bin, &in_file);
if !Path::new(&out_path).exists() { eprintln!("error: failed to produce {}", out_path); std::process::exit(4); }
if !quiet { println!("OK obj:{}", out_path); }
if !Path::new(&out_path).exists() {
eprintln!("error: failed to produce {}", out_path);
std::process::exit(4);
}
if !quiet {
println!("OK obj:{}", out_path);
}
}
"exe" => {
let obj_path = format!("{}/target/aot_objects/__tmp_mir_builder.o", std::env::current_dir().unwrap().display());
let obj_path = format!(
"{}/target/aot_objects/__tmp_mir_builder.o",
std::env::current_dir().unwrap().display()
);
std::env::set_var("NYASH_LLVM_OBJ_OUT", &obj_path);
if verify { std::env::set_var("NYASH_LLVM_VERIFY", "1"); }
if verify {
std::env::set_var("NYASH_LLVM_VERIFY", "1");
}
std::env::set_var("NYASH_LLVM_USE_HARNESS", "1");
let _ = fs::remove_file(&obj_path);
run_nyash_pipe(&nyash_bin, &in_file);
if !Path::new(&obj_path).exists() { eprintln!("error: failed to produce object {}", obj_path); std::process::exit(4); }
if !Path::new(&obj_path).exists() {
eprintln!("error: failed to produce object {}", obj_path);
std::process::exit(4);
}
// Link with NyRT
if let Err(e) = link_exe(&obj_path, &out_path, &nyrt_dir) {
eprintln!("error: link failed: {}", e);
std::process::exit(5);
}
if !quiet { println!("OK exe:{}", out_path); }
if !quiet {
println!("OK exe:{}", out_path);
}
}
_ => unreachable!(),
}
@ -108,17 +154,23 @@ fn current_dir_bin(name: &str) -> PathBuf {
if let Ok(cur) = std::env::current_exe() {
if let Some(dir) = cur.parent() {
let cand = dir.join(name);
if cand.exists() { return cand; }
if cand.exists() {
return cand;
}
#[cfg(windows)]
{
let cand = dir.join(format!("{}.exe", name));
if cand.exists() { return cand; }
if cand.exists() {
return cand;
}
}
}
}
// Fallback to target/release
let cand = PathBuf::from("target/release").join(name);
if cand.exists() { return cand; }
if cand.exists() {
return cand;
}
#[cfg(windows)]
{
let cand = PathBuf::from("target/release").join(format!("{}.exe", name));
@ -157,10 +209,17 @@ fn link_exe(obj_path: &str, out_path: &str, nyrt_dir: &str) -> Result<(), String
args.push(obj_path.to_string());
// Provide LIBPATH and library name (prefer nyrt.lib)
args.push(format!("/LIBPATH:{}", nyrt_release));
if std::path::Path::new(&lib_nyrt_lib).exists() { args.push("nyrt.lib".to_string()); }
if std::path::Path::new(&lib_nyrt_lib).exists() {
args.push("nyrt.lib".to_string());
}
// lld-link cannot consume .a directly; rely on .lib
let status = PCommand::new("lld-link").args(args.iter().map(|s| s.as_str())).status().map_err(|e| e.to_string())?;
if status.success() { return Ok(()); }
let status = PCommand::new("lld-link")
.args(args.iter().map(|s| s.as_str()))
.status()
.map_err(|e| e.to_string())?;
if status.success() {
return Ok(());
}
return Err(format!("lld-link failed: status {:?}", status.code()));
}
if which::which("link").is_ok() {
@ -168,9 +227,16 @@ fn link_exe(obj_path: &str, out_path: &str, nyrt_dir: &str) -> Result<(), String
args.push(format!("/OUT:{}", out_path));
args.push(obj_path.to_string());
args.push(format!("/LIBPATH:{}", nyrt_release));
if std::path::Path::new(&lib_nyrt_lib).exists() { args.push("nyrt.lib".to_string()); }
let status = PCommand::new("link").args(args.iter().map(|s| s.as_str())).status().map_err(|e| e.to_string())?;
if status.success() { return Ok(()); }
if std::path::Path::new(&lib_nyrt_lib).exists() {
args.push("nyrt.lib".to_string());
}
let status = PCommand::new("link")
.args(args.iter().map(|s| s.as_str()))
.status()
.map_err(|e| e.to_string())?;
if status.success() {
return Ok(());
}
return Err(format!("link.exe failed: status {:?}", status.code()));
}
// Fallback: try cc with MinGW-like flags
@ -178,8 +244,11 @@ fn link_exe(obj_path: &str, out_path: &str, nyrt_dir: &str) -> Result<(), String
.args([obj_path])
.args(["-L", &format!("{}/target/release", nyrt_dir)])
.args(["-lnyrt", "-o", out_path])
.status().map_err(|e| e.to_string())?;
if status.success() { return Ok(()); }
.status()
.map_err(|e| e.to_string())?;
if status.success() {
return Ok(());
}
return Err(format!("cc link failed: status {:?}", status.code()));
}
#[cfg(not(target_os = "windows"))]
@ -190,7 +259,12 @@ fn link_exe(obj_path: &str, out_path: &str, nyrt_dir: &str) -> Result<(), String
.args(["-L", &format!("{}/target/release", nyrt_dir)])
.args(["-Wl,--whole-archive", "-lnyrt", "-Wl,--no-whole-archive"])
.args(["-lpthread", "-ldl", "-lm", "-o", out_path])
.status().map_err(|e| e.to_string())?;
if status.success() { Ok(()) } else { Err(format!("cc failed: status {:?}", status.code())) }
.status()
.map_err(|e| e.to_string())?;
if status.success() {
Ok(())
} else {
Err(format!("cc failed: status {:?}", status.code()))
}
}
}

View File

@ -5,13 +5,13 @@ use nyash_rust::runtime::{get_global_loader_v2, init_global_loader_v2};
fn main() {
env_logger::init();
println!("=== v2 Plugin Loader Test (Phase 12 prep) ===\n");
// Load configuration
let config_path = "test_nyash_v2.toml";
println!("Loading configuration from: {}", config_path);
let config = match NyashConfigV2::from_file(config_path) {
Ok(cfg) => cfg,
Err(e) => {
@ -19,10 +19,10 @@ fn main() {
return;
}
};
println!("Configuration loaded successfully!");
println!("Is v2 format: {}", config.is_v2_format());
if let Some(libs) = &config.plugins.libraries {
println!("\nLibraries found:");
for (name, lib) in libs {
@ -30,7 +30,7 @@ fn main() {
println!(" Provides: {:?}", lib.provides);
}
}
// Initialize and load plugins
println!("\nLoading plugins...");
if let Err(e) = init_global_loader_v2(config_path) {
@ -39,12 +39,14 @@ fn main() {
}
let loader = get_global_loader_v2();
let loader = loader.read().unwrap();
// Test box type resolution
println!("\nTesting box type resolution:");
for box_type in ["StringBox", "FileBox", "MapBox"] {
match config.find_library_for_box(box_type) {
Some((name, lib)) => println!(" {} -> library: {} (path={})", box_type, name, lib.path),
Some((name, lib)) => {
println!(" {} -> library: {} (path={})", box_type, name, lib.path)
}
None => println!(" {} -> not found in config", box_type),
}
}
@ -55,12 +57,13 @@ fn main() {
} else {
println!("create_box(StringBox) not available or failed (ok for stub)");
}
// Simple reverse host-call exercise (simulate plugin calling host via C ABI by-slot)
println!("\nReverse host-call (by-slot) quick test:");
// Create ArrayBox and obtain HostHandle
let mut arr = nyash_rust::boxes::ArrayBox::new();
arr.push(Box::new(nyash_rust::box_trait::StringBox::new("init")) as Box<dyn nyash_rust::box_trait::NyashBox>);
arr.push(Box::new(nyash_rust::box_trait::StringBox::new("init"))
as Box<dyn nyash_rust::box_trait::NyashBox>);
let handle = nyash_rust::runtime::host_handles::to_handle_box(Box::new(arr));
// Call Array.set(0, "hello") via slot=101
let mut tlv = nyash_rust::runtime::plugin_ffi_common::encode_tlv_header(2);
@ -68,21 +71,44 @@ fn main() {
nyash_rust::runtime::plugin_ffi_common::encode::string(&mut tlv, "hello");
let mut out = vec![0u8; 256];
let mut out_len: usize = out.len();
let code = unsafe { nyash_rust::runtime::host_api::nyrt_host_call_slot(handle, 101, tlv.as_ptr(), tlv.len(), out.as_mut_ptr(), &mut out_len) };
let code = unsafe {
nyash_rust::runtime::host_api::nyrt_host_call_slot(
handle,
101,
tlv.as_ptr(),
tlv.len(),
out.as_mut_ptr(),
&mut out_len,
)
};
println!(" set(slot=101) -> code={}, out_len={}", code, out_len);
// Call Array.get(0) via slot=100 and decode
let mut tlv2 = nyash_rust::runtime::plugin_ffi_common::encode_tlv_header(1);
nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut tlv2, 0);
let mut out2 = vec![0u8; 256];
let mut out2_len: usize = out2.len();
let code2 = unsafe { nyash_rust::runtime::host_api::nyrt_host_call_slot(handle, 100, tlv2.as_ptr(), tlv2.len(), out2.as_mut_ptr(), &mut out2_len) };
let code2 = unsafe {
nyash_rust::runtime::host_api::nyrt_host_call_slot(
handle,
100,
tlv2.as_ptr(),
tlv2.len(),
out2.as_mut_ptr(),
&mut out2_len,
)
};
if code2 == 0 {
if let Some((tag, _sz, payload)) = nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(&out2[..out2_len]) {
if tag == 6 || tag == 7 { // string/bytes
if let Some((tag, _sz, payload)) =
nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(&out2[..out2_len])
{
if tag == 6 || tag == 7 {
// string/bytes
let s = nyash_rust::runtime::plugin_ffi_common::decode::string(payload);
println!(" get(slot=100) -> tag={}, value='{}'", tag, s);
} else if tag == 3 { // i64
let v = nyash_rust::runtime::plugin_ffi_common::decode::i32(payload).unwrap_or_default();
} else if tag == 3 {
// i64
let v = nyash_rust::runtime::plugin_ffi_common::decode::i32(payload)
.unwrap_or_default();
println!(" get(slot=100) -> tag={}, i32={}", tag, v);
} else {
println!(" get(slot=100) -> tag={}, size={}", tag, _sz);
@ -102,16 +128,36 @@ fn main() {
nyash_rust::runtime::plugin_ffi_common::encode::string(&mut tlv_set, "v");
let mut out_s = vec![0u8; 256];
let mut out_s_len: usize = out_s.len();
let code_s = unsafe { nyash_rust::runtime::host_api::nyrt_host_call_slot(map_h, 204, tlv_set.as_ptr(), tlv_set.len(), out_s.as_mut_ptr(), &mut out_s_len) };
let code_s = unsafe {
nyash_rust::runtime::host_api::nyrt_host_call_slot(
map_h,
204,
tlv_set.as_ptr(),
tlv_set.len(),
out_s.as_mut_ptr(),
&mut out_s_len,
)
};
println!(" set(slot=204) -> code={}, out_len={}", code_s, out_s_len);
// get("k") → slot=203
let mut tlv_get = nyash_rust::runtime::plugin_ffi_common::encode_tlv_header(1);
nyash_rust::runtime::plugin_ffi_common::encode::string(&mut tlv_get, "k");
let mut out_g = vec![0u8; 256];
let mut out_g_len: usize = out_g.len();
let code_g = unsafe { nyash_rust::runtime::host_api::nyrt_host_call_slot(map_h, 203, tlv_get.as_ptr(), tlv_get.len(), out_g.as_mut_ptr(), &mut out_g_len) };
let code_g = unsafe {
nyash_rust::runtime::host_api::nyrt_host_call_slot(
map_h,
203,
tlv_get.as_ptr(),
tlv_get.len(),
out_g.as_mut_ptr(),
&mut out_g_len,
)
};
if code_g == 0 {
if let Some((tag, _sz, payload)) = nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(&out_g[..out_g_len]) {
if let Some((tag, _sz, payload)) =
nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(&out_g[..out_g_len])
{
if tag == 6 || tag == 7 {
let s = nyash_rust::runtime::plugin_ffi_common::decode::string(payload);
println!(" get(slot=203) -> '{}'", s);
@ -123,13 +169,37 @@ fn main() {
// has("k") → slot=202
let mut out_hb = vec![0u8; 16];
let mut out_hb_len: usize = out_hb.len();
let code_hb = unsafe { nyash_rust::runtime::host_api::nyrt_host_call_slot(map_h, 202, tlv_get.as_ptr(), tlv_get.len(), out_hb.as_mut_ptr(), &mut out_hb_len) };
println!(" has(slot=202) -> code={}, out_len={}", code_hb, out_hb_len);
let code_hb = unsafe {
nyash_rust::runtime::host_api::nyrt_host_call_slot(
map_h,
202,
tlv_get.as_ptr(),
tlv_get.len(),
out_hb.as_mut_ptr(),
&mut out_hb_len,
)
};
println!(
" has(slot=202) -> code={}, out_len={}",
code_hb, out_hb_len
);
// size() → slot=200
let mut out_sz = vec![0u8; 32];
let mut out_sz_len: usize = out_sz.len();
let code_sz = unsafe { nyash_rust::runtime::host_api::nyrt_host_call_slot(map_h, 200, std::ptr::null(), 0, out_sz.as_mut_ptr(), &mut out_sz_len) };
println!(" size(slot=200) -> code={}, out_len={}", code_sz, out_sz_len);
let code_sz = unsafe {
nyash_rust::runtime::host_api::nyrt_host_call_slot(
map_h,
200,
std::ptr::null(),
0,
out_sz.as_mut_ptr(),
&mut out_sz_len,
)
};
println!(
" size(slot=200) -> code={}, out_len={}",
code_sz, out_sz_len
);
println!("\nTest completed!");
}

View File

@ -85,6 +85,18 @@ pub fn await_max_ms() -> u64 {
std::env::var("NYASH_AWAIT_MAX_MS").ok().and_then(|s| s.parse().ok()).unwrap_or(5000)
}
// ---- MIR PHI-less (edge-copy) mode ----
/// Enable MIR PHI non-generation. Bridge/Builder emit edge copies instead of PHI.
pub fn mir_no_phi() -> bool { std::env::var("NYASH_MIR_NO_PHI").ok().as_deref() == Some("1") }
/// Allow verifier to skip SSA/dominance/merge checks for PHI-less MIR.
pub fn verify_allow_no_phi() -> bool {
std::env::var("NYASH_VERIFY_ALLOW_NO_PHI").ok().as_deref() == Some("1") || mir_no_phi()
}
// ---- LLVM harness toggle (llvmlite) ----
pub fn llvm_use_harness() -> bool { std::env::var("NYASH_LLVM_USE_HARNESS").ok().as_deref() == Some("1") }
// ---- Phase 11.8 MIR cleanup toggles ----
/// Core-13 minimal MIR mode toggle
/// Default: ON (unless explicitly disabled with NYASH_MIR_CORE13=0)

View File

@ -95,7 +95,7 @@ pub(crate) extern "C" fn nyash_plugin_invoke3_i64(type_id: i64, method_id: i64,
if let Some((tag, _sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(out_slice) {
match tag {
3 => { if let Some(v) = crate::runtime::plugin_ffi_common::decode::i32(payload) { return v as i64; } if payload.len() == 8 { let mut b=[0u8;8]; b.copy_from_slice(payload); return i64::from_le_bytes(b); } }
8 => { if _sz == 8 { let mut t=[0u8;4]; t.copy_from_slice(&payload[0..4]); let mut i=[0u8;4]; i.copy_from_slice(&payload[4..8]); let r_type = u32::from_le_bytes(t); let r_inst = u32::from_le_bytes(i); let box_type_name = crate::runtime::plugin_loader_unified::get_global_plugin_host().read().ok().and_then(|h| h.config_ref().map(|cfg| cfg.box_types.clone())).and_then(|m| m.into_iter().find(|(_k,v)| *v == r_type).map(|(k,_v)| k)).unwrap_or_else(|| "PluginBox".to_string()); let pb = crate::runtime::plugin_loader_v2::make_plugin_box_v2(box_type_name, r_type, r_inst, invoke.unwrap()); let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> = std::sync::Arc::new(pb); let h = crate::jit::rt::handles::to_handle(arc); return h as i64; } }
8 => { if _sz == 8 { let mut t=[0u8;4]; t.copy_from_slice(&payload[0..4]); let mut i=[0u8;4]; i.copy_from_slice(&payload[4..8]); let r_type = u32::from_le_bytes(t); let r_inst = u32::from_le_bytes(i); let meta_opt = crate::runtime::plugin_loader_v2::metadata_for_type_id(r_type); let (box_type_name, invoke_ptr) = if let Some(meta) = meta_opt { (meta.box_type.clone(), meta.invoke_fn) } else { ("PluginBox".to_string(), invoke.unwrap()) }; let pb = crate::runtime::plugin_loader_v2::make_plugin_box_v2(box_type_name, r_type, r_inst, invoke_ptr); let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> = std::sync::Arc::new(pb); let h = crate::jit::rt::handles::to_handle(arc); return h as i64; } }
1 => { return if crate::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false) { 1 } else { 0 }; }
5 => { if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref() == Some("1") { if _sz == 8 { let mut b=[0u8;8]; b.copy_from_slice(payload); let f = f64::from_le_bytes(b); return f as i64; } } }
_ => {}
@ -171,7 +171,7 @@ fn nyash_plugin_invoke_name_common_i64(method: &str, argc: i64, a0: i64, a1: i64
if let Some((tag, _sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(out_slice) {
match tag {
3 => { if payload.len() == 8 { let mut b=[0u8;8]; b.copy_from_slice(payload); return i64::from_le_bytes(b); } if let Some(v) = crate::runtime::plugin_ffi_common::decode::i32(payload) { return v as i64; } }
8 => { if _sz == 8 { let mut t=[0u8;4]; t.copy_from_slice(&payload[0..4]); let mut i=[0u8;4]; i.copy_from_slice(&payload[4..8]); let r_type = u32::from_le_bytes(t); let r_inst = u32::from_le_bytes(i); let pb = crate::runtime::plugin_loader_v2::make_plugin_box_v2(box_type, r_type, r_inst, invoke.unwrap()); let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> = std::sync::Arc::new(pb); let h = crate::jit::rt::handles::to_handle(arc); return h as i64; } }
8 => { if _sz == 8 { let mut t=[0u8;4]; t.copy_from_slice(&payload[0..4]); let mut i=[0u8;4]; i.copy_from_slice(&payload[4..8]); let r_type = u32::from_le_bytes(t); let r_inst = u32::from_le_bytes(i); let meta_opt = crate::runtime::plugin_loader_v2::metadata_for_type_id(r_type); let (meta_box, invoke_ptr) = if let Some(meta) = meta_opt { (meta.box_type, meta.invoke_fn) } else { (box_type.clone(), invoke.unwrap()) }; let pb = crate::runtime::plugin_loader_v2::make_plugin_box_v2(meta_box, r_type, r_inst, invoke_ptr); let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> = std::sync::Arc::new(pb); let h = crate::jit::rt::handles::to_handle(arc); return h as i64; } }
1 => { return if crate::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false) { 1 } else { 0 }; }
5 => { if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref() == Some("1") { if _sz == 8 { let mut b=[0u8;8]; b.copy_from_slice(payload); let f = f64::from_le_bytes(b); return f as i64; } } }
_ => {}

View File

@ -9,8 +9,6 @@ use crate::jit::r#extern::collections as c;
#[cfg(feature = "cranelift-jit")]
use crate::jit::r#extern::host_bridge as hb;
#[cfg(feature = "cranelift-jit")]
use crate::runtime::plugin_loader_unified;
#[cfg(feature = "cranelift-jit")]
use crate::runtime::plugin_loader_v2::PluginBoxV2;
// ---- Generic Birth (handle) ----
@ -19,19 +17,15 @@ pub(super) extern "C" fn nyash_box_birth_h(type_id: i64) -> i64 {
// Map type_id -> type name and create via plugin host; return runtime handle
if type_id <= 0 { return 0; }
let tid = type_id as u32;
let name_opt = crate::runtime::plugin_loader_unified::get_global_plugin_host()
.read().ok()
.and_then(|h| h.config_ref().map(|cfg| cfg.box_types.clone()))
.and_then(|m| m.into_iter().find(|(_k,v)| *v == tid).map(|(k,_v)| k));
if let Some(box_type) = name_opt {
if let Some(meta) = crate::runtime::plugin_loader_v2::metadata_for_type_id(tid) {
if let Ok(host) = crate::runtime::get_global_plugin_host().read() {
if let Ok(b) = host.create_box(&box_type, &[]) {
if let Ok(b) = host.create_box(&meta.box_type, &[]) {
let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> = std::sync::Arc::from(b);
let h = crate::jit::rt::handles::to_handle(arc);
events::emit_runtime(serde_json::json!({"id": "nyash.box.birth_h", "box_type": box_type, "type_id": tid, "handle": h}), "hostcall", "<jit>");
events::emit_runtime(serde_json::json!({"id": "nyash.box.birth_h", "box_type": meta.box_type, "type_id": meta.type_id, "handle": h}), "hostcall", "<jit>");
return h as i64;
} else {
events::emit_runtime(serde_json::json!({"id": "nyash.box.birth_h", "error": "create_failed", "box_type": box_type, "type_id": tid}), "hostcall", "<jit>");
events::emit_runtime(serde_json::json!({"id": "nyash.box.birth_h", "error": "create_failed", "box_type": meta.box_type, "type_id": meta.type_id}), "hostcall", "<jit>");
}
}
} else {
@ -44,25 +38,13 @@ pub(super) extern "C" fn nyash_box_birth_h(type_id: i64) -> i64 {
pub(super) extern "C" fn nyash_box_birth_i64(type_id: i64, argc: i64, a1: i64, a2: i64) -> i64 {
use crate::runtime::plugin_loader_v2::PluginBoxV2;
if type_id <= 0 { return 0; }
// Resolve invoke for the type by creating a temp instance
let mut invoke: Option<unsafe extern "C" fn(u32,u32,u32,*const u8,usize,*mut u8,*mut usize)->i32> = None;
let mut box_type = String::new();
if let Some(name) = crate::runtime::plugin_loader_unified::get_global_plugin_host()
.read().ok()
.and_then(|h| h.config_ref().map(|cfg| cfg.box_types.clone()))
.and_then(|m| m.into_iter().find(|(_k,v)| *v == (type_id as u32)).map(|(k,_v)| k))
{
box_type = name;
if let Ok(host) = crate::runtime::get_global_plugin_host().read() {
if let Ok(b) = host.create_box(&box_type, &[]) {
if let Some(p) = b.as_any().downcast_ref::<PluginBoxV2>() { invoke = Some(p.inner.invoke_fn); }
}
}
}
if invoke.is_none() {
events::emit_runtime(serde_json::json!({"id": "nyash.box.birth_i64", "error": "no_invoke", "type_id": type_id}), "hostcall", "<jit>");
// Resolve invoke for the type via loader metadata
let Some(meta) = crate::runtime::plugin_loader_v2::metadata_for_type_id(type_id as u32) else {
events::emit_runtime(serde_json::json!({"id": "nyash.box.birth_i64", "error": "type_map_failed", "type_id": type_id}), "hostcall", "<jit>");
return 0;
}
};
let invoke_fn = meta.invoke_fn;
let box_type = meta.box_type.clone();
let method_id: u32 = 0; let instance_id: u32 = 0;
// Build TLV from a1/a2
let nargs = argc.max(0) as usize;
@ -94,13 +76,13 @@ pub(super) extern "C" fn nyash_box_birth_i64(type_id: i64, argc: i64, a1: i64, a
if nargs >= 2 { encode_val(a2); }
// Invoke
let mut out = vec![0u8; 1024]; let mut out_len: usize = out.len();
let rc = unsafe { invoke.unwrap()(type_id as u32, method_id, instance_id, buf.as_ptr(), buf.len(), out.as_mut_ptr(), &mut out_len) };
let rc = unsafe { invoke_fn(type_id as u32, method_id, instance_id, buf.as_ptr(), buf.len(), out.as_mut_ptr(), &mut out_len) };
if rc != 0 { events::emit_runtime(serde_json::json!({"id": "nyash.box.birth_i64", "error": "invoke_failed", "type_id": type_id}), "hostcall", "<jit>"); return 0; }
if let Some((tag, _sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) {
if tag == 8 && payload.len()==8 {
let mut t=[0u8;4]; t.copy_from_slice(&payload[0..4]); let mut i=[0u8;4]; i.copy_from_slice(&payload[4..8]);
let r_type = u32::from_le_bytes(t); let r_inst = u32::from_le_bytes(i);
let pb = crate::runtime::plugin_loader_v2::make_plugin_box_v2(box_type.clone(), r_type, r_inst, invoke.unwrap());
let pb = crate::runtime::plugin_loader_v2::make_plugin_box_v2(box_type.clone(), r_type, r_inst, invoke_fn);
let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> = std::sync::Arc::new(pb);
let h = crate::jit::rt::handles::to_handle(arc);
events::emit_runtime(serde_json::json!({"id": "nyash.box.birth_i64", "box_type": box_type, "type_id": type_id, "argc": nargs, "handle": h}), "hostcall", "<jit>");

View File

@ -87,12 +87,16 @@ pub struct MirBuilder {
/// Top of stack corresponds to the innermost active loop
pub(super) loop_header_stack: Vec<BasicBlockId>,
pub(super) loop_exit_stack: Vec<BasicBlockId>,
/// Whether PHI emission is disabled (edge-copy mode)
pub(super) no_phi_mode: bool,
}
impl MirBuilder {
/// Create a new MIR builder
pub fn new() -> Self {
let plugin_method_sigs = plugin_sigs::load_plugin_method_sigs();
let no_phi_mode = crate::config::env::mir_no_phi();
Self {
current_module: None,
current_function: None,
@ -112,6 +116,7 @@ impl MirBuilder {
include_box_map: HashMap::new(),
loop_header_stack: Vec::new(),
loop_exit_stack: Vec::new(),
no_phi_mode,
}
}
@ -651,6 +656,42 @@ impl MirBuilder {
Err("No current function".to_string())
}
}
pub(super) fn is_no_phi_mode(&self) -> bool {
self.no_phi_mode
}
/// Insert a Copy instruction into `block_id`, defining `dst` from `src`.
/// Skips blocks that terminate via return/throw, and avoids duplicate copies.
pub(super) fn insert_edge_copy(
&mut self,
block_id: BasicBlockId,
dst: ValueId,
src: ValueId,
) -> Result<(), String> {
if let Some(ref mut function) = self.current_function {
let block = function
.get_block_mut(block_id)
.ok_or_else(|| format!("Basic block {} does not exist", block_id))?;
if let Some(term) = &block.terminator {
if matches!(term, MirInstruction::Return { .. } | MirInstruction::Throw { .. }) {
return Ok(());
}
}
let already_present = block.instructions.iter().any(|inst| {
matches!(inst, MirInstruction::Copy { dst: existing_dst, .. } if *existing_dst == dst)
});
if !already_present {
block.add_instruction(MirInstruction::Copy { dst, src });
}
if let Some(ty) = self.value_types.get(&src).cloned() {
self.value_types.insert(dst, ty);
}
Ok(())
} else {
Err("No current function".to_string())
}
}
// moved to builder/utils.rs: ensure_block_exists

View File

@ -40,7 +40,13 @@ impl super::MirBuilder {
phi_inputs.push((else_block, else_val));
self.emit_instruction(super::MirInstruction::Jump { target: merge_block })?;
self.start_new_block(merge_block)?;
self.emit_instruction(super::MirInstruction::Phi { dst: result_val, inputs: phi_inputs })?;
if self.is_no_phi_mode() {
for (pred, val) in phi_inputs {
self.insert_edge_copy(pred, result_val, val)?;
}
} else {
self.emit_instruction(super::MirInstruction::Phi { dst: result_val, inputs: phi_inputs })?;
}
return Ok(result_val);
}
@ -83,7 +89,13 @@ impl super::MirBuilder {
// Merge and yield result
self.start_new_block(merge_block)?;
self.emit_instruction(super::MirInstruction::Phi { dst: result_val, inputs: phi_inputs })?;
if self.is_no_phi_mode() {
for (pred, val) in phi_inputs {
self.insert_edge_copy(pred, result_val, val)?;
}
} else {
self.emit_instruction(super::MirInstruction::Phi { dst: result_val, inputs: phi_inputs })?;
}
Ok(result_val)
}
}

View File

@ -73,6 +73,30 @@ impl MirBuilder {
let assigned_var_else = else_ast_for_analysis.as_ref().and_then(|a| extract_assigned_var(a));
let result_val = self.value_gen.next();
if self.is_no_phi_mode() {
if let Some(var_name) = assigned_var_then.clone() {
let else_assigns_same = assigned_var_else.as_ref().map(|s| s == &var_name).unwrap_or(false);
let then_value_for_var = then_var_map_end.get(&var_name).copied().unwrap_or(then_value_raw);
let else_value_for_var = if else_assigns_same {
else_var_map_end_opt
.as_ref()
.and_then(|m| m.get(&var_name).copied())
.unwrap_or(else_value_raw)
} else {
pre_then_var_value.unwrap_or(else_value_raw)
};
self.insert_edge_copy(then_block, result_val, then_value_for_var)?;
self.insert_edge_copy(else_block, result_val, else_value_for_var)?;
self.variable_map = pre_if_var_map.clone();
self.variable_map.insert(var_name, result_val);
} else {
self.insert_edge_copy(then_block, result_val, then_value_raw)?;
self.insert_edge_copy(else_block, result_val, else_value_raw)?;
self.variable_map = pre_if_var_map.clone();
}
return Ok(result_val);
}
if let Some(var_name) = assigned_var_then.clone() {
let else_assigns_same = assigned_var_else.as_ref().map(|s| s == &var_name).unwrap_or(false);
// Resolve branch-end values for the assigned variable

View File

@ -10,7 +10,7 @@ use super::{
ConstValue
};
use crate::ast::ASTNode;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
/// 不完全なPhi nodeの情報
#[derive(Debug, Clone)]
@ -40,6 +40,9 @@ pub struct LoopBuilder<'a> {
/// continue文からの変数スナップショット
continue_snapshots: Vec<(BasicBlockId, HashMap<String, ValueId>)>,
/// PHI を生成しないモードかどうか
no_phi_mode: bool,
}
// Local copy: detect a variable name assigned within an AST fragment
@ -68,12 +71,14 @@ fn extract_assigned_var_local(ast: &ASTNode) -> Option<String> {
impl<'a> LoopBuilder<'a> {
/// 新しいループビルダーを作成
pub fn new(parent: &'a mut super::builder::MirBuilder) -> Self {
let no_phi_mode = parent.is_no_phi_mode();
Self {
parent_builder: parent,
incomplete_phis: HashMap::new(),
block_var_maps: HashMap::new(),
loop_header: None,
continue_snapshots: Vec::new(),
no_phi_mode,
}
}
@ -175,10 +180,14 @@ impl<'a> LoopBuilder<'a> {
incomplete_phis.push(incomplete_phi);
if self.no_phi_mode {
self.parent_builder.insert_edge_copy(preheader_id, phi_id, value_before)?;
}
// 変数マップを更新Phi nodeの結果を使用
self.update_variable(var_name.clone(), phi_id);
}
// 不完全なPhi nodeを記録
self.incomplete_phis.insert(header_id, incomplete_phis);
@ -208,7 +217,16 @@ impl<'a> LoopBuilder<'a> {
phi.known_inputs.push((latch_id, value_after));
self.emit_phi_at_block_start(block_id, phi.phi_id, phi.known_inputs)?;
if self.no_phi_mode {
let mut seen: HashSet<BasicBlockId> = HashSet::new();
for &(pred, val) in &phi.known_inputs {
if seen.insert(pred) {
self.parent_builder.insert_edge_copy(pred, phi.phi_id, val)?;
}
}
} else {
self.emit_phi_at_block_start(block_id, phi.phi_id, phi.known_inputs)?;
}
self.update_variable(phi.var_name.clone(), phi.phi_id);
}
}
@ -422,7 +440,12 @@ impl<'a> LoopBuilder<'a> {
};
if let (Some(tv), Some(ev)) = (then_value_for_var, else_value_for_var) {
let phi_id = self.new_value();
self.emit_phi_at_block_start(merge_bb, phi_id, vec![(then_bb, tv), (else_bb, ev)])?;
if self.no_phi_mode {
self.parent_builder.insert_edge_copy(then_bb, phi_id, tv)?;
self.parent_builder.insert_edge_copy(else_bb, phi_id, ev)?;
} else {
self.emit_phi_at_block_start(merge_bb, phi_id, vec![(then_bb, tv), (else_bb, ev)])?;
}
// Reset to pre-if map and bind the phi result
self.parent_builder.variable_map = pre_if_var_map.clone();
self.parent_builder.variable_map.insert(var_name, phi_id);

View File

@ -428,6 +428,11 @@ impl MirVerifier {
/// Verify SSA form properties
fn verify_ssa_form(&self, function: &MirFunction) -> Result<(), Vec<VerificationError>> {
// Allow non-SSA (edge-copy) mode for PHI-less MIR when enabled via env
if crate::config::env::verify_allow_no_phi()
{
return Ok(());
}
let mut errors = Vec::new();
let mut definitions = HashMap::new();
@ -470,6 +475,11 @@ impl MirVerifier {
/// Verify dominance relations (def must dominate use across blocks)
fn verify_dominance(&self, function: &MirFunction) -> Result<(), Vec<VerificationError>> {
// Allow non-SSA (edge-copy) mode for PHI-less MIR when enabled via env
if crate::config::env::verify_allow_no_phi()
{
return Ok(());
}
let mut errors = Vec::new();
// Build def -> block map and dominators
@ -536,6 +546,11 @@ impl MirVerifier {
/// Verify that blocks with multiple predecessors do not use predecessor-defined values directly.
/// In merge blocks, values coming from predecessors must be routed through Phi.
fn verify_merge_uses(&self, function: &MirFunction) -> Result<(), Vec<VerificationError>> {
// Allow non-SSA (edge-copy) mode for PHI-less MIR when enabled via env
if crate::config::env::verify_allow_no_phi()
{
return Ok(());
}
let mut errors = Vec::new();
let preds = self.compute_predecessors(function);
let def_block = self.compute_def_blocks(function);

File diff suppressed because it is too large Load Diff