🔧 Add sealed SSA mode for PHI debugging (ChatGPT5)
Added NYASH_LLVM_PHI_SEALED env var to toggle PHI wiring modes: - NYASH_LLVM_PHI_SEALED=0 (default): immediate PHI wiring - NYASH_LLVM_PHI_SEALED=1: sealed SSA style (wire after block completion) - Added seal_block() function for deferred PHI incoming setup - Enhanced PHI tracing with NYASH_LLVM_TRACE_PHI=1 This helps debug 'phi incoming value missing' errors by comparing immediate vs sealed wiring approaches.
This commit is contained in:
@ -54,6 +54,8 @@ pub(in super::super) fn emit_jump<'ctx>(
|
|||||||
>,
|
>,
|
||||||
vmap: &HashMap<ValueId, BasicValueEnum<'ctx>>,
|
vmap: &HashMap<ValueId, BasicValueEnum<'ctx>>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
|
let sealed = std::env::var("NYASH_LLVM_PHI_SEALED").ok().as_deref() == Some("1");
|
||||||
|
if !sealed {
|
||||||
if let Some(list) = phis_by_block.get(target) {
|
if let Some(list) = phis_by_block.get(target) {
|
||||||
for (_dst, phi, inputs) in list {
|
for (_dst, phi, inputs) in list {
|
||||||
if let Some((_, in_vid)) = inputs.iter().find(|(pred, _)| pred == &bid) {
|
if let Some((_, in_vid)) = inputs.iter().find(|(pred, _)| pred == &bid) {
|
||||||
@ -83,6 +85,7 @@ pub(in super::super) fn emit_jump<'ctx>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
let tbb = *bb_map.get(target).ok_or("target bb missing")?;
|
let tbb = *bb_map.get(target).ok_or("target bb missing")?;
|
||||||
codegen
|
codegen
|
||||||
.builder
|
.builder
|
||||||
@ -106,7 +109,9 @@ pub(in super::super) fn emit_branch<'ctx>(
|
|||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let cond_v = *vmap.get(condition).ok_or("cond missing")?;
|
let cond_v = *vmap.get(condition).ok_or("cond missing")?;
|
||||||
let b = to_bool(codegen.context, cond_v, &codegen.builder)?;
|
let b = to_bool(codegen.context, cond_v, &codegen.builder)?;
|
||||||
|
let sealed = std::env::var("NYASH_LLVM_PHI_SEALED").ok().as_deref() == Some("1");
|
||||||
// then
|
// then
|
||||||
|
if !sealed {
|
||||||
if let Some(list) = phis_by_block.get(then_bb) {
|
if let Some(list) = phis_by_block.get(then_bb) {
|
||||||
for (_dst, phi, inputs) in list {
|
for (_dst, phi, inputs) in list {
|
||||||
if let Some((_, in_vid)) = inputs.iter().find(|(pred, _)| pred == &bid) {
|
if let Some((_, in_vid)) = inputs.iter().find(|(pred, _)| pred == &bid) {
|
||||||
@ -137,7 +142,9 @@ pub(in super::super) fn emit_branch<'ctx>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// else
|
// else
|
||||||
|
if !sealed {
|
||||||
if let Some(list) = phis_by_block.get(else_bb) {
|
if let Some(list) = phis_by_block.get(else_bb) {
|
||||||
for (_dst, phi, inputs) in list {
|
for (_dst, phi, inputs) in list {
|
||||||
if let Some((_, in_vid)) = inputs.iter().find(|(pred, _)| pred == &bid) {
|
if let Some((_, in_vid)) = inputs.iter().find(|(pred, _)| pred == &bid) {
|
||||||
@ -168,6 +175,7 @@ pub(in super::super) fn emit_branch<'ctx>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
let tbb = *bb_map.get(then_bb).ok_or("then bb missing")?;
|
let tbb = *bb_map.get(then_bb).ok_or("then bb missing")?;
|
||||||
let ebb = *bb_map.get(else_bb).ok_or("else bb missing")?;
|
let ebb = *bb_map.get(else_bb).ok_or("else bb missing")?;
|
||||||
codegen
|
codegen
|
||||||
@ -233,3 +241,50 @@ fn coerce_to_type<'ctx>(
|
|||||||
(_, v) => Ok(v),
|
(_, v) => Ok(v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sealed-SSA style: when a block is finalized, add PHI incoming for all successor blocks.
|
||||||
|
pub(in super::super) fn seal_block<'ctx>(
|
||||||
|
codegen: &CodegenContext<'ctx>,
|
||||||
|
bid: BasicBlockId,
|
||||||
|
succs: &HashMap<BasicBlockId, Vec<BasicBlockId>>,
|
||||||
|
bb_map: &HashMap<BasicBlockId, BasicBlock<'ctx>>,
|
||||||
|
phis_by_block: &HashMap<
|
||||||
|
BasicBlockId,
|
||||||
|
Vec<(ValueId, PhiValue<'ctx>, Vec<(BasicBlockId, ValueId)>)>,
|
||||||
|
>,
|
||||||
|
vmap: &HashMap<ValueId, BasicValueEnum<'ctx>>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
if let Some(slist) = succs.get(&bid) {
|
||||||
|
for sb in slist {
|
||||||
|
if let Some(pl) = phis_by_block.get(sb) {
|
||||||
|
for (_dst, phi, inputs) in pl {
|
||||||
|
if let Some((_, in_vid)) = inputs.iter().find(|(pred, _)| pred == &bid) {
|
||||||
|
let mut val = *vmap.get(in_vid).ok_or("phi incoming (seal) value missing")?;
|
||||||
|
let pred_bb = *bb_map.get(&bid).ok_or("pred bb missing")?;
|
||||||
|
val = coerce_to_type(codegen, phi, val)?;
|
||||||
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
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()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
mod blocks;
|
mod blocks;
|
||||||
mod flow;
|
pub mod flow;
|
||||||
mod externcall;
|
mod externcall;
|
||||||
mod newbox;
|
mod newbox;
|
||||||
mod boxcall;
|
mod boxcall;
|
||||||
|
|||||||
@ -122,6 +122,12 @@ impl LLVMCompiler {
|
|||||||
crate::mir::BasicBlockId,
|
crate::mir::BasicBlockId,
|
||||||
Vec<(ValueId, PhiValue, Vec<(crate::mir::BasicBlockId, ValueId)>)>,
|
Vec<(ValueId, PhiValue, Vec<(crate::mir::BasicBlockId, ValueId)>)>,
|
||||||
> = HashMap::new();
|
> = HashMap::new();
|
||||||
|
// Build successors map (for optional sealed-SSA PHI wiring)
|
||||||
|
let mut succs: HashMap<crate::mir::BasicBlockId, Vec<crate::mir::BasicBlockId>> = HashMap::new();
|
||||||
|
for (bid, block) in &func.blocks {
|
||||||
|
let v: Vec<crate::mir::BasicBlockId> = block.successors.iter().copied().collect();
|
||||||
|
succs.insert(*bid, v);
|
||||||
|
}
|
||||||
// Bind parameters
|
// Bind parameters
|
||||||
for (i, pid) in func.params.iter().enumerate() {
|
for (i, pid) in func.params.iter().enumerate() {
|
||||||
if let Some(av) = llvm_func.get_nth_param(i as u32) {
|
if let Some(av) = llvm_func.get_nth_param(i as u32) {
|
||||||
@ -160,6 +166,25 @@ impl LLVMCompiler {
|
|||||||
.entry(*bid)
|
.entry(*bid)
|
||||||
.or_default()
|
.or_default()
|
||||||
.push((*dst, phi, inputs.clone()));
|
.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<String> = 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(",")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,6 +193,7 @@ impl LLVMCompiler {
|
|||||||
let const_strs = build_const_str_map(func);
|
let const_strs = build_const_str_map(func);
|
||||||
|
|
||||||
// Lower body
|
// Lower body
|
||||||
|
let sealed_mode = std::env::var("NYASH_LLVM_PHI_SEALED").ok().as_deref() == Some("1");
|
||||||
for (bi, bid) in block_ids.iter().enumerate() {
|
for (bi, bid) in block_ids.iter().enumerate() {
|
||||||
let bb = *bb_map.get(bid).unwrap();
|
let bb = *bb_map.get(bid).unwrap();
|
||||||
if codegen
|
if codegen
|
||||||
@ -319,6 +345,9 @@ impl LLVMCompiler {
|
|||||||
instructions::emit_jump(&codegen, *bid, &entry_first, &bb_map, &phis_by_block, &vmap)?;
|
instructions::emit_jump(&codegen, *bid, &entry_first, &bb_map, &phis_by_block, &vmap)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if sealed_mode {
|
||||||
|
instructions::flow::seal_block(&codegen, *bid, &succs, &bb_map, &phis_by_block, &vmap)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Verify the fully-lowered function once, after all blocks
|
// Verify the fully-lowered function once, after all blocks
|
||||||
if !llvm_func.verify(true) {
|
if !llvm_func.verify(true) {
|
||||||
|
|||||||
Reference in New Issue
Block a user