refactor(joinir): Phase 108 unify Pattern2 policy routing
This commit is contained in:
@ -57,6 +57,7 @@ pub(in crate::mir::builder) mod common_init;
|
|||||||
pub(in crate::mir::builder) mod loop_true_counter_extractor; // Phase 104: loop(true) counter extraction for Pattern2
|
pub(in crate::mir::builder) mod loop_true_counter_extractor; // Phase 104: loop(true) counter extraction for Pattern2
|
||||||
pub(in crate::mir::builder) mod read_digits_break_condition_box; // Phase 104: break cond normalization for read_digits(loop(true))
|
pub(in crate::mir::builder) mod read_digits_break_condition_box; // Phase 104: break cond normalization for read_digits(loop(true))
|
||||||
pub(in crate::mir::builder) mod pattern2_break_condition_policy_router; // Phase 105: policy router box for Pattern2 break condition
|
pub(in crate::mir::builder) mod pattern2_break_condition_policy_router; // Phase 105: policy router box for Pattern2 break condition
|
||||||
|
pub(in crate::mir::builder) mod pattern2_policy_router; // Phase 108: unified Pattern2 policy router (balanced/read_digits/default)
|
||||||
pub(in crate::mir::builder) mod pattern2_inputs_facts_box; // Phase 105: Pattern2 input facts (analysis only)
|
pub(in crate::mir::builder) mod pattern2_inputs_facts_box; // Phase 105: Pattern2 input facts (analysis only)
|
||||||
pub(in crate::mir::builder) mod pattern2_lowering_orchestrator; // Phase 105: Pattern2 orchestration (wiring/emission)
|
pub(in crate::mir::builder) mod pattern2_lowering_orchestrator; // Phase 105: Pattern2 orchestration (wiring/emission)
|
||||||
pub(in crate::mir::builder) mod pattern2_steps; // Phase 106: Pattern2 step boxes (pipeline SSOT)
|
pub(in crate::mir::builder) mod pattern2_steps; // Phase 106: Pattern2 step boxes (pipeline SSOT)
|
||||||
|
|||||||
@ -94,7 +94,7 @@ pub(in crate::mir::builder) struct Pattern2Inputs {
|
|||||||
pub carrier_updates_override: Option<std::collections::BTreeMap<String, UpdateExpr>>,
|
pub carrier_updates_override: Option<std::collections::BTreeMap<String, UpdateExpr>>,
|
||||||
/// Phase 107: Post-loop early return plan for return-in-loop normalization.
|
/// Phase 107: Post-loop early return plan for return-in-loop normalization.
|
||||||
pub post_loop_early_return: Option<
|
pub post_loop_early_return: Option<
|
||||||
crate::mir::builder::control_flow::joinir::patterns::policies::balanced_depth_scan_policy::PostLoopEarlyReturnPlan,
|
crate::mir::builder::control_flow::joinir::patterns::policies::post_loop_early_return_plan::PostLoopEarlyReturnPlan,
|
||||||
>,
|
>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,80 @@
|
|||||||
|
//! Pattern2 policy router (Phase 108)
|
||||||
|
//!
|
||||||
|
//! Responsibility (SSOT):
|
||||||
|
//! - Decide which Pattern2 policy route applies (balanced depth-scan / loop(true) read-digits / default).
|
||||||
|
//! - Normalize the outputs into a single routing struct consumed by ApplyPolicyStepBox.
|
||||||
|
//!
|
||||||
|
//! NOTE: This box does not emit JoinIR. It only provides "what to do" facts.
|
||||||
|
|
||||||
|
use crate::ast::ASTNode;
|
||||||
|
use crate::mir::join_ir::lowering::common::body_local_slot::ReadOnlyBodyLocalSlotBox;
|
||||||
|
|
||||||
|
use super::pattern2_break_condition_policy_router::Pattern2BreakConditionPolicyRouterBox;
|
||||||
|
use super::pattern2_inputs_facts_box::BodyLocalHandlingPolicy;
|
||||||
|
use super::policies::balanced_depth_scan_policy_box::BalancedDepthScanPolicyBox;
|
||||||
|
use super::policies::PolicyDecision;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct Pattern2PolicyRouting {
|
||||||
|
pub allowed_body_locals_for_conditions: Vec<String>,
|
||||||
|
pub body_local_handling: BodyLocalHandlingPolicy,
|
||||||
|
pub read_only_body_local_slot: Option<crate::mir::join_ir::lowering::common::body_local_slot::ReadOnlyBodyLocalSlot>,
|
||||||
|
pub break_condition_node: ASTNode,
|
||||||
|
pub is_loop_true_read_digits: bool,
|
||||||
|
pub balanced_depth_scan_recipe:
|
||||||
|
Option<crate::mir::join_ir::lowering::common::balanced_depth_scan_emitter::BalancedDepthScanRecipe>,
|
||||||
|
pub carrier_updates_override: Option<
|
||||||
|
std::collections::BTreeMap<String, crate::mir::join_ir::lowering::loop_update_analyzer::UpdateExpr>,
|
||||||
|
>,
|
||||||
|
pub post_loop_early_return: Option<
|
||||||
|
crate::mir::builder::control_flow::joinir::patterns::policies::post_loop_early_return_plan::PostLoopEarlyReturnPlan,
|
||||||
|
>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct Pattern2PolicyRouterBox;
|
||||||
|
|
||||||
|
impl Pattern2PolicyRouterBox {
|
||||||
|
pub(crate) fn route(condition: &ASTNode, body: &[ASTNode]) -> Result<Pattern2PolicyRouting, String> {
|
||||||
|
// Route 1 (Phase 107): balanced depth-scan (return-in-loop normalization).
|
||||||
|
match BalancedDepthScanPolicyBox::decide(condition, body) {
|
||||||
|
PolicyDecision::Use(result) => {
|
||||||
|
return Ok(Pattern2PolicyRouting {
|
||||||
|
allowed_body_locals_for_conditions: result.allowed_body_locals_for_conditions,
|
||||||
|
body_local_handling: BodyLocalHandlingPolicy::SkipPromotion,
|
||||||
|
read_only_body_local_slot: None,
|
||||||
|
break_condition_node: result.break_condition_node,
|
||||||
|
is_loop_true_read_digits: false,
|
||||||
|
balanced_depth_scan_recipe: Some(result.derived_recipe),
|
||||||
|
carrier_updates_override: Some(result.carrier_updates_override),
|
||||||
|
post_loop_early_return: Some(result.post_loop_early_return),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
PolicyDecision::Reject(reason) => return Err(reason),
|
||||||
|
PolicyDecision::None => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Route 2 (Phase 105): loop(true) read-digits family + default break-cond SSOT.
|
||||||
|
let break_routing = Pattern2BreakConditionPolicyRouterBox::route(condition, body)?;
|
||||||
|
|
||||||
|
let read_only_body_local_slot = if break_routing.allowed_body_locals_for_conditions.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(ReadOnlyBodyLocalSlotBox::extract_single(
|
||||||
|
&break_routing.allowed_body_locals_for_conditions,
|
||||||
|
body,
|
||||||
|
)?)
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Pattern2PolicyRouting {
|
||||||
|
allowed_body_locals_for_conditions: break_routing.allowed_body_locals_for_conditions,
|
||||||
|
body_local_handling: BodyLocalHandlingPolicy::DefaultPromotion,
|
||||||
|
read_only_body_local_slot,
|
||||||
|
break_condition_node: break_routing.break_condition_node,
|
||||||
|
is_loop_true_read_digits: break_routing.is_loop_true_read_digits,
|
||||||
|
balanced_depth_scan_recipe: None,
|
||||||
|
carrier_updates_override: None,
|
||||||
|
post_loop_early_return: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -4,60 +4,14 @@
|
|||||||
|
|
||||||
use crate::ast::ASTNode;
|
use crate::ast::ASTNode;
|
||||||
|
|
||||||
use super::super::pattern2_break_condition_policy_router::Pattern2BreakConditionPolicyRouterBox;
|
use super::super::pattern2_policy_router::Pattern2PolicyRouterBox;
|
||||||
use super::super::pattern2_inputs_facts_box::{Pattern2Facts, Pattern2Inputs};
|
use super::super::pattern2_inputs_facts_box::{Pattern2Facts, Pattern2Inputs};
|
||||||
|
|
||||||
pub(crate) struct ApplyPolicyStepBox;
|
pub(crate) struct ApplyPolicyStepBox;
|
||||||
|
|
||||||
impl ApplyPolicyStepBox {
|
impl ApplyPolicyStepBox {
|
||||||
pub(crate) fn apply(condition: &ASTNode, body: &[ASTNode], facts: Pattern2Facts) -> Result<Pattern2Inputs, String> {
|
pub(crate) fn apply(condition: &ASTNode, body: &[ASTNode], facts: Pattern2Facts) -> Result<Pattern2Inputs, String> {
|
||||||
use super::super::policies::balanced_depth_scan_policy_box::BalancedDepthScanPolicyBox;
|
let policy = Pattern2PolicyRouterBox::route(condition, body)?;
|
||||||
use super::super::policies::PolicyDecision;
|
|
||||||
use crate::mir::builder::control_flow::joinir::patterns::pattern2_inputs_facts_box::BodyLocalHandlingPolicy;
|
|
||||||
use crate::mir::join_ir::lowering::common::body_local_slot::ReadOnlyBodyLocalSlotBox;
|
|
||||||
|
|
||||||
// Phase 107: balanced depth-scan (return-in-loop) policy.
|
|
||||||
// This route provides its own break-cond + derived recipe + post-loop early return plan.
|
|
||||||
match BalancedDepthScanPolicyBox::decide(condition, body) {
|
|
||||||
PolicyDecision::Use(result) => {
|
|
||||||
return Ok(Pattern2Inputs {
|
|
||||||
loop_var_name: facts.loop_var_name,
|
|
||||||
loop_var_id: facts.loop_var_id,
|
|
||||||
carrier_info: facts.carrier_info,
|
|
||||||
scope: facts.scope,
|
|
||||||
captured_env: facts.captured_env,
|
|
||||||
join_value_space: facts.join_value_space,
|
|
||||||
env: facts.env,
|
|
||||||
condition_bindings: facts.condition_bindings,
|
|
||||||
body_local_env: facts.body_local_env,
|
|
||||||
allowed_body_locals_for_conditions: result.allowed_body_locals_for_conditions,
|
|
||||||
body_local_handling: BodyLocalHandlingPolicy::SkipPromotion,
|
|
||||||
read_only_body_local_slot: None,
|
|
||||||
break_condition_node: result.break_condition_node,
|
|
||||||
is_loop_true_read_digits: false,
|
|
||||||
condition_only_recipe: None,
|
|
||||||
body_local_derived_recipe: None,
|
|
||||||
balanced_depth_scan_recipe: Some(result.derived_recipe),
|
|
||||||
carrier_updates_override: Some(result.carrier_updates_override),
|
|
||||||
post_loop_early_return: Some(result.post_loop_early_return),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
PolicyDecision::Reject(reason) => {
|
|
||||||
return Err(reason);
|
|
||||||
}
|
|
||||||
PolicyDecision::None => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
let break_routing = Pattern2BreakConditionPolicyRouterBox::route(condition, body)?;
|
|
||||||
|
|
||||||
let read_only_body_local_slot = if break_routing.allowed_body_locals_for_conditions.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(ReadOnlyBodyLocalSlotBox::extract_single(
|
|
||||||
&break_routing.allowed_body_locals_for_conditions,
|
|
||||||
body,
|
|
||||||
)?)
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Pattern2Inputs {
|
Ok(Pattern2Inputs {
|
||||||
loop_var_name: facts.loop_var_name,
|
loop_var_name: facts.loop_var_name,
|
||||||
@ -69,16 +23,16 @@ impl ApplyPolicyStepBox {
|
|||||||
env: facts.env,
|
env: facts.env,
|
||||||
condition_bindings: facts.condition_bindings,
|
condition_bindings: facts.condition_bindings,
|
||||||
body_local_env: facts.body_local_env,
|
body_local_env: facts.body_local_env,
|
||||||
allowed_body_locals_for_conditions: break_routing.allowed_body_locals_for_conditions,
|
allowed_body_locals_for_conditions: policy.allowed_body_locals_for_conditions,
|
||||||
body_local_handling: BodyLocalHandlingPolicy::DefaultPromotion,
|
body_local_handling: policy.body_local_handling,
|
||||||
read_only_body_local_slot,
|
read_only_body_local_slot: policy.read_only_body_local_slot,
|
||||||
break_condition_node: break_routing.break_condition_node,
|
break_condition_node: policy.break_condition_node,
|
||||||
is_loop_true_read_digits: break_routing.is_loop_true_read_digits,
|
is_loop_true_read_digits: policy.is_loop_true_read_digits,
|
||||||
condition_only_recipe: None,
|
condition_only_recipe: None,
|
||||||
body_local_derived_recipe: None,
|
body_local_derived_recipe: None,
|
||||||
balanced_depth_scan_recipe: None,
|
balanced_depth_scan_recipe: policy.balanced_depth_scan_recipe,
|
||||||
carrier_updates_override: None,
|
carrier_updates_override: policy.carrier_updates_override,
|
||||||
post_loop_early_return: None,
|
post_loop_early_return: policy.post_loop_early_return,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
//! early-return guard. This keeps JoinIR lowering "break-only" while preserving
|
//! early-return guard. This keeps JoinIR lowering "break-only" while preserving
|
||||||
//! source semantics for the recognized family.
|
//! source semantics for the recognized family.
|
||||||
|
|
||||||
use crate::ast::{ASTNode, BinaryOperator, Span};
|
use crate::ast::{ASTNode, Span};
|
||||||
use crate::mir::builder::MirBuilder;
|
use crate::mir::builder::MirBuilder;
|
||||||
|
|
||||||
pub(crate) struct PostLoopEarlyReturnStepBox;
|
pub(crate) struct PostLoopEarlyReturnStepBox;
|
||||||
@ -13,35 +13,18 @@ impl PostLoopEarlyReturnStepBox {
|
|||||||
pub(crate) fn maybe_emit(
|
pub(crate) fn maybe_emit(
|
||||||
builder: &mut MirBuilder,
|
builder: &mut MirBuilder,
|
||||||
plan: Option<
|
plan: Option<
|
||||||
&crate::mir::builder::control_flow::joinir::patterns::policies::balanced_depth_scan_policy::PostLoopEarlyReturnPlan,
|
&crate::mir::builder::control_flow::joinir::patterns::policies::post_loop_early_return_plan::PostLoopEarlyReturnPlan,
|
||||||
>,
|
>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let Some(plan) = plan else { return Ok(()); };
|
let Some(plan) = plan else { return Ok(()); };
|
||||||
|
|
||||||
// if i < n { return i }
|
|
||||||
let cond = ASTNode::BinaryOp {
|
|
||||||
operator: BinaryOperator::Less,
|
|
||||||
left: Box::new(ASTNode::Variable {
|
|
||||||
name: plan.loop_counter_name.clone(),
|
|
||||||
span: Span::unknown(),
|
|
||||||
}),
|
|
||||||
right: Box::new(ASTNode::Variable {
|
|
||||||
name: plan.bound_name.clone(),
|
|
||||||
span: Span::unknown(),
|
|
||||||
}),
|
|
||||||
span: Span::unknown(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let ret_stmt = ASTNode::Return {
|
let ret_stmt = ASTNode::Return {
|
||||||
value: Some(Box::new(ASTNode::Variable {
|
value: Some(Box::new(plan.ret_expr.clone())),
|
||||||
name: plan.loop_counter_name.clone(),
|
|
||||||
span: Span::unknown(),
|
|
||||||
})),
|
|
||||||
span: Span::unknown(),
|
span: Span::unknown(),
|
||||||
};
|
};
|
||||||
|
|
||||||
builder.build_statement(ASTNode::If {
|
builder.build_statement(ASTNode::If {
|
||||||
condition: Box::new(cond),
|
condition: Box::new(plan.cond.clone()),
|
||||||
then_body: vec![ret_stmt],
|
then_body: vec![ret_stmt],
|
||||||
else_body: None,
|
else_body: None,
|
||||||
span: Span::unknown(),
|
span: Span::unknown(),
|
||||||
|
|||||||
@ -12,14 +12,9 @@ use crate::mir::join_ir::lowering::loop_update_analyzer::{UpdateExpr, UpdateRhs}
|
|||||||
use crate::mir::join_ir::BinOpKind;
|
use crate::mir::join_ir::BinOpKind;
|
||||||
|
|
||||||
use super::PolicyDecision;
|
use super::PolicyDecision;
|
||||||
|
use super::post_loop_early_return_plan::PostLoopEarlyReturnPlan;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub(crate) struct PostLoopEarlyReturnPlan {
|
|
||||||
pub loop_counter_name: String,
|
|
||||||
pub bound_name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct BalancedDepthScanPolicyResult {
|
pub(crate) struct BalancedDepthScanPolicyResult {
|
||||||
pub break_condition_node: ASTNode,
|
pub break_condition_node: ASTNode,
|
||||||
@ -101,8 +96,13 @@ fn classify_balanced_depth_scan(
|
|||||||
depth_next_name,
|
depth_next_name,
|
||||||
},
|
},
|
||||||
post_loop_early_return: PostLoopEarlyReturnPlan {
|
post_loop_early_return: PostLoopEarlyReturnPlan {
|
||||||
loop_counter_name,
|
cond: ASTNode::BinaryOp {
|
||||||
bound_name,
|
operator: BinaryOperator::Less,
|
||||||
|
left: Box::new(var(&loop_counter_name)),
|
||||||
|
right: Box::new(var(&bound_name)),
|
||||||
|
span: Span::unknown(),
|
||||||
|
},
|
||||||
|
ret_expr: var(&loop_counter_name),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -618,8 +618,28 @@ mod tests {
|
|||||||
other => panic!("expected Use, got {:?}", other),
|
other => panic!("expected Use, got {:?}", other),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(result.post_loop_early_return.loop_counter_name, "i");
|
assert!(
|
||||||
assert_eq!(result.post_loop_early_return.bound_name, "n");
|
matches!(
|
||||||
|
&result.post_loop_early_return.cond,
|
||||||
|
ASTNode::BinaryOp {
|
||||||
|
operator: BinaryOperator::Less,
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
..
|
||||||
|
} if matches!(left.as_ref(), ASTNode::Variable { name, .. } if name == "i")
|
||||||
|
&& matches!(right.as_ref(), ASTNode::Variable { name, .. } if name == "n")
|
||||||
|
),
|
||||||
|
"post-loop cond must be `i < n`, got {:?}",
|
||||||
|
result.post_loop_early_return.cond
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
matches!(
|
||||||
|
&result.post_loop_early_return.ret_expr,
|
||||||
|
ASTNode::Variable { name, .. } if name == "i"
|
||||||
|
),
|
||||||
|
"post-loop ret_expr must be `i`, got {:?}",
|
||||||
|
result.post_loop_early_return.ret_expr
|
||||||
|
);
|
||||||
assert!(result.allowed_body_locals_for_conditions.contains(&"ch".to_string()));
|
assert!(result.allowed_body_locals_for_conditions.contains(&"ch".to_string()));
|
||||||
assert!(result.allowed_body_locals_for_conditions.contains(&"depth_next".to_string()));
|
assert!(result.allowed_body_locals_for_conditions.contains(&"depth_next".to_string()));
|
||||||
assert!(result.carrier_updates_override.contains_key("i"));
|
assert!(result.carrier_updates_override.contains_key("i"));
|
||||||
|
|||||||
@ -35,3 +35,4 @@ pub(in crate::mir::builder) mod trim_policy;
|
|||||||
pub(in crate::mir::builder) mod loop_true_read_digits_policy;
|
pub(in crate::mir::builder) mod loop_true_read_digits_policy;
|
||||||
pub(in crate::mir::builder) mod balanced_depth_scan_policy;
|
pub(in crate::mir::builder) mod balanced_depth_scan_policy;
|
||||||
pub(in crate::mir::builder) mod balanced_depth_scan_policy_box;
|
pub(in crate::mir::builder) mod balanced_depth_scan_policy_box;
|
||||||
|
pub(in crate::mir::builder) mod post_loop_early_return_plan;
|
||||||
|
|||||||
@ -0,0 +1,15 @@
|
|||||||
|
//! Post-loop early return plan (policy-level SSOT)
|
||||||
|
//!
|
||||||
|
//! Responsibility:
|
||||||
|
//! - Describe a post-loop guard that emulates an in-loop `return` without making
|
||||||
|
//! Pattern2 lowering itself return-in-loop aware.
|
||||||
|
//! - Keep the plan policy-agnostic so multiple Pattern2 families can reuse it.
|
||||||
|
|
||||||
|
use crate::ast::ASTNode;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) struct PostLoopEarlyReturnPlan {
|
||||||
|
pub cond: ASTNode,
|
||||||
|
pub ret_expr: ASTNode,
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user