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:
@ -62,6 +62,22 @@ Scaffold — 2025‑09‑12 (Resolver i64 minimal)
|
||||
- Added src/backend/llvm/compiler/codegen/instructions/resolver.rs with `Resolver::resolve_i64(...)` and per-block cache.
|
||||
- docs/RESOLVER_API.md documents goals/usage; wiring to replace `localize_to_i64` callsites comes next.
|
||||
|
||||
Hot Update — 2025‑09‑12 (Structural Invariants v1)
|
||||
- Core invariants adopted to eliminate dominance violations structurally:
|
||||
- Resolver-only value access: forbid direct `vmap.get(...)` in lowering; always use `Resolver::{resolve_i64, resolve_ptr, resolve_f64}`.
|
||||
- Localization discipline: PHIs are created at BB head; casts/incoming coercions are inserted at predecessor end via `BuilderCursor::with_block` (never inside PHI site).
|
||||
- Strings rule: across blocks keep StrHandle(i64) only; convert to StrPtr(i8*) at call sites inside the same BB. Return values of string ops are stored as i64 handles.
|
||||
- LoopForm rule: preheader is mandatory; header condition resolved via Resolver; dispatch-only PHI; optional latch→header normalization is gated.
|
||||
- BuilderCursor guard: deny post‑terminator insertion; avoid raw builder calls from lowering sites.
|
||||
- Acceptance A2.5 (added): sealed=ON (default) with dominator violations = 0 on `apps/selfhost/tools/dep_tree_min_string.nyash`.
|
||||
|
||||
Next Plan(精密タスク v2)
|
||||
- Deny-Direct purge: eliminate remaining `vmap.get(...)` in lowering modules(maps.rs, arith.rs, arith_ops.rs, externcall/env.rs, boxcall/{invoke,marshal,fields}.rs, mem.rs ほか)。
|
||||
- Enforce string handle invariant end-to-end(helpers/Resolverに軽量StrHandle/StrPtr型or helperを追加、戻りは常にi64、呼び出し直前だけinttoptr)。
|
||||
- L-Dispatch-PHI checker(dev): dispatch以外のPHI検出でpanic/ログ。preheader必須の検証も追加。
|
||||
- Seal配線の堅牢化: snapshot優先・pred末端cast挿入の徹底、フォールバックゼロ合成の縮小(非param値のvmapフォールバックを禁止)。
|
||||
- Regression: `dep_tree_min_string` オブジェクト生成→ LLVM verifier green(支配違反ゼロ); Deny-Direct grep=0 をCIチェックに追加。
|
||||
|
||||
Docs — LLVM layer overview (2025‑09‑12)
|
||||
- Added docs/LLVM_LAYER_OVERVIEW.md and linked it with existing docs:
|
||||
- docs/LOWERING_LLVM.md — concrete lowering rules and RT calls
|
||||
@ -218,6 +234,35 @@ Hot Update — 2025‑09‑12 (Resolver 適用拡大 + sealed 既定ON)
|
||||
- 非sealed配線の削除: emit_jump/emit_branch 内の直接incoming追加を撤去。sealedスナップショット+Resolverの需要駆動で一本化。
|
||||
- 既定: `NYASH_LLVM_PHI_SEALED` 未設定=ON(`0` のみOFF)。
|
||||
|
||||
Status — VMap Purge (Phase 1 done)
|
||||
- Done (Resolver化済み):
|
||||
- flow: branch 条件(i64→i1)
|
||||
- strings: substring/concat(si|is)、lastIndexOf の needle
|
||||
- arith_ops: 整数演算の左右オペランド(i64 正規化)
|
||||
- compare: 整数比較の左右(i64 正規化)
|
||||
- externcall: console(log/warn/error/trace) は handle 経路に統一(resolve_i64)。future.spawn_instance 名は resolve_ptr。
|
||||
- env.box.new_i64: int/ptr 引数は resolve_i64(f64→from_f64)
|
||||
- arrays/maps: index/key/value の整数/ptr は resolve_i64
|
||||
- call: 実引数を callee 期待型へ(int→resolve_i64、ptr→i64→i2p、float→resolve_f64)
|
||||
- mem.store: 値を resolve_i64/resolve_f64/resolve_ptr の順で解決
|
||||
- boxcall: recv を i64/ptr 両形で取得、direct call 引数を Resolver 経由に統一
|
||||
|
||||
- Remaining (vmap.get 参照の置換ターゲット):
|
||||
- flow.emit_return: 戻り値の型に応じて resolve_* に統一(シグネチャ拡張)
|
||||
- loopform.lower_while_loopform: 条件の vmap 直参照→ resolve_i64(シグネチャ拡張)
|
||||
- strings.try_handle_string_method: 一部 vmap 残存箇所の整理(recv は boxcall 側で ptr 統一済み)
|
||||
- extern.env: env.box.new の型名(arg0)や一部名引数を resolve_ptr に統一
|
||||
- arith/arith_ops/compare: 局所の vmap 判定/ゼロフォールバックの縮小(Resolver 経由へ)
|
||||
- snapshot 用の vmap アクセス(compile_module 内のスナップショット作成)は維持(仕様上の例外)
|
||||
|
||||
Plan — Next (precise)
|
||||
1) flow.emit_return を Resolver 化(resolve_i64/resolve_f64/inttoptr へ)。
|
||||
2) loopform.lower_while_loopform に Resolver/CFG を渡し、条件解決を resolve_i64 に統一。
|
||||
3) strings の vmap 残を metadata + Resolver へ置換(concat rhs 判別の簡素化)。
|
||||
4) extern.env.* 名引数を resolve_ptr 化(local.get / box.new 名など)。
|
||||
5) marshal/fields の vmap 読みを Resolver/型注釈へ段階置換(最小に縮退させる)。
|
||||
6) LoopForm: preheader 既定化 + 最小 LoopState(tag+i64)導入→ dispatch-only PHI 完了(ゲート)。
|
||||
|
||||
Smoke(sealed=ON, dep_tree_min_string)所見
|
||||
- 進展: PHI 欠落は再現せず、sealed での incoming 配線は安定
|
||||
- 依然NG: Main.node_json/3 で dominator 違反(Instruction does not dominate all uses!)
|
||||
|
||||
@ -22,21 +22,16 @@ pub(in super::super) fn lower_compare<'ctx, 'b>(
|
||||
block_end_values: &std::collections::HashMap<crate::mir::BasicBlockId, std::collections::HashMap<ValueId, BasicValueEnum<'ctx>>>,
|
||||
) -> Result<BasicValueEnum<'ctx>, String> {
|
||||
use crate::backend::llvm::compiler::helpers::{as_float, as_int};
|
||||
let lv = if let Some(v) = vmap.get(lhs).copied() {
|
||||
v
|
||||
} else {
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[cmp] lhs missing: {} (fallback zero)", lhs.as_u32());
|
||||
}
|
||||
guessed_zero(codegen, func, lhs)
|
||||
// Synthesize proxy values via Resolver according to metadata
|
||||
let lv: BasicValueEnum<'ctx> = match func.metadata.value_types.get(lhs) {
|
||||
Some(crate::mir::MirType::Float) => resolver.resolve_f64(codegen, cursor, cur_bid, *lhs, bb_map, preds, block_end_values, vmap)?.into(),
|
||||
Some(crate::mir::MirType::String) | Some(crate::mir::MirType::Box(_)) => resolver.resolve_ptr(codegen, cursor, cur_bid, *lhs, bb_map, preds, block_end_values, vmap)?.into(),
|
||||
_ => resolver.resolve_i64(codegen, cursor, cur_bid, *lhs, bb_map, preds, block_end_values, vmap)?.into(),
|
||||
};
|
||||
let rv = if let Some(v) = vmap.get(rhs).copied() {
|
||||
v
|
||||
} else {
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[cmp] rhs missing: {} (fallback zero)", rhs.as_u32());
|
||||
}
|
||||
guessed_zero(codegen, func, rhs)
|
||||
let rv: BasicValueEnum<'ctx> = match func.metadata.value_types.get(rhs) {
|
||||
Some(crate::mir::MirType::Float) => resolver.resolve_f64(codegen, cursor, cur_bid, *rhs, bb_map, preds, block_end_values, vmap)?.into(),
|
||||
Some(crate::mir::MirType::String) | Some(crate::mir::MirType::Box(_)) => resolver.resolve_ptr(codegen, cursor, cur_bid, *rhs, bb_map, preds, block_end_values, vmap)?.into(),
|
||||
_ => resolver.resolve_i64(codegen, cursor, cur_bid, *rhs, bb_map, preds, block_end_values, vmap)?.into(),
|
||||
};
|
||||
// String equality/inequality by content when annotated as String/StringBox
|
||||
if matches!(op, CompareOp::Eq | CompareOp::Ne) {
|
||||
|
||||
@ -11,42 +11,34 @@ use super::builder_cursor::BuilderCursor;
|
||||
pub(in super::super) fn lower_unary<'ctx, 'b>(
|
||||
codegen: &CodegenContext<'ctx>,
|
||||
cursor: &mut BuilderCursor<'ctx, 'b>,
|
||||
resolver: &mut super::Resolver<'ctx>,
|
||||
cur_bid: BasicBlockId,
|
||||
func: &MirFunction,
|
||||
vmap: &mut HashMap<ValueId, BasicValueEnum<'ctx>>,
|
||||
dst: ValueId,
|
||||
op: &UnaryOp,
|
||||
operand: &ValueId,
|
||||
bb_map: &std::collections::HashMap<crate::mir::BasicBlockId, inkwell::basic_block::BasicBlock<'ctx>>,
|
||||
preds: &std::collections::HashMap<crate::mir::BasicBlockId, Vec<crate::mir::BasicBlockId>>,
|
||||
block_end_values: &std::collections::HashMap<crate::mir::BasicBlockId, std::collections::HashMap<ValueId, BasicValueEnum<'ctx>>>,
|
||||
) -> Result<(), String> {
|
||||
use crate::backend::llvm::compiler::helpers::{as_float, as_int};
|
||||
let v = *vmap.get(operand).ok_or("operand missing")?;
|
||||
use crate::mir::MirType as MT;
|
||||
let out = match op {
|
||||
UnaryOp::Neg => {
|
||||
if let Some(iv) = as_int(v) {
|
||||
cursor
|
||||
.emit_instr(cur_bid, |b| b
|
||||
.build_int_neg(iv, "ineg"))
|
||||
.map_err(|e| e.to_string())?
|
||||
.into()
|
||||
} else if let Some(fv) = as_float(v) {
|
||||
cursor
|
||||
.emit_instr(cur_bid, |b| b
|
||||
.build_float_neg(fv, "fneg"))
|
||||
.map_err(|e| e.to_string())?
|
||||
.into()
|
||||
} else {
|
||||
return Err("neg on non-number".to_string());
|
||||
match func.metadata.value_types.get(operand) {
|
||||
Some(MT::Float) => {
|
||||
let fv = resolver.resolve_f64(codegen, cursor, cur_bid, *operand, bb_map, preds, block_end_values, vmap)?;
|
||||
cursor.emit_instr(cur_bid, |b| b.build_float_neg(fv, "fneg")).map_err(|e| e.to_string())?.into()
|
||||
}
|
||||
_ => {
|
||||
let iv = resolver.resolve_i64(codegen, cursor, cur_bid, *operand, bb_map, preds, block_end_values, vmap)?;
|
||||
cursor.emit_instr(cur_bid, |b| b.build_int_neg(iv, "ineg")).map_err(|e| e.to_string())?.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
UnaryOp::Not | UnaryOp::BitNot => {
|
||||
if let Some(iv) = as_int(v) {
|
||||
cursor
|
||||
.emit_instr(cur_bid, |b| b
|
||||
.build_not(iv, "inot"))
|
||||
.map_err(|e| e.to_string())?
|
||||
.into()
|
||||
} else {
|
||||
return Err("not on non-int".to_string());
|
||||
}
|
||||
let iv = resolver.resolve_i64(codegen, cursor, cur_bid, *operand, bb_map, preds, block_end_values, vmap)?;
|
||||
cursor.emit_instr(cur_bid, |b| b.build_not(iv, "inot")).map_err(|e| e.to_string())?.into()
|
||||
}
|
||||
};
|
||||
vmap.insert(dst, out);
|
||||
@ -72,21 +64,16 @@ pub(in super::super) fn lower_binop<'ctx, 'b>(
|
||||
use crate::backend::llvm::compiler::helpers::{as_float, as_int};
|
||||
use inkwell::values::BasicValueEnum as BVE;
|
||||
use inkwell::IntPredicate;
|
||||
let lv = if let Some(v) = vmap.get(lhs).copied() {
|
||||
v
|
||||
} else {
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[binop] lhs missing: {} (fallback zero)", lhs.as_u32());
|
||||
}
|
||||
guessed_zero(codegen, func, lhs)
|
||||
// Construct lhs/rhs proxy values via Resolver according to metadata (no vmap direct access)
|
||||
let lv: BasicValueEnum<'ctx> = match func.metadata.value_types.get(lhs) {
|
||||
Some(crate::mir::MirType::Float) => resolver.resolve_f64(codegen, cursor, cur_bid, *lhs, bb_map, preds, block_end_values, vmap)?.into(),
|
||||
Some(crate::mir::MirType::String) | Some(crate::mir::MirType::Box(_)) => resolver.resolve_ptr(codegen, cursor, cur_bid, *lhs, bb_map, preds, block_end_values, vmap)?.into(),
|
||||
_ => resolver.resolve_i64(codegen, cursor, cur_bid, *lhs, bb_map, preds, block_end_values, vmap)?.into(),
|
||||
};
|
||||
let rv = if let Some(v) = vmap.get(rhs).copied() {
|
||||
v
|
||||
} else {
|
||||
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[binop] rhs missing: {} (fallback zero)", rhs.as_u32());
|
||||
}
|
||||
guessed_zero(codegen, func, rhs)
|
||||
let rv: BasicValueEnum<'ctx> = match func.metadata.value_types.get(rhs) {
|
||||
Some(crate::mir::MirType::Float) => resolver.resolve_f64(codegen, cursor, cur_bid, *rhs, bb_map, preds, block_end_values, vmap)?.into(),
|
||||
Some(crate::mir::MirType::String) | Some(crate::mir::MirType::Box(_)) => resolver.resolve_ptr(codegen, cursor, cur_bid, *rhs, bb_map, preds, block_end_values, vmap)?.into(),
|
||||
_ => resolver.resolve_i64(codegen, cursor, cur_bid, *rhs, bb_map, preds, block_end_values, vmap)?.into(),
|
||||
};
|
||||
let mut handled_concat = false;
|
||||
if let BinaryOp::Add = op {
|
||||
@ -113,7 +100,12 @@ pub(in super::super) fn lower_binop<'ctx, 'b>(
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("concat_ss returned void".to_string())?;
|
||||
vmap.insert(dst, rv);
|
||||
// store as handle (i64) across blocks
|
||||
let i64t = codegen.context.i64_type();
|
||||
let h = cursor
|
||||
.emit_instr(cur_bid, |b| b.build_ptr_to_int(rv.into_pointer_value(), i64t, "str_ptr2i"))
|
||||
.map_err(|e| e.to_string())?;
|
||||
vmap.insert(dst, h.into());
|
||||
handled_concat = true;
|
||||
}
|
||||
(BVE::PointerValue(lp), BVE::IntValue(ri)) => {
|
||||
@ -163,7 +155,11 @@ pub(in super::super) fn lower_binop<'ctx, 'b>(
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("concat_si returned void".to_string())?;
|
||||
vmap.insert(dst, rv);
|
||||
let i64t = codegen.context.i64_type();
|
||||
let h = cursor
|
||||
.emit_instr(cur_bid, |b| b.build_ptr_to_int(rv.into_pointer_value(), i64t, "str_ptr2i"))
|
||||
.map_err(|e| e.to_string())?;
|
||||
vmap.insert(dst, h.into());
|
||||
handled_concat = true;
|
||||
}
|
||||
}
|
||||
@ -214,7 +210,11 @@ pub(in super::super) fn lower_binop<'ctx, 'b>(
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("concat_is returned void".to_string())?;
|
||||
vmap.insert(dst, rv);
|
||||
let i64t = codegen.context.i64_type();
|
||||
let h = cursor
|
||||
.emit_instr(cur_bid, |b| b.build_ptr_to_int(rv.into_pointer_value(), i64t, "str_ptr2i"))
|
||||
.map_err(|e| e.to_string())?;
|
||||
vmap.insert(dst, h.into());
|
||||
handled_concat = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,21 +34,13 @@ pub(in super::super) fn lower_boxcall<'ctx, 'b>(
|
||||
use crate::backend::llvm::compiler::helpers::{as_float, as_int};
|
||||
use super::super::types::classify_tag;
|
||||
let i64t = codegen.context.i64_type();
|
||||
// Resolve receiver as handle and pointer (i8*)
|
||||
let pty = codegen.context.ptr_type(AddressSpace::from(0));
|
||||
let recv_h = cursor
|
||||
.emit_instr(cur_bid, |b| {
|
||||
// If vmap has pointer, use it; if int, use it; else zero
|
||||
match vmap.get(box_val).copied() {
|
||||
Some(BVE::PointerValue(pv)) => b.build_ptr_to_int(pv, i64t, "recv_p2i").map_err(|e| e.to_string()),
|
||||
Some(BVE::IntValue(iv)) => Ok(iv),
|
||||
_ => Ok(i64t.const_zero()),
|
||||
}
|
||||
})
|
||||
.map_err(|e| e.to_string())?;
|
||||
let recv_p = cursor
|
||||
.emit_instr(cur_bid, |b| b.build_int_to_ptr(recv_h, pty, "recv_i2p"))
|
||||
.map_err(|e| e.to_string())?;
|
||||
// Resolve receiver as handle and pointer (i8*) via Resolver to ensure dominance safety
|
||||
let recv_h = resolver.resolve_i64(
|
||||
codegen, cursor, cur_bid, *box_val, bb_map, preds, block_end_values, vmap,
|
||||
)?;
|
||||
let recv_p = resolver.resolve_ptr(
|
||||
codegen, cursor, cur_bid, *box_val, bb_map, preds, block_end_values, vmap,
|
||||
)?;
|
||||
let recv_v: BVE = recv_p.into();
|
||||
|
||||
// Resolve type_id
|
||||
@ -62,7 +54,7 @@ pub(in super::super) fn lower_boxcall<'ctx, 'b>(
|
||||
|
||||
// Delegate String methods
|
||||
if super::strings::try_handle_string_method(
|
||||
codegen, cursor, resolver, cur_bid, func, vmap, dst, box_val, method, args, recv_v,
|
||||
codegen, cursor, resolver, cur_bid, func, vmap, dst, box_val, method, args,
|
||||
bb_map, preds, block_end_values,
|
||||
)? {
|
||||
return Ok(());
|
||||
@ -98,7 +90,7 @@ pub(in super::super) fn lower_boxcall<'ctx, 'b>(
|
||||
}
|
||||
|
||||
// getField/setField
|
||||
if fields::try_handle_field_method(codegen, cursor, cur_bid, vmap, dst, method, args, recv_h)? {
|
||||
if fields::try_handle_field_method(codegen, cursor, cur_bid, vmap, dst, method, args, recv_h, resolver, bb_map, preds, block_end_values)? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@ -126,6 +118,8 @@ pub(in super::super) fn lower_boxcall<'ctx, 'b>(
|
||||
invoke::try_handle_tagged_invoke(
|
||||
codegen,
|
||||
func,
|
||||
cursor,
|
||||
resolver,
|
||||
vmap,
|
||||
dst,
|
||||
*mid,
|
||||
@ -133,6 +127,10 @@ pub(in super::super) fn lower_boxcall<'ctx, 'b>(
|
||||
recv_h,
|
||||
args,
|
||||
entry_builder,
|
||||
cur_bid,
|
||||
bb_map,
|
||||
preds,
|
||||
block_end_values,
|
||||
)?;
|
||||
return Ok(());
|
||||
} else {
|
||||
@ -160,9 +158,30 @@ pub(in super::super) fn lower_boxcall<'ctx, 'b>(
|
||||
if exp_tys.len() != args.len() { return Err("boxcall direct-call: arg count mismatch".to_string()); }
|
||||
let mut call_args: Vec<inkwell::values::BasicMetadataValueEnum> = Vec::with_capacity(args.len());
|
||||
for (i, a) in args.iter().enumerate() {
|
||||
let v = *vmap.get(a).ok_or("boxcall func arg missing")?;
|
||||
let tv = coerce_to_type(codegen, v, exp_tys[i])?;
|
||||
call_args.push(tv.into());
|
||||
use inkwell::types::BasicMetadataTypeEnum as BMTy;
|
||||
let coerced: BVE<'ctx> = match exp_tys[i] {
|
||||
BMTy::IntType(it) => {
|
||||
// Use Resolver via our surrounding lowering
|
||||
let iv = resolver.resolve_i64(codegen, cursor, cur_bid, *a, bb_map, preds, block_end_values, vmap)?;
|
||||
let bw_dst = it.get_bit_width();
|
||||
let bw_src = iv.get_type().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, "boxcall_arg_zext")).map_err(|e| e.to_string())?.into() }
|
||||
else if bw_dst == 1 { super::super::types::to_bool(codegen.context, iv.into(), &codegen.builder)?.into() }
|
||||
else { cursor.emit_instr(cur_bid, |b| b.build_int_truncate(iv, it, "boxcall_arg_trunc")).map_err(|e| e.to_string())?.into() }
|
||||
}
|
||||
BMTy::PointerType(pt) => {
|
||||
let iv = resolver.resolve_i64(codegen, cursor, cur_bid, *a, bb_map, preds, block_end_values, vmap)?;
|
||||
let p = cursor.emit_instr(cur_bid, |b| b.build_int_to_ptr(iv, pt, "boxcall_arg_i2p")).map_err(|e| e.to_string())?;
|
||||
p.into()
|
||||
}
|
||||
BMTy::FloatType(ft) => {
|
||||
let fv = resolver.resolve_f64(codegen, cursor, cur_bid, *a, 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, "boxcall_arg_fcast")).map_err(|e| e.to_string())?.into() }
|
||||
}
|
||||
_ => return Err("boxcall direct-call: unsupported parameter type".to_string()),
|
||||
};
|
||||
call_args.push(coerced.into());
|
||||
}
|
||||
let call = cursor
|
||||
.emit_instr(cur_bid, |b| b.build_call(callee, &call_args, "user_meth_call"))
|
||||
@ -177,15 +196,14 @@ pub(in super::super) fn lower_boxcall<'ctx, 'b>(
|
||||
}
|
||||
// Last resort: invoke plugin by name (host resolves method_id)
|
||||
{
|
||||
use crate::backend::llvm::compiler::codegen::instructions::boxcall::marshal::get_i64 as get_i64_any;
|
||||
let i64t = codegen.context.i64_type();
|
||||
let argc = i64t.const_int(args.len() as u64, false);
|
||||
let mname = cursor
|
||||
.emit_instr(cur_bid, |b| b.build_global_string_ptr(method, "meth_name"))
|
||||
.map_err(|e| e.to_string())?;
|
||||
// up to 2 args for this minimal path
|
||||
let a1 = if let Some(v0) = args.get(0) { get_i64_any(codegen, vmap, *v0)? } else { i64t.const_zero() };
|
||||
let a2 = if let Some(v1) = args.get(1) { get_i64_any(codegen, vmap, *v1)? } else { i64t.const_zero() };
|
||||
let a1 = if let Some(v0) = args.get(0) { resolver.resolve_i64(codegen, cursor, cur_bid, *v0, bb_map, preds, block_end_values, vmap)? } else { i64t.const_zero() };
|
||||
let a2 = if let Some(v1) = args.get(1) { resolver.resolve_i64(codegen, cursor, cur_bid, *v1, bb_map, preds, block_end_values, vmap)? } else { i64t.const_zero() };
|
||||
let fnty = i64t.fn_type(
|
||||
&[
|
||||
i64t.into(), // recv handle
|
||||
|
||||
@ -10,13 +10,17 @@ use super::super::builder_cursor::BuilderCursor;
|
||||
|
||||
pub(super) fn try_handle_field_method<'ctx, 'b>(
|
||||
codegen: &CodegenContext<'ctx>,
|
||||
_cursor: &mut BuilderCursor<'ctx, 'b>,
|
||||
_cur_bid: crate::mir::BasicBlockId,
|
||||
cursor: &mut BuilderCursor<'ctx, 'b>,
|
||||
cur_bid: crate::mir::BasicBlockId,
|
||||
vmap: &mut HashMap<ValueId, inkwell::values::BasicValueEnum<'ctx>>,
|
||||
dst: &Option<ValueId>,
|
||||
method: &str,
|
||||
args: &[ValueId],
|
||||
recv_h: inkwell::values::IntValue<'ctx>,
|
||||
resolver: &mut super::super::Resolver<'ctx>,
|
||||
bb_map: &std::collections::HashMap<crate::mir::BasicBlockId, inkwell::basic_block::BasicBlock<'ctx>>,
|
||||
preds: &std::collections::HashMap<crate::mir::BasicBlockId, Vec<crate::mir::BasicBlockId>>,
|
||||
block_end_values: &std::collections::HashMap<crate::mir::BasicBlockId, std::collections::HashMap<ValueId, inkwell::values::BasicValueEnum<'ctx>>>,
|
||||
) -> Result<bool, String> {
|
||||
let i64t = codegen.context.i64_type();
|
||||
match method {
|
||||
@ -24,19 +28,16 @@ pub(super) fn try_handle_field_method<'ctx, 'b>(
|
||||
if args.len() != 1 {
|
||||
return Err("getField expects 1 arg (name)".to_string());
|
||||
}
|
||||
let name_p = match vmap.get(&args[0]).copied() {
|
||||
Some(BVE::PointerValue(pv)) => pv,
|
||||
_ => return Err("getField name must be pointer".to_string()),
|
||||
};
|
||||
let name_p = resolver
|
||||
.resolve_ptr(codegen, cursor, cur_bid, args[0], bb_map, preds, block_end_values, vmap)?;
|
||||
let i8p = codegen.context.ptr_type(AddressSpace::from(0));
|
||||
let fnty = i64t.fn_type(&[i64t.into(), i8p.into()], false);
|
||||
let callee = codegen
|
||||
.module
|
||||
.get_function("nyash.instance.get_field_h")
|
||||
.unwrap_or_else(|| codegen.module.add_function("nyash.instance.get_field_h", fnty, None));
|
||||
let call = codegen
|
||||
.builder
|
||||
.build_call(callee, &[recv_h.into(), name_p.into()], "getField")
|
||||
let call = cursor
|
||||
.emit_instr(cur_bid, |b| b.build_call(callee, &[recv_h.into(), name_p.into()], "getField"))
|
||||
.map_err(|e| e.to_string())?;
|
||||
if let Some(d) = dst {
|
||||
let rv = call
|
||||
@ -61,24 +62,18 @@ pub(super) fn try_handle_field_method<'ctx, 'b>(
|
||||
if args.len() != 2 {
|
||||
return Err("setField expects 2 args (name, value)".to_string());
|
||||
}
|
||||
let name_p = match vmap.get(&args[0]).copied() {
|
||||
Some(BVE::PointerValue(pv)) => pv,
|
||||
_ => return Err("setField name must be pointer".to_string()),
|
||||
};
|
||||
let val_h = match vmap.get(&args[1]).copied() {
|
||||
Some(BVE::PointerValue(pv)) => codegen.builder.build_ptr_to_int(pv, i64t, "valp2i").map_err(|e| e.to_string())?,
|
||||
Some(BVE::IntValue(iv)) => iv,
|
||||
_ => return Err("setField value must be int/handle".to_string()),
|
||||
};
|
||||
let name_p = resolver
|
||||
.resolve_ptr(codegen, cursor, cur_bid, args[0], bb_map, preds, block_end_values, vmap)?;
|
||||
let val_h = resolver
|
||||
.resolve_i64(codegen, cursor, cur_bid, args[1], bb_map, preds, block_end_values, vmap)?;
|
||||
let i8p = codegen.context.ptr_type(AddressSpace::from(0));
|
||||
let fnty = i64t.fn_type(&[i64t.into(), i8p.into(), i64t.into()], false);
|
||||
let callee = codegen
|
||||
.module
|
||||
.get_function("nyash.instance.set_field_h")
|
||||
.unwrap_or_else(|| codegen.module.add_function("nyash.instance.set_field_h", fnty, None));
|
||||
let _ = codegen
|
||||
.builder
|
||||
.build_call(callee, &[recv_h.into(), name_p.into(), val_h.into()], "setField")
|
||||
let _ = cursor
|
||||
.emit_instr(cur_bid, |b| b.build_call(callee, &[recv_h.into(), name_p.into(), val_h.into()], "setField"))
|
||||
.map_err(|e| e.to_string())?;
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
@ -8,9 +8,11 @@ use crate::mir::{function::MirFunction, ValueId};
|
||||
// use super::marshal::{get_i64, get_tag_const};
|
||||
|
||||
/// Handle method_id-tagged plugin invoke path; returns Ok(()) if handled.
|
||||
pub(super) fn try_handle_tagged_invoke<'ctx>(
|
||||
pub(super) fn try_handle_tagged_invoke<'ctx, 'b>(
|
||||
codegen: &CodegenContext<'ctx>,
|
||||
func: &MirFunction,
|
||||
cursor: &mut crate::backend::llvm::compiler::codegen::instructions::builder_cursor::BuilderCursor<'ctx, 'b>,
|
||||
resolver: &mut super::super::Resolver<'ctx>,
|
||||
vmap: &mut HashMap<ValueId, inkwell::values::BasicValueEnum<'ctx>>,
|
||||
dst: &Option<ValueId>,
|
||||
mid: u16,
|
||||
@ -18,6 +20,10 @@ pub(super) fn try_handle_tagged_invoke<'ctx>(
|
||||
recv_h: inkwell::values::IntValue<'ctx>,
|
||||
args: &[ValueId],
|
||||
entry_builder: &inkwell::builder::Builder<'ctx>,
|
||||
cur_bid: crate::mir::BasicBlockId,
|
||||
bb_map: &std::collections::HashMap<crate::mir::BasicBlockId, inkwell::basic_block::BasicBlock<'ctx>>,
|
||||
preds: &std::collections::HashMap<crate::mir::BasicBlockId, Vec<crate::mir::BasicBlockId>>,
|
||||
block_end_values: &std::collections::HashMap<crate::mir::BasicBlockId, std::collections::HashMap<ValueId, inkwell::values::BasicValueEnum<'ctx>>>,
|
||||
) -> Result<(), String> {
|
||||
let i64t = codegen.context.i64_type();
|
||||
let argc_val = i64t.const_int(args.len() as u64, false);
|
||||
@ -26,17 +32,15 @@ pub(super) fn try_handle_tagged_invoke<'ctx>(
|
||||
if args.len() <= 4 {
|
||||
let mut a = [i64t.const_zero(); 4];
|
||||
for (i, vid) in args.iter().enumerate() {
|
||||
// Prefer Resolver-style i64 handles: assume ints/ptrs are bridged to i64
|
||||
let iv = match vmap.get(vid).copied() {
|
||||
Some(BVE::IntValue(iv)) => iv,
|
||||
Some(BVE::PointerValue(pv)) => codegen.builder.build_ptr_to_int(pv, i64t, "arg_p2i").map_err(|e| e.to_string())?,
|
||||
Some(BVE::FloatValue(fv)) => {
|
||||
let iv = match func.metadata.value_types.get(vid) {
|
||||
Some(crate::mir::MirType::Float) => {
|
||||
let fv = resolver.resolve_f64(codegen, cursor, cur_bid, *vid, bb_map, preds, block_end_values, vmap)?;
|
||||
let fnty = i64t.fn_type(&[codegen.context.f64_type().into()], false);
|
||||
let callee = codegen.module.get_function("nyash.box.from_f64").unwrap_or_else(|| codegen.module.add_function("nyash.box.from_f64", fnty, None));
|
||||
let call = codegen.builder.build_call(callee, &[fv.into()], "arg_f2h").map_err(|e| e.to_string())?;
|
||||
call.try_as_basic_value().left().ok_or("from_f64 returned void".to_string())?.into_int_value()
|
||||
}
|
||||
_ => i64t.const_zero(),
|
||||
_ => resolver.resolve_i64(codegen, cursor, cur_bid, *vid, bb_map, preds, block_end_values, vmap)?,
|
||||
};
|
||||
a[i] = iv;
|
||||
}
|
||||
@ -111,16 +115,15 @@ pub(super) fn try_handle_tagged_invoke<'ctx>(
|
||||
.build_in_bounds_gep(arr_ty, tags_arr, &idx, &format!("t_gep_{}", i))
|
||||
.map_err(|e| e.to_string())?
|
||||
};
|
||||
let vi = match vmap.get(vid).copied() {
|
||||
Some(BVE::IntValue(iv)) => iv,
|
||||
Some(BVE::PointerValue(pv)) => codegen.builder.build_ptr_to_int(pv, i64t, "arg_p2i").map_err(|e| e.to_string())?,
|
||||
Some(BVE::FloatValue(fv)) => {
|
||||
let vi = match func.metadata.value_types.get(vid) {
|
||||
Some(crate::mir::MirType::Float) => {
|
||||
let fv = resolver.resolve_f64(codegen, cursor, cur_bid, *vid, bb_map, preds, block_end_values, vmap)?;
|
||||
let fnty = i64t.fn_type(&[codegen.context.f64_type().into()], false);
|
||||
let callee = codegen.module.get_function("nyash.box.from_f64").unwrap_or_else(|| codegen.module.add_function("nyash.box.from_f64", fnty, None));
|
||||
let call = codegen.builder.build_call(callee, &[fv.into()], "arg_f2h").map_err(|e| e.to_string())?;
|
||||
call.try_as_basic_value().left().ok_or("from_f64 returned void".to_string())?.into_int_value()
|
||||
}
|
||||
_ => i64t.const_zero(),
|
||||
_ => resolver.resolve_i64(codegen, cursor, cur_bid, *vid, bb_map, preds, block_end_values, vmap)?,
|
||||
};
|
||||
let ti = match func.metadata.value_types.get(vid) {
|
||||
Some(crate::mir::MirType::Float) => i64t.const_int(5, false),
|
||||
@ -208,19 +211,9 @@ fn store_invoke_return<'ctx>(
|
||||
}
|
||||
}
|
||||
crate::mir::MirType::String => {
|
||||
// Normalize to i8* for String to align with PHI/type inference
|
||||
// Plugins return i64 handle; convert handle -> i8* here.
|
||||
let h = if let BVE::IntValue(iv) = rv {
|
||||
iv
|
||||
} else {
|
||||
return Err("invoke ret expected i64 for String".to_string());
|
||||
};
|
||||
let pty = codegen.context.ptr_type(inkwell::AddressSpace::from(0));
|
||||
let ptr = codegen
|
||||
.builder
|
||||
.build_int_to_ptr(h, pty, "ret_string_handle_to_ptr")
|
||||
.map_err(|e| e.to_string())?;
|
||||
vmap.insert(dst, ptr.into());
|
||||
// Keep as i64 handle across blocks (pointer is produced on demand via Resolver)
|
||||
if let BVE::IntValue(iv) = rv { vmap.insert(dst, iv.into()); }
|
||||
else { return Err("invoke ret expected i64 for String".to_string()); }
|
||||
}
|
||||
crate::mir::MirType::Box(_)
|
||||
| crate::mir::MirType::Array(_)
|
||||
|
||||
@ -4,56 +4,59 @@ use inkwell::{values::BasicValueEnum as BVE, AddressSpace};
|
||||
use crate::backend::llvm::compiler::codegen::types;
|
||||
|
||||
use crate::backend::llvm::context::CodegenContext;
|
||||
use crate::mir::ValueId;
|
||||
use crate::mir::{ValueId, function::MirFunction};
|
||||
|
||||
/// Convert a value to i64 handle/int for plugin invoke (ptr->i64, f64->box->i64)
|
||||
pub(super) fn get_i64<'ctx>(
|
||||
pub(super) fn get_i64<'ctx, 'b>(
|
||||
codegen: &CodegenContext<'ctx>,
|
||||
cursor: &mut crate::backend::llvm::compiler::codegen::instructions::builder_cursor::BuilderCursor<'ctx, 'b>,
|
||||
resolver: &mut super::super::Resolver<'ctx>,
|
||||
cur_bid: crate::mir::BasicBlockId,
|
||||
func: &MirFunction,
|
||||
vmap: &HashMap<ValueId, inkwell::values::BasicValueEnum<'ctx>>,
|
||||
vid: ValueId,
|
||||
bb_map: &std::collections::HashMap<crate::mir::BasicBlockId, inkwell::basic_block::BasicBlock<'ctx>>,
|
||||
preds: &std::collections::HashMap<crate::mir::BasicBlockId, Vec<crate::mir::BasicBlockId>>,
|
||||
block_end_values: &std::collections::HashMap<crate::mir::BasicBlockId, std::collections::HashMap<ValueId, inkwell::values::BasicValueEnum<'ctx>>>,
|
||||
) -> Result<inkwell::values::IntValue<'ctx>, String> {
|
||||
let i64t = codegen.context.i64_type();
|
||||
let bv = *vmap.get(&vid).ok_or("arg missing")?;
|
||||
match bv {
|
||||
BVE::IntValue(iv) => Ok(iv),
|
||||
BVE::PointerValue(pv) => codegen
|
||||
.builder
|
||||
.build_ptr_to_int(pv, i64t, "p2i")
|
||||
.map_err(|e| e.to_string()),
|
||||
BVE::FloatValue(fv) => {
|
||||
match func.metadata.value_types.get(&vid) {
|
||||
Some(crate::mir::MirType::Float) => {
|
||||
// Box f64 then use its handle
|
||||
let fv = resolver.resolve_f64(codegen, cursor, cur_bid, vid, bb_map, preds, block_end_values, vmap)?;
|
||||
let fnty = i64t.fn_type(&[codegen.context.f64_type().into()], false);
|
||||
let callee = codegen
|
||||
.module
|
||||
.get_function("nyash.box.from_f64")
|
||||
.unwrap_or_else(|| codegen.module.add_function("nyash.box.from_f64", fnty, None));
|
||||
let call = codegen
|
||||
.builder
|
||||
.build_call(callee, &[fv.into()], "arg_f64_to_box")
|
||||
let call = cursor
|
||||
.emit_instr(cur_bid, |b| b.build_call(callee, &[fv.into()], "arg_f64_to_box"))
|
||||
.map_err(|e| e.to_string())?;
|
||||
let rv = call
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("from_f64 returned void".to_string())?;
|
||||
if let BVE::IntValue(h) = rv {
|
||||
Ok(h)
|
||||
} else {
|
||||
Err("from_f64 ret expected i64".to_string())
|
||||
if let BVE::IntValue(h) = rv { Ok(h) } else { Err("from_f64 ret expected i64".to_string()) }
|
||||
}
|
||||
}
|
||||
_ => Err("unsupported arg value".to_string()),
|
||||
_ => resolver.resolve_i64(codegen, cursor, cur_bid, vid, bb_map, preds, block_end_values, vmap),
|
||||
}
|
||||
}
|
||||
|
||||
/// Classify a value into tag constant i64 (uses types::classify_tag)
|
||||
pub(super) fn get_tag_const<'ctx>(
|
||||
codegen: &CodegenContext<'ctx>,
|
||||
vmap: &HashMap<ValueId, inkwell::values::BasicValueEnum<'ctx>>,
|
||||
func: &MirFunction,
|
||||
vid: ValueId,
|
||||
) -> inkwell::values::IntValue<'ctx> {
|
||||
let i64t = codegen.context.i64_type();
|
||||
let tag = vmap
|
||||
.get(&vid)
|
||||
.map(|v| types::classify_tag(*v))
|
||||
.unwrap_or(3);
|
||||
let tag = match func.metadata.value_types.get(&vid) {
|
||||
Some(crate::mir::MirType::Float) => 5,
|
||||
Some(crate::mir::MirType::String)
|
||||
| Some(crate::mir::MirType::Box(_))
|
||||
| Some(crate::mir::MirType::Array(_))
|
||||
| Some(crate::mir::MirType::Future(_))
|
||||
| Some(crate::mir::MirType::Unknown) => 8,
|
||||
_ => 3,
|
||||
};
|
||||
i64t.const_int(tag as u64, false)
|
||||
}
|
||||
|
||||
@ -24,41 +24,10 @@ pub(super) fn lower_log_or_trace<'ctx, 'b>(
|
||||
if args.len() != 1 {
|
||||
return Err(format!("{}.{} expects 1 arg", iface_name, method_name));
|
||||
}
|
||||
let av = *vmap.get(&args[0]).ok_or("extern arg missing")?;
|
||||
match av {
|
||||
// If argument is i8* (string), call string variant
|
||||
BVE::PointerValue(pv) => {
|
||||
let i8p = codegen.context.ptr_type(AddressSpace::from(0));
|
||||
let fnty = codegen.context.i64_type().fn_type(&[i8p.into()], false);
|
||||
let fname = if iface_name == "env.console" {
|
||||
match method_name {
|
||||
"log" => "nyash.console.log",
|
||||
"warn" => "nyash.console.warn",
|
||||
_ => "nyash.console.error",
|
||||
}
|
||||
} else {
|
||||
"nyash.debug.trace"
|
||||
};
|
||||
let callee = codegen
|
||||
.module
|
||||
.get_function(fname)
|
||||
.unwrap_or_else(|| codegen.module.add_function(fname, fnty, None));
|
||||
let _ = cursor
|
||||
.emit_instr(cur_bid, |b| b.build_call(callee, &[pv.into()], "console_log_p"))
|
||||
.map_err(|e| e.to_string())?;
|
||||
if let Some(d) = dst {
|
||||
vmap.insert(*d, codegen.context.i64_type().const_zero().into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
// Otherwise, convert to i64 and call handle variant
|
||||
_ => {
|
||||
// Localize to i64 to satisfy dominance
|
||||
// Localize to i64 (handle path) to avoid vmap shape inspection
|
||||
let arg_val = resolver.resolve_i64(codegen, cursor, cur_bid, args[0], bb_map, preds, block_end_values, vmap)?;
|
||||
let fnty = codegen
|
||||
.context
|
||||
.i64_type()
|
||||
.fn_type(&[codegen.context.i64_type().into()], false);
|
||||
let i64t = codegen.context.i64_type();
|
||||
let fnty = i64t.fn_type(&[i64t.into()], false);
|
||||
let fname = if iface_name == "env.console" {
|
||||
match method_name {
|
||||
"log" => "nyash.console.log_handle",
|
||||
@ -80,8 +49,6 @@ pub(super) fn lower_log_or_trace<'ctx, 'b>(
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn lower_readline<'ctx, 'b>(
|
||||
codegen: &CodegenContext<'ctx>,
|
||||
|
||||
@ -24,19 +24,8 @@ pub(super) fn lower_future_spawn_instance<'ctx, 'b>(
|
||||
}
|
||||
let i64t = codegen.context.i64_type();
|
||||
let i8p = codegen.context.ptr_type(AddressSpace::from(0));
|
||||
let recv_v = *vmap.get(&args[0]).ok_or("recv missing")?;
|
||||
let recv_h = match recv_v {
|
||||
BVE::IntValue(_) | BVE::PointerValue(_) => {
|
||||
// Localize to i64 to satisfy dominance; converts ptr→i64 if needed
|
||||
resolver.resolve_i64(codegen, cursor, cur_bid, args[0], bb_map, preds, block_end_values, vmap)?
|
||||
}
|
||||
_ => return Err("spawn_instance recv must be int or ptr".to_string()),
|
||||
};
|
||||
let name_v = *vmap.get(&args[1]).ok_or("method name missing")?;
|
||||
let name_p = match name_v {
|
||||
BVE::PointerValue(pv) => pv,
|
||||
_ => return Err("spawn_instance method name must be i8*".to_string()),
|
||||
};
|
||||
let recv_h = resolver.resolve_i64(codegen, cursor, cur_bid, args[0], bb_map, preds, block_end_values, vmap)?;
|
||||
let name_p = resolver.resolve_ptr(codegen, cursor, cur_bid, args[1], bb_map, preds, block_end_values, vmap)?;
|
||||
let fnty = i64t.fn_type(&[i64t.into(), i8p.into()], false);
|
||||
let callee = codegen
|
||||
.module
|
||||
@ -71,12 +60,8 @@ pub(super) fn lower_local_get<'ctx, 'b>(
|
||||
if args.len() != 1 {
|
||||
return Err("env.local.get expects 1 arg".to_string());
|
||||
}
|
||||
let name_v = *vmap.get(&args[0]).ok_or("local.get name missing")?;
|
||||
let name_p = if let BVE::PointerValue(pv) = name_v {
|
||||
pv
|
||||
} else {
|
||||
return Err("env.local.get name must be i8*".to_string());
|
||||
};
|
||||
let name_p = _resolver
|
||||
.resolve_ptr(codegen, cursor, cur_bid, args[0], _bb_map, _preds, _block_end_values, vmap)?;
|
||||
let i64t = codegen.context.i64_type();
|
||||
let i8p = codegen.context.ptr_type(AddressSpace::from(0));
|
||||
let fnty = i64t.fn_type(&[i8p.into()], false);
|
||||
@ -129,6 +114,7 @@ pub(super) fn lower_box_new<'ctx, 'b>(
|
||||
cursor: &mut BuilderCursor<'ctx, 'b>,
|
||||
resolver: &mut super::super::Resolver<'ctx>,
|
||||
cur_bid: BasicBlockId,
|
||||
func: &MirFunction,
|
||||
vmap: &mut HashMap<ValueId, BVE<'ctx>>,
|
||||
dst: &Option<ValueId>,
|
||||
args: &[ValueId],
|
||||
@ -141,12 +127,8 @@ pub(super) fn lower_box_new<'ctx, 'b>(
|
||||
let i64t = codegen.context.i64_type();
|
||||
let i8p = codegen.context.ptr_type(AddressSpace::from(0));
|
||||
if args.len() == 1 {
|
||||
let name_v = *vmap.get(&args[0]).ok_or("env.box.new name missing")?;
|
||||
let name_p = if let BVE::PointerValue(pv) = name_v {
|
||||
pv
|
||||
} else {
|
||||
return Err("env.box.new name must be i8*".to_string());
|
||||
};
|
||||
let name_p = resolver
|
||||
.resolve_ptr(codegen, cursor, cur_bid, args[0], bb_map, preds, block_end_values, vmap)?;
|
||||
let fnty = i64t.fn_type(&[i8p.into()], false);
|
||||
let callee = codegen
|
||||
.module
|
||||
@ -190,18 +172,13 @@ pub(super) fn lower_box_new<'ctx, 'b>(
|
||||
if args.is_empty() {
|
||||
return Err("env.box.new_i64 requires at least type name".to_string());
|
||||
}
|
||||
let ty_ptr = match *vmap.get(&args[0]).ok_or("type name missing")? {
|
||||
BVE::PointerValue(pv) => pv,
|
||||
_ => return Err("env.box.new_i64 arg0 must be i8* type name".to_string()),
|
||||
};
|
||||
let ty_ptr = resolver
|
||||
.resolve_ptr(codegen, cursor, cur_bid, args[0], bb_map, preds, block_end_values, vmap)?;
|
||||
let mut a1 = i64t.const_zero();
|
||||
if args.len() >= 2 {
|
||||
let bv = *vmap.get(&args[1]).ok_or("arg missing")?;
|
||||
a1 = match bv {
|
||||
BVE::IntValue(_) | BVE::PointerValue(_) => {
|
||||
resolver.resolve_i64(codegen, cursor, cur_bid, args[1], bb_map, preds, block_end_values, vmap)?
|
||||
}
|
||||
BVE::FloatValue(fv) => {
|
||||
a1 = match func.metadata.value_types.get(&args[1]) {
|
||||
Some(crate::mir::MirType::Float) => {
|
||||
let fv = resolver.resolve_f64(codegen, cursor, cur_bid, args[1], bb_map, preds, block_end_values, vmap)?;
|
||||
let fnty = i64t.fn_type(&[codegen.context.f64_type().into()], false);
|
||||
let callee = codegen
|
||||
.module
|
||||
@ -210,24 +187,28 @@ pub(super) fn lower_box_new<'ctx, 'b>(
|
||||
let call = cursor
|
||||
.emit_instr(cur_bid, |b| b.build_call(callee, &[fv.into()], "arg1_f64_to_box"))
|
||||
.map_err(|e| e.to_string())?;
|
||||
let rv = call
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("from_f64 returned void".to_string())?;
|
||||
if let BVE::IntValue(h) = rv { h } else { return Err("from_f64 ret expected i64".to_string()); }
|
||||
call.try_as_basic_value().left().ok_or("from_f64 returned void".to_string())?.into_int_value()
|
||||
}
|
||||
// Pointer handled above by resolve_i64
|
||||
_ => return Err("unsupported arg value for env.box.new".to_string()),
|
||||
Some(crate::mir::MirType::String) => {
|
||||
let pv = resolver.resolve_ptr(codegen, cursor, cur_bid, args[1], bb_map, preds, block_end_values, vmap)?;
|
||||
let fnty = i64t.fn_type(&[i8p.into()], false);
|
||||
let callee = codegen
|
||||
.module
|
||||
.get_function("nyash.box.from_i8_string")
|
||||
.unwrap_or_else(|| codegen.module.add_function("nyash.box.from_i8_string", fnty, None));
|
||||
let call = cursor
|
||||
.emit_instr(cur_bid, |b| b.build_call(callee, &[pv.into()], "arg1_i8_to_box"))
|
||||
.map_err(|e| e.to_string())?;
|
||||
call.try_as_basic_value().left().ok_or("from_i8_string returned void".to_string())?.into_int_value()
|
||||
}
|
||||
_ => resolver.resolve_i64(codegen, cursor, cur_bid, args[1], bb_map, preds, block_end_values, vmap)?,
|
||||
};
|
||||
}
|
||||
let mut a2 = i64t.const_zero();
|
||||
if args.len() >= 3 {
|
||||
let bv = *vmap.get(&args[2]).ok_or("arg missing")?;
|
||||
a2 = match bv {
|
||||
BVE::IntValue(_) | BVE::PointerValue(_) => {
|
||||
resolver.resolve_i64(codegen, cursor, cur_bid, args[2], bb_map, preds, block_end_values, vmap)?
|
||||
}
|
||||
BVE::FloatValue(fv) => {
|
||||
a2 = match func.metadata.value_types.get(&args[2]) {
|
||||
Some(crate::mir::MirType::Float) => {
|
||||
let fv = resolver.resolve_f64(codegen, cursor, cur_bid, args[2], bb_map, preds, block_end_values, vmap)?;
|
||||
let fnty = i64t.fn_type(&[codegen.context.f64_type().into()], false);
|
||||
let callee = codegen
|
||||
.module
|
||||
@ -236,22 +217,31 @@ pub(super) fn lower_box_new<'ctx, 'b>(
|
||||
let call = cursor
|
||||
.emit_instr(cur_bid, |b| b.build_call(callee, &[fv.into()], "arg2_f64_to_box"))
|
||||
.map_err(|e| e.to_string())?;
|
||||
let rv = call
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("from_f64 returned void".to_string())?;
|
||||
if let BVE::IntValue(h) = rv { h } else { return Err("from_f64 ret expected i64".to_string()); }
|
||||
call.try_as_basic_value().left().ok_or("from_f64 returned void".to_string())?.into_int_value()
|
||||
}
|
||||
// Pointer handled above by resolve_i64
|
||||
_ => return Err("unsupported arg value for env.box.new".to_string()),
|
||||
Some(crate::mir::MirType::String) => {
|
||||
let pv = resolver.resolve_ptr(codegen, cursor, cur_bid, args[2], bb_map, preds, block_end_values, vmap)?;
|
||||
let fnty = i64t.fn_type(&[i8p.into()], false);
|
||||
let callee = codegen
|
||||
.module
|
||||
.get_function("nyash.box.from_i8_string")
|
||||
.unwrap_or_else(|| codegen.module.add_function("nyash.box.from_i8_string", fnty, None));
|
||||
let call = cursor
|
||||
.emit_instr(cur_bid, |b| b.build_call(callee, &[pv.into()], "arg2_i8_to_box"))
|
||||
.map_err(|e| e.to_string())?;
|
||||
call.try_as_basic_value().left().ok_or("from_i8_string returned void".to_string())?.into_int_value()
|
||||
}
|
||||
_ => resolver.resolve_i64(codegen, cursor, cur_bid, args[2], bb_map, preds, block_end_values, vmap)?,
|
||||
};
|
||||
}
|
||||
let mut a3 = i64t.const_zero();
|
||||
if args.len() >= 4 {
|
||||
let bv = *vmap.get(&args[3]).ok_or("arg missing")?;
|
||||
a3 = match bv {
|
||||
BVE::IntValue(iv) => iv,
|
||||
BVE::FloatValue(fv) => {
|
||||
a3 = match func.metadata.value_types.get(&args[3]) {
|
||||
Some(crate::mir::MirType::Integer) | Some(crate::mir::MirType::Bool) => {
|
||||
resolver.resolve_i64(codegen, cursor, cur_bid, args[3], bb_map, preds, block_end_values, vmap)?
|
||||
}
|
||||
Some(crate::mir::MirType::Float) => {
|
||||
let fv = resolver.resolve_f64(codegen, cursor, cur_bid, args[3], bb_map, preds, block_end_values, vmap)?;
|
||||
let fnty = i64t.fn_type(&[codegen.context.f64_type().into()], false);
|
||||
let callee = codegen
|
||||
.module
|
||||
@ -260,13 +250,10 @@ pub(super) fn lower_box_new<'ctx, 'b>(
|
||||
let call = cursor
|
||||
.emit_instr(cur_bid, |b| b.build_call(callee, &[fv.into()], "arg3_f64_to_box"))
|
||||
.map_err(|e| e.to_string())?;
|
||||
let rv = call
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("from_f64 returned void".to_string())?;
|
||||
if let BVE::IntValue(h) = rv { h } else { return Err("from_f64 ret expected i64".to_string()); }
|
||||
call.try_as_basic_value().left().ok_or("from_f64 returned void".to_string())?.into_int_value()
|
||||
}
|
||||
BVE::PointerValue(pv) => {
|
||||
Some(crate::mir::MirType::String) => {
|
||||
let pv = resolver.resolve_ptr(codegen, cursor, cur_bid, args[3], bb_map, preds, block_end_values, vmap)?;
|
||||
let fnty = i64t.fn_type(&[i8p.into()], false);
|
||||
let callee = codegen
|
||||
.module
|
||||
@ -275,18 +262,19 @@ pub(super) fn lower_box_new<'ctx, 'b>(
|
||||
let call = cursor
|
||||
.emit_instr(cur_bid, |b| b.build_call(callee, &[pv.into()], "arg3_i8_to_box"))
|
||||
.map_err(|e| e.to_string())?;
|
||||
let rv = call.try_as_basic_value().left().ok_or("from_i8_string returned void".to_string())?;
|
||||
if let BVE::IntValue(h) = rv { h } else { return Err("from_i8_string ret expected i64".to_string()); }
|
||||
call.try_as_basic_value().left().ok_or("from_i8_string returned void".to_string())?.into_int_value()
|
||||
}
|
||||
_ => return Err("unsupported arg value for env.box.new".to_string()),
|
||||
};
|
||||
}
|
||||
let mut a4 = i64t.const_zero();
|
||||
if args.len() >= 5 {
|
||||
let bv = *vmap.get(&args[4]).ok_or("arg missing")?;
|
||||
a4 = match bv {
|
||||
BVE::IntValue(iv) => iv,
|
||||
BVE::FloatValue(fv) => {
|
||||
a4 = match func.metadata.value_types.get(&args[4]) {
|
||||
Some(crate::mir::MirType::Integer) | Some(crate::mir::MirType::Bool) => {
|
||||
resolver.resolve_i64(codegen, cursor, cur_bid, args[4], bb_map, preds, block_end_values, vmap)?
|
||||
}
|
||||
Some(crate::mir::MirType::Float) => {
|
||||
let fv = resolver.resolve_f64(codegen, cursor, cur_bid, args[4], bb_map, preds, block_end_values, vmap)?;
|
||||
let fnty = i64t.fn_type(&[codegen.context.f64_type().into()], false);
|
||||
let callee = codegen
|
||||
.module
|
||||
@ -295,13 +283,10 @@ pub(super) fn lower_box_new<'ctx, 'b>(
|
||||
let call = cursor
|
||||
.emit_instr(cur_bid, |b| b.build_call(callee, &[fv.into()], "arg4_f64_to_box"))
|
||||
.map_err(|e| e.to_string())?;
|
||||
let rv = call
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("from_f64 returned void".to_string())?;
|
||||
if let BVE::IntValue(h) = rv { h } else { return Err("from_f64 ret expected i64".to_string()); }
|
||||
call.try_as_basic_value().left().ok_or("from_f64 returned void".to_string())?.into_int_value()
|
||||
}
|
||||
BVE::PointerValue(pv) => {
|
||||
Some(crate::mir::MirType::String) => {
|
||||
let pv = resolver.resolve_ptr(codegen, cursor, cur_bid, args[4], bb_map, preds, block_end_values, vmap)?;
|
||||
let fnty = i64t.fn_type(&[i8p.into()], false);
|
||||
let callee = codegen
|
||||
.module
|
||||
@ -310,8 +295,7 @@ pub(super) fn lower_box_new<'ctx, 'b>(
|
||||
let call = cursor
|
||||
.emit_instr(cur_bid, |b| b.build_call(callee, &[pv.into()], "arg4_i8_to_box"))
|
||||
.map_err(|e| e.to_string())?;
|
||||
let rv = call.try_as_basic_value().left().ok_or("from_i8_string returned void".to_string())?;
|
||||
if let BVE::IntValue(h) = rv { h } else { return Err("from_i8_string ret expected i64".to_string()); }
|
||||
call.try_as_basic_value().left().ok_or("from_i8_string returned void".to_string())?.into_int_value()
|
||||
}
|
||||
_ => return Err("unsupported arg value for env.box.new".to_string()),
|
||||
};
|
||||
|
||||
@ -43,7 +43,7 @@ pub(in super::super) fn lower_externcall<'ctx, 'b>(
|
||||
return env::lower_local_get(codegen, cursor, resolver, cur_bid, func, vmap, dst, args, bb_map, preds, block_end_values);
|
||||
}
|
||||
if iface_name == "env.box" && method_name == "new" {
|
||||
return env::lower_box_new(codegen, cursor, resolver, cur_bid, vmap, dst, args, bb_map, preds, block_end_values);
|
||||
return env::lower_box_new(codegen, cursor, resolver, cur_bid, func, vmap, dst, args, bb_map, preds, block_end_values);
|
||||
}
|
||||
|
||||
Err(format!(
|
||||
|
||||
@ -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) => {
|
||||
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() }
|
||||
}
|
||||
BT::FloatType(_ft) => {
|
||||
*vmap.get(vid).ok_or("ret value missing")?
|
||||
}
|
||||
_ => *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(_) => {
|
||||
// Localize condition as i64 and convert to i1 via != 0(Resolver 経由のみ)
|
||||
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)?,
|
||||
};
|
||||
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,27 +202,14 @@ 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
|
||||
// Synthesize zero to avoid dominance violations(vmap には依存しない)
|
||||
let bt = phi.as_basic_value().get_type();
|
||||
use inkwell::types::BasicTypeEnum as BT;
|
||||
match bt {
|
||||
@ -228,7 +221,6 @@ pub(in super::super) fn seal_block<'ctx, 'b>(
|
||||
bid.as_u32(), sb.as_u32(), in_vid.as_u32()
|
||||
)),
|
||||
}
|
||||
}
|
||||
};
|
||||
// Insert any required casts in the predecessor block, right before its terminator
|
||||
if let Some(pred_llbb) = bb_map.get(&bid) {
|
||||
@ -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 zero(vmap には依存しない)
|
||||
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 {
|
||||
// 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.into() }
|
||||
else { codegen.builder.build_int_z_extend(iv, i64t, "loc_zext_p").map_err(|e| e.to_string())?.into() }
|
||||
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())?.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(),
|
||||
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(),
|
||||
};
|
||||
match val {
|
||||
BasicValueEnum::IntValue(iv) => phi.add_incoming(&[(&iv, pred_bb)]),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
});
|
||||
phi.add_incoming(&[(&iv_out, pred_bb)]);
|
||||
}
|
||||
// Restore insertion point
|
||||
if let Some(bb) = saved_ip { codegen.builder.position_at_end(bb); }
|
||||
|
||||
@ -12,6 +12,7 @@ use crate::mir::{
|
||||
};
|
||||
|
||||
use super::builder_cursor::BuilderCursor;
|
||||
use super::Resolver;
|
||||
use super::super::types::to_bool;
|
||||
|
||||
/// LoopForm scaffolding — fixed block layout for while/loop normalization
|
||||
@ -62,6 +63,7 @@ impl<'ctx> LoopFormContext<'ctx> {
|
||||
pub fn lower_while_loopform<'ctx, 'b>(
|
||||
codegen: &CodegenContext<'ctx>,
|
||||
cursor: &mut BuilderCursor<'ctx, 'b>,
|
||||
resolver: &mut Resolver<'ctx>,
|
||||
func: &MirFunction,
|
||||
llvm_func: FunctionValue<'ctx>,
|
||||
condition: &ValueId,
|
||||
@ -73,6 +75,8 @@ pub fn lower_while_loopform<'ctx, 'b>(
|
||||
after_bb: BasicBlockId,
|
||||
bb_map: &std::collections::HashMap<BasicBlockId, BasicBlock<'ctx>>,
|
||||
vmap: &std::collections::HashMap<ValueId, BasicValueEnum<'ctx>>,
|
||||
preds: &std::collections::HashMap<BasicBlockId, Vec<BasicBlockId>>,
|
||||
block_end_values: &std::collections::HashMap<BasicBlockId, std::collections::HashMap<ValueId, BasicValueEnum<'ctx>>>,
|
||||
// Registry to allow later body→dispatch wiring (simple bodies)
|
||||
registry: &mut std::collections::HashMap<BasicBlockId, (BasicBlock<'ctx>, PhiValue<'ctx>, PhiValue<'ctx>, BasicBlock<'ctx>)>,
|
||||
body_to_header: &mut std::collections::HashMap<BasicBlockId, BasicBlockId>,
|
||||
@ -90,9 +94,17 @@ pub fn lower_while_loopform<'ctx, 'b>(
|
||||
.map_err(|e| e.to_string())
|
||||
.unwrap();
|
||||
|
||||
// Header: evaluate condition and branch to body (for true) or dispatch (for false)
|
||||
let cond_v = *vmap.get(condition).ok_or("loopform: condition value missing")?;
|
||||
let cond_i1 = to_bool(codegen.context, cond_v, &codegen.builder)?;
|
||||
// Header: evaluate condition via Resolver and branch to body (for true) or dispatch (for false)
|
||||
let ci = resolver.resolve_i64(codegen, cursor, header_bid, *condition, bb_map, preds, block_end_values, vmap)?;
|
||||
let cond_i1 = codegen
|
||||
.builder
|
||||
.build_int_compare(
|
||||
inkwell::IntPredicate::NE,
|
||||
ci,
|
||||
codegen.context.i64_type().const_zero(),
|
||||
"lf_cond_nez",
|
||||
)
|
||||
.map_err(|e| e.to_string())?;
|
||||
cursor.emit_term(header_bid, |b| {
|
||||
b.build_conditional_branch(cond_i1, lf.body, lf.dispatch)
|
||||
.map_err(|e| e.to_string())
|
||||
|
||||
@ -58,10 +58,21 @@ pub(super) fn try_handle_map_method<'ctx, 'b>(
|
||||
if args.len() != 1 {
|
||||
return Err("MapBox.has expects 1 arg".to_string());
|
||||
}
|
||||
let key_v = *vmap.get(&args[0]).ok_or("map.has key missing")?;
|
||||
let key_i = match key_v {
|
||||
BVE::IntValue(_) | BVE::PointerValue(_) => resolver.resolve_i64(codegen, cursor, cur_bid, args[0], &std::collections::HashMap::new(), &std::collections::HashMap::new(), &std::collections::HashMap::new(), vmap).map_err(|e| e.to_string())?,
|
||||
_ => return Err("map.has key must be int or handle ptr".to_string()),
|
||||
let key_i = match func.metadata.value_types.get(&args[0]) {
|
||||
Some(crate::mir::MirType::String) => {
|
||||
// string key: i8* -> handle
|
||||
let pv = resolver.resolve_ptr(codegen, cursor, cur_bid, args[0], &std::collections::HashMap::new(), &std::collections::HashMap::new(), &std::collections::HashMap::new(), vmap)?;
|
||||
let fnty_conv = i64t.fn_type(&[codegen.context.ptr_type(AddressSpace::from(0)).into()], false);
|
||||
let conv = codegen
|
||||
.module
|
||||
.get_function("nyash.box.from_i8_string")
|
||||
.unwrap_or_else(|| codegen.module.add_function("nyash.box.from_i8_string", fnty_conv, None));
|
||||
let kcall = cursor
|
||||
.emit_instr(cur_bid, |b| b.build_call(conv, &[pv.into()], "key_i8_to_handle"))
|
||||
.map_err(|e| e.to_string())?;
|
||||
kcall.try_as_basic_value().left().ok_or("from_i8_string returned void".to_string())?.into_int_value()
|
||||
}
|
||||
_ => resolver.resolve_i64(codegen, cursor, cur_bid, args[0], &std::collections::HashMap::new(), &std::collections::HashMap::new(), &std::collections::HashMap::new(), vmap)?
|
||||
};
|
||||
let fnty = i64t.fn_type(&[i64t.into(), i64t.into()], false);
|
||||
let callee = codegen
|
||||
@ -87,21 +98,10 @@ pub(super) fn try_handle_map_method<'ctx, 'b>(
|
||||
if args.len() != 1 {
|
||||
return Err("MapBox.get expects 1 arg".to_string());
|
||||
}
|
||||
let key_v = *vmap.get(&args[0]).ok_or("map.get key missing")?;
|
||||
let call = match key_v {
|
||||
BVE::IntValue(_) => {
|
||||
let iv = resolver.resolve_i64(codegen, cursor, cur_bid, args[0], &std::collections::HashMap::new(), &std::collections::HashMap::new(), &std::collections::HashMap::new(), vmap)?;
|
||||
let fnty = i64t.fn_type(&[i64t.into(), i64t.into()], false);
|
||||
let callee = codegen
|
||||
.module
|
||||
.get_function("nyash.map.get_h")
|
||||
.unwrap_or_else(|| codegen.module.add_function("nyash.map.get_h", fnty, None));
|
||||
cursor
|
||||
.emit_instr(cur_bid, |b| b.build_call(callee, &[recv_h.into(), iv.into()], "mget"))
|
||||
.map_err(|e| e.to_string())?
|
||||
}
|
||||
BVE::PointerValue(pv) => {
|
||||
let call = match func.metadata.value_types.get(&args[0]) {
|
||||
Some(crate::mir::MirType::String) => {
|
||||
// key: i8* -> i64 handle via from_i8_string (string key)
|
||||
let pv = resolver.resolve_ptr(codegen, cursor, cur_bid, args[0], &std::collections::HashMap::new(), &std::collections::HashMap::new(), &std::collections::HashMap::new(), vmap)?;
|
||||
let fnty_conv = i64t
|
||||
.fn_type(&[codegen.context.ptr_type(AddressSpace::from(0)).into()], false);
|
||||
let conv = codegen
|
||||
@ -125,7 +125,17 @@ pub(super) fn try_handle_map_method<'ctx, 'b>(
|
||||
.emit_instr(cur_bid, |b| b.build_call(callee, &[recv_h.into(), kh.into()], "mget_hh"))
|
||||
.map_err(|e| e.to_string())?
|
||||
}
|
||||
_ => return Err("map.get key must be int or pointer".to_string()),
|
||||
_ => {
|
||||
let iv = resolver.resolve_i64(codegen, cursor, cur_bid, args[0], &std::collections::HashMap::new(), &std::collections::HashMap::new(), &std::collections::HashMap::new(), vmap)?;
|
||||
let fnty = i64t.fn_type(&[i64t.into(), i64t.into()], false);
|
||||
let callee = codegen
|
||||
.module
|
||||
.get_function("nyash.map.get_h")
|
||||
.unwrap_or_else(|| codegen.module.add_function("nyash.map.get_h", fnty, None));
|
||||
cursor
|
||||
.emit_instr(cur_bid, |b| b.build_call(callee, &[recv_h.into(), iv.into()], "mget"))
|
||||
.map_err(|e| e.to_string())?
|
||||
}
|
||||
};
|
||||
if let Some(d) = dst {
|
||||
let rv = call
|
||||
@ -143,16 +153,22 @@ pub(super) fn try_handle_map_method<'ctx, 'b>(
|
||||
if args.len() != 2 {
|
||||
return Err("MapBox.set expects 2 args (key, value)".to_string());
|
||||
}
|
||||
let key_v = *vmap.get(&args[0]).ok_or("map.set key missing")?;
|
||||
let val_v = *vmap.get(&args[1]).ok_or("map.set value missing")?;
|
||||
let key_i = match key_v {
|
||||
BVE::IntValue(_) | BVE::PointerValue(_) => resolver.resolve_i64(codegen, cursor, cur_bid, args[0], &std::collections::HashMap::new(), &std::collections::HashMap::new(), &std::collections::HashMap::new(), vmap).map_err(|e| e.to_string())?,
|
||||
_ => return Err("map.set key must be int or handle ptr".to_string()),
|
||||
};
|
||||
let val_i = match val_v {
|
||||
BVE::IntValue(_) | BVE::PointerValue(_) => resolver.resolve_i64(codegen, cursor, cur_bid, args[1], &std::collections::HashMap::new(), &std::collections::HashMap::new(), &std::collections::HashMap::new(), vmap).map_err(|e| e.to_string())?,
|
||||
_ => return Err("map.set value must be int or handle ptr".to_string()),
|
||||
let key_i = match func.metadata.value_types.get(&args[0]) {
|
||||
Some(crate::mir::MirType::String) => {
|
||||
let pv = resolver.resolve_ptr(codegen, cursor, cur_bid, args[0], &std::collections::HashMap::new(), &std::collections::HashMap::new(), &std::collections::HashMap::new(), vmap)?;
|
||||
let fnty_conv = i64t.fn_type(&[codegen.context.ptr_type(AddressSpace::from(0)).into()], false);
|
||||
let conv = codegen
|
||||
.module
|
||||
.get_function("nyash.box.from_i8_string")
|
||||
.unwrap_or_else(|| codegen.module.add_function("nyash.box.from_i8_string", fnty_conv, None));
|
||||
let kcall = cursor
|
||||
.emit_instr(cur_bid, |b| b.build_call(conv, &[pv.into()], "key_i8_to_handle"))
|
||||
.map_err(|e| e.to_string())?;
|
||||
kcall.try_as_basic_value().left().ok_or("from_i8_string returned void".to_string())?.into_int_value()
|
||||
}
|
||||
_ => resolver.resolve_i64(codegen, cursor, cur_bid, args[0], &std::collections::HashMap::new(), &std::collections::HashMap::new(), &std::collections::HashMap::new(), vmap)?
|
||||
};
|
||||
let val_i = resolver.resolve_i64(codegen, cursor, cur_bid, args[1], &std::collections::HashMap::new(), &std::collections::HashMap::new(), &std::collections::HashMap::new(), vmap)?;
|
||||
let fnty = i64t.fn_type(&[i64t.into(), i64t.into(), i64t.into()], false);
|
||||
let callee = codegen
|
||||
.module
|
||||
|
||||
@ -58,44 +58,15 @@ impl<'ctx> Resolver<'ctx> {
|
||||
if let Some(pv) = self.ptr_locals.get(&(cur_bid, vid)).copied() {
|
||||
return Ok(pv);
|
||||
}
|
||||
// Avoid using current vmap directly to keep dominance safe under multiple predecessors.
|
||||
// Strategy: localize as i64 (dominance-safe PHI), then convert to i8* in current block.
|
||||
let i8p = codegen.context.ptr_type(inkwell::AddressSpace::from(0));
|
||||
let cur_llbb = *bb_map.get(&cur_bid).ok_or("cur bb missing")?;
|
||||
let pred_list = preds.get(&cur_bid).cloned().unwrap_or_default();
|
||||
// Insert PHI at block start
|
||||
let saved_ip = codegen.builder.get_insert_block();
|
||||
if let Some(first) = cur_llbb.get_first_instruction() { codegen.builder.position_before(&first); }
|
||||
else { codegen.builder.position_at_end(cur_llbb); }
|
||||
let phi = codegen.builder.build_phi(i8p, &format!("loc_p_{}", vid.as_u32())).map_err(|e| e.to_string())?;
|
||||
if pred_list.is_empty() {
|
||||
// Entry-like block: derive from vmap or zero
|
||||
let base = vmap.get(&vid).copied().unwrap_or_else(|| i8p.const_zero().into());
|
||||
let coerced = match base {
|
||||
BVE::PointerValue(pv) => pv,
|
||||
BVE::IntValue(iv) => cursor.emit_instr(cur_bid, |b| b.build_int_to_ptr(iv, i8p, "loc_i2p")).map_err(|e| e.to_string())?,
|
||||
BVE::FloatValue(_) => i8p.const_zero(),
|
||||
_ => i8p.const_zero(),
|
||||
};
|
||||
phi.add_incoming(&[(&coerced, cur_llbb)]);
|
||||
} else {
|
||||
for p in &pred_list {
|
||||
let pred_bb = *bb_map.get(p).ok_or("pred bb missing")?;
|
||||
let base = block_end_values
|
||||
.get(p)
|
||||
.and_then(|m| m.get(&vid).copied())
|
||||
.unwrap_or_else(|| i8p.const_zero().into());
|
||||
let coerced = match base {
|
||||
BVE::PointerValue(pv) => pv,
|
||||
BVE::IntValue(iv) => codegen.builder.build_int_to_ptr(iv, i8p, "loc_i2p_p").map_err(|e| e.to_string())?,
|
||||
BVE::FloatValue(_) => i8p.const_zero(),
|
||||
_ => i8p.const_zero(),
|
||||
};
|
||||
phi.add_incoming(&[(&coerced, pred_bb)]);
|
||||
}
|
||||
}
|
||||
if let Some(bb) = saved_ip { codegen.builder.position_at_end(bb); }
|
||||
let out = phi.as_basic_value().into_pointer_value();
|
||||
self.ptr_locals.insert((cur_bid, vid), out);
|
||||
Ok(out)
|
||||
let iv = localize_to_i64(codegen, cursor, cur_bid, vid, bb_map, preds, block_end_values, vmap)?;
|
||||
let pv = cursor
|
||||
.emit_instr(cur_bid, |b| b.build_int_to_ptr(iv, i8p, "loc_i2p_dom"))
|
||||
.map_err(|e| e.to_string())?;
|
||||
self.ptr_locals.insert((cur_bid, vid), pv);
|
||||
Ok(pv)
|
||||
}
|
||||
|
||||
/// Resolve a MIR value as an f64 dominating the current block.
|
||||
@ -113,6 +84,7 @@ impl<'ctx> Resolver<'ctx> {
|
||||
if let Some(fv) = self.f64_locals.get(&(cur_bid, vid)).copied() {
|
||||
return Ok(fv);
|
||||
}
|
||||
// Avoid using current vmap directly to keep dominance safe under multiple predecessors.
|
||||
let f64t = codegen.context.f64_type();
|
||||
let cur_llbb = *bb_map.get(&cur_bid).ok_or("cur bb missing")?;
|
||||
let pred_list = preds.get(&cur_bid).cloned().unwrap_or_default();
|
||||
@ -121,14 +93,9 @@ impl<'ctx> Resolver<'ctx> {
|
||||
else { codegen.builder.position_at_end(cur_llbb); }
|
||||
let phi = codegen.builder.build_phi(f64t, &format!("loc_f64_{}", vid.as_u32())).map_err(|e| e.to_string())?;
|
||||
if pred_list.is_empty() {
|
||||
let base = vmap.get(&vid).copied().unwrap_or_else(|| f64t.const_zero().into());
|
||||
let coerced = match base {
|
||||
BVE::FloatValue(fv) => fv,
|
||||
BVE::IntValue(iv) => codegen.builder.build_signed_int_to_float(iv, f64t, "loc_i2f").map_err(|e| e.to_string())?,
|
||||
BVE::PointerValue(_) => f64t.const_zero(),
|
||||
_ => f64t.const_zero(),
|
||||
};
|
||||
phi.add_incoming(&[(&coerced, cur_llbb)]);
|
||||
// No predecessor: conservatively zero(vmap には依存しない)
|
||||
let z = f64t.const_zero();
|
||||
phi.add_incoming(&[(&z, cur_llbb)]);
|
||||
} else {
|
||||
for p in &pred_list {
|
||||
let pred_bb = *bb_map.get(p).ok_or("pred bb missing")?;
|
||||
@ -136,12 +103,17 @@ impl<'ctx> Resolver<'ctx> {
|
||||
.get(p)
|
||||
.and_then(|m| m.get(&vid).copied())
|
||||
.unwrap_or_else(|| f64t.const_zero().into());
|
||||
let coerced = match base {
|
||||
let mut coerced = f64t.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); }
|
||||
coerced = match base {
|
||||
BVE::FloatValue(fv) => fv,
|
||||
BVE::IntValue(iv) => codegen.builder.build_signed_int_to_float(iv, f64t, "loc_i2f_p").map_err(|e| e.to_string())?,
|
||||
BVE::IntValue(iv) => codegen.builder.build_signed_int_to_float(iv, f64t, "loc_i2f_p").map_err(|e| e.to_string()).unwrap(),
|
||||
BVE::PointerValue(_) => f64t.const_zero(),
|
||||
_ => f64t.const_zero(),
|
||||
};
|
||||
});
|
||||
phi.add_incoming(&[(&coerced, pred_bb)]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,18 +19,17 @@ pub(super) fn try_handle_string_method<'ctx, 'b>(
|
||||
box_val: &ValueId,
|
||||
method: &str,
|
||||
args: &[ValueId],
|
||||
recv_v: BVE<'ctx>,
|
||||
bb_map: &std::collections::HashMap<crate::mir::BasicBlockId, inkwell::basic_block::BasicBlock<'ctx>>,
|
||||
preds: &std::collections::HashMap<crate::mir::BasicBlockId, Vec<crate::mir::BasicBlockId>>,
|
||||
block_end_values: &std::collections::HashMap<crate::mir::BasicBlockId, std::collections::HashMap<ValueId, inkwell::values::BasicValueEnum<'ctx>>>,
|
||||
) -> Result<bool, String> {
|
||||
// Act if receiver is annotated as String/StringBox, or if the actual value is an i8* (string literal path)
|
||||
let is_string_recv = match func.metadata.value_types.get(box_val) {
|
||||
// Receiver annotation check (kept for future diagnostics)
|
||||
let _is_string_recv = match func.metadata.value_types.get(box_val) {
|
||||
Some(crate::mir::MirType::String) => true,
|
||||
Some(crate::mir::MirType::Box(b)) if b == "StringBox" => true,
|
||||
_ => matches!(recv_v, BVE::PointerValue(_)),
|
||||
_ => false,
|
||||
};
|
||||
// Do not early-return; allow method-specific checks below to validate types
|
||||
// Do not early-return; method-specific checksで型検証を行う
|
||||
|
||||
// concat fast-paths
|
||||
if method == "concat" {
|
||||
@ -38,8 +37,20 @@ pub(super) fn try_handle_string_method<'ctx, 'b>(
|
||||
return Err("String.concat expects 1 arg".to_string());
|
||||
}
|
||||
let i8p = codegen.context.ptr_type(AddressSpace::from(0));
|
||||
let rhs_v = *vmap.get(&args[0]).ok_or("concat arg missing")?;
|
||||
match (recv_v, rhs_v) {
|
||||
// Resolve rhs as either pointer (string) or i64 (handle/int)
|
||||
let rhs_val = match func.metadata.value_types.get(&args[0]) {
|
||||
Some(crate::mir::MirType::String) => {
|
||||
let p = resolver.resolve_ptr(codegen, cursor, cur_bid, args[0], bb_map, preds, block_end_values, vmap)?;
|
||||
BVE::PointerValue(p)
|
||||
}
|
||||
_ => {
|
||||
// Default to integer form for non-String metadata
|
||||
let iv = resolver.resolve_i64(codegen, cursor, cur_bid, args[0], bb_map, preds, block_end_values, vmap)?;
|
||||
BVE::IntValue(iv)
|
||||
}
|
||||
};
|
||||
let lp = resolver.resolve_ptr(codegen, cursor, cur_bid, *box_val, bb_map, preds, block_end_values, vmap)?;
|
||||
match (BVE::PointerValue(lp), rhs_val) {
|
||||
(BVE::PointerValue(lp), BVE::PointerValue(rp)) => {
|
||||
let fnty = i8p.fn_type(&[i8p.into(), i8p.into()], false);
|
||||
let callee = codegen
|
||||
@ -55,7 +66,12 @@ pub(super) fn try_handle_string_method<'ctx, 'b>(
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("concat_ss returned void".to_string())?;
|
||||
vmap.insert(*d, rv);
|
||||
// return as handle (i64) across blocks
|
||||
let i64t = codegen.context.i64_type();
|
||||
let h = cursor
|
||||
.emit_instr(cur_bid, |b| b.build_ptr_to_int(rv.into_pointer_value(), i64t, "str_ptr2i"))
|
||||
.map_err(|e| e.to_string())?;
|
||||
vmap.insert(*d, h.into());
|
||||
}
|
||||
return Ok(true);
|
||||
}
|
||||
@ -77,11 +93,15 @@ pub(super) fn try_handle_string_method<'ctx, 'b>(
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("concat_si returned void".to_string())?;
|
||||
vmap.insert(*d, rv);
|
||||
let i64t = codegen.context.i64_type();
|
||||
let h = cursor
|
||||
.emit_instr(cur_bid, |b| b.build_ptr_to_int(rv.into_pointer_value(), i64t, "str_ptr2i"))
|
||||
.map_err(|e| e.to_string())?;
|
||||
vmap.insert(*d, h.into());
|
||||
}
|
||||
return Ok(true);
|
||||
}
|
||||
(BVE::IntValue(_li), BVE::PointerValue(rp)) => {
|
||||
(BVE::PointerValue(_li_as_p), BVE::PointerValue(rp)) => {
|
||||
let i64t = codegen.context.i64_type();
|
||||
// Localize receiver integer in current block (box_val)
|
||||
let li = resolver.resolve_i64(codegen, cursor, cur_bid, *box_val, bb_map, preds, block_end_values, vmap)?;
|
||||
@ -99,7 +119,11 @@ pub(super) fn try_handle_string_method<'ctx, 'b>(
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("concat_is returned void".to_string())?;
|
||||
vmap.insert(*d, rv);
|
||||
let i64t = codegen.context.i64_type();
|
||||
let h = cursor
|
||||
.emit_instr(cur_bid, |b| b.build_ptr_to_int(rv.into_pointer_value(), i64t, "str_ptr2i"))
|
||||
.map_err(|e| e.to_string())?;
|
||||
vmap.insert(*d, h.into());
|
||||
}
|
||||
return Ok(true);
|
||||
}
|
||||
@ -111,9 +135,11 @@ pub(super) fn try_handle_string_method<'ctx, 'b>(
|
||||
if method == "length" || method == "len" {
|
||||
let i64t = codegen.context.i64_type();
|
||||
// Ensure handle for receiver (i8* -> i64 via from_i8_string)
|
||||
let recv_h = match recv_v {
|
||||
BVE::IntValue(h) => h,
|
||||
BVE::PointerValue(p) => {
|
||||
let recv_h = {
|
||||
// Prefer i64 handle from resolver; if metadata says String but actual is i8*, box it
|
||||
if let Some(crate::mir::MirType::String) = func.metadata.value_types.get(box_val) {
|
||||
// Receiver is a String: resolve pointer then box to i64
|
||||
let p = resolver.resolve_ptr(codegen, cursor, cur_bid, *box_val, bb_map, preds, block_end_values, vmap)?;
|
||||
let fnty = i64t.fn_type(&[codegen.context.ptr_type(AddressSpace::from(0)).into()], false);
|
||||
let callee = codegen
|
||||
.module
|
||||
@ -123,18 +149,14 @@ pub(super) fn try_handle_string_method<'ctx, 'b>(
|
||||
.emit_instr(cur_bid, |b| b
|
||||
.build_call(callee, &[p.into()], "str_ptr_to_handle"))
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let rv = call
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("from_i8_string returned void".to_string())?;
|
||||
if let BVE::IntValue(iv) = rv {
|
||||
iv
|
||||
if let BVE::IntValue(iv) = rv { iv } else { return Err("from_i8_string ret expected i64".to_string()); }
|
||||
} else {
|
||||
return Err("from_i8_string ret expected i64".to_string());
|
||||
resolver.resolve_i64(codegen, cursor, cur_bid, *box_val, bb_map, preds, block_end_values, vmap)?
|
||||
}
|
||||
}
|
||||
_ => return Err("String.length receiver type unsupported".to_string()),
|
||||
};
|
||||
// call i64 @nyash.string.len_h(i64)
|
||||
let fnty = i64t.fn_type(&[i64t.into()], false);
|
||||
@ -163,15 +185,8 @@ pub(super) fn try_handle_string_method<'ctx, 'b>(
|
||||
}
|
||||
let i64t = codegen.context.i64_type();
|
||||
let i8p = codegen.context.ptr_type(AddressSpace::from(0));
|
||||
// receiver preferably i8*; if it's a handle (i64), conservatively cast to i8*
|
||||
let recv_p = match recv_v {
|
||||
BVE::PointerValue(p) => p,
|
||||
BVE::IntValue(iv) => cursor
|
||||
.emit_instr(cur_bid, |b| b
|
||||
.build_int_to_ptr(iv, codegen.context.ptr_type(AddressSpace::from(0)), "str_h2p_sub"))
|
||||
.map_err(|e| e.to_string())?,
|
||||
_ => return Ok(false),
|
||||
};
|
||||
// receiver pointer via Resolver
|
||||
let recv_p = resolver.resolve_ptr(codegen, cursor, cur_bid, *box_val, bb_map, preds, block_end_values, vmap)?;
|
||||
// Localize start/end indices to current block via sealed snapshots (i64)
|
||||
let s = resolver.resolve_i64(codegen, cursor, cur_bid, args[0], bb_map, preds, block_end_values, vmap)?;
|
||||
let e = resolver.resolve_i64(codegen, cursor, cur_bid, args[1], bb_map, preds, block_end_values, vmap)?;
|
||||
@ -189,7 +204,11 @@ pub(super) fn try_handle_string_method<'ctx, 'b>(
|
||||
.try_as_basic_value()
|
||||
.left()
|
||||
.ok_or("substring returned void".to_string())?;
|
||||
vmap.insert(*d, rv);
|
||||
let i64t = codegen.context.i64_type();
|
||||
let h = cursor
|
||||
.emit_instr(cur_bid, |b| b.build_ptr_to_int(rv.into_pointer_value(), i64t, "str_ptr2i_sub"))
|
||||
.map_err(|e| e.to_string())?;
|
||||
vmap.insert(*d, h.into());
|
||||
}
|
||||
return Ok(true);
|
||||
}
|
||||
@ -201,16 +220,10 @@ pub(super) fn try_handle_string_method<'ctx, 'b>(
|
||||
}
|
||||
let i64t = codegen.context.i64_type();
|
||||
let i8p = codegen.context.ptr_type(AddressSpace::from(0));
|
||||
// receiver must be i8* for this fast path
|
||||
let recv_p = match recv_v {
|
||||
BVE::PointerValue(p) => p,
|
||||
_ => return Ok(false),
|
||||
};
|
||||
let a0 = *vmap.get(&args[0]).ok_or("lastIndexOf arg missing")?;
|
||||
let needle_p = match a0 {
|
||||
BVE::PointerValue(p) => p,
|
||||
_ => return Err("lastIndexOf arg must be i8*".to_string()),
|
||||
};
|
||||
// receiver pointer via Resolver (String fast path)
|
||||
let recv_p = resolver.resolve_ptr(codegen, cursor, cur_bid, *box_val, bb_map, preds, block_end_values, vmap)?;
|
||||
let needle_p = resolver
|
||||
.resolve_ptr(codegen, cursor, cur_bid, args[0], bb_map, preds, block_end_values, vmap)?;
|
||||
let fnty = i64t.fn_type(&[i8p.into(), i8p.into()], false);
|
||||
let callee = codegen
|
||||
.module
|
||||
|
||||
@ -356,7 +356,20 @@ impl LLVMCompiler {
|
||||
if let Some(d) = dst { defined_in_block.insert(*d); }
|
||||
},
|
||||
MirInstruction::UnaryOp { dst, op, operand } => {
|
||||
instructions::lower_unary(&codegen, &mut cursor, *bid, &mut vmap, *dst, op, operand)?;
|
||||
instructions::lower_unary(
|
||||
&codegen,
|
||||
&mut cursor,
|
||||
&mut resolver,
|
||||
*bid,
|
||||
func,
|
||||
&mut vmap,
|
||||
*dst,
|
||||
op,
|
||||
operand,
|
||||
&bb_map,
|
||||
&preds,
|
||||
&block_end_values,
|
||||
)?;
|
||||
defined_in_block.insert(*dst);
|
||||
},
|
||||
MirInstruction::BinOp { dst, op, lhs, rhs } => {
|
||||
@ -407,7 +420,18 @@ impl LLVMCompiler {
|
||||
cursor.at_end(*bid, bb);
|
||||
match term {
|
||||
MirInstruction::Return { value } => {
|
||||
instructions::emit_return(&codegen, &mut cursor, *bid, func, &vmap, value)?;
|
||||
instructions::emit_return(
|
||||
&codegen,
|
||||
&mut cursor,
|
||||
&mut resolver,
|
||||
*bid,
|
||||
func,
|
||||
&vmap,
|
||||
value,
|
||||
&bb_map,
|
||||
&preds,
|
||||
&block_end_values,
|
||||
)?;
|
||||
}
|
||||
MirInstruction::Jump { target } => {
|
||||
// LoopForm simple body→dispatch wiring: if this block is a loop body
|
||||
@ -480,6 +504,7 @@ impl LLVMCompiler {
|
||||
handled_by_loopform = instructions::lower_while_loopform(
|
||||
&codegen,
|
||||
&mut cursor,
|
||||
&mut resolver,
|
||||
func,
|
||||
llvm_func,
|
||||
condition,
|
||||
@ -491,6 +516,8 @@ impl LLVMCompiler {
|
||||
after_sel,
|
||||
&bb_map,
|
||||
&vmap,
|
||||
&preds,
|
||||
&block_end_values,
|
||||
&mut loopform_registry,
|
||||
&mut loopform_body_to_header,
|
||||
)?;
|
||||
|
||||
Reference in New Issue
Block a user