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:
@ -3,6 +3,72 @@
|
||||
//! Consolidates NYASH_* environment variables across subsystems and
|
||||
//! optionally applies overrides from `nyash.toml`.
|
||||
//!
|
||||
//! # Global Environment Configuration (管理棟)
|
||||
//!
|
||||
//! ## 環境変数の集約と直読み禁止
|
||||
//!
|
||||
//! **Phase 286A/B** で、全システムの環境変数を `src/config/env/` 以下のモジュールに集約しました。
|
||||
//!
|
||||
//! - **直読み禁止**: `std::env::var()` / `std::env::set_var()` の直接呼び出しは禁止です。
|
||||
//! - **必ず `src/config/env/*` 経由でアクセス**: 各サブシステムのフラグモジュール (`macro_flags`, `box_factory_flags`, etc.) を使用してください。
|
||||
//!
|
||||
//! ## モジュール構成
|
||||
//!
|
||||
//! | モジュール | 担当環境変数 | 用途 |
|
||||
//! | --- | --- | --- |
|
||||
//! | `macro_flags` | `NYASH_MACRO_*` | Macro システム設定 |
|
||||
//! | `box_factory_flags` | `NYASH_BOX_FACTORY_*`, `NYASH_DISABLE_PLUGINS` | Box Factory / プラグイン設定 |
|
||||
//! | `joinir_flags` | `NYASH_JOINIR_*` | JoinIR 設定 |
|
||||
//! | `mir_flags` | `NYASH_MIR_*` | MIR 設定 |
|
||||
//! | `vm_backend_flags` | `NYASH_VM_*` | VM / Backend 設定 |
|
||||
//! | `parser_flags` | `NYASH_PARSER_*` | Parser 設定 |
|
||||
//! | `using_flags` | `NYASH_USING_*` | Using / Namespace 設定 |
|
||||
//! | `verification_flags` | `NYASH_VERIFY_*` | Verification 設定 |
|
||||
//! | `selfhost_flags` | `NYASH_NY_COMPILER_*` | Selfhost compiler 設定 |
|
||||
//!
|
||||
//! ## 新規環境変数追加の手順
|
||||
//!
|
||||
//! 1. **モジュール選択**: 上記のモジュールから適切なものを選択。
|
||||
//! 2. **関数定義**: 選択したモジュールに `fn env_var_name() -> type` 形式で関数を定義。
|
||||
//! 3. **再export**: `src/config/env.rs` で `pub use module::*;` を確認(既に集約済み)。
|
||||
//! 4. **ドキュメント追記**: `docs/reference/environment-variables.md` に必ず追記してください。
|
||||
//!
|
||||
//! ## 直読み禁止のチェック
|
||||
//!
|
||||
//! 置換漏れを確認するには、以下のコマンドを使用してください:
|
||||
//!
|
||||
//! ```bash
|
||||
//! # std::env::var() の直接呼び出しを検索(src/config/env/ 以外は禁止)
|
||||
//! rg -n "std::env::(var|set_var|remove_var)\(" src | rg -v "src/config/env/"
|
||||
//!
|
||||
//! # NYASH_* 環境変数の直読みを検索(src/config/env/ 以外は禁止)
|
||||
//! rg -n "NYASH_(MACRO|BOX_FACTORY|DISABLE_PLUGINS)" src | rg -v "src/config/env/"
|
||||
//! ```
|
||||
//!
|
||||
//! ## 再export ポリシー
|
||||
//!
|
||||
//! 全ての環境変数関数は `src/config/env.rs` で再exportされています:
|
||||
//!
|
||||
//! ```rust
|
||||
//! // Backward-compatible re-exports (NO BREAKING CHANGES!)
|
||||
//! pub use box_factory_flags::*;
|
||||
//! pub use joinir_flags::*;
|
||||
//! pub use macro_flags::*;
|
||||
//! pub use mir_flags::*;
|
||||
//! pub use parser_flags::*;
|
||||
//! pub use selfhost_flags::*;
|
||||
//! pub use using_flags::*;
|
||||
//! pub use verification_flags::*;
|
||||
//! pub use vm_backend_flags::*;
|
||||
//! ```
|
||||
//!
|
||||
//! 使用時は `crate::config::env::function_name()` でアクセスしてください。
|
||||
//!
|
||||
//! ## 参照
|
||||
//!
|
||||
//! - SSOT ドキュメント: `docs/reference/environment-variables.md`
|
||||
//! - AGENTS.md 5.3: 環境変数スパロー防止ポリシー
|
||||
//!
|
||||
//! # Modular Organization
|
||||
//!
|
||||
//! Environment flags are now organized into focused Box modules:
|
||||
|
||||
20
src/config/env/macro_flags.rs
vendored
20
src/config/env/macro_flags.rs
vendored
@ -216,3 +216,23 @@ pub fn test_return() -> Option<String> {
|
||||
pub fn macro_syntax_sugar_level() -> Option<String> {
|
||||
std::env::var("NYASH_SYNTAX_SUGAR_LEVEL").ok()
|
||||
}
|
||||
|
||||
/// NYASH_MACRO_CAP_IO (capability: IO allowed)
|
||||
pub fn macro_cap_io() -> Option<bool> {
|
||||
std::env::var("NYASH_MACRO_CAP_IO")
|
||||
.ok()
|
||||
.map(|v| {
|
||||
let lv = v.to_ascii_lowercase();
|
||||
lv == "1" || lv == "true" || lv == "on"
|
||||
})
|
||||
}
|
||||
|
||||
/// NYASH_MACRO_CAP_NET (capability: NET allowed)
|
||||
pub fn macro_cap_net() -> Option<bool> {
|
||||
std::env::var("NYASH_MACRO_CAP_NET")
|
||||
.ok()
|
||||
.map(|v| {
|
||||
let lv = v.to_ascii_lowercase();
|
||||
lv == "1" || lv == "true" || lv == "on"
|
||||
})
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ from naming_helper import encode_static_method
|
||||
|
||||
def ensure_ny_main(builder) -> None:
|
||||
"""Ensure ny_main wrapper exists by delegating to Main.main/1 or main().
|
||||
Phase 285LLVM-1.1: Register user box declarations before calling main.
|
||||
Modifies builder.module in place; no return value.
|
||||
"""
|
||||
has_ny_main = any(f.name == 'ny_main' for f in builder.module.functions)
|
||||
@ -34,6 +35,11 @@ def ensure_ny_main(builder) -> None:
|
||||
ny_main = ir.Function(builder.module, ny_main_ty, name='ny_main')
|
||||
entry = ny_main.append_basic_block('entry')
|
||||
b = ir.IRBuilder(entry)
|
||||
|
||||
# Phase 285LLVM-1.1: Register user box declarations before calling main
|
||||
user_box_decls = getattr(builder, 'user_box_decls', [])
|
||||
if user_box_decls:
|
||||
_emit_user_box_registration(b, builder.module, user_box_decls)
|
||||
if fn_main_box is not None:
|
||||
# Build args
|
||||
i64 = builder.i64
|
||||
@ -84,3 +90,90 @@ def ensure_ny_main(builder) -> None:
|
||||
b.ret(rv)
|
||||
else:
|
||||
b.ret(ir.Constant(builder.i64, 0))
|
||||
|
||||
|
||||
def _emit_user_box_registration(b, module, user_box_decls):
|
||||
"""Emit calls to nyrt_register_user_box_decl() for each user box declaration.
|
||||
|
||||
Phase 285LLVM-1.1: Register user-defined boxes before main execution.
|
||||
|
||||
Args:
|
||||
b: IRBuilder instance (positioned at ny_main entry block)
|
||||
module: LLVM module
|
||||
user_box_decls: List[dict] with format [{"name": "SomeBox", "fields": ["x"]}, ...]
|
||||
"""
|
||||
from llvmlite import ir
|
||||
import json
|
||||
|
||||
i32 = ir.IntType(32)
|
||||
i8 = ir.IntType(8)
|
||||
i8p = i8.as_pointer()
|
||||
|
||||
# Declare nyrt_register_user_box_decl if not exists
|
||||
reg_func = None
|
||||
for f in module.functions:
|
||||
if f.name == "nyrt_register_user_box_decl":
|
||||
reg_func = f
|
||||
break
|
||||
|
||||
if not reg_func:
|
||||
# i32 nyrt_register_user_box_decl(i8* name, i8* fields_json)
|
||||
reg_func_type = ir.FunctionType(i32, [i8p, i8p])
|
||||
reg_func = ir.Function(module, reg_func_type, name="nyrt_register_user_box_decl")
|
||||
|
||||
# Emit registration calls for each box declaration
|
||||
for box_decl in user_box_decls:
|
||||
name = box_decl.get("name", "")
|
||||
fields = box_decl.get("fields", [])
|
||||
|
||||
if not name:
|
||||
continue
|
||||
|
||||
# Create global string constant for name
|
||||
name_bytes = (name + "\0").encode('utf-8')
|
||||
name_arr_ty = ir.ArrayType(i8, len(name_bytes))
|
||||
name_global = f".user_box_name_{name}"
|
||||
|
||||
# Check if global already exists
|
||||
existing_global = None
|
||||
for g in module.global_values:
|
||||
if g.name == name_global:
|
||||
existing_global = g
|
||||
break
|
||||
|
||||
if existing_global is None:
|
||||
g_name = ir.GlobalVariable(module, name_arr_ty, name=name_global)
|
||||
g_name.linkage = 'private'
|
||||
g_name.global_constant = True
|
||||
g_name.initializer = ir.Constant(name_arr_ty, bytearray(name_bytes))
|
||||
else:
|
||||
g_name = existing_global
|
||||
|
||||
# Create global string constant for fields JSON
|
||||
fields_json = json.dumps(fields)
|
||||
fields_bytes = (fields_json + "\0").encode('utf-8')
|
||||
fields_arr_ty = ir.ArrayType(i8, len(fields_bytes))
|
||||
fields_global = f".user_box_fields_{name}"
|
||||
|
||||
# Check if global already exists
|
||||
existing_fields_global = None
|
||||
for g in module.global_values:
|
||||
if g.name == fields_global:
|
||||
existing_fields_global = g
|
||||
break
|
||||
|
||||
if existing_fields_global is None:
|
||||
g_fields = ir.GlobalVariable(module, fields_arr_ty, name=fields_global)
|
||||
g_fields.linkage = 'private'
|
||||
g_fields.global_constant = True
|
||||
g_fields.initializer = ir.Constant(fields_arr_ty, bytearray(fields_bytes))
|
||||
else:
|
||||
g_fields = existing_fields_global
|
||||
|
||||
# Get pointers to strings
|
||||
c0 = ir.Constant(ir.IntType(32), 0)
|
||||
name_ptr = b.gep(g_name, [c0, c0], inbounds=True)
|
||||
fields_ptr = b.gep(g_fields, [c0, c0], inbounds=True)
|
||||
|
||||
# Call nyrt_register_user_box_decl(name, fields_json)
|
||||
b.call(reg_func, [name_ptr, fields_ptr])
|
||||
|
||||
@ -22,6 +22,7 @@ from instructions.select import lower_select # Phase 256 P1.5: Select instructi
|
||||
from instructions.loopform import lower_while_loopform
|
||||
from instructions.controlflow.while_ import lower_while_regular
|
||||
from instructions.mir_call import lower_mir_call # New unified handler
|
||||
from instructions.weak import lower_weak_new, lower_weak_load # Phase 285LLVM-1: WeakRef
|
||||
|
||||
|
||||
def lower_instruction(owner, builder: ir.IRBuilder, inst: Dict[str, Any], func: ir.Function):
|
||||
@ -185,6 +186,18 @@ def lower_instruction(owner, builder: ir.IRBuilder, inst: Dict[str, Any], func:
|
||||
vmap_ctx, owner.preds, owner.block_end_values, owner.bb_map,
|
||||
ctx=getattr(owner, 'ctx', None))
|
||||
|
||||
elif op == "weak_new":
|
||||
# Phase 285LLVM-1: WeakNew instruction (strong → weak)
|
||||
dst = inst.get("dst")
|
||||
box_val = inst.get("box_val")
|
||||
lower_weak_new(builder, owner.module, dst, box_val, vmap_ctx, ctx=getattr(owner, 'ctx', None))
|
||||
|
||||
elif op == "weak_load":
|
||||
# Phase 285LLVM-1: WeakLoad instruction (weak → strong or 0/Void)
|
||||
dst = inst.get("dst")
|
||||
weak_ref = inst.get("weak_ref")
|
||||
lower_weak_load(builder, owner.module, dst, weak_ref, vmap_ctx, ctx=getattr(owner, 'ctx', None))
|
||||
|
||||
elif op == "while":
|
||||
# Experimental LoopForm lowering inside a block
|
||||
cond = inst.get("cond")
|
||||
|
||||
111
src/llvm_py/instructions/weak.py
Normal file
111
src/llvm_py/instructions/weak.py
Normal file
@ -0,0 +1,111 @@
|
||||
"""
|
||||
Phase 285LLVM-1: WeakRef instruction lowering
|
||||
Handles weak reference creation and upgrade (weak_new, weak_load)
|
||||
|
||||
SSOT: docs/reference/language/lifecycle.md:179
|
||||
"""
|
||||
|
||||
import llvmlite.ir as ir
|
||||
from typing import Dict, Optional, Any
|
||||
|
||||
|
||||
def lower_weak_new(
|
||||
builder: ir.IRBuilder,
|
||||
module: ir.Module,
|
||||
dst_vid: int,
|
||||
box_val_vid: int,
|
||||
vmap: Dict[int, ir.Value],
|
||||
ctx: Optional[Any] = None
|
||||
) -> None:
|
||||
"""
|
||||
Lower MIR WeakNew instruction
|
||||
|
||||
Converts strong BoxRef to WeakRef.
|
||||
|
||||
MIR: WeakNew { dst: ValueId(10), box_val: ValueId(5) }
|
||||
LLVM IR: %10 = call i64 @nyrt_weak_new(i64 %5)
|
||||
|
||||
Args:
|
||||
builder: Current LLVM IR builder
|
||||
module: LLVM module
|
||||
dst_vid: Destination value ID for weak handle
|
||||
box_val_vid: Source BoxRef value ID
|
||||
vmap: Value map
|
||||
ctx: Optional context
|
||||
"""
|
||||
i64 = ir.IntType(64)
|
||||
|
||||
# Get or declare nyrt_weak_new function
|
||||
nyrt_weak_new = None
|
||||
for f in module.functions:
|
||||
if f.name == "nyrt_weak_new":
|
||||
nyrt_weak_new = f
|
||||
break
|
||||
|
||||
if not nyrt_weak_new:
|
||||
# Declare: i64 @nyrt_weak_new(i64 strong_handle)
|
||||
func_type = ir.FunctionType(i64, [i64])
|
||||
nyrt_weak_new = ir.Function(module, func_type, name="nyrt_weak_new")
|
||||
|
||||
# Get strong handle from vmap
|
||||
strong_handle = vmap.get(box_val_vid)
|
||||
if strong_handle is None:
|
||||
# Fallback: treat as literal 0 (invalid)
|
||||
strong_handle = ir.Constant(i64, 0)
|
||||
|
||||
# Call nyrt_weak_new
|
||||
weak_handle = builder.call(nyrt_weak_new, [strong_handle], name=f"weak_{dst_vid}")
|
||||
|
||||
# Store result in vmap
|
||||
vmap[dst_vid] = weak_handle
|
||||
|
||||
|
||||
def lower_weak_load(
|
||||
builder: ir.IRBuilder,
|
||||
module: ir.Module,
|
||||
dst_vid: int,
|
||||
weak_ref_vid: int,
|
||||
vmap: Dict[int, ir.Value],
|
||||
ctx: Optional[Any] = None
|
||||
) -> None:
|
||||
"""
|
||||
Lower MIR WeakLoad instruction
|
||||
|
||||
Upgrades WeakRef to BoxRef (returns 0/Void on failure).
|
||||
|
||||
MIR: WeakLoad { dst: ValueId(20), weak_ref: ValueId(10) }
|
||||
LLVM IR: %20 = call i64 @nyrt_weak_to_strong(i64 %10)
|
||||
|
||||
Args:
|
||||
builder: Current LLVM IR builder
|
||||
module: LLVM module
|
||||
dst_vid: Destination value ID for strong handle (or 0)
|
||||
weak_ref_vid: Source WeakRef value ID
|
||||
vmap: Value map
|
||||
ctx: Optional context
|
||||
"""
|
||||
i64 = ir.IntType(64)
|
||||
|
||||
# Get or declare nyrt_weak_to_strong function
|
||||
nyrt_weak_to_strong = None
|
||||
for f in module.functions:
|
||||
if f.name == "nyrt_weak_to_strong":
|
||||
nyrt_weak_to_strong = f
|
||||
break
|
||||
|
||||
if not nyrt_weak_to_strong:
|
||||
# Declare: i64 @nyrt_weak_to_strong(i64 weak_handle)
|
||||
func_type = ir.FunctionType(i64, [i64])
|
||||
nyrt_weak_to_strong = ir.Function(module, func_type, name="nyrt_weak_to_strong")
|
||||
|
||||
# Get weak handle from vmap
|
||||
weak_handle = vmap.get(weak_ref_vid)
|
||||
if weak_handle is None:
|
||||
# Fallback: treat as literal 0 (invalid)
|
||||
weak_handle = ir.Constant(i64, 0)
|
||||
|
||||
# Call nyrt_weak_to_strong
|
||||
strong_handle = builder.call(nyrt_weak_to_strong, [weak_handle], name=f"strong_{dst_vid}")
|
||||
|
||||
# Store result in vmap
|
||||
vmap[dst_vid] = strong_handle
|
||||
@ -138,6 +138,9 @@ class NyashLLVMBuilder:
|
||||
|
||||
def build_from_mir(self, mir_json: Dict[str, Any]) -> str:
|
||||
"""Build LLVM IR from MIR JSON"""
|
||||
# Phase 285LLVM-1.1: Extract user box declarations for registration
|
||||
self.user_box_decls = mir_json.get("user_box_decls", [])
|
||||
|
||||
# Parse MIR
|
||||
reader = MIRReader(mir_json)
|
||||
functions = reader.get_functions()
|
||||
|
||||
@ -459,8 +459,8 @@ struct NyChildMacroBox {
|
||||
}
|
||||
|
||||
fn caps_allow_macro_source(ast: &ASTNode) -> Result<(), String> {
|
||||
let allow_io = crate::config::env::env_flag("NYASH_MACRO_CAP_IO").unwrap_or(false);
|
||||
let allow_net = crate::config::env::env_flag("NYASH_MACRO_CAP_NET").unwrap_or(false);
|
||||
let allow_io = crate::config::env::macro_cap_io().unwrap_or(false);
|
||||
let allow_net = crate::config::env::macro_cap_net().unwrap_or(false);
|
||||
use nyash_rust::ast::ASTNode as A;
|
||||
fn scan(n: &A, seen: &mut Vec<String>) {
|
||||
match n {
|
||||
|
||||
@ -968,7 +968,7 @@ impl MirBuilder {
|
||||
// - For user-defined boxes (no explicit constructor), do NOT emit BoxCall("birth").
|
||||
// VM will treat plain NewBox as constructed; dev verify warns if needed.
|
||||
// - For builtins/plugins, keep BoxCall("birth") fallback to preserve legacy init.
|
||||
let is_user_box = self.comp_ctx.user_defined_boxes.contains(&class);
|
||||
let is_user_box = self.comp_ctx.user_defined_boxes.contains_key(&class); // Phase 285LLVM-1.1: HashMap
|
||||
// Dev safety: allow disabling birth() injection for builtins to avoid
|
||||
// unified-call method dispatch issues while migrating. Off by default unless explicitly enabled.
|
||||
let allow_builtin_birth = std::env::var("NYASH_DEV_BIRTH_INJECT_BUILTINS")
|
||||
|
||||
@ -45,7 +45,10 @@ pub(crate) struct CompilationContext {
|
||||
pub current_static_box: Option<String>,
|
||||
|
||||
/// Names of user-defined boxes declared in the current module
|
||||
pub user_defined_boxes: HashSet<String>,
|
||||
/// Phase 285LLVM-1.1: Extended to track fields (box name → field names)
|
||||
/// For static boxes: empty Vec (no fields)
|
||||
/// For instance boxes: Vec of field names
|
||||
pub user_defined_boxes: HashMap<String, Vec<String>>,
|
||||
|
||||
/// Phase 201-A: Reserved ValueIds that must not be allocated
|
||||
/// These are PHI dst ValueIds created by LoopHeaderPhiBuilder.
|
||||
@ -98,7 +101,7 @@ impl CompilationContext {
|
||||
Self {
|
||||
compilation_context: None,
|
||||
current_static_box: None,
|
||||
user_defined_boxes: HashSet::new(),
|
||||
user_defined_boxes: HashMap::new(), // Phase 285LLVM-1.1: HashMap for fields
|
||||
reserved_value_ids: HashSet::new(),
|
||||
fn_body_ast: None,
|
||||
weak_fields_by_box: HashMap::new(),
|
||||
@ -124,12 +127,17 @@ impl CompilationContext {
|
||||
|
||||
/// Check if a box is user-defined
|
||||
pub fn is_user_defined_box(&self, name: &str) -> bool {
|
||||
self.user_defined_boxes.contains(name)
|
||||
self.user_defined_boxes.contains_key(name) // Phase 285LLVM-1.1: HashMap check
|
||||
}
|
||||
|
||||
/// Register a user-defined box
|
||||
/// Register a user-defined box (backward compatibility - no fields)
|
||||
pub fn register_user_box(&mut self, name: String) {
|
||||
self.user_defined_boxes.insert(name);
|
||||
self.user_defined_boxes.insert(name, Vec::new()); // Phase 285LLVM-1.1: Empty fields
|
||||
}
|
||||
|
||||
/// Phase 285LLVM-1.1: Register a user-defined box with field information
|
||||
pub fn register_user_box_with_fields(&mut self, name: String, fields: Vec<String>) {
|
||||
self.user_defined_boxes.insert(name, fields);
|
||||
}
|
||||
|
||||
/// Check if a ValueId is reserved
|
||||
|
||||
@ -82,13 +82,17 @@ impl super::MirBuilder {
|
||||
}
|
||||
ASTNode::BoxDeclaration {
|
||||
name,
|
||||
fields, // Phase 285LLVM-1.1: Extract fields
|
||||
methods,
|
||||
is_static,
|
||||
..
|
||||
} => {
|
||||
if !*is_static {
|
||||
self.comp_ctx.user_defined_boxes.insert(name.clone());
|
||||
// Phase 285LLVM-1.1: Register instance box with field information
|
||||
self.comp_ctx.register_user_box_with_fields(name.clone(), fields.clone());
|
||||
} else {
|
||||
// Static box: no fields
|
||||
self.comp_ctx.register_user_box(name.clone());
|
||||
for (mname, mast) in methods {
|
||||
if let ASTNode::FunctionDeclaration { params, .. } = mast {
|
||||
self.comp_ctx
|
||||
@ -223,7 +227,8 @@ impl super::MirBuilder {
|
||||
}
|
||||
} else {
|
||||
// Instance box: register type and lower instance methods/ctors as functions
|
||||
self.comp_ctx.user_defined_boxes.insert(name.clone());
|
||||
// Phase 285LLVM-1.1: Register with field information for LLVM harness
|
||||
self.comp_ctx.register_user_box_with_fields(name.clone(), fields.clone());
|
||||
self.build_box_declaration(
|
||||
name.clone(),
|
||||
methods.clone(),
|
||||
@ -534,6 +539,9 @@ impl super::MirBuilder {
|
||||
// main 関数スコープの SlotRegistry を解放するよ。
|
||||
self.comp_ctx.current_slot_registry = None;
|
||||
|
||||
// Phase 285LLVM-1.1: Copy user box declarations to module metadata for LLVM harness
|
||||
module.metadata.user_box_decls = self.comp_ctx.user_defined_boxes.clone();
|
||||
|
||||
Ok(module)
|
||||
}
|
||||
|
||||
|
||||
@ -49,7 +49,7 @@ pub(crate) fn try_known_rewrite(
|
||||
return None;
|
||||
}
|
||||
// Only user-defined boxes (plugin/core boxesは対象外)
|
||||
if !builder.comp_ctx.user_defined_boxes.contains(cls) {
|
||||
if !builder.comp_ctx.user_defined_boxes.contains_key(cls) { // Phase 285LLVM-1.1: HashMap
|
||||
return None;
|
||||
}
|
||||
// Policy gates(従来互換)
|
||||
@ -124,7 +124,7 @@ pub(crate) fn try_known_rewrite_to_dst(
|
||||
{
|
||||
return None;
|
||||
}
|
||||
if !builder.comp_ctx.user_defined_boxes.contains(cls) {
|
||||
if !builder.comp_ctx.user_defined_boxes.contains_key(cls) { // Phase 285LLVM-1.1: HashMap
|
||||
return None;
|
||||
}
|
||||
let allow_userbox_rewrite =
|
||||
@ -204,7 +204,7 @@ pub(crate) fn try_unique_suffix_rewrite(
|
||||
let fname = cands.remove(0);
|
||||
// 🎯 Phase 21.7++ Phase 3: StaticMethodId SSOT 実装
|
||||
let id = crate::mir::naming::StaticMethodId::parse(&fname)?;
|
||||
if !builder.comp_ctx.user_defined_boxes.contains(&id.box_name) {
|
||||
if !builder.comp_ctx.user_defined_boxes.contains_key(&id.box_name) { // Phase 285LLVM-1.1: HashMap
|
||||
return None;
|
||||
}
|
||||
// unified
|
||||
@ -260,7 +260,7 @@ pub(crate) fn try_unique_suffix_rewrite_to_dst(
|
||||
let fname = cands.remove(0);
|
||||
// 🎯 Phase 21.7++ Phase 3: StaticMethodId SSOT 実装
|
||||
let id = crate::mir::naming::StaticMethodId::parse(&fname)?;
|
||||
if !builder.comp_ctx.user_defined_boxes.contains(&id.box_name) {
|
||||
if !builder.comp_ctx.user_defined_boxes.contains_key(&id.box_name) { // Phase 285LLVM-1.1: HashMap
|
||||
return None;
|
||||
}
|
||||
let _name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname)
|
||||
|
||||
@ -414,6 +414,10 @@ pub struct ModuleMetadata {
|
||||
/// Dev idempotence markers for passes (optional; default empty)
|
||||
/// Key format suggestion: "pass_name:function_name"
|
||||
pub dev_processed_markers: HashSet<String>,
|
||||
|
||||
/// Phase 285LLVM-1.1: User-defined box declarations with fields
|
||||
/// HashMap: box name → field names (empty Vec for static boxes)
|
||||
pub user_box_decls: std::collections::HashMap<String, Vec<String>>,
|
||||
}
|
||||
|
||||
impl MirModule {
|
||||
|
||||
@ -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)"))
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,6 +31,7 @@ pub mod unified_registry; // Deprecation warnings with warn-once guards
|
||||
pub mod extern_registry; // ExternCall (env.*) 登録・診断用レジストリ
|
||||
pub mod host_api; // C ABI: plugins -> host 逆呼び出しAPI(TLSでVMに橋渡し)
|
||||
pub mod host_handles; // C ABI(TLV) 向け HostHandle レジストリ(ユーザー/内蔵Box受け渡し)
|
||||
pub mod weak_handles; // Phase 285LLVM-1: WeakRef Handle レジストリ(bit 63 = 1)
|
||||
pub mod modules_registry;
|
||||
pub mod type_box_abi; // Phase 12: Nyash ABI (vtable) 雛形
|
||||
pub mod type_meta;
|
||||
|
||||
146
src/runtime/weak_handles.rs
Normal file
146
src/runtime/weak_handles.rs
Normal file
@ -0,0 +1,146 @@
|
||||
/*!
|
||||
* Weak Handle Registry (Phase 285LLVM-1)
|
||||
*
|
||||
* 目的:
|
||||
* - WeakRef のための LLVM handle 管理を提供。
|
||||
* - i64 handle (bit 63 = 1) → Weak<dyn NyashBox> をグローバルに保持。
|
||||
* - LLVM backend から FFI 経由でアクセス可能。
|
||||
*
|
||||
* Runtime 表現:
|
||||
* - Strong handle: 0x0000_0000_0000_0001 ~ 0x7FFF_FFFF_FFFF_FFFF (bit 63 = 0)
|
||||
* - Weak handle: 0x8000_0000_0000_0001 ~ 0xFFFF_FFFF_FFFF_FFFF (bit 63 = 1)
|
||||
*/
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{
|
||||
atomic::{AtomicU64, Ordering},
|
||||
Arc, RwLock, Weak,
|
||||
};
|
||||
|
||||
use crate::box_trait::NyashBox;
|
||||
|
||||
/// Weak handle marker (bit 63 = 1)
|
||||
const WEAK_HANDLE_MARKER: u64 = 0x8000_0000_0000_0000;
|
||||
|
||||
/// Extract raw handle ID (clear bit 63)
|
||||
#[inline]
|
||||
fn extract_weak_id(handle: i64) -> u64 {
|
||||
(handle as u64) & !WEAK_HANDLE_MARKER
|
||||
}
|
||||
|
||||
/// Mark handle as weak (set bit 63)
|
||||
#[inline]
|
||||
fn mark_weak_handle(id: u64) -> i64 {
|
||||
(id | WEAK_HANDLE_MARKER) as i64
|
||||
}
|
||||
|
||||
/// Check if handle is weak (bit 63 = 1)
|
||||
#[inline]
|
||||
pub fn is_weak_handle(handle: i64) -> bool {
|
||||
(handle as u64 & WEAK_HANDLE_MARKER) != 0
|
||||
}
|
||||
|
||||
struct WeakRegistry {
|
||||
next: AtomicU64,
|
||||
map: RwLock<HashMap<u64, Weak<dyn NyashBox>>>,
|
||||
}
|
||||
|
||||
impl WeakRegistry {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
next: AtomicU64::new(1),
|
||||
map: RwLock::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc(&self, weak: Weak<dyn NyashBox>) -> i64 {
|
||||
let id = self.next.fetch_add(1, Ordering::Relaxed);
|
||||
if let Ok(mut m) = self.map.write() {
|
||||
m.insert(id, weak);
|
||||
}
|
||||
mark_weak_handle(id)
|
||||
}
|
||||
|
||||
fn get(&self, handle: i64) -> Option<Weak<dyn NyashBox>> {
|
||||
let id = extract_weak_id(handle);
|
||||
self.map.read().ok().and_then(|m| m.get(&id).cloned())
|
||||
}
|
||||
|
||||
fn drop_handle(&self, handle: i64) {
|
||||
let id = extract_weak_id(handle);
|
||||
if let Ok(mut m) = self.map.write() {
|
||||
m.remove(&id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static WEAK_REG: OnceCell<WeakRegistry> = OnceCell::new();
|
||||
fn weak_reg() -> &'static WeakRegistry {
|
||||
WEAK_REG.get_or_init(WeakRegistry::new)
|
||||
}
|
||||
|
||||
/// Weak<dyn NyashBox> → Weak Handle (i64, bit 63 = 1)
|
||||
pub fn to_handle_weak(weak: Weak<dyn NyashBox>) -> i64 {
|
||||
weak_reg().alloc(weak)
|
||||
}
|
||||
|
||||
/// Weak Handle (i64) → Weak<dyn NyashBox>
|
||||
pub fn get_weak(handle: i64) -> Option<Weak<dyn NyashBox>> {
|
||||
weak_reg().get(handle)
|
||||
}
|
||||
|
||||
/// Drop weak handle (release from registry)
|
||||
pub fn drop_weak_handle(handle: i64) {
|
||||
weak_reg().drop_handle(handle)
|
||||
}
|
||||
|
||||
/// Upgrade weak handle to strong handle
|
||||
/// Returns: strong handle (>0) on success, 0 (Void) on failure
|
||||
pub fn upgrade_weak_handle(weak_handle: i64) -> i64 {
|
||||
if let Some(weak) = get_weak(weak_handle) {
|
||||
if let Some(arc) = weak.upgrade() {
|
||||
return crate::runtime::host_handles::to_handle_arc(arc) as i64;
|
||||
}
|
||||
}
|
||||
0 // Void (null)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::box_trait::StringBox;
|
||||
|
||||
#[test]
|
||||
fn test_weak_handle_marker() {
|
||||
let strong_handle = 0x0000_0000_0000_0001i64;
|
||||
let weak_handle = 0x8000_0000_0000_0001i64;
|
||||
|
||||
assert!(!is_weak_handle(strong_handle));
|
||||
assert!(is_weak_handle(weak_handle));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_weak_handle_lifecycle() {
|
||||
let arc: Arc<dyn NyashBox> = Arc::new(StringBox::new("test"));
|
||||
let weak = Arc::downgrade(&arc);
|
||||
|
||||
// Allocate weak handle
|
||||
let weak_handle = to_handle_weak(weak.clone());
|
||||
assert!(is_weak_handle(weak_handle));
|
||||
|
||||
// Upgrade should succeed (arc is alive)
|
||||
let strong_handle = upgrade_weak_handle(weak_handle);
|
||||
assert!(strong_handle > 0);
|
||||
|
||||
// Drop arc
|
||||
drop(arc);
|
||||
|
||||
// Upgrade should fail (arc is dead)
|
||||
let result = upgrade_weak_handle(weak_handle);
|
||||
assert_eq!(result, 0); // Void
|
||||
|
||||
// Cleanup
|
||||
drop_weak_handle(weak_handle);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user