feat(joinir): Phase 227 - CarrierRole separation (LoopState vs ConditionOnly)
- Add CarrierRole enum to distinguish state carriers from condition-only carriers - ConditionOnly carriers (is_digit_pos) skip exit PHI but keep header PHI - Update all test struct literals with role field - 877/884 tests PASS (7 pre-existing failures unrelated) Remaining: ValueId(0) undefined - header PHI initialization for ConditionOnly 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -89,10 +89,12 @@ impl ExitMetaCollector {
|
|||||||
|
|
||||||
// Look up host slot from variable_map
|
// Look up host slot from variable_map
|
||||||
if let Some(&host_slot) = builder.variable_map.get(carrier_name) {
|
if let Some(&host_slot) = builder.variable_map.get(carrier_name) {
|
||||||
|
use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
|
||||||
let binding = LoopExitBinding {
|
let binding = LoopExitBinding {
|
||||||
carrier_name: carrier_name.clone(),
|
carrier_name: carrier_name.clone(),
|
||||||
join_exit_value: *join_exit_value,
|
join_exit_value: *join_exit_value,
|
||||||
host_slot,
|
host_slot,
|
||||||
|
role: CarrierRole::LoopState, // Phase 227: Default to LoopState (caller should update if needed)
|
||||||
};
|
};
|
||||||
|
|
||||||
eprintln!(
|
eprintln!(
|
||||||
|
|||||||
@ -134,6 +134,7 @@ mod tests {
|
|||||||
carrier_name: "sum".to_string(),
|
carrier_name: "sum".to_string(),
|
||||||
join_exit_value: ValueId(18),
|
join_exit_value: ValueId(18),
|
||||||
host_slot: ValueId(5),
|
host_slot: ValueId(5),
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
};
|
};
|
||||||
|
|
||||||
let remapper = JoinIrIdRemapper::new();
|
let remapper = JoinIrIdRemapper::new();
|
||||||
|
|||||||
@ -607,8 +607,19 @@ pub(super) fn merge_and_rewrite(
|
|||||||
//
|
//
|
||||||
// For each carrier, use the header PHI dst instead of
|
// For each carrier, use the header PHI dst instead of
|
||||||
// the undefined exit binding value.
|
// the undefined exit binding value.
|
||||||
|
//
|
||||||
|
// Phase 227: Filter out ConditionOnly carriers from exit PHI
|
||||||
if let Some(b) = boundary {
|
if let Some(b) = boundary {
|
||||||
for binding in &b.exit_bindings {
|
for binding in &b.exit_bindings {
|
||||||
|
// Phase 227: Skip ConditionOnly carriers (they don't need exit PHI)
|
||||||
|
if binding.role == crate::mir::join_ir::lowering::carrier_info::CarrierRole::ConditionOnly {
|
||||||
|
eprintln!(
|
||||||
|
"[DEBUG-177] Phase 227: Skipping ConditionOnly carrier '{}' from exit PHI",
|
||||||
|
binding.carrier_name
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(phi_dst) = loop_header_phi_info.get_carrier_phi(&binding.carrier_name) {
|
if let Some(phi_dst) = loop_header_phi_info.get_carrier_phi(&binding.carrier_name) {
|
||||||
carrier_inputs.entry(binding.carrier_name.clone())
|
carrier_inputs.entry(binding.carrier_name.clone())
|
||||||
.or_insert_with(Vec::new)
|
.or_insert_with(Vec::new)
|
||||||
|
|||||||
@ -82,6 +82,7 @@ impl LoopHeaderPhiBuilder {
|
|||||||
phi_dst: loop_var_phi_dst,
|
phi_dst: loop_var_phi_dst,
|
||||||
entry_incoming: (entry_block, loop_var_init),
|
entry_incoming: (entry_block, loop_var_init),
|
||||||
latch_incoming: None,
|
latch_incoming: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, // Phase 227: Loop var is always LoopState
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
// Phase 177-STRUCT-2: Record insertion order
|
// Phase 177-STRUCT-2: Record insertion order
|
||||||
@ -103,6 +104,7 @@ impl LoopHeaderPhiBuilder {
|
|||||||
phi_dst,
|
phi_dst,
|
||||||
entry_incoming: (entry_block, *init_value),
|
entry_incoming: (entry_block, *init_value),
|
||||||
latch_incoming: None,
|
latch_incoming: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, // Phase 227: Default to LoopState (will be updated later if needed)
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
// Phase 177-STRUCT-2: Record insertion order
|
// Phase 177-STRUCT-2: Record insertion order
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
//! - ExitLineReconnector: to update variable_map with final values
|
//! - ExitLineReconnector: to update variable_map with final values
|
||||||
|
|
||||||
use crate::mir::{BasicBlockId, ValueId};
|
use crate::mir::{BasicBlockId, ValueId};
|
||||||
|
use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
/// Information about loop header PHIs
|
/// Information about loop header PHIs
|
||||||
@ -55,6 +56,9 @@ pub struct CarrierPhiEntry {
|
|||||||
|
|
||||||
/// Latch edge: (from_block, updated_value) - set after instruction rewrite
|
/// Latch edge: (from_block, updated_value) - set after instruction rewrite
|
||||||
pub latch_incoming: Option<(BasicBlockId, ValueId)>,
|
pub latch_incoming: Option<(BasicBlockId, ValueId)>,
|
||||||
|
|
||||||
|
/// Phase 227: Role of this carrier (LoopState or ConditionOnly)
|
||||||
|
pub role: CarrierRole,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LoopHeaderPhiInfo {
|
impl LoopHeaderPhiInfo {
|
||||||
@ -152,6 +156,7 @@ mod tests {
|
|||||||
phi_dst: ValueId(100),
|
phi_dst: ValueId(100),
|
||||||
entry_incoming: (BasicBlockId(1), ValueId(5)),
|
entry_incoming: (BasicBlockId(1), ValueId(5)),
|
||||||
latch_incoming: None,
|
latch_incoming: None,
|
||||||
|
role: CarrierRole::LoopState,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -162,6 +162,7 @@ impl CommonPatternInitializer {
|
|||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
host_id: ValueId(0), // Dummy
|
host_id: ValueId(0), // Dummy
|
||||||
join_id: None,
|
join_id: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState, // Phase 227: Default
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|||||||
@ -142,6 +142,7 @@ mod tests {
|
|||||||
name: "sum".to_string(),
|
name: "sum".to_string(),
|
||||||
host_id: ValueId(10),
|
host_id: ValueId(10),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -181,11 +182,13 @@ mod tests {
|
|||||||
name: "printed".to_string(),
|
name: "printed".to_string(),
|
||||||
host_id: ValueId(11),
|
host_id: ValueId(11),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
},
|
},
|
||||||
CarrierVar {
|
CarrierVar {
|
||||||
name: "sum".to_string(),
|
name: "sum".to_string(),
|
||||||
host_id: ValueId(10),
|
host_id: ValueId(10),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -228,6 +231,7 @@ mod tests {
|
|||||||
name: "sum".to_string(),
|
name: "sum".to_string(),
|
||||||
host_id: ValueId(10),
|
host_id: ValueId(10),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -256,6 +260,7 @@ mod tests {
|
|||||||
name: "sum".to_string(),
|
name: "sum".to_string(),
|
||||||
host_id: ValueId(10),
|
host_id: ValueId(10),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -284,6 +289,7 @@ mod tests {
|
|||||||
name: "sum".to_string(),
|
name: "sum".to_string(),
|
||||||
host_id: ValueId(10),
|
host_id: ValueId(10),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -315,6 +321,7 @@ mod tests {
|
|||||||
name: "sum".to_string(),
|
name: "sum".to_string(),
|
||||||
host_id: ValueId(10),
|
host_id: ValueId(10),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -52,6 +52,7 @@ pub fn apply_exit_bindings_to_boundary(
|
|||||||
carrier_name: carrier.name.clone(),
|
carrier_name: carrier.name.clone(),
|
||||||
host_slot: post_loop_id,
|
host_slot: post_loop_id,
|
||||||
join_exit_value: join_exit_id,
|
join_exit_value: join_exit_id,
|
||||||
|
role: carrier.role, // Phase 227: Propagate role from CarrierInfo
|
||||||
});
|
});
|
||||||
|
|
||||||
join_outputs.push(join_exit_id);
|
join_outputs.push(join_exit_id);
|
||||||
@ -83,10 +84,12 @@ pub fn apply_exit_bindings_to_boundary(
|
|||||||
///
|
///
|
||||||
/// LoopExitBinding for the loop variable
|
/// LoopExitBinding for the loop variable
|
||||||
pub fn create_loop_var_exit_binding(carrier_info: &CarrierInfo) -> LoopExitBinding {
|
pub fn create_loop_var_exit_binding(carrier_info: &CarrierInfo) -> LoopExitBinding {
|
||||||
|
use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
|
||||||
LoopExitBinding {
|
LoopExitBinding {
|
||||||
carrier_name: carrier_info.loop_var_name.clone(),
|
carrier_name: carrier_info.loop_var_name.clone(),
|
||||||
join_exit_value: carrier_info.loop_var_id, // Loop var maps to itself
|
join_exit_value: carrier_info.loop_var_id, // Loop var maps to itself
|
||||||
host_slot: carrier_info.loop_var_id,
|
host_slot: carrier_info.loop_var_id,
|
||||||
|
role: CarrierRole::LoopState, // Phase 227: Loop var is always LoopState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,6 +107,7 @@ mod tests {
|
|||||||
name: "sum".to_string(),
|
name: "sum".to_string(),
|
||||||
host_id: ValueId(10),
|
host_id: ValueId(10),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -40,6 +40,7 @@ pub fn build_loop_exit_bindings(
|
|||||||
carrier_name: carrier.name.clone(),
|
carrier_name: carrier.name.clone(),
|
||||||
join_exit_value: join_exit_id,
|
join_exit_value: join_exit_id,
|
||||||
host_slot: carrier.host_id,
|
host_slot: carrier.host_id,
|
||||||
|
role: carrier.role, // Phase 227: Propagate role from CarrierInfo
|
||||||
});
|
});
|
||||||
|
|
||||||
// Allocate new ValueId for post-loop carrier value
|
// Allocate new ValueId for post-loop carrier value
|
||||||
@ -90,6 +91,7 @@ mod tests {
|
|||||||
name: "sum".to_string(),
|
name: "sum".to_string(),
|
||||||
host_id: ValueId(10),
|
host_id: ValueId(10),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -127,11 +129,13 @@ mod tests {
|
|||||||
name: "printed".to_string(),
|
name: "printed".to_string(),
|
||||||
host_id: ValueId(11),
|
host_id: ValueId(11),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
},
|
},
|
||||||
CarrierVar {
|
CarrierVar {
|
||||||
name: "sum".to_string(),
|
name: "sum".to_string(),
|
||||||
host_id: ValueId(10),
|
host_id: ValueId(10),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@ -69,6 +69,7 @@ mod tests {
|
|||||||
name: "sum".to_string(),
|
name: "sum".to_string(),
|
||||||
host_id: ValueId(10),
|
host_id: ValueId(10),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -88,11 +89,13 @@ mod tests {
|
|||||||
name: "printed".to_string(),
|
name: "printed".to_string(),
|
||||||
host_id: ValueId(11),
|
host_id: ValueId(11),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
},
|
},
|
||||||
CarrierVar {
|
CarrierVar {
|
||||||
name: "sum".to_string(),
|
name: "sum".to_string(),
|
||||||
host_id: ValueId(10),
|
host_id: ValueId(10),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -115,6 +118,7 @@ mod tests {
|
|||||||
name: "sum".to_string(),
|
name: "sum".to_string(),
|
||||||
host_id: ValueId(10),
|
host_id: ValueId(10),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -135,6 +139,7 @@ mod tests {
|
|||||||
name: "sum".to_string(),
|
name: "sum".to_string(),
|
||||||
host_id: ValueId(10),
|
host_id: ValueId(10),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -155,6 +160,7 @@ mod tests {
|
|||||||
name: "sum".to_string(),
|
name: "sum".to_string(),
|
||||||
host_id: ValueId(10),
|
host_id: ValueId(10),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
}],
|
}],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -475,9 +475,13 @@ impl MirBuilder {
|
|||||||
// Issue: CommonPatternInitializer includes all variables in variable_map as carriers,
|
// Issue: CommonPatternInitializer includes all variables in variable_map as carriers,
|
||||||
// but only variables with updates in the loop body are true carriers.
|
// but only variables with updates in the loop body are true carriers.
|
||||||
// Condition-only variables (like 'len', 's') should be excluded.
|
// Condition-only variables (like 'len', 's') should be excluded.
|
||||||
|
//
|
||||||
|
// Phase 227: Keep ConditionOnly carriers even if they don't have updates
|
||||||
|
// (they're used in loop/break conditions, not updated)
|
||||||
let original_carrier_count = carrier_info.carriers.len();
|
let original_carrier_count = carrier_info.carriers.len();
|
||||||
carrier_info.carriers.retain(|carrier| {
|
carrier_info.carriers.retain(|carrier| {
|
||||||
carrier_updates.contains_key(&carrier.name)
|
use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
|
||||||
|
carrier_updates.contains_key(&carrier.name) || carrier.role == CarrierRole::ConditionOnly
|
||||||
});
|
});
|
||||||
|
|
||||||
eprintln!(
|
eprintln!(
|
||||||
|
|||||||
@ -276,16 +276,19 @@ mod tests {
|
|||||||
name: "i".to_string(),
|
name: "i".to_string(),
|
||||||
host_id: ValueId(1),
|
host_id: ValueId(1),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
},
|
},
|
||||||
CarrierVar {
|
CarrierVar {
|
||||||
name: "sum".to_string(),
|
name: "sum".to_string(),
|
||||||
host_id: ValueId(2),
|
host_id: ValueId(2),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
},
|
},
|
||||||
CarrierVar {
|
CarrierVar {
|
||||||
name: "M".to_string(),
|
name: "M".to_string(),
|
||||||
host_id: ValueId(3),
|
host_id: ValueId(3),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
trim_helper: None,
|
trim_helper: None,
|
||||||
|
|||||||
@ -396,11 +396,13 @@ mod tests {
|
|||||||
name: "sum".to_string(),
|
name: "sum".to_string(),
|
||||||
host_id: ValueId(10),
|
host_id: ValueId(10),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
},
|
},
|
||||||
CarrierVar {
|
CarrierVar {
|
||||||
name: "count".to_string(),
|
name: "count".to_string(),
|
||||||
host_id: ValueId(11),
|
host_id: ValueId(11),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
trim_helper: None,
|
trim_helper: None,
|
||||||
|
|||||||
@ -18,6 +18,42 @@
|
|||||||
use crate::mir::ValueId;
|
use crate::mir::ValueId;
|
||||||
use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for determinism
|
use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for determinism
|
||||||
|
|
||||||
|
/// Phase 227: CarrierRole - Distinguishes loop state carriers from condition-only carriers
|
||||||
|
///
|
||||||
|
/// When LoopBodyLocal variables are promoted to carriers, we need to know whether
|
||||||
|
/// they carry loop state (need exit PHI) or are only used in conditions (no exit PHI).
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// // LoopState carrier: sum needs exit PHI (value persists after loop)
|
||||||
|
/// loop(i < n) {
|
||||||
|
/// sum = sum + i; // sum updated in loop body
|
||||||
|
/// }
|
||||||
|
/// print(sum); // sum used after loop
|
||||||
|
///
|
||||||
|
/// // ConditionOnly carrier: is_digit_pos does NOT need exit PHI
|
||||||
|
/// loop(p < s.length()) {
|
||||||
|
/// local digit_pos = digits.indexOf(s.substring(p, p+1));
|
||||||
|
/// if digit_pos < 0 { break; } // Only used in condition
|
||||||
|
/// num_str = num_str + ch;
|
||||||
|
/// p = p + 1;
|
||||||
|
/// }
|
||||||
|
/// // digit_pos not used after loop
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum CarrierRole {
|
||||||
|
/// Value needed after loop (sum, result, count, p, num_str)
|
||||||
|
/// - Participates in header PHI (loop iteration)
|
||||||
|
/// - Participates in exit PHI (final value after loop)
|
||||||
|
LoopState,
|
||||||
|
|
||||||
|
/// Only used for loop condition (is_digit_pos, is_whitespace)
|
||||||
|
/// - Participates in header PHI (loop iteration)
|
||||||
|
/// - Does NOT participate in exit PHI (not needed after loop)
|
||||||
|
ConditionOnly,
|
||||||
|
}
|
||||||
|
|
||||||
/// Phase 224-D: Alias for promoted LoopBodyLocal in condition expressions
|
/// Phase 224-D: Alias for promoted LoopBodyLocal in condition expressions
|
||||||
///
|
///
|
||||||
/// When a LoopBodyLocal variable is promoted to a carrier, the original variable
|
/// When a LoopBodyLocal variable is promoted to a carrier, the original variable
|
||||||
@ -44,7 +80,7 @@ pub struct ConditionAlias {
|
|||||||
/// Information about a single carrier variable
|
/// Information about a single carrier variable
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct CarrierVar {
|
pub struct CarrierVar {
|
||||||
/// Variable name (e.g., "sum", "printed")
|
/// Variable name (e.g., "sum", "printed", "is_digit_pos")
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// Host ValueId for this variable (MIR側)
|
/// Host ValueId for this variable (MIR側)
|
||||||
pub host_id: ValueId,
|
pub host_id: ValueId,
|
||||||
@ -56,6 +92,36 @@ pub struct CarrierVar {
|
|||||||
/// - `Some(vid)`: Header PHI生成後にセットされる
|
/// - `Some(vid)`: Header PHI生成後にセットされる
|
||||||
/// - `None`: まだPHI生成前、または該当なし
|
/// - `None`: まだPHI生成前、または該当なし
|
||||||
pub join_id: Option<ValueId>,
|
pub join_id: Option<ValueId>,
|
||||||
|
/// Phase 227: Role of this carrier (LoopState or ConditionOnly)
|
||||||
|
///
|
||||||
|
/// - `LoopState`: Value needed after loop (participates in exit PHI)
|
||||||
|
/// - `ConditionOnly`: Only used for loop condition (no exit PHI)
|
||||||
|
pub role: CarrierRole,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CarrierVar {
|
||||||
|
/// Create a new CarrierVar with default LoopState role
|
||||||
|
///
|
||||||
|
/// This is the primary constructor for CarrierVar. Use this instead of
|
||||||
|
/// struct literal syntax to ensure role defaults to LoopState.
|
||||||
|
pub fn new(name: String, host_id: ValueId) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
host_id,
|
||||||
|
join_id: None,
|
||||||
|
role: CarrierRole::LoopState,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a CarrierVar with explicit role
|
||||||
|
pub fn with_role(name: String, host_id: ValueId, role: CarrierRole) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
host_id,
|
||||||
|
join_id: None,
|
||||||
|
role,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Complete carrier information for a loop
|
/// Complete carrier information for a loop
|
||||||
@ -130,6 +196,7 @@ impl CarrierInfo {
|
|||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
host_id: id,
|
host_id: id,
|
||||||
join_id: None, // Phase 177-STRUCT-1: Set by header PHI generation
|
join_id: None, // Phase 177-STRUCT-1: Set by header PHI generation
|
||||||
|
role: CarrierRole::LoopState, // Phase 227: Default to LoopState
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@ -189,6 +256,7 @@ impl CarrierInfo {
|
|||||||
name,
|
name,
|
||||||
host_id,
|
host_id,
|
||||||
join_id: None, // Phase 177-STRUCT-1: Set by header PHI generation
|
join_id: None, // Phase 177-STRUCT-1: Set by header PHI generation
|
||||||
|
role: CarrierRole::LoopState, // Phase 227: Default to LoopState
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -507,6 +575,7 @@ mod tests {
|
|||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
host_id: ValueId(id),
|
host_id: ValueId(id),
|
||||||
join_id: None, // Phase 177-STRUCT-1
|
join_id: None, // Phase 177-STRUCT-1
|
||||||
|
role: CarrierRole::LoopState, // Phase 227: Default to LoopState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -421,6 +421,7 @@ mod tests {
|
|||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
host_id: ValueId(host_id),
|
host_id: ValueId(host_id),
|
||||||
join_id: None, // Phase 177-STRUCT-1
|
join_id: None, // Phase 177-STRUCT-1
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -43,6 +43,7 @@
|
|||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use crate::mir::ValueId;
|
use crate::mir::ValueId;
|
||||||
|
use super::carrier_info::CarrierRole;
|
||||||
|
|
||||||
/// Explicit binding between JoinIR exit value and host variable
|
/// Explicit binding between JoinIR exit value and host variable
|
||||||
///
|
///
|
||||||
@ -68,13 +69,13 @@ use crate::mir::ValueId;
|
|||||||
///
|
///
|
||||||
/// ```text
|
/// ```text
|
||||||
/// vec![
|
/// vec![
|
||||||
/// LoopExitBinding { carrier_name: "sum", join_exit_value: ValueId(18), host_slot: ValueId(5) },
|
/// LoopExitBinding { carrier_name: "sum", join_exit_value: ValueId(18), host_slot: ValueId(5), role: LoopState },
|
||||||
/// LoopExitBinding { carrier_name: "count", join_exit_value: ValueId(19), host_slot: ValueId(6) },
|
/// LoopExitBinding { carrier_name: "count", join_exit_value: ValueId(19), host_slot: ValueId(6), role: LoopState },
|
||||||
/// ]
|
/// ]
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct LoopExitBinding {
|
pub struct LoopExitBinding {
|
||||||
/// Carrier variable name (e.g., "sum", "count")
|
/// Carrier variable name (e.g., "sum", "count", "is_digit_pos")
|
||||||
///
|
///
|
||||||
/// This is the variable name in the host's variable_map that should
|
/// This is the variable name in the host's variable_map that should
|
||||||
/// receive the exit value.
|
/// receive the exit value.
|
||||||
@ -91,6 +92,13 @@ pub struct LoopExitBinding {
|
|||||||
/// This is the host function's ValueId for the variable that should be
|
/// This is the host function's ValueId for the variable that should be
|
||||||
/// updated with the exit PHI result.
|
/// updated with the exit PHI result.
|
||||||
pub host_slot: ValueId,
|
pub host_slot: ValueId,
|
||||||
|
|
||||||
|
/// Phase 227: Role of this carrier (LoopState or ConditionOnly)
|
||||||
|
///
|
||||||
|
/// Determines whether this carrier should participate in exit PHI:
|
||||||
|
/// - LoopState: Needs exit PHI (value used after loop)
|
||||||
|
/// - ConditionOnly: No exit PHI (only used in loop condition)
|
||||||
|
pub role: CarrierRole,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Boundary information for inlining a JoinIR fragment into a host function
|
/// Boundary information for inlining a JoinIR fragment into a host function
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
use crate::mir::ValueId;
|
use crate::mir::ValueId;
|
||||||
use super::inline_boundary::{JoinInlineBoundary, LoopExitBinding};
|
use super::inline_boundary::{JoinInlineBoundary, LoopExitBinding};
|
||||||
use super::condition_to_joinir::ConditionBinding;
|
use super::condition_to_joinir::ConditionBinding;
|
||||||
|
use super::carrier_info::CarrierRole;
|
||||||
|
|
||||||
/// Role of a parameter in JoinIR lowering (Phase 200-A)
|
/// Role of a parameter in JoinIR lowering (Phase 200-A)
|
||||||
///
|
///
|
||||||
@ -299,6 +300,7 @@ mod tests {
|
|||||||
carrier_name: "sum".to_string(),
|
carrier_name: "sum".to_string(),
|
||||||
join_exit_value: ValueId(18),
|
join_exit_value: ValueId(18),
|
||||||
host_slot: ValueId(5),
|
host_slot: ValueId(5),
|
||||||
|
role: CarrierRole::LoopState,
|
||||||
};
|
};
|
||||||
|
|
||||||
let boundary = JoinInlineBoundaryBuilder::new()
|
let boundary = JoinInlineBoundaryBuilder::new()
|
||||||
@ -346,6 +348,7 @@ mod tests {
|
|||||||
carrier_name: "sum".to_string(),
|
carrier_name: "sum".to_string(),
|
||||||
join_exit_value: ValueId(18),
|
join_exit_value: ValueId(18),
|
||||||
host_slot: ValueId(101),
|
host_slot: ValueId(101),
|
||||||
|
role: CarrierRole::LoopState,
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
.with_loop_var_name(Some("i".to_string()))
|
.with_loop_var_name(Some("i".to_string()))
|
||||||
@ -372,11 +375,13 @@ mod tests {
|
|||||||
carrier_name: "i".to_string(),
|
carrier_name: "i".to_string(),
|
||||||
join_exit_value: ValueId(11),
|
join_exit_value: ValueId(11),
|
||||||
host_slot: ValueId(100),
|
host_slot: ValueId(100),
|
||||||
|
role: CarrierRole::LoopState,
|
||||||
},
|
},
|
||||||
LoopExitBinding {
|
LoopExitBinding {
|
||||||
carrier_name: "sum".to_string(),
|
carrier_name: "sum".to_string(),
|
||||||
join_exit_value: ValueId(20),
|
join_exit_value: ValueId(20),
|
||||||
host_slot: ValueId(101),
|
host_slot: ValueId(101),
|
||||||
|
role: CarrierRole::LoopState,
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
.with_loop_var_name(Some("i".to_string()))
|
.with_loop_var_name(Some("i".to_string()))
|
||||||
|
|||||||
@ -298,6 +298,7 @@ mod tests {
|
|||||||
name: "count".to_string(),
|
name: "count".to_string(),
|
||||||
host_id: crate::mir::ValueId(0),
|
host_id: crate::mir::ValueId(0),
|
||||||
join_id: None, // Phase 177-STRUCT-1
|
join_id: None, // Phase 177-STRUCT-1
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let updates = LoopUpdateAnalyzer::analyze_carrier_updates(&body, &carriers);
|
let updates = LoopUpdateAnalyzer::analyze_carrier_updates(&body, &carriers);
|
||||||
@ -355,6 +356,7 @@ mod tests {
|
|||||||
name: "result".to_string(),
|
name: "result".to_string(),
|
||||||
host_id: crate::mir::ValueId(0),
|
host_id: crate::mir::ValueId(0),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let updates = LoopUpdateAnalyzer::analyze_carrier_updates(&body, &carriers);
|
let updates = LoopUpdateAnalyzer::analyze_carrier_updates(&body, &carriers);
|
||||||
@ -413,6 +415,7 @@ mod tests {
|
|||||||
name: "result".to_string(),
|
name: "result".to_string(),
|
||||||
host_id: crate::mir::ValueId(0),
|
host_id: crate::mir::ValueId(0),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let updates = LoopUpdateAnalyzer::analyze_carrier_updates(&body, &carriers);
|
let updates = LoopUpdateAnalyzer::analyze_carrier_updates(&body, &carriers);
|
||||||
@ -471,6 +474,7 @@ mod tests {
|
|||||||
name: "result".to_string(),
|
name: "result".to_string(),
|
||||||
host_id: crate::mir::ValueId(0),
|
host_id: crate::mir::ValueId(0),
|
||||||
join_id: None,
|
join_id: None,
|
||||||
|
role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let updates = LoopUpdateAnalyzer::analyze_carrier_updates(&body, &carriers);
|
let updates = LoopUpdateAnalyzer::analyze_carrier_updates(&body, &carriers);
|
||||||
|
|||||||
@ -377,6 +377,23 @@ pub(crate) fn lower_loop_with_break_minimal(
|
|||||||
for (idx, carrier) in carrier_info.carriers.iter().enumerate() {
|
for (idx, carrier) in carrier_info.carriers.iter().enumerate() {
|
||||||
let carrier_name = &carrier.name;
|
let carrier_name = &carrier.name;
|
||||||
|
|
||||||
|
// Phase 227: ConditionOnly carriers don't have update expressions
|
||||||
|
// They just pass through their current value unchanged
|
||||||
|
use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
|
||||||
|
if carrier.role == CarrierRole::ConditionOnly {
|
||||||
|
// ConditionOnly carrier: just pass through the current value
|
||||||
|
// The carrier's ValueId from env is passed unchanged
|
||||||
|
let current_value = env.get(carrier_name).ok_or_else(|| {
|
||||||
|
format!("ConditionOnly carrier '{}' not found in env", carrier_name)
|
||||||
|
})?;
|
||||||
|
updated_carrier_values.push(current_value);
|
||||||
|
eprintln!(
|
||||||
|
"[loop/carrier_update] Phase 227: ConditionOnly carrier '{}' passthrough: {:?}",
|
||||||
|
carrier_name, current_value
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Get the update expression for this carrier
|
// Get the update expression for this carrier
|
||||||
let update_expr = carrier_updates.get(carrier_name).ok_or_else(|| {
|
let update_expr = carrier_updates.get(carrier_name).ok_or_else(|| {
|
||||||
format!(
|
format!(
|
||||||
|
|||||||
@ -183,11 +183,12 @@ impl DigitPosPromoter {
|
|||||||
// For DigitPos pattern, we add a NEW carrier (not replace loop_var)
|
// For DigitPos pattern, we add a NEW carrier (not replace loop_var)
|
||||||
let carrier_name = format!("is_{}", var_in_cond);
|
let carrier_name = format!("is_{}", var_in_cond);
|
||||||
|
|
||||||
use crate::mir::join_ir::lowering::carrier_info::CarrierVar;
|
use crate::mir::join_ir::lowering::carrier_info::{CarrierVar, CarrierRole};
|
||||||
let promoted_carrier = CarrierVar {
|
let promoted_carrier = CarrierVar {
|
||||||
name: carrier_name.clone(),
|
name: carrier_name.clone(),
|
||||||
host_id: ValueId(0), // Placeholder (will be remapped)
|
host_id: ValueId(0), // Placeholder (will be remapped)
|
||||||
join_id: None, // Will be allocated later
|
join_id: None, // Will be allocated later
|
||||||
|
role: CarrierRole::ConditionOnly, // Phase 227: DigitPos is condition-only
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create CarrierInfo with a dummy loop_var_name (will be ignored during merge)
|
// Create CarrierInfo with a dummy loop_var_name (will be ignored during merge)
|
||||||
|
|||||||
Reference in New Issue
Block a user