feat(joinir): Phase 248 - Normalized JoinIR infrastructure
Major refactoring of JoinIR normalization pipeline: Key changes: - Structured→Normalized→MIR(direct) pipeline established - ShapeGuard enhanced with Pattern2 loop validation - dev_env.rs: New development fixtures and env control - fixtures.rs: jsonparser_parse_number_real fixture - normalized_bridge/direct.rs: Direct MIR generation from Normalized - pattern2_step_schedule.rs: Extracted step scheduling logic Files changed: - normalized.rs: Enhanced NormalizedJoinModule with DevEnv support - shape_guard.rs: Pattern2-specific validation (+300 lines) - normalized_bridge.rs: Unified bridge with direct path - loop_with_break_minimal.rs: Integrated step scheduling - Deleted: step_schedule.rs (moved to pattern2_step_schedule.rs) New files: - param_guess.rs: Loop parameter inference - pattern2_step_schedule.rs: Step scheduling for Pattern2 - phase43-norm-canon-p2-mid.md: Design doc Tests: 937/937 PASS (+6 from baseline 931) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -93,6 +93,8 @@ pub enum JpOp {
|
||||
Unary(UnaryOp),
|
||||
Compare(CompareOp),
|
||||
BoxCall { box_name: String, method: String },
|
||||
/// 三項演算子(cond ? then : else)
|
||||
Select,
|
||||
}
|
||||
|
||||
/// Normalized JoinIR モジュール(テスト専用)。
|
||||
@ -115,7 +117,7 @@ impl NormalizedModule {
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
fn verify_normalized_pattern1(module: &NormalizedModule) -> Result<(), String> {
|
||||
if module.phase != JoinIrPhase::Normalized {
|
||||
return Err("Normalized verifier: phase must be Normalized".to_string());
|
||||
return Err("[joinir/normalized-dev] pattern1: phase must be Normalized".to_string());
|
||||
}
|
||||
|
||||
// Env field bounds check
|
||||
@ -127,7 +129,7 @@ fn verify_normalized_pattern1(module: &NormalizedModule) -> Result<(), String> {
|
||||
JpInst::EnvLoad { field, .. } | JpInst::EnvStore { field, .. } => {
|
||||
if *field >= field_count {
|
||||
return Err(format!(
|
||||
"Env field out of range: {} (fields={})",
|
||||
"[joinir/normalized-dev] pattern1: env field out of range: {} (fields={})",
|
||||
field, field_count
|
||||
));
|
||||
}
|
||||
@ -156,7 +158,7 @@ fn verify_normalized_pattern1(module: &NormalizedModule) -> Result<(), String> {
|
||||
JpInst::TailCallFn { .. } | JpInst::TailCallKont { .. } | JpInst::If { .. } => {}
|
||||
_ => {
|
||||
return Err(format!(
|
||||
"Function '{}' does not end with tail call/if",
|
||||
"[joinir/normalized-dev] pattern1: function '{}' does not end with tail call/if",
|
||||
func.name
|
||||
))
|
||||
}
|
||||
@ -221,6 +223,12 @@ pub fn normalized_pattern1_to_structured(norm: &NormalizedModule) -> JoinModule
|
||||
op: *op,
|
||||
operand: args.get(0).copied().unwrap_or(ValueId(0)),
|
||||
})),
|
||||
JpOp::Select => func.body.push(JoinInst::Compute(MirLikeInst::Select {
|
||||
dst: *dst,
|
||||
cond: args.get(0).copied().unwrap_or(ValueId(0)),
|
||||
then_val: args.get(1).copied().unwrap_or(ValueId(0)),
|
||||
else_val: args.get(2).copied().unwrap_or(ValueId(0)),
|
||||
})),
|
||||
JpOp::Compare(op) => func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||
dst: *dst,
|
||||
op: *op,
|
||||
@ -300,8 +308,30 @@ pub fn normalize_pattern2_minimal(structured: &JoinModule) -> NormalizedModule {
|
||||
let mut max = 3;
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
{
|
||||
if shape_guard::is_jsonparser_atoi_mini(structured) {
|
||||
max = 8;
|
||||
let shapes = shape_guard::supported_shapes(structured);
|
||||
if shapes
|
||||
.iter()
|
||||
.any(|s| matches!(s, NormalizedDevShape::JsonparserAtoiMini))
|
||||
{
|
||||
max = max.max(8);
|
||||
}
|
||||
if shapes
|
||||
.iter()
|
||||
.any(|s| matches!(s, NormalizedDevShape::JsonparserAtoiReal))
|
||||
{
|
||||
max = max.max(10);
|
||||
}
|
||||
if shapes
|
||||
.iter()
|
||||
.any(|s| matches!(s, NormalizedDevShape::JsonparserParseNumberReal))
|
||||
{
|
||||
max = max.max(12);
|
||||
}
|
||||
if shapes
|
||||
.iter()
|
||||
.any(|s| matches!(s, NormalizedDevShape::JsonparserSkipWsReal))
|
||||
{
|
||||
max = max.max(6);
|
||||
}
|
||||
}
|
||||
max
|
||||
@ -397,6 +427,18 @@ pub fn normalize_pattern2_minimal(structured: &JoinModule) -> NormalizedModule {
|
||||
args: vec![*lhs, *rhs],
|
||||
})
|
||||
}
|
||||
JoinInst::Compute(MirLikeInst::Select {
|
||||
dst,
|
||||
cond,
|
||||
then_val,
|
||||
else_val,
|
||||
}) => {
|
||||
body.push(JpInst::Let {
|
||||
dst: *dst,
|
||||
op: JpOp::Select,
|
||||
args: vec![*cond, *then_val, *else_val],
|
||||
})
|
||||
}
|
||||
JoinInst::Jump { cont, args, cond } => {
|
||||
if let Some(cond_val) = cond {
|
||||
body.push(JpInst::If {
|
||||
@ -412,6 +454,19 @@ pub fn normalize_pattern2_minimal(structured: &JoinModule) -> NormalizedModule {
|
||||
});
|
||||
}
|
||||
}
|
||||
JoinInst::Select {
|
||||
dst,
|
||||
cond,
|
||||
then_val,
|
||||
else_val,
|
||||
..
|
||||
} => {
|
||||
body.push(JpInst::Let {
|
||||
dst: *dst,
|
||||
op: JpOp::Select,
|
||||
args: vec![*cond, *then_val, *else_val],
|
||||
});
|
||||
}
|
||||
JoinInst::Call { func, args, k_next, .. } => {
|
||||
if k_next.is_none() {
|
||||
body.push(JpInst::TailCallFn {
|
||||
@ -523,6 +578,14 @@ pub fn normalized_pattern2_to_structured(norm: &NormalizedModule) -> JoinModule
|
||||
op: *op,
|
||||
operand: args.get(0).copied().unwrap_or(ValueId(0)),
|
||||
})),
|
||||
JpOp::Select => func
|
||||
.body
|
||||
.push(JoinInst::Compute(MirLikeInst::Select {
|
||||
dst: *dst,
|
||||
cond: args.get(0).copied().unwrap_or(ValueId(0)),
|
||||
then_val: args.get(1).copied().unwrap_or(ValueId(0)),
|
||||
else_val: args.get(2).copied().unwrap_or(ValueId(0)),
|
||||
})),
|
||||
JpOp::Compare(op) => func.body.push(JoinInst::Compute(MirLikeInst::Compare {
|
||||
dst: *dst,
|
||||
op: *op,
|
||||
@ -578,7 +641,9 @@ fn verify_normalized_pattern2(
|
||||
max_env_fields: usize,
|
||||
) -> Result<(), String> {
|
||||
if module.phase != JoinIrPhase::Normalized {
|
||||
return Err("Normalized verifier (Pattern2): phase must be Normalized".to_string());
|
||||
return Err(
|
||||
"[joinir/normalized-dev] pattern2: phase must be Normalized".to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
let mut layout_sizes: HashMap<u32, usize> = HashMap::new();
|
||||
@ -586,7 +651,7 @@ fn verify_normalized_pattern2(
|
||||
let size = layout.fields.len();
|
||||
if !(1..=max_env_fields).contains(&size) {
|
||||
return Err(format!(
|
||||
"Normalized Pattern2 expects 1..={} env fields, got {}",
|
||||
"[joinir/normalized-dev] pattern2: expected 1..={} env fields, got {}",
|
||||
max_env_fields, size
|
||||
));
|
||||
}
|
||||
@ -615,7 +680,10 @@ fn verify_normalized_pattern2(
|
||||
| JpInst::If { env, .. } => {
|
||||
if let Some(expected) = expected_env_len {
|
||||
if env.is_empty() {
|
||||
return Err("Normalized Pattern2 env must not be empty".to_string());
|
||||
return Err(
|
||||
"[joinir/normalized-dev] pattern2: env must not be empty"
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
let _ = expected;
|
||||
}
|
||||
@ -631,7 +699,7 @@ fn verify_normalized_pattern2(
|
||||
| JpInst::If { .. } => {}
|
||||
_ => {
|
||||
return Err(format!(
|
||||
"Function '{}' does not end with tail call/if",
|
||||
"[joinir/normalized-dev] pattern2: function '{}' does not end with tail call/if",
|
||||
func.name
|
||||
));
|
||||
}
|
||||
@ -813,11 +881,14 @@ pub(crate) fn normalized_dev_roundtrip_structured(
|
||||
return Err("[joinir/normalized-dev] module shape is not supported by normalized_dev".into());
|
||||
}
|
||||
|
||||
let verbose = crate::config::env::joinir_dev_enabled();
|
||||
let debug = dev_env::normalized_dev_logs_enabled() && crate::config::env::joinir_dev_enabled();
|
||||
|
||||
for shape in shapes {
|
||||
if verbose {
|
||||
eprintln!("[joinir/normalized-dev] attempting {:?} normalization", shape);
|
||||
if debug {
|
||||
eprintln!(
|
||||
"[joinir/normalized-dev/roundtrip] attempting {:?} normalization",
|
||||
shape
|
||||
);
|
||||
}
|
||||
|
||||
let attempt = match shape {
|
||||
@ -827,7 +898,10 @@ pub(crate) fn normalized_dev_roundtrip_structured(
|
||||
})),
|
||||
NormalizedDevShape::Pattern2Mini
|
||||
| NormalizedDevShape::JsonparserSkipWsMini
|
||||
| NormalizedDevShape::JsonparserAtoiMini => catch_unwind(AssertUnwindSafe(|| {
|
||||
| NormalizedDevShape::JsonparserSkipWsReal
|
||||
| NormalizedDevShape::JsonparserAtoiMini
|
||||
| NormalizedDevShape::JsonparserAtoiReal
|
||||
| NormalizedDevShape::JsonparserParseNumberReal => catch_unwind(AssertUnwindSafe(|| {
|
||||
let norm = normalize_pattern2_minimal(module);
|
||||
normalized_pattern2_to_structured(&norm)
|
||||
})),
|
||||
@ -835,9 +909,9 @@ pub(crate) fn normalized_dev_roundtrip_structured(
|
||||
|
||||
match attempt {
|
||||
Ok(structured) => {
|
||||
if verbose {
|
||||
if debug {
|
||||
eprintln!(
|
||||
"[joinir/normalized-dev] {:?} normalization succeeded (functions={})",
|
||||
"[joinir/normalized-dev/roundtrip] {:?} normalization succeeded (functions={})",
|
||||
shape,
|
||||
structured.functions.len()
|
||||
);
|
||||
@ -845,9 +919,9 @@ pub(crate) fn normalized_dev_roundtrip_structured(
|
||||
return Ok(structured);
|
||||
}
|
||||
Err(_) => {
|
||||
if verbose {
|
||||
if debug {
|
||||
eprintln!(
|
||||
"[joinir/normalized-dev] {:?} normalization failed (unsupported)",
|
||||
"[joinir/normalized-dev/roundtrip] {:?} normalization failed (unsupported)",
|
||||
shape
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user