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:
@ -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); }
|
||||
|
||||
@ -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(())
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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!");
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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; } } }
|
||||
_ => {}
|
||||
|
||||
@ -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>");
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
Reference in New Issue
Block a user