diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index 09fe0a69..d03df19f 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -24,6 +24,15 @@ What Changed (today) - dev プロファイル `tools/dev_env.sh phi_off` を追加。ルート清掃ユーティリティ `tools/clean_root_artifacts.sh` を追加。 - CI(GH Actions)を curated LLVM(PHI‑on/off)実行に刷新。旧JITジョブは停止。 +Self‑Hosting plumbing (2025‑09‑16, later in day) +- Runner: 自己ホスト経路で子プログラム(`apps/selfhost-compiler/compiler.nyash`)を優先実行し、`--read-tmp` 常時付与で安定運用に変更。 +- PyVM 優先の統一(`NYASH_VM_USE_PY=1`)は EXE/inline/child の全分岐で尊重。 +- CLI: `--stage3` を追加(`NYASH_NY_COMPILER_STAGE3=1` を設定)。 +- Script args パススルー(B案)実装: `nyash ... FILE -- arg1 arg2` → `NYASH_SCRIPT_ARGS_JSON=["arg1","arg2"]` として `Main.main(args)` に ArrayBox で注入。 + - Interpreter: `ArrayBox.of(...)` を追加して初期配列を簡潔に構築。 + - Runner: Python MVP は `NYASH_NY_COMPILER_SKIP_PY=1` でスキップ可能(自己ホスト子を優先)。 + + Decision (Phase‑15 wrap‑up) - MIR13 移行(PHI 非生成): Phase‑15 の締めとして、MIR 生成層(Bridge/Builder)は PHI を生成しない方針に切替。PHI 合成は LLVM 層(llvmlite/Resolver)に集約。 - LoopForm は次フェーズ(MIR18)で導入: まずは MIR14 を維持し、次フェーズで `LoopHeader/Enter/Latch` 等の占位命令を追加。現行 Phase‑15 は CFG パターン検知でループ搬送値を合成。 @@ -36,6 +45,22 @@ Next Focus (Throw/Try — LLVM first) - LLVM 実装方針: Throw/Try の MIR 命令を LLVM 側がどう扱うか(panic扱い or fallback)を設計し、smoke 更新案を作る(現状 Throw は trap/unreachable 最小降ろし完了)。 - テスト計画: JSON フィクスチャと `tools/llvm_smoke.sh` を中心に Stage-3 例外用のスモーク/単体テストを整備。 +Open Items (handoff) +- Selfhost Stage‑3 E2E smoke(Runner→子→JSON→Bridge)の最終緑化 + - 現状: 子へ `--read-tmp` は付与済み。`--stage3` は env→子へ透過済み。 + - 直近 TODO: tools/selfhost_stage3_accept_smoke.sh を Runner 優先経路に再調整し、`NYASH_NY_COMPILER_SKIP_PY=1` 併用で安定化。 + - 期待値: try/finally/break/continue/throw(accept) で exit=0(degrade 経路) +- CLI argv 前処理の挙動監視 + - `--` なしの通常実行での Positional FILE 受理を再検証(Clapの警告再現時は build_command の宣言順/Arg要件を再点検)。 +- LLVM: Throw 実投げの終了コード方針を決定し、trap on/off の期待値を固定化。 + +Next Steps (suggested order) +1) Selfhost Stage‑3 E2E 緑化(smoke 修正 → 確認) +2) CLI FILE positional の回帰があれば即修正(Clap設定の微調整) +3) LLVM Throw 実投げの設計(終了コード/シグナル)+スモーク +4) Runner 実装の集約(modes/common → selfhost.rs)と微ノイズ削減 +5) CI に Selfhost Stage‑2/E2E(軽量)をオプションで追加 + ※ Cranelift/JIT 系は当面対象外。ビルド時も LLVM のみを有効化(JIT 関連 feature/CI は無視)。 Runner updates (2025‑09‑16) diff --git a/src/backend/llvm/compiler/codegen/function.rs b/src/backend/llvm/compiler/codegen/function.rs index 1335a838..cd93f58e 100644 --- a/src/backend/llvm/compiler/codegen/function.rs +++ b/src/backend/llvm/compiler/codegen/function.rs @@ -10,7 +10,8 @@ pub(super) fn lower_one_function<'ctx>( ) -> Result<(), String> { // Create basic blocks (prefix names with function label to avoid any ambiguity) let fn_label = sanitize_symbol(name); - let (mut bb_map, entry_bb) = instructions::create_basic_blocks(codegen, llvm_func, func, &fn_label); + let (mut bb_map, entry_bb) = + instructions::create_basic_blocks(codegen, llvm_func, func, &fn_label); let mut cursor = instructions::builder_cursor::BuilderCursor::new(&codegen.builder); cursor.at_end(func.entry_block, entry_bb); let mut vmap: HashMap = HashMap::new(); @@ -23,19 +24,25 @@ pub(super) fn lower_one_function<'ctx>( Vec<(ValueId, PhiValue, Vec<(crate::mir::BasicBlockId, ValueId)>)>, > = HashMap::new(); // Snapshot of values at the end of each basic block (for sealed-SSA PHI wiring) - let mut block_end_values: HashMap> = HashMap::new(); + let mut block_end_values: HashMap> = + HashMap::new(); // Build successors and predecessors map (for optional sealed-SSA PHI wiring) - let mut succs: HashMap> = HashMap::new(); + let mut succs: HashMap> = + HashMap::new(); for (bid, block) in &func.blocks { let v: Vec = block.successors.iter().copied().collect(); succs.insert(*bid, v); } - let mut preds: HashMap> = HashMap::new(); + let mut preds: HashMap> = + HashMap::new(); for (b, ss) in &succs { - for s in ss { preds.entry(*s).or_default().push(*b); } + for s in ss { + preds.entry(*s).or_default().push(*b); + } } // Track sealed blocks to know when all preds of a successor are sealed - let mut sealed_blocks: std::collections::HashSet = std::collections::HashSet::new(); + let mut sealed_blocks: std::collections::HashSet = + std::collections::HashSet::new(); // Bind parameters for (i, pid) in func.params.iter().enumerate() { if let Some(av) = llvm_func.get_nth_param(i as u32) { @@ -105,8 +112,17 @@ pub(super) fn lower_one_function<'ctx>( // Default sealed-SSA ON unless explicitly disabled with NYASH_LLVM_PHI_SEALED=0 let sealed_mode = std::env::var("NYASH_LLVM_PHI_SEALED").ok().as_deref() != Some("0"); // LoopForm registry (per-function lowering; gated) - let mut loopform_registry: HashMap = HashMap::new(); - let mut loopform_body_to_header: HashMap = HashMap::new(); + let mut loopform_registry: HashMap< + crate::mir::BasicBlockId, + ( + inkwell::basic_block::BasicBlock, + PhiValue, + PhiValue, + inkwell::basic_block::BasicBlock, + ), + > = HashMap::new(); + let mut loopform_body_to_header: HashMap = + HashMap::new(); // Per-function Resolver for dominance-safe value access (i64 minimal) let mut resolver = instructions::Resolver::new(); for (bi, bid) in block_ids.iter().enumerate() { @@ -117,10 +133,15 @@ pub(super) fn lower_one_function<'ctx>( eprintln!("[LLVM] lowering bb={}", bid.as_u32()); } let block = func.blocks.get(bid).unwrap(); - let mut defined_in_block: std::collections::HashSet = std::collections::HashSet::new(); + let mut defined_in_block: std::collections::HashSet = + std::collections::HashSet::new(); for inst in &block.instructions { match inst { - MirInstruction::NewBox { dst, box_type, args } => { + MirInstruction::NewBox { + dst, + box_type, + args, + } => { instructions::lower_newbox( codegen, &mut cursor, @@ -136,42 +157,74 @@ pub(super) fn lower_one_function<'ctx>( &block_end_values, )?; defined_in_block.insert(*dst); - }, + } MirInstruction::Const { dst, value } => { let bval = match value { - ConstValue::Integer(i) => codegen.context.i64_type().const_int(*i as u64, true).into(), + ConstValue::Integer(i) => { + codegen.context.i64_type().const_int(*i as u64, true).into() + } ConstValue::Float(f) => codegen.context.f64_type().const_float(*f).into(), - ConstValue::Bool(b) => codegen.context.bool_type().const_int(*b as u64, false).into(), + ConstValue::Bool(b) => codegen + .context + .bool_type() + .const_int(*b as u64, false) + .into(), ConstValue::String(s) => { // Hoist string creation to entry block to dominate all uses. let entry_term = unsafe { entry_bb.get_terminator() }; - if let Some(t) = entry_term { entry_builder.position_before(&t); } - else { entry_builder.position_at_end(entry_bb); } + if let Some(t) = entry_term { + entry_builder.position_before(&t); + } else { + entry_builder.position_at_end(entry_bb); + } let gv = entry_builder .build_global_string_ptr(s, "str") .map_err(|e| e.to_string())?; let len = codegen.context.i32_type().const_int(s.len() as u64, false); let rt = codegen.context.ptr_type(inkwell::AddressSpace::from(0)); - let fn_ty = rt.fn_type(&[ - codegen.context.ptr_type(inkwell::AddressSpace::from(0)).into(), - codegen.context.i32_type().into(), - ], false); + let fn_ty = rt.fn_type( + &[ + codegen + .context + .ptr_type(inkwell::AddressSpace::from(0)) + .into(), + codegen.context.i32_type().into(), + ], + false, + ); let callee = codegen .module .get_function("nyash_string_new") - .unwrap_or_else(|| codegen.module.add_function("nyash_string_new", fn_ty, None)); + .unwrap_or_else(|| { + codegen.module.add_function("nyash_string_new", fn_ty, None) + }); let call = entry_builder - .build_call(callee, &[gv.as_pointer_value().into(), len.into()], "strnew") + .build_call( + callee, + &[gv.as_pointer_value().into(), len.into()], + "strnew", + ) .map_err(|e| e.to_string())?; - call.try_as_basic_value().left().ok_or("nyash_string_new returned void".to_string())? + call.try_as_basic_value() + .left() + .ok_or("nyash_string_new returned void".to_string())? } - ConstValue::Null => codegen.context.ptr_type(inkwell::AddressSpace::from(0)).const_zero().into(), + ConstValue::Null => codegen + .context + .ptr_type(inkwell::AddressSpace::from(0)) + .const_zero() + .into(), ConstValue::Void => codegen.context.i64_type().const_zero().into(), }; vmap.insert(*dst, bval); defined_in_block.insert(*dst); - }, - MirInstruction::Call { dst, func: callee, args, .. } => { + } + MirInstruction::Call { + dst, + func: callee, + args, + .. + } => { instructions::lower_call( codegen, &mut cursor, @@ -188,9 +241,18 @@ pub(super) fn lower_one_function<'ctx>( &preds, &block_end_values, )?; - if let Some(d) = dst { defined_in_block.insert(*d); } - }, - MirInstruction::BoxCall { dst, box_val, method, method_id, args, effects: _ } => { + if let Some(d) = dst { + defined_in_block.insert(*d); + } + } + MirInstruction::BoxCall { + dst, + box_val, + method, + method_id, + args, + effects: _, + } => { instructions::lower_boxcall( codegen, &mut cursor, @@ -209,9 +271,17 @@ pub(super) fn lower_one_function<'ctx>( &preds, &block_end_values, )?; - if let Some(d) = dst { defined_in_block.insert(*d); } - }, - MirInstruction::ExternCall { dst, iface_name, method_name, args, effects: _ } => { + if let Some(d) = dst { + defined_in_block.insert(*d); + } + } + MirInstruction::ExternCall { + dst, + iface_name, + method_name, + args, + effects: _, + } => { instructions::lower_externcall( codegen, &mut cursor, @@ -227,8 +297,10 @@ pub(super) fn lower_one_function<'ctx>( &preds, &block_end_values, )?; - if let Some(d) = dst { defined_in_block.insert(*d); } - }, + if let Some(d) = dst { + defined_in_block.insert(*d); + } + } MirInstruction::UnaryOp { dst, op, operand } => { instructions::lower_unary( codegen, @@ -245,16 +317,43 @@ pub(super) fn lower_one_function<'ctx>( &block_end_values, )?; defined_in_block.insert(*dst); - }, + } MirInstruction::BinOp { dst, op, lhs, rhs } => { - instructions::lower_binop(codegen, &mut cursor, &mut resolver, *bid, func, &mut vmap, *dst, op, lhs, rhs, &bb_map, &preds, &block_end_values)?; + instructions::lower_binop( + codegen, + &mut cursor, + &mut resolver, + *bid, + func, + &mut vmap, + *dst, + op, + lhs, + rhs, + &bb_map, + &preds, + &block_end_values, + )?; defined_in_block.insert(*dst); - }, + } MirInstruction::Compare { dst, op, lhs, rhs } => { - let out = instructions::lower_compare(codegen, &mut cursor, &mut resolver, *bid, func, &vmap, op, lhs, rhs, &bb_map, &preds, &block_end_values)?; + let out = instructions::lower_compare( + codegen, + &mut cursor, + &mut resolver, + *bid, + func, + &vmap, + op, + lhs, + rhs, + &bb_map, + &preds, + &block_end_values, + )?; vmap.insert(*dst, out); defined_in_block.insert(*dst); - }, + } MirInstruction::Store { value, ptr } => { instructions::lower_store( codegen, @@ -270,17 +369,30 @@ pub(super) fn lower_one_function<'ctx>( &preds, &block_end_values, )?; - }, + } MirInstruction::Load { dst, ptr } => { - instructions::lower_load(codegen, &mut cursor, *bid, &mut vmap, &mut allocas, &mut alloca_elem_types, dst, ptr)?; + instructions::lower_load( + codegen, + &mut cursor, + *bid, + &mut vmap, + &mut allocas, + &mut alloca_elem_types, + dst, + ptr, + )?; defined_in_block.insert(*dst); - }, + } MirInstruction::Phi { .. } => { /* precreated */ } _ => { /* ignore others */ } } // Snapshot end-of-block values let mut snap: HashMap = HashMap::new(); - for vid in &defined_in_block { if let Some(v) = vmap.get(vid).copied() { snap.insert(*vid, v); } } + for vid in &defined_in_block { + if let Some(v) = vmap.get(vid).copied() { + snap.insert(*vid, v); + } + } block_end_values.insert(*bid, snap); } // Terminator handling @@ -288,42 +400,85 @@ pub(super) fn lower_one_function<'ctx>( cursor.at_end(*bid, bb); match term { MirInstruction::Return { value } => { - instructions::emit_return(codegen, &mut cursor, &mut resolver, *bid, func, &vmap, value, &bb_map, &preds, &block_end_values)?; + instructions::emit_return( + codegen, + &mut cursor, + &mut resolver, + *bid, + func, + &vmap, + value, + &bb_map, + &preds, + &block_end_values, + )?; } MirInstruction::Jump { target } => { let mut handled = false; - if std::env::var("NYASH_ENABLE_LOOPFORM").ok().as_deref() == Some("1") && - std::env::var("NYASH_LOOPFORM_BODY2DISPATCH").ok().as_deref() == Some("1") { + if std::env::var("NYASH_ENABLE_LOOPFORM").ok().as_deref() == Some("1") + && std::env::var("NYASH_LOOPFORM_BODY2DISPATCH") + .ok() + .as_deref() + == Some("1") + { if let Some(hdr) = loopform_body_to_header.get(bid) { if hdr == target { - if let Some((dispatch_bb, tag_phi, payload_phi, _latch_bb)) = loopform_registry.get(hdr) { + if let Some((dispatch_bb, tag_phi, payload_phi, _latch_bb)) = + loopform_registry.get(hdr) + { let i8t = codegen.context.i8_type(); let i64t = codegen.context.i64_type(); - let pred_llbb = *bb_map.get(bid).ok_or("loopform: body llbb missing")?; + let pred_llbb = + *bb_map.get(bid).ok_or("loopform: body llbb missing")?; let z = i8t.const_zero(); let pz = i64t.const_zero(); tag_phi.add_incoming(&[(&z, pred_llbb)]); payload_phi.add_incoming(&[(&pz, pred_llbb)]); - cursor.emit_term(*bid, |b| { b.build_unconditional_branch(*dispatch_bb).unwrap(); }); + cursor.emit_term(*bid, |b| { + b.build_unconditional_branch(*dispatch_bb).unwrap(); + }); handled = true; } } } } if !handled { - instructions::emit_jump(codegen, &mut cursor, *bid, target, &bb_map, &phis_by_block)?; + instructions::emit_jump( + codegen, + &mut cursor, + *bid, + target, + &bb_map, + &phis_by_block, + )?; } } - MirInstruction::Branch { condition, then_bb, else_bb } => { + MirInstruction::Branch { + condition, + then_bb, + else_bb, + } => { let mut handled_by_loopform = false; if std::env::var("NYASH_ENABLE_LOOPFORM").ok().as_deref() == Some("1") { let mut is_back = |start: crate::mir::BasicBlockId| -> u8 { if let Some(b) = func.blocks.get(&start) { - if let Some(crate::mir::instruction::MirInstruction::Jump { target }) = &b.terminator { - if target == bid { return 1; } + if let Some(crate::mir::instruction::MirInstruction::Jump { + target, + }) = &b.terminator + { + if target == bid { + return 1; + } if let Some(b2) = func.blocks.get(target) { - if let Some(crate::mir::instruction::MirInstruction::Jump { target: t2 }) = &b2.terminator { - if t2 == bid { return 2; } + if let Some( + crate::mir::instruction::MirInstruction::Jump { + target: t2, + }, + ) = &b2.terminator + { + if t2 == bid { + return 2; + } } } } @@ -332,73 +487,163 @@ pub(super) fn lower_one_function<'ctx>( }; let d_then = is_back(*then_bb); let d_else = is_back(*else_bb); - let choose_body = if d_then > 0 && d_else == 0 { Some((*then_bb, *else_bb)) } - else if d_else > 0 && d_then == 0 { Some((*else_bb, *then_bb)) } - else if d_then > 0 && d_else > 0 { if d_then <= d_else { Some((*then_bb, *else_bb)) } else { Some((*else_bb, *then_bb)) } } - else { None }; + let choose_body = if d_then > 0 && d_else == 0 { + Some((*then_bb, *else_bb)) + } else if d_else > 0 && d_then == 0 { + Some((*else_bb, *then_bb)) + } else if d_then > 0 && d_else > 0 { + if d_then <= d_else { + Some((*then_bb, *else_bb)) + } else { + Some((*else_bb, *then_bb)) + } + } else { + None + }; if let Some((body_sel, after_sel)) = choose_body { let body_block = func.blocks.get(&body_sel).unwrap(); handled_by_loopform = instructions::lower_while_loopform( - codegen, &mut cursor, &mut resolver, func, llvm_func, condition, &body_block.instructions, - loopform_loop_id, &fn_label, *bid, body_sel, after_sel, &bb_map, &vmap, &preds, &block_end_values, - &mut loopform_registry, &mut loopform_body_to_header, + codegen, + &mut cursor, + &mut resolver, + func, + llvm_func, + condition, + &body_block.instructions, + loopform_loop_id, + &fn_label, + *bid, + body_sel, + after_sel, + &bb_map, + &vmap, + &preds, + &block_end_values, + &mut loopform_registry, + &mut loopform_body_to_header, )?; loopform_loop_id = loopform_loop_id.wrapping_add(1); } } if !handled_by_loopform { - instructions::emit_branch(codegen, &mut cursor, &mut resolver, *bid, condition, then_bb, else_bb, &bb_map, &phis_by_block, &vmap, &preds, &block_end_values)?; + instructions::emit_branch( + codegen, + &mut cursor, + &mut resolver, + *bid, + condition, + then_bb, + else_bb, + &bb_map, + &phis_by_block, + &vmap, + &preds, + &block_end_values, + )?; } } _ => { cursor.at_end(*bid, bb); if let Some(next_bid) = block_ids.get(bi + 1) { - instructions::emit_jump(codegen, &mut cursor, *bid, next_bid, &bb_map, &phis_by_block)?; + instructions::emit_jump( + codegen, + &mut cursor, + *bid, + next_bid, + &bb_map, + &phis_by_block, + )?; } else { let entry_first = func.entry_block; - instructions::emit_jump(codegen, &mut cursor, *bid, &entry_first, &bb_map, &phis_by_block)?; + instructions::emit_jump( + codegen, + &mut cursor, + *bid, + &entry_first, + &bb_map, + &phis_by_block, + )?; } } } } else { if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("[LLVM] no terminator in MIR for bb={} (fallback)", bid.as_u32()); + eprintln!( + "[LLVM] no terminator in MIR for bb={} (fallback)", + bid.as_u32() + ); } cursor.at_end(*bid, bb); if let Some(next_bid) = block_ids.get(bi + 1) { - instructions::emit_jump(codegen, &mut cursor, *bid, next_bid, &bb_map, &phis_by_block)?; + instructions::emit_jump( + codegen, + &mut cursor, + *bid, + next_bid, + &bb_map, + &phis_by_block, + )?; } else { let entry_first = func.entry_block; - instructions::emit_jump(codegen, &mut cursor, *bid, &entry_first, &bb_map, &phis_by_block)?; + instructions::emit_jump( + codegen, + &mut cursor, + *bid, + &entry_first, + &bb_map, + &phis_by_block, + )?; } } if unsafe { bb.get_terminator() }.is_none() { if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("[LLVM] extra guard inserting fallback for bb={}", bid.as_u32()); + eprintln!( + "[LLVM] extra guard inserting fallback for bb={}", + bid.as_u32() + ); } cursor.at_end(*bid, bb); if let Some(next_bid) = block_ids.get(bi + 1) { - instructions::emit_jump(codegen, &mut cursor, *bid, next_bid, &bb_map, &phis_by_block)?; + instructions::emit_jump( + codegen, + &mut cursor, + *bid, + next_bid, + &bb_map, + &phis_by_block, + )?; } else { let entry_first = func.entry_block; - instructions::emit_jump(codegen, &mut cursor, *bid, &entry_first, &bb_map, &phis_by_block)?; + instructions::emit_jump( + codegen, + &mut cursor, + *bid, + &entry_first, + &bb_map, + &phis_by_block, + )?; } } if sealed_mode { - instructions::flow::seal_block(codegen, &mut cursor, func, *bid, &succs, &bb_map, &phis_by_block, &block_end_values)?; + instructions::flow::seal_block( + codegen, + &mut cursor, + func, + *bid, + &succs, + &bb_map, + &phis_by_block, + &block_end_values, + )?; sealed_blocks.insert(*bid); } } - if std::env::var("NYASH_ENABLE_LOOPFORM").ok().as_deref() == Some("1") && - std::env::var("NYASH_LOOPFORM_LATCH2HEADER").ok().as_deref() == Some("1") { + if std::env::var("NYASH_ENABLE_LOOPFORM").ok().as_deref() == Some("1") + && std::env::var("NYASH_LOOPFORM_LATCH2HEADER").ok().as_deref() == Some("1") + { for (hdr_bid, (_dispatch_bb, _tag_phi, _payload_phi, latch_bb)) in &loopform_registry { if let Some(phis) = phis_by_block.get(hdr_bid) { - instructions::normalize_header_phis_for_latch( - codegen, - *hdr_bid, - *latch_bb, - phis, - )?; + instructions::normalize_header_phis_for_latch(codegen, *hdr_bid, *latch_bb, phis)?; } } instructions::dev_check_dispatch_only_phi(&phis_by_block, &loopform_registry); diff --git a/src/backend/llvm/compiler/codegen/instructions/arith.rs b/src/backend/llvm/compiler/codegen/instructions/arith.rs index 582e59f8..576d5ef0 100644 --- a/src/backend/llvm/compiler/codegen/instructions/arith.rs +++ b/src/backend/llvm/compiler/codegen/instructions/arith.rs @@ -2,9 +2,9 @@ use std::collections::HashMap; use inkwell::values::BasicValueEnum; +use super::builder_cursor::BuilderCursor; use crate::backend::llvm::context::CodegenContext; use crate::mir::{function::MirFunction, BasicBlockId, CompareOp, ValueId}; -use super::builder_cursor::BuilderCursor; /// Compare lowering: return the resulting BasicValueEnum (i1) pub(in super::super) fn lower_compare<'ctx, 'b>( @@ -17,21 +17,93 @@ pub(in super::super) fn lower_compare<'ctx, 'b>( op: &CompareOp, lhs: &ValueId, rhs: &ValueId, - bb_map: &std::collections::HashMap>, + bb_map: &std::collections::HashMap< + crate::mir::BasicBlockId, + inkwell::basic_block::BasicBlock<'ctx>, + >, preds: &std::collections::HashMap>, - block_end_values: &std::collections::HashMap>>, + block_end_values: &std::collections::HashMap< + crate::mir::BasicBlockId, + std::collections::HashMap>, + >, ) -> Result, String> { use crate::backend::llvm::compiler::helpers::{as_float, as_int}; // 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(), + 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: 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(), + 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) { @@ -48,38 +120,67 @@ pub(in super::super) fn lower_compare<'ctx, 'b>( if l_is_str && r_is_str { let i64t = codegen.context.i64_type(); // Convert both sides to handles if needed - let mut to_handle = |v: BasicValueEnum<'ctx>| -> Result, String> { - match v { - BasicValueEnum::IntValue(iv) => { - if iv.get_type() == i64t { Ok(iv) } else { cursor.emit_instr(cur_bid, |b| b.build_int_s_extend(iv, i64t, "i2i64")).map_err(|e| e.to_string()) } + let mut to_handle = + |v: BasicValueEnum<'ctx>| -> Result, String> { + match v { + BasicValueEnum::IntValue(iv) => { + if iv.get_type() == i64t { + Ok(iv) + } else { + cursor + .emit_instr(cur_bid, |b| { + b.build_int_s_extend(iv, i64t, "i2i64") + }) + .map_err(|e| e.to_string()) + } + } + BasicValueEnum::PointerValue(pv) => { + let fnty = i64t.fn_type( + &[codegen + .context + .ptr_type(inkwell::AddressSpace::from(0)) + .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()], "str_ptr_to_handle_cmp") + }) + .map_err(|e| e.to_string())?; + let rv = call + .try_as_basic_value() + .left() + .ok_or("from_i8_string returned void".to_string())?; + Ok(rv.into_int_value()) + } + _ => Err("unsupported value for string compare".to_string()), } - BasicValueEnum::PointerValue(pv) => { - let fnty = i64t.fn_type(&[codegen.context.ptr_type(inkwell::AddressSpace::from(0)).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()], "str_ptr_to_handle_cmp")) - .map_err(|e| e.to_string())?; - let rv = call - .try_as_basic_value() - .left() - .ok_or("from_i8_string returned void".to_string())?; - Ok(rv.into_int_value()) - } - _ => Err("unsupported value for string compare".to_string()), - } - }; + }; let lh = to_handle(lv)?; let rh = to_handle(rv)?; let fnty = i64t.fn_type(&[i64t.into(), i64t.into()], false); let callee = codegen .module .get_function("nyash.string.eq_hh") - .unwrap_or_else(|| codegen.module.add_function("nyash.string.eq_hh", fnty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.string.eq_hh", fnty, None) + }); let call = cursor - .emit_instr(cur_bid, |b| b.build_call(callee, &[lh.into(), rh.into()], "str_eq_hh")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[lh.into(), rh.into()], "str_eq_hh") + }) .map_err(|e| e.to_string())?; let iv = call .try_as_basic_value() @@ -93,7 +194,9 @@ pub(in super::super) fn lower_compare<'ctx, 'b>( inkwell::IntPredicate::EQ }; let b = cursor - .emit_instr(cur_bid, |bd| bd.build_int_compare(pred, iv, zero, "str_eq_to_bool")) + .emit_instr(cur_bid, |bd| { + bd.build_int_compare(pred, iv, zero, "str_eq_to_bool") + }) .map_err(|e| e.to_string())?; return Ok(b.into()); } @@ -101,10 +204,28 @@ pub(in super::super) fn lower_compare<'ctx, 'b>( let out = if let (Some(_li0), Some(_ri0)) = (as_int(lv), as_int(rv)) { // Localize integer operands into current block to satisfy dominance let mut li = resolver - .resolve_i64(codegen, cursor, cur_bid, *lhs, bb_map, preds, block_end_values, vmap) + .resolve_i64( + codegen, + cursor, + cur_bid, + *lhs, + bb_map, + preds, + block_end_values, + vmap, + ) .unwrap_or_else(|_| as_int(lv).unwrap()); let mut ri = resolver - .resolve_i64(codegen, cursor, cur_bid, *rhs, bb_map, preds, block_end_values, vmap) + .resolve_i64( + codegen, + cursor, + cur_bid, + *rhs, + bb_map, + preds, + block_end_values, + vmap, + ) .unwrap_or_else(|_| as_int(rv).unwrap()); // Normalize integer widths: extend the narrower to match the wider to satisfy LLVM let lw = li.get_type().get_bit_width(); @@ -112,11 +233,15 @@ pub(in super::super) fn lower_compare<'ctx, 'b>( if lw != rw { if lw < rw { li = cursor - .emit_instr(cur_bid, |b| b.build_int_z_extend(li, ri.get_type(), "icmp_zext_l")) + .emit_instr(cur_bid, |b| { + b.build_int_z_extend(li, ri.get_type(), "icmp_zext_l") + }) .map_err(|e| e.to_string())?; } else { ri = cursor - .emit_instr(cur_bid, |b| b.build_int_z_extend(ri, li.get_type(), "icmp_zext_r")) + .emit_instr(cur_bid, |b| { + b.build_int_z_extend(ri, li.get_type(), "icmp_zext_r") + }) .map_err(|e| e.to_string())?; } } @@ -213,14 +338,21 @@ pub(in super::super) fn lower_compare<'ctx, 'b>( Ok(out) } -fn guessed_zero<'ctx>(codegen: &CodegenContext<'ctx>, func: &MirFunction, vid: &crate::mir::ValueId) -> BasicValueEnum<'ctx> { +fn guessed_zero<'ctx>( + codegen: &CodegenContext<'ctx>, + func: &MirFunction, + vid: &crate::mir::ValueId, +) -> BasicValueEnum<'ctx> { use crate::mir::MirType as MT; match func.metadata.value_types.get(vid) { Some(MT::Bool) => codegen.context.bool_type().const_zero().into(), Some(MT::Integer) => codegen.context.i64_type().const_zero().into(), Some(MT::Float) => codegen.context.f64_type().const_zero().into(), - Some(MT::String) | Some(MT::Box(_)) | Some(MT::Array(_)) | Some(MT::Future(_)) | Some(MT::Unknown) | Some(MT::Void) | None => { - codegen.context.ptr_type(inkwell::AddressSpace::from(0)).const_zero().into() - } + Some(MT::String) | Some(MT::Box(_)) | Some(MT::Array(_)) | Some(MT::Future(_)) + | Some(MT::Unknown) | Some(MT::Void) | None => codegen + .context + .ptr_type(inkwell::AddressSpace::from(0)) + .const_zero() + .into(), } } diff --git a/src/backend/llvm/compiler/codegen/instructions/arith_ops.rs b/src/backend/llvm/compiler/codegen/instructions/arith_ops.rs index d5a3fc89..103a63ac 100644 --- a/src/backend/llvm/compiler/codegen/instructions/arith_ops.rs +++ b/src/backend/llvm/compiler/codegen/instructions/arith_ops.rs @@ -1,11 +1,11 @@ use std::collections::HashMap; -use inkwell::{values::BasicValueEnum, AddressSpace}; use crate::backend::llvm::compiler::codegen::types; +use inkwell::{values::BasicValueEnum, AddressSpace}; +use super::builder_cursor::BuilderCursor; use crate::backend::llvm::context::CodegenContext; use crate::mir::{function::MirFunction, instruction::UnaryOp, BasicBlockId, BinaryOp, ValueId}; -use super::builder_cursor::BuilderCursor; /// Lower UnaryOp and store into vmap (0-diff) pub(in super::super) fn lower_unary<'ctx, 'b>( @@ -18,27 +18,67 @@ pub(in super::super) fn lower_unary<'ctx, 'b>( dst: ValueId, op: &UnaryOp, operand: &ValueId, - bb_map: &std::collections::HashMap>, + bb_map: &std::collections::HashMap< + crate::mir::BasicBlockId, + inkwell::basic_block::BasicBlock<'ctx>, + >, preds: &std::collections::HashMap>, - block_end_values: &std::collections::HashMap>>, + block_end_values: &std::collections::HashMap< + crate::mir::BasicBlockId, + std::collections::HashMap>, + >, ) -> Result<(), String> { use crate::mir::MirType as MT; let out = match op { - UnaryOp::Neg => { - 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::Neg => 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 => { - 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() + 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); @@ -57,23 +97,95 @@ pub(in super::super) fn lower_binop<'ctx, 'b>( op: &BinaryOp, lhs: &ValueId, rhs: &ValueId, - bb_map: &std::collections::HashMap>, + bb_map: &std::collections::HashMap< + crate::mir::BasicBlockId, + inkwell::basic_block::BasicBlock<'ctx>, + >, preds: &std::collections::HashMap>, - block_end_values: &std::collections::HashMap>>, + block_end_values: &std::collections::HashMap< + crate::mir::BasicBlockId, + std::collections::HashMap>, + >, ) -> Result<(), String> { use crate::backend::llvm::compiler::helpers::{as_float, as_int}; use inkwell::values::BasicValueEnum as BVE; use inkwell::IntPredicate; // 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(), + 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: 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(), + 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 { @@ -91,10 +203,15 @@ pub(in super::super) fn lower_binop<'ctx, 'b>( let callee = codegen .module .get_function("nyash.string.concat_ss") - .unwrap_or_else(|| codegen.module.add_function("nyash.string.concat_ss", fnty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.string.concat_ss", fnty, None) + }); let call = cursor - .emit_instr(cur_bid, |b| b - .build_call(callee, &[lp.into(), rp.into()], "concat_ss")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[lp.into(), rp.into()], "concat_ss") + }) .map_err(|e| e.to_string())?; let rv = call .try_as_basic_value() @@ -103,7 +220,9 @@ pub(in super::super) fn lower_binop<'ctx, 'b>( // 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")) + .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; @@ -115,10 +234,15 @@ pub(in super::super) fn lower_binop<'ctx, 'b>( 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)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.box.from_i8_string", fnty_conv, None) + }); let call_c = cursor - .emit_instr(cur_bid, |b| b - .build_call(conv, &[lp.into()], "lhs_i8_to_handle")) + .emit_instr(cur_bid, |b| { + b.build_call(conv, &[lp.into()], "lhs_i8_to_handle") + }) .map_err(|e| e.to_string())?; let lh = call_c .try_as_basic_value() @@ -129,10 +253,15 @@ pub(in super::super) fn lower_binop<'ctx, 'b>( let callee = codegen .module .get_function("nyash.string.concat_hh") - .unwrap_or_else(|| codegen.module.add_function("nyash.string.concat_hh", fnty_hh, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.string.concat_hh", fnty_hh, None) + }); let call = cursor - .emit_instr(cur_bid, |b| b - .build_call(callee, &[lh.into(), ri.into()], "concat_hh")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[lh.into(), ri.into()], "concat_hh") + }) .map_err(|e| e.to_string())?; let rv = call .try_as_basic_value() @@ -146,7 +275,11 @@ pub(in super::super) fn lower_binop<'ctx, 'b>( let callee = codegen .module .get_function("nyash.string.concat_si") - .unwrap_or_else(|| codegen.module.add_function("nyash.string.concat_si", fnty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.string.concat_si", fnty, None) + }); let call = codegen .builder .build_call(callee, &[lp.into(), ri.into()], "concat_si") @@ -157,7 +290,9 @@ pub(in super::super) fn lower_binop<'ctx, 'b>( .ok_or("concat_si returned void".to_string())?; 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")) + .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; @@ -170,7 +305,11 @@ pub(in super::super) fn lower_binop<'ctx, 'b>( 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)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.box.from_i8_string", fnty_conv, None) + }); let call_c = codegen .builder .build_call(conv, &[rp.into()], "rhs_i8_to_handle") @@ -184,10 +323,15 @@ pub(in super::super) fn lower_binop<'ctx, 'b>( let callee = codegen .module .get_function("nyash.string.concat_hh") - .unwrap_or_else(|| codegen.module.add_function("nyash.string.concat_hh", fnty_hh, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.string.concat_hh", fnty_hh, None) + }); let call = cursor - .emit_instr(cur_bid, |b| b - .build_call(callee, &[li.into(), rh.into()], "concat_hh")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[li.into(), rh.into()], "concat_hh") + }) .map_err(|e| e.to_string())?; let rv = call .try_as_basic_value() @@ -201,10 +345,15 @@ pub(in super::super) fn lower_binop<'ctx, 'b>( let callee = codegen .module .get_function("nyash.string.concat_is") - .unwrap_or_else(|| codegen.module.add_function("nyash.string.concat_is", fnty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.string.concat_is", fnty, None) + }); let call = cursor - .emit_instr(cur_bid, |b| b - .build_call(callee, &[li.into(), rp.into()], "concat_is")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[li.into(), rp.into()], "concat_is") + }) .map_err(|e| e.to_string())?; let rv = call .try_as_basic_value() @@ -212,7 +361,9 @@ pub(in super::super) fn lower_binop<'ctx, 'b>( .ok_or("concat_is returned void".to_string())?; 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")) + .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; @@ -227,39 +378,107 @@ pub(in super::super) fn lower_binop<'ctx, 'b>( let out = if let (Some(_li0), Some(_ri0)) = (as_int(lv), as_int(rv)) { // Localize integer operands into current block to satisfy dominance (normalize to i64) - let li = resolver.resolve_i64(codegen, cursor, cur_bid, *lhs, bb_map, preds, block_end_values, vmap) + let li = resolver + .resolve_i64( + codegen, + cursor, + cur_bid, + *lhs, + bb_map, + preds, + block_end_values, + vmap, + ) .unwrap_or_else(|_| codegen.context.i64_type().const_zero()); - let ri = resolver.resolve_i64(codegen, cursor, cur_bid, *rhs, bb_map, preds, block_end_values, vmap) + let ri = resolver + .resolve_i64( + codegen, + cursor, + cur_bid, + *rhs, + bb_map, + preds, + block_end_values, + vmap, + ) .unwrap_or_else(|_| codegen.context.i64_type().const_zero()); use BinaryOp as B; match op { - B::Add => cursor.emit_instr(cur_bid, |b| b.build_int_add(li, ri, "iadd")).map_err(|e| e.to_string())?.into(), - B::Sub => cursor.emit_instr(cur_bid, |b| b.build_int_sub(li, ri, "isub")).map_err(|e| e.to_string())?.into(), - B::Mul => cursor.emit_instr(cur_bid, |b| b.build_int_mul(li, ri, "imul")).map_err(|e| e.to_string())?.into(), - B::Div => cursor.emit_instr(cur_bid, |b| b.build_int_signed_div(li, ri, "idiv")).map_err(|e| e.to_string())?.into(), - B::Mod => cursor.emit_instr(cur_bid, |b| b.build_int_signed_rem(li, ri, "imod")).map_err(|e| e.to_string())?.into(), - B::BitAnd => cursor.emit_instr(cur_bid, |b| b.build_and(li, ri, "iand")).map_err(|e| e.to_string())?.into(), - B::BitOr => cursor.emit_instr(cur_bid, |b| b.build_or(li, ri, "ior")).map_err(|e| e.to_string())?.into(), - B::BitXor => cursor.emit_instr(cur_bid, |b| b.build_xor(li, ri, "ixor")).map_err(|e| e.to_string())?.into(), - B::Shl => cursor.emit_instr(cur_bid, |b| b.build_left_shift(li, ri, "ishl")).map_err(|e| e.to_string())?.into(), - B::Shr => cursor.emit_instr(cur_bid, |b| b.build_right_shift(li, ri, false, "ishr")).map_err(|e| e.to_string())?.into(), + B::Add => cursor + .emit_instr(cur_bid, |b| b.build_int_add(li, ri, "iadd")) + .map_err(|e| e.to_string())? + .into(), + B::Sub => cursor + .emit_instr(cur_bid, |b| b.build_int_sub(li, ri, "isub")) + .map_err(|e| e.to_string())? + .into(), + B::Mul => cursor + .emit_instr(cur_bid, |b| b.build_int_mul(li, ri, "imul")) + .map_err(|e| e.to_string())? + .into(), + B::Div => cursor + .emit_instr(cur_bid, |b| b.build_int_signed_div(li, ri, "idiv")) + .map_err(|e| e.to_string())? + .into(), + B::Mod => cursor + .emit_instr(cur_bid, |b| b.build_int_signed_rem(li, ri, "imod")) + .map_err(|e| e.to_string())? + .into(), + B::BitAnd => cursor + .emit_instr(cur_bid, |b| b.build_and(li, ri, "iand")) + .map_err(|e| e.to_string())? + .into(), + B::BitOr => cursor + .emit_instr(cur_bid, |b| b.build_or(li, ri, "ior")) + .map_err(|e| e.to_string())? + .into(), + B::BitXor => cursor + .emit_instr(cur_bid, |b| b.build_xor(li, ri, "ixor")) + .map_err(|e| e.to_string())? + .into(), + B::Shl => cursor + .emit_instr(cur_bid, |b| b.build_left_shift(li, ri, "ishl")) + .map_err(|e| e.to_string())? + .into(), + B::Shr => cursor + .emit_instr(cur_bid, |b| b.build_right_shift(li, ri, false, "ishr")) + .map_err(|e| e.to_string())? + .into(), B::And | B::Or => { // Treat as logical on integers: convert to i1 and and/or let lb = types::to_bool(codegen.context, li.into(), &codegen.builder)?; let rb = types::to_bool(codegen.context, ri.into(), &codegen.builder)?; match op { - B::And => cursor.emit_instr(cur_bid, |b| b.build_and(lb, rb, "land")).map_err(|e| e.to_string())?.into(), - _ => cursor.emit_instr(cur_bid, |b| b.build_or(lb, rb, "lor")).map_err(|e| e.to_string())?.into(), + B::And => cursor + .emit_instr(cur_bid, |b| b.build_and(lb, rb, "land")) + .map_err(|e| e.to_string())? + .into(), + _ => cursor + .emit_instr(cur_bid, |b| b.build_or(lb, rb, "lor")) + .map_err(|e| e.to_string())? + .into(), } } } } else if let (Some(lf), Some(rf)) = (as_float(lv), as_float(rv)) { use BinaryOp as B; match op { - B::Add => cursor.emit_instr(cur_bid, |b| b.build_float_add(lf, rf, "fadd")).map_err(|e| e.to_string())?.into(), - B::Sub => cursor.emit_instr(cur_bid, |b| b.build_float_sub(lf, rf, "fsub")).map_err(|e| e.to_string())?.into(), - B::Mul => cursor.emit_instr(cur_bid, |b| b.build_float_mul(lf, rf, "fmul")).map_err(|e| e.to_string())?.into(), - B::Div => cursor.emit_instr(cur_bid, |b| b.build_float_div(lf, rf, "fdiv")).map_err(|e| e.to_string())?.into(), + B::Add => cursor + .emit_instr(cur_bid, |b| b.build_float_add(lf, rf, "fadd")) + .map_err(|e| e.to_string())? + .into(), + B::Sub => cursor + .emit_instr(cur_bid, |b| b.build_float_sub(lf, rf, "fsub")) + .map_err(|e| e.to_string())? + .into(), + B::Mul => cursor + .emit_instr(cur_bid, |b| b.build_float_mul(lf, rf, "fmul")) + .map_err(|e| e.to_string())? + .into(), + B::Div => cursor + .emit_instr(cur_bid, |b| b.build_float_div(lf, rf, "fdiv")) + .map_err(|e| e.to_string())? + .into(), B::Mod => return Err("fmod not supported yet".to_string()), _ => return Err("bit/logic ops on float".to_string()), } @@ -273,14 +492,21 @@ pub(in super::super) fn lower_binop<'ctx, 'b>( Ok(()) } -fn guessed_zero<'ctx>(codegen: &CodegenContext<'ctx>, func: &MirFunction, vid: &ValueId) -> BasicValueEnum<'ctx> { +fn guessed_zero<'ctx>( + codegen: &CodegenContext<'ctx>, + func: &MirFunction, + vid: &ValueId, +) -> BasicValueEnum<'ctx> { use crate::mir::MirType as MT; match func.metadata.value_types.get(vid) { Some(MT::Bool) => codegen.context.bool_type().const_zero().into(), Some(MT::Integer) => codegen.context.i64_type().const_zero().into(), Some(MT::Float) => codegen.context.f64_type().const_zero().into(), - Some(MT::String) | Some(MT::Box(_)) | Some(MT::Array(_)) | Some(MT::Future(_)) | Some(MT::Unknown) | Some(MT::Void) | None => { - codegen.context.ptr_type(AddressSpace::from(0)).const_zero().into() - } + Some(MT::String) | Some(MT::Box(_)) | Some(MT::Array(_)) | Some(MT::Future(_)) + | Some(MT::Unknown) | Some(MT::Void) | None => codegen + .context + .ptr_type(AddressSpace::from(0)) + .const_zero() + .into(), } } diff --git a/src/backend/llvm/compiler/codegen/instructions/arrays.rs b/src/backend/llvm/compiler/codegen/instructions/arrays.rs index 500b0d53..574b4f79 100644 --- a/src/backend/llvm/compiler/codegen/instructions/arrays.rs +++ b/src/backend/llvm/compiler/codegen/instructions/arrays.rs @@ -2,9 +2,9 @@ use std::collections::HashMap; use inkwell::values::BasicValueEnum as BVE; +use super::builder_cursor::BuilderCursor; use crate::backend::llvm::context::CodegenContext; use crate::mir::{function::MirFunction, BasicBlockId, ValueId}; -use super::builder_cursor::BuilderCursor; /// Handle ArrayBox fast-paths. Returns true if handled. pub(super) fn try_handle_array_method<'ctx, 'b>( @@ -19,9 +19,15 @@ pub(super) fn try_handle_array_method<'ctx, 'b>( method: &str, args: &[ValueId], recv_h: inkwell::values::IntValue<'ctx>, - bb_map: &std::collections::HashMap>, + bb_map: &std::collections::HashMap< + crate::mir::BasicBlockId, + inkwell::basic_block::BasicBlock<'ctx>, + >, preds: &std::collections::HashMap>, - block_end_values: &std::collections::HashMap>>, + block_end_values: &std::collections::HashMap< + crate::mir::BasicBlockId, + std::collections::HashMap>, + >, ) -> Result { // Only when receiver is ArrayBox let is_array = matches!(func.metadata.value_types.get(box_val), Some(crate::mir::MirType::Box(b)) if b == "ArrayBox") @@ -38,14 +44,25 @@ pub(super) fn try_handle_array_method<'ctx, 'b>( if args.len() != 1 { return Err("ArrayBox.get expects 1 arg".to_string()); } - let idx_i = resolver.resolve_i64(codegen, cursor, cur_bid, args[0], bb_map, preds, block_end_values, vmap)?; + let idx_i = resolver.resolve_i64( + codegen, + cursor, + cur_bid, + args[0], + bb_map, + preds, + block_end_values, + vmap, + )?; let fnty = i64t.fn_type(&[i64t.into(), i64t.into()], false); let callee = codegen .module .get_function("nyash_array_get_h") .unwrap_or_else(|| codegen.module.add_function("nyash_array_get_h", fnty, None)); let call = cursor - .emit_instr(cur_bid, |b| b.build_call(callee, &[recv_h.into(), idx_i.into()], "aget")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[recv_h.into(), idx_i.into()], "aget") + }) .map_err(|e| e.to_string())?; if let Some(d) = dst { let rv = call @@ -63,15 +80,35 @@ pub(super) fn try_handle_array_method<'ctx, 'b>( if args.len() != 2 { return Err("ArrayBox.set expects 2 arg".to_string()); } - let idx_i = resolver.resolve_i64(codegen, cursor, cur_bid, args[0], bb_map, preds, block_end_values, vmap)?; - let val_i = resolver.resolve_i64(codegen, cursor, cur_bid, args[1], bb_map, preds, block_end_values, vmap)?; + let idx_i = resolver.resolve_i64( + codegen, + cursor, + cur_bid, + args[0], + bb_map, + preds, + block_end_values, + vmap, + )?; + let val_i = resolver.resolve_i64( + codegen, + cursor, + cur_bid, + args[1], + bb_map, + preds, + block_end_values, + vmap, + )?; let fnty = i64t.fn_type(&[i64t.into(), i64t.into(), i64t.into()], false); let callee = codegen .module .get_function("nyash_array_set_h") .unwrap_or_else(|| codegen.module.add_function("nyash_array_set_h", fnty, None)); let _ = cursor - .emit_instr(cur_bid, |b| b.build_call(callee, &[recv_h.into(), idx_i.into(), val_i.into()], "aset")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[recv_h.into(), idx_i.into(), val_i.into()], "aset") + }) .map_err(|e| e.to_string())?; Ok(true) } @@ -82,14 +119,29 @@ pub(super) fn try_handle_array_method<'ctx, 'b>( if args.len() != 1 { return Err("ArrayBox.push expects 1 arg".to_string()); } - let val_i = resolver.resolve_i64(codegen, cursor, cur_bid, args[0], bb_map, preds, block_end_values, vmap)?; + let val_i = resolver.resolve_i64( + codegen, + cursor, + cur_bid, + args[0], + bb_map, + preds, + block_end_values, + vmap, + )?; let fnty = i64t.fn_type(&[i64t.into(), i64t.into()], false); let callee = codegen .module .get_function("nyash_array_push_h") - .unwrap_or_else(|| codegen.module.add_function("nyash_array_push_h", fnty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash_array_push_h", fnty, None) + }); let _ = cursor - .emit_instr(cur_bid, |b| b.build_call(callee, &[recv_h.into(), val_i.into()], "apush")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[recv_h.into(), val_i.into()], "apush") + }) .map_err(|e| e.to_string())?; Ok(true) } @@ -104,7 +156,11 @@ pub(super) fn try_handle_array_method<'ctx, 'b>( let callee = codegen .module .get_function("nyash_array_length_h") - .unwrap_or_else(|| codegen.module.add_function("nyash_array_length_h", fnty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash_array_length_h", fnty, None) + }); let call = cursor .emit_instr(cur_bid, |b| b.build_call(callee, &[recv_h.into()], "alen")) .map_err(|e| e.to_string())?; diff --git a/src/backend/llvm/compiler/codegen/instructions/blocks.rs b/src/backend/llvm/compiler/codegen/instructions/blocks.rs index 24dcd2f4..1808f96a 100644 --- a/src/backend/llvm/compiler/codegen/instructions/blocks.rs +++ b/src/backend/llvm/compiler/codegen/instructions/blocks.rs @@ -2,8 +2,8 @@ use inkwell::basic_block::BasicBlock; use inkwell::values::{BasicValueEnum, FunctionValue, PhiValue}; use std::collections::HashMap; -use crate::backend::llvm::context::CodegenContext; use super::super::types::map_mirtype_to_basic; +use crate::backend::llvm::context::CodegenContext; use crate::mir::{function::MirFunction, BasicBlockId, ValueId}; // Small, safe extraction: create LLVM basic blocks for a MIR function and @@ -16,9 +16,10 @@ pub(in super::super) fn create_basic_blocks<'ctx>( ) -> (HashMap>, BasicBlock<'ctx>) { let mut bb_map: HashMap = HashMap::new(); let entry_first = func.entry_block; - let entry_bb = codegen - .context - .append_basic_block(llvm_func, &format!("{}_bb{}", fn_label, entry_first.as_u32())); + let entry_bb = codegen.context.append_basic_block( + llvm_func, + &format!("{}_bb{}", fn_label, entry_first.as_u32()), + ); bb_map.insert(entry_first, entry_bb); for bid in func.block_ids() { if bid == entry_first { @@ -38,10 +39,7 @@ pub(in super::super) fn precreate_phis<'ctx>( bb_map: &HashMap>, vmap: &mut HashMap>, ) -> Result< - HashMap< - BasicBlockId, - Vec<(ValueId, PhiValue<'ctx>, Vec<(BasicBlockId, ValueId)>)>, - >, + HashMap, Vec<(BasicBlockId, ValueId)>)>>, String, > { use super::super::types::map_mirtype_to_basic; @@ -63,15 +61,34 @@ pub(in super::super) fn precreate_phis<'ctx>( // Prefer pointer when any input (or dst) is String/Box/Array/Future/Unknown let mut wants_ptr = false; if let Some(mt) = func.metadata.value_types.get(dst) { - wants_ptr |= matches!(mt, crate::mir::MirType::String | crate::mir::MirType::Box(_) | crate::mir::MirType::Array(_) | crate::mir::MirType::Future(_) | crate::mir::MirType::Unknown); + wants_ptr |= matches!( + mt, + crate::mir::MirType::String + | crate::mir::MirType::Box(_) + | crate::mir::MirType::Array(_) + | crate::mir::MirType::Future(_) + | crate::mir::MirType::Unknown + ); } for (_, iv) in inputs.iter() { if let Some(mt) = func.metadata.value_types.get(iv) { - wants_ptr |= matches!(mt, crate::mir::MirType::String | crate::mir::MirType::Box(_) | crate::mir::MirType::Array(_) | crate::mir::MirType::Future(_) | crate::mir::MirType::Unknown); + wants_ptr |= matches!( + mt, + crate::mir::MirType::String + | crate::mir::MirType::Box(_) + | crate::mir::MirType::Array(_) + | crate::mir::MirType::Future(_) + | crate::mir::MirType::Unknown + ); } } if wants_ptr { - phi_ty = Some(codegen.context.ptr_type(inkwell::AddressSpace::from(0)).into()); + phi_ty = Some( + codegen + .context + .ptr_type(inkwell::AddressSpace::from(0)) + .into(), + ); } else if let Some(mt) = func.metadata.value_types.get(dst) { phi_ty = Some(map_mirtype_to_basic(codegen.context, mt)); } else if let Some((_, iv)) = inputs.first() { diff --git a/src/backend/llvm/compiler/codegen/instructions/boxcall.rs b/src/backend/llvm/compiler/codegen/instructions/boxcall.rs index 5b6d4c06..0a7212ad 100644 --- a/src/backend/llvm/compiler/codegen/instructions/boxcall.rs +++ b/src/backend/llvm/compiler/codegen/instructions/boxcall.rs @@ -1,17 +1,17 @@ use std::collections::HashMap; -use inkwell::AddressSpace; use inkwell::values::BasicValueEnum as BVE; +use inkwell::AddressSpace; use crate::backend::llvm::context::CodegenContext; mod fields; pub(crate) mod invoke; mod marshal; -use self::marshal as marshal_mod; use self::invoke as invoke_mod; -use crate::mir::{function::MirFunction, BasicBlockId, ValueId}; +use self::marshal as marshal_mod; use super::builder_cursor::BuilderCursor; -use super::ctx::{LowerFnCtx, BlockCtx}; +use super::ctx::{BlockCtx, LowerFnCtx}; +use crate::mir::{function::MirFunction, BasicBlockId, ValueId}; // BoxCall lowering (large): mirrors existing logic; kept in one function for now pub(in super::super) fn lower_boxcall<'ctx, 'b>( @@ -28,46 +28,95 @@ pub(in super::super) fn lower_boxcall<'ctx, 'b>( args: &[ValueId], box_type_ids: &HashMap, entry_builder: &inkwell::builder::Builder<'ctx>, - bb_map: &std::collections::HashMap>, + bb_map: &std::collections::HashMap< + crate::mir::BasicBlockId, + inkwell::basic_block::BasicBlock<'ctx>, + >, preds: &std::collections::HashMap>, - block_end_values: &std::collections::HashMap>>, + block_end_values: &std::collections::HashMap< + crate::mir::BasicBlockId, + std::collections::HashMap>, + >, ) -> Result<(), String> { - use crate::backend::llvm::compiler::helpers::{as_float, as_int}; use super::super::types::classify_tag; + use crate::backend::llvm::compiler::helpers::{as_float, as_int}; let i64t = codegen.context.i64_type(); // 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, + 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, + codegen, + cursor, + cur_bid, + *box_val, + bb_map, + preds, + block_end_values, + vmap, )?; let recv_v: BVE = recv_p.into(); // Resolve type_id - let type_id: i64 = if let Some(crate::mir::MirType::Box(bname)) = func.metadata.value_types.get(box_val) { - *box_type_ids.get(bname).unwrap_or(&0) - } else if let Some(crate::mir::MirType::String) = func.metadata.value_types.get(box_val) { - *box_type_ids.get("StringBox").unwrap_or(&0) - } else { - 0 - }; + let type_id: i64 = + if let Some(crate::mir::MirType::Box(bname)) = func.metadata.value_types.get(box_val) { + *box_type_ids.get(bname).unwrap_or(&0) + } else if let Some(crate::mir::MirType::String) = func.metadata.value_types.get(box_val) { + *box_type_ids.get("StringBox").unwrap_or(&0) + } else { + 0 + }; // Delegate String methods if super::strings::try_handle_string_method( - codegen, cursor, resolver, cur_bid, func, vmap, dst, box_val, method, args, - bb_map, preds, block_end_values, + codegen, + cursor, + resolver, + cur_bid, + func, + vmap, + dst, + box_val, + method, + args, + bb_map, + preds, + block_end_values, )? { return Ok(()); } // Delegate Map methods first (to avoid Array fallback catching get/set ambiguously) - if super::maps::try_handle_map_method(codegen, cursor, resolver, cur_bid, func, vmap, dst, box_val, method, args, recv_h)? { + if super::maps::try_handle_map_method( + codegen, cursor, resolver, cur_bid, func, vmap, dst, box_val, method, args, recv_h, + )? { return Ok(()); } // Delegate Array methods - if super::arrays::try_handle_array_method(codegen, cursor, resolver, cur_bid, func, vmap, dst, box_val, method, args, recv_h, bb_map, preds, block_end_values)? { + if super::arrays::try_handle_array_method( + codegen, + cursor, + resolver, + cur_bid, + func, + vmap, + dst, + box_val, + method, + args, + recv_h, + bb_map, + preds, + block_end_values, + )? { return Ok(()); } @@ -114,9 +163,15 @@ pub(in super::super) fn lower_boxcall<'ctx, 'b>( let callee = codegen .module .get_function("nyash_array_length_h") - .unwrap_or_else(|| codegen.module.add_function("nyash_array_length_h", fnty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash_array_length_h", fnty, None) + }); let call = cursor - .emit_instr(cur_bid, |b| b.build_call(callee, &[recv_h.into()], "alen_fallback")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[recv_h.into()], "alen_fallback") + }) .map_err(|e| e.to_string())?; if let Some(d) = dst { let rv = call @@ -163,42 +218,116 @@ pub(in super::super) fn lower_boxcall<'ctx, 'b>( // Sanitize symbol the same way as codegen/mod.rs does let sym: String = { let mut s = String::from("ny_f_"); - s.push_str(&candidate.replace('.', "_").replace('/', "_").replace('-', "_")); + s.push_str( + &candidate + .replace('.', "_") + .replace('/', "_") + .replace('-', "_"), + ); s }; if let Some(callee) = codegen.module.get_function(&sym) { // Coerce arguments to callee parameter types let exp_tys = callee.get_type().get_param_types(); - if exp_tys.len() != args.len() { return Err("boxcall direct-call: arg count mismatch".to_string()); } - let mut call_args: Vec = Vec::with_capacity(args.len()); + if exp_tys.len() != args.len() { + return Err("boxcall direct-call: arg count mismatch".to_string()); + } + let mut call_args: Vec = + Vec::with_capacity(args.len()); for (i, a) in args.iter().enumerate() { 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 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() } + 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())?; + 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() } + 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() + ) } - _ => 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")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &call_args, "user_meth_call") + }) .map_err(|e| e.to_string())?; if let Some(d) = dst { if let Some(rv) = call.try_as_basic_value().left() { @@ -216,22 +345,66 @@ pub(in super::super) fn lower_boxcall<'ctx, 'b>( .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) { 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 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 - codegen.context.ptr_type(AddressSpace::from(0)).into(), // method cstr - i64t.into(), i64t.into(), i64t.into(), // argc, a1, a2 + i64t.into(), // recv handle + codegen.context.ptr_type(AddressSpace::from(0)).into(), // method cstr + i64t.into(), + i64t.into(), + i64t.into(), // argc, a1, a2 ], false, ); let callee = codegen .module .get_function("nyash.plugin.invoke_by_name_i64") - .unwrap_or_else(|| codegen.module.add_function("nyash.plugin.invoke_by_name_i64", fnty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.plugin.invoke_by_name_i64", fnty, None) + }); let call = cursor - .emit_instr(cur_bid, |b| b.build_call(callee, &[recv_h.into(), mname.as_pointer_value().into(), argc.into(), a1.into(), a2.into()], "pinvoke_by_name")) + .emit_instr(cur_bid, |b| { + b.build_call( + callee, + &[ + recv_h.into(), + mname.as_pointer_value().into(), + argc.into(), + a1.into(), + a2.into(), + ], + "pinvoke_by_name", + ) + }) .map_err(|e| e.to_string())?; if let Some(d) = dst { let rv = call @@ -241,28 +414,66 @@ pub(in super::super) fn lower_boxcall<'ctx, 'b>( // Inline minimal return normalization similar to store_invoke_return() if let Some(mt) = func.metadata.value_types.get(d) { match mt { - crate::mir::MirType::Integer => { vmap.insert(*d, rv); } + crate::mir::MirType::Integer => { + vmap.insert(*d, rv); + } crate::mir::MirType::Bool => { if let BVE::IntValue(iv) = rv { let i64t = codegen.context.i64_type(); let zero = i64t.const_zero(); - let b1 = cursor.emit_instr(cur_bid, |bd| bd.build_int_compare(inkwell::IntPredicate::NE, iv, zero, "bool_i64_to_i1")).map_err(|e| e.to_string())?; + let b1 = cursor + .emit_instr(cur_bid, |bd| { + bd.build_int_compare( + inkwell::IntPredicate::NE, + iv, + zero, + "bool_i64_to_i1", + ) + }) + .map_err(|e| e.to_string())?; vmap.insert(*d, b1.into()); - } else { vmap.insert(*d, rv); } + } else { + vmap.insert(*d, rv); + } } crate::mir::MirType::String => { if let BVE::IntValue(iv) = rv { - let p = cursor.emit_instr(cur_bid, |bd| bd.build_int_to_ptr(iv, codegen.context.ptr_type(AddressSpace::from(0)), "str_h2p_ret")).map_err(|e| e.to_string())?; + let p = cursor + .emit_instr(cur_bid, |bd| { + bd.build_int_to_ptr( + iv, + codegen.context.ptr_type(AddressSpace::from(0)), + "str_h2p_ret", + ) + }) + .map_err(|e| e.to_string())?; vmap.insert(*d, p.into()); - } else { vmap.insert(*d, rv); } + } else { + vmap.insert(*d, rv); + } } - crate::mir::MirType::Box(_) | crate::mir::MirType::Array(_) | crate::mir::MirType::Future(_) | crate::mir::MirType::Unknown => { + crate::mir::MirType::Box(_) + | crate::mir::MirType::Array(_) + | crate::mir::MirType::Future(_) + | crate::mir::MirType::Unknown => { if let BVE::IntValue(iv) = rv { - let p = cursor.emit_instr(cur_bid, |bd| bd.build_int_to_ptr(iv, codegen.context.ptr_type(AddressSpace::from(0)), "h2p_ret")).map_err(|e| e.to_string())?; + let p = cursor + .emit_instr(cur_bid, |bd| { + bd.build_int_to_ptr( + iv, + codegen.context.ptr_type(AddressSpace::from(0)), + "h2p_ret", + ) + }) + .map_err(|e| e.to_string())?; vmap.insert(*d, p.into()); - } else { vmap.insert(*d, rv); } + } else { + vmap.insert(*d, rv); + } + } + _ => { + vmap.insert(*d, rv); } - _ => { vmap.insert(*d, rv); } } } else { vmap.insert(*d, rv); @@ -286,7 +497,9 @@ pub(in super::super) fn lower_boxcall_boxed<'ctx, 'b>( entry_builder: &inkwell::builder::Builder<'ctx>, ) -> Result<(), String> { // Optional dev check: ensure block is open for insertion - if ctx.dev_checks { ctx.cursor.assert_open(blk.cur_bid); } + if ctx.dev_checks { + ctx.cursor.assert_open(blk.cur_bid); + } lower_boxcall( ctx.codegen, ctx.cursor, @@ -299,7 +512,8 @@ pub(in super::super) fn lower_boxcall_boxed<'ctx, 'b>( method, method_id, args, - ctx.box_type_ids.ok_or_else(|| "LowerFnCtx.box_type_ids missing".to_string())?, + ctx.box_type_ids + .ok_or_else(|| "LowerFnCtx.box_type_ids missing".to_string())?, entry_builder, ctx.bb_map, ctx.preds, @@ -322,9 +536,15 @@ pub(in super::super) fn lower_boxcall_via_ctx<'ctx, 'b>( args: &[ValueId], box_type_ids: &'b HashMap, entry_builder: &inkwell::builder::Builder<'ctx>, - bb_map: &'b std::collections::HashMap>, + bb_map: &'b std::collections::HashMap< + crate::mir::BasicBlockId, + inkwell::basic_block::BasicBlock<'ctx>, + >, preds: &'b std::collections::HashMap>, - block_end_values: &'b std::collections::HashMap>>, + block_end_values: &'b std::collections::HashMap< + crate::mir::BasicBlockId, + std::collections::HashMap>, + >, ) -> Result<(), String> { let llbb = *bb_map.get(&cur_bid).ok_or("missing cur bb")?; let blkctx = BlockCtx::new(cur_bid, llbb); @@ -364,18 +584,45 @@ fn coerce_to_type<'ctx>( if bw_src == bw_dst { Ok(iv.into()) } else if bw_src < bw_dst { - Ok(codegen.builder.build_int_z_extend(iv, it, "bc_zext").map_err(|e| e.to_string())?.into()) + Ok(codegen + .builder + .build_int_z_extend(iv, it, "bc_zext") + .map_err(|e| e.to_string())? + .into()) } else if bw_dst == 1 { - Ok(super::super::types::to_bool(codegen.context, iv.into(), &codegen.builder)?.into()) + Ok( + super::super::types::to_bool(codegen.context, iv.into(), &codegen.builder)? + .into(), + ) } else { - Ok(codegen.builder.build_int_truncate(iv, it, "bc_trunc").map_err(|e| e.to_string())?.into()) + Ok(codegen + .builder + .build_int_truncate(iv, it, "bc_trunc") + .map_err(|e| e.to_string())? + .into()) } } - (inkwell::values::BasicValueEnum::PointerValue(pv), BT::IntType(it)) => Ok(codegen.builder.build_ptr_to_int(pv, it, "bc_p2i").map_err(|e| e.to_string())?.into()), - (inkwell::values::BasicValueEnum::FloatValue(fv), BT::IntType(it)) => Ok(codegen.builder.build_float_to_signed_int(fv, it, "bc_f2i").map_err(|e| e.to_string())?.into()), - (inkwell::values::BasicValueEnum::IntValue(iv), BT::PointerType(pt)) => Ok(codegen.builder.build_int_to_ptr(iv, pt, "bc_i2p").map_err(|e| e.to_string())?.into()), + (inkwell::values::BasicValueEnum::PointerValue(pv), BT::IntType(it)) => Ok(codegen + .builder + .build_ptr_to_int(pv, it, "bc_p2i") + .map_err(|e| e.to_string())? + .into()), + (inkwell::values::BasicValueEnum::FloatValue(fv), BT::IntType(it)) => Ok(codegen + .builder + .build_float_to_signed_int(fv, it, "bc_f2i") + .map_err(|e| e.to_string())? + .into()), + (inkwell::values::BasicValueEnum::IntValue(iv), BT::PointerType(pt)) => Ok(codegen + .builder + .build_int_to_ptr(iv, pt, "bc_i2p") + .map_err(|e| e.to_string())? + .into()), (inkwell::values::BasicValueEnum::PointerValue(pv), BT::PointerType(_)) => Ok(pv.into()), - (inkwell::values::BasicValueEnum::IntValue(iv), BT::FloatType(ft)) => Ok(codegen.builder.build_signed_int_to_float(iv, ft, "bc_i2f").map_err(|e| e.to_string())?.into()), + (inkwell::values::BasicValueEnum::IntValue(iv), BT::FloatType(ft)) => Ok(codegen + .builder + .build_signed_int_to_float(iv, ft, "bc_i2f") + .map_err(|e| e.to_string())? + .into()), (inkwell::values::BasicValueEnum::FloatValue(fv), BT::FloatType(_)) => Ok(fv.into()), (v, _) => Ok(v), } diff --git a/src/backend/llvm/compiler/codegen/instructions/boxcall/fields.rs b/src/backend/llvm/compiler/codegen/instructions/boxcall/fields.rs index df8109d2..5446f6f2 100644 --- a/src/backend/llvm/compiler/codegen/instructions/boxcall/fields.rs +++ b/src/backend/llvm/compiler/codegen/instructions/boxcall/fields.rs @@ -1,9 +1,9 @@ use std::collections::HashMap; -use inkwell::{values::BasicValueEnum as BVE, AddressSpace}; +use super::super::ctx::{BlockCtx, LowerFnCtx}; use crate::backend::llvm::context::CodegenContext; use crate::mir::ValueId; -use super::super::ctx::{LowerFnCtx, BlockCtx}; +use inkwell::{values::BasicValueEnum as BVE, AddressSpace}; /// Handle getField/setField; returns true if handled. use super::super::builder_cursor::BuilderCursor; @@ -18,9 +18,15 @@ pub(super) fn try_handle_field_method<'ctx, 'b>( args: &[ValueId], recv_h: inkwell::values::IntValue<'ctx>, resolver: &mut super::super::Resolver<'ctx>, - bb_map: &std::collections::HashMap>, + bb_map: &std::collections::HashMap< + crate::mir::BasicBlockId, + inkwell::basic_block::BasicBlock<'ctx>, + >, preds: &std::collections::HashMap>, - block_end_values: &std::collections::HashMap>>, + block_end_values: &std::collections::HashMap< + crate::mir::BasicBlockId, + std::collections::HashMap>, + >, ) -> Result { let i64t = codegen.context.i64_type(); match method { @@ -28,16 +34,30 @@ 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 = resolver - .resolve_ptr(codegen, cursor, cur_bid, args[0], bb_map, preds, block_end_values, vmap)?; + 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)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.instance.get_field_h", fnty, None) + }); let call = cursor - .emit_instr(cur_bid, |b| b.build_call(callee, &[recv_h.into(), name_p.into()], "getField")) + .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 @@ -49,12 +69,12 @@ pub(super) fn try_handle_field_method<'ctx, 'b>( } else { return Err("get_field ret expected i64".to_string()); }; - let pty = codegen.context.ptr_type(AddressSpace::from(0)); - let ptr = codegen + let pty = codegen.context.ptr_type(AddressSpace::from(0)); + let ptr = codegen .builder .build_int_to_ptr(h, pty, "gf_handle_to_ptr") .map_err(|e| e.to_string())?; - vmap.insert(*d, ptr.into()); + vmap.insert(*d, ptr.into()); } Ok(true) } @@ -62,18 +82,44 @@ 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 = 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 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)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.instance.set_field_h", fnty, None) + }); let _ = cursor - .emit_instr(cur_bid, |b| b.build_call(callee, &[recv_h.into(), name_p.into(), val_h.into()], "setField")) + .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) } diff --git a/src/backend/llvm/compiler/codegen/instructions/boxcall/invoke.rs b/src/backend/llvm/compiler/codegen/instructions/boxcall/invoke.rs index 0a03469f..96f93be3 100644 --- a/src/backend/llvm/compiler/codegen/instructions/boxcall/invoke.rs +++ b/src/backend/llvm/compiler/codegen/instructions/boxcall/invoke.rs @@ -2,9 +2,9 @@ use std::collections::HashMap; use inkwell::{values::BasicValueEnum as BVE, AddressSpace}; +use super::super::ctx::{BlockCtx, LowerFnCtx}; use crate::backend::llvm::context::CodegenContext; use crate::mir::{function::MirFunction, ValueId}; -use super::super::ctx::{LowerFnCtx, BlockCtx}; // use super::marshal::{get_i64, get_tag_const}; @@ -22,9 +22,15 @@ pub(super) fn try_handle_tagged_invoke<'ctx, 'b>( args: &[ValueId], entry_builder: &inkwell::builder::Builder<'ctx>, cur_bid: crate::mir::BasicBlockId, - bb_map: &std::collections::HashMap>, + bb_map: &std::collections::HashMap< + crate::mir::BasicBlockId, + inkwell::basic_block::BasicBlock<'ctx>, + >, preds: &std::collections::HashMap>, - block_end_values: &std::collections::HashMap>>, + block_end_values: &std::collections::HashMap< + crate::mir::BasicBlockId, + std::collections::HashMap>, + >, ) -> Result<(), String> { let i64t = codegen.context.i64_type(); let argc_val = i64t.const_int(args.len() as u64, false); @@ -35,13 +41,44 @@ pub(super) fn try_handle_tagged_invoke<'ctx, 'b>( for (i, vid) in args.iter().enumerate() { 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 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() + 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() } - _ => resolver.resolve_i64(codegen, cursor, cur_bid, *vid, bb_map, preds, block_end_values, vmap)?, + _ => resolver.resolve_i64( + codegen, + cursor, + cur_bid, + *vid, + bb_map, + preds, + block_end_values, + vmap, + )?, }; a[i] = iv; } @@ -49,23 +86,40 @@ pub(super) fn try_handle_tagged_invoke<'ctx, 'b>( for (i, vid) in args.iter().enumerate() { 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, + 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, }; tags[i] = i64t.const_int(tag as u64, false); } let fnty = i64t.fn_type( &[ - i64t.into(), i64t.into(), i64t.into(), i64t.into(), - i64t.into(), i64t.into(), i64t.into(), i64t.into(), - i64t.into(), i64t.into(), i64t.into(), i64t.into(), + i64t.into(), + i64t.into(), + i64t.into(), + i64t.into(), + i64t.into(), + i64t.into(), + i64t.into(), + i64t.into(), + i64t.into(), + i64t.into(), + i64t.into(), + i64t.into(), ], false, ); let callee = codegen .module .get_function("nyash_plugin_invoke3_tagged_i64") - .unwrap_or_else(|| codegen.module.add_function("nyash_plugin_invoke3_tagged_i64", fnty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash_plugin_invoke3_tagged_i64", fnty, None) + }); let tid = i64t.const_int(type_id as u64, true); let midv = i64t.const_int(mid as u64, false); let call = codegen @@ -73,9 +127,18 @@ pub(super) fn try_handle_tagged_invoke<'ctx, 'b>( .build_call( callee, &[ - tid.into(), midv.into(), argc_val.into(), recv_h.into(), - a[0].into(), tags[0].into(), a[1].into(), tags[1].into(), - a[2].into(), tags[2].into(), a[3].into(), tags[3].into(), + tid.into(), + midv.into(), + argc_val.into(), + recv_h.into(), + a[0].into(), + tags[0].into(), + a[1].into(), + tags[1].into(), + a[2].into(), + tags[2].into(), + a[3].into(), + tags[3].into(), ], "pinvoke_tagged", ) @@ -118,21 +181,62 @@ pub(super) fn try_handle_tagged_invoke<'ctx, 'b>( }; 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 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() + 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() } - _ => resolver.resolve_i64(codegen, cursor, cur_bid, *vid, bb_map, preds, block_end_values, vmap)?, + _ => 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), - 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) => i64t.const_int(8, false), + 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) => i64t.const_int(8, false), _ => i64t.const_int(3, false), }; - codegen.builder.build_store(gep_v, vi).map_err(|e| e.to_string())?; - codegen.builder.build_store(gep_t, ti).map_err(|e| e.to_string())?; + codegen + .builder + .build_store(gep_v, vi) + .map_err(|e| e.to_string())?; + codegen + .builder + .build_store(gep_t, ti) + .map_err(|e| e.to_string())?; } let vals_ptr = codegen .builder @@ -164,14 +268,25 @@ pub(super) fn try_handle_tagged_invoke<'ctx, 'b>( let callee = codegen .module .get_function("nyash.plugin.invoke_tagged_v_i64") - .unwrap_or_else(|| codegen.module.add_function("nyash.plugin.invoke_tagged_v_i64", fnty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.plugin.invoke_tagged_v_i64", fnty, None) + }); let tid = i64t.const_int(type_id as u64, true); let midv = i64t.const_int(mid as u64, false); let call = codegen .builder .build_call( callee, - &[tid.into(), midv.into(), argc_val.into(), recv_h.into(), vals_ptr.into(), tags_ptr.into()], + &[ + tid.into(), + midv.into(), + argc_val.into(), + recv_h.into(), + vals_ptr.into(), + tags_ptr.into(), + ], "pinvoke_tagged_v", ) .map_err(|e| e.to_string())?; @@ -213,8 +328,11 @@ fn store_invoke_return<'ctx>( } crate::mir::MirType::String => { // 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()); } + 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(_) diff --git a/src/backend/llvm/compiler/codegen/instructions/boxcall/marshal.rs b/src/backend/llvm/compiler/codegen/instructions/boxcall/marshal.rs index 21783024..d409d540 100644 --- a/src/backend/llvm/compiler/codegen/instructions/boxcall/marshal.rs +++ b/src/backend/llvm/compiler/codegen/instructions/boxcall/marshal.rs @@ -1,10 +1,10 @@ use std::collections::HashMap; -use inkwell::{values::BasicValueEnum as BVE, AddressSpace}; use crate::backend::llvm::compiler::codegen::types; +use inkwell::{values::BasicValueEnum as BVE, AddressSpace}; use crate::backend::llvm::context::CodegenContext; -use crate::mir::{ValueId, function::MirFunction}; +use crate::mir::{function::MirFunction, ValueId}; /// Convert a value to i64 handle/int for plugin invoke (ptr->i64, f64->box->i64) pub(super) fn get_i64<'ctx, 'b>( @@ -15,30 +15,64 @@ pub(super) fn get_i64<'ctx, 'b>( func: &MirFunction, vmap: &HashMap>, vid: ValueId, - bb_map: &std::collections::HashMap>, + bb_map: &std::collections::HashMap< + crate::mir::BasicBlockId, + inkwell::basic_block::BasicBlock<'ctx>, + >, preds: &std::collections::HashMap>, - block_end_values: &std::collections::HashMap>>, + block_end_values: &std::collections::HashMap< + crate::mir::BasicBlockId, + std::collections::HashMap>, + >, ) -> Result, String> { let i64t = codegen.context.i64_type(); 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 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)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.box.from_f64", fnty, None) + }); let call = cursor - .emit_instr(cur_bid, |b| b.build_call(callee, &[fv.into()], "arg_f64_to_box")) + .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()) + } } - _ => resolver.resolve_i64(codegen, cursor, cur_bid, vid, bb_map, preds, block_end_values, vmap), + _ => resolver.resolve_i64( + codegen, + cursor, + cur_bid, + vid, + bb_map, + preds, + block_end_values, + vmap, + ), } } diff --git a/src/backend/llvm/compiler/codegen/instructions/builder_cursor.rs b/src/backend/llvm/compiler/codegen/instructions/builder_cursor.rs index eed102c0..3bb15ac4 100644 --- a/src/backend/llvm/compiler/codegen/instructions/builder_cursor.rs +++ b/src/backend/llvm/compiler/codegen/instructions/builder_cursor.rs @@ -1,9 +1,6 @@ use std::collections::HashMap; -use inkwell::{ - basic_block::BasicBlock, - builder::Builder, -}; +use inkwell::{basic_block::BasicBlock, builder::Builder}; use crate::mir::BasicBlockId; @@ -17,11 +14,21 @@ pub struct BuilderCursor<'ctx, 'b> { impl<'ctx, 'b> BuilderCursor<'ctx, 'b> { pub fn new(builder: &'b Builder<'ctx>) -> Self { - Self { builder, closed_by_bid: HashMap::new(), cur_bid: None, cur_llbb: None } + Self { + builder, + closed_by_bid: HashMap::new(), + cur_bid: None, + cur_llbb: None, + } } /// Temporarily switch to another block, run body, then restore previous position/state. - pub fn with_block(&mut self, bid: BasicBlockId, bb: BasicBlock<'ctx>, body: impl FnOnce(&mut BuilderCursor<'ctx, 'b>) -> R) -> R { + pub fn with_block( + &mut self, + bid: BasicBlockId, + bb: BasicBlock<'ctx>, + body: impl FnOnce(&mut BuilderCursor<'ctx, 'b>) -> R, + ) -> R { let prev_bid = self.cur_bid; let prev_bb = self.cur_llbb; // Preserve previous closed state @@ -66,7 +73,11 @@ impl<'ctx, 'b> BuilderCursor<'ctx, 'b> { pub fn assert_open(&self, bid: BasicBlockId) { if let Some(closed) = self.closed_by_bid.get(&bid) { - assert!(!closed, "attempt to insert into closed block {}", bid.as_u32()); + assert!( + !closed, + "attempt to insert into closed block {}", + bid.as_u32() + ); } } @@ -86,7 +97,11 @@ impl<'ctx, 'b> BuilderCursor<'ctx, 'b> { f(self.builder); // After emitting a terminator, assert the current basic block now has one if let Some(bb) = self.cur_llbb { - assert!(unsafe { bb.get_terminator() }.is_some(), "expected terminator in bb {}", bid.as_u32()); + assert!( + unsafe { bb.get_terminator() }.is_some(), + "expected terminator in bb {}", + bid.as_u32() + ); } self.closed_by_bid.insert(bid, true); } diff --git a/src/backend/llvm/compiler/codegen/instructions/call.rs b/src/backend/llvm/compiler/codegen/instructions/call.rs index 887baefc..3b0c8477 100644 --- a/src/backend/llvm/compiler/codegen/instructions/call.rs +++ b/src/backend/llvm/compiler/codegen/instructions/call.rs @@ -1,10 +1,13 @@ use std::collections::HashMap; -use inkwell::{types::BasicMetadataTypeEnum as BMT, values::{BasicMetadataValueEnum, BasicValueEnum as BVE, FunctionValue}}; +use inkwell::{ + types::BasicMetadataTypeEnum as BMT, + values::{BasicMetadataValueEnum, BasicValueEnum as BVE, FunctionValue}, +}; +use crate::backend::llvm::compiler::codegen::instructions::builder_cursor::BuilderCursor; use crate::backend::llvm::context::CodegenContext; use crate::mir::{function::MirFunction, BasicBlockId, ValueId}; -use crate::backend::llvm::compiler::codegen::instructions::builder_cursor::BuilderCursor; /// Lower a direct Call where callee is provided as a const string ValueId in MIR14. /// @@ -23,9 +26,15 @@ pub(in super::super) fn lower_call<'ctx, 'b>( args: &[ValueId], const_strs: &HashMap, llvm_funcs: &HashMap>, - bb_map: &std::collections::HashMap>, + bb_map: &std::collections::HashMap< + crate::mir::BasicBlockId, + inkwell::basic_block::BasicBlock<'ctx>, + >, preds: &std::collections::HashMap>, - block_end_values: &std::collections::HashMap>>, + block_end_values: &std::collections::HashMap< + crate::mir::BasicBlockId, + std::collections::HashMap>, + >, ) -> Result<(), String> { let name_s = const_strs .get(callee) @@ -51,17 +60,47 @@ pub(in super::super) fn lower_call<'ctx, 'b>( let coerced: BVE<'ctx> = match exp_tys[i] { BMTy::IntType(it) => { // Localize as i64, then adjust width to callee expectation - let iv = resolver.resolve_i64(codegen, cursor, cur_bid, *a, bb_map, preds, block_end_values, vmap)?; + 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, "call_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, "call_arg_trunc")).map_err(|e| e.to_string())?.into() } + 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, "call_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, "call_arg_trunc")) + .map_err(|e| e.to_string())? + .into() + } } BMTy::PointerType(pt) => { // Localize as i64 handle and convert to expected pointer type - let iv = resolver.resolve_i64(codegen, cursor, cur_bid, *a, bb_map, preds, block_end_values, vmap)?; + 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, "call_arg_i2p")) .map_err(|e| e.to_string())?; @@ -69,9 +108,19 @@ pub(in super::super) fn lower_call<'ctx, 'b>( } BMTy::FloatType(ft) => { // Localize as f64, then adjust to callee expectation width if needed - 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 { + 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 { // Cast f64<->f32 as needed cursor .emit_instr(cur_bid, |b| b.build_float_cast(fv, ft, "call_arg_fcast")) diff --git a/src/backend/llvm/compiler/codegen/instructions/consts.rs b/src/backend/llvm/compiler/codegen/instructions/consts.rs index dbe552a3..e5583b80 100644 --- a/src/backend/llvm/compiler/codegen/instructions/consts.rs +++ b/src/backend/llvm/compiler/codegen/instructions/consts.rs @@ -12,11 +12,7 @@ pub(in super::super) fn lower_const<'ctx>( value: &ConstValue, ) -> Result<(), String> { let bval = match value { - ConstValue::Integer(i) => codegen - .context - .i64_type() - .const_int(*i as u64, true) - .into(), + ConstValue::Integer(i) => codegen.context.i64_type().const_int(*i as u64, true).into(), ConstValue::Float(f) => codegen.context.f64_type().const_float(*f).into(), ConstValue::Bool(b) => codegen .context @@ -28,10 +24,7 @@ pub(in super::super) fn lower_const<'ctx>( .builder .build_global_string_ptr(s, "str") .map_err(|e| e.to_string())?; - let len = codegen - .context - .i32_type() - .const_int(s.len() as u64, false); + let len = codegen.context.i32_type().const_int(s.len() as u64, false); // declare i8* @nyash_string_new(i8*, i32) let rt = codegen.context.ptr_type(inkwell::AddressSpace::from(0)); let fn_ty = rt.fn_type( @@ -50,7 +43,11 @@ pub(in super::super) fn lower_const<'ctx>( .unwrap_or_else(|| codegen.module.add_function("nyash_string_new", fn_ty, None)); let call = codegen .builder - .build_call(callee, &[gv.as_pointer_value().into(), len.into()], "strnew") + .build_call( + callee, + &[gv.as_pointer_value().into(), len.into()], + "strnew", + ) .map_err(|e| e.to_string())?; call.try_as_basic_value() .left() diff --git a/src/backend/llvm/compiler/codegen/instructions/ctx.rs b/src/backend/llvm/compiler/codegen/instructions/ctx.rs index 95e2d1b6..308bc913 100644 --- a/src/backend/llvm/compiler/codegen/instructions/ctx.rs +++ b/src/backend/llvm/compiler/codegen/instructions/ctx.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use inkwell::{ basic_block::BasicBlock, - values::{BasicValueEnum as BVE, IntValue, PointerValue, FloatValue}, + values::{BasicValueEnum as BVE, FloatValue, IntValue, PointerValue}, }; use crate::backend::llvm::context::CodegenContext; @@ -70,49 +70,46 @@ impl<'ctx, 'b> LowerFnCtx<'ctx, 'b> { #[inline] pub fn ensure_i64(&mut self, blk: &BlockCtx<'ctx>, v: ValueId) -> LlResult> { self.cursor.assert_open(blk.cur_bid); - self.resolver - .resolve_i64( - self.codegen, - self.cursor, - blk.cur_bid, - v, - self.bb_map, - self.preds, - self.block_end_values, - self.vmap, - ) + self.resolver.resolve_i64( + self.codegen, + self.cursor, + blk.cur_bid, + v, + self.bb_map, + self.preds, + self.block_end_values, + self.vmap, + ) } #[inline] pub fn ensure_ptr(&mut self, blk: &BlockCtx<'ctx>, v: ValueId) -> LlResult> { self.cursor.assert_open(blk.cur_bid); - self.resolver - .resolve_ptr( - self.codegen, - self.cursor, - blk.cur_bid, - v, - self.bb_map, - self.preds, - self.block_end_values, - self.vmap, - ) + self.resolver.resolve_ptr( + self.codegen, + self.cursor, + blk.cur_bid, + v, + self.bb_map, + self.preds, + self.block_end_values, + self.vmap, + ) } #[inline] pub fn ensure_f64(&mut self, blk: &BlockCtx<'ctx>, v: ValueId) -> LlResult> { self.cursor.assert_open(blk.cur_bid); - self.resolver - .resolve_f64( - self.codegen, - self.cursor, - blk.cur_bid, - v, - self.bb_map, - self.preds, - self.block_end_values, - self.vmap, - ) + self.resolver.resolve_f64( + self.codegen, + self.cursor, + blk.cur_bid, + v, + self.bb_map, + self.preds, + self.block_end_values, + self.vmap, + ) } } diff --git a/src/backend/llvm/compiler/codegen/instructions/externcall/console.rs b/src/backend/llvm/compiler/codegen/instructions/externcall/console.rs index ca48e2fb..e99c7e85 100644 --- a/src/backend/llvm/compiler/codegen/instructions/externcall/console.rs +++ b/src/backend/llvm/compiler/codegen/instructions/externcall/console.rs @@ -3,9 +3,9 @@ use std::collections::HashMap; use inkwell::values::BasicValueEnum as BVE; use inkwell::AddressSpace; +use crate::backend::llvm::compiler::codegen::instructions::builder_cursor::BuilderCursor; use crate::backend::llvm::context::CodegenContext; use crate::mir::{BasicBlockId, ValueId}; -use crate::backend::llvm::compiler::codegen::instructions::builder_cursor::BuilderCursor; pub(super) fn lower_log_or_trace<'ctx, 'b>( codegen: &CodegenContext<'ctx>, @@ -17,15 +17,30 @@ pub(super) fn lower_log_or_trace<'ctx, 'b>( iface_name: &str, method_name: &str, args: &[ValueId], - bb_map: &std::collections::HashMap>, + bb_map: &std::collections::HashMap< + crate::mir::BasicBlockId, + inkwell::basic_block::BasicBlock<'ctx>, + >, preds: &std::collections::HashMap>, - block_end_values: &std::collections::HashMap>>, + block_end_values: &std::collections::HashMap< + crate::mir::BasicBlockId, + std::collections::HashMap>, + >, ) -> Result<(), String> { if args.len() != 1 { return Err(format!("{}.{} expects 1 arg", iface_name, method_name)); } // 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 arg_val = resolver.resolve_i64( + codegen, + cursor, + cur_bid, + args[0], + bb_map, + preds, + block_end_values, + vmap, + )?; let i64t = codegen.context.i64_type(); let fnty = i64t.fn_type(&[i64t.into()], false); let fname = if iface_name == "env.console" { @@ -42,7 +57,9 @@ pub(super) fn lower_log_or_trace<'ctx, 'b>( .get_function(fname) .unwrap_or_else(|| codegen.module.add_function(fname, fnty, None)); let _ = cursor - .emit_instr(cur_bid, |b| b.build_call(callee, &[arg_val.into()], "console_log_h")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[arg_val.into()], "console_log_h") + }) .map_err(|e| e.to_string())?; if let Some(d) = dst { vmap.insert(*d, codegen.context.i64_type().const_zero().into()); @@ -66,7 +83,11 @@ pub(super) fn lower_readline<'ctx, 'b>( let callee = codegen .module .get_function("nyash.console.readline") - .unwrap_or_else(|| codegen.module.add_function("nyash.console.readline", fnty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.console.readline", fnty, None) + }); let call = cursor .emit_instr(cur_bid, |b| b.build_call(callee, &[], "readline")) .map_err(|e| e.to_string())?; diff --git a/src/backend/llvm/compiler/codegen/instructions/externcall/env.rs b/src/backend/llvm/compiler/codegen/instructions/externcall/env.rs index 42ee9d8d..54ee32fb 100644 --- a/src/backend/llvm/compiler/codegen/instructions/externcall/env.rs +++ b/src/backend/llvm/compiler/codegen/instructions/externcall/env.rs @@ -3,9 +3,9 @@ use std::collections::HashMap; use inkwell::values::BasicValueEnum as BVE; use inkwell::AddressSpace; +use crate::backend::llvm::compiler::codegen::instructions::builder_cursor::BuilderCursor; use crate::backend::llvm::context::CodegenContext; use crate::mir::{function::MirFunction, BasicBlockId, ValueId}; -use crate::backend::llvm::compiler::codegen::instructions::builder_cursor::BuilderCursor; pub(super) fn lower_future_spawn_instance<'ctx, 'b>( codegen: &CodegenContext<'ctx>, @@ -15,24 +15,54 @@ pub(super) fn lower_future_spawn_instance<'ctx, 'b>( vmap: &mut HashMap>, dst: &Option, args: &[ValueId], - bb_map: &std::collections::HashMap>, + bb_map: &std::collections::HashMap< + crate::mir::BasicBlockId, + inkwell::basic_block::BasicBlock<'ctx>, + >, preds: &std::collections::HashMap>, - block_end_values: &std::collections::HashMap>>, + block_end_values: &std::collections::HashMap< + crate::mir::BasicBlockId, + std::collections::HashMap>, + >, ) -> Result<(), String> { if args.len() < 2 { return Err("env.future.spawn_instance expects at least (recv, method_name)".to_string()); } let i64t = codegen.context.i64_type(); let i8p = codegen.context.ptr_type(AddressSpace::from(0)); - 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 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 .get_function("nyash.future.spawn_instance") - .unwrap_or_else(|| codegen.module.add_function("nyash.future.spawn_instance", fnty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.future.spawn_instance", fnty, None) + }); let call = cursor - .emit_instr(cur_bid, |b| b.build_call(callee, &[recv_h.into(), name_p.into()], "spawn_instance")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[recv_h.into(), name_p.into()], "spawn_instance") + }) .map_err(|e| e.to_string())?; if let Some(d) = dst { let rv = call @@ -53,24 +83,44 @@ pub(super) fn lower_local_get<'ctx, 'b>( vmap: &mut HashMap>, dst: &Option, args: &[ValueId], - _bb_map: &std::collections::HashMap>, + _bb_map: &std::collections::HashMap< + crate::mir::BasicBlockId, + inkwell::basic_block::BasicBlock<'ctx>, + >, _preds: &std::collections::HashMap>, - _block_end_values: &std::collections::HashMap>>, + _block_end_values: &std::collections::HashMap< + crate::mir::BasicBlockId, + std::collections::HashMap>, + >, ) -> Result<(), String> { if args.len() != 1 { return Err("env.local.get expects 1 arg".to_string()); } - let name_p = _resolver - .resolve_ptr(codegen, cursor, cur_bid, args[0], _bb_map, _preds, _block_end_values, vmap)?; + 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); let callee = codegen .module .get_function("nyash.env.local.get_h") - .unwrap_or_else(|| codegen.module.add_function("nyash.env.local.get_h", fnty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.env.local.get_h", fnty, None) + }); let call = cursor - .emit_instr(cur_bid, |b| b.build_call(callee, &[name_p.into()], "local_get_h")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[name_p.into()], "local_get_h") + }) .map_err(|e| e.to_string())?; let rv = call .try_as_basic_value() @@ -94,7 +144,9 @@ pub(super) fn lower_local_get<'ctx, 'b>( let h = rv.into_int_value(); let pty = codegen.context.ptr_type(AddressSpace::from(0)); let ptr = cursor - .emit_instr(cur_bid, |b| b.build_int_to_ptr(h, pty, "local_get_handle_to_ptr")) + .emit_instr(cur_bid, |b| { + b.build_int_to_ptr(h, pty, "local_get_handle_to_ptr") + }) .map_err(|e| e.to_string())?; vmap.insert(*d, ptr.into()); } @@ -118,24 +170,40 @@ pub(super) fn lower_box_new<'ctx, 'b>( vmap: &mut HashMap>, dst: &Option, args: &[ValueId], - bb_map: &std::collections::HashMap>, + bb_map: &std::collections::HashMap< + crate::mir::BasicBlockId, + inkwell::basic_block::BasicBlock<'ctx>, + >, preds: &std::collections::HashMap>, - block_end_values: &std::collections::HashMap>>, + block_end_values: &std::collections::HashMap< + crate::mir::BasicBlockId, + std::collections::HashMap>, + >, ) -> Result<(), String> { // Two variants: (name) and (argc, arg1, arg2, arg3, arg4) with optional ptr conversion // Prefer the i64 birth when possible; else call env.box.new(name) let i64t = codegen.context.i64_type(); let i8p = codegen.context.ptr_type(AddressSpace::from(0)); if args.len() == 1 { - let name_p = resolver - .resolve_ptr(codegen, cursor, cur_bid, args[0], bb_map, preds, block_end_values, vmap)?; + 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 .get_function("nyash.env.box.new") .unwrap_or_else(|| codegen.module.add_function("nyash.env.box.new", fnty, None)); let call = cursor - .emit_instr(cur_bid, |b| b.build_call(callee, &[name_p.into()], "env_box_new")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[name_p.into()], "env_box_new") + }) .map_err(|e| e.to_string())?; let h = call .try_as_basic_value() @@ -167,102 +235,248 @@ pub(super) fn lower_box_new<'ctx, 'b>( let callee = codegen .module .get_function("nyash.env.box.new_i64") - .unwrap_or_else(|| codegen.module.add_function("nyash.env.box.new_i64", fnty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.env.box.new_i64", fnty, None) + }); // arg0: type name string pointer if args.is_empty() { return Err("env.box.new_i64 requires at least type name".to_string()); } - let ty_ptr = resolver - .resolve_ptr(codegen, cursor, cur_bid, args[0], bb_map, preds, block_end_values, vmap)?; + 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 { 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 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 .get_function("nyash.box.from_f64") - .unwrap_or_else(|| codegen.module.add_function("nyash.box.from_f64", fnty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.box.from_f64", fnty, None) + }); let call = cursor - .emit_instr(cur_bid, |b| b.build_call(callee, &[fv.into()], "arg1_f64_to_box")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[fv.into()], "arg1_f64_to_box") + }) .map_err(|e| e.to_string())?; - call.try_as_basic_value().left().ok_or("from_f64 returned void".to_string())?.into_int_value() + call.try_as_basic_value() + .left() + .ok_or("from_f64 returned void".to_string())? + .into_int_value() } Some(crate::mir::MirType::String) => { - let pv = resolver.resolve_ptr(codegen, cursor, cur_bid, args[1], bb_map, preds, block_end_values, vmap)?; + 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)); + .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")) + .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() + 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)?, + _ => 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 { 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 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 .get_function("nyash.box.from_f64") - .unwrap_or_else(|| codegen.module.add_function("nyash.box.from_f64", fnty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.box.from_f64", fnty, None) + }); let call = cursor - .emit_instr(cur_bid, |b| b.build_call(callee, &[fv.into()], "arg2_f64_to_box")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[fv.into()], "arg2_f64_to_box") + }) .map_err(|e| e.to_string())?; - call.try_as_basic_value().left().ok_or("from_f64 returned void".to_string())?.into_int_value() + call.try_as_basic_value() + .left() + .ok_or("from_f64 returned void".to_string())? + .into_int_value() } Some(crate::mir::MirType::String) => { - let pv = resolver.resolve_ptr(codegen, cursor, cur_bid, args[2], bb_map, preds, block_end_values, vmap)?; + 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)); + .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")) + .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() + 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)?, + _ => 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 { 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::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 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 .get_function("nyash.box.from_f64") - .unwrap_or_else(|| codegen.module.add_function("nyash.box.from_f64", fnty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.box.from_f64", fnty, None) + }); let call = cursor - .emit_instr(cur_bid, |b| b.build_call(callee, &[fv.into()], "arg3_f64_to_box")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[fv.into()], "arg3_f64_to_box") + }) .map_err(|e| e.to_string())?; - call.try_as_basic_value().left().ok_or("from_f64 returned void".to_string())?.into_int_value() + call.try_as_basic_value() + .left() + .ok_or("from_f64 returned void".to_string())? + .into_int_value() } Some(crate::mir::MirType::String) => { - let pv = resolver.resolve_ptr(codegen, cursor, cur_bid, args[3], bb_map, preds, block_end_values, vmap)?; + 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 .get_function("nyash.box.from_i8_string") - .unwrap_or_else(|| codegen.module.add_function("nyash.box.from_i8_string", fnty, None)); + .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()], "arg3_i8_to_box")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[pv.into()], "arg3_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() + 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()), }; @@ -270,50 +484,109 @@ pub(super) fn lower_box_new<'ctx, 'b>( let mut a4 = i64t.const_zero(); if args.len() >= 5 { 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::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 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 .get_function("nyash.box.from_f64") - .unwrap_or_else(|| codegen.module.add_function("nyash.box.from_f64", fnty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.box.from_f64", fnty, None) + }); let call = cursor - .emit_instr(cur_bid, |b| b.build_call(callee, &[fv.into()], "arg4_f64_to_box")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[fv.into()], "arg4_f64_to_box") + }) .map_err(|e| e.to_string())?; - call.try_as_basic_value().left().ok_or("from_f64 returned void".to_string())?.into_int_value() + call.try_as_basic_value() + .left() + .ok_or("from_f64 returned void".to_string())? + .into_int_value() } Some(crate::mir::MirType::String) => { - let pv = resolver.resolve_ptr(codegen, cursor, cur_bid, args[4], bb_map, preds, block_end_values, vmap)?; + 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 .get_function("nyash.box.from_i8_string") - .unwrap_or_else(|| codegen.module.add_function("nyash.box.from_i8_string", fnty, None)); + .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()], "arg4_i8_to_box")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[pv.into()], "arg4_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() + 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 call = cursor - .emit_instr(cur_bid, |b| b.build_call( - callee, - &[ty_ptr.into(), argc_val.into(), a1.into(), a2.into(), a3.into(), a4.into()], - "env_box_new_i64x", - )) + .emit_instr(cur_bid, |b| { + b.build_call( + callee, + &[ + ty_ptr.into(), + argc_val.into(), + a1.into(), + a2.into(), + a3.into(), + a4.into(), + ], + "env_box_new_i64x", + ) + }) .map_err(|e| e.to_string())?; let rv = call .try_as_basic_value() .left() .ok_or("env.box.new_i64 returned void".to_string())?; - let i64v = if let BVE::IntValue(iv) = rv { iv } else { return Err("env.box.new_i64 ret expected i64".to_string()); }; + let i64v = if let BVE::IntValue(iv) = rv { + iv + } else { + return Err("env.box.new_i64 ret expected i64".to_string()); + }; let out_ptr = cursor - .emit_instr(cur_bid, |b| b.build_int_to_ptr(i64v, i8p, "box_handle_to_ptr")) + .emit_instr(cur_bid, |b| { + b.build_int_to_ptr(i64v, i8p, "box_handle_to_ptr") + }) .map_err(|e| e.to_string())?; if let Some(d) = dst { vmap.insert(*d, out_ptr.into()); diff --git a/src/backend/llvm/compiler/codegen/instructions/externcall/mod.rs b/src/backend/llvm/compiler/codegen/instructions/externcall/mod.rs index e7a361f4..d846b823 100644 --- a/src/backend/llvm/compiler/codegen/instructions/externcall/mod.rs +++ b/src/backend/llvm/compiler/codegen/instructions/externcall/mod.rs @@ -3,10 +3,10 @@ mod env; use std::collections::HashMap; +use crate::backend::llvm::compiler::codegen::instructions::builder_cursor::BuilderCursor; use crate::backend::llvm::context::CodegenContext; use crate::mir::{function::MirFunction, BasicBlockId, ValueId}; use inkwell::values::BasicValueEnum as BVE; -use crate::backend::llvm::compiler::codegen::instructions::builder_cursor::BuilderCursor; /// Full ExternCall lowering dispatcher (console/debug/env.*) pub(in super::super) fn lower_externcall<'ctx, 'b>( @@ -20,16 +20,34 @@ pub(in super::super) fn lower_externcall<'ctx, 'b>( iface_name: &str, method_name: &str, args: &[ValueId], - bb_map: &std::collections::HashMap>, + bb_map: &std::collections::HashMap< + crate::mir::BasicBlockId, + inkwell::basic_block::BasicBlock<'ctx>, + >, preds: &std::collections::HashMap>, - block_end_values: &std::collections::HashMap>>, + block_end_values: &std::collections::HashMap< + crate::mir::BasicBlockId, + std::collections::HashMap>, + >, ) -> Result<(), String> { // console/debug - if (iface_name == "env.console" - && matches!(method_name, "log" | "warn" | "error")) + if (iface_name == "env.console" && matches!(method_name, "log" | "warn" | "error")) || (iface_name == "env.debug" && method_name == "trace") { - return console::lower_log_or_trace(codegen, cursor, resolver, cur_bid, vmap, dst, iface_name, method_name, args, bb_map, preds, block_end_values); + return console::lower_log_or_trace( + codegen, + cursor, + resolver, + cur_bid, + vmap, + dst, + iface_name, + method_name, + args, + bb_map, + preds, + block_end_values, + ); } if iface_name == "env.console" && method_name == "readLine" { return console::lower_readline(codegen, cursor, cur_bid, vmap, dst, args); @@ -37,13 +55,48 @@ pub(in super::super) fn lower_externcall<'ctx, 'b>( // env.* if iface_name == "env.future" && method_name == "spawn_instance" { - return env::lower_future_spawn_instance(codegen, cursor, resolver, cur_bid, vmap, dst, args, bb_map, preds, block_end_values); + return env::lower_future_spawn_instance( + codegen, + cursor, + resolver, + cur_bid, + vmap, + dst, + args, + bb_map, + preds, + block_end_values, + ); } if iface_name == "env.local" && method_name == "get" { - return env::lower_local_get(codegen, cursor, resolver, cur_bid, func, vmap, dst, args, bb_map, preds, block_end_values); + 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, func, 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!( diff --git a/src/backend/llvm/compiler/codegen/instructions/flow.rs b/src/backend/llvm/compiler/codegen/instructions/flow.rs index 087c4549..a26f2cf5 100644 --- a/src/backend/llvm/compiler/codegen/instructions/flow.rs +++ b/src/backend/llvm/compiler/codegen/instructions/flow.rs @@ -5,11 +5,13 @@ use std::collections::HashMap; use crate::backend::llvm::context::CodegenContext; use crate::mir::{function::MirFunction, BasicBlockId, ValueId}; -use super::super::types::{to_bool, map_mirtype_to_basic}; +use super::super::types::{map_mirtype_to_basic, to_bool}; use super::builder_cursor::BuilderCursor; use super::Resolver; -fn phi_trace_on() -> bool { std::env::var("NYASH_LLVM_TRACE_PHI").ok().as_deref() == Some("1") } +fn phi_trace_on() -> bool { + std::env::var("NYASH_LLVM_TRACE_PHI").ok().as_deref() == Some("1") +} pub(in super::super) fn emit_return<'ctx, 'b>( codegen: &CodegenContext<'ctx>, @@ -25,7 +27,9 @@ pub(in super::super) fn emit_return<'ctx, 'b>( ) -> Result<(), String> { match (&func.signature.return_type, value) { (crate::mir::MirType::Void, _) => { - cursor.emit_term(cur_bid, |b| { b.build_return(None).unwrap(); }); + cursor.emit_term(cur_bid, |b| { + b.build_return(None).unwrap(); + }); Ok(()) } (_t, Some(vid)) => { @@ -34,30 +38,83 @@ pub(in super::super) fn emit_return<'ctx, 'b>( use inkwell::types::BasicTypeEnum as BT; 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)?; + 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() } + 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) => { - let pv = resolver.resolve_ptr(codegen, cursor, cur_bid, *vid, bb_map, preds, block_end_values, vmap)?; + 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() } + 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() } + 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() + } } _ => return Err("unsupported return basic type".to_string()), }; cursor.emit_term(cur_bid, |b| { - b.build_return(Some(&v_adj)).map_err(|e| e.to_string()).unwrap(); + b.build_return(Some(&v_adj)) + .map_err(|e| e.to_string()) + .unwrap(); }); Ok(()) } @@ -82,10 +139,16 @@ pub(in super::super) fn emit_jump<'ctx, 'b>( eprintln!("[LLVM] emit_jump: {} -> {}", bid.as_u32(), target.as_u32()); } cursor.emit_term(bid, |b| { - b.build_unconditional_branch(tbb).map_err(|e| e.to_string()).unwrap(); + b.build_unconditional_branch(tbb) + .map_err(|e| e.to_string()) + .unwrap(); }); if phi_trace_on() { - eprintln!("[PHI:jump] pred={} -> succ={}", bid.as_u32(), target.as_u32()); + eprintln!( + "[PHI:jump] pred={} -> succ={}", + bid.as_u32(), + target.as_u32() + ); } Ok(()) } @@ -108,7 +171,16 @@ pub(in super::super) fn emit_branch<'ctx, 'b>( block_end_values: &HashMap>>, ) -> Result<(), String> { // 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 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 @@ -118,10 +190,17 @@ pub(in super::super) fn emit_branch<'ctx, 'b>( let tbb = *bb_map.get(then_bb).ok_or("then bb missing")?; let ebb = *bb_map.get(else_bb).ok_or("else bb missing")?; if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("[LLVM] emit_branch: {} -> then {} / else {}", bid.as_u32(), then_bb.as_u32(), else_bb.as_u32()); + eprintln!( + "[LLVM] emit_branch: {} -> then {} / else {}", + bid.as_u32(), + then_bb.as_u32(), + else_bb.as_u32() + ); } cursor.emit_term(bid, |bd| { - bd.build_conditional_branch(b, tbb, ebb).map_err(|e| e.to_string()).unwrap(); + bd.build_conditional_branch(b, tbb, ebb) + .map_err(|e| e.to_string()) + .unwrap(); }); Ok(()) } @@ -147,7 +226,10 @@ fn coerce_to_type<'ctx>( .into()) } else if bw_dst == 1 { // Narrow to i1 via != 0 - Ok(super::super::types::to_bool(codegen.context, iv.into(), &codegen.builder)?.into()) + Ok( + super::super::types::to_bool(codegen.context, iv.into(), &codegen.builder)? + .into(), + ) } else { Ok(codegen .builder @@ -206,8 +288,8 @@ pub(in super::super) fn seal_block<'ctx, 'b>( if let Some((_, in_vid)) = inputs.iter().find(|(p, _)| p == &bid) { // Prefer the predecessorの block-end snapshot。なければ型ゼロを合成 let snap_opt = block_end_values - .get(&bid) - .and_then(|m| m.get(in_vid).copied()); + .get(&bid) + .and_then(|m| m.get(in_vid).copied()); let mut val = if let Some(sv) = snap_opt { sv } else { @@ -224,60 +306,70 @@ pub(in super::super) fn seal_block<'ctx, 'b>( )), } }; - // Insert any required casts in the predecessor block, right before its terminator - if let Some(pred_llbb) = bb_map.get(&bid) { - cursor.with_block(bid, *pred_llbb, |c| { - let term = unsafe { pred_llbb.get_terminator() }; - if let Some(t) = term { codegen.builder.position_before(&t); } - else { c.position_at_end(*pred_llbb); } - val = coerce_to_type(codegen, phi, val).expect("coerce_to_type in seal_block"); - }); - } - let pred_bb = *bb_map.get(&bid).ok_or("pred bb missing")?; - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - let tys = phi - .as_basic_value() - .get_type() - .print_to_string() - .to_string(); - eprintln!( - "[PHI] sealed add pred_bb={} val={} ty={}{}", - bid.as_u32(), - in_vid.as_u32(), - tys, - if snap_opt.is_some() { " (snapshot)" } else { " (synth)" } - ); - } - match val { - BasicValueEnum::IntValue(iv) => phi.add_incoming(&[(&iv, pred_bb)]), - BasicValueEnum::FloatValue(fv) => phi.add_incoming(&[(&fv, pred_bb)]), - BasicValueEnum::PointerValue(pv) => phi.add_incoming(&[(&pv, pred_bb)]), - _ => return Err("unsupported phi incoming value (seal)".to_string()), - } + // Insert any required casts in the predecessor block, right before its terminator + if let Some(pred_llbb) = bb_map.get(&bid) { + cursor.with_block(bid, *pred_llbb, |c| { + let term = unsafe { pred_llbb.get_terminator() }; + if let Some(t) = term { + codegen.builder.position_before(&t); + } else { + c.position_at_end(*pred_llbb); + } + val = coerce_to_type(codegen, phi, val) + .expect("coerce_to_type in seal_block"); + }); + } + let pred_bb = *bb_map.get(&bid).ok_or("pred bb missing")?; + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + let tys = phi + .as_basic_value() + .get_type() + .print_to_string() + .to_string(); + eprintln!( + "[PHI] sealed add pred_bb={} val={} ty={}{}", + bid.as_u32(), + in_vid.as_u32(), + tys, + if snap_opt.is_some() { + " (snapshot)" + } else { + " (synth)" + } + ); + } + match val { + BasicValueEnum::IntValue(iv) => phi.add_incoming(&[(&iv, pred_bb)]), + BasicValueEnum::FloatValue(fv) => phi.add_incoming(&[(&fv, pred_bb)]), + BasicValueEnum::PointerValue(pv) => phi.add_incoming(&[(&pv, pred_bb)]), + _ => return Err("unsupported phi incoming value (seal)".to_string()), + } } else { // Missing mapping for this predecessor: synthesize a typed zero - let pred_bb = *bb_map.get(&bid).ok_or("pred bb missing")?; - use inkwell::types::BasicTypeEnum as BT; - let bt = phi.as_basic_value().get_type(); - let z: BasicValueEnum = 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("unsupported phi type for zero synth (seal)".to_string()), - }; - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!( - "[PHI] sealed add (synth) pred_bb={} zero-ty={}", - bid.as_u32(), - bt.print_to_string().to_string() - ); - } - match z { - BasicValueEnum::IntValue(iv) => phi.add_incoming(&[(&iv, pred_bb)]), - BasicValueEnum::FloatValue(fv) => phi.add_incoming(&[(&fv, pred_bb)]), - BasicValueEnum::PointerValue(pv) => phi.add_incoming(&[(&pv, pred_bb)]), - _ => return Err("unsupported phi incoming (synth)".to_string()), + let pred_bb = *bb_map.get(&bid).ok_or("pred bb missing")?; + use inkwell::types::BasicTypeEnum as BT; + let bt = phi.as_basic_value().get_type(); + let z: BasicValueEnum = 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("unsupported phi type for zero synth (seal)".to_string()) } + }; + if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { + eprintln!( + "[PHI] sealed add (synth) pred_bb={} zero-ty={}", + bid.as_u32(), + bt.print_to_string().to_string() + ); + } + match z { + BasicValueEnum::IntValue(iv) => phi.add_incoming(&[(&iv, pred_bb)]), + BasicValueEnum::FloatValue(fv) => phi.add_incoming(&[(&fv, pred_bb)]), + BasicValueEnum::PointerValue(pv) => phi.add_incoming(&[(&pv, pred_bb)]), + _ => return Err("unsupported phi incoming (synth)".to_string()), + } } } } @@ -303,7 +395,9 @@ pub(in super::super) fn finalize_phis<'ctx, 'b>( vmap: &HashMap>, ) -> Result<(), String> { let pred_list = preds.get(&succ_bb).cloned().unwrap_or_default(); - if pred_list.is_empty() { return Ok(()); } + if pred_list.is_empty() { + return Ok(()); + } if let Some(phis) = phis_by_block.get(&succ_bb) { for (_dst, phi, inputs) in phis { for pred in &pred_list { @@ -314,7 +408,9 @@ pub(in super::super) fn finalize_phis<'ctx, 'b>( // we add at most once per pred in seal_block. If duplicates occurred earlier, // adding again is harmlessly ignored by verifier if identical; otherwise rely on our new regime. // Fetch value snapshot at end of pred; fallback per our policy - let snap_opt = block_end_values.get(pred).and_then(|m| m.get(in_vid).copied()); + 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 { @@ -334,17 +430,26 @@ pub(in super::super) fn finalize_phis<'ctx, 'b>( if let Some(pred_llbb) = bb_map.get(pred) { cursor.with_block(*pred, *pred_llbb, |c| { let term = unsafe { pred_llbb.get_terminator() }; - if let Some(t) = term { codegen.builder.position_before(&t); } - else { c.position_at_end(*pred_llbb); } - val = coerce_to_type(codegen, phi, val).expect("coerce_to_type finalize_phis"); + if let Some(t) = term { + codegen.builder.position_before(&t); + } else { + c.position_at_end(*pred_llbb); + } + val = coerce_to_type(codegen, phi, val) + .expect("coerce_to_type finalize_phis"); }); } let pred_bb = *bb_map.get(pred).ok_or("pred bb missing")?; if phi_trace_on() { eprintln!( "[PHI:finalize] succ={} pred={} vid={} ty={}", - succ_bb.as_u32(), pred.as_u32(), in_vid.as_u32(), - phi.as_basic_value().get_type().print_to_string().to_string() + succ_bb.as_u32(), + pred.as_u32(), + in_vid.as_u32(), + phi.as_basic_value() + .get_type() + .print_to_string() + .to_string() ); } match val { @@ -362,12 +467,16 @@ pub(in super::super) fn finalize_phis<'ctx, 'b>( BT::IntType(it) => it.const_zero().into(), BT::FloatType(ft) => ft.const_zero().into(), BT::PointerType(pt) => pt.const_zero().into(), - _ => return Err("unsupported phi type for zero synth (finalize)".to_string()), + _ => { + return Err("unsupported phi type for zero synth (finalize)".to_string()) + } }; if phi_trace_on() { eprintln!( "[PHI:finalize] succ={} pred={} vid=? ty={} src=synth_zero", - succ_bb.as_u32(), pred.as_u32(), bt.print_to_string().to_string() + succ_bb.as_u32(), + pred.as_u32(), + bt.print_to_string().to_string() ); } match z { @@ -393,7 +502,10 @@ pub(in super::super) fn localize_to_i64<'ctx, 'b>( vid: ValueId, bb_map: &std::collections::HashMap>, preds: &std::collections::HashMap>, - block_end_values: &std::collections::HashMap>>, + block_end_values: &std::collections::HashMap< + BasicBlockId, + std::collections::HashMap>, + >, vmap: &std::collections::HashMap>, ) -> Result, String> { let i64t = codegen.context.i64_type(); @@ -413,7 +525,10 @@ pub(in super::super) fn localize_to_i64<'ctx, 'b>( } else { codegen.builder.position_at_end(cur_llbb); } - let phi = codegen.builder.build_phi(i64t, &format!("loc_i64_{}", vid.as_u32())).map_err(|e| e.to_string())?; + let phi = codegen + .builder + .build_phi(i64t, &format!("loc_i64_{}", vid.as_u32())) + .map_err(|e| e.to_string())?; 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 @@ -425,13 +540,33 @@ pub(in super::super) fn localize_to_i64<'ctx, 'b>( 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); } + 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() } + 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(), + 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(), }; }); @@ -439,11 +574,15 @@ pub(in super::super) fn localize_to_i64<'ctx, 'b>( if phi_trace_on() { eprintln!( "[PHI:resolve] cur={} pred={} vid={} ty=i64", - cur_bid.as_u32(), p.as_u32(), vid.as_u32() + cur_bid.as_u32(), + p.as_u32(), + vid.as_u32() ); } } // Restore insertion point - if let Some(bb) = saved_ip { codegen.builder.position_at_end(bb); } + if let Some(bb) = saved_ip { + codegen.builder.position_at_end(bb); + } Ok(phi.as_basic_value().into_int_value()) } diff --git a/src/backend/llvm/compiler/codegen/instructions/loopform.rs b/src/backend/llvm/compiler/codegen/instructions/loopform.rs index 928942c0..b5b78cf0 100644 --- a/src/backend/llvm/compiler/codegen/instructions/loopform.rs +++ b/src/backend/llvm/compiler/codegen/instructions/loopform.rs @@ -4,16 +4,11 @@ use inkwell::{ }; use crate::backend::llvm::context::CodegenContext; -use crate::mir::{ - function::MirFunction, - instruction::MirInstruction, - BasicBlockId, - ValueId, -}; +use crate::mir::{function::MirFunction, instruction::MirInstruction, BasicBlockId, ValueId}; +use super::super::types::to_bool; use super::builder_cursor::BuilderCursor; use super::Resolver; -use super::super::types::to_bool; /// LoopForm scaffolding — fixed block layout for while/loop normalization pub struct LoopFormContext<'ctx> { @@ -52,7 +47,15 @@ impl<'ctx> LoopFormContext<'ctx> { let exit = codegen .context .append_basic_block(function, &format!("{}_lf{}_exit", prefix, loop_id)); - Self { preheader, header, body, dispatch, latch, exit, loop_id } + Self { + preheader, + header, + body, + dispatch, + latch, + exit, + loop_id, + } } } @@ -76,13 +79,26 @@ pub fn lower_while_loopform<'ctx, 'b>( bb_map: &std::collections::HashMap>, vmap: &std::collections::HashMap>, preds: &std::collections::HashMap>, - block_end_values: &std::collections::HashMap>>, + block_end_values: &std::collections::HashMap< + BasicBlockId, + std::collections::HashMap>, + >, // Registry to allow later body→dispatch wiring (simple bodies) - registry: &mut std::collections::HashMap, PhiValue<'ctx>, PhiValue<'ctx>, BasicBlock<'ctx>)>, + registry: &mut std::collections::HashMap< + BasicBlockId, + ( + BasicBlock<'ctx>, + PhiValue<'ctx>, + PhiValue<'ctx>, + BasicBlock<'ctx>, + ), + >, body_to_header: &mut std::collections::HashMap, ) -> Result { let enabled = std::env::var("NYASH_ENABLE_LOOPFORM").ok().as_deref() == Some("1"); - if !enabled { return Ok(false); } + if !enabled { + return Ok(false); + } // Create LoopForm fixed blocks under the same function let lf = LoopFormContext::new(codegen, llvm_func, loop_id, prefix); @@ -95,7 +111,16 @@ pub fn lower_while_loopform<'ctx, 'b>( .unwrap(); // 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 ci = resolver.resolve_i64( + codegen, + cursor, + header_bid, + *condition, + bb_map, + preds, + block_end_values, + vmap, + )?; let cond_i1 = codegen .builder .build_int_compare( @@ -124,7 +149,9 @@ pub fn lower_while_loopform<'ctx, 'b>( // Dispatch: create PHIs (tag i8, payload i64) and switch(tag) // For now, only header(false) contributes (Break=1); body path does not reach dispatch in Phase 1 wiring. let orig_after = *bb_map.get(&after_bb).ok_or("loopform: after bb missing")?; - let header_llbb = *bb_map.get(&header_bid).ok_or("loopform: header bb missing")?; + let header_llbb = *bb_map + .get(&header_bid) + .ok_or("loopform: header bb missing")?; let (tag_phi, payload_phi) = cursor.with_block(after_bb, lf.dispatch, |c| { let i8t = codegen.context.i8_type(); let i64t = codegen.context.i64_type(); @@ -219,20 +246,35 @@ pub fn normalize_header_phis_for_latch<'ctx>( pub(in super::super) fn dev_check_dispatch_only_phi<'ctx>( phis_by_block: &std::collections::HashMap< crate::mir::BasicBlockId, - Vec<(crate::mir::ValueId, inkwell::values::PhiValue<'ctx>, Vec<(crate::mir::BasicBlockId, crate::mir::ValueId)>)>, + Vec<( + crate::mir::ValueId, + inkwell::values::PhiValue<'ctx>, + Vec<(crate::mir::BasicBlockId, crate::mir::ValueId)>, + )>, >, loopform_registry: &std::collections::HashMap< crate::mir::BasicBlockId, - (inkwell::basic_block::BasicBlock<'ctx>, inkwell::values::PhiValue<'ctx>, inkwell::values::PhiValue<'ctx>, inkwell::basic_block::BasicBlock<'ctx>) + ( + inkwell::basic_block::BasicBlock<'ctx>, + inkwell::values::PhiValue<'ctx>, + inkwell::values::PhiValue<'ctx>, + inkwell::basic_block::BasicBlock<'ctx>, + ), >, ) { - if std::env::var("NYASH_DEV_CHECK_DISPATCH_ONLY_PHI").ok().as_deref() != Some("1") { + if std::env::var("NYASH_DEV_CHECK_DISPATCH_ONLY_PHI") + .ok() + .as_deref() + != Some("1") + { return; } // Best-effort: Just report PHI presence per block when LoopForm registry is non-empty. if !loopform_registry.is_empty() { for (bid, phis) in phis_by_block.iter() { - if phis.is_empty() { continue; } + if phis.is_empty() { + continue; + } eprintln!("[DEV][PHI] bb={} has {} PHI(s)", bid.as_u32(), phis.len()); } } diff --git a/src/backend/llvm/compiler/codegen/instructions/maps.rs b/src/backend/llvm/compiler/codegen/instructions/maps.rs index ef43a33b..6111e1c7 100644 --- a/src/backend/llvm/compiler/codegen/instructions/maps.rs +++ b/src/backend/llvm/compiler/codegen/instructions/maps.rs @@ -2,9 +2,9 @@ use std::collections::HashMap; use inkwell::{values::BasicValueEnum as BVE, AddressSpace}; +use super::builder_cursor::BuilderCursor; use crate::backend::llvm::context::CodegenContext; use crate::mir::{function::MirFunction, BasicBlockId, ValueId}; -use super::builder_cursor::BuilderCursor; /// Handle MapBox fast-paths (core-first). Returns true if handled. pub(super) fn try_handle_map_method<'ctx, 'b>( @@ -61,18 +61,49 @@ pub(super) fn try_handle_map_method<'ctx, 'b>( 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 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)); + .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")) + .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() + 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)? + _ => 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 @@ -80,7 +111,9 @@ pub(super) fn try_handle_map_method<'ctx, 'b>( .get_function("nyash.map.has_h") .unwrap_or_else(|| codegen.module.add_function("nyash.map.has_h", fnty, None)); let call = cursor - .emit_instr(cur_bid, |b| b.build_call(callee, &[recv_h.into(), key_i.into()], "mhas")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[recv_h.into(), key_i.into()], "mhas") + }) .map_err(|e| e.to_string())?; if let Some(d) = dst { let rv = call @@ -101,15 +134,32 @@ pub(super) fn try_handle_map_method<'ctx, 'b>( 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 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)); + .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")) + .emit_instr(cur_bid, |b| { + b.build_call(conv, &[pv.into()], "key_i8_to_handle") + }) .map_err(|e| e.to_string())?; let kh = kcall .try_as_basic_value() @@ -120,20 +170,37 @@ pub(super) fn try_handle_map_method<'ctx, 'b>( let callee = codegen .module .get_function("nyash.map.get_hh") - .unwrap_or_else(|| codegen.module.add_function("nyash.map.get_hh", fnty, None)); + .unwrap_or_else(|| { + codegen.module.add_function("nyash.map.get_hh", fnty, None) + }); cursor - .emit_instr(cur_bid, |b| b.build_call(callee, &[recv_h.into(), kh.into()], "mget_hh")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[recv_h.into(), kh.into()], "mget_hh") + }) .map_err(|e| e.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 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)); + .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")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[recv_h.into(), iv.into()], "mget") + }) .map_err(|e| e.to_string())? } }; @@ -155,27 +222,69 @@ pub(super) fn try_handle_map_method<'ctx, 'b>( } 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 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)); + .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")) + .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() + 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)? + _ => 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 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 .get_function("nyash.map.set_h") .unwrap_or_else(|| codegen.module.add_function("nyash.map.set_h", fnty, None)); let _ = cursor - .emit_instr(cur_bid, |b| b.build_call(callee, &[recv_h.into(), key_i.into(), val_i.into()], "mset")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[recv_h.into(), key_i.into(), val_i.into()], "mset") + }) .map_err(|e| e.to_string())?; Ok(true) } diff --git a/src/backend/llvm/compiler/codegen/instructions/mem.rs b/src/backend/llvm/compiler/codegen/instructions/mem.rs index 27dbe02b..1625ccb7 100644 --- a/src/backend/llvm/compiler/codegen/instructions/mem.rs +++ b/src/backend/llvm/compiler/codegen/instructions/mem.rs @@ -2,9 +2,9 @@ use std::collections::HashMap; use inkwell::values::BasicValueEnum; +use super::builder_cursor::BuilderCursor; use crate::backend::llvm::context::CodegenContext; use crate::mir::{BasicBlockId, ValueId}; -use super::builder_cursor::BuilderCursor; // Lower Store: handle allocas with element type tracking and integer width adjust pub(in super::super) fn lower_store<'ctx, 'b>( @@ -17,18 +17,51 @@ pub(in super::super) fn lower_store<'ctx, 'b>( alloca_elem_types: &mut HashMap>, value: &ValueId, ptr: &ValueId, - bb_map: &std::collections::HashMap>, + bb_map: &std::collections::HashMap< + crate::mir::BasicBlockId, + inkwell::basic_block::BasicBlock<'ctx>, + >, preds: &std::collections::HashMap>, - block_end_values: &std::collections::HashMap>>, + block_end_values: &std::collections::HashMap< + crate::mir::BasicBlockId, + std::collections::HashMap>, + >, ) -> Result<(), String> { use inkwell::types::BasicTypeEnum; // Resolve value preferring native kind; try i64, then f64, else pointer let i64t = codegen.context.i64_type(); - let val: BasicValueEnum = if let Ok(iv) = resolver.resolve_i64(codegen, cursor, cur_bid, *value, bb_map, preds, block_end_values, vmap) { + let val: BasicValueEnum = if let Ok(iv) = resolver.resolve_i64( + codegen, + cursor, + cur_bid, + *value, + bb_map, + preds, + block_end_values, + vmap, + ) { iv.into() - } else if let Ok(fv) = resolver.resolve_f64(codegen, cursor, cur_bid, *value, bb_map, preds, block_end_values, vmap) { + } else if let Ok(fv) = resolver.resolve_f64( + codegen, + cursor, + cur_bid, + *value, + bb_map, + preds, + block_end_values, + vmap, + ) { fv.into() - } else if let Ok(pv) = resolver.resolve_ptr(codegen, cursor, cur_bid, *value, bb_map, preds, block_end_values, vmap) { + } else if let Ok(pv) = resolver.resolve_ptr( + codegen, + cursor, + cur_bid, + *value, + bb_map, + preds, + block_end_values, + vmap, + ) { pv.into() } else { // Fallback: zero i64 @@ -41,7 +74,9 @@ pub(in super::super) fn lower_store<'ctx, 'b>( _ => return Err("unsupported store value type".to_string()), }; if let Some(existing) = allocas.get(ptr).copied() { - let existing_elem = *alloca_elem_types.get(ptr).ok_or("alloca elem type missing")?; + let existing_elem = *alloca_elem_types + .get(ptr) + .ok_or("alloca elem type missing")?; if existing_elem != elem_ty { match (val, existing_elem) { (BasicValueEnum::IntValue(iv), BasicTypeEnum::IntType(t)) => { @@ -93,7 +128,9 @@ pub(in super::super) fn lower_store<'ctx, 'b>( } } else { let slot = cursor - .emit_instr(cur_bid, |b| b.build_alloca(elem_ty, &format!("slot_{}", ptr.as_u32()))) + .emit_instr(cur_bid, |b| { + b.build_alloca(elem_ty, &format!("slot_{}", ptr.as_u32())) + }) .map_err(|e| e.to_string())?; cursor .emit_instr(cur_bid, |b| b.build_store(slot, val)) @@ -116,20 +153,26 @@ pub(in super::super) fn lower_load<'ctx, 'b>( ) -> Result<(), String> { use inkwell::types::BasicTypeEnum; let (slot, elem_ty) = if let Some(s) = allocas.get(ptr).copied() { - let et = *alloca_elem_types.get(ptr).ok_or("alloca elem type missing")?; + let et = *alloca_elem_types + .get(ptr) + .ok_or("alloca elem type missing")?; (s, et) } else { // Default new slot as i64 for uninitialized loads let i64t = codegen.context.i64_type(); let slot = cursor - .emit_instr(cur_bid, |b| b.build_alloca(i64t, &format!("slot_{}", ptr.as_u32()))) + .emit_instr(cur_bid, |b| { + b.build_alloca(i64t, &format!("slot_{}", ptr.as_u32())) + }) .map_err(|e| e.to_string())?; allocas.insert(*ptr, slot); alloca_elem_types.insert(*ptr, i64t.into()); (slot, i64t.into()) }; let lv = cursor - .emit_instr(cur_bid, |b| b.build_load(elem_ty, slot, &format!("load_{}", dst.as_u32()))) + .emit_instr(cur_bid, |b| { + b.build_load(elem_ty, slot, &format!("load_{}", dst.as_u32())) + }) .map_err(|e| e.to_string())?; vmap.insert(*dst, lv); Ok(()) @@ -145,9 +188,15 @@ pub(in super::super) fn lower_copy<'ctx, 'b>( vmap: &mut HashMap>, dst: &ValueId, src: &ValueId, - bb_map: &std::collections::HashMap>, + bb_map: &std::collections::HashMap< + crate::mir::BasicBlockId, + inkwell::basic_block::BasicBlock<'ctx>, + >, preds: &std::collections::HashMap>, - block_end_values: &std::collections::HashMap>>, + block_end_values: &std::collections::HashMap< + crate::mir::BasicBlockId, + std::collections::HashMap>, + >, ) -> Result<(), String> { // Choose resolution kind based on metadata type preference use inkwell::types::BasicTypeEnum as BT; @@ -160,20 +209,56 @@ pub(in super::super) fn lower_copy<'ctx, 'b>( let out: BasicValueEnum<'ctx> = match expected_bt { Some(BT::IntType(_)) | None => { // Prefer i64 for unknown - let iv = resolver.resolve_i64(codegen, cursor, cur_bid, *src, bb_map, preds, block_end_values, vmap)?; + let iv = resolver.resolve_i64( + codegen, + cursor, + cur_bid, + *src, + bb_map, + preds, + block_end_values, + vmap, + )?; iv.into() } Some(BT::PointerType(_)) => { - let pv = resolver.resolve_ptr(codegen, cursor, cur_bid, *src, bb_map, preds, block_end_values, vmap)?; + let pv = resolver.resolve_ptr( + codegen, + cursor, + cur_bid, + *src, + bb_map, + preds, + block_end_values, + vmap, + )?; pv.into() } Some(BT::FloatType(_)) => { - let fv = resolver.resolve_f64(codegen, cursor, cur_bid, *src, bb_map, preds, block_end_values, vmap)?; + let fv = resolver.resolve_f64( + codegen, + cursor, + cur_bid, + *src, + bb_map, + preds, + block_end_values, + vmap, + )?; fv.into() } _ => { // Fallback i64 - let iv = resolver.resolve_i64(codegen, cursor, cur_bid, *src, bb_map, preds, block_end_values, vmap)?; + let iv = resolver.resolve_i64( + codegen, + cursor, + cur_bid, + *src, + bb_map, + preds, + block_end_values, + vmap, + )?; iv.into() } }; diff --git a/src/backend/llvm/compiler/codegen/instructions/mod.rs b/src/backend/llvm/compiler/codegen/instructions/mod.rs index 01bb3d25..d17b84fd 100644 --- a/src/backend/llvm/compiler/codegen/instructions/mod.rs +++ b/src/backend/llvm/compiler/codegen/instructions/mod.rs @@ -1,34 +1,34 @@ -mod blocks; -pub mod builder_cursor; -pub mod flow; -mod externcall; -mod newbox; -mod boxcall; -pub mod ctx; -pub mod string_ops; mod arith; -mod mem; -mod consts; -mod strings; -mod arrays; -mod maps; mod arith_ops; +mod arrays; +mod blocks; +mod boxcall; +pub mod builder_cursor; mod call; +mod consts; +pub mod ctx; +mod externcall; +pub mod flow; mod loopform; +mod maps; +mod mem; +mod newbox; mod resolver; +pub mod string_ops; +mod strings; -pub(super) use blocks::{create_basic_blocks, precreate_phis}; -pub(super) use flow::{emit_branch, emit_jump, emit_return}; -pub(super) use externcall::lower_externcall; -pub(super) use newbox::lower_newbox; -pub(super) use boxcall::{lower_boxcall, lower_boxcall_boxed, lower_boxcall_via_ctx}; pub(super) use arith::lower_compare; -pub(super) use mem::{lower_load, lower_store}; -pub(super) use mem::lower_copy; -pub(super) use consts::lower_const; pub(super) use arith_ops::{lower_binop, lower_unary}; +pub(super) use blocks::{create_basic_blocks, precreate_phis}; +pub(super) use boxcall::{lower_boxcall, lower_boxcall_boxed, lower_boxcall_via_ctx}; pub(super) use call::lower_call; -pub(super) use loopform::{LoopFormContext, lower_while_loopform}; -pub(super) use loopform::normalize_header_phis_for_latch; +pub(super) use consts::lower_const; +pub(super) use externcall::lower_externcall; +pub(super) use flow::{emit_branch, emit_jump, emit_return}; pub(super) use loopform::dev_check_dispatch_only_phi; +pub(super) use loopform::normalize_header_phis_for_latch; +pub(super) use loopform::{lower_while_loopform, LoopFormContext}; +pub(super) use mem::lower_copy; +pub(super) use mem::{lower_load, lower_store}; +pub(super) use newbox::lower_newbox; pub(super) use resolver::Resolver; diff --git a/src/backend/llvm/compiler/codegen/instructions/newbox.rs b/src/backend/llvm/compiler/codegen/instructions/newbox.rs index c8b65d42..62f55c03 100644 --- a/src/backend/llvm/compiler/codegen/instructions/newbox.rs +++ b/src/backend/llvm/compiler/codegen/instructions/newbox.rs @@ -1,11 +1,11 @@ use std::collections::HashMap; -use inkwell::AddressSpace; use inkwell::values::BasicValueEnum as BVE; +use inkwell::AddressSpace; +use super::builder_cursor::BuilderCursor; use crate::backend::llvm::context::CodegenContext; use crate::mir::{BasicBlockId, ValueId}; -use super::builder_cursor::BuilderCursor; // NewBox lowering (subset consistent with existing code) pub(in super::super) fn lower_newbox<'ctx, 'b>( @@ -18,14 +18,29 @@ pub(in super::super) fn lower_newbox<'ctx, 'b>( box_type: &str, args: &[ValueId], box_type_ids: &HashMap, - bb_map: &std::collections::HashMap>, + bb_map: &std::collections::HashMap< + crate::mir::BasicBlockId, + inkwell::basic_block::BasicBlock<'ctx>, + >, preds: &std::collections::HashMap>, - block_end_values: &std::collections::HashMap>>, + block_end_values: &std::collections::HashMap< + crate::mir::BasicBlockId, + std::collections::HashMap>, + >, ) -> Result<(), String> { match (box_type, args.len()) { ("StringBox", 1) => { // Resolve as i8* string pointer (AOT string fast-path) - let p = resolver.resolve_ptr(codegen, cursor, cur_bid, args[0], bb_map, preds, block_end_values, vmap)?; + let p = resolver.resolve_ptr( + codegen, + cursor, + cur_bid, + args[0], + bb_map, + preds, + block_end_values, + vmap, + )?; vmap.insert(dst, p.into()); Ok(()) } @@ -36,19 +51,47 @@ pub(in super::super) fn lower_newbox<'ctx, 'b>( let callee = codegen .module .get_function("nyash.box.birth_i64") - .unwrap_or_else(|| codegen.module.add_function("nyash.box.birth_i64", fnty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.box.birth_i64", fnty, None) + }); let argc = i64t.const_int(args.len() as u64, false); let mut a1 = i64t.const_zero(); let mut a2 = i64t.const_zero(); if args.len() >= 1 { - a1 = resolver.resolve_i64(codegen, cursor, cur_bid, args[0], bb_map, preds, block_end_values, vmap)?; + a1 = resolver.resolve_i64( + codegen, + cursor, + cur_bid, + args[0], + bb_map, + preds, + block_end_values, + vmap, + )?; } if args.len() >= 2 { - a2 = resolver.resolve_i64(codegen, cursor, cur_bid, args[1], bb_map, preds, block_end_values, vmap)?; + a2 = resolver.resolve_i64( + codegen, + cursor, + cur_bid, + args[1], + bb_map, + preds, + block_end_values, + vmap, + )?; } let tid = i64t.const_int(type_id as u64, true); let call = cursor - .emit_instr(cur_bid, |b| b.build_call(callee, &[tid.into(), argc.into(), a1.into(), a2.into()], "birth_i64")) + .emit_instr(cur_bid, |b| { + b.build_call( + callee, + &[tid.into(), argc.into(), a1.into(), a2.into()], + "birth_i64", + ) + }) .map_err(|e| e.to_string())?; let h = call .try_as_basic_value() @@ -70,10 +113,8 @@ pub(in super::super) fn lower_newbox<'ctx, 'b>( } let type_id = *box_type_ids.get(box_type).unwrap_or(&0); // Temporary gate: allow forcing MapBox to plugin path explicitly - let force_plugin_map = std::env::var("NYASH_LLVM_FORCE_PLUGIN_MAP") - .ok() - .as_deref() - == Some("1"); + let force_plugin_map = + std::env::var("NYASH_LLVM_FORCE_PLUGIN_MAP").ok().as_deref() == Some("1"); let i64t = codegen.context.i64_type(); // Core-first: avoid birth_h for built-ins we provide directly (MapBox/ArrayBox) let is_core_builtin = box_type == "MapBox" || box_type == "ArrayBox"; @@ -83,7 +124,11 @@ pub(in super::super) fn lower_newbox<'ctx, 'b>( let callee = codegen .module .get_function("nyash.box.birth_h") - .unwrap_or_else(|| codegen.module.add_function("nyash.box.birth_h", fn_ty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.box.birth_h", fn_ty, None) + }); let tid = i64t.const_int(type_id as u64, true); let call = cursor .emit_instr(cur_bid, |b| b.build_call(callee, &[tid.into()], "birth")) @@ -106,12 +151,20 @@ pub(in super::super) fn lower_newbox<'ctx, 'b>( let callee = codegen .module .get_function("nyash.env.box.new") - .unwrap_or_else(|| codegen.module.add_function("nyash.env.box.new", fn_ty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.env.box.new", fn_ty, None) + }); let tn = cursor - .emit_instr(cur_bid, |b| b.build_global_string_ptr(box_type, "box_type_name")) + .emit_instr(cur_bid, |b| { + b.build_global_string_ptr(box_type, "box_type_name") + }) .map_err(|e| e.to_string())?; let call = cursor - .emit_instr(cur_bid, |b| b.build_call(callee, &[tn.as_pointer_value().into()], "env_box_new")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[tn.as_pointer_value().into()], "env_box_new") + }) .map_err(|e| e.to_string())?; let h_i64 = call .try_as_basic_value() diff --git a/src/backend/llvm/compiler/codegen/instructions/resolver.rs b/src/backend/llvm/compiler/codegen/instructions/resolver.rs index a747ad33..84072113 100644 --- a/src/backend/llvm/compiler/codegen/instructions/resolver.rs +++ b/src/backend/llvm/compiler/codegen/instructions/resolver.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; -use inkwell::values::{BasicValueEnum as BVE, IntValue}; use inkwell::values::PointerValue; +use inkwell::values::{BasicValueEnum as BVE, IntValue}; use crate::backend::llvm::context::CodegenContext; use crate::mir::{BasicBlockId, ValueId}; @@ -19,7 +19,11 @@ pub struct Resolver<'ctx> { impl<'ctx> Resolver<'ctx> { pub fn new() -> Self { - Self { i64_locals: HashMap::new(), ptr_locals: HashMap::new(), f64_locals: HashMap::new() } + Self { + i64_locals: HashMap::new(), + ptr_locals: HashMap::new(), + f64_locals: HashMap::new(), + } } /// Resolve a MIR value as an i64 dominating the current block. @@ -32,13 +36,25 @@ impl<'ctx> Resolver<'ctx> { vid: ValueId, bb_map: &std::collections::HashMap>, preds: &std::collections::HashMap>, - block_end_values: &std::collections::HashMap>>, + block_end_values: &std::collections::HashMap< + BasicBlockId, + std::collections::HashMap>, + >, vmap: &std::collections::HashMap>, ) -> Result, String> { if let Some(iv) = self.i64_locals.get(&(cur_bid, vid)).copied() { return Ok(iv); } - let iv = localize_to_i64(codegen, cursor, cur_bid, vid, bb_map, preds, block_end_values, vmap)?; + let iv = localize_to_i64( + codegen, + cursor, + cur_bid, + vid, + bb_map, + preds, + block_end_values, + vmap, + )?; self.i64_locals.insert((cur_bid, vid), iv); Ok(iv) } @@ -52,7 +68,10 @@ impl<'ctx> Resolver<'ctx> { vid: ValueId, bb_map: &std::collections::HashMap>, preds: &std::collections::HashMap>, - block_end_values: &std::collections::HashMap>>, + block_end_values: &std::collections::HashMap< + BasicBlockId, + std::collections::HashMap>, + >, vmap: &std::collections::HashMap>, ) -> Result, String> { if let Some(pv) = self.ptr_locals.get(&(cur_bid, vid)).copied() { @@ -61,7 +80,16 @@ impl<'ctx> Resolver<'ctx> { // 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 iv = localize_to_i64(codegen, cursor, cur_bid, vid, bb_map, preds, block_end_values, vmap)?; + 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())?; @@ -78,7 +106,10 @@ impl<'ctx> Resolver<'ctx> { vid: ValueId, bb_map: &std::collections::HashMap>, preds: &std::collections::HashMap>, - block_end_values: &std::collections::HashMap>>, + block_end_values: &std::collections::HashMap< + BasicBlockId, + std::collections::HashMap>, + >, vmap: &std::collections::HashMap>, ) -> Result, String> { if let Some(fv) = self.f64_locals.get(&(cur_bid, vid)).copied() { @@ -89,9 +120,15 @@ impl<'ctx> Resolver<'ctx> { let cur_llbb = *bb_map.get(&cur_bid).ok_or("cur bb missing")?; let pred_list = preds.get(&cur_bid).cloned().unwrap_or_default(); 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(f64t, &format!("loc_f64_{}", vid.as_u32())).map_err(|e| e.to_string())?; + 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(f64t, &format!("loc_f64_{}", vid.as_u32())) + .map_err(|e| e.to_string())?; if pred_list.is_empty() { // No predecessor: conservatively zero(vmap には依存しない) let z = f64t.const_zero(); @@ -106,10 +143,18 @@ impl<'ctx> Resolver<'ctx> { 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); } + 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()).unwrap(), + 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(), }; @@ -117,7 +162,9 @@ impl<'ctx> Resolver<'ctx> { phi.add_incoming(&[(&coerced, pred_bb)]); } } - if let Some(bb) = saved_ip { codegen.builder.position_at_end(bb); } + if let Some(bb) = saved_ip { + codegen.builder.position_at_end(bb); + } let out = phi.as_basic_value().into_float_value(); self.f64_locals.insert((cur_bid, vid), out); Ok(out) diff --git a/src/backend/llvm/compiler/codegen/instructions/string_ops.rs b/src/backend/llvm/compiler/codegen/instructions/string_ops.rs index 916b16a9..27269798 100644 --- a/src/backend/llvm/compiler/codegen/instructions/string_ops.rs +++ b/src/backend/llvm/compiler/codegen/instructions/string_ops.rs @@ -7,10 +7,13 @@ pub struct StrPtr<'ctx>(pub PointerValue<'ctx>); impl<'ctx> StrHandle<'ctx> { #[inline] - pub fn as_i64(&self) -> IntValue<'ctx> { self.0 } + pub fn as_i64(&self) -> IntValue<'ctx> { + self.0 + } } impl<'ctx> From> for StrPtr<'ctx> { - fn from(p: PointerValue<'ctx>) -> Self { Self(p) } + fn from(p: PointerValue<'ctx>) -> Self { + Self(p) + } } - diff --git a/src/backend/llvm/compiler/codegen/instructions/strings.rs b/src/backend/llvm/compiler/codegen/instructions/strings.rs index 39d189d8..56a08ba2 100644 --- a/src/backend/llvm/compiler/codegen/instructions/strings.rs +++ b/src/backend/llvm/compiler/codegen/instructions/strings.rs @@ -2,10 +2,10 @@ use std::collections::HashMap; use inkwell::{values::BasicValueEnum as BVE, AddressSpace}; -use crate::backend::llvm::context::CodegenContext; -use crate::mir::{function::MirFunction, BasicBlockId, ValueId}; use super::builder_cursor::BuilderCursor; use super::Resolver; +use crate::backend::llvm::context::CodegenContext; +use crate::mir::{function::MirFunction, BasicBlockId, ValueId}; /// Handle String-specific methods. Returns true if handled, false to let caller continue. pub(super) fn try_handle_string_method<'ctx, 'b>( @@ -19,9 +19,15 @@ pub(super) fn try_handle_string_method<'ctx, 'b>( box_val: &ValueId, method: &str, args: &[ValueId], - bb_map: &std::collections::HashMap>, + bb_map: &std::collections::HashMap< + crate::mir::BasicBlockId, + inkwell::basic_block::BasicBlock<'ctx>, + >, preds: &std::collections::HashMap>, - block_end_values: &std::collections::HashMap>>, + block_end_values: &std::collections::HashMap< + crate::mir::BasicBlockId, + std::collections::HashMap>, + >, ) -> Result { // Receiver annotation check (kept for future diagnostics) let _is_string_recv = match func.metadata.value_types.get(box_val) { @@ -40,26 +46,58 @@ pub(super) fn try_handle_string_method<'ctx, 'b>( // 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)?; + 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)?; + 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)?; + 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 .module .get_function("nyash.string.concat_ss") - .unwrap_or_else(|| codegen.module.add_function("nyash.string.concat_ss", fnty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.string.concat_ss", fnty, None) + }); let call = cursor - .emit_instr(cur_bid, |b| b - .build_call(callee, &[lp.into(), rp.into()], "concat_ss_call")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[lp.into(), rp.into()], "concat_ss_call") + }) .map_err(|e| e.to_string())?; if let Some(d) = dst { let rv = call @@ -69,7 +107,9 @@ pub(super) fn try_handle_string_method<'ctx, 'b>( // 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")) + .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()); } @@ -78,15 +118,29 @@ pub(super) fn try_handle_string_method<'ctx, 'b>( (BVE::PointerValue(lp), BVE::IntValue(_ri)) => { let i64t = codegen.context.i64_type(); // Localize rhs integer in current block via Resolver - let ri = resolver.resolve_i64(codegen, cursor, cur_bid, args[0], bb_map, preds, block_end_values, vmap)?; + let ri = resolver.resolve_i64( + codegen, + cursor, + cur_bid, + args[0], + bb_map, + preds, + block_end_values, + vmap, + )?; let fnty = i8p.fn_type(&[i8p.into(), i64t.into()], false); let callee = codegen .module .get_function("nyash.string.concat_si") - .unwrap_or_else(|| codegen.module.add_function("nyash.string.concat_si", fnty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.string.concat_si", fnty, None) + }); let call = cursor - .emit_instr(cur_bid, |b| b - .build_call(callee, &[lp.into(), ri.into()], "concat_si_call")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[lp.into(), ri.into()], "concat_si_call") + }) .map_err(|e| e.to_string())?; if let Some(d) = dst { let rv = call @@ -95,7 +149,9 @@ pub(super) fn try_handle_string_method<'ctx, 'b>( .ok_or("concat_si returned void".to_string())?; 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")) + .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()); } @@ -104,15 +160,29 @@ pub(super) fn try_handle_string_method<'ctx, 'b>( (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)?; + let li = resolver.resolve_i64( + codegen, + cursor, + cur_bid, + *box_val, + bb_map, + preds, + block_end_values, + vmap, + )?; let fnty = i8p.fn_type(&[i64t.into(), i8p.into()], false); let callee = codegen .module .get_function("nyash.string.concat_is") - .unwrap_or_else(|| codegen.module.add_function("nyash.string.concat_is", fnty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.string.concat_is", fnty, None) + }); let call = cursor - .emit_instr(cur_bid, |b| b - .build_call(callee, &[li.into(), rp.into()], "concat_is_call")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[li.into(), rp.into()], "concat_is_call") + }) .map_err(|e| e.to_string())?; if let Some(d) = dst { let rv = call @@ -121,7 +191,9 @@ pub(super) fn try_handle_string_method<'ctx, 'b>( .ok_or("concat_is returned void".to_string())?; 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")) + .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()); } @@ -139,23 +211,53 @@ pub(super) fn try_handle_string_method<'ctx, 'b>( // 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 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 .get_function("nyash.box.from_i8_string") - .unwrap_or_else(|| codegen.module.add_function("nyash.box.from_i8_string", fnty, None)); + .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, &[p.into()], "str_ptr_to_handle")) + .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 } else { return Err("from_i8_string ret expected i64".to_string()); } + if let BVE::IntValue(iv) = rv { + iv + } else { + return Err("from_i8_string ret expected i64".to_string()); + } } else { - resolver.resolve_i64(codegen, cursor, cur_bid, *box_val, bb_map, preds, block_end_values, vmap)? + resolver.resolve_i64( + codegen, + cursor, + cur_bid, + *box_val, + bb_map, + preds, + block_end_values, + vmap, + )? } }; // call i64 @nyash.string.len_h(i64) @@ -163,10 +265,15 @@ pub(super) fn try_handle_string_method<'ctx, 'b>( let callee = codegen .module .get_function("nyash.string.len_h") - .unwrap_or_else(|| codegen.module.add_function("nyash.string.len_h", fnty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.string.len_h", fnty, None) + }); let call = cursor - .emit_instr(cur_bid, |b| b - .build_call(callee, &[recv_h.into()], "strlen_h")) + .emit_instr(cur_bid, |b| { + b.build_call(callee, &[recv_h.into()], "strlen_h") + }) .map_err(|e| e.to_string())?; if let Some(d) = dst { let rv = call @@ -186,18 +293,54 @@ 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 pointer via Resolver - let recv_p = resolver.resolve_ptr(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, + )?; // 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)?; + 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, + )?; let fnty = i8p.fn_type(&[i8p.into(), i64t.into(), i64t.into()], false); let callee = codegen .module .get_function("nyash.string.substring_sii") - .unwrap_or_else(|| codegen.module.add_function("nyash.string.substring_sii", fnty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.string.substring_sii", fnty, None) + }); let call = cursor - .emit_instr(cur_bid, |b| b - .build_call(callee, &[recv_p.into(), s.into(), e.into()], "substring_call")) + .emit_instr(cur_bid, |b| { + b.build_call( + callee, + &[recv_p.into(), s.into(), e.into()], + "substring_call", + ) + }) .map_err(|e| e.to_string())?; if let Some(d) = dst { let rv = call @@ -206,7 +349,9 @@ pub(super) fn try_handle_string_method<'ctx, 'b>( .ok_or("substring returned void".to_string())?; 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")) + .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()); } @@ -221,17 +366,43 @@ 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 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 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 .get_function("nyash.string.lastIndexOf_ss") - .unwrap_or_else(|| codegen.module.add_function("nyash.string.lastIndexOf_ss", fnty, None)); + .unwrap_or_else(|| { + codegen + .module + .add_function("nyash.string.lastIndexOf_ss", fnty, None) + }); let call = cursor - .emit_instr(cur_bid, |b| b - .build_call(callee, &[recv_p.into(), needle_p.into()], "lastindexof_call")) + .emit_instr(cur_bid, |b| { + b.build_call( + callee, + &[recv_p.into(), needle_p.into()], + "lastindexof_call", + ) + }) .map_err(|e| e.to_string())?; if let Some(d) = dst { let rv = call diff --git a/src/backend/llvm/compiler/codegen/mod.rs b/src/backend/llvm/compiler/codegen/mod.rs index 3694ceb0..83ed8962 100644 --- a/src/backend/llvm/compiler/codegen/mod.rs +++ b/src/backend/llvm/compiler/codegen/mod.rs @@ -7,512 +7,31 @@ //! - `types.rs`: MIR→LLVM type mapping and classifiers //! //! Keep this file slim: predeclare functions, delegate lowering, emit entry wrapper/object. -use super::helpers::{as_float, as_int, map_type}; +use super::helpers::map_type; use super::LLVMCompiler; -use crate::backend::llvm::context::CodegenContext; +pub(super) use crate::backend::llvm::context::CodegenContext; use crate::mir::function::MirModule; -use crate::mir::instruction::{ConstValue, MirInstruction, UnaryOp}; -use crate::mir::ValueId; +pub(super) use crate::mir::instruction::{ConstValue, MirInstruction, UnaryOp}; +pub(super) use crate::mir::ValueId; use inkwell::context::Context; -use inkwell::{ +pub(super) use inkwell::{ types::{BasicTypeEnum, FloatType, IntType, PointerType}, values::{BasicValueEnum, FloatValue, FunctionValue, IntValue, PhiValue, PointerValue}, AddressSpace, }; use std::collections::HashMap; -// Submodules: helpers for type conversion/classification used by lowering mod types; -use self::types::{ +pub(super) use self::types::{ classify_tag, cmp_eq_ne_any, i64_to_ptr, map_mirtype_to_basic, to_bool, to_i64_any, }; -mod instructions; -mod utils; mod function; +mod instructions; +mod object; +mod utils; -// --- Local helpers (refactor to keep compile_module slim) --- - -// Moved to utils.rs -fn sanitize_symbol(name: &str) -> String { utils::sanitize_symbol(name) } - -// Moved to utils.rs -fn build_const_str_map(f: &crate::mir::function::MirFunction) -> HashMap { utils::build_const_str_map(f) } - -// moved: lower_one_function is implemented in function.rs -#[cfg(any())] -fn lower_one_function<'ctx>( - codegen: &CodegenContext<'ctx>, - llvm_func: FunctionValue<'ctx>, - func: &crate::mir::function::MirFunction, - name: &str, - box_type_ids: &HashMap, - llvm_funcs: &HashMap>, -) -> Result<(), String> { - // Create basic blocks (prefix names with function label to avoid any ambiguity) - let fn_label = sanitize_symbol(name); - let (mut bb_map, entry_bb) = instructions::create_basic_blocks(codegen, llvm_func, func, &fn_label); - let mut cursor = instructions::builder_cursor::BuilderCursor::new(&codegen.builder); - cursor.at_end(func.entry_block, entry_bb); - let mut vmap: HashMap = HashMap::new(); - let mut allocas: HashMap = HashMap::new(); - let entry_builder = codegen.context.create_builder(); - entry_builder.position_at_end(entry_bb); - let mut alloca_elem_types: HashMap = HashMap::new(); - let mut phis_by_block: HashMap< - crate::mir::BasicBlockId, - Vec<(ValueId, PhiValue, Vec<(crate::mir::BasicBlockId, ValueId)>)>, - > = HashMap::new(); - // Snapshot of values at the end of each basic block (for sealed-SSA PHI wiring) - let mut block_end_values: HashMap> = HashMap::new(); - // Build successors and predecessors map (for optional sealed-SSA PHI wiring) - let mut succs: HashMap> = HashMap::new(); - for (bid, block) in &func.blocks { - let v: Vec = block.successors.iter().copied().collect(); - succs.insert(*bid, v); - } - let mut preds: HashMap> = HashMap::new(); - for (b, ss) in &succs { - for s in ss { preds.entry(*s).or_default().push(*b); } - } - // Track sealed blocks to know when all preds of a successor are sealed - let mut sealed_blocks: std::collections::HashSet = std::collections::HashSet::new(); - // Bind parameters - for (i, pid) in func.params.iter().enumerate() { - if let Some(av) = llvm_func.get_nth_param(i as u32) { - vmap.insert(*pid, av); - } - } - // Gather block order once for fallthrough handling - let block_ids: Vec = func.block_ids().into_iter().collect(); - - // Precreate phis - for bid in &block_ids { - let bb = *bb_map.get(bid).ok_or("missing bb in map")?; - codegen.builder.position_at_end(bb); - let block = func.blocks.get(bid).unwrap(); - for inst in block - .instructions - .iter() - .take_while(|i| matches!(i, MirInstruction::Phi { .. })) - { - if let MirInstruction::Phi { dst, inputs } = inst { - let mut phi_ty: Option = None; - if let Some(mt) = func.metadata.value_types.get(dst) { - phi_ty = Some(map_mirtype_to_basic(codegen.context, mt)); - } else if let Some((_, iv)) = inputs.first() { - if let Some(mt) = func.metadata.value_types.get(iv) { - phi_ty = Some(map_mirtype_to_basic(codegen.context, mt)); - } - } - let phi_ty = phi_ty.unwrap_or_else(|| codegen.context.i64_type().into()); - let phi = codegen - .builder - .build_phi(phi_ty, &format!("phi_{}", dst.as_u32())) - .map_err(|e| e.to_string())?; - vmap.insert(*dst, phi.as_basic_value()); - phis_by_block - .entry(*bid) - .or_default() - .push((*dst, phi, inputs.clone())); - if std::env::var("NYASH_LLVM_TRACE_PHI").ok().as_deref() == Some("1") { - let ty_str = phi - .as_basic_value() - .get_type() - .print_to_string() - .to_string(); - let mut pairs: Vec = Vec::new(); - for (pb, vid) in inputs { - pairs.push(format!("({}->{})", pb.as_u32(), vid.as_u32())); - } - eprintln!( - "[PHI:new] fn={} bb={} dst={} ty={} inputs={}", - fn_label, - bid.as_u32(), - dst.as_u32(), - ty_str, - pairs.join(",") - ); - } - } - } - } - - // Map of const strings for Call resolution - let const_strs = utils::build_const_str_map(func); - - // Lower body - let mut loopform_loop_id: u32 = 0; - // Default sealed-SSA ON unless explicitly disabled with NYASH_LLVM_PHI_SEALED=0 - let sealed_mode = std::env::var("NYASH_LLVM_PHI_SEALED").ok().as_deref() != Some("0"); - // LoopForm registry (per-function lowering; gated) - let mut loopform_registry: HashMap = HashMap::new(); - let mut loopform_body_to_header: HashMap = HashMap::new(); - // Per-function Resolver for dominance-safe value access (i64 minimal) - let mut resolver = instructions::Resolver::new(); - for (bi, bid) in block_ids.iter().enumerate() { - let bb = *bb_map.get(bid).unwrap(); - // Use cursor to position at BB start for lowering - cursor.at_end(*bid, bb); - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("[LLVM] lowering bb={}", bid.as_u32()); - } - let block = func.blocks.get(bid).unwrap(); - let mut defined_in_block: std::collections::HashSet = std::collections::HashSet::new(); - for inst in &block.instructions { - match inst { - MirInstruction::NewBox { dst, box_type, args } => { - instructions::lower_newbox( - codegen, - &mut cursor, - &mut resolver, - *bid, - &mut vmap, - *dst, - box_type, - args, - box_type_ids, - &bb_map, - &preds, - &block_end_values, - )?; - defined_in_block.insert(*dst); - }, - MirInstruction::Const { dst, value } => { - let bval = match value { - ConstValue::Integer(i) => codegen.context.i64_type().const_int(*i as u64, true).into(), - ConstValue::Float(f) => codegen.context.f64_type().const_float(*f).into(), - ConstValue::Bool(b) => codegen.context.bool_type().const_int(*b as u64, false).into(), - ConstValue::String(s) => { - // Hoist string creation to entry block to dominate all uses. - let entry_term = unsafe { entry_bb.get_terminator() }; - if let Some(t) = entry_term { entry_builder.position_before(&t); } - else { entry_builder.position_at_end(entry_bb); } - let gv = entry_builder - .build_global_string_ptr(s, "str") - .map_err(|e| e.to_string())?; - let len = codegen.context.i32_type().const_int(s.len() as u64, false); - let rt = codegen.context.ptr_type(inkwell::AddressSpace::from(0)); - let fn_ty = rt.fn_type(&[ - codegen.context.ptr_type(inkwell::AddressSpace::from(0)).into(), - codegen.context.i32_type().into(), - ], false); - let callee = codegen - .module - .get_function("nyash_string_new") - .unwrap_or_else(|| codegen.module.add_function("nyash_string_new", fn_ty, None)); - let call = entry_builder - .build_call(callee, &[gv.as_pointer_value().into(), len.into()], "strnew") - .map_err(|e| e.to_string())?; - call.try_as_basic_value().left().ok_or("nyash_string_new returned void".to_string())? - } - ConstValue::Null => codegen.context.ptr_type(inkwell::AddressSpace::from(0)).const_zero().into(), - ConstValue::Void => codegen.context.i64_type().const_zero().into(), - }; - vmap.insert(*dst, bval); - defined_in_block.insert(*dst); - }, - MirInstruction::Call { dst, func: callee, args, .. } => { - instructions::lower_call( - codegen, - &mut cursor, - &mut resolver, - *bid, - func, - &mut vmap, - dst, - callee, - args, - &build_const_str_map(func), - llvm_funcs, - &bb_map, - &preds, - &block_end_values, - )?; - if let Some(d) = dst { defined_in_block.insert(*d); } - }, - MirInstruction::BoxCall { dst, box_val, method, method_id, args, effects: _ } => { - instructions::lower_boxcall( - codegen, - &mut cursor, - &mut resolver, - *bid, - func, - &mut vmap, - dst, - box_val, - method, - method_id, - args, - box_type_ids, - &entry_builder, - &bb_map, - &preds, - &block_end_values, - )?; - if let Some(d) = dst { defined_in_block.insert(*d); } - }, - MirInstruction::ExternCall { dst, iface_name, method_name, args, effects: _ } => { - instructions::lower_externcall( - codegen, - &mut cursor, - &mut resolver, - *bid, - func, - &mut vmap, - dst, - iface_name, - method_name, - args, - &bb_map, - &preds, - &block_end_values, - )?; - if let Some(d) = dst { defined_in_block.insert(*d); } - }, - MirInstruction::UnaryOp { 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 } => { - instructions::lower_binop(codegen, &mut cursor, &mut resolver, *bid, func, &mut vmap, *dst, op, lhs, rhs, &bb_map, &preds, &block_end_values)?; - defined_in_block.insert(*dst); - }, - MirInstruction::Compare { dst, op, lhs, rhs } => { - let out = instructions::lower_compare(codegen, &mut cursor, &mut resolver, *bid, func, &vmap, op, lhs, rhs, &bb_map, &preds, &block_end_values)?; - vmap.insert(*dst, out); - defined_in_block.insert(*dst); - }, - MirInstruction::Store { value, ptr } => { - instructions::lower_store( - codegen, - &mut cursor, - &mut resolver, - *bid, - &vmap, - &mut allocas, - &mut alloca_elem_types, - value, - ptr, - &bb_map, - &preds, - &block_end_values, - )?; - }, - MirInstruction::Load { dst, ptr } => { - instructions::lower_load(codegen, &mut cursor, *bid, &mut vmap, &mut allocas, &mut alloca_elem_types, dst, ptr)?; - defined_in_block.insert(*dst); - }, - MirInstruction::Phi { .. } => { /* precreated */ } - _ => { /* ignore others */ } - } - // Snapshot end-of-block values - let mut snap: HashMap = HashMap::new(); - for vid in &defined_in_block { if let Some(v) = vmap.get(vid).copied() { snap.insert(*vid, v); } } - block_end_values.insert(*bid, snap); - } - // Terminator handling - if let Some(term) = &block.terminator { - cursor.at_end(*bid, bb); - match term { - MirInstruction::Return { value } => { - instructions::emit_return(codegen, &mut cursor, &mut resolver, *bid, func, &vmap, value, &bb_map, &preds, &block_end_values)?; - } - MirInstruction::Jump { target } => { - let mut handled = false; - if std::env::var("NYASH_ENABLE_LOOPFORM").ok().as_deref() == Some("1") && - std::env::var("NYASH_LOOPFORM_BODY2DISPATCH").ok().as_deref() == Some("1") { - if let Some(hdr) = loopform_body_to_header.get(bid) { - if hdr == target { - if let Some((dispatch_bb, tag_phi, payload_phi, _latch_bb)) = loopform_registry.get(hdr) { - let i8t = codegen.context.i8_type(); - let i64t = codegen.context.i64_type(); - let pred_llbb = *bb_map.get(bid).ok_or("loopform: body llbb missing")?; - let z = i8t.const_zero(); - let pz = i64t.const_zero(); - tag_phi.add_incoming(&[(&z, pred_llbb)]); - payload_phi.add_incoming(&[(&pz, pred_llbb)]); - cursor.emit_term(*bid, |b| { b.build_unconditional_branch(*dispatch_bb).unwrap(); }); - handled = true; - } - } - } - } - if !handled { - instructions::emit_jump(codegen, &mut cursor, *bid, target, &bb_map, &phis_by_block)?; - } - } - MirInstruction::Branch { condition, then_bb, else_bb } => { - let mut handled_by_loopform = false; - if std::env::var("NYASH_ENABLE_LOOPFORM").ok().as_deref() == Some("1") { - let mut is_back = |start: crate::mir::BasicBlockId| -> u8 { - if let Some(b) = func.blocks.get(&start) { - if let Some(crate::mir::instruction::MirInstruction::Jump { target }) = &b.terminator { - if target == bid { return 1; } - if let Some(b2) = func.blocks.get(target) { - if let Some(crate::mir::instruction::MirInstruction::Jump { target: t2 }) = &b2.terminator { - if t2 == bid { return 2; } - } - } - } - } - 0 - }; - let d_then = is_back(*then_bb); - let d_else = is_back(*else_bb); - let choose_body = if d_then > 0 && d_else == 0 { Some((*then_bb, *else_bb)) } - else if d_else > 0 && d_then == 0 { Some((*else_bb, *then_bb)) } - else if d_then > 0 && d_else > 0 { if d_then <= d_else { Some((*then_bb, *else_bb)) } else { Some((*else_bb, *then_bb)) } } - else { None }; - if let Some((body_sel, after_sel)) = choose_body { - let body_block = func.blocks.get(&body_sel).unwrap(); - handled_by_loopform = instructions::lower_while_loopform( - codegen, &mut cursor, &mut resolver, func, llvm_func, condition, &body_block.instructions, - loopform_loop_id, &fn_label, *bid, body_sel, after_sel, &bb_map, &vmap, &preds, &block_end_values, - &mut loopform_registry, &mut loopform_body_to_header, - )?; - loopform_loop_id = loopform_loop_id.wrapping_add(1); - } - } - if !handled_by_loopform { - instructions::emit_branch(codegen, &mut cursor, &mut resolver, *bid, condition, then_bb, else_bb, &bb_map, &phis_by_block, &vmap, &preds, &block_end_values)?; - } - } - _ => { - cursor.at_end(*bid, bb); - if let Some(next_bid) = block_ids.get(bi + 1) { - instructions::emit_jump(codegen, &mut cursor, *bid, next_bid, &bb_map, &phis_by_block)?; - } else { - let entry_first = func.entry_block; - instructions::emit_jump(codegen, &mut cursor, *bid, &entry_first, &bb_map, &phis_by_block)?; - } - } - } - } else { - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("[LLVM] no terminator in MIR for bb={} (fallback)", bid.as_u32()); - } - cursor.at_end(*bid, bb); - if let Some(next_bid) = block_ids.get(bi + 1) { - instructions::emit_jump(codegen, &mut cursor, *bid, next_bid, &bb_map, &phis_by_block)?; - } else { - let entry_first = func.entry_block; - instructions::emit_jump(codegen, &mut cursor, *bid, &entry_first, &bb_map, &phis_by_block)?; - } - } - if unsafe { bb.get_terminator() }.is_none() { - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("[LLVM] extra guard inserting fallback for bb={}", bid.as_u32()); - } - cursor.at_end(*bid, bb); - if let Some(next_bid) = block_ids.get(bi + 1) { - instructions::emit_jump(codegen, &mut cursor, *bid, next_bid, &bb_map, &phis_by_block)?; - } else { - let entry_first = func.entry_block; - instructions::emit_jump(codegen, &mut cursor, *bid, &entry_first, &bb_map, &phis_by_block)?; - } - } - if sealed_mode { - instructions::flow::seal_block(codegen, &mut cursor, func, *bid, &succs, &bb_map, &phis_by_block, &block_end_values)?; - sealed_blocks.insert(*bid); - } - } - if std::env::var("NYASH_ENABLE_LOOPFORM").ok().as_deref() == Some("1") && - std::env::var("NYASH_LOOPFORM_LATCH2HEADER").ok().as_deref() == Some("1") { - for (hdr_bid, (_dispatch_bb, _tag_phi, _payload_phi, latch_bb)) in &loopform_registry { - if let Some(phis) = phis_by_block.get(hdr_bid) { - instructions::normalize_header_phis_for_latch( - codegen, - *hdr_bid, - *latch_bb, - phis, - )?; - } - } - instructions::dev_check_dispatch_only_phi(&phis_by_block, &loopform_registry); - } - for bb in llvm_func.get_basic_blocks() { - if unsafe { bb.get_terminator() }.is_none() { - codegen.builder.position_at_end(bb); - let _ = codegen.builder.build_unreachable(); - } - } - if !llvm_func.verify(true) { - if std::env::var("NYASH_LLVM_DUMP_ON_FAIL").ok().as_deref() == Some("1") { - let ir = codegen.module.print_to_string().to_string(); - let dump_dir = std::path::Path::new("tmp"); - let _ = std::fs::create_dir_all(dump_dir); - let dump_path = dump_dir.join(format!("llvm_fail_{}.ll", sanitize_symbol(name))); - let _ = std::fs::write(&dump_path, ir); - eprintln!("[LLVM] wrote IR dump: {}", dump_path.display()); - } - return Err(format!("Function verification failed: {}", name)); - } - Ok(()) -} - -fn emit_wrapper_and_object<'ctx>( - codegen: &CodegenContext<'ctx>, - entry_name: &str, - output_path: &str, -) -> Result<(), String> { - let i64t = codegen.context.i64_type(); - let ny_main_ty = i64t.fn_type(&[], false); - let ny_main = codegen.module.add_function("ny_main", ny_main_ty, None); - let entry_bb = codegen.context.append_basic_block(ny_main, "entry"); - codegen.builder.position_at_end(entry_bb); - let entry_sym = format!("ny_f_{}", sanitize_symbol(&entry_name)); - let entry_fn = codegen - .module - .get_function(&entry_sym) - .ok_or_else(|| format!("entry function symbol not found: {}", entry_sym))?; - let call = codegen.builder.build_call(entry_fn, &[], "call_main").map_err(|e| e.to_string())?; - let rv = call.try_as_basic_value().left(); - let ret_v = if let Some(v) = rv { - match v { - BasicValueEnum::IntValue(iv) => { - if iv.get_type().get_bit_width() == 64 { iv } else { codegen.builder.build_int_z_extend(iv, i64t, "ret_zext").map_err(|e| e.to_string())? } - } - BasicValueEnum::PointerValue(pv) => codegen.builder.build_ptr_to_int(pv, i64t, "ret_p2i").map_err(|e| e.to_string())?, - BasicValueEnum::FloatValue(fv) => codegen.builder.build_float_to_signed_int(fv, i64t, "ret_f2i").map_err(|e| e.to_string())?, - _ => i64t.const_zero(), - } - } else { i64t.const_zero() }; - codegen.builder.build_return(Some(&ret_v)).map_err(|e| e.to_string())?; - if !ny_main.verify(true) { return Err("ny_main verification failed".to_string()); } - let verbose = std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1"); - if verbose { eprintln!("[LLVM] emitting object to {} (begin)", output_path); } - match codegen.target_machine.write_to_file(&codegen.module, inkwell::targets::FileType::Object, std::path::Path::new(output_path)) { - Ok(_) => { - if std::fs::metadata(output_path).is_err() { - let buf = codegen.target_machine.write_to_memory_buffer(&codegen.module, inkwell::targets::FileType::Object).map_err(|e| format!("Failed to get object buffer: {}", e))?; - std::fs::write(output_path, buf.as_slice()).map_err(|e| format!("Failed to write object to '{}': {}", output_path, e))?; - if verbose { eprintln!("[LLVM] wrote object via memory buffer fallback: {} ({} bytes)", output_path, buf.get_size()); } - } else if verbose { - if let Ok(meta) = std::fs::metadata(output_path) { eprintln!("[LLVM] wrote object via file API: {} ({} bytes)", output_path, meta.len()); } - } - if verbose { eprintln!("[LLVM] emit complete (Ok branch) for {}", output_path); } - Ok(()) - } - Err(e) => { - let buf = codegen.target_machine.write_to_memory_buffer(&codegen.module, inkwell::targets::FileType::Object).map_err(|ee| format!("Failed to write object ({}); and memory buffer failed: {}", e, ee))?; - std::fs::write(output_path, buf.as_slice()).map_err(|ee| format!("Failed to write object to '{}': {} (original error: {})", output_path, ee, e))?; - if verbose { eprintln!("[LLVM] wrote object via error fallback: {} ({} bytes)", output_path, buf.get_size()); eprintln!("[LLVM] emit complete (Err branch handled) for {}", output_path); } - Ok(()) - } - } +fn sanitize_symbol(name: &str) -> String { + utils::sanitize_symbol(name) } impl LLVMCompiler { @@ -562,738 +81,6 @@ impl LLVMCompiler { for pt in &f.signature.params { params_bt.push(map_type(codegen.context, pt)?); } - let ll_fn_ty = match ret_bt { - BasicTypeEnum::IntType(t) => t.fn_type(¶ms_bt.iter().map(|t| (*t).into()).collect::>(), false), - BasicTypeEnum::FloatType(t) => t.fn_type(¶ms_bt.iter().map(|t| (*t).into()).collect::>(), false), - BasicTypeEnum::PointerType(t) => t.fn_type(¶ms_bt.iter().map(|t| (*t).into()).collect::>(), false), - _ => return Err("Unsupported return basic type".to_string()), - }; - let sym = format!("ny_f_{}", utils::sanitize_symbol(name)); - let lf = codegen.module.add_function(&sym, ll_fn_ty, None); - llvm_funcs.insert(name.clone(), lf); - } - - // Lower all functions - for (name, func) in &mir_module.functions { - let llvm_func = *llvm_funcs.get(name).ok_or("predecl not found")?; - function::lower_one_function(&codegen, llvm_func, func, name, &box_type_ids, &llvm_funcs)?; - } - - // Build entry wrapper and emit object - emit_wrapper_and_object(&codegen, &entry_name, output_path) - } -} - -/* - MirInstruction::NewBox { dst, box_type, args } => { - instructions::lower_newbox( - &codegen, - &mut cursor, - &mut resolver, - *bid, - &mut vmap, - *dst, - box_type, - args, - &box_type_ids, - &bb_map, - &preds, - &block_end_values, - )?; - defined_in_block.insert(*dst); - }, - MirInstruction::Const { dst, value } => { - let bval = match value { - ConstValue::Integer(i) => { - codegen.context.i64_type().const_int(*i as u64, true).into() - } - ConstValue::Float(f) => { - codegen.context.f64_type().const_float(*f).into() - } - ConstValue::Bool(b) => codegen - .context - .bool_type() - .const_int(*b as u64, false) - .into(), - ConstValue::String(s) => { - // Hoist string creation to entry block to dominate all uses. - // If the entry block already has a terminator, insert just before it. - let entry_term = unsafe { entry_bb.get_terminator() }; - if let Some(t) = entry_term { entry_builder.position_before(&t); } - else { entry_builder.position_at_end(entry_bb); } - let gv = entry_builder - .build_global_string_ptr(s, "str") - .map_err(|e| e.to_string())?; - let len = codegen.context.i32_type().const_int(s.len() as u64, false); - let rt = codegen.context.ptr_type(inkwell::AddressSpace::from(0)); - let fn_ty = rt.fn_type(&[ - codegen.context.ptr_type(inkwell::AddressSpace::from(0)).into(), - codegen.context.i32_type().into(), - ], false); - let callee = codegen - .module - .get_function("nyash_string_new") - .unwrap_or_else(|| codegen.module.add_function("nyash_string_new", fn_ty, None)); - let call = entry_builder - .build_call(callee, &[gv.as_pointer_value().into(), len.into()], "strnew") - .map_err(|e| e.to_string())?; - call.try_as_basic_value().left().ok_or("nyash_string_new returned void".to_string())? - } - ConstValue::Null => codegen - .context - .ptr_type(inkwell::AddressSpace::from(0)) - .const_zero() - .into(), - ConstValue::Void => { - // Use i64 0 as a neutral placeholder for void constants in MIR. - // This keeps the value map total without impacting semantics. - codegen.context.i64_type().const_zero().into() - } - }; - vmap.insert(*dst, bval); - defined_in_block.insert(*dst); - }, - MirInstruction::Call { dst, func: callee, args, .. } => { - instructions::lower_call( - &codegen, - &mut cursor, - &mut resolver, - *bid, - func, - &mut vmap, - dst, - callee, - args, - &const_strs, - &llvm_funcs, - &bb_map, - &preds, - &block_end_values, - )?; - if let Some(d) = dst { defined_in_block.insert(*d); } - } - MirInstruction::BoxCall { - dst, - box_val, - method, - method_id, - args, - effects: _, - } => { - // Delegate to refactored lowering and skip legacy body - { - instructions::lower_boxcall( - &codegen, - &mut cursor, - &mut resolver, - *bid, - func, - &mut vmap, - dst, - box_val, - method, - method_id, - args, - &box_type_ids, - &entry_builder, - &bb_map, - &preds, - &block_end_values, - )?; - } - if let Some(d) = dst { defined_in_block.insert(*d); } - }, - MirInstruction::ExternCall { dst, iface_name, method_name, args, effects: _ } => { - instructions::lower_externcall( - &codegen, - &mut cursor, - &mut resolver, - *bid, - func, - &mut vmap, - dst, - iface_name, - method_name, - args, - &bb_map, - &preds, - &block_end_values, - )?; - if let Some(d) = dst { defined_in_block.insert(*d); } - }, - MirInstruction::UnaryOp { 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 } => { - instructions::lower_binop(&codegen, &mut cursor, &mut resolver, *bid, func, &mut vmap, *dst, op, lhs, rhs, &bb_map, &preds, &block_end_values)?; - defined_in_block.insert(*dst); - }, - MirInstruction::Compare { dst, op, lhs, rhs } => { - let out = instructions::lower_compare(&codegen, &mut cursor, &mut resolver, *bid, func, &vmap, op, lhs, rhs, &bb_map, &preds, &block_end_values)?; - vmap.insert(*dst, out); - defined_in_block.insert(*dst); - }, - MirInstruction::Store { value, ptr } => { - instructions::lower_store( - &codegen, - &mut cursor, - &mut resolver, - *bid, - &vmap, - &mut allocas, - &mut alloca_elem_types, - value, - ptr, - &bb_map, - &preds, - &block_end_values, - )?; - }, - MirInstruction::Load { dst, ptr } => { - instructions::lower_load(&codegen, &mut cursor, *bid, &mut vmap, &mut allocas, &mut alloca_elem_types, dst, ptr)?; - defined_in_block.insert(*dst); - }, - MirInstruction::Copy { dst, src } => { - instructions::lower_copy( - &codegen, - &mut cursor, - &mut resolver, - *bid, - func, - &mut vmap, - dst, - src, - &bb_map, - &preds, - &block_end_values, - )?; - defined_in_block.insert(*dst); - }, - MirInstruction::Phi { .. } => { - // Already created in pre-pass; nothing to do here. - } - _ => { /* ignore other ops for 11.1 */ }, - } - // Capture a filtered snapshot of the value map at the end of this block's body - let mut snap: HashMap = HashMap::new(); - for vid in &defined_in_block { if let Some(v) = vmap.get(vid).copied() { snap.insert(*vid, v); } } - block_end_values.insert(*bid, snap); - } - // Emit terminators and provide a conservative fallback when absent - if let Some(term) = &block.terminator { - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("[LLVM] terminator present for bb={}", bid.as_u32()); - } - // Ensure builder is positioned at current block before emitting terminator - cursor.at_end(*bid, bb); - match term { - MirInstruction::Return { value } => { - instructions::emit_return( - &codegen, - &mut cursor, - &mut resolver, - *bid, - func, - &vmap, - value, - &bb_map, - &preds, - &block_end_values, - )?; - } - MirInstruction::Throw { .. } => { - // Minimal lowering: call llvm.trap (optional) then unreachable - let use_trap = std::env::var("NYASH_LLVM_TRAP_ON_THROW").ok().as_deref() != Some("0"); - if use_trap { - let fn_ty = codegen.context.void_type().fn_type(&[], false); - let trap = codegen - .module - .get_function("llvm.trap") - .unwrap_or_else(|| codegen.module.add_function("llvm.trap", fn_ty, None)); - cursor.emit_term(*bid, |b| { - let _ = b.build_call(trap, &[], "trap"); - }); - } - // Ensure we end the block - cursor.at_end(*bid, bb); - let _ = codegen.builder.build_unreachable(); - } - MirInstruction::Jump { target } => { - // LoopForm simple body→dispatch wiring: if this block is a loop body - // and jumps back to its header, redirect to dispatch and add PHI incoming - let mut handled = false; - if std::env::var("NYASH_ENABLE_LOOPFORM").ok().as_deref() == Some("1") && - std::env::var("NYASH_LOOPFORM_BODY2DISPATCH").ok().as_deref() == Some("1") { - if let Some(hdr) = loopform_body_to_header.get(bid) { - if hdr == target { - if let Some((dispatch_bb, tag_phi, payload_phi, _latch_bb)) = loopform_registry.get(hdr) { - // Add Next(tag=0) + payload=0 incoming from this pred - let i8t = codegen.context.i8_type(); - let i64t = codegen.context.i64_type(); - let pred_llbb = *bb_map.get(bid).ok_or("loopform: body llbb missing")?; - let z = i8t.const_zero(); - let pz = i64t.const_zero(); - tag_phi.add_incoming(&[(&z, pred_llbb)]); - payload_phi.add_incoming(&[(&pz, pred_llbb)]); - // Redirect to dispatch - cursor.emit_term(*bid, |b| { - b.build_unconditional_branch(*dispatch_bb).map_err(|e| e.to_string()).unwrap(); - }); - handled = true; - } - } - } - } - if !handled { - instructions::emit_jump(&codegen, &mut cursor, *bid, target, &bb_map, &phis_by_block)?; - } - } - MirInstruction::Branch { condition, then_bb, else_bb } => { - // LoopForm Phase 1 (gated): detect simple while-pattern and rewire header - let mut handled_by_loopform = false; - if std::env::var("NYASH_ENABLE_LOOPFORM").ok().as_deref() == Some("1") { - // Helper: minimal back-edge detection allowing up to 2-step jump chains via Jump-only - let mut is_back = |start: crate::mir::BasicBlockId| -> u8 { - // direct jump back - if let Some(b) = func.blocks.get(&start) { - if let Some(crate::mir::instruction::MirInstruction::Jump { target }) = &b.terminator { - if target == bid { return 1; } - // one more hop if that block is a Jump back to header - if let Some(b2) = func.blocks.get(target) { - if let Some(crate::mir::instruction::MirInstruction::Jump { target: t2 }) = &b2.terminator { - if t2 == bid { return 2; } - } - } - } - } - 0 - }; - let d_then = is_back(*then_bb); - let d_else = is_back(*else_bb); - let choose_body = if d_then > 0 && d_else == 0 { - Some((*then_bb, *else_bb)) - } else if d_else > 0 && d_then == 0 { - Some((*else_bb, *then_bb)) - } else if d_then > 0 && d_else > 0 { - // Prefer shorter back-edge; tie-breaker favors then - if d_then <= d_else { Some((*then_bb, *else_bb)) } else { Some((*else_bb, *then_bb)) } - } else { None }; - if let Some((body_sel, after_sel)) = choose_body { - let body_block = func.blocks.get(&body_sel).unwrap(); - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!( - "[LoopForm] detect while-pattern+: header={} body={} after={} (id={})", - bid.as_u32(), body_sel.as_u32(), after_sel.as_u32(), loopform_loop_id - ); - } - handled_by_loopform = instructions::lower_while_loopform( - &codegen, - &mut cursor, - &mut resolver, - func, - llvm_func, - condition, - &body_block.instructions, - loopform_loop_id, - &fn_label, - *bid, - body_sel, - after_sel, - &bb_map, - &vmap, - &preds, - &block_end_values, - &mut loopform_registry, - &mut loopform_body_to_header, - )?; - loopform_loop_id = loopform_loop_id.wrapping_add(1); - } - } - if !handled_by_loopform { - instructions::emit_branch(&codegen, &mut cursor, &mut resolver, *bid, condition, then_bb, else_bb, &bb_map, &phis_by_block, &vmap, &preds, &block_end_values)?; - } - } - _ => { - // Ensure builder is at this block before fallback branch - cursor.at_end(*bid, bb); - // Unknown/unhandled terminator: conservatively branch forward - if let Some(next_bid) = block_ids.get(bi + 1) { - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("[LLVM] unknown terminator fallback: bb={} -> next={}", bid.as_u32(), next_bid.as_u32()); - } - instructions::emit_jump(&codegen, &mut cursor, *bid, next_bid, &bb_map, &phis_by_block)?; - } else { - let entry_first = func.entry_block; - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("[LLVM] unknown terminator fallback: bb={} -> entry={}", bid.as_u32(), entry_first.as_u32()); - } - instructions::emit_jump(&codegen, &mut cursor, *bid, &entry_first, &bb_map, &phis_by_block)?; - } - } - } - } else { - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("[LLVM] no terminator in MIR for bb={} (fallback)", bid.as_u32()); - } - // Ensure builder is at this block before fallback branch - cursor.at_end(*bid, bb); - // Fallback: branch to the next block if any; otherwise loop to entry - if let Some(next_bid) = block_ids.get(bi + 1) { - instructions::emit_jump(&codegen, &mut cursor, *bid, next_bid, &bb_map, &phis_by_block)?; - } else { - // last block, loop to entry to satisfy verifier - let entry_first = func.entry_block; - instructions::emit_jump(&codegen, &mut cursor, *bid, &entry_first, &bb_map, &phis_by_block)?; - } - } - // Extra guard: if the current LLVM basic block still lacks a terminator for any reason, - // insert a conservative branch to the next block (or entry if last) to satisfy verifier. - if unsafe { bb.get_terminator() }.is_none() { - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("[LLVM] extra guard inserting fallback for bb={}", bid.as_u32()); - } - // Ensure the builder is positioned at the end of this block before inserting the fallback terminator - cursor.at_end(*bid, bb); - if let Some(next_bid) = block_ids.get(bi + 1) { - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("[LLVM] fallback terminator: bb={} -> next={}", bid.as_u32(), next_bid.as_u32()); - } - instructions::emit_jump(&codegen, &mut cursor, *bid, next_bid, &bb_map, &phis_by_block)?; - } else { - let entry_first = func.entry_block; - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("[LLVM] fallback terminator: bb={} -> entry={}", bid.as_u32(), entry_first.as_u32()); - } - instructions::emit_jump(&codegen, &mut cursor, *bid, &entry_first, &bb_map, &phis_by_block)?; - } - } - if sealed_mode { - instructions::flow::seal_block(&codegen, &mut cursor, func, *bid, &succs, &bb_map, &phis_by_block, &block_end_values)?; - sealed_blocks.insert(*bid); - // In sealed mode, we rely on seal_block to add incoming per pred when each pred is sealed. - // finalize_phis is intentionally skipped to avoid duplicate incoming entries. - // LoopForm latch→header is normalized in a separate post-pass below. - } - } - // LoopForm header PHI normalization when latch→header is enabled (post-pass per function) - if std::env::var("NYASH_ENABLE_LOOPFORM").ok().as_deref() == Some("1") && - std::env::var("NYASH_LOOPFORM_LATCH2HEADER").ok().as_deref() == Some("1") { - for (hdr_bid, (_dispatch_bb, _tag_phi, _payload_phi, latch_bb)) in &loopform_registry { - if let Some(phis) = phis_by_block.get(hdr_bid) { - instructions::normalize_header_phis_for_latch( - &codegen, - *hdr_bid, - *latch_bb, - phis, - )?; - } - } - // Dev check (optional): ensure PHIs live only in dispatch blocks - instructions::dev_check_dispatch_only_phi(&phis_by_block, &loopform_registry); - } - // Finalize function: ensure every basic block is closed with a terminator. - // As a last resort, insert 'unreachable' into blocks that remain unterminated. - for bb in llvm_func.get_basic_blocks() { - if unsafe { bb.get_terminator() }.is_none() { - codegen.builder.position_at_end(bb); - let _ = codegen.builder.build_unreachable(); - } - } - // Verify the fully-lowered function once, after all blocks - if !llvm_func.verify(true) { - if std::env::var("NYASH_LLVM_DUMP_ON_FAIL").ok().as_deref() == Some("1") { - let ir = codegen.module.print_to_string().to_string(); - let dump_dir = std::path::Path::new("tmp"); - let _ = std::fs::create_dir_all(dump_dir); - let dump_path = dump_dir.join(format!("llvm_fail_{}.ll", sanitize(name))); - if let Err(e) = std::fs::write(&dump_path, ir) { - eprintln!("[LLVM] failed to write IR dump: {}", e); - } else { - eprintln!("[LLVM] wrote IR dump: {}", dump_path.display()); - } - } - return Err(format!("Function verification failed: {}", name)); - } - - } - // End of per-function lowering loop - - // Build entry wrapper ny_main -> call entry function - let i64t = codegen.context.i64_type(); - let ny_main_ty = i64t.fn_type(&[], false); - let ny_main = codegen.module.add_function("ny_main", ny_main_ty, None); - let entry_bb = codegen.context.append_basic_block(ny_main, "entry"); - codegen.builder.position_at_end(entry_bb); - let entry_sym = format!("ny_f_{}", sanitize(&entry_name)); - let entry_fn = codegen - .module - .get_function(&entry_sym) - .ok_or_else(|| format!("entry function symbol not found: {}", entry_sym))?; - let call = codegen - .builder - .build_call(entry_fn, &[], "call_main") - .map_err(|e| e.to_string())?; - let rv = call.try_as_basic_value().left(); - // Normalize to i64 return - let ret_v = if let Some(v) = rv { - match v { - BasicValueEnum::IntValue(iv) => { - if iv.get_type().get_bit_width() == 64 { - iv - } else { - codegen - .builder - .build_int_z_extend(iv, i64t, "ret_zext") - .map_err(|e| e.to_string())? - } - } - BasicValueEnum::PointerValue(pv) => codegen - .builder - .build_ptr_to_int(pv, i64t, "ret_p2i") - .map_err(|e| e.to_string())?, - BasicValueEnum::FloatValue(fv) => codegen - .builder - .build_float_to_signed_int(fv, i64t, "ret_f2i") - .map_err(|e| e.to_string())?, - _ => i64t.const_zero(), - } - } else { - i64t.const_zero() - }; - codegen.builder.build_return(Some(&ret_v)).map_err(|e| e.to_string())?; - - // Verify and emit final object - if !ny_main.verify(true) { - return Err("ny_main verification failed".to_string()); - } - // Try writing via file API first; if it succeeds but file is missing due to env/FS quirks, - // also write via memory buffer as a fallback to ensure presence. - let verbose = std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1"); - if verbose { - eprintln!("[LLVM] emitting object to {} (begin)", output_path); - } - match codegen.target_machine.write_to_file( - &codegen.module, - inkwell::targets::FileType::Object, - std::path::Path::new(output_path), - ) { - Ok(_) => { - // Verify; if missing, fallback to memory buffer write - if std::fs::metadata(output_path).is_err() { - let buf = codegen - .target_machine - .write_to_memory_buffer(&codegen.module, inkwell::targets::FileType::Object) - .map_err(|e| format!("Failed to get object buffer: {}", e))?; - std::fs::write(output_path, buf.as_slice()).map_err(|e| { - format!("Failed to write object to '{}': {}", output_path, e) - })?; - if verbose { - eprintln!( - "[LLVM] wrote object via memory buffer fallback: {} ({} bytes)", - output_path, - buf.get_size() - ); - } - } else if verbose { - if let Ok(meta) = std::fs::metadata(output_path) { - eprintln!( - "[LLVM] wrote object via file API: {} ({} bytes)", - output_path, - meta.len() - ); - } - } - if verbose { - eprintln!("[LLVM] emit complete (Ok branch) for {}", output_path); - } - Ok(()) - } - Err(e) => { - // Fallback: memory buffer - let buf = codegen - .target_machine - .write_to_memory_buffer(&codegen.module, inkwell::targets::FileType::Object) - .map_err(|ee| { - format!( - "Failed to write object ({}); and memory buffer failed: {}", - e, ee - ) - })?; - std::fs::write(output_path, buf.as_slice()).map_err(|ee| { - format!( - "Failed to write object to '{}': {} (original error: {})", - output_path, ee, e - ) - })?; - if verbose { - eprintln!( - "[LLVM] wrote object via error fallback: {} ({} bytes)", - output_path, - buf.get_size() - ); - } - if verbose { - eprintln!( - "[LLVM] emit complete (Err branch handled) for {}", - output_path - ); - } - Ok(()) - } - } - } -} -*/ - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_compiler_creation() { - let compiler = LLVMCompiler::new(); - assert!(compiler.is_ok()); - } -} -//! LLVM Codegen Orchestrator -//! -//! Structure -//! - `function.rs`: per-function lowering (MIR → LLVM IR) -//! - `utils.rs`: helpers like `sanitize_symbol` -//! - `instructions/*`: focused lowerers and terminators (branch/jump/return, calls, boxcall, externcall) -//! - `types.rs`: MIR→LLVM type mapping and classifiers -//! -//! Keep this file slim: predeclare functions, delegate lowering, emit entry wrapper/object. - -use super::helpers::map_type; -use super::LLVMCompiler; -use crate::backend::llvm::context::CodegenContext; -use crate::mir::function::MirModule; -use inkwell::context::Context; -use inkwell::{ - types::BasicTypeEnum, - values::{BasicValueEnum, FunctionValue}, -}; -use std::collections::HashMap; - -// Submodules -mod types; -mod instructions; -mod utils; -mod function; - -// Local helpers (thin wrappers) -fn sanitize_symbol(name: &str) -> String { utils::sanitize_symbol(name) } - -fn emit_wrapper_and_object<'ctx>( - codegen: &CodegenContext<'ctx>, - entry_name: &str, - output_path: &str, -) -> Result<(), String> { - let i64t = codegen.context.i64_type(); - let ny_main_ty = i64t.fn_type(&[], false); - let ny_main = codegen.module.add_function("ny_main", ny_main_ty, None); - let entry_bb = codegen.context.append_basic_block(ny_main, "entry"); - codegen.builder.position_at_end(entry_bb); - let entry_sym = format!("ny_f_{}", sanitize_symbol(&entry_name)); - let entry_fn = codegen - .module - .get_function(&entry_sym) - .ok_or_else(|| format!("entry function symbol not found: {}", entry_sym))?; - let call = codegen.builder.build_call(entry_fn, &[], "call_main").map_err(|e| e.to_string())?; - let rv = call.try_as_basic_value().left(); - let ret_v = if let Some(v) = rv { - match v { - BasicValueEnum::IntValue(iv) => { - if iv.get_type().get_bit_width() == 64 { iv } else { codegen.builder.build_int_z_extend(iv, i64t, "ret_zext").map_err(|e| e.to_string())? } - } - BasicValueEnum::PointerValue(pv) => codegen.builder.build_ptr_to_int(pv, i64t, "ret_p2i").map_err(|e| e.to_string())?, - BasicValueEnum::FloatValue(fv) => codegen.builder.build_float_to_signed_int(fv, i64t, "ret_f2i").map_err(|e| e.to_string())?, - _ => i64t.const_zero(), - } - } else { i64t.const_zero() }; - codegen.builder.build_return(Some(&ret_v)).map_err(|e| e.to_string())?; - if !ny_main.verify(true) { return Err("ny_main verification failed".to_string()); } - let verbose = std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1"); - if verbose { eprintln!("[LLVM] emitting object to {} (begin)", output_path); } - match codegen.target_machine.write_to_file(&codegen.module, inkwell::targets::FileType::Object, std::path::Path::new(output_path)) { - Ok(_) => { - if std::fs::metadata(output_path).is_err() { - let buf = codegen.target_machine.write_to_memory_buffer(&codegen.module, inkwell::targets::FileType::Object).map_err(|e| format!("Failed to get object buffer: {}", e))?; - std::fs::write(output_path, buf.as_slice()).map_err(|e| format!("Failed to write object to '{}': {}", output_path, e))?; - if verbose { eprintln!("[LLVM] wrote object via memory buffer fallback: {} ({} bytes)", output_path, buf.get_size()); } - } else if verbose { - if let Ok(meta) = std::fs::metadata(output_path) { eprintln!("[LLVM] wrote object via file API: {} ({} bytes)", output_path, meta.len()); } - } - if verbose { eprintln!("[LLVM] emit complete (Ok branch) for {}", output_path); } - Ok(()) - } - Err(e) => { - let buf = codegen.target_machine.write_to_memory_buffer(&codegen.module, inkwell::targets::FileType::Object).map_err(|ee| format!("Failed to write object ({}); and memory buffer failed: {}", e, ee))?; - std::fs::write(output_path, buf.as_slice()).map_err(|ee| format!("Failed to write object to '{}': {} (original error: {})", output_path, ee, e))?; - if verbose { eprintln!("[LLVM] wrote object via error fallback: {} ({} bytes)", output_path, buf.get_size()); eprintln!("[LLVM] emit complete (Err branch handled) for {}", output_path); } - Ok(()) - } - } -} - -impl LLVMCompiler { - pub fn new() -> Result { - Ok(Self { values: HashMap::new() }) - } - - pub fn compile_module(&self, mir_module: &MirModule, output_path: &str) -> Result<(), String> { - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!( - "[LLVM] compile_module start: functions={}, out={}", - mir_module.functions.len(), - output_path - ); - } - let context = Context::create(); - let codegen = CodegenContext::new(&context, "nyash_module")?; - let box_type_ids = crate::backend::llvm::box_types::load_box_type_ids(); - - // Find entry function - let (entry_name, _entry_func_ref) = if let Some((n, f)) = mir_module - .functions - .iter() - .find(|(_n, f)| f.metadata.is_entry_point) - { - (n.clone(), f) - } else if let Some(f) = mir_module.functions.get("Main.main") { - ("Main.main".to_string(), f) - } else if let Some(f) = mir_module.functions.get("main") { - ("main".to_string(), f) - } else if let Some((n, f)) = mir_module.functions.iter().next() { - (n.clone(), f) - } else { - return Err("Main.main function not found in module".to_string()); - }; - - // Predeclare all MIR functions as LLVM functions - let mut llvm_funcs: HashMap = HashMap::new(); - for (name, f) in &mir_module.functions { - let ret_bt = match f.signature.return_type { - crate::mir::MirType::Void => codegen.context.i64_type().into(), - ref t => map_type(codegen.context, t)?, - }; - let mut params_bt: Vec = Vec::new(); - for pt in &f.signature.params { params_bt.push(map_type(codegen.context, pt)?); } let param_vals: Vec<_> = params_bt.iter().map(|t| (*t).into()).collect(); let ll_fn_ty = match ret_bt { BasicTypeEnum::IntType(t) => t.fn_type(¶m_vals, false), @@ -1301,7 +88,7 @@ impl LLVMCompiler { BasicTypeEnum::PointerType(t) => t.fn_type(¶m_vals, false), _ => return Err("Unsupported return basic type".to_string()), }; - let sym = format!("ny_f_{}", utils::sanitize_symbol(name)); + let sym = format!("ny_f_{}", sanitize_symbol(name)); let lf = codegen.module.add_function(&sym, ll_fn_ty, None); llvm_funcs.insert(name.clone(), lf); } @@ -1309,11 +96,18 @@ impl LLVMCompiler { // Lower all functions for (name, func) in &mir_module.functions { let llvm_func = *llvm_funcs.get(name).ok_or("predecl not found")?; - function::lower_one_function(&codegen, llvm_func, func, name, &box_type_ids, &llvm_funcs)?; + function::lower_one_function( + &codegen, + llvm_func, + func, + name, + &box_type_ids, + &llvm_funcs, + )?; } // Build entry wrapper and emit object - emit_wrapper_and_object(&codegen, &entry_name, output_path) + object::emit_wrapper_and_object(&codegen, &entry_name, output_path) } } diff --git a/src/backend/llvm/compiler/codegen/object.rs b/src/backend/llvm/compiler/codegen/object.rs new file mode 100644 index 00000000..33e3c794 --- /dev/null +++ b/src/backend/llvm/compiler/codegen/object.rs @@ -0,0 +1,125 @@ +use super::sanitize_symbol; +use crate::backend::llvm::context::CodegenContext; +use inkwell::values::BasicValueEnum; + +pub(super) fn emit_wrapper_and_object<'ctx>( + codegen: &CodegenContext<'ctx>, + entry_name: &str, + output_path: &str, +) -> Result<(), String> { + let i64t = codegen.context.i64_type(); + let ny_main_ty = i64t.fn_type(&[], false); + let ny_main = codegen.module.add_function("ny_main", ny_main_ty, None); + let entry_bb = codegen.context.append_basic_block(ny_main, "entry"); + codegen.builder.position_at_end(entry_bb); + let entry_sym = format!("ny_f_{}", sanitize_symbol(entry_name)); + let entry_fn = codegen + .module + .get_function(&entry_sym) + .ok_or_else(|| format!("entry function symbol not found: {}", entry_sym))?; + let call = codegen + .builder + .build_call(entry_fn, &[], "call_main") + .map_err(|e| e.to_string())?; + let rv = call.try_as_basic_value().left(); + let ret_v = if let Some(v) = rv { + match v { + BasicValueEnum::IntValue(iv) => { + if iv.get_type().get_bit_width() == 64 { + iv + } else { + codegen + .builder + .build_int_z_extend(iv, i64t, "ret_zext") + .map_err(|e| e.to_string())? + } + } + BasicValueEnum::PointerValue(pv) => codegen + .builder + .build_ptr_to_int(pv, i64t, "ret_p2i") + .map_err(|e| e.to_string())?, + BasicValueEnum::FloatValue(fv) => codegen + .builder + .build_float_to_signed_int(fv, i64t, "ret_f2i") + .map_err(|e| e.to_string())?, + _ => i64t.const_zero(), + } + } else { + i64t.const_zero() + }; + codegen + .builder + .build_return(Some(&ret_v)) + .map_err(|e| e.to_string())?; + if !ny_main.verify(true) { + return Err("ny_main verification failed".to_string()); + } + let verbose = std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1"); + if verbose { + eprintln!("[LLVM] emitting object to {} (begin)", output_path); + } + match codegen.target_machine.write_to_file( + &codegen.module, + inkwell::targets::FileType::Object, + std::path::Path::new(output_path), + ) { + Ok(_) => { + if std::fs::metadata(output_path).is_err() { + let buf = codegen + .target_machine + .write_to_memory_buffer(&codegen.module, inkwell::targets::FileType::Object) + .map_err(|e| format!("Failed to get object buffer: {}", e))?; + std::fs::write(output_path, buf.as_slice()) + .map_err(|e| format!("Failed to write object to '{}': {}", output_path, e))?; + if verbose { + eprintln!( + "[LLVM] wrote object via memory buffer fallback: {} ({} bytes)", + output_path, + buf.get_size() + ); + } + } else if verbose { + if let Ok(meta) = std::fs::metadata(output_path) { + eprintln!( + "[LLVM] wrote object via file API: {} ({} bytes)", + output_path, + meta.len() + ); + } + } + if verbose { + eprintln!("[LLVM] emit complete (Ok branch) for {}", output_path); + } + Ok(()) + } + Err(e) => { + let buf = codegen + .target_machine + .write_to_memory_buffer(&codegen.module, inkwell::targets::FileType::Object) + .map_err(|ee| { + format!( + "Failed to write object ({}); and memory buffer failed: {}", + e, ee + ) + })?; + std::fs::write(output_path, buf.as_slice()).map_err(|ee| { + format!( + "Failed to write object to '{}': {} (original error: {})", + output_path, ee, e + ) + })?; + if verbose { + eprintln!( + "[LLVM] wrote object via error fallback: {} ({} bytes)", + output_path, + buf.get_size() + ); + eprintln!( + "[LLVM] emit complete (Err branch handled) for {}", + output_path + ); + } + Ok(()) + } + } +} diff --git a/src/backend/llvm/compiler/codegen/types.rs b/src/backend/llvm/compiler/codegen/types.rs index 684cabe8..87cdae53 100644 --- a/src/backend/llvm/compiler/codegen/types.rs +++ b/src/backend/llvm/compiler/codegen/types.rs @@ -54,9 +54,7 @@ pub(super) fn to_i64_any<'ctx>( let castp = builder .build_pointer_cast(tmp, fptr_ty, "i64p_to_f64p") .map_err(|e| e.to_string())?; - builder - .build_store(castp, fv) - .map_err(|e| e.to_string())?; + builder.build_store(castp, fv).map_err(|e| e.to_string())?; builder .build_load(i64t, tmp, "ld_f2i") .map_err(|e| e.to_string())? diff --git a/src/backend/llvm/compiler/codegen/utils.rs b/src/backend/llvm/compiler/codegen/utils.rs index 1c004290..fbdad829 100644 --- a/src/backend/llvm/compiler/codegen/utils.rs +++ b/src/backend/llvm/compiler/codegen/utils.rs @@ -1,27 +1,39 @@ -use std::collections::HashMap; +use crate::mir::instruction::{ConstValue, MirInstruction}; use crate::mir::ValueId; -use crate::mir::instruction::{MirInstruction, ConstValue}; +use std::collections::HashMap; pub(super) fn sanitize_symbol(name: &str) -> String { name.chars() - .map(|c| match c { '.' | '/' | '-' => '_', other => other }) + .map(|c| match c { + '.' | '/' | '-' => '_', + other => other, + }) .collect() } -pub(super) fn build_const_str_map(f: &crate::mir::function::MirFunction) -> HashMap { +pub(super) fn build_const_str_map( + f: &crate::mir::function::MirFunction, +) -> HashMap { let mut m = HashMap::new(); for bid in f.block_ids() { if let Some(b) = f.blocks.get(&bid) { for inst in &b.instructions { - if let MirInstruction::Const { dst, value: ConstValue::String(s) } = inst { + if let MirInstruction::Const { + dst, + value: ConstValue::String(s), + } = inst + { m.insert(*dst, s.clone()); } } - if let Some(MirInstruction::Const { dst, value: ConstValue::String(s) }) = &b.terminator { + if let Some(MirInstruction::Const { + dst, + value: ConstValue::String(s), + }) = &b.terminator + { m.insert(*dst, s.clone()); } } } m } - diff --git a/src/runner/json_v0_bridge.rs b/src/runner/json_v0_bridge.rs index 4cb7185a..9bd5005d 100644 --- a/src/runner/json_v0_bridge.rs +++ b/src/runner/json_v0_bridge.rs @@ -3,6 +3,7 @@ use crate::mir::{ MirModule, MirPrinter, MirType, ValueId, }; use serde::{Deserialize, Serialize}; +use std::collections::HashMap; #[derive(Debug, Deserialize, Serialize)] struct ProgramV0 { @@ -125,12 +126,97 @@ struct LoopContext { exit_bb: BasicBlockId, } +struct BridgeEnv { + throw_enabled: bool, + mir_no_phi: bool, + allow_me_dummy: bool, + me_class: String, +} + +impl BridgeEnv { + fn load() -> Self { + Self { + throw_enabled: std::env::var("NYASH_BRIDGE_THROW_ENABLE").ok().as_deref() == Some("1"), + mir_no_phi: crate::config::env::mir_no_phi(), + allow_me_dummy: std::env::var("NYASH_BRIDGE_ME_DUMMY").ok().as_deref() == Some("1"), + me_class: std::env::var("NYASH_BRIDGE_ME_CLASS").unwrap_or_else(|_| "Main".to_string()), + } + } +} + +trait VarScope { + fn resolve( + &mut self, + env: &BridgeEnv, + f: &mut MirFunction, + cur_bb: BasicBlockId, + name: &str, + ) -> Result, String>; +} + +struct NoVars; + +impl VarScope for NoVars { + fn resolve( + &mut self, + _env: &BridgeEnv, + _f: &mut MirFunction, + _cur_bb: BasicBlockId, + name: &str, + ) -> Result, String> { + Err(format!("undefined variable in this context: {}", name)) + } +} + +struct MapVars<'a> { + vars: &'a mut HashMap, +} + +impl<'a> MapVars<'a> { + fn new(vars: &'a mut HashMap) -> Self { + Self { vars } + } +} + +impl<'a> VarScope for MapVars<'a> { + fn resolve( + &mut self, + env: &BridgeEnv, + f: &mut MirFunction, + cur_bb: BasicBlockId, + name: &str, + ) -> Result, String> { + if let Some(&vid) = self.vars.get(name) { + return Ok(Some(vid)); + } + if name == "me" { + if env.allow_me_dummy { + let dst = f.next_value_id(); + if let Some(bb) = f.get_block_mut(cur_bb) { + bb.add_instruction(MirInstruction::NewBox { + dst, + box_type: env.me_class.clone(), + args: vec![], + }); + } + self.vars.insert(name.to_string(), dst); + Ok(Some(dst)) + } else { + Err("undefined 'me' outside box context (set NYASH_BRIDGE_ME_DUMMY=1 to inject placeholder)".into()) + } + } else { + Ok(None) + } + } +} + fn lower_throw( + env: &BridgeEnv, f: &mut MirFunction, cur_bb: BasicBlockId, exception_value: ValueId, ) -> (ValueId, BasicBlockId) { - if std::env::var("NYASH_BRIDGE_THROW_ENABLE").ok().as_deref() == Some("1") { + if env.throw_enabled { if let Some(bb) = f.get_block_mut(cur_bb) { bb.set_terminator(MirInstruction::Throw { exception: exception_value, @@ -158,6 +244,7 @@ pub fn parse_json_v0_to_module(json: &str) -> Result { } // Create module and main function let mut module = MirModule::new("ny_json_v0".into()); + let env = BridgeEnv::load(); let sig = FunctionSignature { name: "main".into(), params: vec![], @@ -172,12 +259,17 @@ pub fn parse_json_v0_to_module(json: &str) -> Result { } // Variable map for simple locals (Stage-2; currently minimal) - let mut var_map: std::collections::HashMap = - std::collections::HashMap::new(); + let mut var_map: HashMap = HashMap::new(); let mut loop_stack: Vec = Vec::new(); let start_bb = f.entry_block; - let end_bb = - lower_stmt_list_with_vars(&mut f, start_bb, &prog.body, &mut var_map, &mut loop_stack)?; + let end_bb = lower_stmt_list_with_vars( + &mut f, + start_bb, + &prog.body, + &mut var_map, + &mut loop_stack, + &env, + )?; // Ensure function terminates: add `ret 0` to last un-terminated block (prefer end_bb else entry) let need_default_ret = f.blocks.iter().any(|(_k, b)| !b.is_terminated()); if need_default_ret { @@ -211,11 +303,13 @@ fn next_block_id(f: &MirFunction) -> BasicBlockId { BasicBlockId::new(mx) } -fn lower_expr( +fn lower_expr_with_scope( + env: &BridgeEnv, f: &mut MirFunction, cur_bb: BasicBlockId, e: &ExprV0, -) -> Result<(crate::mir::ValueId, BasicBlockId), String> { + vars: &mut S, +) -> Result<(ValueId, BasicBlockId), String> { match e { ExprV0::Int { value } => { // Accept number or stringified digits @@ -256,8 +350,8 @@ fn lower_expr( Ok((dst, cur_bb)) } ExprV0::Binary { op, lhs, rhs } => { - let (l, cur_after_l) = lower_expr(f, cur_bb, lhs)?; - let (r, cur_after_r) = lower_expr(f, cur_after_l, rhs)?; + let (l, cur_after_l) = lower_expr_with_scope(env, f, cur_bb, lhs, vars)?; + let (r, cur_after_r) = lower_expr_with_scope(env, f, cur_after_l, rhs, vars)?; let bop = match op.as_str() { "+" => BinaryOp::Add, "-" => BinaryOp::Sub, @@ -281,7 +375,7 @@ fn lower_expr( method, args, } => { - let (arg_ids, cur2) = lower_args(f, cur_bb, args)?; + let (arg_ids, cur2) = lower_args_with_scope(env, f, cur_bb, args, vars)?; let dst = f.next_value_id(); if let Some(bb) = f.get_block_mut(cur2) { bb.add_instruction(MirInstruction::ExternCall { @@ -295,8 +389,8 @@ fn lower_expr( Ok((dst, cur2)) } ExprV0::Compare { op, lhs, rhs } => { - let (l, cur_after_l) = lower_expr(f, cur_bb, lhs)?; - let (r, cur_after_r) = lower_expr(f, cur_after_l, rhs)?; + let (l, cur_after_l) = lower_expr_with_scope(env, f, cur_bb, lhs, vars)?; + let (r, cur_after_r) = lower_expr_with_scope(env, f, cur_after_l, rhs, vars)?; let cop = match op.as_str() { "==" => crate::mir::CompareOp::Eq, "!=" => crate::mir::CompareOp::Ne, @@ -319,7 +413,7 @@ fn lower_expr( } ExprV0::Logical { op, lhs, rhs } => { // Short-circuit boolean logic with branches (+phi or edge-copy) - let (l, cur_after_l) = lower_expr(f, cur_bb, lhs)?; + let (l, cur_after_l) = lower_expr_with_scope(env, f, cur_bb, lhs, vars)?; let rhs_bb = next_block_id(f); let fall_bb = BasicBlockId::new(rhs_bb.0 + 1); let merge_bb = BasicBlockId::new(rhs_bb.0 + 2); @@ -380,7 +474,7 @@ fn lower_expr( bb.set_terminator(MirInstruction::Jump { target: merge_bb }); } // evaluate rhs starting at rhs_bb and ensure the terminal block jumps to merge - let (rval, rhs_end) = lower_expr(f, rhs_bb, rhs)?; + let (rval, rhs_end) = lower_expr_with_scope(env, f, rhs_bb, rhs, vars)?; if let Some(bb) = f.get_block_mut(rhs_end) { if !bb.is_terminated() { bb.set_terminator(MirInstruction::Jump { target: merge_bb }); @@ -393,15 +487,21 @@ fn lower_expr( ); } // Merge: PHI または edge-copy で合流値を定義 - let no_phi = crate::config::env::mir_no_phi(); + let no_phi = env.mir_no_phi; let out = f.next_value_id(); if no_phi { // Edge copies in predecessors if let Some(bb) = f.get_block_mut(fall_bb) { - bb.add_instruction(MirInstruction::Copy { dst: out, src: cdst }); + bb.add_instruction(MirInstruction::Copy { + dst: out, + src: cdst, + }); } if let Some(bb) = f.get_block_mut(rhs_end) { - bb.add_instruction(MirInstruction::Copy { dst: out, src: rval }); + bb.add_instruction(MirInstruction::Copy { + dst: out, + src: rval, + }); } } else if let Some(bb) = f.get_block_mut(merge_bb) { let mut inputs: Vec<(BasicBlockId, ValueId)> = vec![(fall_bb, cdst)]; @@ -432,7 +532,7 @@ fn lower_expr( // For each element: eval then push let mut cur = cur_bb; for e in args { - let (v, c) = lower_expr(f, cur, e)?; + let (v, c) = lower_expr_with_scope(env, f, cur, e, vars)?; cur = c; let tmp = f.next_value_id(); if let Some(bb) = f.get_block_mut(cur) { @@ -462,9 +562,9 @@ fn lower_expr( let mut it = args.iter(); while let Some(k) = it.next() { if let Some(v) = it.next() { - let (kv, cur2) = lower_expr(f, cur, k)?; + let (kv, cur2) = lower_expr_with_scope(env, f, cur, k, vars)?; cur = cur2; - let (vv, cur3) = lower_expr(f, cur, v)?; + let (vv, cur3) = lower_expr_with_scope(env, f, cur, v, vars)?; cur = cur3; let tmp = f.next_value_id(); if let Some(bb) = f.get_block_mut(cur) { @@ -484,7 +584,7 @@ fn lower_expr( return Ok((mapv, cur)); } // Fallback: treat as normal dynamic call - let (arg_ids, cur) = lower_args(f, cur_bb, args)?; + let (arg_ids, cur) = lower_args_with_scope(env, f, cur_bb, args, vars)?; let fun_val = f.next_value_id(); if let Some(bb) = f.get_block_mut(cur) { bb.add_instruction(MirInstruction::Const { @@ -509,7 +609,7 @@ fn lower_expr( matches!(&**recv, ExprV0::New { class, .. } if class == "ConsoleBox"); if recv_is_console_new && (method == "println" || method == "print" || method == "log") { - let (arg_ids, cur2) = lower_args(f, cur_bb, args)?; + let (arg_ids, cur2) = lower_args_with_scope(env, f, cur_bb, args, vars)?; let dst = f.next_value_id(); if let Some(bb) = f.get_block_mut(cur2) { bb.add_instruction(MirInstruction::ExternCall { @@ -522,8 +622,8 @@ fn lower_expr( } return Ok((dst, cur2)); } - let (recv_v, cur) = lower_expr(f, cur_bb, recv)?; - let (arg_ids, cur2) = lower_args(f, cur, args)?; + let (recv_v, cur) = lower_expr_with_scope(env, f, cur_bb, recv, vars)?; + let (arg_ids, cur2) = lower_args_with_scope(env, f, cur, args, vars)?; let dst = f.next_value_id(); if let Some(bb) = f.get_block_mut(cur2) { bb.add_instruction(MirInstruction::BoxCall { @@ -538,7 +638,7 @@ fn lower_expr( Ok((dst, cur2)) } ExprV0::New { class, args } => { - let (arg_ids, cur) = lower_args(f, cur_bb, args)?; + let (arg_ids, cur) = lower_args_with_scope(env, f, cur_bb, args, vars)?; let dst = f.next_value_id(); if let Some(bb) = f.get_block_mut(cur) { bb.add_instruction(MirInstruction::NewBox { @@ -549,292 +649,50 @@ fn lower_expr( } Ok((dst, cur)) } - ExprV0::Var { name } => Err(format!("undefined variable in this context: {}", name)), + ExprV0::Var { name } => match vars.resolve(env, f, cur_bb, name)? { + Some(v) => Ok((v, cur_bb)), + None => Err(format!("undefined variable: {}", name)), + }, ExprV0::Throw { expr } => { - let (exc, cur) = lower_expr(f, cur_bb, expr)?; - let (dst, cur) = lower_throw(f, cur, exc); + let (exc, cur) = lower_expr_with_scope(env, f, cur_bb, expr, vars)?; + let (dst, cur) = lower_throw(env, f, cur, exc); Ok((dst, cur)) } } } -fn lower_expr_with_vars( +fn lower_expr( + env: &BridgeEnv, f: &mut MirFunction, cur_bb: BasicBlockId, e: &ExprV0, - vars: &mut std::collections::HashMap, -) -> Result<(crate::mir::ValueId, BasicBlockId), String> { - match e { - ExprV0::Var { name } => { - if let Some(&vid) = vars.get(name) { - return Ok((vid, cur_bb)); - } - if name == "me" { - // Optional gate: allow a dummy 'me' instance for Stage-2 JSON smoke - if std::env::var("NYASH_BRIDGE_ME_DUMMY").ok().as_deref() == Some("1") { - let class = std::env::var("NYASH_BRIDGE_ME_CLASS") - .unwrap_or_else(|_| "Main".to_string()); - let dst = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur_bb) { - bb.add_instruction(MirInstruction::NewBox { - dst, - box_type: class, - args: vec![], - }); - } - vars.insert("me".to_string(), dst); - return Ok((dst, cur_bb)); - } else { - return Err("undefined 'me' outside box context (set NYASH_BRIDGE_ME_DUMMY=1 to inject placeholder)".into()); - } - } - Err(format!("undefined variable: {}", name)) - } - ExprV0::Throw { expr } => { - let (exc, cur) = lower_expr_with_vars(f, cur_bb, expr, vars)?; - let (dst, cur) = lower_throw(f, cur, exc); - Ok((dst, cur)) - } - ExprV0::Call { name, args } => { - // Special: array literal lowering in vars context - if name == "array.of" { - let arr = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur_bb) { - bb.add_instruction(MirInstruction::NewBox { - dst: arr, - box_type: "ArrayBox".into(), - args: vec![], - }); - } - let mut cur = cur_bb; - for e in args { - let (v, c) = lower_expr_with_vars(f, cur, e, vars)?; - cur = c; - let tmp = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur) { - bb.add_instruction(MirInstruction::BoxCall { - dst: Some(tmp), - box_val: arr, - method: "push".into(), - method_id: None, - args: vec![v], - effects: EffectMask::READ, - }); - } - } - return Ok((arr, cur)); - } - // Special: map literal lowering in vars context - if name == "map.of" { - let mapv = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur_bb) { - bb.add_instruction(MirInstruction::NewBox { - dst: mapv, - box_type: "MapBox".into(), - args: vec![], - }); - } - let mut cur = cur_bb; - let mut it = args.iter(); - while let Some(k) = it.next() { - if let Some(v) = it.next() { - let (kv, cur2) = lower_expr_with_vars(f, cur, k, vars)?; - cur = cur2; - let (vv, cur3) = lower_expr_with_vars(f, cur, v, vars)?; - cur = cur3; - let tmp = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur) { - bb.add_instruction(MirInstruction::BoxCall { - dst: Some(tmp), - box_val: mapv, - method: "set".into(), - method_id: None, - args: vec![kv, vv], - effects: EffectMask::READ, - }); - } - } else { - break; - } - } - return Ok((mapv, cur)); - } - // Lower args - let (arg_ids, cur) = lower_args_with_vars(f, cur_bb, args, vars)?; - // Encode as: const fun_name; call - let fun_val = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur) { - bb.add_instruction(MirInstruction::Const { - dst: fun_val, - value: ConstValue::String(name.clone()), - }); - } - let dst = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur) { - bb.add_instruction(MirInstruction::Call { - dst: Some(dst), - func: fun_val, - args: arg_ids, - effects: EffectMask::READ, - }); - } - Ok((dst, cur)) - } - ExprV0::Method { recv, method, args } => { - let recv_is_console_new = - matches!(&**recv, ExprV0::New { class, .. } if class == "ConsoleBox"); - if recv_is_console_new && (method == "println" || method == "print" || method == "log") - { - let (arg_ids, cur2) = lower_args_with_vars(f, cur_bb, args, vars)?; - let dst = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur2) { - bb.add_instruction(MirInstruction::ExternCall { - dst: Some(dst), - iface_name: "env.console".into(), - method_name: "log".into(), - args: arg_ids, - effects: EffectMask::READ, - }); - } - return Ok((dst, cur2)); - } - let (recv_v, cur) = lower_expr_with_vars(f, cur_bb, recv, vars)?; - let (arg_ids, cur2) = lower_args_with_vars(f, cur, args, vars)?; - let dst = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur2) { - bb.add_instruction(MirInstruction::BoxCall { - dst: Some(dst), - box_val: recv_v, - method: method.clone(), - method_id: None, - args: arg_ids, - effects: EffectMask::READ, - }); - } - Ok((dst, cur2)) - } - ExprV0::New { class, args } => { - let (arg_ids, cur) = lower_args_with_vars(f, cur_bb, args, vars)?; - let dst = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur) { - bb.add_instruction(MirInstruction::NewBox { - dst, - box_type: class.clone(), - args: arg_ids, - }); - } - Ok((dst, cur)) - } - ExprV0::Binary { op, lhs, rhs } => { - let (l, cur_after_l) = lower_expr_with_vars(f, cur_bb, lhs, vars)?; - let (r, cur_after_r) = lower_expr_with_vars(f, cur_after_l, rhs, vars)?; - let bop = match op.as_str() { - "+" => BinaryOp::Add, - "-" => BinaryOp::Sub, - "*" => BinaryOp::Mul, - "/" => BinaryOp::Div, - _ => return Err("unsupported op".into()), - }; - let dst = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur_after_r) { - bb.add_instruction(MirInstruction::BinOp { - dst, - op: bop, - lhs: l, - rhs: r, - }); - } - Ok((dst, cur_after_r)) - } - ExprV0::Compare { op, lhs, rhs } => { - let (l, cur_after_l) = lower_expr_with_vars(f, cur_bb, lhs, vars)?; - let (r, cur_after_r) = lower_expr_with_vars(f, cur_after_l, rhs, vars)?; - let cop = match op.as_str() { - "==" => crate::mir::CompareOp::Eq, - "!=" => crate::mir::CompareOp::Ne, - "<" => crate::mir::CompareOp::Lt, - "<=" => crate::mir::CompareOp::Le, - ">" => crate::mir::CompareOp::Gt, - ">=" => crate::mir::CompareOp::Ge, - _ => return Err("unsupported compare op".into()), - }; - let dst = f.next_value_id(); - if let Some(bb) = f.get_block_mut(cur_after_r) { - bb.add_instruction(MirInstruction::Compare { - dst, - op: cop, - lhs: l, - rhs: r, - }); - } - Ok((dst, cur_after_r)) - } - ExprV0::Logical { op, lhs, rhs } => { - let (l, cur_after_l) = lower_expr_with_vars(f, cur_bb, lhs, vars)?; - let rhs_bb = next_block_id(f); - let fall_bb = BasicBlockId::new(rhs_bb.0 + 1); - let merge_bb = BasicBlockId::new(rhs_bb.0 + 2); - f.add_block(crate::mir::BasicBlock::new(rhs_bb)); - f.add_block(crate::mir::BasicBlock::new(fall_bb)); - f.add_block(crate::mir::BasicBlock::new(merge_bb)); - let is_and = matches!(op.as_str(), "&&" | "and"); - if let Some(bb) = f.get_block_mut(cur_after_l) { - if is_and { - bb.set_terminator(MirInstruction::Branch { - condition: l, - then_bb: rhs_bb, - else_bb: fall_bb, - }); - } else { - bb.set_terminator(MirInstruction::Branch { - condition: l, - then_bb: fall_bb, - else_bb: rhs_bb, - }); - } - } - let cdst = f.next_value_id(); - if let Some(bb) = f.get_block_mut(fall_bb) { - let cval = if is_and { - ConstValue::Bool(false) - } else { - ConstValue::Bool(true) - }; - bb.add_instruction(MirInstruction::Const { - dst: cdst, - value: cval, - }); - bb.set_terminator(MirInstruction::Jump { target: merge_bb }); - } - let (rval, rhs_end) = lower_expr_with_vars(f, rhs_bb, rhs, vars)?; - if let Some(bb) = f.get_block_mut(rhs_end) { - if !bb.is_terminated() { - bb.set_terminator(MirInstruction::Jump { target: merge_bb }); - } - } - let out = f.next_value_id(); - if let Some(bb) = f.get_block_mut(merge_bb) { - bb.insert_instruction_after_phis(MirInstruction::Phi { - dst: out, - inputs: vec![(rhs_end, rval), (fall_bb, cdst)], - }); - } - Ok((out, merge_bb)) - } - _ => lower_expr(f, cur_bb, e), - } +) -> Result<(ValueId, BasicBlockId), String> { + let mut scope = NoVars; + lower_expr_with_scope(env, f, cur_bb, e, &mut scope) +} + +fn lower_expr_with_vars( + env: &BridgeEnv, + f: &mut MirFunction, + cur_bb: BasicBlockId, + e: &ExprV0, + vars: &mut HashMap, +) -> Result<(ValueId, BasicBlockId), String> { + let mut scope = MapVars::new(vars); + lower_expr_with_scope(env, f, cur_bb, e, &mut scope) } fn lower_stmt_with_vars( f: &mut MirFunction, cur_bb: BasicBlockId, s: &StmtV0, - vars: &mut std::collections::HashMap, + vars: &mut HashMap, loop_stack: &mut Vec, + env: &BridgeEnv, ) -> Result { match s { StmtV0::Return { expr } => { - let (v, cur) = lower_expr_with_vars(f, cur_bb, expr, vars)?; + let (v, cur) = lower_expr_with_vars(env, f, cur_bb, expr, vars)?; if let Some(bb) = f.get_block_mut(cur) { bb.set_terminator(MirInstruction::Return { value: Some(v) }); } @@ -845,7 +703,7 @@ fn lower_stmt_with_vars( method, args, } => { - let (arg_ids, cur) = lower_args_with_vars(f, cur_bb, args, vars)?; + let (arg_ids, cur) = lower_args_with_vars(env, f, cur_bb, args, vars)?; if let Some(bb) = f.get_block_mut(cur) { bb.add_instruction(MirInstruction::ExternCall { dst: None, @@ -858,11 +716,11 @@ fn lower_stmt_with_vars( Ok(cur) } StmtV0::Expr { expr } => { - let (_v, cur) = lower_expr_with_vars(f, cur_bb, expr, vars)?; + let (_v, cur) = lower_expr_with_vars(env, f, cur_bb, expr, vars)?; Ok(cur) } StmtV0::Local { name, expr } => { - let (v, cur) = lower_expr_with_vars(f, cur_bb, expr, vars)?; + let (v, cur) = lower_expr_with_vars(env, f, cur_bb, expr, vars)?; vars.insert(name.clone(), v); Ok(cur) } @@ -918,10 +776,16 @@ fn lower_stmt_with_vars( if !try_enabled || catches.is_empty() || catches.len() > 1 { let mut tmp_vars = vars.clone(); let mut next_bb = - lower_stmt_list_with_vars(f, cur_bb, try_body, &mut tmp_vars, loop_stack)?; + lower_stmt_list_with_vars(f, cur_bb, try_body, &mut tmp_vars, loop_stack, env)?; if !finally.is_empty() { - next_bb = - lower_stmt_list_with_vars(f, next_bb, finally, &mut tmp_vars, loop_stack)?; + next_bb = lower_stmt_list_with_vars( + f, + next_bb, + finally, + &mut tmp_vars, + loop_stack, + env, + )?; } *vars = tmp_vars; return Ok(next_bb); @@ -960,7 +824,7 @@ fn lower_stmt_with_vars( let mut try_vars = vars.clone(); let try_end = - lower_stmt_list_with_vars(f, try_bb, try_body, &mut try_vars, loop_stack)?; + lower_stmt_list_with_vars(f, try_bb, try_body, &mut try_vars, loop_stack, env)?; if let Some(bb) = f.get_block_mut(try_end) { if !bb.is_terminated() { bb.set_terminator(MirInstruction::Jump { @@ -980,6 +844,7 @@ fn lower_stmt_with_vars( &catch_clause.body, &mut catch_vars, loop_stack, + env, )?; if let Some(param) = &catch_clause.param { catch_vars.remove(param); @@ -1045,6 +910,7 @@ fn lower_stmt_with_vars( finally, &mut finally_vars, loop_stack, + env, )?; if let Some(bb) = f.get_block_mut(final_end) { if !bb.is_terminated() { @@ -1098,7 +964,7 @@ fn lower_stmt_with_vars( } StmtV0::If { cond, then, r#else } => { // Lower condition first - let (cval, cur) = lower_expr_with_vars(f, cur_bb, cond, vars)?; + let (cval, cur) = lower_expr_with_vars(env, f, cur_bb, cond, vars)?; // Create then/else/merge blocks let then_bb = next_block_id(f); let else_bb = BasicBlockId::new(then_bb.0 + 1); @@ -1118,7 +984,8 @@ fn lower_stmt_with_vars( // Clone current vars as branch-local maps let base_vars = vars.clone(); let mut then_vars = base_vars.clone(); - let tend = lower_stmt_list_with_vars(f, then_bb, then, &mut then_vars, loop_stack)?; + let tend = + lower_stmt_list_with_vars(f, then_bb, then, &mut then_vars, loop_stack, env)?; if let Some(bb) = f.get_block_mut(tend) { if !bb.is_terminated() { bb.set_terminator(MirInstruction::Jump { target: merge_bb }); @@ -1127,7 +994,7 @@ fn lower_stmt_with_vars( let (else_end_pred, else_vars) = if let Some(elses) = r#else { let mut ev = base_vars.clone(); - let eend = lower_stmt_list_with_vars(f, else_bb, elses, &mut ev, loop_stack)?; + let eend = lower_stmt_list_with_vars(f, else_bb, elses, &mut ev, loop_stack, env)?; if let Some(bb) = f.get_block_mut(eend) { if !bb.is_terminated() { bb.set_terminator(MirInstruction::Jump { target: merge_bb }); @@ -1144,10 +1011,14 @@ fn lower_stmt_with_vars( // Merge at then/else predecessors(PHI or edge-copy) use std::collections::HashSet; - let no_phi = crate::config::env::mir_no_phi(); + let no_phi = env.mir_no_phi; let mut names: HashSet = base_vars.keys().cloned().collect(); - for k in then_vars.keys() { names.insert(k.clone()); } - for k in else_vars.keys() { names.insert(k.clone()); } + for k in then_vars.keys() { + names.insert(k.clone()); + } + for k in else_vars.keys() { + names.insert(k.clone()); + } for name in names { let tv = then_vars.get(&name).copied(); @@ -1155,13 +1026,22 @@ fn lower_stmt_with_vars( let exists_base = base_vars.contains_key(&name); match (tv, ev, exists_base) { (Some(tval), Some(eval), _) => { - let merged = if tval == eval { tval } else { + let merged = if tval == eval { + tval + } else { let dst = f.next_value_id(); if no_phi { - if let Some(bb) = f.get_block_mut(tend) { bb.add_instruction(MirInstruction::Copy { dst, src: tval }); } - if let Some(bb) = f.get_block_mut(else_end_pred) { bb.add_instruction(MirInstruction::Copy { dst, src: eval }); } + if let Some(bb) = f.get_block_mut(tend) { + bb.add_instruction(MirInstruction::Copy { dst, src: tval }); + } + if let Some(bb) = f.get_block_mut(else_end_pred) { + bb.add_instruction(MirInstruction::Copy { dst, src: eval }); + } } else if let Some(bb) = f.get_block_mut(merge_bb) { - bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs: vec![(tend, tval), (else_end_pred, eval)] }); + bb.insert_instruction_after_phis(MirInstruction::Phi { + dst, + inputs: vec![(tend, tval), (else_end_pred, eval)], + }); } dst }; @@ -1169,13 +1049,22 @@ fn lower_stmt_with_vars( } (Some(tval), None, true) => { if let Some(&bval) = base_vars.get(&name) { - let merged = if tval == bval { tval } else { + let merged = if tval == bval { + tval + } else { let dst = f.next_value_id(); if no_phi { - if let Some(bb) = f.get_block_mut(tend) { bb.add_instruction(MirInstruction::Copy { dst, src: tval }); } - if let Some(bb) = f.get_block_mut(else_end_pred) { bb.add_instruction(MirInstruction::Copy { dst, src: bval }); } + if let Some(bb) = f.get_block_mut(tend) { + bb.add_instruction(MirInstruction::Copy { dst, src: tval }); + } + if let Some(bb) = f.get_block_mut(else_end_pred) { + bb.add_instruction(MirInstruction::Copy { dst, src: bval }); + } } else if let Some(bb) = f.get_block_mut(merge_bb) { - bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs: vec![(tend, tval), (else_end_pred, bval)] }); + bb.insert_instruction_after_phis(MirInstruction::Phi { + dst, + inputs: vec![(tend, tval), (else_end_pred, bval)], + }); } dst }; @@ -1184,13 +1073,22 @@ fn lower_stmt_with_vars( } (None, Some(eval), true) => { if let Some(&bval) = base_vars.get(&name) { - let merged = if eval == bval { eval } else { + let merged = if eval == bval { + eval + } else { let dst = f.next_value_id(); if no_phi { - if let Some(bb) = f.get_block_mut(tend) { bb.add_instruction(MirInstruction::Copy { dst, src: bval }); } - if let Some(bb) = f.get_block_mut(else_end_pred) { bb.add_instruction(MirInstruction::Copy { dst, src: eval }); } + if let Some(bb) = f.get_block_mut(tend) { + bb.add_instruction(MirInstruction::Copy { dst, src: bval }); + } + if let Some(bb) = f.get_block_mut(else_end_pred) { + bb.add_instruction(MirInstruction::Copy { dst, src: eval }); + } } else if let Some(bb) = f.get_block_mut(merge_bb) { - bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs: vec![(tend, bval), (else_end_pred, eval)] }); + bb.insert_instruction_after_phis(MirInstruction::Phi { + dst, + inputs: vec![(tend, bval), (else_end_pred, eval)], + }); } dst }; @@ -1220,11 +1118,10 @@ fn lower_stmt_with_vars( } // Snapshot base vars and set up merged ids for loop-carried vars - let no_phi = crate::config::env::mir_no_phi(); + let no_phi = env.mir_no_phi; let base_vars = vars.clone(); let orig_names: Vec = base_vars.keys().cloned().collect(); - let mut phi_map: std::collections::HashMap = - std::collections::HashMap::new(); + let mut phi_map: HashMap = HashMap::new(); for name in &orig_names { if let Some(&bval) = base_vars.get(name) { let dst = f.next_value_id(); @@ -1235,7 +1132,10 @@ fn lower_stmt_with_vars( } } else if let Some(bb) = f.get_block_mut(cond_bb) { // Initial incoming from preheader via PHI - bb.insert_instruction_after_phis(MirInstruction::Phi { dst, inputs: vec![(cur_bb, bval)] }); + bb.insert_instruction_after_phis(MirInstruction::Phi { + dst, + inputs: vec![(cur_bb, bval)], + }); } phi_map.insert(name.clone(), dst); } @@ -1246,7 +1146,7 @@ fn lower_stmt_with_vars( } // Lower condition using phi-backed vars - let (cval, _cend) = lower_expr_with_vars(f, cond_bb, cond, vars)?; + let (cval, _cend) = lower_expr_with_vars(env, f, cond_bb, cond, vars)?; if let Some(bb) = f.get_block_mut(cond_bb) { bb.set_terminator(MirInstruction::Branch { condition: cval, @@ -1258,7 +1158,8 @@ fn lower_stmt_with_vars( // Lower body; record end block and body-out vars let mut body_vars = vars.clone(); loop_stack.push(LoopContext { cond_bb, exit_bb }); - let bend_res = lower_stmt_list_with_vars(f, body_bb, body, &mut body_vars, loop_stack); + let bend_res = + lower_stmt_list_with_vars(f, body_bb, body, &mut body_vars, loop_stack, env); loop_stack.pop(); let bend = bend_res?; if let Some(bb) = f.get_block_mut(bend) { @@ -1278,7 +1179,10 @@ fn lower_stmt_with_vars( for (name, &phi_dst) in &phi_map { if let Some(&latch_val) = body_vars.get(name) { if let Some(bb) = f.get_block_mut(bend) { - bb.add_instruction(MirInstruction::Copy { dst: phi_dst, src: latch_val }); + bb.add_instruction(MirInstruction::Copy { + dst: phi_dst, + src: latch_val, + }); } } } @@ -1317,12 +1221,13 @@ fn lower_stmt_list_with_vars( f: &mut MirFunction, start_bb: BasicBlockId, stmts: &[StmtV0], - vars: &mut std::collections::HashMap, + vars: &mut HashMap, loop_stack: &mut Vec, + env: &BridgeEnv, ) -> Result { let mut cur = start_bb; for s in stmts { - cur = lower_stmt_with_vars(f, cur, s, vars, loop_stack)?; + cur = lower_stmt_with_vars(f, cur, s, vars, loop_stack, env)?; if let Some(bb) = f.blocks.get(&cur) { if bb.is_terminated() { break; @@ -1331,16 +1236,17 @@ fn lower_stmt_list_with_vars( } Ok(cur) } -fn lower_args_with_vars( +fn lower_args_with_scope( + env: &BridgeEnv, f: &mut MirFunction, cur_bb: BasicBlockId, args: &[ExprV0], - vars: &mut std::collections::HashMap, -) -> Result<(Vec, BasicBlockId), String> { + scope: &mut S, +) -> Result<(Vec, BasicBlockId), String> { let mut out = Vec::with_capacity(args.len()); let mut cur = cur_bb; for a in args { - let (v, c) = lower_expr_with_vars(f, cur, a, vars)?; + let (v, c) = lower_expr_with_scope(env, f, cur, a, scope)?; out.push(v); cur = c; } @@ -1348,18 +1254,24 @@ fn lower_args_with_vars( } fn lower_args( + env: &BridgeEnv, f: &mut MirFunction, cur_bb: BasicBlockId, args: &[ExprV0], -) -> Result<(Vec, BasicBlockId), String> { - let mut out = Vec::with_capacity(args.len()); - let mut cur = cur_bb; - for a in args { - let (v, c) = lower_expr(f, cur, a)?; - out.push(v); - cur = c; - } - Ok((out, cur)) +) -> Result<(Vec, BasicBlockId), String> { + let mut scope = NoVars; + lower_args_with_scope(env, f, cur_bb, args, &mut scope) +} + +fn lower_args_with_vars( + env: &BridgeEnv, + f: &mut MirFunction, + cur_bb: BasicBlockId, + args: &[ExprV0], + vars: &mut HashMap, +) -> Result<(Vec, BasicBlockId), String> { + let mut scope = MapVars::new(vars); + lower_args_with_scope(env, f, cur_bb, args, &mut scope) } pub fn maybe_dump_mir(module: &MirModule) { diff --git a/tools/smokes/curated_llvm.sh b/tools/smokes/curated_llvm.sh index 3e027470..5fdfb072 100644 --- a/tools/smokes/curated_llvm.sh +++ b/tools/smokes/curated_llvm.sh @@ -44,4 +44,7 @@ run "$ROOT_DIR/apps/tests/loop_if_phi.nyash" # Peek expression run "$ROOT_DIR/apps/tests/peek_expr_block.nyash" +# Try/finally control-flow without actual throw +run "$ROOT_DIR/apps/tests/try_finally_break_inner_loop.nyash" + echo "[curated-llvm] OK"