Files
hakorune/lang/src/llvm_ir/boxes/aot_prep.hako

163 lines
6.4 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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。失敗時は入力パスを返すFailFastは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 }
// Phase1: 文字列正規化(安定化)
// いまは canonicalize は恒等将来はHostBridgeでキー順安定化
local canon = MirIoBox.normalize(src)
// Phase2: 安全な単一ブロック const/binop(+,-,*)/ret の畳み込み(最小実装)
// 備考: まずは main 関数を優先対象とし、成立時のみ最小 JSON に置換(今後は inplace 置換へ段階移行)。
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
}
}