more build tools
This commit is contained in:
310
diff_ir.ce
Normal file
310
diff_ir.ce
Normal file
@@ -0,0 +1,310 @@
|
||||
// diff_ir.ce — mcode vs streamline diff
|
||||
//
|
||||
// Usage:
|
||||
// cell diff_ir <file> Diff all functions
|
||||
// cell diff_ir --fn <N|name> <file> Diff only one function
|
||||
// cell diff_ir --summary <file> Counts only
|
||||
|
||||
var fd = use("fd")
|
||||
var shop = use("internal/shop")
|
||||
|
||||
var pad_right = function(s, w) {
|
||||
var r = s
|
||||
while (length(r) < w) {
|
||||
r = r + " "
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
var fmt_val = function(v) {
|
||||
if (is_null(v)) return "null"
|
||||
if (is_number(v)) return text(v)
|
||||
if (is_text(v)) return `"${v}"`
|
||||
if (is_object(v)) return text(v)
|
||||
if (is_logical(v)) return v ? "true" : "false"
|
||||
return text(v)
|
||||
}
|
||||
|
||||
var run = function() {
|
||||
var fn_filter = null
|
||||
var show_summary = false
|
||||
var filename = null
|
||||
var i = 0
|
||||
var mcode_ir = null
|
||||
var opt_ir = null
|
||||
var source_text = null
|
||||
var source_lines = null
|
||||
var main_name = null
|
||||
var fi = 0
|
||||
var func = null
|
||||
var opt_func = null
|
||||
var fname = null
|
||||
|
||||
while (i < length(args)) {
|
||||
if (args[i] == '--fn') {
|
||||
i = i + 1
|
||||
fn_filter = args[i]
|
||||
} else if (args[i] == '--summary') {
|
||||
show_summary = true
|
||||
} else if (args[i] == '--help' || args[i] == '-h') {
|
||||
log.console("Usage: cell diff_ir [--fn <N|name>] [--summary] <file>")
|
||||
log.console("")
|
||||
log.console(" --fn <N|name> Filter to function by index or name")
|
||||
log.console(" --summary Show counts only")
|
||||
return null
|
||||
} else if (!starts_with(args[i], '-')) {
|
||||
filename = args[i]
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
|
||||
if (!filename) {
|
||||
log.console("Usage: cell diff_ir [--fn <N|name>] [--summary] <file>")
|
||||
return null
|
||||
}
|
||||
|
||||
mcode_ir = shop.mcode_file(filename)
|
||||
opt_ir = shop.compile_file(filename)
|
||||
|
||||
source_text = text(fd.slurp(filename))
|
||||
source_lines = array(source_text, "\n")
|
||||
|
||||
var get_source_line = function(line_num) {
|
||||
if (line_num < 1 || line_num > length(source_lines)) return null
|
||||
return source_lines[line_num - 1]
|
||||
}
|
||||
|
||||
var fn_matches = function(index, name) {
|
||||
var match = null
|
||||
if (fn_filter == null) return true
|
||||
if (index >= 0 && fn_filter == text(index)) return true
|
||||
if (name != null) {
|
||||
match = search(name, fn_filter)
|
||||
if (match != null && match >= 0) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var fmt_instr = function(instr) {
|
||||
var op = instr[0]
|
||||
var n = length(instr)
|
||||
var parts = []
|
||||
var j = 1
|
||||
var operands = null
|
||||
var line_str = null
|
||||
while (j < n - 2) {
|
||||
push(parts, fmt_val(instr[j]))
|
||||
j = j + 1
|
||||
}
|
||||
operands = text(parts, ", ")
|
||||
line_str = instr[n - 2] != null ? `:${text(instr[n - 2])}` : ""
|
||||
return pad_right(`${pad_right(op, 15)}${operands}`, 45) + line_str
|
||||
}
|
||||
|
||||
var classify = function(before, after) {
|
||||
var bn = 0
|
||||
var an = 0
|
||||
var k = 0
|
||||
if (is_text(after) && starts_with(after, "_nop_")) return "eliminated"
|
||||
if (is_array(before) && is_array(after)) {
|
||||
if (before[0] != after[0]) return "rewritten"
|
||||
bn = length(before)
|
||||
an = length(after)
|
||||
if (bn != an) return "rewritten"
|
||||
k = 1
|
||||
while (k < bn - 2) {
|
||||
if (before[k] != after[k]) return "rewritten"
|
||||
k = k + 1
|
||||
}
|
||||
return "identical"
|
||||
}
|
||||
return "identical"
|
||||
}
|
||||
|
||||
var total_eliminated = 0
|
||||
var total_rewritten = 0
|
||||
var total_funcs = 0
|
||||
|
||||
var diff_function = function(mcode_func, opt_func, name, index) {
|
||||
var nr_args = mcode_func.nr_args != null ? mcode_func.nr_args : 0
|
||||
var nr_slots = mcode_func.nr_slots != null ? mcode_func.nr_slots : 0
|
||||
var m_instrs = mcode_func.instructions
|
||||
var o_instrs = opt_func.instructions
|
||||
var eliminated = 0
|
||||
var rewritten = 0
|
||||
var mi = 0
|
||||
var oi = 0
|
||||
var pc = 0
|
||||
var m_instr = null
|
||||
var o_instr = null
|
||||
var kind = null
|
||||
var last_line = null
|
||||
var instr_line = null
|
||||
var n = 0
|
||||
var src = null
|
||||
var annotation = null
|
||||
|
||||
if (m_instrs == null) m_instrs = []
|
||||
if (o_instrs == null) o_instrs = []
|
||||
|
||||
// First pass: count changes
|
||||
mi = 0
|
||||
oi = 0
|
||||
while (mi < length(m_instrs) && oi < length(o_instrs)) {
|
||||
m_instr = m_instrs[mi]
|
||||
o_instr = o_instrs[oi]
|
||||
|
||||
if (is_text(m_instr)) {
|
||||
mi = mi + 1
|
||||
oi = oi + 1
|
||||
continue
|
||||
}
|
||||
|
||||
if (is_text(o_instr) && starts_with(o_instr, "_nop_")) {
|
||||
if (is_array(m_instr)) {
|
||||
eliminated = eliminated + 1
|
||||
}
|
||||
mi = mi + 1
|
||||
oi = oi + 1
|
||||
continue
|
||||
}
|
||||
|
||||
if (is_array(m_instr) && is_array(o_instr)) {
|
||||
kind = classify(m_instr, o_instr)
|
||||
if (kind == "rewritten") {
|
||||
rewritten = rewritten + 1
|
||||
}
|
||||
}
|
||||
mi = mi + 1
|
||||
oi = oi + 1
|
||||
}
|
||||
|
||||
total_eliminated = total_eliminated + eliminated
|
||||
total_rewritten = total_rewritten + rewritten
|
||||
total_funcs = total_funcs + 1
|
||||
|
||||
if (show_summary) {
|
||||
if (eliminated == 0 && rewritten == 0) {
|
||||
log.compile(` ${pad_right(name + ":", 40)} 0 eliminated, 0 rewritten (unchanged)`)
|
||||
} else {
|
||||
log.compile(` ${pad_right(name + ":", 40)} ${text(eliminated)} eliminated, ${text(rewritten)} rewritten`)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
if (eliminated == 0 && rewritten == 0) return null
|
||||
|
||||
log.compile(`\n=== ${name} (args=${text(nr_args)}, slots=${text(nr_slots)}) ===`)
|
||||
log.compile(` ${text(eliminated)} eliminated, ${text(rewritten)} rewritten`)
|
||||
|
||||
// Second pass: show diffs
|
||||
mi = 0
|
||||
oi = 0
|
||||
pc = 0
|
||||
last_line = null
|
||||
while (mi < length(m_instrs) && oi < length(o_instrs)) {
|
||||
m_instr = m_instrs[mi]
|
||||
o_instr = o_instrs[oi]
|
||||
|
||||
if (is_text(m_instr) && !starts_with(m_instr, "_nop_")) {
|
||||
mi = mi + 1
|
||||
oi = oi + 1
|
||||
continue
|
||||
}
|
||||
|
||||
if (is_text(m_instr) && starts_with(m_instr, "_nop_")) {
|
||||
mi = mi + 1
|
||||
oi = oi + 1
|
||||
continue
|
||||
}
|
||||
|
||||
if (is_text(o_instr) && starts_with(o_instr, "_nop_")) {
|
||||
if (is_array(m_instr)) {
|
||||
n = length(m_instr)
|
||||
instr_line = m_instr[n - 2]
|
||||
if (instr_line != last_line && instr_line != null) {
|
||||
src = get_source_line(instr_line)
|
||||
if (src != null) src = trim(src)
|
||||
if (last_line != null) log.compile("")
|
||||
if (src != null && length(src) > 0) {
|
||||
log.compile(` --- line ${text(instr_line)}: ${src} ---`)
|
||||
}
|
||||
last_line = instr_line
|
||||
}
|
||||
log.compile(` - ${pad_right(text(pc), 6)}${fmt_instr(m_instr)}`)
|
||||
log.compile(` + ${pad_right(text(pc), 6)}${pad_right(o_instr, 45)} (eliminated)`)
|
||||
}
|
||||
mi = mi + 1
|
||||
oi = oi + 1
|
||||
pc = pc + 1
|
||||
continue
|
||||
}
|
||||
|
||||
if (is_array(m_instr) && is_array(o_instr)) {
|
||||
kind = classify(m_instr, o_instr)
|
||||
if (kind != "identical") {
|
||||
n = length(m_instr)
|
||||
instr_line = m_instr[n - 2]
|
||||
if (instr_line != last_line && instr_line != null) {
|
||||
src = get_source_line(instr_line)
|
||||
if (src != null) src = trim(src)
|
||||
if (last_line != null) log.compile("")
|
||||
if (src != null && length(src) > 0) {
|
||||
log.compile(` --- line ${text(instr_line)}: ${src} ---`)
|
||||
}
|
||||
last_line = instr_line
|
||||
}
|
||||
|
||||
annotation = ""
|
||||
if (kind == "rewritten") {
|
||||
if (search(o_instr[0], "_int") != null || search(o_instr[0], "_float") != null || search(o_instr[0], "_text") != null) {
|
||||
annotation = "(specialized)"
|
||||
} else {
|
||||
annotation = "(rewritten)"
|
||||
}
|
||||
}
|
||||
|
||||
log.compile(` - ${pad_right(text(pc), 6)}${fmt_instr(m_instr)}`)
|
||||
log.compile(` + ${pad_right(text(pc), 6)}${fmt_instr(o_instr)} ${annotation}`)
|
||||
}
|
||||
pc = pc + 1
|
||||
}
|
||||
|
||||
mi = mi + 1
|
||||
oi = oi + 1
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
// Process functions
|
||||
main_name = mcode_ir.name != null ? mcode_ir.name : "<main>"
|
||||
|
||||
if (mcode_ir.main != null && opt_ir.main != null) {
|
||||
if (fn_matches(-1, main_name)) {
|
||||
diff_function(mcode_ir.main, opt_ir.main, main_name, -1)
|
||||
}
|
||||
}
|
||||
|
||||
if (mcode_ir.functions != null && opt_ir.functions != null) {
|
||||
fi = 0
|
||||
while (fi < length(mcode_ir.functions) && fi < length(opt_ir.functions)) {
|
||||
func = mcode_ir.functions[fi]
|
||||
opt_func = opt_ir.functions[fi]
|
||||
fname = func.name != null ? func.name : "<anonymous>"
|
||||
if (fn_matches(fi, fname)) {
|
||||
diff_function(func, opt_func, `[${text(fi)}] ${fname}`, fi)
|
||||
}
|
||||
fi = fi + 1
|
||||
}
|
||||
}
|
||||
|
||||
if (show_summary) {
|
||||
log.compile(`\n total: ${text(total_eliminated)} eliminated, ${text(total_rewritten)} rewritten across ${text(total_funcs)} functions`)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
run()
|
||||
$stop()
|
||||
Reference in New Issue
Block a user