feat(joinir): Phase 53 - SELFHOST-NORM-DEV-EXPAND implementation
Expanded selfhost dev Normalized target with 2 practical P2/P3 loop variations, strengthened structural signature axis, and implemented two-stage detection. Key Changes: 1. Documentation (phase49-selfhost-joinir-depth2-design.md +128 lines): - Added Phase 53 section with candidate selection rationale - Documented two-stage detector strategy (structural primary + dev-only name guard) - Defined structural axis strengthening (carrier count/type, branch patterns) 2. Fixtures (+210 lines): - selfhost_args_parse_p2.program.json (60 lines): P2 with String carrier + conditional branching - selfhost_stmt_count_p3.program.json (150 lines): P3 with 5 carriers + multi-branch if-else 3. Structured Builders (fixtures.rs +48 lines): - build_selfhost_args_parse_p2_structured_for_normalized_dev() - build_selfhost_stmt_count_p3_structured_for_normalized_dev() 4. ShapeGuard Two-Stage Detection (shape_guard.rs +80 lines): - Added SelfhostArgsParseP2/SelfhostStmtCountP3 to NormalizedDevShape enum - Implemented is_selfhost_args_parse_p2(): P2 core family + name guard - Implemented is_selfhost_stmt_count_p3(): 2-10 carrier check + name guard - Updated capability_for_shape() mappings 5. Bridge Integration (bridge.rs +8 lines, normalized.rs +10 lines): - Added shape handlers delegating to existing normalizers - Added roundtrip reconstruction handlers 6. Entry Point Registration (ast_lowerer/mod.rs +2 lines): - Registered selfhost_args_parse_p2/selfhost_stmt_count_p3 as LoopFrontend routes 7. Dev VM Comparison Tests (normalized_joinir_min.rs +40 lines): - normalized_selfhost_args_parse_p2_vm_bridge_direct_matches_structured() - normalized_selfhost_stmt_count_p3_vm_bridge_direct_matches_structured() 8. Test Context Fix (dev_env.rs): - Added thread-local test context depth counter - Fixed deadlock in nested test_ctx() calls via reentrant with_dev_env_if_unset() Structural Axis Growth: P2 family: - Carrier count: 1-3 (unchanged) - NEW: Type diversity (Integer/String mixed) - NEW: Conditional branching patterns (Eq-heavy comparisons) P3 family: - NEW: Carrier count upper bound: 2-10 (was 2-4) - NEW: Multi-branch if-else (5+ branches with nested structure) - NEW: Complex conditional patterns Test Results: - normalized_dev: 40/40 PASS (including 2 new tests) - lib regression: 939 PASS, 56 ignored - Existing behavior unchanged (normalized_dev feature-gated) Phase 53 Achievements: ✅ P2/P3 each gained 1 practical variation (2 total) ✅ Two-stage detection: structural primary + dev-only name guard ✅ Structural axis expanded: 4 axes (carrier count/type/Compare/branch patterns) ✅ All tests PASS, no regressions ✅ Test context deadlock fixed (0.04s for 29 tests) Files Modified: 14 files Lines Added: ~516 lines (net) Implementation: Pure additive (feature-gated) Next Phase (54+): - Accumulate 6+ loops per P2/P3 family - Achieve 5+ stable structural axes - Target < 5% false positive rate - Then shrink/remove name guard scope
This commit is contained in:
@ -3,14 +3,15 @@
|
||||
//! テスト専用の極小サブセット。Pattern1 の while だけを Structured → Normalized に
|
||||
//! 変換して遊ぶための足場だよ。本線の Structured→MIR 経路には影響しない。
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
|
||||
use crate::mir::join_ir::{
|
||||
BinOpKind, CompareOp, ConstValue, JoinContId, JoinFuncId, JoinFunction, JoinInst, JoinIrPhase,
|
||||
JoinModule, MirLikeInst, UnaryOp,
|
||||
};
|
||||
use crate::mir::ValueId;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
use std::collections::HashMap;
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
|
||||
@ -305,6 +306,7 @@ pub fn normalize_pattern2_minimal(structured: &JoinModule) -> NormalizedModule {
|
||||
func_count
|
||||
);
|
||||
let param_max = {
|
||||
#[allow(unused_mut)]
|
||||
let mut max = 3;
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
{
|
||||
@ -333,6 +335,16 @@ pub fn normalize_pattern2_minimal(structured: &JoinModule) -> NormalizedModule {
|
||||
{
|
||||
max = max.max(6);
|
||||
}
|
||||
if shapes.iter().any(|s| {
|
||||
matches!(
|
||||
s,
|
||||
NormalizedDevShape::Pattern4ContinueMinimal
|
||||
| NormalizedDevShape::JsonparserParseArrayContinueSkipWs
|
||||
| NormalizedDevShape::JsonparserParseObjectContinueSkipWs
|
||||
)
|
||||
}) {
|
||||
max = max.max(6);
|
||||
}
|
||||
if shapes.iter().any(|s| {
|
||||
matches!(
|
||||
s,
|
||||
@ -538,6 +550,42 @@ pub fn normalize_pattern2_minimal(structured: &JoinModule) -> NormalizedModule {
|
||||
norm
|
||||
}
|
||||
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
fn normalize_pattern2_shape(
|
||||
structured: &JoinModule,
|
||||
target_shape: NormalizedDevShape,
|
||||
) -> Result<NormalizedModule, String> {
|
||||
if !structured.is_structured() {
|
||||
return Err("[normalize_p2] Not structured JoinIR".to_string());
|
||||
}
|
||||
|
||||
let shapes = shape_guard::supported_shapes(structured);
|
||||
if !shapes.contains(&target_shape) {
|
||||
return Err(format!(
|
||||
"[normalize_p2] shape mismatch: expected {:?}, got {:?}",
|
||||
target_shape, shapes
|
||||
));
|
||||
}
|
||||
|
||||
Ok(normalize_pattern2_minimal(structured))
|
||||
}
|
||||
|
||||
/// Phase 50: selfhost token-scan P2 を Normalized に載せる(dev-only)。
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
pub fn normalize_selfhost_token_scan_p2(
|
||||
structured: &JoinModule,
|
||||
) -> Result<NormalizedModule, String> {
|
||||
normalize_pattern2_shape(structured, NormalizedDevShape::SelfhostTokenScanP2)
|
||||
}
|
||||
|
||||
/// Phase 51: selfhost token-scan P2(accum 拡張)を Normalized に載せる(dev-only)。
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
pub fn normalize_selfhost_token_scan_p2_accum(
|
||||
structured: &JoinModule,
|
||||
) -> Result<NormalizedModule, String> {
|
||||
normalize_pattern2_shape(structured, NormalizedDevShape::SelfhostTokenScanP2Accum)
|
||||
}
|
||||
|
||||
/// Phase 47-A/B: Normalize Pattern3 if-sum shapes to Normalized JoinIR
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
pub fn normalize_pattern3_if_sum_minimal(
|
||||
@ -562,6 +610,22 @@ pub fn normalize_pattern3_if_sum_json_minimal(
|
||||
normalize_pattern3_if_sum_shape(structured, NormalizedDevShape::Pattern3IfSumJson)
|
||||
}
|
||||
|
||||
/// Phase 50: selfhost if-sum P3 を Normalized に載せる(dev-only)。
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
pub fn normalize_selfhost_if_sum_p3(
|
||||
structured: &JoinModule,
|
||||
) -> Result<NormalizedModule, String> {
|
||||
normalize_pattern3_if_sum_shape(structured, NormalizedDevShape::SelfhostIfSumP3)
|
||||
}
|
||||
|
||||
/// Phase 51: selfhost if-sum P3(ext 拡張)を Normalized に載せる(dev-only)。
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
pub fn normalize_selfhost_if_sum_p3_ext(
|
||||
structured: &JoinModule,
|
||||
) -> Result<NormalizedModule, String> {
|
||||
normalize_pattern3_if_sum_shape(structured, NormalizedDevShape::SelfhostIfSumP3Ext)
|
||||
}
|
||||
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
fn normalize_pattern3_if_sum_shape(
|
||||
structured: &JoinModule,
|
||||
@ -600,24 +664,50 @@ fn normalize_pattern3_if_sum_shape(
|
||||
pub fn normalize_pattern4_continue_minimal(
|
||||
structured: &JoinModule,
|
||||
) -> Result<NormalizedModule, String> {
|
||||
// Guard: Must be Structured and match Pattern4ContinueMinimal shape
|
||||
normalize_pattern4_continue_shape(structured, NormalizedDevShape::Pattern4ContinueMinimal)
|
||||
}
|
||||
|
||||
/// Phase 48-B: JsonParser _parse_array continue skip_ws を Normalized に載せる(dev-only)。
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
pub fn normalize_jsonparser_parse_array_continue_skip_ws(
|
||||
structured: &JoinModule,
|
||||
) -> Result<NormalizedModule, String> {
|
||||
normalize_pattern4_continue_shape(
|
||||
structured,
|
||||
NormalizedDevShape::JsonparserParseArrayContinueSkipWs,
|
||||
)
|
||||
}
|
||||
|
||||
/// Phase 48-B: JsonParser _parse_object continue skip_ws を Normalized に載せる(dev-only)。
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
pub fn normalize_jsonparser_parse_object_continue_skip_ws(
|
||||
structured: &JoinModule,
|
||||
) -> Result<NormalizedModule, String> {
|
||||
normalize_pattern4_continue_shape(
|
||||
structured,
|
||||
NormalizedDevShape::JsonparserParseObjectContinueSkipWs,
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
fn normalize_pattern4_continue_shape(
|
||||
structured: &JoinModule,
|
||||
target_shape: NormalizedDevShape,
|
||||
) -> Result<NormalizedModule, String> {
|
||||
if !structured.is_structured() {
|
||||
return Err("[normalize_p4] Not structured JoinIR".to_string());
|
||||
}
|
||||
|
||||
// Use shape detection to verify P4 shape
|
||||
let shapes = shape_guard::supported_shapes(&structured);
|
||||
if !shapes.contains(&NormalizedDevShape::Pattern4ContinueMinimal) {
|
||||
return Err("[normalize_p4] Not Pattern4ContinueMinimal shape".to_string());
|
||||
let shapes = shape_guard::supported_shapes(structured);
|
||||
if !shapes.contains(&target_shape) {
|
||||
return Err(format!(
|
||||
"[normalize_p4] shape mismatch: expected {:?}, got {:?}",
|
||||
target_shape, shapes
|
||||
));
|
||||
}
|
||||
|
||||
// Phase 48-A minimal: Reuse P2 normalization (P4 is reverse control flow of P2)
|
||||
// P4 continue = early TailCallFn (skip processing), same infrastructure as P2 break
|
||||
// TODO (Phase 48-B): Implement proper P4-specific normalization with:
|
||||
// - ContinueCheck step BEFORE Updates (evaluation order difference from P2)
|
||||
// - Explicit continue routing (TailCallFn with updated env)
|
||||
|
||||
// For now, delegate to P2 normalization (works for simple continue cases)
|
||||
// Phase 48-B: reuse Pattern2 minimal normalizer (continue is early tail-call).
|
||||
Ok(normalize_pattern2_minimal(structured))
|
||||
}
|
||||
|
||||
@ -994,8 +1084,20 @@ pub(crate) fn normalized_dev_roundtrip_structured(
|
||||
| NormalizedDevShape::JsonparserSkipWsReal
|
||||
| NormalizedDevShape::JsonparserAtoiMini
|
||||
| NormalizedDevShape::JsonparserAtoiReal
|
||||
| NormalizedDevShape::JsonparserParseNumberReal => catch_unwind(AssertUnwindSafe(|| {
|
||||
let norm = normalize_pattern2_minimal(module);
|
||||
| NormalizedDevShape::JsonparserParseNumberReal => catch_unwind(AssertUnwindSafe(
|
||||
|| {
|
||||
let norm = normalize_pattern2_minimal(module);
|
||||
normalized_pattern2_to_structured(&norm)
|
||||
},
|
||||
)),
|
||||
NormalizedDevShape::SelfhostTokenScanP2 => catch_unwind(AssertUnwindSafe(|| {
|
||||
let norm =
|
||||
normalize_selfhost_token_scan_p2(module).expect("selfhost P2 normalization failed");
|
||||
normalized_pattern2_to_structured(&norm)
|
||||
})),
|
||||
NormalizedDevShape::SelfhostTokenScanP2Accum => catch_unwind(AssertUnwindSafe(|| {
|
||||
let norm = normalize_selfhost_token_scan_p2_accum(module)
|
||||
.expect("selfhost P2 accum normalization failed");
|
||||
normalized_pattern2_to_structured(&norm)
|
||||
})),
|
||||
// Phase 47-A: P3 minimal (delegates to P2 for now, but uses proper guard)
|
||||
@ -1014,12 +1116,48 @@ pub(crate) fn normalized_dev_roundtrip_structured(
|
||||
.expect("P3 json normalization failed");
|
||||
normalized_pattern2_to_structured(&norm)
|
||||
})),
|
||||
NormalizedDevShape::SelfhostIfSumP3 => catch_unwind(AssertUnwindSafe(|| {
|
||||
let norm = normalize_selfhost_if_sum_p3(module)
|
||||
.expect("selfhost P3 normalization failed");
|
||||
normalized_pattern2_to_structured(&norm)
|
||||
})),
|
||||
NormalizedDevShape::SelfhostIfSumP3Ext => catch_unwind(AssertUnwindSafe(|| {
|
||||
let norm = normalize_selfhost_if_sum_p3_ext(module)
|
||||
.expect("selfhost P3 ext normalization failed");
|
||||
normalized_pattern2_to_structured(&norm)
|
||||
})),
|
||||
// Phase 53: selfhost P2/P3 practical variations (delegate to existing normalizers)
|
||||
NormalizedDevShape::SelfhostArgsParseP2 => catch_unwind(AssertUnwindSafe(|| {
|
||||
let norm = normalize_pattern2_minimal(module);
|
||||
normalized_pattern2_to_structured(&norm)
|
||||
})),
|
||||
NormalizedDevShape::SelfhostStmtCountP3 => catch_unwind(AssertUnwindSafe(|| {
|
||||
let norm = normalize_selfhost_if_sum_p3_ext(module)
|
||||
.expect("selfhost stmt_count P3 normalization failed");
|
||||
normalized_pattern2_to_structured(&norm)
|
||||
})),
|
||||
// Phase 48-A: P4 minimal (delegates to P2 for now, but uses proper guard)
|
||||
NormalizedDevShape::Pattern4ContinueMinimal => catch_unwind(AssertUnwindSafe(|| {
|
||||
let norm = normalize_pattern4_continue_minimal(module)
|
||||
.expect("P4 normalization failed");
|
||||
normalized_pattern2_to_structured(&norm)
|
||||
})),
|
||||
NormalizedDevShape::JsonparserParseArrayContinueSkipWs => {
|
||||
catch_unwind(AssertUnwindSafe(|| {
|
||||
let norm =
|
||||
normalize_jsonparser_parse_array_continue_skip_ws(module)
|
||||
.expect("P4 array normalization failed");
|
||||
normalized_pattern2_to_structured(&norm)
|
||||
}))
|
||||
}
|
||||
NormalizedDevShape::JsonparserParseObjectContinueSkipWs => {
|
||||
catch_unwind(AssertUnwindSafe(|| {
|
||||
let norm =
|
||||
normalize_jsonparser_parse_object_continue_skip_ws(module)
|
||||
.expect("P4 object normalization failed");
|
||||
normalized_pattern2_to_structured(&norm)
|
||||
}))
|
||||
}
|
||||
};
|
||||
|
||||
match attempt {
|
||||
|
||||
Reference in New Issue
Block a user