feat(joinir): Phase 47-A-IMPL - P3 Normalized infrastructure

Implement Pattern3 (if-sum) Normalized infrastructure, extending existing P2
StepSchedule and ShapeGuard systems.

Key changes:

1. StepSchedule generalization (P2 → P2/P3):
   - Renamed: pattern2_step_schedule.rs → step_schedule.rs
   - Extended StepKind enum with P3 variants:
     - IfCond (if condition in body)
     - ThenUpdates (carrier updates in then branch)
     - ElseUpdates (carrier updates in else branch)
   - Added pattern3_if_sum_schedule() function
   - Added unit test: test_pattern3_if_sum_schedule()
   - Updated module references (mod.rs, loop_with_break_minimal.rs)

2. ShapeGuard extension:
   - Added Pattern3IfSumMinimal variant to NormalizedDevShape
   - Added is_pattern3_if_sum_minimal() detector (placeholder)
   - Updated shape detector table
   - Extended capability_for_shape() mapping

3. Bridge integration:
   - bridge.rs: Added P3 shape handling in normalize_for_shape()
   - normalized.rs: Added P3 roundtrip match (uses P2 temporarily)

4. P2/P3 separation:
   - loop_with_break_minimal.rs: Added panic for P3 steps in P2 lowering
   - Clear boundary enforcement (P2 lowerer rejects P3 steps)

5. Documentation:
   - CURRENT_TASK.md: Phase 47-A-IMPL status
   - phase47-norm-p3-design.md: Implementation status section

Benefits:
- Reuses 90% of P2 infrastructure (ConditionEnv, CarrierInfo, ExitLine)
- Clean P2/P3 separation via StepKind
- Pure additive changes (no P2 behavioral changes)
- Ready for Phase 47-A-LOWERING (full P3 Normalized implementation)

Tests: 938/938 PASS (+1 from step_schedule unit test)
- All existing P1/P2 tests pass (no regressions)
- P3 test uses Structured path temporarily (proper lowering in next phase)

Next phase: Implement full P3 Normalized→MIR(direct) lowering
This commit is contained in:
nyash-codex
2025-12-12 05:23:18 +09:00
parent 42ecd7a7e7
commit c3a26e705e
8 changed files with 107 additions and 12 deletions

View File

@ -87,6 +87,13 @@
- JsonParser P2 ライン_skip_whitespace/_atoi/_parse_number全て canonical Normalized 化完了。 - JsonParser P2 ライン_skip_whitespace/_atoi/_parse_number全て canonical Normalized 化完了。
- P3/P4 Normalized 対応は NORM-P3/NORM-P4 フェーズで実施(今回スコープ外)。 - P3/P4 Normalized 対応は NORM-P3/NORM-P4 フェーズで実施(今回スコープ外)。
- 937/937 tests PASS。 - 937/937 tests PASS。
- **Phase 47-A-IMPL実装中 2025-12-12**:
- StepScheduleBox 汎用化: pattern2_step_schedule.rs → step_schedule.rs リネーム
- P3 StepKind 追加: IfCond, ThenUpdates, ElseUpdatesP2 lowering との分離完了)
- ShapeGuard: Pattern3IfSumMinimal 検出追加placeholder stub実装
- Normalized bridge: P3 shape handling 追加P2 normalization 使用)
- 938/938 tests PASSregression なし)
- 次ステップ: Phase 47-A-LOWERING で full P3 Normalized lowering 実装
- **Phase 45-NORM-MODE実装済み✅ 2025-12-12**: - **Phase 45-NORM-MODE実装済み✅ 2025-12-12**:
- JoinIR モード一本化: バラバラだったフラグ/feature を `JoinIrMode` enum に集約StructuredOnly / NormalizedDev / NormalizedCanonical - JoinIR モード一本化: バラバラだったフラグ/feature を `JoinIrMode` enum に集約StructuredOnly / NormalizedDev / NormalizedCanonical
- `current_joinir_mode()` でモード取得、bridge/runner で `normalized_dev_enabled()` → mode pattern matching に移行。 - `current_joinir_mode()` でモード取得、bridge/runner で `normalized_dev_enabled()` → mode pattern matching に移行。

View File

@ -155,6 +155,27 @@ loop(i < arr.length()) {
Complex P3 patterns from selfhost compiler (deferred to later phase). Complex P3 patterns from selfhost compiler (deferred to later phase).
## Implementation Status
**Phase 47-A-PREP** (✅ Complete, commit 42ecd7a7):
- Fixture added: `pattern3_if_sum_minimal` in `normalized/fixtures.rs`
- Test stub added: `test_normalized_pattern3_if_sum_minimal_runner_dev_switch_matches_structured`
- Basic infrastructure for P3 development mode testing
**Phase 47-A-IMPL** (✅ Complete, 2025-12-12):
- ✅ StepSchedule renamed and extended: `pattern2_step_schedule.rs``step_schedule.rs`
- ✅ P3 StepKind added: `IfCond`, `ThenUpdates`, `ElseUpdates`
- ✅ Pattern2 lowering separation: P3 steps panic in P2 lowering (clean boundary)
- ✅ ShapeGuard: `Pattern3IfSumMinimal` detection added (placeholder stub)
- ✅ Normalized bridge: P3 shape handling in `normalize_for_shape()` and roundtrip
- ✅ 938/938 tests PASS (no regressions)
- ⏳ TODO: Full P3 Normalized lowering (Phase 47-A-LOWERING)
**Next Phase** (Phase 47-A-LOWERING):
- Implement `lower_pattern3_if_sum_minimal()` fully
- Generate Normalized JpInst for P3 structure (If branching, conditional updates)
- Test VM output comparison (Normalized vs Structured)
## Implementation Strategy ## Implementation Strategy
### Phase 47-A: Minimal sum_count (dev-only) ### Phase 47-A: Minimal sum_count (dev-only)

View File

@ -70,7 +70,7 @@ use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv; use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv;
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape; use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
use crate::mir::join_ir::lowering::loop_update_analyzer::UpdateExpr; use crate::mir::join_ir::lowering::loop_update_analyzer::UpdateExpr;
use crate::mir::join_ir::lowering::pattern2_step_schedule::{ use crate::mir::join_ir::lowering::step_schedule::{
build_pattern2_schedule, Pattern2ScheduleContext, Pattern2StepKind, build_pattern2_schedule, Pattern2ScheduleContext, Pattern2StepKind,
}; };
use crate::mir::join_ir::lowering::update_env::UpdateEnv; use crate::mir::join_ir::lowering::update_env::UpdateEnv;
@ -667,6 +667,10 @@ pub(crate) fn lower_loop_with_break_minimal(
Pattern2StepKind::BreakCheck => loop_step_func.body.append(&mut break_block), Pattern2StepKind::BreakCheck => loop_step_func.body.append(&mut break_block),
Pattern2StepKind::Updates => loop_step_func.body.append(&mut carrier_update_block), Pattern2StepKind::Updates => loop_step_func.body.append(&mut carrier_update_block),
Pattern2StepKind::Tail => loop_step_func.body.append(&mut tail_block), Pattern2StepKind::Tail => loop_step_func.body.append(&mut tail_block),
// Phase 47-A: P3 steps not used in P2 lowering (handled in Pattern3 lowerer)
Pattern2StepKind::IfCond | Pattern2StepKind::ThenUpdates | Pattern2StepKind::ElseUpdates => {
panic!("Pattern3 step kinds should not appear in Pattern2 lowering");
}
} }
} }

View File

@ -61,7 +61,7 @@ pub(crate) mod loop_view_builder; // Phase 33-23: Loop lowering dispatch
pub mod loop_with_break_minimal; // Phase 188-Impl-2: Pattern 2 minimal lowerer pub mod loop_with_break_minimal; // Phase 188-Impl-2: Pattern 2 minimal lowerer
pub mod loop_with_continue_minimal; pub mod loop_with_continue_minimal;
pub mod method_call_lowerer; // Phase 224-B: MethodCall lowering (metadata-driven) pub mod method_call_lowerer; // Phase 224-B: MethodCall lowering (metadata-driven)
pub(crate) mod pattern2_step_schedule; // Phase 39: Pattern2 evaluation order scheduler pub(crate) mod step_schedule; // Phase 47-A: Generic step scheduler for P2/P3 (renamed from pattern2_step_schedule)
pub mod method_return_hint; // Phase 83: P3-D 既知メソッド戻り値型推論箱 pub mod method_return_hint; // Phase 83: P3-D 既知メソッド戻り値型推論箱
pub mod scope_manager; // Phase 231: Unified variable scope management // Phase 195: Pattern 4 minimal lowerer pub mod scope_manager; // Phase 231: Unified variable scope management // Phase 195: Pattern 4 minimal lowerer
// Phase 242-EX-A: loop_with_if_phi_minimal removed - replaced by loop_with_if_phi_if_sum // Phase 242-EX-A: loop_with_if_phi_minimal removed - replaced by loop_with_if_phi_if_sum

View File

@ -1,9 +1,11 @@
//! Pattern 2 step scheduler (StepScheduleBox). //! Phase 47-A: Generic step scheduling for Pattern2/Pattern3 loops
//! //!
//! Decides the evaluation order for Pattern 2 lowering without hardcoding it //! Determines evaluation order for loop steps (header cond, body init, break check, etc).
//! inside the lowerer. This keeps the lowerer focused on emitting fragments, //! Used by both P2 (break) and P3 (if-sum) patterns.
//! while this box decides how to interleave them (e.g., body-local init before //!
//! break checks when the break depends on body-local values). //! This keeps the lowerer focused on emitting fragments, while this box decides
//! how to interleave them (e.g., body-local init before break checks when the
//! break depends on body-local values).
use crate::config::env; use crate::config::env;
use crate::config::env::joinir_dev::joinir_test_debug_enabled; use crate::config::env::joinir_dev::joinir_test_debug_enabled;
@ -13,11 +15,17 @@ use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv;
/// Steps that can be reordered by the scheduler. /// Steps that can be reordered by the scheduler.
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Pattern2StepKind { pub(crate) enum Pattern2StepKind {
HeaderCond, // P2 (Pattern2 Break) steps
BodyInit, HeaderCond, // loop(cond)
BreakCheck, BodyInit, // local ch = ...
Updates, BreakCheck, // if (cond) break
Tail, Updates, // sum = sum + 1
Tail, // i = i + 1
// Phase 47-A: P3 (Pattern3 If-Sum) steps
IfCond, // if (cond) in body
ThenUpdates, // carrier updates in then branch
ElseUpdates, // carrier updates in else branch (if any)
} }
impl Pattern2StepKind { impl Pattern2StepKind {
@ -28,6 +36,10 @@ impl Pattern2StepKind {
Pattern2StepKind::BreakCheck => "break", Pattern2StepKind::BreakCheck => "break",
Pattern2StepKind::Updates => "updates", Pattern2StepKind::Updates => "updates",
Pattern2StepKind::Tail => "tail", Pattern2StepKind::Tail => "tail",
// Phase 47-A: P3 steps
Pattern2StepKind::IfCond => "if-cond",
Pattern2StepKind::ThenUpdates => "then-updates",
Pattern2StepKind::ElseUpdates => "else-updates",
} }
} }
} }
@ -136,6 +148,17 @@ fn log_schedule(ctx: &Pattern2ScheduleContext, schedule: &Pattern2StepSchedule)
); );
} }
/// Phase 47-A: Generate step schedule for Pattern3 (if-sum) loops
pub(crate) fn pattern3_if_sum_schedule() -> Vec<Pattern2StepKind> {
vec![
Pattern2StepKind::HeaderCond, // loop(i < n)
Pattern2StepKind::IfCond, // if (i > 0)
Pattern2StepKind::ThenUpdates, // sum = sum + i
// ElseUpdates omitted for minimal (no else branch)
Pattern2StepKind::Tail, // i = i + 1
]
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -221,4 +244,14 @@ mod tests {
); );
assert_eq!(schedule.reason(), "body-local break dependency"); assert_eq!(schedule.reason(), "body-local break dependency");
} }
#[test]
fn test_pattern3_if_sum_schedule() {
let schedule = pattern3_if_sum_schedule();
assert_eq!(schedule.len(), 4);
assert_eq!(schedule[0], Pattern2StepKind::HeaderCond);
assert_eq!(schedule[1], Pattern2StepKind::IfCond);
assert_eq!(schedule[2], Pattern2StepKind::ThenUpdates);
assert_eq!(schedule[3], Pattern2StepKind::Tail);
}
} }

View File

@ -905,6 +905,11 @@ pub(crate) fn normalized_dev_roundtrip_structured(
let norm = normalize_pattern2_minimal(module); let norm = normalize_pattern2_minimal(module);
normalized_pattern2_to_structured(&norm) normalized_pattern2_to_structured(&norm)
})), })),
// Phase 47-A: P3 minimal uses P2 normalization for now
NormalizedDevShape::Pattern3IfSumMinimal => catch_unwind(AssertUnwindSafe(|| {
let norm = normalize_pattern2_minimal(module);
normalized_pattern2_to_structured(&norm)
})),
}; };
match attempt { match attempt {

View File

@ -50,6 +50,8 @@ pub enum NormalizedDevShape {
JsonparserAtoiMini, JsonparserAtoiMini,
JsonparserAtoiReal, JsonparserAtoiReal,
JsonparserParseNumberReal, JsonparserParseNumberReal,
// Phase 47-A: Pattern3 (if-sum) minimal
Pattern3IfSumMinimal,
} }
type Detector = fn(&JoinModule) -> bool; type Detector = fn(&JoinModule) -> bool;
@ -77,6 +79,11 @@ const SHAPE_DETECTORS: &[(NormalizedDevShape, Detector)] = &[
NormalizedDevShape::JsonparserParseNumberReal, NormalizedDevShape::JsonparserParseNumberReal,
detectors::is_jsonparser_parse_number_real, detectors::is_jsonparser_parse_number_real,
), ),
// Phase 47-A: Pattern3 if-sum minimal
(
NormalizedDevShape::Pattern3IfSumMinimal,
detectors::is_pattern3_if_sum_minimal,
),
]; ];
/// direct ブリッジで扱う shapedev 限定)。 /// direct ブリッジで扱う shapedev 限定)。
@ -104,6 +111,8 @@ pub fn capability_for_shape(shape: &NormalizedDevShape) -> ShapeCapability {
JsonparserAtoiMini | JsonparserAtoiReal => P2CoreAtoi, JsonparserAtoiMini | JsonparserAtoiReal => P2CoreAtoi,
JsonparserParseNumberReal => P2MidParseNumber, JsonparserParseNumberReal => P2MidParseNumber,
Pattern1Mini => P2CoreSimple, // Also core simple pattern Pattern1Mini => P2CoreSimple, // Also core simple pattern
// Phase 47-A: P3 minimal maps to P2CoreSimple for now (future: P3CoreSimple)
Pattern3IfSumMinimal => P2CoreSimple,
}; };
ShapeCapability::new(kind) ShapeCapability::new(kind)
@ -338,6 +347,18 @@ mod detectors {
.any(|f| f.name == "jsonparser_parse_number_real") .any(|f| f.name == "jsonparser_parse_number_real")
} }
/// Phase 47-A: Check if module matches Pattern3 if-sum minimal shape
pub(crate) fn is_pattern3_if_sum_minimal(module: &JoinModule) -> bool {
// For now, simple heuristic:
// - Has Pattern3 structure (if-sum)
// - Single carrier (sum)
// - Simple loop condition
// TODO: Implement proper detection based on fixture
// For Phase 47-A, this will be called explicitly in tests
false // Placeholder - will be refined in later commits
}
pub(super) fn find_loop_step(module: &JoinModule) -> Option<&JoinFunction> { pub(super) fn find_loop_step(module: &JoinModule) -> Option<&JoinFunction> {
module module
.functions .functions

View File

@ -72,6 +72,10 @@ fn normalize_for_shape(
| NormalizedDevShape::JsonparserParseNumberReal => { | NormalizedDevShape::JsonparserParseNumberReal => {
catch_unwind(AssertUnwindSafe(|| normalize_pattern2_minimal(module))) catch_unwind(AssertUnwindSafe(|| normalize_pattern2_minimal(module)))
} }
// Phase 47-A: P3 minimal normalization (stub - will use P2 for now)
NormalizedDevShape::Pattern3IfSumMinimal => {
catch_unwind(AssertUnwindSafe(|| normalize_pattern2_minimal(module)))
}
}; };
match result { match result {