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:
@ -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()
|
||||
|
||||
Reference in New Issue
Block a user