Step B実装内容(fibonacci風マルチキャリアループ対応): - LoopFormBox拡張: - multi_count mode追加(build2メソッド) - build_loop_multi_carrierメソッド実装(4-PHI, 5 blocks) - 3変数(i, a, b)同時追跡のfibonacci構造生成 - LowerLoopMultiCarrierBox新規実装: - 複数Local/Assign検出(2+変数) - キャリア変数抽出 - mode="multi_count"でLoopOptsBox.build2呼び出し - Fail-Fast: insufficient_carriersタグ出力 - FuncBodyBasicLowerBox拡張: - _try_lower_loopに呼び出し導線追加 - 優先順位: sum_bc → multi_carrier → simple - [funcs/basic:loop.multi_carrier]タグ出力 - Module export設定: - lang/src/mir/hako_module.toml: sum_bc/multi_carrier追加 - nyash.toml: 対応するmodule path追加 既存mode完全保持(Rust Freeze遵守): - count, sum_bcは一切変更なし - multi_countは完全に独立して追加 - 既存テストへの影響ゼロ Technical Details: - PHI構造: 3-PHI (i, a, b) in Header - Block構成: Preheader → Header → Body → Latch → Exit - Fibonacci計算: t = a+b, a' = b, b' = t - copy命令でLatchから Headerへ値を渡す Task先生調査結果を反映: - Rust層のパターンC(4-PHI, multi-carrier)に対応 - MirSchemaBox経由で型安全なMIR生成 Next: スモークテスト追加、既存テスト全通確認 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
347 lines
14 KiB
Plaintext
347 lines
14 KiB
Plaintext
// selfhost/shared/mir/loop_form_box.hako
|
|
// LoopFormBox — minimal loop structure builder (P2: continue/break snapshots + Exit PHI)
|
|
|
|
using selfhost.shared.mir.schema as MirSchemaBox
|
|
|
|
static box LoopFormBox {
|
|
|
|
// while (i < limit) {
|
|
// if (i == break_value) break;
|
|
// if (i == skip_value) continue;
|
|
// sum = sum + i;
|
|
// i = i + 1;
|
|
// }
|
|
// Builds LoopForm structure with Header/Latch PHI + Exit PHI (continue/break aware)
|
|
method loop_counter(limit, skip_value, break_value) {
|
|
if skip_value == null { skip_value = 2 }
|
|
if break_value == null { break_value = limit }
|
|
|
|
// Preheader (block 0): initialise loop-carried values and constants, then jump header
|
|
local pre = new ArrayBox()
|
|
pre.push(MirSchemaBox.inst_const(1, 0)) // r1 = 0 (initial i)
|
|
pre.push(MirSchemaBox.inst_const(2, limit)) // r2 = limit
|
|
pre.push(MirSchemaBox.inst_const(3, 1)) // r3 = step
|
|
pre.push(MirSchemaBox.inst_const(4, 0)) // r4 = sum
|
|
pre.push(MirSchemaBox.inst_const(5, skip_value)) // r5 = continue trigger
|
|
pre.push(MirSchemaBox.inst_const(6, break_value))// r6 = break trigger
|
|
pre.push(MirSchemaBox.inst_jump(1)) // goto header
|
|
|
|
// Header (block 1): PHI(i), PHI(sum), compare, branch
|
|
local header = new ArrayBox()
|
|
local phi_i_inputs = new ArrayBox()
|
|
phi_i_inputs.push(MirSchemaBox.phi_incoming(0, 1)) // from preheader
|
|
phi_i_inputs.push(MirSchemaBox.phi_incoming(7, 18)) // from latch
|
|
header.push(MirSchemaBox.inst_phi(10, phi_i_inputs)) // r10 = current i
|
|
|
|
local phi_sum_inputs = new ArrayBox()
|
|
phi_sum_inputs.push(MirSchemaBox.phi_incoming(0, 4)) // from preheader
|
|
phi_sum_inputs.push(MirSchemaBox.phi_incoming(7, 19)) // from latch
|
|
header.push(MirSchemaBox.inst_phi(11, phi_sum_inputs))// r11 = current sum
|
|
|
|
header.push(MirSchemaBox.inst_compare("Lt", 10, 2, 12)) // r12 = (i < limit)
|
|
header.push(MirSchemaBox.inst_branch(12, 2, 8)) // body or exit
|
|
|
|
// Body entry (block 2): if (i == break) -> break else check continue
|
|
local body_check_break = new ArrayBox()
|
|
body_check_break.push(MirSchemaBox.inst_compare("Eq", 10, 6, 13)) // r13 = (i == break)
|
|
body_check_break.push(MirSchemaBox.inst_branch(13, 3, 4))
|
|
|
|
// Break path (block 3): jump directly to exit; exit PHI snapshots current sum
|
|
local break_block = new ArrayBox()
|
|
break_block.push(MirSchemaBox.inst_jump(8))
|
|
|
|
// Continue check (block 4): if (i == skip) -> continue else body work
|
|
local continue_check = new ArrayBox()
|
|
continue_check.push(MirSchemaBox.inst_compare("Eq", 10, 5, 14)) // r14 = (i == skip)
|
|
continue_check.push(MirSchemaBox.inst_branch(14, 5, 6))
|
|
|
|
// Continue path (block 5): snapshot sum, increment i, jump latch
|
|
local continue_block = new ArrayBox()
|
|
continue_block.push(MirSchemaBox.inst_binop("Add", 10, 3, 15)) // r15 = i + step
|
|
continue_block.push(MirSchemaBox.inst_jump(7))
|
|
|
|
// Body work (block 6): sum += i; i += step; jump latch
|
|
local body_block = new ArrayBox()
|
|
body_block.push(MirSchemaBox.inst_binop("Add", 11, 10, 16)) // r16 = sum + i
|
|
body_block.push(MirSchemaBox.inst_binop("Add", 10, 3, 17)) // r17 = i + step
|
|
body_block.push(MirSchemaBox.inst_jump(7))
|
|
|
|
// Latch (block 7): PHI merge for continue/normal, then jump header
|
|
local latch_block = new ArrayBox()
|
|
local latch_phi_i = new ArrayBox()
|
|
latch_phi_i.push(MirSchemaBox.phi_incoming(5, 15)) // continue path
|
|
latch_phi_i.push(MirSchemaBox.phi_incoming(6, 17)) // body path
|
|
latch_block.push(MirSchemaBox.inst_phi(18, latch_phi_i)) // feeds header.i
|
|
|
|
local latch_phi_sum = new ArrayBox()
|
|
latch_phi_sum.push(MirSchemaBox.phi_incoming(5, 11)) // continue keeps sum
|
|
latch_phi_sum.push(MirSchemaBox.phi_incoming(6, 16)) // body updated sum
|
|
latch_block.push(MirSchemaBox.inst_phi(19, latch_phi_sum)) // feeds header.sum
|
|
latch_block.push(MirSchemaBox.inst_jump(1))
|
|
|
|
// Exit (block 8): Merge sums from header fallthrough and break edge, then return
|
|
local exit_vals = new ArrayBox()
|
|
exit_vals.push(MirSchemaBox.phi_incoming(1, 11)) // normal completion
|
|
exit_vals.push(MirSchemaBox.phi_incoming(3, 11)) // break path
|
|
local exit_block = new ArrayBox()
|
|
exit_block.push(MirSchemaBox.inst_phi(20, exit_vals))
|
|
exit_block.push(MirSchemaBox.inst_ret(20))
|
|
|
|
// Assemble blocks
|
|
local blocks = new ArrayBox()
|
|
blocks.push(MirSchemaBox.block(0, pre))
|
|
blocks.push(MirSchemaBox.block(1, header))
|
|
blocks.push(MirSchemaBox.block(2, body_check_break))
|
|
blocks.push(MirSchemaBox.block(3, break_block))
|
|
blocks.push(MirSchemaBox.block(4, continue_check))
|
|
blocks.push(MirSchemaBox.block(5, continue_block))
|
|
blocks.push(MirSchemaBox.block(6, body_block))
|
|
blocks.push(MirSchemaBox.block(7, latch_block))
|
|
blocks.push(MirSchemaBox.block(8, exit_block))
|
|
|
|
return MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
|
}
|
|
|
|
// loop_count — Minimal counting loop that returns the final counter value i
|
|
// Shape:
|
|
// preheader: r1=0 (i0), r2=limit, r3=1 (step), jump header
|
|
// header: r10 = PHI(i), r11 = (i < limit), branch then:body else:exit
|
|
// body: r12 = i + step, jump latch
|
|
// latch: jump header (PHI incoming from body)
|
|
// exit: ret r10
|
|
method loop_count(limit) {
|
|
// Preheader
|
|
local pre = new ArrayBox()
|
|
pre.push(MirSchemaBox.inst_const(1, 0))
|
|
pre.push(MirSchemaBox.inst_const(2, limit))
|
|
pre.push(MirSchemaBox.inst_const(3, 1))
|
|
pre.push(MirSchemaBox.inst_jump(1))
|
|
|
|
// Header
|
|
local header = new ArrayBox()
|
|
local inc = new ArrayBox(); inc.push(MirSchemaBox.phi_incoming(0, 1)); inc.push(MirSchemaBox.phi_incoming(3, 12))
|
|
header.push(MirSchemaBox.inst_phi(10, inc))
|
|
header.push(MirSchemaBox.inst_compare("Lt", 10, 2, 11))
|
|
header.push(MirSchemaBox.inst_branch(11, 2, 4))
|
|
|
|
// Body
|
|
local body = new ArrayBox()
|
|
body.push(MirSchemaBox.inst_binop("Add", 10, 3, 12))
|
|
body.push(MirSchemaBox.inst_jump(3))
|
|
|
|
// Latch
|
|
local latch = new ArrayBox()
|
|
latch.push(MirSchemaBox.inst_jump(1))
|
|
|
|
// Exit
|
|
local exit = new ArrayBox()
|
|
exit.push(MirSchemaBox.inst_ret(10))
|
|
|
|
local blocks = new ArrayBox()
|
|
blocks.push(MirSchemaBox.block(0, pre))
|
|
blocks.push(MirSchemaBox.block(1, header))
|
|
blocks.push(MirSchemaBox.block(2, body))
|
|
blocks.push(MirSchemaBox.block(3, latch))
|
|
blocks.push(MirSchemaBox.block(4, exit))
|
|
return MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
|
}
|
|
|
|
// loop_count_param — counting loop with init/step parameters
|
|
// Returns final i value, starting from init, incremented by step while i < limit
|
|
method build_loop_count_param(start_value, limit, step) {
|
|
// Preheader
|
|
local pre = new ArrayBox()
|
|
pre.push(MirSchemaBox.inst_const(1, start_value))
|
|
pre.push(MirSchemaBox.inst_const(2, limit))
|
|
pre.push(MirSchemaBox.inst_const(3, step))
|
|
pre.push(MirSchemaBox.inst_jump(1))
|
|
|
|
// Header
|
|
local header = new ArrayBox()
|
|
local inc = new ArrayBox(); inc.push(MirSchemaBox.phi_incoming(0, 1)); inc.push(MirSchemaBox.phi_incoming(3, 12))
|
|
header.push(MirSchemaBox.inst_phi(10, inc))
|
|
header.push(MirSchemaBox.inst_compare("Lt", 10, 2, 11))
|
|
header.push(MirSchemaBox.inst_branch(11, 2, 4))
|
|
|
|
// Body
|
|
local body = new ArrayBox()
|
|
body.push(MirSchemaBox.inst_binop("Add", 10, 3, 12))
|
|
body.push(MirSchemaBox.inst_jump(3))
|
|
|
|
// Latch
|
|
local latch = new ArrayBox(); latch.push(MirSchemaBox.inst_jump(1))
|
|
|
|
// Exit
|
|
local exit = new ArrayBox(); exit.push(MirSchemaBox.inst_ret(10))
|
|
|
|
local blocks = new ArrayBox()
|
|
blocks.push(MirSchemaBox.block(0, pre))
|
|
blocks.push(MirSchemaBox.block(1, header))
|
|
blocks.push(MirSchemaBox.block(2, body))
|
|
blocks.push(MirSchemaBox.block(3, latch))
|
|
blocks.push(MirSchemaBox.block(4, exit))
|
|
return MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
|
}
|
|
|
|
// Extended param variant: allow custom compare op ("Lt"/"Le"/"Gt"/"Ge") via opts
|
|
// and negative step (handled by passing negative string for step)
|
|
method build_loop_count_param_ex(start_value, limit, step, cmp) {
|
|
local cmpop = "" + cmp
|
|
if cmpop == null || cmpop == "" { cmpop = "Lt" }
|
|
|
|
// Preheader
|
|
local pre = new ArrayBox()
|
|
pre.push(MirSchemaBox.inst_const(1, start_value))
|
|
pre.push(MirSchemaBox.inst_const(2, limit))
|
|
pre.push(MirSchemaBox.inst_const(3, step))
|
|
pre.push(MirSchemaBox.inst_jump(1))
|
|
|
|
// Header
|
|
local header = new ArrayBox()
|
|
local inc = new ArrayBox(); inc.push(MirSchemaBox.phi_incoming(0, 1)); inc.push(MirSchemaBox.phi_incoming(3, 12))
|
|
header.push(MirSchemaBox.inst_phi(10, inc))
|
|
header.push(MirSchemaBox.inst_compare(cmpop, 10, 2, 11))
|
|
header.push(MirSchemaBox.inst_branch(11, 2, 4))
|
|
|
|
// Body
|
|
local body = new ArrayBox()
|
|
body.push(MirSchemaBox.inst_binop("Add", 10, 3, 12))
|
|
body.push(MirSchemaBox.inst_jump(3))
|
|
|
|
// Latch
|
|
local latch = new ArrayBox(); latch.push(MirSchemaBox.inst_jump(1))
|
|
|
|
// Exit
|
|
local exit = new ArrayBox(); exit.push(MirSchemaBox.inst_ret(10))
|
|
|
|
local blocks = new ArrayBox()
|
|
blocks.push(MirSchemaBox.block(0, pre))
|
|
blocks.push(MirSchemaBox.block(1, header))
|
|
blocks.push(MirSchemaBox.block(2, body))
|
|
blocks.push(MirSchemaBox.block(3, latch))
|
|
blocks.push(MirSchemaBox.block(4, exit))
|
|
return MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
|
}
|
|
|
|
// Unified entry — build(mode, limit, skip_value, break_value)
|
|
// mode:
|
|
// - "count" : counting loop that returns final i (uses loop_count)
|
|
// - "sum_bc" : sum with break/continue sentinels (uses loop_counter)
|
|
method build(mode, limit, skip_value, break_value) {
|
|
local m = "" + mode
|
|
if m == "count" {
|
|
return me.loop_count(limit)
|
|
}
|
|
if m == "count_param" {
|
|
// Here, skip_value carries init and break_value carries step (temporary param slots)
|
|
local start_value = skip_value
|
|
local step = break_value
|
|
if start_value == null { start_value = 0 }
|
|
if step == null { step = 1 }
|
|
return me.build_loop_count_param(start_value, limit, step)
|
|
}
|
|
if m == "sum_bc" {
|
|
if skip_value == null { skip_value = 2 }
|
|
if break_value == null { break_value = limit }
|
|
return me.loop_counter(limit, skip_value, break_value)
|
|
}
|
|
print("[loopform/unsupported-mode] " + m)
|
|
return null
|
|
}
|
|
|
|
// Map-based builder: build2({ mode, init, limit, step, skip, break, carriers })
|
|
method build2(opts) {
|
|
if opts == null { return null }
|
|
local mode = "" + opts.get("mode")
|
|
local start_value = opts.get("init")
|
|
local limit = opts.get("limit")
|
|
local step = opts.get("step")
|
|
local skip_v = opts.get("skip")
|
|
local break_v = opts.get("break")
|
|
if mode == "count" {
|
|
if start_value == null { start_value = 0 }
|
|
if step == null { step = 1 }
|
|
local cmp = opts.get("cmp") // optional: "Lt"/"Le"/"Gt"/"Ge"
|
|
if cmp == null { cmp = "Lt" }
|
|
return me.build_loop_count_param_ex(start_value, limit, step, cmp)
|
|
}
|
|
if mode == "sum_bc" { return me.loop_counter(limit, skip_v, break_v) }
|
|
if mode == "multi_count" {
|
|
return me.build_loop_multi_carrier(opts)
|
|
}
|
|
print("[loopform/unsupported-mode] " + mode)
|
|
return null
|
|
}
|
|
|
|
// Multi-carrier loop (fibonacci-style: a, b, i tracking)
|
|
// Shape: i from 0 to limit, with 2 additional carried variables (a, b)
|
|
// carriers param: [init_a, init_b] (e.g. [0, 1] for fibonacci)
|
|
method build_loop_multi_carrier(opts) {
|
|
local limit = opts.get("limit")
|
|
if limit == null { limit = 10 }
|
|
local carriers = opts.get("carriers")
|
|
local init_a = 0
|
|
local init_b = 1
|
|
if carriers != null && carriers.length() >= 2 {
|
|
init_a = carriers.get(0)
|
|
init_b = carriers.get(1)
|
|
}
|
|
|
|
// Preheader (block 0): init i=0, limit, a=init_a, b=init_b
|
|
local pre = new ArrayBox()
|
|
pre.push(MirSchemaBox.inst_const(1, 0)) // r1 = 0 (i)
|
|
pre.push(MirSchemaBox.inst_const(2, limit)) // r2 = limit
|
|
pre.push(MirSchemaBox.inst_const(3, init_a)) // r3 = init_a
|
|
pre.push(MirSchemaBox.inst_const(4, init_b)) // r4 = init_b
|
|
pre.push(MirSchemaBox.inst_jump(1))
|
|
|
|
// Header (block 1): PHI(i), PHI(a), PHI(b), compare, branch
|
|
local header = new ArrayBox()
|
|
local phi_i_inc = new ArrayBox()
|
|
phi_i_inc.push(MirSchemaBox.phi_incoming(0, 1)) // from preheader
|
|
phi_i_inc.push(MirSchemaBox.phi_incoming(3, 17)) // from latch
|
|
header.push(MirSchemaBox.inst_phi(10, phi_i_inc)) // r10 = i
|
|
|
|
local phi_a_inc = new ArrayBox()
|
|
phi_a_inc.push(MirSchemaBox.phi_incoming(0, 3)) // from preheader
|
|
phi_a_inc.push(MirSchemaBox.phi_incoming(3, 18)) // from latch
|
|
header.push(MirSchemaBox.inst_phi(11, phi_a_inc)) // r11 = a
|
|
|
|
local phi_b_inc = new ArrayBox()
|
|
phi_b_inc.push(MirSchemaBox.phi_incoming(0, 4)) // from preheader
|
|
phi_b_inc.push(MirSchemaBox.phi_incoming(3, 19)) // from latch
|
|
header.push(MirSchemaBox.inst_phi(12, phi_b_inc)) // r12 = b
|
|
|
|
header.push(MirSchemaBox.inst_compare("Lt", 10, 2, 13)) // r13 = (i < limit)
|
|
header.push(MirSchemaBox.inst_branch(13, 2, 4)) // body or exit
|
|
|
|
// Body (block 2): t = a + b; a' = b; b' = t; i' = i + 1
|
|
local body = new ArrayBox()
|
|
body.push(MirSchemaBox.inst_binop("Add", 11, 12, 14)) // r14 = a + b (t)
|
|
body.push(MirSchemaBox.inst_const(20, 1)) // r20 = step (1)
|
|
body.push(MirSchemaBox.inst_binop("Add", 10, 20, 15)) // r15 = i + 1
|
|
body.push(MirSchemaBox.inst_jump(3))
|
|
|
|
// Latch (block 3): pass updated values (i', a'=b, b'=t) back to header
|
|
local latch = new ArrayBox()
|
|
latch.push(MirSchemaBox.inst_copy(15, 17)) // r17 = i'
|
|
latch.push(MirSchemaBox.inst_copy(12, 18)) // r18 = a' (=b)
|
|
latch.push(MirSchemaBox.inst_copy(14, 19)) // r19 = b' (=t)
|
|
latch.push(MirSchemaBox.inst_jump(1))
|
|
|
|
// Exit (block 4): return final b value
|
|
local exit = new ArrayBox()
|
|
exit.push(MirSchemaBox.inst_ret(12))
|
|
|
|
// Assemble blocks
|
|
local blocks = new ArrayBox()
|
|
blocks.push(MirSchemaBox.block(0, pre))
|
|
blocks.push(MirSchemaBox.block(1, header))
|
|
blocks.push(MirSchemaBox.block(2, body))
|
|
blocks.push(MirSchemaBox.block(3, latch))
|
|
blocks.push(MirSchemaBox.block(4, exit))
|
|
|
|
return MirSchemaBox.module(MirSchemaBox.fn_main(blocks))
|
|
}
|
|
}
|