refactor(joinir): split Pattern2 orchestrator into smaller steps
This commit is contained in:
@ -8,9 +8,12 @@ use crate::mir::builder::MirBuilder;
|
|||||||
use crate::mir::ValueId;
|
use crate::mir::ValueId;
|
||||||
|
|
||||||
use super::pattern2_steps::apply_policy_step_box::ApplyPolicyStepBox;
|
use super::pattern2_steps::apply_policy_step_box::ApplyPolicyStepBox;
|
||||||
|
use super::pattern2_steps::body_local_derived_step_box::BodyLocalDerivedStepBox;
|
||||||
|
use super::pattern2_steps::carrier_updates_step_box::CarrierUpdatesStepBox;
|
||||||
use super::pattern2_steps::emit_joinir_step_box::EmitJoinIRStepBox;
|
use super::pattern2_steps::emit_joinir_step_box::EmitJoinIRStepBox;
|
||||||
use super::pattern2_steps::gather_facts_step_box::GatherFactsStepBox;
|
use super::pattern2_steps::gather_facts_step_box::GatherFactsStepBox;
|
||||||
use super::pattern2_steps::merge_step_box::MergeStepBox;
|
use super::pattern2_steps::merge_step_box::MergeStepBox;
|
||||||
|
use super::pattern2_steps::normalize_body_step_box::NormalizeBodyStepBox;
|
||||||
use super::pattern2_steps::post_loop_early_return_step_box::PostLoopEarlyReturnStepBox;
|
use super::pattern2_steps::post_loop_early_return_step_box::PostLoopEarlyReturnStepBox;
|
||||||
use super::pattern2_steps::promote_step_box::PromoteStepBox;
|
use super::pattern2_steps::promote_step_box::PromoteStepBox;
|
||||||
|
|
||||||
@ -38,25 +41,31 @@ impl Pattern2LoweringOrchestrator {
|
|||||||
let facts = GatherFactsStepBox::gather(builder, condition, body, fn_body, &ctx, verbose)?;
|
let facts = GatherFactsStepBox::gather(builder, condition, body, fn_body, &ctx, verbose)?;
|
||||||
let inputs = ApplyPolicyStepBox::apply(condition, body, facts)?;
|
let inputs = ApplyPolicyStepBox::apply(condition, body, facts)?;
|
||||||
|
|
||||||
let mut promoted = PromoteStepBox::run(builder, condition, body, inputs, debug, verbose)?;
|
let promoted = PromoteStepBox::run(builder, condition, body, inputs, debug, verbose)?;
|
||||||
let normalized_body = promoted.normalized_body.take();
|
let mut inputs = promoted.inputs;
|
||||||
|
|
||||||
|
let normalized = NormalizeBodyStepBox::run(builder, condition, body, &mut inputs, verbose)?;
|
||||||
|
let normalized_body = normalized.normalized_body;
|
||||||
let analysis_body = normalized_body.as_deref().unwrap_or(body);
|
let analysis_body = normalized_body.as_deref().unwrap_or(body);
|
||||||
let effective_break_condition = promoted.effective_break_condition;
|
let effective_break_condition = normalized.effective_break_condition;
|
||||||
let carrier_updates = promoted.carrier_updates;
|
|
||||||
|
BodyLocalDerivedStepBox::apply(analysis_body, &mut inputs, verbose)?;
|
||||||
|
let carrier_updates =
|
||||||
|
CarrierUpdatesStepBox::analyze_and_filter(analysis_body, &mut inputs, verbose);
|
||||||
let emitted = EmitJoinIRStepBox::emit(
|
let emitted = EmitJoinIRStepBox::emit(
|
||||||
builder,
|
builder,
|
||||||
condition,
|
condition,
|
||||||
analysis_body,
|
analysis_body,
|
||||||
&effective_break_condition,
|
&effective_break_condition,
|
||||||
&carrier_updates,
|
&carrier_updates,
|
||||||
&mut promoted.inputs,
|
&mut inputs,
|
||||||
debug,
|
debug,
|
||||||
verbose,
|
verbose,
|
||||||
skeleton,
|
skeleton,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let out = MergeStepBox::merge(builder, emitted.join_module, emitted.boundary, debug)?;
|
let out = MergeStepBox::merge(builder, emitted.join_module, emitted.boundary, debug)?;
|
||||||
PostLoopEarlyReturnStepBox::maybe_emit(builder, promoted.inputs.post_loop_early_return.as_ref())?;
|
PostLoopEarlyReturnStepBox::maybe_emit(builder, inputs.post_loop_early_return.as_ref())?;
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,38 @@
|
|||||||
|
//! BodyLocalDerivedStepBox (Phase 94, extracted in Phase 5)
|
||||||
|
//!
|
||||||
|
//! Responsibility:
|
||||||
|
//! - Apply the Phase 94 P5b escape-derived routing to Pattern2 inputs.
|
||||||
|
//! - No JoinIR generation. Purely sets `inputs.body_local_derived_recipe` or fails fast.
|
||||||
|
|
||||||
|
use crate::ast::ASTNode;
|
||||||
|
|
||||||
|
use super::super::pattern2_inputs_facts_box::{Pattern2DebugLog, Pattern2Inputs};
|
||||||
|
use super::super::policies::p5b_escape_derived_policy::classify_p5b_escape_derived;
|
||||||
|
use super::super::policies::PolicyDecision;
|
||||||
|
|
||||||
|
pub(crate) struct BodyLocalDerivedStepBox;
|
||||||
|
|
||||||
|
impl BodyLocalDerivedStepBox {
|
||||||
|
pub(crate) fn apply(
|
||||||
|
analysis_body: &[ASTNode],
|
||||||
|
inputs: &mut Pattern2Inputs,
|
||||||
|
verbose: bool,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
match classify_p5b_escape_derived(analysis_body, &inputs.loop_var_name) {
|
||||||
|
PolicyDecision::Use(recipe) => {
|
||||||
|
Pattern2DebugLog::new(verbose).log(
|
||||||
|
"phase94",
|
||||||
|
format!(
|
||||||
|
"Phase 94: Enabled BodyLocalDerived for '{}' (counter='{}', pre_delta={}, post_delta={})",
|
||||||
|
recipe.name, recipe.loop_counter_name, recipe.pre_delta, recipe.post_delta
|
||||||
|
),
|
||||||
|
);
|
||||||
|
inputs.body_local_derived_recipe = Some(recipe);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
PolicyDecision::Reject(reason) => Err(format!("[cf_loop/pattern2] {}", reason)),
|
||||||
|
PolicyDecision::None => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
//! CarrierUpdatesStepBox (Phase 176+, extracted in Phase 5)
|
||||||
|
//!
|
||||||
|
//! Responsibility:
|
||||||
|
//! - Analyze carrier updates for Pattern2 (or use policy override).
|
||||||
|
//! - Filter carriers to only those required by updates / condition-only / loop-local-zero.
|
||||||
|
//! - Ensure JoinValue env has join-ids for carriers referenced only from body updates.
|
||||||
|
|
||||||
|
use crate::ast::ASTNode;
|
||||||
|
use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, CarrierInit, CarrierRole};
|
||||||
|
use crate::mir::join_ir::lowering::condition_env::ConditionBinding;
|
||||||
|
use crate::mir::join_ir::lowering::loop_update_analyzer::{LoopUpdateAnalyzer, UpdateExpr};
|
||||||
|
|
||||||
|
use super::super::pattern2_inputs_facts_box::{Pattern2DebugLog, Pattern2Inputs};
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
pub(crate) struct CarrierUpdatesStepBox;
|
||||||
|
|
||||||
|
impl CarrierUpdatesStepBox {
|
||||||
|
pub(crate) fn analyze_and_filter(
|
||||||
|
analysis_body: &[ASTNode],
|
||||||
|
inputs: &mut Pattern2Inputs,
|
||||||
|
verbose: bool,
|
||||||
|
) -> BTreeMap<String, UpdateExpr> {
|
||||||
|
let carrier_updates = if let Some(override_map) = inputs.carrier_updates_override.take() {
|
||||||
|
override_map
|
||||||
|
} else {
|
||||||
|
LoopUpdateAnalyzer::analyze_carrier_updates(analysis_body, &inputs.carrier_info.carriers)
|
||||||
|
};
|
||||||
|
Pattern2DebugLog::new(verbose).log(
|
||||||
|
"updates",
|
||||||
|
format!("Phase 176-3: Analyzed {} carrier updates", carrier_updates.len()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let original_carrier_count = inputs.carrier_info.carriers.len();
|
||||||
|
filter_carriers_for_updates(&mut inputs.carrier_info, &carrier_updates);
|
||||||
|
Pattern2DebugLog::new(verbose).log(
|
||||||
|
"updates",
|
||||||
|
format!(
|
||||||
|
"Phase 176-4: Filtered carriers: {} → {} (kept only carriers with updates/condition-only/loop-local-zero)",
|
||||||
|
original_carrier_count,
|
||||||
|
inputs.carrier_info.carriers.len()
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ensure env has join-ids for carriers that are referenced only from body updates.
|
||||||
|
for carrier in &inputs.carrier_info.carriers {
|
||||||
|
if inputs.env.get(&carrier.name).is_none() {
|
||||||
|
let join_value = carrier
|
||||||
|
.join_id
|
||||||
|
.unwrap_or_else(|| inputs.join_value_space.alloc_param());
|
||||||
|
inputs.env.insert(carrier.name.clone(), join_value);
|
||||||
|
|
||||||
|
if carrier.init != CarrierInit::LoopLocalZero {
|
||||||
|
inputs.condition_bindings.push(ConditionBinding {
|
||||||
|
name: carrier.name.clone(),
|
||||||
|
host_value: carrier.host_id,
|
||||||
|
join_value,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Pattern2DebugLog::new(verbose).log(
|
||||||
|
"updates",
|
||||||
|
format!(
|
||||||
|
"Phase 247-EX: Skipping host binding for loop-local carrier '{}' (init=LoopLocalZero)",
|
||||||
|
carrier.name
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
carrier_updates
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filter_carriers_for_updates(info: &mut CarrierInfo, updates: &BTreeMap<String, UpdateExpr>) {
|
||||||
|
// Keep carriers that:
|
||||||
|
// - have updates
|
||||||
|
// - are condition-only (used for break condition)
|
||||||
|
// - are loop-local-zero (policy-injected for derived computations)
|
||||||
|
info.carriers.retain(|c| {
|
||||||
|
updates.contains_key(&c.name)
|
||||||
|
|| c.role == CarrierRole::ConditionOnly
|
||||||
|
|| c.init == CarrierInit::LoopLocalZero
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -7,8 +7,11 @@
|
|||||||
//! at the step boundary.
|
//! at the step boundary.
|
||||||
|
|
||||||
pub(crate) mod apply_policy_step_box;
|
pub(crate) mod apply_policy_step_box;
|
||||||
|
pub(crate) mod body_local_derived_step_box;
|
||||||
|
pub(crate) mod carrier_updates_step_box;
|
||||||
pub(crate) mod emit_joinir_step_box;
|
pub(crate) mod emit_joinir_step_box;
|
||||||
pub(crate) mod gather_facts_step_box;
|
pub(crate) mod gather_facts_step_box;
|
||||||
pub(crate) mod merge_step_box;
|
pub(crate) mod merge_step_box;
|
||||||
|
pub(crate) mod normalize_body_step_box;
|
||||||
pub(crate) mod post_loop_early_return_step_box;
|
pub(crate) mod post_loop_early_return_step_box;
|
||||||
pub(crate) mod promote_step_box;
|
pub(crate) mod promote_step_box;
|
||||||
|
|||||||
@ -0,0 +1,86 @@
|
|||||||
|
//! NormalizeBodyStepBox (Trim + break condition normalization)
|
||||||
|
//!
|
||||||
|
//! Responsibility:
|
||||||
|
//! - Apply trim normalization when enabled (Phase 93+).
|
||||||
|
//! - Return the effective break condition and an optional normalized body.
|
||||||
|
|
||||||
|
use crate::ast::ASTNode;
|
||||||
|
use crate::mir::builder::MirBuilder;
|
||||||
|
|
||||||
|
use super::super::pattern2_inputs_facts_box::{Pattern2DebugLog, Pattern2Inputs};
|
||||||
|
|
||||||
|
pub(crate) struct NormalizedBodyResult {
|
||||||
|
pub effective_break_condition: ASTNode,
|
||||||
|
pub normalized_body: Option<Vec<ASTNode>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct NormalizeBodyStepBox;
|
||||||
|
|
||||||
|
impl NormalizeBodyStepBox {
|
||||||
|
pub(crate) fn run(
|
||||||
|
builder: &mut MirBuilder,
|
||||||
|
condition: &ASTNode,
|
||||||
|
body: &[ASTNode],
|
||||||
|
inputs: &mut Pattern2Inputs,
|
||||||
|
verbose: bool,
|
||||||
|
) -> Result<NormalizedBodyResult, String> {
|
||||||
|
let log = Pattern2DebugLog::new(verbose);
|
||||||
|
let mut alloc_join_value = || inputs.join_value_space.alloc_param();
|
||||||
|
|
||||||
|
// Trim-like loops have their own canonicalizer path; keep existing behavior:
|
||||||
|
// read_digits(loop(true)) disables trim handling here.
|
||||||
|
let disable_trim = inputs.is_loop_true_read_digits;
|
||||||
|
|
||||||
|
let effective_break_condition = if !disable_trim {
|
||||||
|
if let Some(trim_result) =
|
||||||
|
super::super::trim_loop_lowering::TrimLoopLowerer::try_lower_trim_like_loop(
|
||||||
|
builder,
|
||||||
|
&inputs.scope,
|
||||||
|
condition,
|
||||||
|
&inputs.break_condition_node,
|
||||||
|
body,
|
||||||
|
&inputs.loop_var_name,
|
||||||
|
&mut inputs.carrier_info,
|
||||||
|
&mut alloc_join_value,
|
||||||
|
)?
|
||||||
|
{
|
||||||
|
log.log("trim", "TrimLoopLowerer processed Trim pattern successfully");
|
||||||
|
inputs.carrier_info = trim_result.carrier_info;
|
||||||
|
inputs.condition_only_recipe = trim_result.condition_only_recipe;
|
||||||
|
trim_result.condition
|
||||||
|
} else {
|
||||||
|
inputs.break_condition_node.clone()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
inputs.break_condition_node.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::mir::join_ir::lowering::complex_addend_normalizer::{
|
||||||
|
ComplexAddendNormalizer, NormalizationResult,
|
||||||
|
};
|
||||||
|
let mut normalized_body = Vec::new();
|
||||||
|
let mut has_normalization = false;
|
||||||
|
|
||||||
|
for node in body {
|
||||||
|
match ComplexAddendNormalizer::normalize_assign(node) {
|
||||||
|
NormalizationResult::Normalized {
|
||||||
|
temp_def, new_assign, ..
|
||||||
|
} => {
|
||||||
|
normalized_body.push(temp_def);
|
||||||
|
normalized_body.push(new_assign);
|
||||||
|
has_normalization = true;
|
||||||
|
}
|
||||||
|
NormalizationResult::Unchanged => normalized_body.push(node.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(NormalizedBodyResult {
|
||||||
|
effective_break_condition,
|
||||||
|
normalized_body: if has_normalization {
|
||||||
|
Some(normalized_body)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,34 +2,18 @@
|
|||||||
//!
|
//!
|
||||||
//! Responsibility:
|
//! Responsibility:
|
||||||
//! - promotion / read-only slot routing for body-local vars in conditions
|
//! - promotion / read-only slot routing for body-local vars in conditions
|
||||||
//! - trim normalization (when enabled)
|
//! - carrier preparation for JoinIR emission
|
||||||
//! - derived-slot routing (Phase 94)
|
|
||||||
//! - carrier update analysis + filtering
|
|
||||||
|
|
||||||
use crate::ast::ASTNode;
|
use crate::ast::ASTNode;
|
||||||
use crate::mir::builder::MirBuilder;
|
use crate::mir::builder::MirBuilder;
|
||||||
use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, CarrierInit};
|
|
||||||
use crate::mir::join_ir::lowering::loop_update_analyzer::UpdateExpr;
|
|
||||||
use crate::mir::loop_pattern_detection::error_messages;
|
use crate::mir::loop_pattern_detection::error_messages;
|
||||||
|
|
||||||
use super::super::body_local_policy::{classify_for_pattern2, BodyLocalRoute};
|
use super::super::body_local_policy::{classify_for_pattern2, BodyLocalRoute};
|
||||||
use super::super::pattern2_inputs_facts_box::{Pattern2DebugLog, Pattern2Inputs};
|
use super::super::pattern2_inputs_facts_box::Pattern2Inputs;
|
||||||
use super::super::policies::p5b_escape_derived_policy::{classify_p5b_escape_derived, P5bEscapeDerivedDecision};
|
|
||||||
use super::super::policies::PolicyDecision;
|
use super::super::policies::PolicyDecision;
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
pub(crate) struct PromoteStepResult {
|
pub(crate) struct PromoteStepResult {
|
||||||
pub inputs: Pattern2Inputs,
|
pub inputs: Pattern2Inputs,
|
||||||
pub effective_break_condition: ASTNode,
|
|
||||||
pub normalized_body: Option<Vec<ASTNode>>,
|
|
||||||
pub carrier_updates: BTreeMap<String, UpdateExpr>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PromoteStepResult {
|
|
||||||
pub(crate) fn analysis_body<'a>(&'a self, original_body: &'a [ASTNode]) -> &'a [ASTNode] {
|
|
||||||
self.normalized_body.as_deref().unwrap_or(original_body)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct PromoteStepBox;
|
pub(crate) struct PromoteStepBox;
|
||||||
@ -44,100 +28,7 @@ impl PromoteStepBox {
|
|||||||
verbose: bool,
|
verbose: bool,
|
||||||
) -> Result<PromoteStepResult, String> {
|
) -> Result<PromoteStepResult, String> {
|
||||||
Self::promote_and_prepare_carriers(builder, condition, body, &mut inputs, debug, verbose)?;
|
Self::promote_and_prepare_carriers(builder, condition, body, &mut inputs, debug, verbose)?;
|
||||||
|
Ok(PromoteStepResult { inputs })
|
||||||
let (effective_break_condition, normalized_body) =
|
|
||||||
Self::apply_trim_and_normalize(builder, condition, body, &mut inputs, verbose)?;
|
|
||||||
|
|
||||||
let analysis_body = normalized_body.as_deref().unwrap_or(body);
|
|
||||||
|
|
||||||
// Phase 94: Detect P5b escape-derived (`ch` reassignment + escape counter).
|
|
||||||
match classify_p5b_escape_derived(analysis_body, &inputs.loop_var_name) {
|
|
||||||
P5bEscapeDerivedDecision::Use(recipe) => {
|
|
||||||
Pattern2DebugLog::new(verbose).log(
|
|
||||||
"phase94",
|
|
||||||
format!(
|
|
||||||
"Phase 94: Enabled BodyLocalDerived for '{}' (counter='{}', pre_delta={}, post_delta={})",
|
|
||||||
recipe.name, recipe.loop_counter_name, recipe.pre_delta, recipe.post_delta
|
|
||||||
),
|
|
||||||
);
|
|
||||||
inputs.body_local_derived_recipe = Some(recipe);
|
|
||||||
}
|
|
||||||
P5bEscapeDerivedDecision::Reject(reason) => return Err(format!("[cf_loop/pattern2] {}", reason)),
|
|
||||||
P5bEscapeDerivedDecision::None => {
|
|
||||||
let has_ch_reassign = analysis_body.iter().any(|n| match n {
|
|
||||||
ASTNode::Assignment { target, .. } => matches!(
|
|
||||||
target.as_ref(),
|
|
||||||
ASTNode::Variable { name, .. } if name == "ch"
|
|
||||||
),
|
|
||||||
_ => false,
|
|
||||||
});
|
|
||||||
if crate::config::env::joinir_dev::strict_enabled() && has_ch_reassign {
|
|
||||||
return Err(format!(
|
|
||||||
"[cf_loop/pattern2] {}",
|
|
||||||
crate::mir::join_ir::lowering::error_tags::freeze(
|
|
||||||
"[phase94/body_local_derived/contract/unhandled_reassign] Body-local reassignment to 'ch' detected but not supported by Phase 94 recipe"
|
|
||||||
)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
use crate::mir::join_ir::lowering::loop_update_analyzer::LoopUpdateAnalyzer;
|
|
||||||
let carrier_updates = if let Some(override_map) = inputs.carrier_updates_override.take() {
|
|
||||||
override_map
|
|
||||||
} else {
|
|
||||||
LoopUpdateAnalyzer::analyze_carrier_updates(analysis_body, &inputs.carrier_info.carriers)
|
|
||||||
};
|
|
||||||
Pattern2DebugLog::new(verbose).log(
|
|
||||||
"updates",
|
|
||||||
format!("Phase 176-3: Analyzed {} carrier updates", carrier_updates.len()),
|
|
||||||
);
|
|
||||||
|
|
||||||
let original_carrier_count = inputs.carrier_info.carriers.len();
|
|
||||||
filter_carriers_for_updates(&mut inputs.carrier_info, &carrier_updates);
|
|
||||||
Pattern2DebugLog::new(verbose).log(
|
|
||||||
"updates",
|
|
||||||
format!(
|
|
||||||
"Phase 176-4: Filtered carriers: {} → {} (kept only carriers with updates/condition-only/loop-local-zero)",
|
|
||||||
original_carrier_count,
|
|
||||||
inputs.carrier_info.carriers.len()
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Ensure env has join-ids for carriers that are referenced only from body updates.
|
|
||||||
for carrier in &inputs.carrier_info.carriers {
|
|
||||||
if inputs.env.get(&carrier.name).is_none() {
|
|
||||||
let join_value = carrier
|
|
||||||
.join_id
|
|
||||||
.unwrap_or_else(|| inputs.join_value_space.alloc_param());
|
|
||||||
|
|
||||||
inputs.env.insert(carrier.name.clone(), join_value);
|
|
||||||
|
|
||||||
if carrier.init != CarrierInit::LoopLocalZero {
|
|
||||||
use crate::mir::join_ir::lowering::condition_env::ConditionBinding;
|
|
||||||
inputs.condition_bindings.push(ConditionBinding {
|
|
||||||
name: carrier.name.clone(),
|
|
||||||
host_value: carrier.host_id,
|
|
||||||
join_value,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
Pattern2DebugLog::new(verbose).log(
|
|
||||||
"updates",
|
|
||||||
format!(
|
|
||||||
"Phase 247-EX: Skipping host binding for loop-local carrier '{}' (init=LoopLocalZero)",
|
|
||||||
carrier.name
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(PromoteStepResult {
|
|
||||||
inputs,
|
|
||||||
effective_break_condition,
|
|
||||||
normalized_body,
|
|
||||||
carrier_updates,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(in crate::mir::builder) fn promote_and_prepare_carriers(
|
pub(in crate::mir::builder) fn promote_and_prepare_carriers(
|
||||||
@ -286,67 +177,4 @@ impl PromoteStepBox {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_trim_and_normalize(
|
|
||||||
builder: &mut MirBuilder,
|
|
||||||
condition: &ASTNode,
|
|
||||||
body: &[ASTNode],
|
|
||||||
inputs: &mut Pattern2Inputs,
|
|
||||||
verbose: bool,
|
|
||||||
) -> Result<(ASTNode, Option<Vec<ASTNode>>), String> {
|
|
||||||
let log = Pattern2DebugLog::new(verbose);
|
|
||||||
let mut alloc_join_value = || inputs.join_value_space.alloc_param();
|
|
||||||
|
|
||||||
let disable_trim = inputs.is_loop_true_read_digits;
|
|
||||||
|
|
||||||
let effective_break_condition = if !disable_trim {
|
|
||||||
if let Some(trim_result) = super::super::trim_loop_lowering::TrimLoopLowerer::try_lower_trim_like_loop(
|
|
||||||
builder,
|
|
||||||
&inputs.scope,
|
|
||||||
condition,
|
|
||||||
&inputs.break_condition_node,
|
|
||||||
body,
|
|
||||||
&inputs.loop_var_name,
|
|
||||||
&mut inputs.carrier_info,
|
|
||||||
&mut alloc_join_value,
|
|
||||||
)? {
|
|
||||||
log.log("trim", "TrimLoopLowerer processed Trim pattern successfully");
|
|
||||||
inputs.carrier_info = trim_result.carrier_info;
|
|
||||||
inputs.condition_only_recipe = trim_result.condition_only_recipe;
|
|
||||||
trim_result.condition
|
|
||||||
} else {
|
|
||||||
inputs.break_condition_node.clone()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
inputs.break_condition_node.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::mir::join_ir::lowering::complex_addend_normalizer::{ComplexAddendNormalizer, NormalizationResult};
|
|
||||||
let mut normalized_body = Vec::new();
|
|
||||||
let mut has_normalization = false;
|
|
||||||
|
|
||||||
for node in body {
|
|
||||||
match ComplexAddendNormalizer::normalize_assign(node) {
|
|
||||||
NormalizationResult::Normalized { temp_def, new_assign, .. } => {
|
|
||||||
normalized_body.push(temp_def);
|
|
||||||
normalized_body.push(new_assign);
|
|
||||||
has_normalization = true;
|
|
||||||
}
|
|
||||||
NormalizationResult::Unchanged => normalized_body.push(node.clone()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
effective_break_condition,
|
|
||||||
if has_normalization { Some(normalized_body) } else { None },
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn filter_carriers_for_updates(carrier_info: &mut CarrierInfo, carrier_updates: &BTreeMap<String, UpdateExpr>) {
|
|
||||||
use crate::mir::join_ir::lowering::carrier_info::{CarrierInit, CarrierRole};
|
|
||||||
carrier_info.carriers.retain(|carrier| {
|
|
||||||
carrier_updates.contains_key(&carrier.name)
|
|
||||||
|| carrier.role == CarrierRole::ConditionOnly
|
|
||||||
|| carrier.init == CarrierInit::LoopLocalZero
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,12 +29,26 @@ pub fn classify_p5b_escape_derived(
|
|||||||
body: &[ASTNode],
|
body: &[ASTNode],
|
||||||
loop_var_name: &str,
|
loop_var_name: &str,
|
||||||
) -> P5bEscapeDerivedDecision {
|
) -> P5bEscapeDerivedDecision {
|
||||||
|
let strict = joinir_dev::strict_enabled();
|
||||||
|
let has_ch_init = find_local_init_expr(body, "ch").is_some();
|
||||||
|
let has_ch_reassign = has_assignment_to_var(body, "ch");
|
||||||
|
|
||||||
let Some(info) = super::super::ast_feature_extractor::detect_escape_skip_pattern(body) else {
|
let Some(info) = super::super::ast_feature_extractor::detect_escape_skip_pattern(body) else {
|
||||||
|
if strict && has_ch_init && has_ch_reassign {
|
||||||
|
return P5bEscapeDerivedDecision::Reject(error_tags::freeze(
|
||||||
|
"[phase94/body_local_derived/contract/unhandled_reassign] Body-local reassignment to 'ch' detected but escape shape is not recognized",
|
||||||
|
));
|
||||||
|
}
|
||||||
return P5bEscapeDerivedDecision::None;
|
return P5bEscapeDerivedDecision::None;
|
||||||
};
|
};
|
||||||
|
|
||||||
if info.counter_name != loop_var_name {
|
if info.counter_name != loop_var_name {
|
||||||
// Not the loop counter we lower as the JoinIR loop var; ignore to avoid misrouting.
|
// Not the loop counter we lower as the JoinIR loop var; ignore to avoid misrouting.
|
||||||
|
if strict && has_ch_init && has_ch_reassign {
|
||||||
|
return P5bEscapeDerivedDecision::Reject(error_tags::freeze(
|
||||||
|
"[phase94/body_local_derived/contract/loop_counter_mismatch] Body-local reassignment to 'ch' detected but loop counter does not match Pattern2 loop var",
|
||||||
|
));
|
||||||
|
}
|
||||||
return P5bEscapeDerivedDecision::None;
|
return P5bEscapeDerivedDecision::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,6 +69,29 @@ pub fn classify_p5b_escape_derived(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn has_assignment_to_var(body: &[ASTNode], name: &str) -> bool {
|
||||||
|
fn node_has_assignment(node: &ASTNode, name: &str) -> bool {
|
||||||
|
match node {
|
||||||
|
ASTNode::Assignment { target, .. } => is_var_named(target.as_ref(), name),
|
||||||
|
ASTNode::If {
|
||||||
|
then_body,
|
||||||
|
else_body,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
then_body.iter().any(|n| node_has_assignment(n, name))
|
||||||
|
|| else_body
|
||||||
|
.as_ref()
|
||||||
|
.map_or(false, |e| e.iter().any(|n| node_has_assignment(n, name)))
|
||||||
|
}
|
||||||
|
ASTNode::Loop { body, .. } => body.iter().any(|n| node_has_assignment(n, name)),
|
||||||
|
ASTNode::ScopeBox { body, .. } => body.iter().any(|n| node_has_assignment(n, name)),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body.iter().any(|n| node_has_assignment(n, name))
|
||||||
|
}
|
||||||
|
|
||||||
fn build_recipe_from_info(
|
fn build_recipe_from_info(
|
||||||
body: &[ASTNode],
|
body: &[ASTNode],
|
||||||
info: &EscapeSkipPatternInfo,
|
info: &EscapeSkipPatternInfo,
|
||||||
|
|||||||
Reference in New Issue
Block a user