feat(phase161): Add MirAnalyzerBox implementation (Phase 161-2 基本実装)
Task先生による Phase 161-2 実装成果: **tools/hako_shared/mir_analyzer.hako** (375行) - MirAnalyzerBox: MIR JSON v1 パーサー&アナライザー - Core Methods: - birth(mirJsonText): JSON パース&キャッシュ - validateSchema(): MIR v1 構造検証 - summarize_function(funcIndex): メタデータ抽出 - count_phis(funcIndex): PHI 命令検出 - count_loops(funcIndex): CFG backward edge によるループ検出 **テストインフラ** - test_mir_analyzer.hako: テストハーネスフレームワーク - test_rep1_inline.hako: インラインテスト (rep1_if_simple) - rep1_if_simple.mir.json: MIR JSON テストデータ (8.5KB) - rep2_loop_simple.mir.json: ループパターンテストデータ (9.6KB) **箱理論適用** - 箱化: MirAnalyzerBox = MIR 分析専任(単一責務) - 境界: JsonParserBox との完全分離 - Fail-Fast: 明示的エラー、フォールバック無し - 遅延SG: _functions キャッシュ、オンデマンド計算 **発見された課題** - JsonParserBox._parse_number() 無限ループ問題(次タスクで対処) - VM ステップ予算超過でフル MIR JSON テスト一時ブロック Status: Phase 161-2 80%完了(コア実装OK、テスト検証はJsonParser修正後) Next: _parse_number() 修正 → Phase 161-2 テスト完了 → Phase 161-3 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
213
local_tests/phase161/README.md
Normal file
213
local_tests/phase161/README.md
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
# Phase 161 Representative Functions Test Suite
|
||||||
|
|
||||||
|
This directory contains 5 representative Nyash functions that exercise all key analyzer patterns for the Phase 161 JoinIR/MIR to .hako migration.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
These functions are used to validate Phase 161-2+ implementation of MirAnalyzerBox and related analyzer infrastructure. Each function is carefully designed to cover a specific control flow pattern.
|
||||||
|
|
||||||
|
## Representative Functions
|
||||||
|
|
||||||
|
### 1. rep1_if_simple.hako
|
||||||
|
**Pattern**: Simple if/else with PHI merge
|
||||||
|
**Complexity**: ⭐ Simple
|
||||||
|
**Tests**:
|
||||||
|
- Branch detection
|
||||||
|
- If-merge identification
|
||||||
|
- Single PHI instruction
|
||||||
|
- PHI incoming values from both branches
|
||||||
|
|
||||||
|
**Expected MIR Analysis**:
|
||||||
|
- 1 PHI instruction
|
||||||
|
- 1 Branch instruction
|
||||||
|
- 1 If structure detected
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. rep2_loop_simple.hako
|
||||||
|
**Pattern**: Simple loop with back edge
|
||||||
|
**Complexity**: ⭐ Simple
|
||||||
|
**Tests**:
|
||||||
|
- Loop detection via backward edge
|
||||||
|
- Loop-carried PHI at header
|
||||||
|
- Back edge identification
|
||||||
|
- Loop body block identification
|
||||||
|
|
||||||
|
**Expected MIR Analysis**:
|
||||||
|
- 1 Loop detected
|
||||||
|
- 1 PHI instruction (at loop header)
|
||||||
|
- Backward edge: Block 2 → Block 1
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. rep3_if_loop.hako
|
||||||
|
**Pattern**: Nested if inside loop
|
||||||
|
**Complexity**: ⭐⭐ Medium
|
||||||
|
**Tests**:
|
||||||
|
- Complex nested control flow
|
||||||
|
- Multiple PHI instructions (loop PHI + if PHI)
|
||||||
|
- Interaction between loop and if patterns
|
||||||
|
- Correct block hierarchy
|
||||||
|
|
||||||
|
**Expected MIR Analysis**:
|
||||||
|
- 1 Loop detected (loop header)
|
||||||
|
- 1 If detected (nested within loop body)
|
||||||
|
- 3 PHI instructions total:
|
||||||
|
- 2 at loop header (for loop carries)
|
||||||
|
- 1 at if merge point
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. rep4_loop_break.hako
|
||||||
|
**Pattern**: Loop with break statement
|
||||||
|
**Complexity**: ⭐⭐ Medium
|
||||||
|
**Tests**:
|
||||||
|
- Loop with multiple exits
|
||||||
|
- Break target resolution
|
||||||
|
- Exit PHI merge with multiple incoming paths
|
||||||
|
- Complex control flow merging
|
||||||
|
|
||||||
|
**Expected MIR Analysis**:
|
||||||
|
- 1 Loop detected
|
||||||
|
- Multiple exit paths from loop
|
||||||
|
- Break condition identified
|
||||||
|
- Exit merge block identified
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5. rep5_type_prop.hako
|
||||||
|
**Pattern**: Type propagation through loop
|
||||||
|
**Complexity**: ⭐⭐ Medium
|
||||||
|
**Tests**:
|
||||||
|
- Type inference through PHI chains
|
||||||
|
- BinOp type preservation
|
||||||
|
- Type consistency across loop iterations
|
||||||
|
- Compare operation type hints
|
||||||
|
|
||||||
|
**Expected MIR Analysis**:
|
||||||
|
- Type propagation converges
|
||||||
|
- All ValueIds have consistent types
|
||||||
|
- PHI merges compatible types
|
||||||
|
- 4-iteration propagation completes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Generating MIR JSON
|
||||||
|
|
||||||
|
To generate reference MIR JSON for each representative:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./target/release/nyash --dump-mir --emit-mir-json rep1_if_simple.mir.json rep1_if_simple.hako
|
||||||
|
./target/release/nyash --dump-mir --emit-mir-json rep2_loop_simple.mir.json rep2_loop_simple.hako
|
||||||
|
./target/release/nyash --dump-mir --emit-mir-json rep3_if_loop.mir.json rep3_if_loop.hako
|
||||||
|
./target/release/nyash --dump-mir --emit-mir-json rep4_loop_break.mir.json rep4_loop_break.hako
|
||||||
|
./target/release/nyash --dump-mir --emit-mir-json rep5_type_prop.mir.json rep5_type_prop.hako
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates the reference `.mir.json` files that MirAnalyzerBox will parse.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testing MirAnalyzerBox
|
||||||
|
|
||||||
|
Phase 161-2+ will implement analyzer methods that should produce these results:
|
||||||
|
|
||||||
|
### rep1_if_simple
|
||||||
|
|
||||||
|
```
|
||||||
|
MirAnalyzerBox analyzer("rep1_if_simple.mir.json", text)
|
||||||
|
analyzer.list_phis() → [{ block_id: 4, dest: ValueId, incoming: [...] }]
|
||||||
|
analyzer.list_ifs() → [{ condition_block: 1, merge_block: 4, ... }]
|
||||||
|
analyzer.summarize_function(0) → { has_phis: true, has_ifs: true, ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
### rep2_loop_simple
|
||||||
|
|
||||||
|
```
|
||||||
|
analyzer.list_loops() → [{ header_block: 1, contains_blocks: [1,2], ... }]
|
||||||
|
analyzer.list_phis() → [{ block_id: 1, dest: ValueId, incoming: [...] }]
|
||||||
|
analyzer.summarize_function(0) → { has_loops: true, has_phis: true, ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
### rep3_if_loop
|
||||||
|
|
||||||
|
```
|
||||||
|
analyzer.list_loops() → 1 loop
|
||||||
|
analyzer.list_ifs() → 1 if (nested in loop body)
|
||||||
|
analyzer.list_phis() → 3 PHI instructions
|
||||||
|
```
|
||||||
|
|
||||||
|
### rep4_loop_break
|
||||||
|
|
||||||
|
```
|
||||||
|
analyzer.list_loops() → 1 loop with multiple exits
|
||||||
|
analyzer.list_ifs() → 1 if (for break condition)
|
||||||
|
```
|
||||||
|
|
||||||
|
### rep5_type_prop
|
||||||
|
|
||||||
|
```
|
||||||
|
analyzer.propagate_types(0) → { ValueId: "i64", ... }
|
||||||
|
// All types should be consistent, no conflicts
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Structure of MIR JSON
|
||||||
|
|
||||||
|
Each `rep_N.mir.json` follows the schema defined in [phase161_joinir_analyzer_design.md](../../docs/development/current/main/phase161_joinir_analyzer_design.md):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"schema_version": "1",
|
||||||
|
"functions": [
|
||||||
|
{
|
||||||
|
"name": "main",
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"instructions": [
|
||||||
|
{
|
||||||
|
"op": "const",
|
||||||
|
"dest": 1,
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
...
|
||||||
|
]
|
||||||
|
},
|
||||||
|
...
|
||||||
|
],
|
||||||
|
"cfg": {
|
||||||
|
"entry": 0,
|
||||||
|
"targets": { "0": [1], "1": [2, 3], ... }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 161 Roadmap Usage
|
||||||
|
|
||||||
|
These representatives are used in:
|
||||||
|
|
||||||
|
1. **Phase 161-2**: Basic MirAnalyzerBox structure
|
||||||
|
- Implement on rep1 and rep2 (simple patterns)
|
||||||
|
|
||||||
|
2. **Phase 161-3**: PHI/Loop/If detection
|
||||||
|
- Full testing on all 5 representatives
|
||||||
|
|
||||||
|
3. **Phase 161-4**: Type propagation
|
||||||
|
- Validate rep5_type_prop
|
||||||
|
|
||||||
|
4. **Phase 161-5**: Full test suite
|
||||||
|
- All representatives passing all analyzer methods
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [phase161_analyzer_box_design.md](../../docs/development/current/main/phase161_analyzer_box_design.md) - Analyzer Box design
|
||||||
|
- [phase161_representative_functions.md](../../docs/development/current/main/phase161_representative_functions.md) - Function selection criteria
|
||||||
|
- [phase161_joinir_analyzer_design.md](../../docs/development/current/main/phase161_joinir_analyzer_design.md) - JSON schema reference
|
||||||
18
local_tests/phase161/rep1_if_simple.hako
Normal file
18
local_tests/phase161/rep1_if_simple.hako
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// Phase 161 Representative Function 1: Simple If/Else with PHI Merge
|
||||||
|
// Pattern: Basic if/else statement that generates single PHI merge
|
||||||
|
// Tests: Branch detection, if-merge identification, single PHI
|
||||||
|
|
||||||
|
box Main {
|
||||||
|
main() {
|
||||||
|
local x = 5
|
||||||
|
local result
|
||||||
|
|
||||||
|
if x > 3 {
|
||||||
|
result = 10
|
||||||
|
} else {
|
||||||
|
result = 20
|
||||||
|
}
|
||||||
|
|
||||||
|
print(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
426
local_tests/phase161/rep1_if_simple.mir.json
Normal file
426
local_tests/phase161/rep1_if_simple.mir.json
Normal file
@ -0,0 +1,426 @@
|
|||||||
|
{
|
||||||
|
"capabilities": [
|
||||||
|
"unified_call",
|
||||||
|
"phi",
|
||||||
|
"effects",
|
||||||
|
"callee_typing"
|
||||||
|
],
|
||||||
|
"cfg": {
|
||||||
|
"functions": [
|
||||||
|
{
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"reachable": false,
|
||||||
|
"successors": [],
|
||||||
|
"terminator": "Return"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"entry_block": 0,
|
||||||
|
"name": "condition_fn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"reachable": false,
|
||||||
|
"successors": [],
|
||||||
|
"terminator": "Return"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"entry_block": 12,
|
||||||
|
"name": "Main.equals/1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"reachable": false,
|
||||||
|
"successors": [
|
||||||
|
8,
|
||||||
|
9
|
||||||
|
],
|
||||||
|
"terminator": "Branch"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"reachable": false,
|
||||||
|
"successors": [
|
||||||
|
10
|
||||||
|
],
|
||||||
|
"terminator": "Jump"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 9,
|
||||||
|
"reachable": false,
|
||||||
|
"successors": [
|
||||||
|
10
|
||||||
|
],
|
||||||
|
"terminator": "Jump"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"reachable": false,
|
||||||
|
"successors": [],
|
||||||
|
"terminator": "Return"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"entry_block": 7,
|
||||||
|
"name": "Main.main/0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"reachable": false,
|
||||||
|
"successors": [],
|
||||||
|
"terminator": "Return"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"entry_block": 0,
|
||||||
|
"name": "main"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"id": 11,
|
||||||
|
"reachable": false,
|
||||||
|
"successors": [],
|
||||||
|
"terminator": "Return"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"entry_block": 11,
|
||||||
|
"name": "Main.toString/0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"functions": [
|
||||||
|
{
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"instructions": [
|
||||||
|
{
|
||||||
|
"dst": 1,
|
||||||
|
"op": "const",
|
||||||
|
"value": {
|
||||||
|
"type": "i64",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "ret",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "condition_fn",
|
||||||
|
"params": [
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"instructions": [
|
||||||
|
{
|
||||||
|
"dst": 3,
|
||||||
|
"op": "const",
|
||||||
|
"value": {
|
||||||
|
"type": "i64",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "ret",
|
||||||
|
"value": 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "Main.equals/1",
|
||||||
|
"params": [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"instructions": [
|
||||||
|
{
|
||||||
|
"dst": 6,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 9,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 10,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 12,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 11
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 2,
|
||||||
|
"op": "const",
|
||||||
|
"value": {
|
||||||
|
"type": "i64",
|
||||||
|
"value": 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 5,
|
||||||
|
"op": "const",
|
||||||
|
"value": {
|
||||||
|
"type": "i64",
|
||||||
|
"value": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 6,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 9,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 10,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 8,
|
||||||
|
"lhs": 9,
|
||||||
|
"op": "compare",
|
||||||
|
"operation": ">",
|
||||||
|
"rhs": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 12,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 11
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 3,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 7,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 11,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cond": 12,
|
||||||
|
"else": 9,
|
||||||
|
"op": "branch",
|
||||||
|
"then": 8
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"instructions": [
|
||||||
|
{
|
||||||
|
"dst": 18,
|
||||||
|
"op": "const",
|
||||||
|
"value": {
|
||||||
|
"type": "i64",
|
||||||
|
"value": 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "jump",
|
||||||
|
"target": 10
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 9,
|
||||||
|
"instructions": [
|
||||||
|
{
|
||||||
|
"dst": 24,
|
||||||
|
"op": "const",
|
||||||
|
"value": {
|
||||||
|
"type": "i64",
|
||||||
|
"value": 20
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "jump",
|
||||||
|
"target": 10
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"instructions": [
|
||||||
|
{
|
||||||
|
"dst": 30,
|
||||||
|
"incoming": [
|
||||||
|
[
|
||||||
|
18,
|
||||||
|
8
|
||||||
|
],
|
||||||
|
[
|
||||||
|
24,
|
||||||
|
9
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"op": "phi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": null,
|
||||||
|
"mir_call": {
|
||||||
|
"args": [
|
||||||
|
31
|
||||||
|
],
|
||||||
|
"callee": {
|
||||||
|
"name": "print",
|
||||||
|
"type": "Global"
|
||||||
|
},
|
||||||
|
"effects": [
|
||||||
|
"IO"
|
||||||
|
],
|
||||||
|
"flags": {}
|
||||||
|
},
|
||||||
|
"op": "mir_call"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 32,
|
||||||
|
"op": "const",
|
||||||
|
"value": {
|
||||||
|
"type": "void",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 31,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "ret",
|
||||||
|
"value": 32
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "Main.main/0",
|
||||||
|
"params": [
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"instructions": [
|
||||||
|
{
|
||||||
|
"dst": 9,
|
||||||
|
"op": "const",
|
||||||
|
"value": {
|
||||||
|
"type": "void",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "ret",
|
||||||
|
"value": 9
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "main",
|
||||||
|
"params": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"id": 11,
|
||||||
|
"instructions": [
|
||||||
|
{
|
||||||
|
"dst": 2,
|
||||||
|
"op": "const",
|
||||||
|
"value": {
|
||||||
|
"type": {
|
||||||
|
"box_type": "StringBox",
|
||||||
|
"kind": "handle"
|
||||||
|
},
|
||||||
|
"value": "Main("
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 3,
|
||||||
|
"op": "const",
|
||||||
|
"value": {
|
||||||
|
"type": {
|
||||||
|
"box_type": "StringBox",
|
||||||
|
"kind": "handle"
|
||||||
|
},
|
||||||
|
"value": ")"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 6,
|
||||||
|
"lhs": 4,
|
||||||
|
"op": "binop",
|
||||||
|
"operation": "+",
|
||||||
|
"rhs": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 4,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 5,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "ret",
|
||||||
|
"value": 6
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "Main.toString/0",
|
||||||
|
"params": [
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"build_time": "Phase 15.5 Development",
|
||||||
|
"features": [
|
||||||
|
"mir_call_unification",
|
||||||
|
"json_v1_schema"
|
||||||
|
],
|
||||||
|
"generator": "nyash-rust",
|
||||||
|
"phase": "15.5"
|
||||||
|
},
|
||||||
|
"schema_version": "1.0"
|
||||||
|
}
|
||||||
14
local_tests/phase161/rep2_loop_simple.hako
Normal file
14
local_tests/phase161/rep2_loop_simple.hako
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// Phase 161 Representative Function 2: Simple Loop with Back Edge
|
||||||
|
// Pattern: Basic loop statement that generates back edge and loop-carried PHI
|
||||||
|
// Tests: Loop detection, back edge identification, loop-carried PHI
|
||||||
|
|
||||||
|
box Main {
|
||||||
|
main() {
|
||||||
|
local i = 0
|
||||||
|
|
||||||
|
loop(i < 10) {
|
||||||
|
print(i)
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
481
local_tests/phase161/rep2_loop_simple.mir.json
Normal file
481
local_tests/phase161/rep2_loop_simple.mir.json
Normal file
@ -0,0 +1,481 @@
|
|||||||
|
{
|
||||||
|
"capabilities": [
|
||||||
|
"unified_call",
|
||||||
|
"phi",
|
||||||
|
"effects",
|
||||||
|
"callee_typing"
|
||||||
|
],
|
||||||
|
"cfg": {
|
||||||
|
"functions": [
|
||||||
|
{
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"reachable": false,
|
||||||
|
"successors": [
|
||||||
|
11
|
||||||
|
],
|
||||||
|
"terminator": "Jump"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 11,
|
||||||
|
"reachable": false,
|
||||||
|
"successors": [
|
||||||
|
12
|
||||||
|
],
|
||||||
|
"terminator": "Jump"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"reachable": false,
|
||||||
|
"successors": [
|
||||||
|
13,
|
||||||
|
15
|
||||||
|
],
|
||||||
|
"terminator": "Branch"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"reachable": false,
|
||||||
|
"successors": [
|
||||||
|
14
|
||||||
|
],
|
||||||
|
"terminator": "Jump"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 14,
|
||||||
|
"reachable": false,
|
||||||
|
"successors": [
|
||||||
|
12
|
||||||
|
],
|
||||||
|
"terminator": "Jump"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 15,
|
||||||
|
"reachable": false,
|
||||||
|
"successors": [],
|
||||||
|
"terminator": "Return"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 16,
|
||||||
|
"reachable": false,
|
||||||
|
"successors": [
|
||||||
|
12
|
||||||
|
],
|
||||||
|
"terminator": "Jump"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"entry_block": 10,
|
||||||
|
"name": "Main.main/0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"id": 17,
|
||||||
|
"reachable": false,
|
||||||
|
"successors": [],
|
||||||
|
"terminator": "Return"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"entry_block": 17,
|
||||||
|
"name": "Main.equals/1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"id": 18,
|
||||||
|
"reachable": false,
|
||||||
|
"successors": [],
|
||||||
|
"terminator": "Return"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"entry_block": 18,
|
||||||
|
"name": "Main.toString/0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"reachable": false,
|
||||||
|
"successors": [],
|
||||||
|
"terminator": "Return"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"entry_block": 0,
|
||||||
|
"name": "condition_fn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"reachable": false,
|
||||||
|
"successors": [],
|
||||||
|
"terminator": "Return"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"entry_block": 0,
|
||||||
|
"name": "main"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"functions": [
|
||||||
|
{
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"instructions": [
|
||||||
|
{
|
||||||
|
"dst": 2,
|
||||||
|
"op": "const",
|
||||||
|
"value": {
|
||||||
|
"type": "i64",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 3,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "jump",
|
||||||
|
"target": 11
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 11,
|
||||||
|
"instructions": [
|
||||||
|
{
|
||||||
|
"dst": 4,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 4,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "jump",
|
||||||
|
"target": 12
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"instructions": [
|
||||||
|
{
|
||||||
|
"dst": 5,
|
||||||
|
"incoming": [
|
||||||
|
[
|
||||||
|
4,
|
||||||
|
11
|
||||||
|
],
|
||||||
|
[
|
||||||
|
18,
|
||||||
|
14
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"op": "phi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 12,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 13,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 8,
|
||||||
|
"op": "const",
|
||||||
|
"value": {
|
||||||
|
"type": "i64",
|
||||||
|
"value": 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 12,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 13,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 11,
|
||||||
|
"lhs": 12,
|
||||||
|
"op": "compare",
|
||||||
|
"operation": "<",
|
||||||
|
"rhs": 13
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 9,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 10,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 14,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 11
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cond": 14,
|
||||||
|
"else": 15,
|
||||||
|
"op": "branch",
|
||||||
|
"then": 13
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"instructions": [
|
||||||
|
{
|
||||||
|
"dst": 16,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": null,
|
||||||
|
"mir_call": {
|
||||||
|
"args": [
|
||||||
|
9
|
||||||
|
],
|
||||||
|
"callee": {
|
||||||
|
"name": "print",
|
||||||
|
"type": "Global"
|
||||||
|
},
|
||||||
|
"effects": [
|
||||||
|
"IO"
|
||||||
|
],
|
||||||
|
"flags": {}
|
||||||
|
},
|
||||||
|
"op": "mir_call"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 15,
|
||||||
|
"op": "const",
|
||||||
|
"value": {
|
||||||
|
"type": "i64",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 16,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 18,
|
||||||
|
"lhs": 16,
|
||||||
|
"op": "binop",
|
||||||
|
"operation": "+",
|
||||||
|
"rhs": 17
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 17,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 15
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "jump",
|
||||||
|
"target": 14
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 14,
|
||||||
|
"instructions": [
|
||||||
|
{
|
||||||
|
"op": "jump",
|
||||||
|
"target": 12
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 15,
|
||||||
|
"instructions": [
|
||||||
|
{
|
||||||
|
"dst": 20,
|
||||||
|
"op": "const",
|
||||||
|
"value": {
|
||||||
|
"type": "void",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "ret",
|
||||||
|
"value": 20
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 16,
|
||||||
|
"instructions": [
|
||||||
|
{
|
||||||
|
"op": "jump",
|
||||||
|
"target": 12
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "Main.main/0",
|
||||||
|
"params": [
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"id": 17,
|
||||||
|
"instructions": [
|
||||||
|
{
|
||||||
|
"dst": 3,
|
||||||
|
"op": "const",
|
||||||
|
"value": {
|
||||||
|
"type": "i64",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "ret",
|
||||||
|
"value": 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "Main.equals/1",
|
||||||
|
"params": [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"id": 18,
|
||||||
|
"instructions": [
|
||||||
|
{
|
||||||
|
"dst": 2,
|
||||||
|
"op": "const",
|
||||||
|
"value": {
|
||||||
|
"type": {
|
||||||
|
"box_type": "StringBox",
|
||||||
|
"kind": "handle"
|
||||||
|
},
|
||||||
|
"value": "Main("
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 3,
|
||||||
|
"op": "const",
|
||||||
|
"value": {
|
||||||
|
"type": {
|
||||||
|
"box_type": "StringBox",
|
||||||
|
"kind": "handle"
|
||||||
|
},
|
||||||
|
"value": ")"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 6,
|
||||||
|
"lhs": 4,
|
||||||
|
"op": "binop",
|
||||||
|
"operation": "+",
|
||||||
|
"rhs": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 4,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dst": 5,
|
||||||
|
"op": "copy",
|
||||||
|
"src": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "ret",
|
||||||
|
"value": 6
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "Main.toString/0",
|
||||||
|
"params": [
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"instructions": [
|
||||||
|
{
|
||||||
|
"dst": 1,
|
||||||
|
"op": "const",
|
||||||
|
"value": {
|
||||||
|
"type": "i64",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "ret",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "condition_fn",
|
||||||
|
"params": [
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"instructions": [
|
||||||
|
{
|
||||||
|
"dst": 9,
|
||||||
|
"op": "const",
|
||||||
|
"value": {
|
||||||
|
"type": "void",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"op": "ret",
|
||||||
|
"value": 9
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "main",
|
||||||
|
"params": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"build_time": "Phase 15.5 Development",
|
||||||
|
"features": [
|
||||||
|
"mir_call_unification",
|
||||||
|
"json_v1_schema"
|
||||||
|
],
|
||||||
|
"generator": "nyash-rust",
|
||||||
|
"phase": "15.5"
|
||||||
|
},
|
||||||
|
"schema_version": "1.0"
|
||||||
|
}
|
||||||
21
local_tests/phase161/rep3_if_loop.hako
Normal file
21
local_tests/phase161/rep3_if_loop.hako
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Phase 161 Representative Function 3: Nested If Inside Loop
|
||||||
|
// Pattern: If statement nested inside loop generating multiple PHI instructions
|
||||||
|
// Tests: Complex PHI detection, nested block analysis, mixed if/loop patterns
|
||||||
|
|
||||||
|
box Main {
|
||||||
|
main() {
|
||||||
|
local i = 0
|
||||||
|
local sum = 0
|
||||||
|
|
||||||
|
loop(i < 10) {
|
||||||
|
if i % 2 == 0 {
|
||||||
|
sum = sum + i
|
||||||
|
} else {
|
||||||
|
sum = sum - i
|
||||||
|
}
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
print(sum)
|
||||||
|
}
|
||||||
|
}
|
||||||
17
local_tests/phase161/rep4_loop_break.hako
Normal file
17
local_tests/phase161/rep4_loop_break.hako
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Phase 161 Representative Function 4: Loop with Break Statement
|
||||||
|
// Pattern: Loop with break that creates multiple exit paths
|
||||||
|
// Tests: Loop with multiple exits, break target resolution, exit PHI merge
|
||||||
|
|
||||||
|
box Main {
|
||||||
|
main() {
|
||||||
|
local i = 0
|
||||||
|
|
||||||
|
loop(true) {
|
||||||
|
if i == 5 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
print(i)
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
local_tests/phase161/rep5_type_prop.hako
Normal file
21
local_tests/phase161/rep5_type_prop.hako
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Phase 161 Representative Function 5: Type Propagation Through Loop
|
||||||
|
// Pattern: Loop with type-carrying PHI and arithmetic operations
|
||||||
|
// Tests: Type propagation, type inference through PHI chain, BinOp type preservation
|
||||||
|
|
||||||
|
box Main {
|
||||||
|
main() {
|
||||||
|
local x = 0
|
||||||
|
local y = 10
|
||||||
|
|
||||||
|
loop(x < y) {
|
||||||
|
local z = x + 1
|
||||||
|
if z > 5 {
|
||||||
|
x = z * 2
|
||||||
|
} else {
|
||||||
|
x = z - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
170
local_tests/phase161/test_mir_analyzer.hako
Normal file
170
local_tests/phase161/test_mir_analyzer.hako
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
// local_tests/phase161/test_mir_analyzer.hako
|
||||||
|
// Test harness for MirAnalyzerBox (Phase 161-2)
|
||||||
|
|
||||||
|
// Test runner for MIR analysis
|
||||||
|
box TestRunner {
|
||||||
|
analyzer: MirAnalyzerBox
|
||||||
|
test_name: string
|
||||||
|
|
||||||
|
birth(name, mir_json_text) {
|
||||||
|
me.test_name = name
|
||||||
|
print("==================================================")
|
||||||
|
print("Test: " + name)
|
||||||
|
print("==================================================")
|
||||||
|
|
||||||
|
// Parse MIR JSON
|
||||||
|
me.analyzer = new MirAnalyzerBox()
|
||||||
|
local result = me.analyzer.birth(mir_json_text)
|
||||||
|
|
||||||
|
if result == null {
|
||||||
|
print("FAIL: Failed to parse MIR JSON")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
print("PASS: MIR JSON parsed successfully")
|
||||||
|
return me
|
||||||
|
}
|
||||||
|
|
||||||
|
method test_validate_schema() {
|
||||||
|
print("")
|
||||||
|
print("--- Test: validateSchema() ---")
|
||||||
|
|
||||||
|
local valid = me.analyzer.validateSchema()
|
||||||
|
|
||||||
|
if valid == 1 {
|
||||||
|
print("PASS: Schema validation successful")
|
||||||
|
} else {
|
||||||
|
print("FAIL: Schema validation failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid
|
||||||
|
}
|
||||||
|
|
||||||
|
method test_summarize_function(funcIndex, expected_name) {
|
||||||
|
print("")
|
||||||
|
print("--- Test: summarize_function(" + funcIndex + ") ---")
|
||||||
|
|
||||||
|
local summary = me.analyzer.summarize_function(funcIndex)
|
||||||
|
|
||||||
|
if summary == null {
|
||||||
|
print("FAIL: summarize_function returned null")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
local name = summary.get("name")
|
||||||
|
local block_count = summary.get("block_count")
|
||||||
|
local instruction_count = summary.get("instruction_count")
|
||||||
|
local has_phis = summary.get("has_phis")
|
||||||
|
local has_loops = summary.get("has_loops")
|
||||||
|
local has_ifs = summary.get("has_ifs")
|
||||||
|
|
||||||
|
print("Function name: " + name)
|
||||||
|
print("Blocks: " + block_count)
|
||||||
|
print("Instructions: " + instruction_count)
|
||||||
|
print("Has PHI: " + has_phis)
|
||||||
|
print("Has loops: " + has_loops)
|
||||||
|
print("Has ifs: " + has_ifs)
|
||||||
|
|
||||||
|
if name == expected_name {
|
||||||
|
print("PASS: Function name matches expected: " + expected_name)
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
print("FAIL: Function name mismatch. Expected: " + expected_name + ", Got: " + name)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
method test_count_phis(funcIndex, expected_count) {
|
||||||
|
print("")
|
||||||
|
print("--- Test: count_phis(" + funcIndex + ") ---")
|
||||||
|
|
||||||
|
local phi_list = me.analyzer.count_phis(funcIndex)
|
||||||
|
|
||||||
|
if phi_list == null {
|
||||||
|
print("FAIL: count_phis returned null")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
local count = phi_list.size()
|
||||||
|
print("PHI count: " + count)
|
||||||
|
|
||||||
|
if count == expected_count {
|
||||||
|
print("PASS: PHI count matches expected: " + expected_count)
|
||||||
|
|
||||||
|
// Print details
|
||||||
|
local i = 0
|
||||||
|
loop(i < count) {
|
||||||
|
local phi = phi_list.at(i)
|
||||||
|
local block_id = phi.get("block_id")
|
||||||
|
local dest = phi.get("dest")
|
||||||
|
local incoming_count = phi.get("incoming_count")
|
||||||
|
|
||||||
|
print(" PHI #" + i + ": block=" + block_id + " dest=r" + dest + " incoming=" + incoming_count)
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
print("FAIL: PHI count mismatch. Expected: " + expected_count + ", Got: " + count)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
method test_count_loops(funcIndex, expected_count) {
|
||||||
|
print("")
|
||||||
|
print("--- Test: count_loops(" + funcIndex + ") ---")
|
||||||
|
|
||||||
|
local loop_count = me.analyzer.count_loops(funcIndex)
|
||||||
|
|
||||||
|
print("Loop count: " + loop_count)
|
||||||
|
|
||||||
|
if loop_count == expected_count {
|
||||||
|
print("PASS: Loop count matches expected: " + expected_count)
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
print("FAIL: Loop count mismatch. Expected: " + expected_count + ", Got: " + loop_count)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
method run_all_tests(funcIndex, expected_name, expected_phi_count, expected_loop_count) {
|
||||||
|
me.test_validate_schema()
|
||||||
|
me.test_summarize_function(funcIndex, expected_name)
|
||||||
|
me.test_count_phis(funcIndex, expected_phi_count)
|
||||||
|
me.test_count_loops(funcIndex, expected_loop_count)
|
||||||
|
|
||||||
|
print("")
|
||||||
|
print("==================================================")
|
||||||
|
print("Test '" + me.test_name + "' completed")
|
||||||
|
print("==================================================")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to read file contents (simplified for testing)
|
||||||
|
box FileHelper {
|
||||||
|
method read_file(path) {
|
||||||
|
// Placeholder: In real implementation, use FileBox
|
||||||
|
// For now, return null to indicate we need to pass content directly
|
||||||
|
print("[FileHelper] File reading not implemented yet")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main entry point
|
||||||
|
static box Main {
|
||||||
|
main(args) {
|
||||||
|
print("Phase 161-2: MirAnalyzerBox Test Suite")
|
||||||
|
print("")
|
||||||
|
|
||||||
|
// Note: In actual testing, we'll load JSON files from disk
|
||||||
|
// For now, this is a skeleton that shows the test structure
|
||||||
|
|
||||||
|
print("To run tests:")
|
||||||
|
print("1. Generate MIR JSON: ./target/release/hakorune --dump-mir --emit-mir-json rep1.mir.json rep1_if_simple.hako")
|
||||||
|
print("2. Pass JSON content to TestRunner")
|
||||||
|
print("")
|
||||||
|
print("Test structure defined successfully")
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
164
local_tests/phase161/test_rep1_inline.hako
Normal file
164
local_tests/phase161/test_rep1_inline.hako
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
// local_tests/phase161/test_rep1_inline.hako
|
||||||
|
// Inline test for rep1_if_simple MIR analysis
|
||||||
|
|
||||||
|
static box Main {
|
||||||
|
main(args) {
|
||||||
|
print("Phase 161-2: Testing MirAnalyzerBox on rep1_if_simple")
|
||||||
|
print("=======================================================")
|
||||||
|
|
||||||
|
// Minimal MIR JSON for testing (simplified from actual rep1)
|
||||||
|
local mir_json = '{"capabilities":["unified_call","phi","effects","callee_typing"],"functions":[{"name":"Main.main/0","params":[],"entry":7,"blocks":[{"id":7,"instructions":[{"op":"const","dst":2,"value":{"type":"i64","value":5}},{"op":"const","dst":5,"value":{"type":"i64","value":3}},{"op":"compare","operation":">","lhs":9,"rhs":10,"dst":8},{"op":"branch","cond":12,"then":8,"else":9}]},{"id":8,"instructions":[{"op":"const","dst":18,"value":{"type":"i64","value":10}},{"op":"jump","target":10}]},{"id":9,"instructions":[{"op":"const","dst":24,"value":{"type":"i64","value":20}},{"op":"jump","target":10}]},{"id":10,"instructions":[{"op":"phi","dst":30,"incoming":[[18,8],[24,9]]},{"op":"ret","value":32}]}]}]}'
|
||||||
|
|
||||||
|
// Parse and analyze
|
||||||
|
print("")
|
||||||
|
print("Step 1: Creating MirAnalyzerBox...")
|
||||||
|
|
||||||
|
local analyzer = new MirAnalyzerBox(mir_json)
|
||||||
|
|
||||||
|
if analyzer == null {
|
||||||
|
print("FAIL: Failed to create analyzer")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
print("SUCCESS: MirAnalyzerBox created")
|
||||||
|
|
||||||
|
// Test 1: Validate schema
|
||||||
|
print("")
|
||||||
|
print("Step 2: Validating schema...")
|
||||||
|
|
||||||
|
local valid = analyzer.validateSchema()
|
||||||
|
if valid == 1 {
|
||||||
|
print("SUCCESS: Schema is valid")
|
||||||
|
} else {
|
||||||
|
print("FAIL: Schema validation failed")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 2: Summarize function
|
||||||
|
print("")
|
||||||
|
print("Step 3: Summarizing function 0...")
|
||||||
|
|
||||||
|
local summary = analyzer.summarize_function(0)
|
||||||
|
if summary == null {
|
||||||
|
print("FAIL: summarize_function returned null")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
local name = summary.get("name")
|
||||||
|
local blocks = summary.get("block_count")
|
||||||
|
local instructions = summary.get("instruction_count")
|
||||||
|
local has_phi = summary.get("has_phis")
|
||||||
|
local has_if = summary.get("has_ifs")
|
||||||
|
local has_loop = summary.get("has_loops")
|
||||||
|
|
||||||
|
print("Function name: " + name)
|
||||||
|
print("Blocks: " + blocks)
|
||||||
|
print("Instructions: " + instructions)
|
||||||
|
print("Has PHI: " + has_phi)
|
||||||
|
print("Has If: " + has_if)
|
||||||
|
print("Has Loop: " + has_loop)
|
||||||
|
|
||||||
|
// Verify expected values
|
||||||
|
local pass_count = 0
|
||||||
|
|
||||||
|
if name == "Main.main/0" {
|
||||||
|
print(" [OK] Function name correct")
|
||||||
|
pass_count = pass_count + 1
|
||||||
|
} else {
|
||||||
|
print(" [FAIL] Function name mismatch")
|
||||||
|
}
|
||||||
|
|
||||||
|
if blocks == 4 {
|
||||||
|
print(" [OK] Block count correct (4)")
|
||||||
|
pass_count = pass_count + 1
|
||||||
|
} else {
|
||||||
|
print(" [FAIL] Block count mismatch (expected 4, got " + blocks + ")")
|
||||||
|
}
|
||||||
|
|
||||||
|
if has_phi == 1 {
|
||||||
|
print(" [OK] PHI detected")
|
||||||
|
pass_count = pass_count + 1
|
||||||
|
} else {
|
||||||
|
print(" [FAIL] PHI not detected")
|
||||||
|
}
|
||||||
|
|
||||||
|
if has_if == 1 {
|
||||||
|
print(" [OK] If detected")
|
||||||
|
pass_count = pass_count + 1
|
||||||
|
} else {
|
||||||
|
print(" [FAIL] If not detected")
|
||||||
|
}
|
||||||
|
|
||||||
|
if has_loop == 0 {
|
||||||
|
print(" [OK] No loop (correct)")
|
||||||
|
pass_count = pass_count + 1
|
||||||
|
} else {
|
||||||
|
print(" [FAIL] Loop incorrectly detected")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 3: Count PHI instructions
|
||||||
|
print("")
|
||||||
|
print("Step 4: Counting PHI instructions...")
|
||||||
|
|
||||||
|
local phi_list = analyzer.count_phis(0)
|
||||||
|
if phi_list == null {
|
||||||
|
print("FAIL: count_phis returned null")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
local phi_count = phi_list.size()
|
||||||
|
print("PHI count: " + phi_count)
|
||||||
|
|
||||||
|
if phi_count == 1 {
|
||||||
|
print(" [OK] Correct PHI count (1)")
|
||||||
|
pass_count = pass_count + 1
|
||||||
|
|
||||||
|
local phi = phi_list.at(0)
|
||||||
|
local block_id = phi.get("block_id")
|
||||||
|
local dest = phi.get("dest")
|
||||||
|
local incoming_count = phi.get("incoming_count")
|
||||||
|
|
||||||
|
print(" PHI details: block=" + block_id + " dest=r" + dest + " incoming=" + incoming_count)
|
||||||
|
|
||||||
|
if block_id == 10 {
|
||||||
|
print(" [OK] PHI in correct block (10)")
|
||||||
|
pass_count = pass_count + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if incoming_count == 2 {
|
||||||
|
print(" [OK] Correct incoming count (2)")
|
||||||
|
pass_count = pass_count + 1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print(" [FAIL] Incorrect PHI count (expected 1, got " + phi_count + ")")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 4: Count loops
|
||||||
|
print("")
|
||||||
|
print("Step 5: Counting loops...")
|
||||||
|
|
||||||
|
local loop_count = analyzer.count_loops(0)
|
||||||
|
print("Loop count: " + loop_count)
|
||||||
|
|
||||||
|
if loop_count == 0 {
|
||||||
|
print(" [OK] No loops detected (correct)")
|
||||||
|
pass_count = pass_count + 1
|
||||||
|
} else {
|
||||||
|
print(" [FAIL] Loops incorrectly detected (expected 0, got " + loop_count + ")")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Final results
|
||||||
|
print("")
|
||||||
|
print("=======================================================")
|
||||||
|
print("Test Results: " + pass_count + " / 9 checks passed")
|
||||||
|
print("=======================================================")
|
||||||
|
|
||||||
|
if pass_count == 9 {
|
||||||
|
print("SUCCESS: All tests passed!")
|
||||||
|
return 0
|
||||||
|
} else {
|
||||||
|
print("PARTIAL: Some tests failed")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
382
tools/hako_shared/mir_analyzer.hako
Normal file
382
tools/hako_shared/mir_analyzer.hako
Normal file
@ -0,0 +1,382 @@
|
|||||||
|
// tools/hako_shared/mir_analyzer.hako - MirAnalyzerBox (Phase 161-2)
|
||||||
|
// .hako native MIR JSON v1 analyzer for JoinIR/MIR analysis
|
||||||
|
// Provides PHI/loop/if detection algorithms
|
||||||
|
|
||||||
|
// MirAnalyzerBox: Main analyzer for MIR JSON v1
|
||||||
|
box MirAnalyzerBox {
|
||||||
|
_mir_json: MapBox // Parsed MIR JSON root
|
||||||
|
_functions: ArrayBox // Cached functions array
|
||||||
|
_verbose: integer // Verbose mode (0=off, 1=on)
|
||||||
|
|
||||||
|
// Constructor: Parse MIR JSON v1 from text
|
||||||
|
birth(mir_json_text) {
|
||||||
|
me._verbose = 0
|
||||||
|
|
||||||
|
// Parse JSON using JsonParserBox
|
||||||
|
me._mir_json = JsonParserBox.parse_object(mir_json_text)
|
||||||
|
|
||||||
|
if me._mir_json == null {
|
||||||
|
print("[MirAnalyzerBox] ERROR: Failed to parse MIR JSON")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract functions array
|
||||||
|
me._functions = me._mir_json.get("functions")
|
||||||
|
|
||||||
|
if me._functions == null {
|
||||||
|
print("[MirAnalyzerBox] ERROR: No 'functions' field in MIR JSON")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if me._verbose == 1 {
|
||||||
|
print("[MirAnalyzerBox] Parsed MIR JSON successfully")
|
||||||
|
print("[MirAnalyzerBox] Functions count: " + me._functions.size())
|
||||||
|
}
|
||||||
|
|
||||||
|
return me
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate MIR JSON v1 schema
|
||||||
|
validateSchema() {
|
||||||
|
if me._mir_json == null { return 0 }
|
||||||
|
|
||||||
|
// Check required top-level fields
|
||||||
|
local capabilities = me._mir_json.get("capabilities")
|
||||||
|
if capabilities == null {
|
||||||
|
print("[MirAnalyzerBox] ERROR: Missing 'capabilities' field")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if me._functions == null {
|
||||||
|
print("[MirAnalyzerBox] ERROR: Missing 'functions' field")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check functions array is not empty
|
||||||
|
if me._functions.size() == 0 {
|
||||||
|
print("[MirAnalyzerBox] WARNING: Empty functions array")
|
||||||
|
}
|
||||||
|
|
||||||
|
if me._verbose == 1 {
|
||||||
|
print("[MirAnalyzerBox] Schema validation: PASS")
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Summarize function metadata
|
||||||
|
// Returns: MapBox with {name, params, blocks, instructions, has_loops, has_ifs, has_phis}
|
||||||
|
summarize_function(funcIndex) {
|
||||||
|
local func = me._get_function(funcIndex)
|
||||||
|
if func == null {
|
||||||
|
print("[MirAnalyzerBox] ERROR: Function index out of bounds: " + funcIndex)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
local summary = new MapBox()
|
||||||
|
|
||||||
|
// Basic metadata
|
||||||
|
local name = func.get("name")
|
||||||
|
summary.set("name", name)
|
||||||
|
|
||||||
|
local params = func.get("params")
|
||||||
|
if params == null {
|
||||||
|
summary.set("param_count", 0)
|
||||||
|
} else {
|
||||||
|
summary.set("param_count", params.size())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block count
|
||||||
|
local blocks = func.get("blocks")
|
||||||
|
if blocks == null {
|
||||||
|
summary.set("block_count", 0)
|
||||||
|
summary.set("instruction_count", 0)
|
||||||
|
summary.set("has_phis", 0)
|
||||||
|
summary.set("has_loops", 0)
|
||||||
|
summary.set("has_ifs", 0)
|
||||||
|
return summary
|
||||||
|
}
|
||||||
|
|
||||||
|
summary.set("block_count", blocks.size())
|
||||||
|
|
||||||
|
// Count instructions and detect patterns
|
||||||
|
local total_instructions = 0
|
||||||
|
local has_phi = 0
|
||||||
|
local has_branch = 0
|
||||||
|
|
||||||
|
local i = 0
|
||||||
|
loop(i < blocks.size()) {
|
||||||
|
local block = blocks.at(i)
|
||||||
|
local instructions = block.get("instructions")
|
||||||
|
|
||||||
|
if instructions != null {
|
||||||
|
total_instructions = total_instructions + instructions.size()
|
||||||
|
|
||||||
|
// Check for PHI and Branch instructions
|
||||||
|
local j = 0
|
||||||
|
loop(j < instructions.size()) {
|
||||||
|
local inst = instructions.at(j)
|
||||||
|
local op = inst.get("op")
|
||||||
|
|
||||||
|
if op == "phi" {
|
||||||
|
has_phi = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if op == "branch" {
|
||||||
|
has_branch = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
j = j + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
summary.set("instruction_count", total_instructions)
|
||||||
|
summary.set("has_phis", has_phi)
|
||||||
|
summary.set("has_ifs", has_branch)
|
||||||
|
|
||||||
|
// Loop detection (basic backward edge check)
|
||||||
|
local has_loop = me._has_backward_edge(blocks)
|
||||||
|
summary.set("has_loops", has_loop)
|
||||||
|
|
||||||
|
if me._verbose == 1 {
|
||||||
|
print("[MirAnalyzerBox] Summary for " + name + ":")
|
||||||
|
print(" Blocks: " + blocks.size())
|
||||||
|
print(" Instructions: " + total_instructions)
|
||||||
|
print(" Has PHI: " + has_phi)
|
||||||
|
print(" Has Branch: " + has_branch)
|
||||||
|
print(" Has Loop: " + has_loop)
|
||||||
|
}
|
||||||
|
|
||||||
|
return summary
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count PHI instructions in function
|
||||||
|
// Returns: ArrayBox of {block_id, dest, incoming_count}
|
||||||
|
count_phis(funcIndex) {
|
||||||
|
local func = me._get_function(funcIndex)
|
||||||
|
if func == null {
|
||||||
|
print("[MirAnalyzerBox] ERROR: Function index out of bounds: " + funcIndex)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
local blocks = func.get("blocks")
|
||||||
|
if blocks == null {
|
||||||
|
return new ArrayBox()
|
||||||
|
}
|
||||||
|
|
||||||
|
local phi_list = new ArrayBox()
|
||||||
|
|
||||||
|
local i = 0
|
||||||
|
loop(i < blocks.size()) {
|
||||||
|
local block = blocks.at(i)
|
||||||
|
local block_id = block.get("id")
|
||||||
|
local instructions = block.get("instructions")
|
||||||
|
|
||||||
|
if instructions != null {
|
||||||
|
local j = 0
|
||||||
|
loop(j < instructions.size()) {
|
||||||
|
local inst = instructions.at(j)
|
||||||
|
local op = inst.get("op")
|
||||||
|
|
||||||
|
if op == "phi" {
|
||||||
|
local phi_info = new MapBox()
|
||||||
|
phi_info.set("block_id", block_id)
|
||||||
|
|
||||||
|
local dest = inst.get("dst")
|
||||||
|
phi_info.set("dest", dest)
|
||||||
|
|
||||||
|
local incoming = inst.get("incoming")
|
||||||
|
if incoming == null {
|
||||||
|
phi_info.set("incoming_count", 0)
|
||||||
|
} else {
|
||||||
|
phi_info.set("incoming_count", incoming.size())
|
||||||
|
}
|
||||||
|
|
||||||
|
phi_list.push(phi_info)
|
||||||
|
|
||||||
|
if me._verbose == 1 {
|
||||||
|
print("[MirAnalyzerBox] PHI found: block=" + block_id + " dest=" + dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
j = j + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return phi_list
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count loops in function (via CFG backward edge detection)
|
||||||
|
// Returns: integer count of loops detected
|
||||||
|
count_loops(funcIndex) {
|
||||||
|
local func = me._get_function(funcIndex)
|
||||||
|
if func == null {
|
||||||
|
print("[MirAnalyzerBox] ERROR: Function index out of bounds: " + funcIndex)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
local blocks = func.get("blocks")
|
||||||
|
if blocks == null {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build block_id -> block_index map for quick lookup
|
||||||
|
local block_map = new MapBox()
|
||||||
|
local i = 0
|
||||||
|
loop(i < blocks.size()) {
|
||||||
|
local block = blocks.at(i)
|
||||||
|
local block_id = block.get("id")
|
||||||
|
block_map.set("" + block_id, i)
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count backward edges (target_id < source_id)
|
||||||
|
local loop_count = 0
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
loop(i < blocks.size()) {
|
||||||
|
local block = blocks.at(i)
|
||||||
|
local block_id = block.get("id")
|
||||||
|
local instructions = block.get("instructions")
|
||||||
|
|
||||||
|
if instructions != null {
|
||||||
|
// Check last instruction for control flow
|
||||||
|
local last_idx = instructions.size() - 1
|
||||||
|
if last_idx >= 0 {
|
||||||
|
local last_inst = instructions.at(last_idx)
|
||||||
|
local op = last_inst.get("op")
|
||||||
|
|
||||||
|
// Check jump instruction
|
||||||
|
if op == "jump" {
|
||||||
|
local target = last_inst.get("target")
|
||||||
|
if target != null {
|
||||||
|
if target < block_id {
|
||||||
|
loop_count = loop_count + 1
|
||||||
|
|
||||||
|
if me._verbose == 1 {
|
||||||
|
print("[MirAnalyzerBox] Backward edge: block " + block_id + " -> " + target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check branch instruction (both targets)
|
||||||
|
if op == "branch" {
|
||||||
|
local then_target = last_inst.get("then")
|
||||||
|
local else_target = last_inst.get("else")
|
||||||
|
|
||||||
|
if then_target != null {
|
||||||
|
if then_target < block_id {
|
||||||
|
loop_count = loop_count + 1
|
||||||
|
|
||||||
|
if me._verbose == 1 {
|
||||||
|
print("[MirAnalyzerBox] Backward edge (then): block " + block_id + " -> " + then_target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if else_target != null {
|
||||||
|
if else_target < block_id {
|
||||||
|
loop_count = loop_count + 1
|
||||||
|
|
||||||
|
if me._verbose == 1 {
|
||||||
|
print("[MirAnalyzerBox] Backward edge (else): block " + block_id + " -> " + else_target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return loop_count
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable/disable verbose mode
|
||||||
|
set_verbose(enabled) {
|
||||||
|
if enabled == 1 {
|
||||||
|
me._verbose = 1
|
||||||
|
print("[MirAnalyzerBox] Verbose mode: ON")
|
||||||
|
} else {
|
||||||
|
me._verbose = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal: Get function by index
|
||||||
|
_get_function(funcIndex) {
|
||||||
|
if me._functions == null { return null }
|
||||||
|
if funcIndex < 0 { return null }
|
||||||
|
if funcIndex >= me._functions.size() { return null }
|
||||||
|
|
||||||
|
return me._functions.at(funcIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal: Check if function has backward edges (loop indicator)
|
||||||
|
_has_backward_edge(blocks) {
|
||||||
|
if blocks == null { return 0 }
|
||||||
|
|
||||||
|
local i = 0
|
||||||
|
loop(i < blocks.size()) {
|
||||||
|
local block = blocks.at(i)
|
||||||
|
local block_id = block.get("id")
|
||||||
|
local instructions = block.get("instructions")
|
||||||
|
|
||||||
|
if instructions != null {
|
||||||
|
local last_idx = instructions.size() - 1
|
||||||
|
if last_idx >= 0 {
|
||||||
|
local last_inst = instructions.at(last_idx)
|
||||||
|
local op = last_inst.get("op")
|
||||||
|
|
||||||
|
if op == "jump" {
|
||||||
|
local target = last_inst.get("target")
|
||||||
|
if target != null {
|
||||||
|
if target < block_id {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if op == "branch" {
|
||||||
|
local then_target = last_inst.get("then")
|
||||||
|
local else_target = last_inst.get("else")
|
||||||
|
|
||||||
|
if then_target != null {
|
||||||
|
if then_target < block_id {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if else_target != null {
|
||||||
|
if else_target < block_id {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i = i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main entry point for standalone testing
|
||||||
|
static box MirAnalyzerMain {
|
||||||
|
main(args) {
|
||||||
|
print("[MirAnalyzerMain] Phase 161-2 Basic MirAnalyzerBox Implementation")
|
||||||
|
print("[MirAnalyzerMain] Usage: provide MIR JSON file path as argument")
|
||||||
|
|
||||||
|
// TODO: Add file reading and analysis test
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user