Files
hakorune/src/llvm_py/instructions/safepoint.py
Selfhosting Dev ef44801fa6 Python LLVM backend implementation (experimental)
- Created llvmlite-based LLVM backend in src/llvm_py/
- Implemented all MIR14 instructions (const, binop, jump, branch, ret, compare, phi, call, boxcall, externcall, typeop, newbox, safepoint, barrier)
- Experimental LoopForm support
- ~2000 lines of clean Python code vs complex Rust/inkwell
- Useful for PHI/SSA validation and rapid prototyping
- Added documentation to CLAUDE.md

This was created while waiting for ChatGPT's investigation of BuilderCursor issues.
2025-09-12 20:55:13 +09:00

107 lines
3.4 KiB
Python

"""
Safepoint instruction lowering
GC safepoints where runtime can safely collect garbage
"""
import llvmlite.ir as ir
from typing import Dict, List, Optional
def lower_safepoint(
builder: ir.IRBuilder,
module: ir.Module,
live_values: List[int],
vmap: Dict[int, ir.Value],
safepoint_id: Optional[int] = None
) -> None:
"""
Lower MIR Safepoint instruction
Safepoints are places where GC can safely run.
Live values must be tracked for potential relocation.
Args:
builder: Current LLVM IR builder
module: LLVM module
live_values: List of value IDs that are live across safepoint
vmap: Value map
safepoint_id: Optional safepoint identifier
"""
# Look up or declare safepoint function
safepoint_func = None
for f in module.functions:
if f.name == "ny_safepoint":
safepoint_func = f
break
if not safepoint_func:
# Declare ny_safepoint(live_count: i64, live_values: i64*) -> void
i64 = ir.IntType(64)
void = ir.VoidType()
func_type = ir.FunctionType(void, [i64, i64.as_pointer()])
safepoint_func = ir.Function(module, func_type, name="ny_safepoint")
# Prepare live values array
i64 = ir.IntType(64)
if live_values:
# Allocate array for live values
array_size = len(live_values)
live_array = builder.alloca(i64, size=array_size, name="live_vals")
# Store each live value
for i, vid in enumerate(live_values):
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)
idx = ir.Constant(ir.IntType(32), i)
ptr = builder.gep(live_array, [idx])
builder.store(val, ptr)
# Call safepoint
count = ir.Constant(i64, array_size)
builder.call(safepoint_func, [count, live_array])
# After safepoint, reload values (they may have moved)
for i, vid in enumerate(live_values):
idx = ir.Constant(ir.IntType(32), i)
ptr = builder.gep(live_array, [idx])
new_val = builder.load(ptr, name=f"reload_{vid}")
vmap[vid] = new_val
else:
# No live values
zero = ir.Constant(i64, 0)
null = ir.Constant(i64.as_pointer(), None)
builder.call(safepoint_func, [zero, null])
def insert_automatic_safepoint(
builder: ir.IRBuilder,
module: ir.Module,
location: str # "loop_header", "function_call", etc.
) -> None:
"""
Insert automatic safepoint at strategic locations
Args:
builder: Current LLVM IR builder
module: LLVM module
location: Location type for debugging
"""
# Simple safepoint without tracking specific values
# Runtime will scan stack/registers
check_func = None
for f in module.functions:
if f.name == "ny_check_safepoint":
check_func = f
break
if not check_func:
# Declare ny_check_safepoint() -> void
void = ir.VoidType()
func_type = ir.FunctionType(void, [])
check_func = ir.Function(module, func_type, name="ny_check_safepoint")
# Insert safepoint check
builder.call(check_func, [], name=f"safepoint_{location}")