Files
hakorune/docs/development/current/main/phase33-optimization-guide.md
nyash-codex 4e32a803a7 feat(joinir): Phase 33-22 CommonPatternInitializer & JoinIRConversionPipeline integration
Unifies initialization and conversion logic across all 4 loop patterns,
eliminating code duplication and establishing single source of truth.

## Changes

### Infrastructure (New)
- CommonPatternInitializer (117 lines): Unified loop var extraction + CarrierInfo building
- JoinIRConversionPipeline (127 lines): Unified JoinIR→MIR→Merge flow

### Pattern Refactoring
- Pattern 1: Uses CommonPatternInitializer + JoinIRConversionPipeline (-25 lines)
- Pattern 2: Uses CommonPatternInitializer + JoinIRConversionPipeline (-25 lines)
- Pattern 3: Uses CommonPatternInitializer + JoinIRConversionPipeline (-25 lines)
- Pattern 4: Uses CommonPatternInitializer + JoinIRConversionPipeline (-40 lines)

### Code Reduction
- Total reduction: ~115 lines across all patterns
- Zero code duplication in initialization/conversion
- Pattern files: 806 lines total (down from ~920)

### Quality Improvements
- Single source of truth for initialization
- Consistent conversion flow across all patterns
- Guaranteed boundary.loop_var_name setting (prevents SSA-undef bugs)
- Improved maintainability and testability

### Testing
- All 4 patterns tested and passing:
  - Pattern 1 (Simple While): 
  - Pattern 2 (With Break): 
  - Pattern 3 (If-Else PHI): 
  - Pattern 4 (With Continue): 

### Documentation
- Phase 33-22 inventory and results document
- Updated joinir-architecture-overview.md with new infrastructure

## Breaking Changes
None - pure refactoring with no API changes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2025-12-07 21:02:20 +09:00

14 KiB
Raw Blame History

Phase 33 最適化実装ガイド - Step by Step

所要時間: 2.5時間CommonPatternInitializer: 1h + JoinIRConversionPipeline: 1h + 検証: 0.5h 削減見込み: 351行patterns/モジュール: 200行 + merge/mod.rs: 31行 + パイプライン: 120行


🚀 Phase 1: CommonPatternInitializer箱化1時間

Step 1.1: 新規ファイル作成5分

cd /home/tomoaki/git/hakorune-selfhost

# 新規ファイル作成
cat > src/mir/builder/control_flow/joinir/patterns/common_init.rs << 'EOF'
//! Phase 33-22: Common Pattern Initializer Box
//!
//! Extracts duplicate initialization logic from Pattern 1-4 lowerers.
//!
//! # Purpose
//!
//! All 4 patterns (Pattern1Minimal, Pattern2WithBreak, Pattern3WithIfPhi, Pattern4WithContinue)
//! share the same initialization steps:
//! 1. Extract loop variable name from condition
//! 2. Look up ValueId in variable_map
//! 3. Trace variable map state
//!
//! This module provides a unified initializer to eliminate 200 lines of duplicate code.

use crate::ast::ASTNode;
use crate::mir::builder::MirBuilder;
use crate::mir::ValueId;
use super::super::trace;

/// Loop context extracted from condition and variable_map
#[derive(Debug, Clone)]
pub struct LoopContext {
    pub loop_var_name: String,
    pub loop_var_id: ValueId,
}

/// Common Pattern Initializer Box
pub struct CommonPatternInitializer;

impl CommonPatternInitializer {
    /// Extract loop context from condition
    ///
    /// # Arguments
    ///
    /// * `builder` - MirBuilder instance (for variable_map access)
    /// * `condition` - Loop condition AST node
    /// * `pattern_name` - Pattern identifier for error messages (e.g., "pattern1", "pattern2")
    ///
    /// # Returns
    ///
    /// LoopContext containing loop_var_name and loop_var_id
    ///
    /// # Example
    ///
    /// ```rust
    /// let ctx = CommonPatternInitializer::extract_loop_context(
    ///     builder,
    ///     condition,
    ///     "pattern1",
    /// )?;
    /// // ctx.loop_var_name = "i"
    /// // ctx.loop_var_id = ValueId(42)
    /// ```
    pub fn extract_loop_context(
        builder: &MirBuilder,
        condition: &ASTNode,
        pattern_name: &str,
    ) -> Result<LoopContext, String> {
        // Step 1: Extract loop variable name from condition (e.g., "i" from "i < 3")
        let loop_var_name = builder.extract_loop_variable_from_condition(condition)?;

        // Step 2: Look up ValueId in variable_map
        let loop_var_id = builder
            .variable_map
            .get(&loop_var_name)
            .copied()
            .ok_or_else(|| {
                format!(
                    "[cf_loop/{}] Loop variable '{}' not found in variable_map",
                    pattern_name, loop_var_name
                )
            })?;

        // Step 3: Trace variable map state for debugging
        trace::trace().varmap(&format!("{}_start", pattern_name), &builder.variable_map);

        Ok(LoopContext {
            loop_var_name,
            loop_var_id,
        })
    }
}
EOF

# mod.rsに追加
echo "pub mod common_init;" >> src/mir/builder/control_flow/joinir/patterns/mod.rs

Step 1.2: Pattern 1に適用15分

Before (pattern1_minimal.rs:64-79):

let loop_var_name = self.extract_loop_variable_from_condition(condition)?;
let loop_var_id = self
    .variable_map
    .get(&loop_var_name)
    .copied()
    .ok_or_else(|| {
        format!(
            "[cf_loop/pattern1] Loop variable '{}' not found in variable_map",
            loop_var_name
        )
    })?;

trace::trace().varmap("pattern1_start", &self.variable_map);

After:

use super::common_init::{CommonPatternInitializer, LoopContext};

// ...

let LoopContext { loop_var_name, loop_var_id } =
    CommonPatternInitializer::extract_loop_context(self, condition, "pattern1")?;

編集コマンド:

# Pattern 1のファイルを開く
vim src/mir/builder/control_flow/joinir/patterns/pattern1_minimal.rs

# 3行目あたりにuse追加:
# use super::common_init::{CommonPatternInitializer, LoopContext};

# 64-79行を削除して1行に置き換え:
# let LoopContext { loop_var_name, loop_var_id } =
#     CommonPatternInitializer::extract_loop_context(self, condition, "pattern1")?;

テスト:

cargo test --release loop_min_while -- --nocapture

Step 1.3: Pattern 2, 3, 4に適用各10分 = 30分

Pattern 2:

vim src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs

# 56-71行を削除して1行に置き換え
# let LoopContext { loop_var_name, loop_var_id } =
#     CommonPatternInitializer::extract_loop_context(self, condition, "pattern2")?;

cargo test --release loop_with_break -- --nocapture

Pattern 3:

vim src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs

# 56-71行を削除して1行に置き換え
# let LoopContext { loop_var_name, loop_var_id } =
#     CommonPatternInitializer::extract_loop_context(self, condition, "pattern3")?;

cargo test --release loop_with_if_phi_sum -- --nocapture

Pattern 4:

vim src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs

# 115-130行を削除して1行に置き換え
# let LoopContext { loop_var_name, loop_var_id } =
#     CommonPatternInitializer::extract_loop_context(self, condition, "pattern4")?;

cargo test --release loop_with_continue -- --nocapture

Step 1.4: 全体テスト10分

# ビルド確認
cargo build --release

# 全パターンテスト
cargo test --release loop_min_while loop_with_break \
  loop_with_if_phi_sum loop_with_continue

# SSA-undefエラーチェック
cargo test --release 2>&1 | grep -i "ssa-undef\|undefined"

# 結果確認
if [ $? -eq 0 ]; then
  echo "✅ Phase 1完了200行削減達成"
else
  echo "❌ Phase 1失敗、ロールバックが必要"
fi

🎯 Phase 2: JoinIRConversionPipeline箱化1時間

Step 2.1: 新規ファイル作成5分

cat > src/mir/builder/control_flow/joinir/patterns/conversion_pipeline.rs << 'EOF'
//! Phase 33-22: JoinIR Conversion Pipeline Box
//!
//! Unified pipeline for JoinModule → MIR conversion + merge.
//!
//! # Purpose
//!
//! All 4 patterns share the same conversion flow:
//! 1. convert_join_module_to_mir_with_meta()
//! 2. trace::joinir_stats()
//! 3. merge_joinir_mir_blocks()
//!
//! This module eliminates 120 lines of duplicate code.

use crate::mir::builder::MirBuilder;
use crate::mir::join_ir::JoinModule;
use crate::mir::join_ir_vm_bridge::convert_join_module_to_mir_with_meta;
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
use crate::mir::ValueId;
use std::collections::BTreeMap;
use super::super::trace;

/// JoinIR Conversion Pipeline Box
pub struct JoinIRConversionPipeline;

impl JoinIRConversionPipeline {
    /// Convert JoinModule to MIR and merge into host function
    ///
    /// # Arguments
    ///
    /// * `builder` - MirBuilder instance (mutable for merge)
    /// * `join_module` - JoinModule generated by pattern lowerer
    /// * `boundary` - JoinInlineBoundary for input/output mapping
    /// * `pattern_name` - Pattern identifier for trace/error messages
    /// * `debug` - Debug flag for verbose output
    ///
    /// # Returns
    ///
    /// Option<ValueId> from merge operation (loop result value)
    ///
    /// # Example
    ///
    /// ```rust
    /// let boundary = JoinInlineBoundary::new_inputs_only(
    ///     vec![ValueId(0)],
    ///     vec![loop_var_id],
    /// );
    ///
    /// let result = JoinIRConversionPipeline::convert_and_merge(
    ///     builder,
    ///     join_module,
    ///     boundary,
    ///     "pattern1",
    ///     debug,
    /// )?;
    /// ```
    pub fn convert_and_merge(
        builder: &mut MirBuilder,
        join_module: JoinModule,
        boundary: JoinInlineBoundary,
        pattern_name: &str,
        debug: bool,
    ) -> Result<Option<ValueId>, String> {
        // Step 1: Convert JoinModule to MIR
        let empty_meta = BTreeMap::new();
        let mir_module = convert_join_module_to_mir_with_meta(&join_module, &empty_meta)
            .map_err(|e| {
                format!(
                    "[cf_loop/joinir/{}] MIR conversion failed: {:?}",
                    pattern_name, e
                )
            })?;

        // Step 2: Trace JoinIR stats for debugging
        trace::trace().joinir_stats(
            pattern_name,
            join_module.functions.len(),
            mir_module.blocks.len(),
        );

        // Step 3: Merge MIR blocks into host function
        builder.merge_joinir_mir_blocks(&mir_module, Some(&boundary), debug)
    }
}
EOF

# mod.rsに追加
echo "pub mod conversion_pipeline;" >> src/mir/builder/control_flow/joinir/patterns/mod.rs

Step 2.2: Pattern 1-4に適用各10分 = 40分

Pattern 1の例:

vim src/mir/builder/control_flow/joinir/patterns/pattern1_minimal.rs

# use追加:
# use super::conversion_pipeline::JoinIRConversionPipeline;

# 100-130行を削除して以下に置き換え:
# let boundary = JoinInlineBoundary::new_inputs_only(
#     vec![ValueId(0)],
#     vec![loop_var_id],
# );
#
# let _ = JoinIRConversionPipeline::convert_and_merge(
#     self,
#     join_module,
#     boundary,
#     "pattern1",
#     debug,
# )?;

cargo test --release loop_min_while

Pattern 2, 3, 4も同様に適用各10分

Step 2.3: 全体テスト15分

cargo build --release
cargo test --release

# 削減確認
wc -l src/mir/builder/control_flow/joinir/patterns/pattern*.rs
# Pattern 1: 176 → 126行
# Pattern 2: 219 → 169行
# Pattern 3: 165 → 115行
# Pattern 4: 343 → 293行

⚠️ Phase 3: Legacy Fallback削除検証30分

Step 3.1: Fallbackコメントアウト5分

vim src/mir/builder/control_flow/joinir/merge/mod.rs

# 277-307行をコメントアウト:
# /*
# if function_params.get(main_func_name).is_none() && ...
#     ...
# }
# */

Step 3.2: テスト実行20分

cargo test --release loop_min_while loop_with_break \
  loop_with_if_phi_sum loop_with_continue 2>&1 | tee /tmp/fallback-test.log

# エラー確認
grep -i "error\|failed" /tmp/fallback-test.log

Step 3.3: 判定5分

テスト全てPASS → Fallback削除OK:

# 277-307行を完全削除
vim src/mir/builder/control_flow/joinir/merge/mod.rs

# コミット
git add -A
git commit -m "feat(joinir): Phase 33-22 Remove legacy fallback (31 lines)"

テスト失敗 → Fallback必要:

# コメントアウトを戻す
git checkout src/mir/builder/control_flow/joinir/merge/mod.rs

# コメント追加(なぜ必要か記録)
vim src/mir/builder/control_flow/joinir/merge/mod.rs
# 277行目あたりに:
# // Phase 33-22検証済み: このFallbackはのケースで必要
# // 削除するとtest_XXXが失敗する

完了チェックリスト

Phase 1: CommonPatternInitializer

  • common_init.rs作成済み60行
  • Pattern 1適用済み176 → 126行
  • Pattern 2適用済み219 → 169行
  • Pattern 3適用済み165 → 115行
  • Pattern 4適用済み343 → 293行
  • テスト全PASS
  • ビルド警告ゼロ

Phase 2: JoinIRConversionPipeline

  • conversion_pipeline.rs作成済み50行
  • Pattern 1適用済みさらに30行削減
  • Pattern 2適用済みさらに30行削減
  • Pattern 3適用済みさらに30行削減
  • Pattern 4適用済みさらに30行削減
  • テスト全PASS
  • ビルド警告ゼロ

Phase 3: Legacy Fallback削除

  • Fallbackコメントアウト済み
  • テスト実行済み
  • 判定完了(削除 or 保持)
  • ドキュメント更新済み

🚨 トラブルシューティング

Q1: テストが失敗する

症状:

test loop_min_while ... FAILED
  SSA-undef: ValueId(42) not found

原因: LoopContext.loop_var_idのマッピングミス

対処:

# デバッグ出力有効化
NYASH_TRACE_VARMAP=1 cargo test --release loop_min_while -- --nocapture

# variable_mapの状態確認
grep "varmap.*pattern1" の出力を確認

Q2: ビルドエラー

症状:

error[E0433]: failed to resolve: use of undeclared type `LoopContext`

原因: use文の追加忘れ

対処:

# 各patternファイルに追加
use super::common_init::{CommonPatternInitializer, LoopContext};
use super::conversion_pipeline::JoinIRConversionPipeline;

Q3: Fallback削除でテスト失敗

症状:

test loop_XXX ... FAILED
  ValueId(0) not found in remapper

原因: 一部パターンでFallbackが必要

対処:

# Fallbackを保持
git checkout src/mir/builder/control_flow/joinir/merge/mod.rs

# コメント追加
# このFallbackはPattern Xで必要理由: ...

📚 参考ドキュメント


📝 完了後のコミット

git add -A
git commit -m "feat(joinir): Phase 33-22 CommonPatternInitializer + JoinIRConversionPipeline

- CommonPatternInitializer: Pattern 1-4の初期化ロジック統一化200行削減
- JoinIRConversionPipeline: JoinIR変換フロー統一化120行削減
- Legacy Fallback削除: merge/mod.rs 277-307行削除31行削減

Total: 351行削減

Phase 33-22完了"

最終確認:

# ビルド成功
cargo build --release

# テスト全PASS
cargo test --release

# 削減確認
git diff --stat HEAD~1
# patterns/ モジュール: -200行
# merge/mod.rs: -31行
# conversion_pipeline.rs: +50行
# common_init.rs: +60行
# 実質削減: -121行

Phase 33-22最適化完了