fix(aot/numeric_core): implement PHI type propagation for copy→phi→copy chains
- Add propagate_copy_types() to track MatI64 through copy instructions
- Fix PHI detection bug: indexOf("{") → indexOf("\"op\":\"")
- Add 4-iteration loop for multi-step propagation chains
- Enhance diagnostics with MatI64 vids list and skip reasons
This fixes type propagation for complex SSA patterns where MatI64 types
flow through multiple copy and phi instructions. Small test cases now
pass successfully.
Note: microbench matmul_core still has issues - investigating separately.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -22,10 +22,41 @@ static box AotPrepNumericCoreBox {
|
|||||||
|
|
||||||
// Phase 25 MVP: Build type table and transform BoxCall → Call
|
// Phase 25 MVP: Build type table and transform BoxCall → Call
|
||||||
local tmap = AotPrepNumericCoreBox.build_type_table(text, trace)
|
local tmap = AotPrepNumericCoreBox.build_type_table(text, trace)
|
||||||
// Propagate MatI64 type through simple PHI chains(全incomingがMatI64ならdstもMatI64とみなす)
|
|
||||||
tmap = AotPrepNumericCoreBox.propagate_phi_types(text, tmap, trace)
|
|
||||||
local copy_map = AotPrepNumericCoreBox.build_copy_map(text, trace)
|
local copy_map = AotPrepNumericCoreBox.build_copy_map(text, trace)
|
||||||
|
|
||||||
|
// Iterative type propagation: alternate copy and phi propagation to handle chains
|
||||||
|
// like: MatI64.new() → r2 → copy → r10 → phi → r15 → copy → r31
|
||||||
|
local iter = 0
|
||||||
|
loop(iter < 4) {
|
||||||
|
tmap = AotPrepNumericCoreBox.propagate_copy_types(text, tmap, trace)
|
||||||
|
tmap = AotPrepNumericCoreBox.propagate_phi_types(text, tmap, trace)
|
||||||
|
iter = iter + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Diagnostic: show all MatI64 vids if trace enabled
|
||||||
|
if trace != null && ("" + trace) == "1" {
|
||||||
|
local mat_vids = new ArrayBox()
|
||||||
|
local keys = tmap.keys()
|
||||||
|
local ki = 0
|
||||||
|
loop(ki < keys.size()) {
|
||||||
|
local key = keys.get(ki)
|
||||||
|
if tmap.get(key) == "MatI64" {
|
||||||
|
mat_vids.push("r" + key)
|
||||||
|
}
|
||||||
|
ki = ki + 1
|
||||||
|
}
|
||||||
|
if mat_vids.size() > 0 {
|
||||||
|
local vids_str = ""
|
||||||
|
local vi = 0
|
||||||
|
loop(vi < mat_vids.size()) {
|
||||||
|
if vi > 0 { vids_str = vids_str + "," }
|
||||||
|
vids_str = vids_str + mat_vids.get(vi)
|
||||||
|
vi = vi + 1
|
||||||
|
}
|
||||||
|
print("[aot/numeric_core] MatI64 vids: " + vids_str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
local result = AotPrepNumericCoreBox.transform_boxcalls(text, tmap, copy_map, trace)
|
local result = AotPrepNumericCoreBox.transform_boxcalls(text, tmap, copy_map, trace)
|
||||||
|
|
||||||
// If nothing happened and trace is ON, surface a hint
|
// If nothing happened and trace is ON, surface a hint
|
||||||
@ -94,13 +125,6 @@ static box AotPrepNumericCoreBox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if op == "copy" {
|
|
||||||
// Propagate type through copy
|
|
||||||
local dst = AotPrepNumericCoreBox.read_digits_field(inst, "dst")
|
|
||||||
local src = AotPrepNumericCoreBox.read_digits_field(inst, "src")
|
|
||||||
if dst != "" && src != "" && tmap.has(src) {
|
|
||||||
tmap.set(dst, tmap.get(src))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pos = obj_end + 1
|
pos = obj_end + 1
|
||||||
@ -149,6 +173,42 @@ static box AotPrepNumericCoreBox {
|
|||||||
return cmap
|
return cmap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Propagate MatI64 type through copy instructions
|
||||||
|
// Scans all copy ops and propagates type from src to dst
|
||||||
|
propagate_copy_types(text, tmap, trace) {
|
||||||
|
local pos = 0
|
||||||
|
loop(true) {
|
||||||
|
local op_marker = text.indexOf("\"op\":\"", pos)
|
||||||
|
if op_marker < 0 { break }
|
||||||
|
|
||||||
|
local obj_start = text.substring(0, op_marker).lastIndexOf("{")
|
||||||
|
if obj_start < 0 { pos = op_marker + 1 continue }
|
||||||
|
|
||||||
|
local obj_end = AotPrepHelpers._seek_object_end(text, obj_start)
|
||||||
|
if obj_end <= obj_start { pos = op_marker + 1 continue }
|
||||||
|
|
||||||
|
local inst = text.substring(obj_start, obj_end + 1)
|
||||||
|
local op = AotPrepNumericCoreBox.read_field(inst, "op")
|
||||||
|
|
||||||
|
if op == "copy" {
|
||||||
|
local dst = AotPrepNumericCoreBox.read_digits_field(inst, "dst")
|
||||||
|
local src = AotPrepNumericCoreBox.read_digits_field(inst, "src")
|
||||||
|
if dst != "" && src != "" && tmap.has(src) && !tmap.has(dst) {
|
||||||
|
local src_type = tmap.get(src)
|
||||||
|
if src_type == "MatI64" {
|
||||||
|
tmap.set(dst, "MatI64")
|
||||||
|
if trace != null && ("" + trace) == "1" {
|
||||||
|
print("[aot/numeric_core] copy-prop MatI64: r" + src + " → r" + dst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = obj_end + 1
|
||||||
|
}
|
||||||
|
return tmap
|
||||||
|
}
|
||||||
|
|
||||||
// Propagate MatI64 type information through phi nodes.
|
// Propagate MatI64 type information through phi nodes.
|
||||||
// Policy: 「少なくとも1つ MatI64 で、かつMatI64以外の型が混ざっていない」場合に dst を MatI64 とみなす。
|
// Policy: 「少なくとも1つ MatI64 で、かつMatI64以外の型が混ざっていない」場合に dst を MatI64 とみなす。
|
||||||
propagate_phi_types(text, tmap, trace) {
|
propagate_phi_types(text, tmap, trace) {
|
||||||
@ -158,10 +218,17 @@ static box AotPrepNumericCoreBox {
|
|||||||
changed = 0
|
changed = 0
|
||||||
local pos = 0
|
local pos = 0
|
||||||
loop(true) {
|
loop(true) {
|
||||||
local obj_start = text.indexOf("{", pos)
|
// Find next "op":" marker (same pattern as build_type_table/propagate_copy_types)
|
||||||
if obj_start < 0 { break }
|
local op_marker = text.indexOf("\"op\":\"", pos)
|
||||||
|
if op_marker < 0 { break }
|
||||||
|
|
||||||
|
// Find the start of this instruction object (the { before "op")
|
||||||
|
local obj_start = text.substring(0, op_marker).lastIndexOf("{")
|
||||||
|
if obj_start < 0 { pos = op_marker + 1 continue }
|
||||||
|
|
||||||
|
// Find the end of this instruction object
|
||||||
local obj_end = AotPrepHelpers._seek_object_end(text, obj_start)
|
local obj_end = AotPrepHelpers._seek_object_end(text, obj_start)
|
||||||
if obj_end <= obj_start { pos = obj_start + 1 continue }
|
if obj_end <= obj_start { pos = op_marker + 1 continue }
|
||||||
|
|
||||||
local inst = text.substring(obj_start, obj_end + 1)
|
local inst = text.substring(obj_start, obj_end + 1)
|
||||||
local op = AotPrepNumericCoreBox.read_field(inst, "op")
|
local op = AotPrepNumericCoreBox.read_field(inst, "op")
|
||||||
@ -197,7 +264,7 @@ static box AotPrepNumericCoreBox {
|
|||||||
tmap.set(dst, "MatI64")
|
tmap.set(dst, "MatI64")
|
||||||
changed = 1
|
changed = 1
|
||||||
if trace != null && ("" + trace) == "1" {
|
if trace != null && ("" + trace) == "1" {
|
||||||
print("[aot/numeric_core] phi-prop MatI64 at r" + dst)
|
print("[aot/numeric_core] phi-prop MatI64: r" + dst)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -259,6 +326,23 @@ static box AotPrepNumericCoreBox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Diagnostic logging for skipped transformations
|
||||||
|
if should_transform == 0 && method == "mul_naive" {
|
||||||
|
local reason = "unknown"
|
||||||
|
if box_vid == "" {
|
||||||
|
reason = "no_box_vid"
|
||||||
|
} else if !tmap.has(box_vid) {
|
||||||
|
reason = "type_unknown"
|
||||||
|
} else if tmap.get(box_vid) != "MatI64" {
|
||||||
|
reason = "type_conflict (expected MatI64, got " + tmap.get(box_vid) + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
if trace != null && ("" + trace) == "1" {
|
||||||
|
local dst = AotPrepNumericCoreBox.read_digits_field(inst, "dst")
|
||||||
|
print("[aot/numeric_core] skip mul_naive@r" + dst + ": box=r" + box_vid + " reason=" + reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if should_transform == 1 {
|
if should_transform == 1 {
|
||||||
// Transform BoxCall → Call
|
// Transform BoxCall → Call
|
||||||
// Copy text before this boxcall
|
// Copy text before this boxcall
|
||||||
|
|||||||
Reference in New Issue
Block a user