feat(joinir): Phase 49-3.2 merge_joinir_mir_blocks full implementation

Implement actual block merging for JoinIR Frontend mainline integration:

- Block ID remapping: Allocate new IDs from block_gen for all JoinIR blocks
- Value ID remapping: Allocate new IDs from next_value_id() for all values
- Instruction cloning: Clone all instructions with remapped IDs
- Return→Jump conversion: Convert Return terminators to Jump to exit block
- Control flow wiring: Jump from current block to JoinIR entry

Helper functions added:
- collect_values_in_block(): Collect all ValueIds in a block
- collect_values_in_instruction(): Collect all ValueIds in an instruction
- remap_instruction(): Remap ValueIds and BlockIds in an instruction

A/B tests (3 total):
- phase49_joinir_mainline_pipeline_smoke
- phase49_joinir_mainline_fallback_without_flag
- phase49_joinir_mainline_ab_comparison (Route A vs Route B)

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-28 19:45:23 +09:00
parent 736df58b29
commit 20d9b412b2
2 changed files with 416 additions and 24 deletions

View File

@ -1,11 +1,11 @@
// Phase 49-3: JoinIR Frontend Mainline Integration Test
// Phase 49-3.2: JoinIR Frontend Mainline Integration Test
//
// このテストは cf_loop の JoinIR Frontend mainline route が
// 正常に動作することを確認する。
//
// MVP 制限:
// - merge_joinir_mir_blocks() はログ出力のみ
// - 完全な A/B 比較は Phase 49-3.2(ブロックマージ実装)待ち
// Phase 49-3.2 実装済み:
// - merge_joinir_mir_blocks() によるブロックマージ
// - A/B 比較テストRoute A vs Route B
//
// テスト方法:
// HAKO_JOINIR_PRINT_TOKENS_MAIN=1 cargo test --release joinir_mainline_phase49
@ -129,3 +129,96 @@ static box Main {
std::env::remove_var("HAKO_PARSER_STAGE3");
std::env::remove_var("NYASH_DISABLE_PLUGINS");
}
/// Phase 49-3.2: A/B 比較テスト - Route A (legacy) vs Route B (JoinIR)
///
/// このテストは同じソースコードを2つの経路でコンパイルし、
/// 両方が正常に完了することを確認する。
#[test]
fn phase49_joinir_mainline_ab_comparison() {
let src = r#"
box JsonTokenizer {
tokens: ArrayBox
birth() {
me.tokens = new ArrayBox()
}
print_tokens() {
local i = 0
loop(i < me.tokens.length()) {
i = i + 1
}
}
}
static box Main {
main() {
local t = new JsonTokenizer()
t.print_tokens()
return 0
}
}
"#;
// Route A: Legacy path (flag OFF)
std::env::remove_var("HAKO_JOINIR_PRINT_TOKENS_MAIN");
std::env::set_var("NYASH_PARSER_STAGE3", "1");
std::env::set_var("HAKO_PARSER_STAGE3", "1");
std::env::set_var("NYASH_DISABLE_PLUGINS", "1");
let ast_a: ASTNode = NyashParser::parse_from_string(src)
.expect("phase49 A/B: parse failed (Route A)");
let mut mc_a = MirCompiler::with_options(false);
let result_a = mc_a.compile(ast_a);
assert!(
result_a.is_ok(),
"Route A compile should succeed: {:?}",
result_a.err()
);
let module_a = result_a.unwrap().module;
let blocks_a: usize = module_a
.functions
.values()
.map(|f| f.blocks.len())
.sum();
// Route B: JoinIR Frontend path (flag ON)
// Re-set parser flags to ensure they're active
std::env::set_var("NYASH_PARSER_STAGE3", "1");
std::env::set_var("HAKO_PARSER_STAGE3", "1");
std::env::set_var("NYASH_DISABLE_PLUGINS", "1");
std::env::set_var("HAKO_JOINIR_PRINT_TOKENS_MAIN", "1");
let ast_b: ASTNode = NyashParser::parse_from_string(src)
.expect("phase49 A/B: parse failed (Route B)");
let mut mc_b = MirCompiler::with_options(false);
let result_b = mc_b.compile(ast_b);
assert!(
result_b.is_ok(),
"Route B compile should succeed: {:?}",
result_b.err()
);
let module_b = result_b.unwrap().module;
let blocks_b: usize = module_b
.functions
.values()
.map(|f| f.blocks.len())
.sum();
// Log block counts for debugging
eprintln!(
"[phase49 A/B] Route A: {} total blocks, Route B: {} total blocks",
blocks_a, blocks_b
);
// Both should complete successfully (main assertion is the compile succeeds)
// Block counts may differ due to JoinIR's different structure
// Future: Add execution comparison
// クリーンアップ
std::env::remove_var("HAKO_JOINIR_PRINT_TOKENS_MAIN");
std::env::remove_var("NYASH_PARSER_STAGE3");
std::env::remove_var("HAKO_PARSER_STAGE3");
std::env::remove_var("NYASH_DISABLE_PLUGINS");
}