refactor(llvm): Complete Resolver pattern implementation across all instructions

Major structural improvement driven by ChatGPT 5 Pro analysis:
- Replace all direct vmap access with Resolver API calls
- Add proper cursor/bb_map/preds/block_end_values to all instruction handlers
- Ensure dominance safety by localizing values through Resolver
- Fix parameter passing in invoke/fields/extern handlers

Key changes:
- boxcall: Use resolver.resolve_i64/ptr instead of direct vmap access
- strings: Remove unused recv_v parameter, use Resolver throughout
- invoke: Add missing context parameters for proper PHI handling
- fields: Add resolver and block context parameters
- flow/arith/maps: Consistent Resolver usage pattern

This addresses the "structural invariant" requirements:
1. All value fetching goes through Resolver (no direct vmap.get)
2. Localization happens at BB boundaries via Resolver
3. Better preparation for PHI-only-in-dispatch pattern

Next: Consider boxing excessive parameters (15+ args in some functions)

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Selfhosting Dev
2025-09-12 22:36:20 +09:00
parent f77bbb5878
commit 8b48480844
16 changed files with 516 additions and 503 deletions

View File

@ -7,45 +7,54 @@ use crate::mir::{function::MirFunction, BasicBlockId, ValueId};
use super::super::types::{to_bool, map_mirtype_to_basic};
use super::builder_cursor::BuilderCursor;
use super::Resolver;
pub(in super::super) fn emit_return<'ctx, 'b>(
codegen: &CodegenContext<'ctx>,
cursor: &mut BuilderCursor<'ctx, 'b>,
_bid: BasicBlockId,
resolver: &mut Resolver<'ctx>,
cur_bid: BasicBlockId,
func: &MirFunction,
vmap: &HashMap<ValueId, BasicValueEnum<'ctx>>,
value: &Option<ValueId>,
bb_map: &HashMap<BasicBlockId, BasicBlock<'ctx>>,
preds: &HashMap<BasicBlockId, Vec<BasicBlockId>>,
block_end_values: &HashMap<BasicBlockId, HashMap<ValueId, BasicValueEnum<'ctx>>>,
) -> Result<(), String> {
match (&func.signature.return_type, value) {
(crate::mir::MirType::Void, _) => {
cursor.emit_term(_bid, |b| { b.build_return(None).unwrap(); });
cursor.emit_term(cur_bid, |b| { b.build_return(None).unwrap(); });
Ok(())
}
(_t, Some(vid)) => {
// Resolve return value according to expected type
let expected = map_mirtype_to_basic(codegen.context, &func.signature.return_type);
use inkwell::types::BasicTypeEnum as BT;
let v_adj = match expected {
BT::IntType(_it) => {
// For now, fallback to vmap; resolver threading requires signature change
*vmap.get(vid).ok_or("ret value missing")?
let v_adj: BasicValueEnum<'ctx> = match expected {
BT::IntType(it) => {
let iv = resolver.resolve_i64(codegen, cursor, cur_bid, *vid, bb_map, preds, block_end_values, vmap)?;
// Cast to expected width
let bw_src = iv.get_type().get_bit_width();
let bw_dst = it.get_bit_width();
if bw_src == bw_dst { iv.into() }
else if bw_src < bw_dst { cursor.emit_instr(cur_bid, |b| b.build_int_z_extend(iv, it, "ret_zext")).map_err(|e| e.to_string())?.into() }
else if bw_dst == 1 { to_bool(codegen.context, iv.into(), &codegen.builder)?.into() }
else { cursor.emit_instr(cur_bid, |b| b.build_int_truncate(iv, it, "ret_trunc")).map_err(|e| e.to_string())?.into() }
}
BT::PointerType(pt) => {
if let Some(BasicValueEnum::IntValue(iv)) = vmap.get(vid).copied() {
cursor
.emit_instr(_bid, |b| b.build_int_to_ptr(iv, pt, "ret_i2p"))
.map_err(|e| e.to_string())?
.into()
} else {
*vmap.get(vid).ok_or("ret value missing")?
}
let pv = resolver.resolve_ptr(codegen, cursor, cur_bid, *vid, bb_map, preds, block_end_values, vmap)?;
// If expected pointer type differs (e.g., typed ptr vs i8*), bitcast
if pv.get_type() == pt { pv.into() }
else { codegen.builder.build_pointer_cast(pv, pt, "ret_bitcast").map_err(|e| e.to_string())?.into() }
}
BT::FloatType(_ft) => {
*vmap.get(vid).ok_or("ret value missing")?
BT::FloatType(ft) => {
let fv = resolver.resolve_f64(codegen, cursor, cur_bid, *vid, bb_map, preds, block_end_values, vmap)?;
if fv.get_type() == ft { fv.into() }
else { cursor.emit_instr(cur_bid, |b| b.build_float_cast(fv, ft, "ret_fcast")).map_err(|e| e.to_string())?.into() }
}
_ => *vmap.get(vid).ok_or("ret value missing")?,
_ => return Err("unsupported return basic type".to_string()),
};
cursor.emit_term(_bid, |b| {
cursor.emit_term(cur_bid, |b| {
b.build_return(Some(&v_adj)).map_err(|e| e.to_string()).unwrap();
});
Ok(())
@ -94,16 +103,13 @@ pub(in super::super) fn emit_branch<'ctx, 'b>(
preds: &HashMap<BasicBlockId, Vec<BasicBlockId>>,
block_end_values: &HashMap<BasicBlockId, HashMap<ValueId, BasicValueEnum<'ctx>>>,
) -> Result<(), String> {
// Localize condition as i64 and convert to i1 via != 0
let cond_v = *vmap.get(condition).ok_or("cond missing")?;
let b = match cond_v {
BasicValueEnum::IntValue(_) | BasicValueEnum::PointerValue(_) | BasicValueEnum::FloatValue(_) => {
let ci = resolver.resolve_i64(codegen, cursor, bid, *condition, bb_map, preds, block_end_values, vmap)?;
let zero = codegen.context.i64_type().const_zero();
codegen.builder.build_int_compare(inkwell::IntPredicate::NE, ci, zero, "cond_nez").map_err(|e| e.to_string())?
}
_ => to_bool(codegen.context, cond_v, &codegen.builder)?,
};
// Localize condition as i64 and convert to i1 via != 0Resolver 経由のみ)
let ci = resolver.resolve_i64(codegen, cursor, bid, *condition, bb_map, preds, block_end_values, vmap)?;
let zero = codegen.context.i64_type().const_zero();
let b = codegen
.builder
.build_int_compare(inkwell::IntPredicate::NE, ci, zero, "cond_nez")
.map_err(|e| e.to_string())?;
// Non-sealed incoming wiring removed: rely on sealed snapshots and resolver-driven PHIs.
let tbb = *bb_map.get(then_bb).ok_or("then bb missing")?;
let ebb = *bb_map.get(else_bb).ok_or("else bb missing")?;
@ -196,38 +202,24 @@ pub(in super::super) fn seal_block<'ctx, 'b>(
for (_dst, phi, inputs) in pl {
// Handle only the current predecessor (bid)
if let Some((_, in_vid)) = inputs.iter().find(|(p, _)| p == &bid) {
// Prefer the predecessor's block-end snapshot; fall back to current vmap
// Prefer the predecessor block-end snapshot。なければ型ゼロを合成
let snap_opt = block_end_values
.get(&bid)
.and_then(|m| m.get(in_vid).copied());
let mut val = if let Some(sv) = snap_opt {
sv
} else {
// Trust vmap only when the value is a function parameter (dominates all paths)
if func.params.contains(in_vid) {
vmap.get(in_vid).copied().unwrap_or_else(|| {
let bt = phi.as_basic_value().get_type();
use inkwell::types::BasicTypeEnum as BT;
match bt {
BT::IntType(it) => it.const_zero().into(),
BT::FloatType(ft) => ft.const_zero().into(),
BT::PointerType(pt) => pt.const_zero().into(),
_ => unreachable!(),
}
})
} else {
// Synthesize zero to avoid dominance violations
let bt = phi.as_basic_value().get_type();
use inkwell::types::BasicTypeEnum as BT;
match bt {
BT::IntType(it) => it.const_zero().into(),
BT::FloatType(ft) => ft.const_zero().into(),
BT::PointerType(pt) => pt.const_zero().into(),
_ => return Err(format!(
"phi incoming (seal) missing: pred={} succ_bb={} in_vid={} (no snapshot)",
bid.as_u32(), sb.as_u32(), in_vid.as_u32()
)),
}
// Synthesize zero to avoid dominance violationsvmap には依存しない)
let bt = phi.as_basic_value().get_type();
use inkwell::types::BasicTypeEnum as BT;
match bt {
BT::IntType(it) => it.const_zero().into(),
BT::FloatType(ft) => ft.const_zero().into(),
BT::PointerType(pt) => pt.const_zero().into(),
_ => return Err(format!(
"phi incoming (seal) missing: pred={} succ_bb={} in_vid={} (no snapshot)",
bid.as_u32(), sb.as_u32(), in_vid.as_u32()
)),
}
};
// Insert any required casts in the predecessor block, right before its terminator
@ -251,7 +243,7 @@ pub(in super::super) fn seal_block<'ctx, 'b>(
bid.as_u32(),
in_vid.as_u32(),
tys,
if snap_opt.is_some() { " (snapshot)" } else { " (vmap)" }
if snap_opt.is_some() { " (snapshot)" } else { " (synth)" }
);
}
match val {
@ -323,17 +315,6 @@ pub(in super::super) fn finalize_phis<'ctx, 'b>(
let snap_opt = block_end_values.get(pred).and_then(|m| m.get(in_vid).copied());
let mut val = if let Some(sv) = snap_opt {
sv
} else if func.params.contains(in_vid) {
vmap.get(in_vid).copied().unwrap_or_else(|| {
let bt = phi.as_basic_value().get_type();
use inkwell::types::BasicTypeEnum as BT;
match bt {
BT::IntType(it) => it.const_zero().into(),
BT::FloatType(ft) => ft.const_zero().into(),
BT::PointerType(pt) => pt.const_zero().into(),
_ => unreachable!(),
}
})
} else {
let bt = phi.as_basic_value().get_type();
use inkwell::types::BasicTypeEnum as BT;
@ -414,21 +395,13 @@ pub(in super::super) fn localize_to_i64<'ctx, 'b>(
vmap: &std::collections::HashMap<ValueId, BasicValueEnum<'ctx>>,
) -> Result<IntValue<'ctx>, String> {
let i64t = codegen.context.i64_type();
// Note: avoid using current vmap directly here, as it may hold values
// defined in non-dominating predecessors. We rely on predecessor snapshots
// in sealed SSA mode to maintain dominance.
let cur_llbb = *bb_map.get(&cur_bid).ok_or("cur bb missing")?;
// If no predecessors, fallback to current vmap or zero
// If no predecessors, conservatively return zerovmap には依存しない)
let pred_list = preds.get(&cur_bid).cloned().unwrap_or_default();
if pred_list.is_empty() {
if let Some(v) = vmap.get(&vid).copied() {
return Ok(match v {
BasicValueEnum::IntValue(iv) => {
if iv.get_type() == i64t { iv }
else { codegen.builder.build_int_z_extend(iv, i64t, "loc_zext").map_err(|e| e.to_string())? }
}
BasicValueEnum::PointerValue(pv) => codegen.builder.build_ptr_to_int(pv, i64t, "loc_p2i").map_err(|e| e.to_string())?,
BasicValueEnum::FloatValue(fv) => codegen.builder.build_float_to_signed_int(fv, i64t, "loc_f2i").map_err(|e| e.to_string())?,
_ => i64t.const_zero(),
});
}
return Ok(i64t.const_zero());
}
// Build PHI at the top of current block (before any non-PHI), then restore insertion point
@ -442,25 +415,25 @@ pub(in super::super) fn localize_to_i64<'ctx, 'b>(
for p in &pred_list {
let pred_bb = *bb_map.get(p).ok_or("pred bb missing")?;
// Fetch snapshot at end of pred; if missing, synthesize zero
let mut val = block_end_values
let base = block_end_values
.get(p)
.and_then(|m| m.get(&vid).copied())
.unwrap_or_else(|| i64t.const_zero().into());
// Coerce to i64
use inkwell::types::BasicTypeEnum as BT;
val = match val {
BasicValueEnum::IntValue(iv) => {
if iv.get_type() == i64t { iv.into() }
else { codegen.builder.build_int_z_extend(iv, i64t, "loc_zext_p").map_err(|e| e.to_string())?.into() }
}
BasicValueEnum::PointerValue(pv) => codegen.builder.build_ptr_to_int(pv, i64t, "loc_p2i_p").map_err(|e| e.to_string())?.into(),
BasicValueEnum::FloatValue(fv) => codegen.builder.build_float_to_signed_int(fv, i64t, "loc_f2i_p").map_err(|e| e.to_string())?.into(),
_ => i64t.const_zero().into(),
};
match val {
BasicValueEnum::IntValue(iv) => phi.add_incoming(&[(&iv, pred_bb)]),
_ => unreachable!(),
}
// Insert required casts in the predecessor block before its terminator
let mut iv_out = i64t.const_zero();
cursor.with_block(*p, pred_bb, |c| {
let term = unsafe { pred_bb.get_terminator() };
if let Some(t) = term { codegen.builder.position_before(&t); } else { c.position_at_end(pred_bb); }
iv_out = match base {
BasicValueEnum::IntValue(iv) => {
if iv.get_type() == i64t { iv } else { codegen.builder.build_int_z_extend(iv, i64t, "loc_zext_p").map_err(|e| e.to_string()).unwrap() }
}
BasicValueEnum::PointerValue(pv) => codegen.builder.build_ptr_to_int(pv, i64t, "loc_p2i_p").map_err(|e| e.to_string()).unwrap(),
BasicValueEnum::FloatValue(fv) => codegen.builder.build_float_to_signed_int(fv, i64t, "loc_f2i_p").map_err(|e| e.to_string()).unwrap(),
_ => i64t.const_zero(),
};
});
phi.add_incoming(&[(&iv_out, pred_bb)]);
}
// Restore insertion point
if let Some(bb) = saved_ip { codegen.builder.position_at_end(bb); }