Files
hakorune/src/llvm_py/instructions/const.py
Selfhosting Dev 3e07763af8 docs: update CURRENT_TASK with Box Theory PHI plan (defer/finalize) and MIR v0.5 type meta; add parity tooling and PyVM scaffolding
impl(pyvm/llvmlite):
- add tools/parity.sh; tools/pyvm_runner.py; src/llvm_py/pyvm/*
- emit string const as handle type in MIR JSON; add dst_type hints
- unify '+' to concat_hh with from_i64/from_i8_string bridges; console print via to_i8p_h
- add runtime bridges: nyash.box.from_i64, nyash.string.to_i8p_h

tests:
- add apps/tests/min_str_cat_loop (minimal repro for string cat loop)
2025-09-14 04:51:33 +09:00

100 lines
3.3 KiB
Python

"""
Const instruction lowering
Handles integer, float, string, and void constants
"""
import llvmlite.ir as ir
from typing import Dict, Any
def lower_const(
builder: ir.IRBuilder,
module: ir.Module,
dst: int,
value: Dict[str, Any],
vmap: Dict[int, ir.Value],
resolver=None
) -> None:
"""
Lower MIR Const instruction
Args:
builder: Current LLVM IR builder
module: LLVM module
dst: Destination value ID
value: Const value dict with 'type' and 'value' fields
vmap: Value map (value_id -> llvm value)
"""
const_type = value.get('type', 'void')
const_val = value.get('value')
if const_type == 'i64':
# Integer constant
i64 = ir.IntType(64)
llvm_val = ir.Constant(i64, int(const_val))
vmap[dst] = llvm_val
elif const_type == 'f64':
# Float constant
f64 = ir.DoubleType()
llvm_val = ir.Constant(f64, float(const_val))
vmap[dst] = llvm_val
elif const_type == 'string' or (isinstance(const_type, dict) and const_type.get('kind') in ('handle','ptr') and const_type.get('box_type') == 'StringBox'):
# String constant - create global and immediately box to i64 handle
i8 = ir.IntType(8)
str_val = str(const_val)
str_bytes = str_val.encode('utf-8') + b'\0'
arr_ty = ir.ArrayType(i8, len(str_bytes))
str_const = ir.Constant(arr_ty, bytearray(str_bytes))
try:
fn = builder.block.parent
fn_name = getattr(fn, 'name', 'fn')
except Exception:
fn_name = 'fn'
base = f".str.{fn_name}.{dst}"
existing = {g.name for g in module.global_values}
name = base
n = 1
while name in existing:
name = f"{base}.{n}"; n += 1
g = ir.GlobalVariable(module, arr_ty, name=name)
g.initializer = str_const
g.linkage = 'private'
g.global_constant = True
# GEP to first element and box to handle immediately
i32 = ir.IntType(32)
c0 = ir.Constant(i32, 0)
gep = builder.gep(g, [c0, c0], inbounds=True)
i8p = i8.as_pointer()
boxer_ty = ir.FunctionType(ir.IntType(64), [i8p])
boxer = None
for f in module.functions:
if f.name == 'nyash.box.from_i8_string':
boxer = f
break
if boxer is None:
boxer = ir.Function(module, boxer_ty, name='nyash.box.from_i8_string')
handle = builder.call(boxer, [gep], name=f"const_str_h_{dst}")
vmap[dst] = handle
if resolver is not None:
if hasattr(resolver, 'string_literals'):
resolver.string_literals[dst] = str_val
# Mark this value-id as string-ish to guide '+' and '==' lowering
if hasattr(resolver, 'mark_string'):
resolver.mark_string(dst)
# Keep raw pointer for potential pointer-API sites (e.g., console.log)
try:
resolver.string_ptrs[dst] = gep
except Exception:
pass
elif const_type == 'void':
# Void/null constant - use i64 zero
i64 = ir.IntType(64)
vmap[dst] = ir.Constant(i64, 0)
else:
# Unknown type - default to i64 zero
i64 = ir.IntType(64)
vmap[dst] = ir.Constant(i64, 0)