diff --git a/crates/nyash_kernel/src/lib.rs b/crates/nyash_kernel/src/lib.rs index c3253440..017562de 100644 --- a/crates/nyash_kernel/src/lib.rs +++ b/crates/nyash_kernel/src/lib.rs @@ -710,6 +710,17 @@ pub extern "C" fn ny_check_safepoint() { nyash_rust::runtime::global_hooks::safepoint_and_poll(); } +// export: ny_release_strong(handle: i64) -> void +// Phase 287: Release strong reference for variable overwrite semantics +#[no_mangle] +pub extern "C" fn ny_release_strong(handle: i64) { + if handle > 0 { + // Drop the handle - this decrements the reference count + // and may trigger deallocation if count reaches zero + handles::drop(handle as u64); + } +} + #[export_name = "nyash.string.birth_h"] pub extern "C" fn nyash_string_birth_h_export() -> i64 { // Create a new StringBox via unified plugin host; return runtime handle as i64 diff --git a/docs/development/current/main/phases/phase-285/P3-INSTRUCTIONS.md b/docs/development/current/main/phases/phase-285/P3-INSTRUCTIONS.md index 8f933b86..28b31700 100644 --- a/docs/development/current/main/phases/phase-285/P3-INSTRUCTIONS.md +++ b/docs/development/current/main/phases/phase-285/P3-INSTRUCTIONS.md @@ -37,3 +37,25 @@ cargo build --release --features llvm - `cargo build --release --features llvm` が通る - 上記 integration filter が PASS または理由付き SKIP - quick gate が緑(154/154 PASS) + +--- + +## Phase 285 P3 実施結果(2025-12-26) + +### 実装完了 +- ✅ LLVM バックエンド: lifecycle.py で lower_keepalive/lower_release_strong 実装 +- ✅ instruction_lower.py: KeepAlive/ReleaseStrong ディスパッチ追加 +- ✅ nyash_kernel: ny_release_strong() ランタイム関数実装 +- ✅ mir_json_emit.rs: KeepAlive/ReleaseStrong JSON シリアライゼーション追加 + +### テスト結果 +- ✅ **phase285_p2_weak_upgrade_fail_llvm**: PASS +- ⏸️ **phase285_p2_weak_upgrade_success_llvm**: SKIP +- ⏸️ **phase284_p2_return_in_loop_llvm**: SKIP + +### SKIP 理由(llvm feature 有効でも SKIP) +テストスクリプトは `./target/release/hakorune --version` の出力に "features.*llvm" が含まれているかをチェックしているが、現在の --version 実装は feature 情報を出力していない。そのため、LLVM feature を有効にしてビルドしても、環境チェックで SKIP される。 + +**技術的詳細**: `--version` が "nyash 1.0" のみを出力し、cargo features 情報を含めていない。weak_upgrade_fail_llvm は別の環境チェック方式(または環境変数ベース)を使用しているため PASS した。 + +**対応方針**: Phase 285 P3 の目的(KeepAlive/ReleaseStrong の LLVM 実装)は達成済み。--version 出力の feature 表示は別タスク(Phase 286 以降)で対応予定。 diff --git a/src/llvm_py/builders/instruction_lower.py b/src/llvm_py/builders/instruction_lower.py index a5608f21..4ceb73bb 100644 --- a/src/llvm_py/builders/instruction_lower.py +++ b/src/llvm_py/builders/instruction_lower.py @@ -23,6 +23,7 @@ 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 +from instructions.lifecycle import lower_keepalive, lower_release_strong # Phase 287: Lifecycle def lower_instruction(owner, builder: ir.IRBuilder, inst: Dict[str, Any], func: ir.Function): @@ -177,6 +178,22 @@ def lower_instruction(owner, builder: ir.IRBuilder, inst: Dict[str, Any], func: barrier_type = inst.get("type", "memory") lower_barrier(builder, barrier_type, ctx=getattr(owner, 'ctx', None)) + elif op == "keepalive": + # Phase 287: KeepAlive (no-op in LLVM, affects DCE/liveness only) + values = inst.get("values", []) + lower_keepalive(builder, owner.module, values, vmap_ctx, + resolver=owner.resolver, preds=owner.preds, + block_end_values=owner.block_end_values, bb_map=owner.bb_map, + ctx=getattr(owner, 'ctx', None)) + + elif op == "release_strong": + # Phase 287: ReleaseStrong (variable overwrite semantics) + values = inst.get("values", []) + lower_release_strong(builder, owner.module, values, vmap_ctx, + resolver=owner.resolver, preds=owner.preds, + block_end_values=owner.block_end_values, bb_map=owner.bb_map, + ctx=getattr(owner, 'ctx', None)) + elif op == "select": # Phase 256 P1.5: Select instruction (ternary conditional) cond = inst.get("cond") diff --git a/src/llvm_py/instructions/__init__.py b/src/llvm_py/instructions/__init__.py index 37ac69fd..f6e08cbb 100644 --- a/src/llvm_py/instructions/__init__.py +++ b/src/llvm_py/instructions/__init__.py @@ -23,11 +23,15 @@ from .newbox import lower_newbox # LoopForm support from .loopform import LoopFormContext, lower_while_loopform +# Phase 287: Lifecycle management +from .lifecycle import lower_keepalive, lower_release_strong + __all__ = [ 'lower_const', 'lower_binop', 'lower_compare', 'lower_jump', 'lower_branch', 'lower_return', 'lower_phi', 'lower_call', 'lower_boxcall', 'lower_externcall', 'lower_typeop', 'lower_safepoint', 'lower_barrier', 'lower_newbox', - 'LoopFormContext', 'lower_while_loopform' + 'LoopFormContext', 'lower_while_loopform', + 'lower_keepalive', 'lower_release_strong' ] diff --git a/src/llvm_py/instructions/lifecycle.py b/src/llvm_py/instructions/lifecycle.py new file mode 100644 index 00000000..dbc54fa8 --- /dev/null +++ b/src/llvm_py/instructions/lifecycle.py @@ -0,0 +1,96 @@ +""" +Lifecycle management instruction lowering (Phase 287) +KeepAlive and ReleaseStrong for reference counting semantics +""" + +import llvmlite.ir as ir +from typing import Dict, List, Any, Optional + +def lower_keepalive( + builder: ir.IRBuilder, + module: ir.Module, + values: List[int], + vmap: Dict[int, ir.Value], + resolver=None, + preds=None, + block_end_values=None, + bb_map=None, + ctx: Optional[Any] = None +) -> None: + """ + Lower MIR KeepAlive instruction + + KeepAlive is a no-op in LLVM backend - it only affects DCE/liveness + analysis in MIR optimization passes. + + Args: + builder: Current LLVM IR builder + module: LLVM module + values: List of value IDs to keep alive + vmap: Value map + """ + # No-op: KeepAlive only affects MIR DCE/liveness analysis + pass + +def lower_release_strong( + builder: ir.IRBuilder, + module: ir.Module, + values: List[int], + vmap: Dict[int, ir.Value], + resolver=None, + preds=None, + block_end_values=None, + bb_map=None, + ctx: Optional[Any] = None +) -> None: + """ + Lower MIR ReleaseStrong instruction + + Releases strong references to the specified values, potentially + triggering deallocation if reference count drops to zero. + + Args: + builder: Current LLVM IR builder + module: LLVM module + values: List of value IDs to release + vmap: Value map + """ + # Look up or declare ny_release_strong function + release_func = None + for f in module.functions: + if f.name == "ny_release_strong": + release_func = f + break + + if not release_func: + # Declare ny_release_strong(handle: i64) -> void + i64 = ir.IntType(64) + void = ir.VoidType() + func_type = ir.FunctionType(void, [i64]) + release_func = ir.Function(module, func_type, name="ny_release_strong") + + # Release each value + i64 = ir.IntType(64) + for vid in values: + # Resolve value (prefer BuildCtx if provided) + r = resolver; p = preds; bev = block_end_values; bbm = bb_map + if ctx is not None: + try: + r = getattr(ctx, 'resolver', r) + p = getattr(ctx, 'preds', p) + bev = getattr(ctx, 'block_end_values', bev) + bbm = getattr(ctx, 'bb_map', bbm) + except Exception: + pass + + if r is not None and p is not None and bev is not None and bbm is not None: + val = r.resolve_i64(vid, builder.block, p, bev, vmap, bbm) + else: + val = vmap.get(vid, ir.Constant(i64, 0)) + + # Ensure i64 (handles are i64) + if hasattr(val, 'type') and val.type.is_pointer: + val = builder.ptrtoint(val, i64, name=f"release_p2i_{vid}") + + # Call release function + builder.call(release_func, [val], name=f"release_{vid}") diff --git a/src/runner/mir_json_emit.rs b/src/runner/mir_json_emit.rs index b4a43d24..1d89f4db 100644 --- a/src/runner/mir_json_emit.rs +++ b/src/runner/mir_json_emit.rs @@ -548,6 +548,15 @@ pub fn emit_mir_json_for_harness( I::WeakLoad { dst, weak_ref } => { insts.push(json!({"op":"weak_load","dst": dst.as_u32(), "weak_ref": weak_ref.as_u32()})); } + // Phase 287: Lifecycle management + I::KeepAlive { values } => { + let values_json: Vec<_> = values.iter().map(|v| json!(v.as_u32())).collect(); + insts.push(json!({"op":"keepalive","values":values_json})); + } + I::ReleaseStrong { values } => { + let values_json: Vec<_> = values.iter().map(|v| json!(v.as_u32())).collect(); + insts.push(json!({"op":"release_strong","values":values_json})); + } _ => { /* skip non-essential ops for initial harness */ } } } @@ -976,6 +985,15 @@ pub fn emit_mir_json_for_harness_bin( I::WeakLoad { dst, weak_ref } => { insts.push(json!({"op":"weak_load","dst": dst.as_u32(), "weak_ref": weak_ref.as_u32()})); } + // Phase 287: Lifecycle management + I::KeepAlive { values } => { + let values_json: Vec<_> = values.iter().map(|v| json!(v.as_u32())).collect(); + insts.push(json!({"op":"keepalive","values":values_json})); + } + I::ReleaseStrong { values } => { + let values_json: Vec<_> = values.iter().map(|v| json!(v.as_u32())).collect(); + insts.push(json!({"op":"release_strong","values":values_json})); + } _ => {} } }