feat: comprehensive development progress

- Pattern matching implementation extended in match_expr.rs
- CLI configuration structured with categorized groups (task recommendation completed)
- Python LLVM builder split into function_lower.py (task recommendation completed)
- parse_box_declaration massive function refactored (task recommendation completed)
- Phase 16 Macro Revolution comprehensive planning and documentation
- Archive legacy phase documentation for clean structure
- HTTP message box improvements and performance optimizations
- MIR builder enhancements and control flow improvements

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Selfhosting Dev
2025-09-19 15:11:57 +09:00
parent 3c7a5de900
commit 811e3eb3f8
18 changed files with 739 additions and 250 deletions

View File

@ -0,0 +1,243 @@
from typing import Dict, Any, List
from llvmlite import ir
from trace import debug as trace_debug
def lower_blocks(builder, func: ir.Function, block_by_id: Dict[int, Dict[str, Any]], order: List[int], loop_plan: Dict[str, Any] | None):
skipped: set[int] = set()
if loop_plan is not None:
try:
for bskip in loop_plan.get('skip_blocks', []):
if bskip != loop_plan.get('header'):
skipped.add(int(bskip))
except Exception:
pass
for bid in order:
block_data = block_by_id.get(bid)
if block_data is None:
continue
# If loop prepass applies, lower while once at header and skip loop-internal blocks
if loop_plan is not None and bid == loop_plan.get('header'):
bb = builder.bb_map[bid]
ib = ir.IRBuilder(bb)
try:
builder.resolver.builder = ib
builder.resolver.module = builder.module
except Exception:
pass
builder.loop_count += 1
body_insts = loop_plan.get('body_insts', [])
cond_vid = loop_plan.get('cond')
from instructions.loopform import lower_while_loopform
ok = False
try:
builder._current_vmap = dict(builder.vmap)
ok = lower_while_loopform(
ib,
func,
cond_vid,
body_insts,
builder.loop_count,
builder.vmap,
builder.bb_map,
builder.resolver,
builder.preds,
builder.block_end_values,
getattr(builder, 'ctx', None),
)
except Exception:
ok = False
if not ok:
try:
builder.resolver._owner_lower_instruction = builder.lower_instruction
except Exception:
pass
from instructions.controlflow.while_ import lower_while_regular
lower_while_regular(ib, func, cond_vid, body_insts,
builder.loop_count, builder.vmap, builder.bb_map,
builder.resolver, builder.preds, builder.block_end_values)
try:
delattr(builder, '_current_vmap')
except Exception:
pass
for bskip in loop_plan.get('skip_blocks', []):
skipped.add(bskip)
# Ensure skipped original blocks have a valid terminator: branch to while exit
try:
exit_name = f"while{builder.loop_count}_exit"
exit_bb = None
for bbf in func.blocks:
try:
if str(bbf.name) == exit_name:
exit_bb = bbf
break
except Exception:
pass
if exit_bb is not None:
try:
orig_exit_bb = builder.bb_map.get(loop_plan.get('exit'))
if orig_exit_bb is not None and exit_bb.terminator is None:
ibx = ir.IRBuilder(exit_bb)
ibx.branch(orig_exit_bb)
except Exception:
pass
for bskip in loop_plan.get('skip_blocks', []):
if bskip == loop_plan.get('header'):
continue
bb_skip = builder.bb_map.get(bskip)
if bb_skip is None:
continue
try:
if bb_skip.terminator is None:
ib = ir.IRBuilder(bb_skip)
if orig_exit_bb is not None:
ib.branch(orig_exit_bb)
except Exception:
pass
except Exception:
pass
continue
if bid in skipped:
continue
bb = builder.bb_map[bid]
ib = ir.IRBuilder(bb)
try:
builder.resolver.builder = ib
builder.resolver.module = builder.module
except Exception:
pass
block_data = block_by_id.get(bid, {})
insts = block_data.get('instructions', []) or []
# Split into body and terminator ops
body_ops: List[Dict[str, Any]] = []
term_ops: List[Dict[str, Any]] = []
for inst in insts:
try:
opx = inst.get('op')
except Exception:
opx = None
if opx in ("ret","jump","branch"):
term_ops.append(inst)
elif opx == "phi":
continue
else:
body_ops.append(inst)
# Per-block SSA map
vmap_cur: Dict[int, ir.Value] = {}
try:
for _vid, _val in (builder.vmap or {}).items():
keep = True
try:
if hasattr(_val, 'add_incoming'):
bb_of = getattr(getattr(_val, 'basic_block', None), 'name', None)
keep = (bb_of == bb.name)
except Exception:
keep = False
if keep:
vmap_cur[_vid] = _val
except Exception:
vmap_cur = dict(builder.vmap)
builder._current_vmap = vmap_cur
created_ids: List[int] = []
defined_here_all: set = set()
for _inst in body_ops:
try:
d = _inst.get('dst')
if isinstance(d, int):
defined_here_all.add(d)
except Exception:
pass
# Lower body ops
for i_idx, inst in enumerate(body_ops):
try:
trace_debug(f"[llvm-py] body op: {inst.get('op')} dst={inst.get('dst')} cond={inst.get('cond')}")
except Exception:
pass
try:
if bb.terminator is not None:
break
except Exception:
pass
ib.position_at_end(bb)
if inst.get('op') == 'copy':
src_i = inst.get('src')
skip_now = False
if isinstance(src_i, int):
try:
for _rest in body_ops[i_idx+1:]:
try:
if int(_rest.get('dst')) == int(src_i):
skip_now = True
break
except Exception:
pass
except Exception:
pass
if skip_now:
pass
else:
builder.lower_instruction(ib, inst, func)
else:
builder.lower_instruction(ib, inst, func)
try:
dst = inst.get("dst")
if isinstance(dst, int):
if dst in builder.vmap:
_gval = builder.vmap[dst]
try:
if hasattr(_gval, 'add_incoming'):
bb_of = getattr(getattr(_gval, 'basic_block', None), 'name', None)
if bb_of == bb.name:
vmap_cur[dst] = _gval
else:
vmap_cur[dst] = _gval
except Exception:
vmap_cur[dst] = _gval
if dst not in created_ids and dst in vmap_cur:
created_ids.append(dst)
except Exception:
pass
# Lower terminators
for inst in term_ops:
try:
trace_debug(f"[llvm-py] term op: {inst.get('op')} dst={inst.get('dst')} cond={inst.get('cond')}")
except Exception:
pass
try:
if bb.terminator is not None:
break
except Exception:
pass
ib.position_at_end(bb)
builder.lower_instruction(ib, inst, func)
try:
for vid in created_ids:
val = vmap_cur.get(vid)
if val is not None and hasattr(val, 'add_incoming'):
try:
builder.vmap[vid] = val
except Exception:
pass
except Exception:
pass
# End-of-block snapshot
snap = dict(vmap_cur)
try:
keys = sorted(list(snap.keys()))
from phi_wiring.common import trace as trace_phi_json
try:
trace_phi_json({"phi": "snapshot", "block": int(bid), "keys": [int(k) for k in keys[:20]]})
except Exception:
pass
except Exception:
pass
for vid in created_ids:
if vid in vmap_cur:
builder.def_blocks.setdefault(vid, set()).add(block_data.get("id", 0))
builder.block_end_values[bid] = snap
try:
delattr(builder, '_current_vmap')
except Exception:
pass

View File

@ -1,84 +1,257 @@
from typing import Dict, Any
from typing import Dict, Any, List
from llvmlite import ir
from trace import debug as trace_debug
from prepass.if_merge import plan_ret_phi_predeclare
from prepass.loops import detect_simple_while
from phi_wiring import setup_phi_placeholders as _setup_phi_placeholders, finalize_phis as _finalize_phis
def lower_function(builder, func_data: Dict[str, Any]):
"""Lower a single MIR function to LLVM IR using the given builder context.
This helper is a thin wrapper that delegates to the NyashLLVMBuilder's
existing methods/attributes, enabling gradual file decomposition without
changing semantics.
This is a faithful extraction of NyashLLVMBuilder.lower_function.
"""
import os, re
name = func_data.get("name", "unknown")
builder.current_function_name = name
import re
params = func_data.get("params", [])
blocks = func_data.get("blocks", [])
# Determine function signature
if name == "ny_main":
func_ty = builder.i32.func_type([])
# Special case: ny_main returns i32
func_ty = ir.FunctionType(builder.i32, [])
else:
# Default: i64(i64, ...) signature; derive arity from '/N' suffix when params missing
m = re.search(r"/(\d+)$", name)
arity = int(m.group(1)) if m else len(params)
param_types = [builder.i64] * arity
func_ty = builder.i64.func_type(param_types)
func_ty = ir.FunctionType(builder.i64, param_types)
# Reset per-function maps and resolver caches to avoid cross-function collisions
try:
builder.vmap.clear()
except Exception:
builder.vmap = {}
builder.bb_map = {}
builder.preds = {}
builder.block_end_values = {}
builder.def_blocks = {}
builder.predeclared_ret_phis = {}
try:
builder.bb_map.clear()
except Exception:
builder.bb_map = {}
try:
# Reset resolver caches keyed by block names
builder.resolver.i64_cache.clear()
builder.resolver.ptr_cache.clear()
builder.resolver.f64_cache.clear()
if hasattr(builder.resolver, '_end_i64_cache'):
builder.resolver._end_i64_cache.clear()
if hasattr(builder.resolver, 'string_ids'):
builder.resolver.string_ids.clear()
if hasattr(builder.resolver, 'string_literals'):
builder.resolver.string_literals.clear()
if hasattr(builder.resolver, 'string_ptrs'):
builder.resolver.string_ptrs.clear()
except Exception:
pass
# Ensure function exists or create one
fn = None
# Create or reuse function
func = None
for f in builder.module.functions:
if f.name == name:
fn = f
func = f
break
if fn is None:
from llvmlite import ir
fn = ir.Function(builder.module, func_ty, name=name)
if func is None:
func = ir.Function(builder.module, func_ty, name=name)
# Create all basic blocks first
from llvmlite import ir
block_by_id = {}
for b in blocks:
bbid = int(b.get("id", 0))
bb = fn.append_basic_block(name=f"bb{bbid}")
block_by_id[bbid] = bb
builder.bb_map[bbid] = bb
# Map parameters to vmap (value_id: 0..arity-1)
try:
arity = len(func.args)
for i in range(arity):
builder.vmap[i] = func.args[i]
except Exception:
pass
# Predeclare ret PHIs if needed (if-merge prepass)
from prepass.if_merge import plan_ret_phi_predeclare
plan = plan_ret_phi_predeclare(block_by_id)
if plan:
# Build predecessor map from control-flow edges
builder.preds = {}
for block_data in blocks:
bid = block_data.get("id", 0)
builder.preds.setdefault(bid, [])
for block_data in blocks:
src = block_data.get("id", 0)
for inst in block_data.get("instructions", []):
op = inst.get("op")
if op == "jump":
t = inst.get("target")
if t is not None:
builder.preds.setdefault(t, []).append(src)
elif op == "branch":
th = inst.get("then")
el = inst.get("else")
if th is not None:
builder.preds.setdefault(th, []).append(src)
if el is not None:
builder.preds.setdefault(el, []).append(src)
# Create all blocks first
for block_data in blocks:
bid = block_data.get("id", 0)
block_name = f"bb{bid}"
bb = func.append_basic_block(block_name)
builder.bb_map[bid] = bb
# Build quick lookup for blocks by id
block_by_id: Dict[int, Dict[str, Any]] = {}
for block_data in blocks:
block_by_id[block_data.get("id", 0)] = block_data
# Determine entry block: first with no predecessors; fallback to first block
entry_bid = None
for bid, preds in builder.preds.items():
if len(preds) == 0:
entry_bid = bid
break
if entry_bid is None and blocks:
entry_bid = blocks[0].get("id", 0)
# Compute approx preds-first order
visited = set()
order: List[int] = []
def visit(bid: int):
if bid in visited:
return
visited.add(bid)
for p in builder.preds.get(bid, []):
visit(p)
order.append(bid)
if entry_bid is not None:
visit(entry_bid)
for bid in block_by_id.keys():
if bid not in visited:
visit(bid)
# Prepass: collect PHI metadata and placeholders
_setup_phi_placeholders(builder, blocks)
# Optional: if-merge prepass (gate NYASH_LLVM_PREPASS_IFMERGE)
try:
if os.environ.get('NYASH_LLVM_PREPASS_IFMERGE') == '1':
plan = plan_ret_phi_predeclare(block_by_id)
if plan:
if not hasattr(builder, 'block_phi_incomings') or builder.block_phi_incomings is None:
builder.block_phi_incomings = {}
for bbid, ret_vid in plan.items():
try:
preds_raw = [p for p in builder.preds.get(bbid, []) if p != bbid]
except Exception:
preds_raw = []
seen = set(); preds_list = []
for p in preds_raw:
if p not in seen:
preds_list.append(p); seen.add(p)
try:
builder.block_phi_incomings.setdefault(int(bbid), {})[int(ret_vid)] = [
(int(p), int(ret_vid)) for p in preds_list
]
except Exception:
pass
try:
trace_debug(f"[prepass] if-merge: plan metadata at bb{bbid} for v{ret_vid} preds={preds_list}")
except Exception:
pass
except Exception:
pass
# Predeclare PHIs for used-in-block values defined in predecessors (multi-pred only)
try:
from cfg.utils import build_preds_succs
local_preds, _ = build_preds_succs(block_by_id)
def _collect_defs(block):
defs = set()
for ins in block.get('instructions') or []:
try:
dstv = ins.get('dst')
if isinstance(dstv, int):
defs.add(int(dstv))
except Exception:
pass
return defs
def _collect_uses(block):
uses = set()
for ins in block.get('instructions') or []:
for k in ('lhs','rhs','value','cond','box_val'):
try:
v = ins.get(k)
if isinstance(v, int):
uses.add(int(v))
except Exception:
pass
return uses
if not hasattr(builder, 'block_phi_incomings') or builder.block_phi_incomings is None:
builder.block_phi_incomings = {}
for (bbid, pairs) in plan.items():
for (ret_vid, preds_list) in pairs.items():
builder.block_phi_incomings.setdefault(int(bbid), {}).setdefault(int(ret_vid), [])
builder.block_phi_incomings[int(bbid)][int(ret_vid)] = [(int(p), int(ret_vid)) for p in preds_list]
for bid, blk in block_by_id.items():
try:
preds_raw = [p for p in local_preds.get(int(bid), []) if p != int(bid)]
except Exception:
preds_raw = []
seen = set(); preds_list = []
for p in preds_raw:
if p not in seen:
preds_list.append(p); seen.add(p)
if len(preds_list) <= 1:
continue
defs = _collect_defs(blk)
uses = _collect_uses(blk)
need = [u for u in uses if u not in defs]
if not need:
continue
for vid in need:
try:
builder.block_phi_incomings.setdefault(int(bid), {}).setdefault(int(vid), [])
builder.block_phi_incomings[int(bid)][int(vid)] = [(int(p), int(vid)) for p in preds_list]
except Exception:
pass
try:
builder.resolver.block_phi_incomings = builder.block_phi_incomings
except Exception:
pass
except Exception:
pass
# Lower instructions per block
from llvmlite.ir import IRBuilder
from instructions import dispatcher # if exists; else inline lowerers
for b in blocks:
bbid = int(b.get("id", 0))
bb = block_by_id[bbid]
builder_bb = IRBuilder(bb)
builder.resolver.attach_function_and_block(fn, bb)
insts = b.get("insts", [])
for inst in insts:
op = inst.get("op")
# Delegate to existing NyashLLVMBuilder method for now
builder.lower_instruction(op, inst, builder_bb)
# Optional: simple loop prepass
loop_plan = None
try:
if os.environ.get('NYASH_LLVM_PREPASS_LOOP') == '1':
loop_plan = detect_simple_while(block_by_id)
if loop_plan is not None:
trace_debug(f"[prepass] detect loop header=bb{loop_plan['header']} then=bb{loop_plan['then']} latch=bb{loop_plan['latch']} exit=bb{loop_plan['exit']}")
except Exception:
loop_plan = None
# Finalize PHIs after the function is fully lowered
from phi_wiring import finalize_phis as _finalize_phis
from builders.block_lower import lower_blocks as _lower_blocks
_lower_blocks(builder, func, block_by_id, order, loop_plan)
# Optional: capture lowering ctx for downstream helpers
try:
builder.ctx = dict(
module=builder.module,
i64=builder.i64,
i32=builder.i32,
i8=builder.i8,
i1=builder.i1,
i8p=builder.i8p,
vmap=builder.vmap,
bb_map=builder.bb_map,
preds=builder.preds,
block_end_values=builder.block_end_values,
resolver=builder.resolver,
trace_phi=os.environ.get('NYASH_LLVM_TRACE_PHI') == '1',
verbose=os.environ.get('NYASH_CLI_VERBOSE') == '1',
)
builder.resolver.ctx = builder.ctx
except Exception:
pass
# Finalize PHIs for this function
_finalize_phis(builder)

View File

@ -0,0 +1,179 @@
from typing import Dict, Any
from llvmlite import ir
from trace import debug as trace_debug
# Import instruction handlers
from instructions.const import lower_const
from instructions.binop import lower_binop
from instructions.compare import lower_compare
from instructions.controlflow.jump import lower_jump
from instructions.controlflow.branch import lower_branch
from instructions.ret import lower_return
from instructions.copy import lower_copy
from instructions.call import lower_call
from instructions.boxcall import lower_boxcall
from instructions.externcall import lower_externcall
from instructions.typeop import lower_typeop
from instructions.newbox import lower_newbox
from instructions.safepoint import lower_safepoint
from instructions.barrier import lower_barrier
from instructions.loopform import lower_while_loopform
from instructions.controlflow.while_ import lower_while_regular
def lower_instruction(owner, builder: ir.IRBuilder, inst: Dict[str, Any], func: ir.Function):
"""Dispatch a single MIR instruction to appropriate lowering helper.
owner is the NyashLLVMBuilder instance to access module, resolver, maps, and ctx.
"""
op = inst.get("op")
# Pick current vmap context (per-block context during lowering)
vmap_ctx = getattr(owner, '_current_vmap', owner.vmap)
if op == "const":
dst = inst.get("dst")
value = inst.get("value")
lower_const(builder, owner.module, dst, value, vmap_ctx, owner.resolver)
elif op == "binop":
operation = inst.get("operation")
lhs = inst.get("lhs")
rhs = inst.get("rhs")
dst = inst.get("dst")
dst_type = inst.get("dst_type")
lower_binop(builder, owner.resolver, operation, lhs, rhs, dst,
vmap_ctx, builder.block, owner.preds, owner.block_end_values, owner.bb_map,
dst_type=dst_type)
elif op == "jump":
target = inst.get("target")
lower_jump(builder, target, owner.bb_map)
elif op == "copy":
dst = inst.get("dst")
src = inst.get("src")
lower_copy(builder, dst, src, vmap_ctx, owner.resolver, builder.block, owner.preds, owner.block_end_values, owner.bb_map, getattr(owner, 'ctx', None))
elif op == "branch":
cond = inst.get("cond")
then_bid = inst.get("then")
else_bid = inst.get("else")
lower_branch(builder, cond, then_bid, else_bid, vmap_ctx, owner.bb_map, owner.resolver, owner.preds, owner.block_end_values)
elif op == "ret":
value = inst.get("value")
lower_return(builder, value, vmap_ctx, func.function_type.return_type,
owner.resolver, owner.preds, owner.block_end_values, owner.bb_map, getattr(owner, 'ctx', None))
elif op == "phi":
# No-op here: PHIはメタのみresolverがondemand生成
return
elif op == "compare":
# Dedicated compare op
operation = inst.get("operation") or inst.get("op")
lhs = inst.get("lhs")
rhs = inst.get("rhs")
dst = inst.get("dst")
cmp_kind = inst.get("cmp_kind")
lower_compare(builder, operation, lhs, rhs, dst, vmap_ctx,
owner.resolver, builder.block, owner.preds, owner.block_end_values, owner.bb_map,
meta={"cmp_kind": cmp_kind} if cmp_kind else None,
ctx=getattr(owner, 'ctx', None))
elif op == "call":
func_name = inst.get("func")
args = inst.get("args", [])
dst = inst.get("dst")
lower_call(builder, owner.module, func_name, args, dst, vmap_ctx, owner.resolver, owner.preds, owner.block_end_values, owner.bb_map, getattr(owner, 'ctx', None))
elif op == "boxcall":
box_vid = inst.get("box")
method = inst.get("method")
args = inst.get("args", [])
dst = inst.get("dst")
lower_boxcall(builder, owner.module, box_vid, method, args, dst,
vmap_ctx, owner.resolver, owner.preds, owner.block_end_values, owner.bb_map, getattr(owner, 'ctx', None))
# Optional: honor explicit dst_type for tagging (string handle)
try:
dst_type = inst.get("dst_type")
if dst is not None and isinstance(dst_type, dict):
if dst_type.get("kind") == "handle" and dst_type.get("box_type") == "StringBox":
if hasattr(owner.resolver, 'mark_string'):
owner.resolver.mark_string(int(dst))
# Track last substring for optional esc_json fallback
try:
if isinstance(method, str) and method == 'substring' and isinstance(dst, int):
owner._last_substring_vid = int(dst)
except Exception:
pass
except Exception:
pass
elif op == "externcall":
func_name = inst.get("func")
args = inst.get("args", [])
dst = inst.get("dst")
lower_externcall(builder, owner.module, func_name, args, dst,
vmap_ctx, owner.resolver, owner.preds, owner.block_end_values, owner.bb_map, getattr(owner, 'ctx', None))
elif op == "newbox":
box_type = inst.get("type")
args = inst.get("args", [])
dst = inst.get("dst")
lower_newbox(builder, owner.module, box_type, args, dst,
vmap_ctx, owner.resolver, getattr(owner, 'ctx', None))
elif op == "typeop":
operation = inst.get("operation")
src = inst.get("src")
dst = inst.get("dst")
target_type = inst.get("target_type")
lower_typeop(builder, operation, src, dst, target_type,
vmap_ctx, owner.resolver, owner.preds, owner.block_end_values, owner.bb_map, getattr(owner, 'ctx', None))
elif op == "safepoint":
live = inst.get("live", [])
lower_safepoint(builder, owner.module, live, 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 == "barrier":
barrier_type = inst.get("type", "memory")
lower_barrier(builder, barrier_type, ctx=getattr(owner, 'ctx', None))
elif op == "while":
# Experimental LoopForm lowering inside a block
cond = inst.get("cond")
body = inst.get("body", [])
owner.loop_count += 1
if not lower_while_loopform(builder, func, cond, body,
owner.loop_count, owner.vmap, owner.bb_map,
owner.resolver, owner.preds, owner.block_end_values,
getattr(owner, 'ctx', None)):
# Fallback to regular while (structured)
try:
owner.resolver._owner_lower_instruction = owner.lower_instruction
except Exception:
pass
lower_while_regular(builder, func, cond, body,
owner.loop_count, owner.vmap, owner.bb_map,
owner.resolver, owner.preds, owner.block_end_values)
else:
trace_debug(f"[Python LLVM] Unknown instruction: {op}")
# Record per-inst definition for lifetime hinting as soon as available
try:
dst_maybe = inst.get("dst")
if isinstance(dst_maybe, int) and dst_maybe in owner.vmap:
cur_bid = None
try:
cur_bid = int(str(builder.block.name).replace('bb',''))
except Exception:
pass
if cur_bid is not None:
owner.def_blocks.setdefault(dst_maybe, set()).add(cur_bid)
except Exception:
pass

View File

@ -184,16 +184,15 @@ class NyashLLVMBuilder:
def lower_function(self, func_data: Dict[str, Any]):
"""Lower a single MIR function to LLVM IR"""
# Optional: delegate to external helper when gated (incremental split)
# Prefer delegated helper (incremental split); fall back on failure
try:
if os.environ.get('NYASH_LLVM_USE_HELPER_LOWER') == '1':
try:
from builders.function_lower import lower_function as _lower
return _lower(self, func_data)
except Exception as _e:
trace_debug(f"[Python LLVM] helper lower_function failed, falling back: {_e}")
except Exception:
pass
from builders.function_lower import lower_function as _lower
return _lower(self, func_data)
except Exception as _e:
try:
trace_debug(f"[Python LLVM] helper lower_function failed, falling back: {_e}")
except Exception:
pass
name = func_data.get("name", "unknown")
self.current_function_name = name
import re
@ -853,156 +852,8 @@ class NyashLLVMBuilder:
pass
def lower_instruction(self, builder: ir.IRBuilder, inst: Dict[str, Any], func: ir.Function):
"""Dispatch instruction to appropriate handler"""
op = inst.get("op")
# Pick current vmap context
vmap_ctx = getattr(self, '_current_vmap', self.vmap)
if op == "const":
dst = inst.get("dst")
value = inst.get("value")
lower_const(builder, self.module, dst, value, vmap_ctx, self.resolver)
elif op == "binop":
operation = inst.get("operation")
lhs = inst.get("lhs")
rhs = inst.get("rhs")
dst = inst.get("dst")
dst_type = inst.get("dst_type")
lower_binop(builder, self.resolver, operation, lhs, rhs, dst,
vmap_ctx, builder.block, self.preds, self.block_end_values, self.bb_map,
dst_type=dst_type)
elif op == "jump":
target = inst.get("target")
lower_jump(builder, target, self.bb_map)
elif op == "copy":
dst = inst.get("dst")
src = inst.get("src")
lower_copy(builder, dst, src, vmap_ctx, self.resolver, builder.block, self.preds, self.block_end_values, self.bb_map, getattr(self, 'ctx', None))
elif op == "branch":
cond = inst.get("cond")
then_bid = inst.get("then")
else_bid = inst.get("else")
lower_branch(builder, cond, then_bid, else_bid, vmap_ctx, self.bb_map, self.resolver, self.preds, self.block_end_values)
elif op == "ret":
value = inst.get("value")
lower_return(builder, value, vmap_ctx, func.function_type.return_type,
self.resolver, self.preds, self.block_end_values, self.bb_map, getattr(self, 'ctx', None))
elif op == "phi":
# No-op here: PHIはメタのみresolverがondemand生成
return
elif op == "compare":
# Dedicated compare op
operation = inst.get("operation") or inst.get("op")
lhs = inst.get("lhs")
rhs = inst.get("rhs")
dst = inst.get("dst")
cmp_kind = inst.get("cmp_kind")
lower_compare(builder, operation, lhs, rhs, dst, vmap_ctx,
self.resolver, builder.block, self.preds, self.block_end_values, self.bb_map,
meta={"cmp_kind": cmp_kind} if cmp_kind else None,
ctx=getattr(self, 'ctx', None))
elif op == "call":
func_name = inst.get("func")
args = inst.get("args", [])
dst = inst.get("dst")
lower_call(builder, self.module, func_name, args, dst, vmap_ctx, self.resolver, self.preds, self.block_end_values, self.bb_map, getattr(self, 'ctx', None))
elif op == "boxcall":
box_vid = inst.get("box")
method = inst.get("method")
args = inst.get("args", [])
dst = inst.get("dst")
lower_boxcall(builder, self.module, box_vid, method, args, dst,
vmap_ctx, self.resolver, self.preds, self.block_end_values, self.bb_map, getattr(self, 'ctx', None))
# Optional: honor explicit dst_type for tagging (string handle)
try:
dst_type = inst.get("dst_type")
if dst is not None and isinstance(dst_type, dict):
if dst_type.get("kind") == "handle" and dst_type.get("box_type") == "StringBox":
if hasattr(self.resolver, 'mark_string'):
self.resolver.mark_string(int(dst))
# Track last substring for optional esc_json fallback
try:
if isinstance(method, str) and method == 'substring' and isinstance(dst, int):
self._last_substring_vid = int(dst)
except Exception:
pass
except Exception:
pass
elif op == "externcall":
func_name = inst.get("func")
args = inst.get("args", [])
dst = inst.get("dst")
lower_externcall(builder, self.module, func_name, args, dst,
vmap_ctx, self.resolver, self.preds, self.block_end_values, self.bb_map, getattr(self, 'ctx', None))
elif op == "newbox":
box_type = inst.get("type")
args = inst.get("args", [])
dst = inst.get("dst")
lower_newbox(builder, self.module, box_type, args, dst,
vmap_ctx, self.resolver, getattr(self, 'ctx', None))
elif op == "typeop":
operation = inst.get("operation")
src = inst.get("src")
dst = inst.get("dst")
target_type = inst.get("target_type")
lower_typeop(builder, operation, src, dst, target_type,
vmap_ctx, self.resolver, self.preds, self.block_end_values, self.bb_map, getattr(self, 'ctx', None))
elif op == "safepoint":
live = inst.get("live", [])
lower_safepoint(builder, self.module, live, vmap_ctx,
resolver=self.resolver, preds=self.preds,
block_end_values=self.block_end_values, bb_map=self.bb_map,
ctx=getattr(self, 'ctx', None))
elif op == "barrier":
barrier_type = inst.get("type", "memory")
lower_barrier(builder, barrier_type, ctx=getattr(self, 'ctx', None))
elif op == "while":
# Experimental LoopForm lowering
cond = inst.get("cond")
body = inst.get("body", [])
self.loop_count += 1
if not lower_while_loopform(builder, func, cond, body,
self.loop_count, self.vmap, self.bb_map,
self.resolver, self.preds, self.block_end_values,
getattr(self, 'ctx', None)):
# Fallback to regular while (structured)
try:
self.resolver._owner_lower_instruction = self.lower_instruction
except Exception:
pass
lower_while_regular(builder, func, cond, body,
self.loop_count, self.vmap, self.bb_map,
self.resolver, self.preds, self.block_end_values)
else:
trace_debug(f"[Python LLVM] Unknown instruction: {op}")
# Record per-inst definition for lifetime hinting as soon as available
try:
dst_maybe = inst.get("dst")
if isinstance(dst_maybe, int) and dst_maybe in self.vmap:
cur_bid = None
try:
cur_bid = int(str(builder.block.name).replace('bb',''))
except Exception:
pass
if cur_bid is not None:
self.def_blocks.setdefault(dst_maybe, set()).add(cur_bid)
except Exception:
pass
from builders.instruction_lower import lower_instruction as _li
return _li(self, builder, inst, func)
# NOTE: regular while lowering is implemented in
# instructions/controlflow/while_.py::lower_while_regular and invoked