diff --git a/src/backend/llvm/compiler/codegen/instructions/loopform.rs b/src/backend/llvm/compiler/codegen/instructions/loopform.rs index bd9f588a..b48ceda0 100644 --- a/src/backend/llvm/compiler/codegen/instructions/loopform.rs +++ b/src/backend/llvm/compiler/codegen/instructions/loopform.rs @@ -1,6 +1,6 @@ use inkwell::{ basic_block::BasicBlock, - values::{BasicValueEnum, FunctionValue}, + values::{BasicValueEnum, FunctionValue, PhiValue}, }; use crate::backend::llvm::context::CodegenContext; @@ -69,6 +69,9 @@ pub fn lower_while_loopform<'ctx, 'b>( after_bb: BasicBlockId, bb_map: &std::collections::HashMap>, vmap: &std::collections::HashMap>, + // Registry to allow later body→dispatch wiring (simple bodies) + registry: &mut std::collections::HashMap, 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); } @@ -95,22 +98,52 @@ pub fn lower_while_loopform<'ctx, 'b>( }); }); - // Dispatch: currently pass-through to original else/after block + // 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")?; - cursor.with_block(after_bb, lf.dispatch, |c| { + 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(); + let tag_ty: inkwell::types::BasicTypeEnum = i8t.into(); + let tag_phi = codegen + .builder + .build_phi(tag_ty, "lf_tag") + .map_err(|e| e.to_string()) + .unwrap(); + let payload_ty: inkwell::types::BasicTypeEnum = i64t.into(); + let payload_phi = codegen + .builder + .build_phi(payload_ty, "lf_payload") + .map_err(|e| e.to_string()) + .unwrap(); + let tag_break = i8t.const_int(1, false); + let payload_zero = i64t.const_zero(); + tag_phi.add_incoming(&[(&tag_break, header_llbb)]); + payload_phi.add_incoming(&[(&payload_zero, header_llbb)]); + let tag_iv = tag_phi.as_basic_value().into_int_value(); c.emit_term(after_bb, |b| { - b.build_unconditional_branch(orig_after) + b.build_switch(tag_iv, lf.exit, &[(i8t.const_int(0, false), lf.latch)]) .map_err(|e| e.to_string()) .unwrap(); }); + (tag_phi, payload_phi) }); - // Latch/Exit are reserved for Phase 2 wiring (PHI + switch), keep them unreachable for now - // to avoid verifier errors from unterminated blocks. + // Register for simple body→dispatch wiring later (at body terminator lowering time) + registry.insert(header_bid, (lf.dispatch, tag_phi, payload_phi, lf.latch)); + body_to_header.insert(body_bb, header_bid); + + // Latch: keep unreachable for now (avoid adding a new predecessor to header) codegen.builder.position_at_end(lf.latch); let _ = codegen.builder.build_unreachable(); + // Exit: to original after codegen.builder.position_at_end(lf.exit); - let _ = codegen.builder.build_unreachable(); + codegen + .builder + .build_unconditional_branch(orig_after) + .map_err(|e| e.to_string()) + .unwrap(); if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { eprintln!( diff --git a/src/backend/llvm/compiler/codegen/mod.rs b/src/backend/llvm/compiler/codegen/mod.rs index ea90934d..aa03090d 100644 --- a/src/backend/llvm/compiler/codegen/mod.rs +++ b/src/backend/llvm/compiler/codegen/mod.rs @@ -204,6 +204,9 @@ impl LLVMCompiler { // Lower body let mut loopform_loop_id: u32 = 0; let sealed_mode = std::env::var("NYASH_LLVM_PHI_SEALED").ok().as_deref() == Some("1"); + // LoopForm registry (per-function lowering; gated) + let mut loopform_registry: HashMap = HashMap::new(); + let mut loopform_body_to_header: HashMap = HashMap::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 @@ -339,7 +342,34 @@ impl LLVMCompiler { instructions::emit_return(&codegen, &mut cursor, *bid, func, &vmap, value)?; } MirInstruction::Jump { target } => { - instructions::emit_jump(&codegen, &mut cursor, *bid, target, &bb_map, &phis_by_block, &vmap)?; + // 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, &vmap)?; + } } MirInstruction::Branch { condition, then_bb, else_bb } => { // LoopForm Phase 1 (gated): detect simple while-pattern and rewire header @@ -393,6 +423,8 @@ impl LLVMCompiler { after_sel, &bb_map, &vmap, + &mut loopform_registry, + &mut loopform_body_to_header, )?; loopform_loop_id = loopform_loop_id.wrapping_add(1); }