inline intrinsics
This commit is contained in:
367
mcode.cm
367
mcode.cm
@@ -879,6 +879,77 @@ var mcode = function(ast) {
|
||||
var inline_some = true
|
||||
var inline_reduce = true
|
||||
var inline_map = true
|
||||
var inline_find = true
|
||||
|
||||
// --- Helper: emit arity-dispatched callback invocation ---
|
||||
// ctx = {fn, fn_arity, result, null_s, frame, zero, one, az, ao, prefix}
|
||||
// args = [slot_for_arg1, slot_for_arg2] — data args (not this)
|
||||
// max_args = 1 or 2 — how many data args to support
|
||||
var emit_arity_call = function(ctx, args, max_args) {
|
||||
var call_one = gen_label(ctx.prefix + "_c1")
|
||||
var call_two = gen_label(ctx.prefix + "_c2")
|
||||
var call_done = gen_label(ctx.prefix + "_cd")
|
||||
emit_3("eq", ctx.az, ctx.fn_arity, ctx.zero)
|
||||
emit_jump_cond("jump_false", ctx.az, call_one)
|
||||
emit_3("frame", ctx.frame, ctx.fn, 0)
|
||||
emit_3("setarg", ctx.frame, 0, ctx.null_s)
|
||||
emit_2("invoke", ctx.frame, ctx.result)
|
||||
emit_jump(call_done)
|
||||
emit_label(call_one)
|
||||
if (max_args >= 2) {
|
||||
emit_3("eq", ctx.ao, ctx.fn_arity, ctx.one)
|
||||
emit_jump_cond("jump_false", ctx.ao, call_two)
|
||||
}
|
||||
emit_3("frame", ctx.frame, ctx.fn, 1)
|
||||
emit_3("setarg", ctx.frame, 0, ctx.null_s)
|
||||
emit_3("setarg", ctx.frame, 1, args[0])
|
||||
emit_2("invoke", ctx.frame, ctx.result)
|
||||
if (max_args < 2) {
|
||||
emit_label(call_done)
|
||||
return null
|
||||
}
|
||||
emit_jump(call_done)
|
||||
emit_label(call_two)
|
||||
emit_3("frame", ctx.frame, ctx.fn, 2)
|
||||
emit_3("setarg", ctx.frame, 0, ctx.null_s)
|
||||
emit_3("setarg", ctx.frame, 1, args[0])
|
||||
emit_3("setarg", ctx.frame, 2, args[1])
|
||||
emit_2("invoke", ctx.frame, ctx.result)
|
||||
emit_label(call_done)
|
||||
return null
|
||||
}
|
||||
|
||||
// --- Helper: forward loop scaffolding ---
|
||||
// L = {arr, len, i, check, item, one, loop_label, done_label}
|
||||
// body_fn(L) — called between element load and increment
|
||||
var emit_forward_loop = function(L, body_fn) {
|
||||
emit_2("int", L.i, 0)
|
||||
emit_label(L.loop_label)
|
||||
emit_3("lt", L.check, L.i, L.len)
|
||||
emit_jump_cond("jump_false", L.check, L.done_label)
|
||||
emit_3("load_index", L.item, L.arr, L.i)
|
||||
body_fn(L)
|
||||
emit_3("add", L.i, L.i, L.one)
|
||||
emit_jump(L.loop_label)
|
||||
emit_label(L.done_label)
|
||||
return null
|
||||
}
|
||||
|
||||
// --- Helper: reverse loop scaffolding ---
|
||||
var emit_reverse_loop = function(L, body_fn) {
|
||||
var zero = alloc_slot()
|
||||
emit_2("int", zero, 0)
|
||||
emit_3("subtract", L.i, L.len, L.one)
|
||||
emit_label(L.loop_label)
|
||||
emit_3("ge", L.check, L.i, zero)
|
||||
emit_jump_cond("jump_false", L.check, L.done_label)
|
||||
emit_3("load_index", L.item, L.arr, L.i)
|
||||
body_fn(L)
|
||||
emit_3("subtract", L.i, L.i, L.one)
|
||||
emit_jump(L.loop_label)
|
||||
emit_label(L.done_label)
|
||||
return null
|
||||
}
|
||||
|
||||
// --- Helper: emit a reduce loop body ---
|
||||
// r = {acc, i, arr, fn, len, fn_arity}; emits loop updating acc in-place.
|
||||
@@ -895,13 +966,12 @@ var mcode = function(ast) {
|
||||
var null_s = alloc_slot()
|
||||
var one = alloc_slot()
|
||||
var zero = alloc_slot()
|
||||
var arity_is_zero = alloc_slot()
|
||||
var arity_is_one = alloc_slot()
|
||||
var az = alloc_slot()
|
||||
var ao = alloc_slot()
|
||||
var f = alloc_slot()
|
||||
var loop_label = gen_label("reduce_loop")
|
||||
var call_one_label = gen_label("reduce_call_one")
|
||||
var call_two_label = gen_label("reduce_call_two")
|
||||
var call_done_label = gen_label("reduce_call_done")
|
||||
var ctx = {fn: fn_slot, fn_arity: fn_arity, result: acc, null_s: null_s,
|
||||
frame: f, zero: zero, one: one, az: az, ao: ao, prefix: "reduce"}
|
||||
emit_2("int", one, 1)
|
||||
emit_2("int", zero, 0)
|
||||
emit_1("null", null_s)
|
||||
@@ -913,27 +983,7 @@ var mcode = function(ast) {
|
||||
}
|
||||
emit_jump_cond("jump_false", check, done_label)
|
||||
emit_3("load_index", item, arr_slot, i)
|
||||
emit_3("eq", arity_is_zero, fn_arity, zero)
|
||||
emit_jump_cond("jump_false", arity_is_zero, call_one_label)
|
||||
emit_3("frame", f, fn_slot, 0)
|
||||
emit_3("setarg", f, 0, null_s)
|
||||
emit_2("invoke", f, acc)
|
||||
emit_jump(call_done_label)
|
||||
emit_label(call_one_label)
|
||||
emit_3("eq", arity_is_one, fn_arity, one)
|
||||
emit_jump_cond("jump_false", arity_is_one, call_two_label)
|
||||
emit_3("frame", f, fn_slot, 1)
|
||||
emit_3("setarg", f, 0, null_s)
|
||||
emit_3("setarg", f, 1, acc)
|
||||
emit_2("invoke", f, acc)
|
||||
emit_jump(call_done_label)
|
||||
emit_label(call_two_label)
|
||||
emit_3("frame", f, fn_slot, 2)
|
||||
emit_3("setarg", f, 0, null_s)
|
||||
emit_3("setarg", f, 1, acc)
|
||||
emit_3("setarg", f, 2, item)
|
||||
emit_2("invoke", f, acc)
|
||||
emit_label(call_done_label)
|
||||
emit_arity_call(ctx, [acc, item], 2)
|
||||
if (forward) {
|
||||
emit_3("add", i, i, one)
|
||||
} else {
|
||||
@@ -942,60 +992,63 @@ var mcode = function(ast) {
|
||||
emit_jump(loop_label)
|
||||
}
|
||||
|
||||
// --- Inline expansion: arrfor(arr, fn) ---
|
||||
var expand_inline_arrfor = function(dest, arr_slot, fn_slot) {
|
||||
// --- Inline expansion: arrfor(arr, fn[, rev[, exit]]) ---
|
||||
var expand_inline_arrfor = function(dest, args, nargs) {
|
||||
var arr_slot = args.arr
|
||||
var fn_slot = args.fn
|
||||
var len = alloc_slot()
|
||||
var i = alloc_slot()
|
||||
var check = alloc_slot()
|
||||
var item = alloc_slot()
|
||||
var fn_arity = alloc_slot()
|
||||
var arity_is_zero = alloc_slot()
|
||||
var arity_is_one = alloc_slot()
|
||||
var az = alloc_slot()
|
||||
var ao = alloc_slot()
|
||||
var null_s = alloc_slot()
|
||||
var zero = alloc_slot()
|
||||
var one = alloc_slot()
|
||||
var f = alloc_slot()
|
||||
var discard = alloc_slot()
|
||||
var loop_label = gen_label("arrfor_loop")
|
||||
var done_label = gen_label("arrfor_done")
|
||||
var call_one_label = gen_label("arrfor_call_one")
|
||||
var call_two_label = gen_label("arrfor_call_two")
|
||||
var call_done_label = gen_label("arrfor_call_done")
|
||||
var val = alloc_slot()
|
||||
var eq_check = alloc_slot()
|
||||
var early_exit = gen_label("arrfor_exit")
|
||||
var done_final = gen_label("arrfor_final")
|
||||
var rev_label = gen_label("arrfor_rev")
|
||||
var final_label = gen_label("arrfor_fwd_done")
|
||||
var fwd_L = {arr: arr_slot, len: len, i: i, check: check, item: item, one: one,
|
||||
loop_label: gen_label("arrfor_fwd"), done_label: gen_label("arrfor_fwd_d")}
|
||||
var rev_L = {arr: arr_slot, len: len, i: i, check: check, item: item, one: one,
|
||||
loop_label: gen_label("arrfor_rev_l"), done_label: gen_label("arrfor_rev_d")}
|
||||
var ctx = {fn: fn_slot, fn_arity: fn_arity, result: val, null_s: null_s,
|
||||
frame: f, zero: zero, one: one, az: az, ao: ao, prefix: "arrfor"}
|
||||
var body_fn = function(L) {
|
||||
emit_arity_call(ctx, [L.item, L.i], 2)
|
||||
if (nargs >= 4 && args.exit >= 0) {
|
||||
emit_3("eq", eq_check, val, args.exit)
|
||||
emit_jump_cond("jump_true", eq_check, early_exit)
|
||||
}
|
||||
return null
|
||||
}
|
||||
emit_2("length", len, arr_slot)
|
||||
emit_2("int", i, 0)
|
||||
emit_2("int", zero, 0)
|
||||
emit_2("int", one, 1)
|
||||
emit_1("null", null_s)
|
||||
emit_2("length", fn_arity, fn_slot)
|
||||
emit_label(loop_label)
|
||||
emit_3("lt", check, i, len)
|
||||
emit_jump_cond("jump_false", check, done_label)
|
||||
emit_3("load_index", item, arr_slot, i)
|
||||
emit_3("eq", arity_is_zero, fn_arity, zero)
|
||||
emit_jump_cond("jump_false", arity_is_zero, call_one_label)
|
||||
emit_3("frame", f, fn_slot, 0)
|
||||
emit_3("setarg", f, 0, null_s)
|
||||
emit_2("invoke", f, discard)
|
||||
emit_jump(call_done_label)
|
||||
emit_label(call_one_label)
|
||||
emit_3("eq", arity_is_one, fn_arity, one)
|
||||
emit_jump_cond("jump_false", arity_is_one, call_two_label)
|
||||
emit_3("frame", f, fn_slot, 1)
|
||||
emit_3("setarg", f, 0, null_s)
|
||||
emit_3("setarg", f, 1, item)
|
||||
emit_2("invoke", f, discard)
|
||||
emit_jump(call_done_label)
|
||||
emit_label(call_two_label)
|
||||
emit_3("frame", f, fn_slot, 2)
|
||||
emit_3("setarg", f, 0, null_s)
|
||||
emit_3("setarg", f, 1, item)
|
||||
emit_3("setarg", f, 2, i)
|
||||
emit_2("invoke", f, discard)
|
||||
emit_label(call_done_label)
|
||||
emit_3("add", i, i, one)
|
||||
emit_jump(loop_label)
|
||||
emit_label(done_label)
|
||||
if (nargs <= 2) {
|
||||
emit_forward_loop(fwd_L, body_fn)
|
||||
} else {
|
||||
emit_jump_cond("jump_true", args.rev, rev_label)
|
||||
emit_forward_loop(fwd_L, body_fn)
|
||||
emit_jump(final_label)
|
||||
emit_label(rev_label)
|
||||
emit_reverse_loop(rev_L, body_fn)
|
||||
emit_label(final_label)
|
||||
}
|
||||
emit_1("null", dest)
|
||||
emit_jump(done_final)
|
||||
if (nargs >= 4 && args.exit >= 0) {
|
||||
emit_label(early_exit)
|
||||
emit_2("move", dest, val)
|
||||
}
|
||||
emit_label(done_final)
|
||||
return dest
|
||||
}
|
||||
|
||||
@@ -1113,61 +1166,151 @@ var mcode = function(ast) {
|
||||
var check = alloc_slot()
|
||||
var item = alloc_slot()
|
||||
var fn_arity = alloc_slot()
|
||||
var arity_is_zero = alloc_slot()
|
||||
var arity_is_one = alloc_slot()
|
||||
var az = alloc_slot()
|
||||
var ao = alloc_slot()
|
||||
var null_s = alloc_slot()
|
||||
var zero = alloc_slot()
|
||||
var one = alloc_slot()
|
||||
var f = alloc_slot()
|
||||
var val = alloc_slot()
|
||||
var loop_label = gen_label("filter_loop")
|
||||
var call_one_label = gen_label("filter_call_one")
|
||||
var call_two_label = gen_label("filter_call_two")
|
||||
var call_done_label = gen_label("filter_call_done")
|
||||
var skip_label = gen_label("filter_skip")
|
||||
var done_label = gen_label("filter_done")
|
||||
var skip = gen_label("filter_skip")
|
||||
var ctx = {fn: fn_slot, fn_arity: fn_arity, result: val, null_s: null_s,
|
||||
frame: f, zero: zero, one: one, az: az, ao: ao, prefix: "filter"}
|
||||
var L = {arr: arr_slot, len: len, i: i, check: check, item: item, one: one,
|
||||
loop_label: gen_label("filter_loop"), done_label: gen_label("filter_done")}
|
||||
add_instr(["array", result, 0])
|
||||
emit_2("length", len, arr_slot)
|
||||
emit_2("int", i, 0)
|
||||
emit_2("int", zero, 0)
|
||||
emit_2("int", one, 1)
|
||||
emit_1("null", null_s)
|
||||
emit_2("length", fn_arity, fn_slot)
|
||||
emit_label(loop_label)
|
||||
emit_3("lt", check, i, len)
|
||||
emit_jump_cond("jump_false", check, done_label)
|
||||
emit_3("load_index", item, arr_slot, i)
|
||||
emit_3("eq", arity_is_zero, fn_arity, zero)
|
||||
emit_jump_cond("jump_false", arity_is_zero, call_one_label)
|
||||
emit_3("frame", f, fn_slot, 0)
|
||||
emit_3("setarg", f, 0, null_s)
|
||||
emit_2("invoke", f, val)
|
||||
emit_jump(call_done_label)
|
||||
emit_label(call_one_label)
|
||||
emit_3("eq", arity_is_one, fn_arity, one)
|
||||
emit_jump_cond("jump_false", arity_is_one, call_two_label)
|
||||
emit_3("frame", f, fn_slot, 1)
|
||||
emit_3("setarg", f, 0, null_s)
|
||||
emit_3("setarg", f, 1, item)
|
||||
emit_2("invoke", f, val)
|
||||
emit_jump(call_done_label)
|
||||
emit_label(call_two_label)
|
||||
emit_3("frame", f, fn_slot, 2)
|
||||
emit_3("setarg", f, 0, null_s)
|
||||
emit_3("setarg", f, 1, item)
|
||||
emit_3("setarg", f, 2, i)
|
||||
emit_2("invoke", f, val)
|
||||
emit_label(call_done_label)
|
||||
emit_jump_cond("jump_false", val, skip_label)
|
||||
emit_2("push", result, item)
|
||||
emit_label(skip_label)
|
||||
emit_3("add", i, i, one)
|
||||
emit_jump(loop_label)
|
||||
emit_label(done_label)
|
||||
emit_forward_loop(L, function(L) {
|
||||
emit_arity_call(ctx, [L.item, L.i], 2)
|
||||
emit_jump_cond("jump_false", val, skip)
|
||||
emit_2("push", result, L.item)
|
||||
emit_label(skip)
|
||||
return null
|
||||
})
|
||||
emit_2("move", dest, result)
|
||||
return dest
|
||||
}
|
||||
|
||||
// --- Inline expansion: find(arr, target[, rev[, from]]) ---
|
||||
var expand_inline_find = function(dest, args, nargs) {
|
||||
var arr_slot = args.arr
|
||||
var target = args.target
|
||||
var len = alloc_slot()
|
||||
var i = alloc_slot()
|
||||
var check = alloc_slot()
|
||||
var item = alloc_slot()
|
||||
var fn_arity = alloc_slot()
|
||||
var az = alloc_slot()
|
||||
var ao = alloc_slot()
|
||||
var null_s = alloc_slot()
|
||||
var zero = alloc_slot()
|
||||
var one = alloc_slot()
|
||||
var f = alloc_slot()
|
||||
var val = alloc_slot()
|
||||
var is_fn = alloc_slot()
|
||||
var eq_check = alloc_slot()
|
||||
var fn_mode_label = gen_label("find_fn")
|
||||
var found_label = gen_label("find_found")
|
||||
var not_found_label = gen_label("find_nf")
|
||||
var final_label = gen_label("find_final")
|
||||
var vrev = gen_label("find_vrev")
|
||||
var vdone = gen_label("find_vdone")
|
||||
var frev = gen_label("find_frev")
|
||||
var fdone = gen_label("find_fdone")
|
||||
var vL = {arr: arr_slot, len: len, i: i, check: check, item: item, one: one,
|
||||
loop_label: gen_label("find_vl"), done_label: gen_label("find_vd")}
|
||||
var vrL = {arr: arr_slot, len: len, i: i, check: check, item: item, one: one,
|
||||
loop_label: gen_label("find_vrl"), done_label: gen_label("find_vrd")}
|
||||
var fL = {arr: arr_slot, len: len, i: i, check: check, item: item, one: one,
|
||||
loop_label: gen_label("find_fl"), done_label: gen_label("find_fd")}
|
||||
var ffL = {arr: arr_slot, len: len, i: i, check: check, item: item, one: one,
|
||||
loop_label: gen_label("find_ffl"), done_label: gen_label("find_ffd")}
|
||||
var frL = {arr: arr_slot, len: len, i: i, check: check, item: item, one: one,
|
||||
loop_label: gen_label("find_frl"), done_label: gen_label("find_frd")}
|
||||
var ctx = {fn: target, fn_arity: fn_arity, result: val, null_s: null_s,
|
||||
frame: f, zero: zero, one: one, az: az, ao: ao, prefix: "find"}
|
||||
var val_body = function(L) {
|
||||
emit_3("eq", eq_check, L.item, target)
|
||||
emit_jump_cond("jump_true", eq_check, found_label)
|
||||
return null
|
||||
}
|
||||
var fn_body = function(L) {
|
||||
emit_arity_call(ctx, [L.item, L.i], 2)
|
||||
emit_jump_cond("jump_true", val, found_label)
|
||||
return null
|
||||
}
|
||||
emit_2("length", len, arr_slot)
|
||||
emit_2("int", zero, 0)
|
||||
emit_2("int", one, 1)
|
||||
emit_1("null", null_s)
|
||||
emit_2("is_func", is_fn, target)
|
||||
emit_jump_cond("jump_true", is_fn, fn_mode_label)
|
||||
// === Value mode ===
|
||||
if (nargs <= 2) {
|
||||
emit_forward_loop(vL, val_body)
|
||||
} else {
|
||||
emit_jump_cond("jump_true", args.rev, vrev)
|
||||
if (nargs >= 4 && args.from >= 0) {
|
||||
emit_2("move", i, args.from)
|
||||
}
|
||||
if (nargs >= 4 && args.from >= 0) {
|
||||
emit_label(vL.loop_label)
|
||||
emit_3("lt", vL.check, vL.i, vL.len)
|
||||
emit_jump_cond("jump_false", vL.check, vL.done_label)
|
||||
emit_3("load_index", vL.item, vL.arr, vL.i)
|
||||
val_body(vL)
|
||||
emit_3("add", vL.i, vL.i, vL.one)
|
||||
emit_jump(vL.loop_label)
|
||||
emit_label(vL.done_label)
|
||||
} else {
|
||||
emit_forward_loop(vL, val_body)
|
||||
}
|
||||
emit_jump(vdone)
|
||||
emit_label(vrev)
|
||||
emit_reverse_loop(vrL, val_body)
|
||||
emit_label(vdone)
|
||||
}
|
||||
emit_jump(not_found_label)
|
||||
// === Function mode ===
|
||||
emit_label(fn_mode_label)
|
||||
emit_2("length", fn_arity, target)
|
||||
if (nargs <= 2) {
|
||||
emit_forward_loop(fL, fn_body)
|
||||
} else {
|
||||
emit_jump_cond("jump_true", args.rev, frev)
|
||||
if (nargs >= 4 && args.from >= 0) {
|
||||
emit_2("move", i, args.from)
|
||||
}
|
||||
if (nargs >= 4 && args.from >= 0) {
|
||||
emit_label(ffL.loop_label)
|
||||
emit_3("lt", ffL.check, ffL.i, ffL.len)
|
||||
emit_jump_cond("jump_false", ffL.check, ffL.done_label)
|
||||
emit_3("load_index", ffL.item, ffL.arr, ffL.i)
|
||||
fn_body(ffL)
|
||||
emit_3("add", ffL.i, ffL.i, ffL.one)
|
||||
emit_jump(ffL.loop_label)
|
||||
emit_label(ffL.done_label)
|
||||
} else {
|
||||
emit_forward_loop(ffL, fn_body)
|
||||
}
|
||||
emit_jump(fdone)
|
||||
emit_label(frev)
|
||||
emit_reverse_loop(frL, fn_body)
|
||||
emit_label(fdone)
|
||||
}
|
||||
emit_label(not_found_label)
|
||||
emit_1("null", dest)
|
||||
emit_jump(final_label)
|
||||
emit_label(found_label)
|
||||
emit_2("move", dest, i)
|
||||
emit_label(final_label)
|
||||
return dest
|
||||
}
|
||||
|
||||
// --- Inline expansion: array(arr, fn) → map ---
|
||||
var expand_inline_map = function(dest, arr_slot, fn_slot) {
|
||||
var result = alloc_slot()
|
||||
@@ -1993,11 +2136,13 @@ var mcode = function(ast) {
|
||||
return a1
|
||||
}
|
||||
// Callback intrinsics → inline mcode loops
|
||||
if (nargs == 2 && fname == "arrfor" && inline_arrfor) {
|
||||
if (fname == "arrfor" && nargs >= 2 && nargs <= 4 && inline_arrfor) {
|
||||
a0 = gen_expr(args_list[0], -1)
|
||||
a1 = gen_expr(args_list[1], -1)
|
||||
a2 = nargs >= 3 ? gen_expr(args_list[2], -1) : -1
|
||||
a3 = nargs >= 4 ? gen_expr(args_list[3], -1) : -1
|
||||
d = alloc_slot()
|
||||
return expand_inline_arrfor(d, a0, a1)
|
||||
return expand_inline_arrfor(d, {arr: a0, fn: a1, rev: a2, exit: a3}, nargs)
|
||||
}
|
||||
if (nargs == 2 && fname == "every" && inline_every) {
|
||||
a0 = gen_expr(args_list[0], -1)
|
||||
@@ -2017,6 +2162,14 @@ var mcode = function(ast) {
|
||||
d = alloc_slot()
|
||||
return expand_inline_filter(d, a0, a1)
|
||||
}
|
||||
if (fname == "find" && nargs >= 2 && nargs <= 4 && inline_find) {
|
||||
a0 = gen_expr(args_list[0], -1)
|
||||
a1 = gen_expr(args_list[1], -1)
|
||||
a2 = nargs >= 3 ? gen_expr(args_list[2], -1) : -1
|
||||
a3 = nargs >= 4 ? gen_expr(args_list[3], -1) : -1
|
||||
d = alloc_slot()
|
||||
return expand_inline_find(d, {arr: a0, target: a1, rev: a2, from: a3}, nargs)
|
||||
}
|
||||
if (fname == "reduce" && nargs >= 2 && nargs <= 4 && inline_reduce) {
|
||||
a0 = gen_expr(args_list[0], -1)
|
||||
a1 = gen_expr(args_list[1], -1)
|
||||
|
||||
@@ -2595,12 +2595,11 @@ var streamline = function(ir, log) {
|
||||
reduce: true, array: true
|
||||
}
|
||||
|
||||
var can_inline = function(callee_func, is_prefer) {
|
||||
// Structural eligibility: closures, get/put, disruption, nested functions
|
||||
var can_inline_structural = function(callee_func) {
|
||||
var instrs = null
|
||||
var i = 0
|
||||
var instr = null
|
||||
var count = 0
|
||||
var limit = 0
|
||||
if (callee_func.nr_close_slots > 0) return false
|
||||
instrs = callee_func.instructions
|
||||
if (instrs == null) return false
|
||||
@@ -2622,7 +2621,16 @@ var streamline = function(ir, log) {
|
||||
if (callee_func.disruption_pc != null && callee_func.disruption_pc > 0) {
|
||||
return false
|
||||
}
|
||||
count = 0
|
||||
return true
|
||||
}
|
||||
|
||||
// Size eligibility: instruction count check
|
||||
var can_inline_size = function(callee_func, is_prefer) {
|
||||
var instrs = callee_func.instructions
|
||||
var count = 0
|
||||
var i = 0
|
||||
var limit = 0
|
||||
if (instrs == null) return false
|
||||
i = 0
|
||||
while (i < length(instrs)) {
|
||||
if (is_array(instrs[i])) count = count + 1
|
||||
@@ -2632,6 +2640,11 @@ var streamline = function(ir, log) {
|
||||
return count <= limit
|
||||
}
|
||||
|
||||
var can_inline = function(callee_func, is_prefer) {
|
||||
if (!can_inline_structural(callee_func)) return false
|
||||
return can_inline_size(callee_func, is_prefer)
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// Pass: inline_calls — inline same-module + sensory functions
|
||||
// =========================================================
|
||||
@@ -2673,6 +2686,9 @@ var streamline = function(ir, log) {
|
||||
var inlined_body = null
|
||||
var fi = null
|
||||
var intrinsic_name = null
|
||||
var is_single_use = false
|
||||
var ref_count = 0
|
||||
var ri = 0
|
||||
|
||||
if (instructions == null) return false
|
||||
num_instr = length(instructions)
|
||||
@@ -2767,8 +2783,33 @@ var streamline = function(ir, log) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if callee is a single-use function literal — skip size limit
|
||||
is_single_use = false
|
||||
if (fi != null) {
|
||||
ref_count = 0
|
||||
ri = 0
|
||||
while (ri < length(instructions)) {
|
||||
if (is_array(instructions[ri])) {
|
||||
// Count frame instructions that use this slot as callee (position 2)
|
||||
if (instructions[ri][0] == "frame" && instructions[ri][2] == callee_slot) {
|
||||
ref_count = ref_count + 1
|
||||
}
|
||||
// Also count setarg where slot is passed as value (position 3)
|
||||
if (instructions[ri][0] == "setarg" && instructions[ri][3] == callee_slot) {
|
||||
ref_count = ref_count + 1
|
||||
}
|
||||
}
|
||||
ri = ri + 1
|
||||
}
|
||||
if (ref_count <= 1) is_single_use = true
|
||||
}
|
||||
|
||||
// Check eligibility
|
||||
if (!can_inline(callee_func, is_prefer)) {
|
||||
if (!can_inline_structural(callee_func)) {
|
||||
i = i + 1
|
||||
continue
|
||||
}
|
||||
if (!is_single_use && !can_inline_size(callee_func, is_prefer)) {
|
||||
i = i + 1
|
||||
continue
|
||||
}
|
||||
|
||||
181
vm_suite.ce
181
vm_suite.ce
@@ -5982,6 +5982,187 @@ run("gc closure - factory pattern survives gc", function() {
|
||||
assert_eq(b.say(), "hello bob", "second factory closure")
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// INLINE LOOP EXPANSION TESTS
|
||||
// ============================================================================
|
||||
|
||||
// --- filter inline expansion ---
|
||||
|
||||
run("filter inline - integer predicate", function() {
|
||||
var result = filter([0, 1.25, 2, 3.5, 4, 5.75], is_integer)
|
||||
assert_eq(length(result), 3, "filter integer count")
|
||||
assert_eq(result[0], 0, "filter integer [0]")
|
||||
assert_eq(result[1], 2, "filter integer [1]")
|
||||
assert_eq(result[2], 4, "filter integer [2]")
|
||||
})
|
||||
|
||||
run("filter inline - all pass", function() {
|
||||
var result = filter([1, 2, 3], function(x) { return true })
|
||||
assert_eq(length(result), 3, "filter all pass length")
|
||||
})
|
||||
|
||||
run("filter inline - none pass", function() {
|
||||
var result = filter([1, 2, 3], function(x) { return false })
|
||||
assert_eq(length(result), 0, "filter none pass length")
|
||||
})
|
||||
|
||||
run("filter inline - empty", function() {
|
||||
var result = filter([], is_integer)
|
||||
assert_eq(length(result), 0, "filter empty length")
|
||||
})
|
||||
|
||||
run("filter inline - with index", function() {
|
||||
var result = filter([10, 20, 30], function(e, i) { return i > 0 })
|
||||
assert_eq(length(result), 2, "filter index length")
|
||||
assert_eq(result[0], 20, "filter index [0]")
|
||||
assert_eq(result[1], 30, "filter index [1]")
|
||||
})
|
||||
|
||||
run("filter inline - large callback", function() {
|
||||
var result = filter([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], function(x) {
|
||||
var a = x * 2
|
||||
var b = a + 1
|
||||
var c = b * 3
|
||||
var d = c - a
|
||||
return d > 20
|
||||
})
|
||||
if (length(result) < 1) fail("filter large callback should return elements")
|
||||
})
|
||||
|
||||
// --- find inline expansion ---
|
||||
|
||||
run("find inline - function forward", function() {
|
||||
var idx = find([1, 2, 3], function(x) { return x == 2 })
|
||||
assert_eq(idx, 1, "find fn forward")
|
||||
})
|
||||
|
||||
run("find inline - function not found", function() {
|
||||
var idx = find([1, 2, 3], function(x) { return x == 99 })
|
||||
assert_eq(idx, null, "find fn not found")
|
||||
})
|
||||
|
||||
run("find inline - value forward", function() {
|
||||
var idx = find([10, 20, 30], 20)
|
||||
assert_eq(idx, 1, "find value forward")
|
||||
})
|
||||
|
||||
run("find inline - value not found", function() {
|
||||
var idx = find([10, 20, 30], 99)
|
||||
assert_eq(idx, null, "find value not found")
|
||||
})
|
||||
|
||||
run("find inline - reverse", function() {
|
||||
var idx = find([1, 2, 1, 2], function(x) { return x == 1 }, true)
|
||||
assert_eq(idx, 2, "find reverse")
|
||||
})
|
||||
|
||||
run("find inline - from", function() {
|
||||
var idx = find([1, 2, 3, 2], function(x) { return x == 2 }, false, 2)
|
||||
assert_eq(idx, 3, "find from")
|
||||
})
|
||||
|
||||
run("find inline - empty", function() {
|
||||
var idx = find([], 1)
|
||||
assert_eq(idx, null, "find empty")
|
||||
})
|
||||
|
||||
run("find inline - value reverse", function() {
|
||||
var idx = find([10, 20, 30, 20], 20, true)
|
||||
assert_eq(idx, 3, "find value reverse")
|
||||
})
|
||||
|
||||
run("find inline - value with from", function() {
|
||||
var idx = find([10, 20, 30, 20], 20, false, 2)
|
||||
assert_eq(idx, 3, "find value with from")
|
||||
})
|
||||
|
||||
run("find inline - with index callback", function() {
|
||||
var idx = find(["a", "b", "c"], (x, i) => i == 2)
|
||||
assert_eq(idx, 2, "find index callback")
|
||||
})
|
||||
|
||||
// --- arrfor inline expansion ---
|
||||
|
||||
run("arrfor inline - basic sum", function() {
|
||||
var sum = 0
|
||||
arrfor([1, 2, 3, 4, 5], function(x) { sum = sum + x })
|
||||
assert_eq(sum, 15, "arrfor basic sum")
|
||||
})
|
||||
|
||||
run("arrfor inline - reverse", function() {
|
||||
var order = []
|
||||
arrfor([1, 2, 3], function(x) { order[] = x }, true)
|
||||
assert_eq(order[0], 3, "arrfor reverse [0]")
|
||||
assert_eq(order[1], 2, "arrfor reverse [1]")
|
||||
assert_eq(order[2], 1, "arrfor reverse [2]")
|
||||
})
|
||||
|
||||
run("arrfor inline - exit", function() {
|
||||
var result = arrfor([1, 2, 3, 4, 5], function(x) {
|
||||
if (x > 3) return true
|
||||
return null
|
||||
}, false, true)
|
||||
assert_eq(result, true, "arrfor exit")
|
||||
})
|
||||
|
||||
run("arrfor inline - no exit returns null", function() {
|
||||
var result = arrfor([1, 2, 3], function(x) { })
|
||||
assert_eq(result, null, "arrfor no exit null")
|
||||
})
|
||||
|
||||
run("arrfor inline - with index", function() {
|
||||
var indices = []
|
||||
arrfor([10, 20, 30], (x, i) => { indices[] = i })
|
||||
assert_eq(indices[0], 0, "arrfor index [0]")
|
||||
assert_eq(indices[1], 1, "arrfor index [1]")
|
||||
assert_eq(indices[2], 2, "arrfor index [2]")
|
||||
})
|
||||
|
||||
run("arrfor inline - reverse with index", function() {
|
||||
var items = []
|
||||
arrfor(["a", "b", "c"], function(x, i) { items[] = text(i) + x }, true)
|
||||
assert_eq(items[0], "2c", "arrfor rev index [0]")
|
||||
assert_eq(items[1], "1b", "arrfor rev index [1]")
|
||||
assert_eq(items[2], "0a", "arrfor rev index [2]")
|
||||
})
|
||||
|
||||
// --- reduce inline expansion ---
|
||||
|
||||
run("reduce inline - no initial forward", function() {
|
||||
var result = reduce([1, 2, 3, 4, 5, 6, 7, 8, 9], function(a, b) { return a + b })
|
||||
assert_eq(result, 45, "reduce sum 1-9")
|
||||
})
|
||||
|
||||
run("reduce inline - single element", function() {
|
||||
var result = reduce([42], function(a, b) { return a + b })
|
||||
assert_eq(result, 42, "reduce single")
|
||||
})
|
||||
|
||||
run("reduce inline - empty", function() {
|
||||
var result = reduce([], function(a, b) { return a + b })
|
||||
assert_eq(result, null, "reduce empty")
|
||||
})
|
||||
|
||||
run("reduce inline - with initial", function() {
|
||||
var result = reduce([1, 2, 3], function(a, b) { return a + b }, 10)
|
||||
assert_eq(result, 16, "reduce with initial")
|
||||
})
|
||||
|
||||
run("reduce inline - with initial empty", function() {
|
||||
var result = reduce([], function(a, b) { return a + b }, 99)
|
||||
assert_eq(result, 99, "reduce initial empty")
|
||||
})
|
||||
|
||||
run("reduce inline - reverse", function() {
|
||||
var result = reduce([1, 2, 3], function(a, b) { return a - b }, 0, true)
|
||||
assert_eq(result, -6, "reduce reverse")
|
||||
})
|
||||
|
||||
run("reduce inline - intrinsic callback", function() {
|
||||
var result = reduce([3, 7, 2, 9, 1], max)
|
||||
assert_eq(result, 9, "reduce max")
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// SUMMARY
|
||||
// ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user