feat(phase285): Complete weak reference implementation (VM + LLVM harness)
Phase 285LLVM-1.1 to 1.4 + weak reference infrastructure: **LLVM Harness** (Phase 285LLVM-1.x): - 285LLVM-1.1: User Box registration & debug output - 285LLVM-1.2: WeakRef basic operations (identity deferred) - 285LLVM-1.3: InstanceBox field access (getField/setField) - 285LLVM-1.4: print Handle resolution (type tag propagation) **VM Runtime** (nyash_kernel): - FFI functions: nyrt_weak_new, nyrt_weak_to_strong, nyrt_weak_drop (crates/nyash_kernel/src/lib.rs: +209 lines) - WeakRef plugin invoke support (crates/nyash_kernel/src/plugin/invoke.rs: +250 lines) - weak_handles.rs: WeakRef handle registry (NEW) **LLVM Python Backend**: - WeakRef instruction lowering (weak.py: NEW) - Entry point integration (entry.py: +93 lines) - Instruction lowering (instruction_lower.py: +13 lines) - LLVM harness runner script (tools/run_llvm_harness.sh: NEW) **MIR & Runtime**: - WeakRef emission & validation - MIR JSON export for weak instructions - Environment variable support (NYASH_WEAK_*, HAKO_WEAK_*) **Documentation**: - CLAUDE.md: Phase 285 completion notes - LANGUAGE_REFERENCE_2025.md: Weak reference syntax - 10-Now.md & 30-Backlog.md: Phase 285 status updates Total: +864 lines, 24 files changed SSOT: docs/reference/language/lifecycle.md Related: Phase 285W-Syntax-0, Phase 285W-Syntax-0.1
This commit is contained in:
@ -523,6 +523,26 @@ pub fn emit_mir_json_for_harness(
|
||||
I::Return { value } => {
|
||||
insts.push(json!({"op":"ret","value": value.map(|v| v.as_u32())}));
|
||||
}
|
||||
// Phase 285LLVM-1: WeakRef support (unified form after normalization)
|
||||
I::WeakRef { dst, op, value } => {
|
||||
use crate::mir::WeakRefOp;
|
||||
let op_name = match op {
|
||||
WeakRefOp::New => "weak_new",
|
||||
WeakRefOp::Load => "weak_load",
|
||||
};
|
||||
let value_field = match op {
|
||||
WeakRefOp::New => "box_val",
|
||||
WeakRefOp::Load => "weak_ref",
|
||||
};
|
||||
insts.push(json!({"op": op_name, "dst": dst.as_u32(), value_field: value.as_u32()}));
|
||||
}
|
||||
// Legacy WeakNew/WeakLoad (before normalization)
|
||||
I::WeakNew { dst, box_val } => {
|
||||
insts.push(json!({"op":"weak_new","dst": dst.as_u32(), "box_val": box_val.as_u32()}));
|
||||
}
|
||||
I::WeakLoad { dst, weak_ref } => {
|
||||
insts.push(json!({"op":"weak_load","dst": dst.as_u32(), "weak_ref": weak_ref.as_u32()}));
|
||||
}
|
||||
_ => { /* skip non-essential ops for initial harness */ }
|
||||
}
|
||||
}
|
||||
@ -580,16 +600,32 @@ pub fn emit_mir_json_for_harness(
|
||||
// Phase 155: Extract CFG information for hako_check
|
||||
let cfg_info = nyash_rust::mir::extract_cfg_info(module);
|
||||
|
||||
// Phase 285LLVM-1.1: Extract user box declarations for LLVM harness
|
||||
let user_box_decls: Vec<serde_json::Value> = module.metadata.user_box_decls
|
||||
.iter()
|
||||
.map(|(name, fields)| {
|
||||
json!({
|
||||
"name": name,
|
||||
"fields": fields
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
let root = if use_v1_schema {
|
||||
let mut root = create_json_v1_root(json!(funs));
|
||||
// Add CFG data to v1 schema
|
||||
// Add CFG data and user box declarations to v1 schema
|
||||
if let Some(obj) = root.as_object_mut() {
|
||||
obj.insert("cfg".to_string(), cfg_info);
|
||||
obj.insert("user_box_decls".to_string(), json!(user_box_decls)); // Phase 285LLVM-1.1
|
||||
}
|
||||
root
|
||||
} else {
|
||||
// v0 legacy format - also add CFG
|
||||
json!({"functions": funs, "cfg": cfg_info})
|
||||
// v0 legacy format - also add CFG and user_box_decls
|
||||
json!({
|
||||
"functions": funs,
|
||||
"cfg": cfg_info,
|
||||
"user_box_decls": user_box_decls // Phase 285LLVM-1.1
|
||||
})
|
||||
};
|
||||
|
||||
// NOTE: numeric_core strict validation is applied on the AotPrep output
|
||||
@ -910,6 +946,26 @@ pub fn emit_mir_json_for_harness_bin(
|
||||
I::Return { value } => {
|
||||
insts.push(json!({"op":"ret","value": value.map(|v| v.as_u32())}));
|
||||
}
|
||||
// Phase 285LLVM-1: WeakRef support (unified form after normalization)
|
||||
I::WeakRef { dst, op, value } => {
|
||||
use crate::mir::WeakRefOp;
|
||||
let op_name = match op {
|
||||
WeakRefOp::New => "weak_new",
|
||||
WeakRefOp::Load => "weak_load",
|
||||
};
|
||||
let value_field = match op {
|
||||
WeakRefOp::New => "box_val",
|
||||
WeakRefOp::Load => "weak_ref",
|
||||
};
|
||||
insts.push(json!({"op": op_name, "dst": dst.as_u32(), value_field: value.as_u32()}));
|
||||
}
|
||||
// Legacy WeakNew/WeakLoad (before normalization)
|
||||
I::WeakNew { dst, box_val } => {
|
||||
insts.push(json!({"op":"weak_new","dst": dst.as_u32(), "box_val": box_val.as_u32()}));
|
||||
}
|
||||
I::WeakLoad { dst, weak_ref } => {
|
||||
insts.push(json!({"op":"weak_load","dst": dst.as_u32(), "weak_ref": weak_ref.as_u32()}));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -954,7 +1010,22 @@ pub fn emit_mir_json_for_harness_bin(
|
||||
// Phase 155: Extract CFG information for hako_check
|
||||
let cfg_info = crate::mir::extract_cfg_info(module);
|
||||
|
||||
let root = json!({"functions": funs, "cfg": cfg_info});
|
||||
// Phase 285LLVM-1.1: Extract user box declarations for LLVM harness
|
||||
let user_box_decls: Vec<serde_json::Value> = module.metadata.user_box_decls
|
||||
.iter()
|
||||
.map(|(name, fields)| {
|
||||
json!({
|
||||
"name": name,
|
||||
"fields": fields
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
let root = json!({
|
||||
"functions": funs,
|
||||
"cfg": cfg_info,
|
||||
"user_box_decls": user_box_decls // Phase 285LLVM-1.1
|
||||
});
|
||||
|
||||
// NOTE: numeric_core strict validation is applied on the AotPrep output
|
||||
// (tools/hakorune_emit_mir.sh) rather than at raw MIR emit time. This keeps
|
||||
|
||||
@ -92,6 +92,22 @@ fn hint_ny_llvmc_missing(path: &std::path::Path) -> String {
|
||||
)
|
||||
}
|
||||
|
||||
fn hint_nyrt_missing(dir: &str) -> String {
|
||||
let lib = Path::new(dir).join("libnyash_kernel.a");
|
||||
format!(
|
||||
"nyrt runtime not found (missing: {}).\nHints:\n - Build it: cargo build -p nyash_kernel --release\n - Or set env NYASH_EMIT_EXE_NYRT=/path/to/nyash_kernel/target/release\n",
|
||||
lib.display()
|
||||
)
|
||||
}
|
||||
|
||||
fn verify_nyrt_dir(dir: &str) -> Result<(), String> {
|
||||
let lib = Path::new(dir).join("libnyash_kernel.a");
|
||||
if lib.exists() {
|
||||
return Ok(());
|
||||
}
|
||||
Err(hint_nyrt_missing(dir))
|
||||
}
|
||||
|
||||
/// Emit native executable via ny-llvmc (lib-side MIR)
|
||||
#[allow(dead_code)]
|
||||
pub fn ny_llvmc_emit_exe_lib(
|
||||
@ -124,11 +140,9 @@ pub fn ny_llvmc_emit_exe_lib(
|
||||
.map(|r| format!("{}/target/release", r))
|
||||
})
|
||||
.unwrap_or_else(|| "target/release".to_string());
|
||||
if let Some(dir) = nyrt_dir {
|
||||
cmd.arg("--nyrt").arg(dir);
|
||||
} else {
|
||||
cmd.arg("--nyrt").arg(default_nyrt);
|
||||
}
|
||||
let nyrt_dir_final = nyrt_dir.unwrap_or(&default_nyrt);
|
||||
verify_nyrt_dir(nyrt_dir_final)?;
|
||||
cmd.arg("--nyrt").arg(nyrt_dir_final);
|
||||
if let Some(flags) = extra_libs {
|
||||
if !flags.trim().is_empty() {
|
||||
cmd.arg("--libs").arg(flags);
|
||||
@ -183,11 +197,9 @@ pub fn ny_llvmc_emit_exe_bin(
|
||||
.map(|r| format!("{}/target/release", r))
|
||||
})
|
||||
.unwrap_or_else(|| "target/release".to_string());
|
||||
if let Some(dir) = nyrt_dir {
|
||||
cmd.arg("--nyrt").arg(dir);
|
||||
} else {
|
||||
cmd.arg("--nyrt").arg(default_nyrt);
|
||||
}
|
||||
let nyrt_dir_final = nyrt_dir.unwrap_or(&default_nyrt);
|
||||
verify_nyrt_dir(nyrt_dir_final)?;
|
||||
cmd.arg("--nyrt").arg(nyrt_dir_final);
|
||||
if let Some(flags) = extra_libs {
|
||||
if !flags.trim().is_empty() {
|
||||
cmd.arg("--libs").arg(flags);
|
||||
|
||||
@ -27,7 +27,10 @@ impl FallbackExecutorBox {
|
||||
// do not silently fall back to mock.
|
||||
if crate::config::env::env_bool("NYASH_LLVM_USE_HARNESS") {
|
||||
return Err(LlvmRunError::fatal(
|
||||
"LLVM harness requested (NYASH_LLVM_USE_HARNESS=1), but this binary was built without `--features llvm` (llvm-harness). Fix: cargo build --release --features llvm"
|
||||
"LLVM harness requested (NYASH_LLVM_USE_HARNESS=1), but this binary was built without `--features llvm` (llvm-harness).\n\
|
||||
Fix:\n cargo build --release -p nyash-rust --features llvm --bin hakorune\n\
|
||||
Then ensure prerequisites:\n cargo build --release -p nyash-llvm-compiler\n cargo build --release -p nyash_kernel\n\
|
||||
Tip: tools/run_llvm_harness.sh <program.hako>"
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@ -23,7 +23,11 @@ impl HarnessExecutorBox {
|
||||
/// Returns Ok(exit_code) on success, Err(LlvmRunError) on failure.
|
||||
#[cfg(feature = "llvm-harness")]
|
||||
pub fn try_execute(module: &MirModule) -> Result<i32, LlvmRunError> {
|
||||
if !crate::config::env::llvm_use_harness() {
|
||||
eprintln!("🎯 [DEBUG] llvm-harness feature IS ENABLED at compile time");
|
||||
let harness_enabled = crate::config::env::llvm_use_harness();
|
||||
eprintln!("🎯 [DEBUG] llvm_use_harness() = {}", harness_enabled);
|
||||
eprintln!("🎯 [DEBUG] NYASH_LLVM_USE_HARNESS env var = {:?}", std::env::var("NYASH_LLVM_USE_HARNESS"));
|
||||
if !harness_enabled {
|
||||
return Err(LlvmRunError::fatal("LLVM harness not enabled (NYASH_LLVM_USE_HARNESS not set)"));
|
||||
}
|
||||
|
||||
@ -62,6 +66,8 @@ impl HarnessExecutorBox {
|
||||
|
||||
#[cfg(not(feature = "llvm-harness"))]
|
||||
pub fn try_execute(_module: &MirModule) -> Result<i32, LlvmRunError> {
|
||||
eprintln!("❌ [DEBUG] llvm-harness feature IS NOT ENABLED at compile time");
|
||||
eprintln!("❌ [DEBUG] You need to rebuild with: cargo build --release --features llvm");
|
||||
Err(LlvmRunError::fatal("LLVM harness feature not enabled (built without --features llvm)"))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user