163 lines
6.4 KiB
Plaintext
163 lines
6.4 KiB
Plaintext
// aot_prep.hako — AotPrepBox (pre-MIR normalizer/optimizer; skeleton)
|
||
// 入出力(最小仕様)
|
||
// - AotPrepBox.prep/1(json_in_path: String) -> String (json_out_path)
|
||
// 責務
|
||
// - JSON(MIR v0) の軽量正規化(キー順/冗長キー削除)と安全な const/binop(+,-,*)/ret の単一ブロック畳み込み
|
||
// - 既定ではパススルー(Rust 側 maybe_prepare_mir_json が実体)。段階的にこちらへ移管する
|
||
|
||
using "lang/src/shared/mir/mir_io_box.hako" as MirIoBox
|
||
using "lang/src/shared/common/string_helpers.hako" as StringHelpers
|
||
|
||
static box AotPrepBox {
|
||
// AotPrepBox.prep
|
||
// 入力: JSONファイルパス(MIR v0)
|
||
// 出力: 正規化後のJSONを書き出したパス(<in>.prep.json)。失敗時は入力パスを返す(Fail‑FastはRust側が継続)。
|
||
prep(json_in_path) {
|
||
if !json_in_path { return json_in_path }
|
||
// Read input
|
||
local fb = new FileBox()
|
||
fb.open(json_in_path, "r")
|
||
local src = fb.read()
|
||
fb.close()
|
||
if !src { return json_in_path }
|
||
|
||
// Phase‑1: 文字列正規化(安定化)
|
||
// いまは canonicalize は恒等(将来はHostBridgeでキー順安定化)
|
||
local canon = MirIoBox.normalize(src)
|
||
|
||
// Phase‑2: 安全な単一ブロック const/binop(+,-,*)/ret の畳み込み(最小実装)
|
||
// 備考: まずは main 関数を優先対象とし、成立時のみ最小 JSON に置換(今後は in‑place 置換へ段階移行)。
|
||
local out = AotPrepBox._try_fold_const_binop_ret(canon)
|
||
if !out { out = canon }
|
||
|
||
// Decide output path
|
||
local out_path = json_in_path + ".prep.json"
|
||
fb.open(out_path, "w")
|
||
fb.write(out)
|
||
fb.close()
|
||
return out_path
|
||
}
|
||
|
||
// 内部: 最小の安全畳み込み(JSON文字列ベース)
|
||
_try_fold_const_binop_ret(json) {
|
||
if !json { return null }
|
||
// Helper: find the [ ... ] span of the first block's instructions for the function near `start_from`.
|
||
local find_instr_span_from = fun(s, start_from) {
|
||
local key = "\"instructions\":["
|
||
local pos = s.indexOf(key, start_from)
|
||
if pos < 0 { return [-1, -1] }
|
||
local ls = s.indexOf("[", pos)
|
||
if ls < 0 { return [-1, -1] }
|
||
local depth = 0
|
||
local i = ls
|
||
local L = s.size()
|
||
local rs = -1
|
||
loop(i < L) {
|
||
local ch = s.substring(i, i+1)
|
||
if ch == "[" { depth = depth + 1 }
|
||
if ch == "]" { depth = depth - 1; if depth == 0 { rs = i; break } }
|
||
i = i + 1
|
||
}
|
||
return [ls, rs]
|
||
}
|
||
// Helper: attempt to fold within a given [arr_start, arr_end] span; return replaced JSON on success
|
||
local try_fold_in_span = fun(s, arr_start, arr_end) {
|
||
if arr_start < 0 || arr_end < 0 { return null }
|
||
local body = s.substring(arr_start, arr_end+1)
|
||
// Need two const, a binop, and a ret in this span
|
||
local p1 = body.indexOf("\"op\":\"const\"")
|
||
local p2 = body.indexOf("\"op\":\"const\"", (p1>=0 ? (p1+1) : 0))
|
||
local pb = body.indexOf("\"op\":\"binop\"", (p2>=0 ? (p2+1) : 0))
|
||
local pr = body.indexOf("\"op\":\"ret\"", (pb>=0 ? (pb+1) : 0))
|
||
if p1 < 0 || p2 < 0 || pb < 0 || pr < 0 { return null }
|
||
// parse helpers within body
|
||
local parse_dst = fun(ss, pos) {
|
||
local k = "\"dst\":"
|
||
local i = ss.indexOf(k, pos)
|
||
if i < 0 { return -1 }
|
||
i = i + k.size()
|
||
local digs = StringHelpers.read_digits(ss, i)
|
||
if digs == "" { return -1 }
|
||
return StringHelpers.to_i64(digs)
|
||
}
|
||
local parse_val = fun(ss, pos) {
|
||
local k = "\"value\":{\"type\":\"i64\",\"value\":"
|
||
local i = ss.indexOf(k, pos)
|
||
if i < 0 { return null }
|
||
i = i + k.size()
|
||
local digs = StringHelpers.read_digits(ss, i)
|
||
if digs == "" { return null }
|
||
return StringHelpers.to_i64(digs)
|
||
}
|
||
local d1 = parse_dst(body, p1)
|
||
local a = parse_val(body, p1)
|
||
local d2 = parse_dst(body, p2)
|
||
local b = parse_val(body, p2)
|
||
if d1 < 0 || d2 < 0 || a == null || b == null { return null }
|
||
local find_num = fun(ss, key, pos) {
|
||
local k = key
|
||
local i = ss.indexOf(k, pos)
|
||
if i < 0 { return -1 }
|
||
i = i + k.size()
|
||
local digs = StringHelpers.read_digits(ss, i)
|
||
if digs == "" { return -1 }
|
||
return StringHelpers.to_i64(digs)
|
||
}
|
||
local find_op = fun(ss, pos) {
|
||
local k = "\"operation\":\""
|
||
local i = ss.indexOf(k, pos)
|
||
if i < 0 { return "" }
|
||
i = i + k.size()
|
||
local j = ss.indexOf("\"", i)
|
||
if j < 0 { return "" }
|
||
return ss.substring(i, j)
|
||
}
|
||
local lhs = find_num(body, "\"lhs\":", pb)
|
||
local rhs = find_num(body, "\"rhs\":", pb)
|
||
local bop = find_op(body, pb)
|
||
local d3 = find_num(body, "\"dst\":", pb)
|
||
if lhs != d1 || rhs != d2 || d3 < 0 { return null }
|
||
local rv = find_num(body, "\"value\":", pr)
|
||
if rv != d3 { return null }
|
||
// binop allowed: +,-,* only
|
||
local res = 0
|
||
if bop == "+" { res = a + b } else { if bop == "-" { res = a - b } else { if bop == "*" { res = a * b } else { return null } } }
|
||
// build new array and replace in-place
|
||
local new_insts = "[" +
|
||
"{\\\"op\\\":\\\"const\\\",\\\"dst\\\":" + StringHelpers.int_to_str(d1) + ",\\\"value\\\":{\\\"type\\\":\\\"i64\\\",\\\"value\\\":" + StringHelpers.int_to_str(res) + "}}," +
|
||
"{\\\"op\\\":\\\"ret\\\",\\\"value\\\":" + StringHelpers.int_to_str(d1) + "}]"
|
||
local head = s.substring(0, arr_start)
|
||
local tail = s.substring(arr_end + 1, s.size())
|
||
return head + new_insts + tail
|
||
}
|
||
// Pass 1: prefer name:"main"
|
||
local fn_pos = json.indexOf("\"name\":\"main\"")
|
||
if fn_pos >= 0 {
|
||
local span = find_instr_span_from(json, fn_pos)
|
||
local ls = span[0]; local rs = span[1]
|
||
local repl = try_fold_in_span(json, ls, rs)
|
||
if repl { return repl }
|
||
}
|
||
// Pass 2: scan functions sequentially and attempt per function
|
||
local froot = json.indexOf("\"functions\":[")
|
||
if froot < 0 { return null }
|
||
local scan = froot
|
||
local tries = 0
|
||
loop(tries < 16) {
|
||
local np = json.indexOf("\"name\":\"", scan+1)
|
||
if np < 0 { break }
|
||
local span2 = find_instr_span_from(json, np)
|
||
local ls2 = span2[0]; local rs2 = span2[1]
|
||
if ls2 >= 0 && rs2 >= 0 {
|
||
local repl2 = try_fold_in_span(json, ls2, rs2)
|
||
if repl2 { return repl2 }
|
||
scan = rs2 + 1
|
||
} else {
|
||
scan = np + 8
|
||
}
|
||
tries = tries + 1
|
||
}
|
||
return null
|
||
}
|
||
}
|