feat(joinir): Phase 228 - CarrierInit for ConditionOnly header PHI initialization

- Add CarrierInit enum (FromHost/BoolConst) for explicit initialization policy
- LoopHeaderPhiBuilder generates Const instruction for BoolConst carriers
- merge/mod.rs uses carrier_info to include ALL carriers in header PHI
- Pattern 2/4 pass carrier_info to boundary builder
- ConditionOnly carriers (is_digit_pos) now included in header PHI

Remaining: latch incoming for ConditionOnly carriers (Phase 228-8)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-10 21:10:28 +09:00
parent 478cc0012e
commit 192620f842
17 changed files with 174 additions and 15 deletions

View File

@ -51,13 +51,19 @@ impl LoopHeaderPhiBuilder {
///
/// LoopHeaderPhiInfo with allocated PHI dsts.
/// Note: latch_incoming is not yet set - that happens in instruction_rewriter.
///
/// # Phase 228 Update
///
/// Added CarrierInit and CarrierRole to carrier tuples:
/// * `CarrierInit::FromHost` - Use host_id directly as PHI init value
/// * `CarrierInit::BoolConst(val)` - Generate explicit bool constant for ConditionOnly carriers
pub fn build(
builder: &mut crate::mir::builder::MirBuilder,
header_block: BasicBlockId,
entry_block: BasicBlockId,
loop_var_name: &str,
loop_var_init: ValueId,
carriers: &[(String, ValueId)], // (name, init_value) pairs
carriers: &[(String, ValueId, crate::mir::join_ir::lowering::carrier_info::CarrierInit, crate::mir::join_ir::lowering::carrier_info::CarrierRole)], // Phase 228: Added CarrierInit and CarrierRole
expr_result_is_loop_var: bool,
debug: bool,
) -> Result<LoopHeaderPhiInfo, String> {
@ -96,15 +102,35 @@ impl LoopHeaderPhiBuilder {
}
// Allocate PHIs for other carriers
for (name, init_value) in carriers {
for (name, host_id, init, role) in carriers {
// Phase 228-5: Generate explicit const for BoolConst, use host_id for FromHost
let init_value = match init {
crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost => *host_id,
crate::mir::join_ir::lowering::carrier_info::CarrierInit::BoolConst(val) => {
// Phase 228: Generate explicit bool constant for ConditionOnly carriers
let const_id = builder.next_value_id();
builder.emit_instruction(MirInstruction::Const {
dst: const_id,
value: crate::mir::types::ConstValue::Bool(*val),
});
if debug {
eprintln!(
"[cf_loop/joinir] Phase 228: Generated const {:?} = Bool({}) for ConditionOnly carrier '{}'",
const_id, val, name
);
}
const_id
}
};
let phi_dst = builder.next_value_id();
info.carrier_phis.insert(
name.clone(),
CarrierPhiEntry {
phi_dst,
entry_incoming: (entry_block, *init_value),
entry_incoming: (entry_block, init_value),
latch_incoming: None,
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, // Phase 227: Default to LoopState (will be updated later if needed)
role: *role, // Phase 228: Use role from carrier_info
},
);
// Phase 177-STRUCT-2: Record insertion order
@ -112,8 +138,8 @@ impl LoopHeaderPhiBuilder {
if debug {
eprintln!(
"[cf_loop/joinir] Carrier '{}' PHI: {:?} = phi [(from {:?}, {:?}), (latch TBD)]",
name, phi_dst, entry_block, init_value
"[cf_loop/joinir] Carrier '{}' PHI: {:?} = phi [(from {:?}, {:?}), (latch TBD)], role={:?}",
name, phi_dst, entry_block, init_value, role
);
}
}

View File

@ -141,12 +141,23 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
"Phase 201-A: No host_inputs in boundary for loop_var_init"
)?;
// Extract other carriers from exit_bindings
let other_carriers: Vec<(String, ValueId)> = boundary.exit_bindings
.iter()
.filter(|b| b.carrier_name != *loop_var_name)
.map(|b| (b.carrier_name.clone(), b.host_slot))
.collect();
// Phase 228-4: Extract carriers with their initialization strategy
let other_carriers: Vec<(String, ValueId, crate::mir::join_ir::lowering::carrier_info::CarrierInit, crate::mir::join_ir::lowering::carrier_info::CarrierRole)> =
if let Some(ref carrier_info) = boundary.carrier_info {
// Use carrier_info if available (Phase 228)
carrier_info.carriers
.iter()
.filter(|c| c.name != *loop_var_name)
.map(|c| (c.name.clone(), c.host_id, c.init, c.role))
.collect()
} else {
// Fallback: exit_bindings から取得(既存動作)
boundary.exit_bindings
.iter()
.filter(|b| b.carrier_name != *loop_var_name)
.map(|b| (b.carrier_name.clone(), b.host_slot, crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState))
.collect()
};
if debug {
eprintln!(
@ -156,7 +167,7 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
eprintln!(
"[cf_loop/joinir] loop_var_init={:?}, carriers={:?}",
loop_var_init,
other_carriers.iter().map(|(n, _)| n.as_str()).collect::<Vec<_>>()
other_carriers.iter().map(|(n, _, _, _)| n.as_str()).collect::<Vec<_>>()
);
}

View File

@ -163,6 +163,7 @@ impl CommonPatternInitializer {
host_id: ValueId(0), // Dummy
join_id: None,
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, // Phase 227: Default
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228: Default
})
} else {
None

View File

@ -143,6 +143,7 @@ mod tests {
host_id: ValueId(10),
join_id: None,
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
}],
);
@ -183,12 +184,14 @@ mod tests {
host_id: ValueId(11),
join_id: None,
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
},
CarrierVar {
name: "sum".to_string(),
host_id: ValueId(10),
join_id: None,
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
},
],
);
@ -232,6 +235,7 @@ mod tests {
host_id: ValueId(10),
join_id: None,
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
}],
);
@ -261,6 +265,7 @@ mod tests {
host_id: ValueId(10),
join_id: None,
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
}],
);
@ -290,6 +295,7 @@ mod tests {
host_id: ValueId(10),
join_id: None,
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
}],
);
@ -322,6 +328,7 @@ mod tests {
host_id: ValueId(10),
join_id: None,
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
}],
);
@ -352,6 +359,7 @@ mod tests {
condition_bindings: vec![], // Phase 171-fix: Add missing field
expr_result: None, // Phase 33-14: Add missing field
loop_var_name: None, // Phase 33-16: Add missing field
carrier_info: None, // Phase 228: Add missing field
};
builder.apply_to_boundary(&mut boundary)

View File

@ -108,6 +108,7 @@ mod tests {
host_id: ValueId(10),
join_id: None,
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
}],
);
@ -134,6 +135,7 @@ mod tests {
condition_bindings: vec![], // Phase 171-fix: Add missing field
expr_result: None, // Phase 33-14: Add missing field
loop_var_name: None, // Phase 33-16: Add missing field
carrier_info: None, // Phase 228: Add missing field
};
apply_exit_bindings_to_boundary(&carrier_info, &exit_meta, &variable_map, &mut boundary)

View File

@ -92,6 +92,7 @@ mod tests {
host_id: ValueId(10),
join_id: None,
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
}],
);
@ -130,12 +131,14 @@ mod tests {
host_id: ValueId(11),
join_id: None,
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
},
CarrierVar {
name: "sum".to_string(),
host_id: ValueId(10),
join_id: None,
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
},
],
);

View File

@ -70,6 +70,7 @@ mod tests {
host_id: ValueId(10),
join_id: None,
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
}],
);
@ -90,12 +91,14 @@ mod tests {
host_id: ValueId(11),
join_id: None,
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
},
CarrierVar {
name: "sum".to_string(),
host_id: ValueId(10),
join_id: None,
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
},
],
);
@ -119,6 +122,7 @@ mod tests {
host_id: ValueId(10),
join_id: None,
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
}],
);
@ -140,6 +144,7 @@ mod tests {
host_id: ValueId(10),
join_id: None,
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
}],
);
@ -161,6 +166,7 @@ mod tests {
host_id: ValueId(10),
join_id: None,
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
}],
);

View File

@ -578,6 +578,7 @@ impl MirBuilder {
.with_exit_bindings(exit_bindings.clone())
.with_expr_result(fragment_meta.expr_result) // Phase 33-14: Pass expr_result to merger
.with_loop_var_name(Some(loop_var_name.clone())) // Phase 33-16: For LoopHeaderPhiBuilder
.with_carrier_info(carrier_info.clone()) // Phase 228-6: Pass carrier_info for ConditionOnly header PHI init
.build();
// Phase 33-22: Use JoinIRConversionPipeline for unified conversion flow

View File

@ -277,18 +277,21 @@ mod tests {
host_id: ValueId(1),
join_id: None,
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
},
CarrierVar {
name: "sum".to_string(),
host_id: ValueId(2),
join_id: None,
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
},
CarrierVar {
name: "M".to_string(),
host_id: ValueId(3),
join_id: None,
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
},
],
trim_helper: None,

View File

@ -378,6 +378,7 @@ impl MirBuilder {
.with_inputs(join_inputs, host_inputs) // Dynamic carrier count
.with_exit_bindings(exit_bindings)
.with_loop_var_name(Some(loop_var_name.clone())) // Phase 33-19: Enable exit PHI collection
.with_carrier_info(carrier_info.clone()) // Phase 228-6: Pass carrier_info for ConditionOnly header PHI init
.build();
// Phase 33-22: Use JoinIRConversionPipeline for unified conversion flow

View File

@ -397,12 +397,14 @@ mod tests {
host_id: ValueId(10),
join_id: None,
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
},
CarrierVar {
name: "count".to_string(),
host_id: ValueId(11),
join_id: None,
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
},
],
trim_helper: None,