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)
|
||||
|
||||
Reference in New Issue
Block a user