aot pass all tests
This commit is contained in:
412
qbe_emit.cm
412
qbe_emit.cm
@@ -360,10 +360,93 @@ ${sw("w", "%fp", "%dest", "%r")}
|
||||
// Category C: Allocating helpers (return fp or 0)
|
||||
// ============================================================
|
||||
|
||||
// Allocating binary ops: read 2 slots, call C, refresh, write dest
|
||||
// add: int fast path in-helper, slow path calls runtime
|
||||
h[] = `export function l $__add_ss(l %ctx, l %fp, l %dest, l %s1, l %s2) {
|
||||
@entry
|
||||
${sr("a", "%s1")}
|
||||
${sr("b", "%s2")}
|
||||
%a_tag =l and %a, 1
|
||||
%b_tag =l and %b, 1
|
||||
%a_is_int =w ceql %a_tag, 0
|
||||
%b_is_int =w ceql %b_tag, 0
|
||||
%both_int =w and %a_is_int, %b_is_int
|
||||
jnz %both_int, @int_fast, @slow
|
||||
@int_fast
|
||||
%ai =l sar %a, 1
|
||||
%bi =l sar %b, 1
|
||||
%sum =l add %ai, %bi
|
||||
%sumw =w copy %sum
|
||||
%sumext =l extsw %sumw
|
||||
%sum_ok =w ceql %sumext, %sum
|
||||
jnz %sum_ok, @int_store, @slow
|
||||
@int_store
|
||||
%rtag =l shl %sum, 1
|
||||
${sw("w", "%fp", "%dest", "%rtag")}
|
||||
ret %fp
|
||||
@slow
|
||||
%r =l call $cell_rt_add(l %ctx, l %a, l %b)
|
||||
${alloc_tail("%r")}
|
||||
}`
|
||||
|
||||
// sub: int fast path in-helper, slow path calls float helper
|
||||
h[] = `export function l $__sub_ss(l %ctx, l %fp, l %dest, l %s1, l %s2) {
|
||||
@entry
|
||||
${sr("a", "%s1")}
|
||||
${sr("b", "%s2")}
|
||||
%a_tag =l and %a, 1
|
||||
%b_tag =l and %b, 1
|
||||
%a_is_int =w ceql %a_tag, 0
|
||||
%b_is_int =w ceql %b_tag, 0
|
||||
%both_int =w and %a_is_int, %b_is_int
|
||||
jnz %both_int, @int_fast, @slow
|
||||
@int_fast
|
||||
%ai =l sar %a, 1
|
||||
%bi =l sar %b, 1
|
||||
%diff =l sub %ai, %bi
|
||||
%diffw =w copy %diff
|
||||
%diffext =l extsw %diffw
|
||||
%diff_ok =w ceql %diffext, %diff
|
||||
jnz %diff_ok, @int_store, @slow
|
||||
@int_store
|
||||
%rtag =l shl %diff, 1
|
||||
${sw("w", "%fp", "%dest", "%rtag")}
|
||||
ret %fp
|
||||
@slow
|
||||
%r =l call $qbe_float_sub(l %ctx, l %a, l %b)
|
||||
${alloc_tail("%r")}
|
||||
}`
|
||||
|
||||
// mul: int fast path in-helper, slow path calls float helper
|
||||
h[] = `export function l $__mul_ss(l %ctx, l %fp, l %dest, l %s1, l %s2) {
|
||||
@entry
|
||||
${sr("a", "%s1")}
|
||||
${sr("b", "%s2")}
|
||||
%a_tag =l and %a, 1
|
||||
%b_tag =l and %b, 1
|
||||
%a_is_int =w ceql %a_tag, 0
|
||||
%b_is_int =w ceql %b_tag, 0
|
||||
%both_int =w and %a_is_int, %b_is_int
|
||||
jnz %both_int, @int_fast, @slow
|
||||
@int_fast
|
||||
%ai =l sar %a, 1
|
||||
%bi =l sar %b, 1
|
||||
%prod =l mul %ai, %bi
|
||||
%prodw =w copy %prod
|
||||
%prodext =l extsw %prodw
|
||||
%prod_ok =w ceql %prodext, %prod
|
||||
jnz %prod_ok, @int_store, @slow
|
||||
@int_store
|
||||
%rtag =l shl %prod, 1
|
||||
${sw("w", "%fp", "%dest", "%rtag")}
|
||||
ret %fp
|
||||
@slow
|
||||
%r =l call $qbe_float_mul(l %ctx, l %a, l %b)
|
||||
${alloc_tail("%r")}
|
||||
}`
|
||||
|
||||
// Remaining allocating binary ops: call C, refresh, write dest
|
||||
var ab_ops = [
|
||||
["add", "cell_rt_add"], ["sub", "qbe_float_sub"],
|
||||
["mul", "qbe_float_mul"], ["div", "qbe_float_div"],
|
||||
["div", "qbe_float_div"],
|
||||
["mod", "qbe_float_mod"], ["pow", "qbe_float_pow"],
|
||||
["concat", "JS_ConcatString"]
|
||||
]
|
||||
@@ -685,11 +768,27 @@ var qbe_emit = function(ir, qbe, export_name) {
|
||||
var si = 0
|
||||
var scan = null
|
||||
var scan_op = null
|
||||
var label_pos = {}
|
||||
var instr_idx = 0
|
||||
var has_invokes = false
|
||||
var seg_counter = 0
|
||||
var ri = 0
|
||||
var seg_num = 0
|
||||
var resume_val = 0
|
||||
var j_lbl = null
|
||||
var j_idx = null
|
||||
var jt_lbl = null
|
||||
var jt_idx = null
|
||||
var jt_backedge = false
|
||||
var jf_lbl = null
|
||||
var jf_idx = null
|
||||
var jf_backedge = false
|
||||
var jn_lbl = null
|
||||
var jn_idx = null
|
||||
var jn_backedge = false
|
||||
var jnn_lbl = null
|
||||
var jnn_idx = null
|
||||
var jnn_backedge = false
|
||||
|
||||
// Pre-scan: count invoke/tail_invoke points to assign segment numbers.
|
||||
// Must skip dead code (instructions after terminators) the same way
|
||||
@@ -701,6 +800,7 @@ var qbe_emit = function(ir, qbe, export_name) {
|
||||
scan = instrs[si]
|
||||
si = si + 1
|
||||
if (is_text(scan)) {
|
||||
label_pos[sanitize(scan)] = si - 1
|
||||
// Labels reset dead code state (unless they're nop pseudo-labels)
|
||||
if (!starts_with(scan, "_nop_ur_") && !starts_with(scan, "_nop_tc_"))
|
||||
scan_dead = false
|
||||
@@ -709,11 +809,11 @@ var qbe_emit = function(ir, qbe, export_name) {
|
||||
if (scan_dead) continue
|
||||
if (!is_array(scan)) continue
|
||||
scan_op = scan[0]
|
||||
if (scan_op == "invoke" || scan_op == "tail_invoke") {
|
||||
if (scan_op == "invoke") {
|
||||
invoke_count = invoke_count + 1
|
||||
}
|
||||
// Track terminators — same set as in the main loop
|
||||
if (scan_op == "return" || scan_op == "jump" || scan_op == "goinvoke" || scan_op == "disrupt") {
|
||||
if (scan_op == "return" || scan_op == "jump" || scan_op == "goinvoke" || scan_op == "tail_invoke" || scan_op == "disrupt") {
|
||||
scan_dead = true
|
||||
}
|
||||
}
|
||||
@@ -795,11 +895,24 @@ var qbe_emit = function(ir, qbe, export_name) {
|
||||
emit(`@${lbl}_ok`)
|
||||
}
|
||||
|
||||
// Poll pause/interrupt state on taken backward jumps.
|
||||
var emit_backedge_branch = function(target_label) {
|
||||
var chk_lbl = fresh()
|
||||
emit(` %${chk_lbl} =w call $cell_rt_check_backedge(l %ctx)`)
|
||||
if (has_handler && !in_handler) {
|
||||
emit(` jnz %${chk_lbl}, @disruption_handler, @${target_label}`)
|
||||
} else {
|
||||
needs_exc_ret = true
|
||||
emit(` jnz %${chk_lbl}, @_exc_ret, @${target_label}`)
|
||||
}
|
||||
}
|
||||
|
||||
// Walk instructions
|
||||
var last_was_term = false
|
||||
i = 0
|
||||
while (i < length(instrs)) {
|
||||
instr = instrs[i]
|
||||
instr_idx = i
|
||||
|
||||
// Emit @disruption_handler at the right flat index
|
||||
// disruption_pc counts all entries (labels + instructions)
|
||||
@@ -909,18 +1022,117 @@ var qbe_emit = function(ir, qbe, export_name) {
|
||||
// --- Generic arithmetic (VM dispatches int/float) ---
|
||||
|
||||
if (op == "add") {
|
||||
emit(` %fp =l call $__add_ss(l %ctx, l %fp, l ${text(a1)}, l ${text(a2)}, l ${text(a3)})`)
|
||||
emit_exc_check()
|
||||
lhs = s_read(a2)
|
||||
rhs = s_read(a3)
|
||||
p = fresh()
|
||||
emit(` %${p}_a_tag =l and ${lhs}, 1`)
|
||||
emit(` %${p}_b_tag =l and ${rhs}, 1`)
|
||||
emit(` %${p}_a_int =w ceql %${p}_a_tag, 0`)
|
||||
emit(` %${p}_b_int =w ceql %${p}_b_tag, 0`)
|
||||
emit(` %${p}_both_int =w and %${p}_a_int, %${p}_b_int`)
|
||||
emit(` jnz %${p}_both_int, @${p}_int, @${p}_slow`)
|
||||
emit(`@${p}_int`)
|
||||
emit(` %${p}_ai =l sar ${lhs}, 1`)
|
||||
emit(` %${p}_bi =l sar ${rhs}, 1`)
|
||||
emit(` %${p}_sum =l add %${p}_ai, %${p}_bi`)
|
||||
emit(` %${p}_sumw =w copy %${p}_sum`)
|
||||
emit(` %${p}_sumext =l extsw %${p}_sumw`)
|
||||
emit(` %${p}_sum_ok =w ceql %${p}_sumext, %${p}_sum`)
|
||||
emit(` jnz %${p}_sum_ok, @${p}_int_store, @${p}_slow`)
|
||||
emit(`@${p}_int_store`)
|
||||
emit(` %${p}_tag =l shl %${p}_sum, 1`)
|
||||
s_write(a1, `%${p}_tag`)
|
||||
emit(` jmp @${p}_done`)
|
||||
emit(`@${p}_slow`)
|
||||
emit(` %${p}_r =l call $cell_rt_add(l %ctx, l ${lhs}, l ${rhs})`)
|
||||
emit(` %fp =l call $cell_rt_refresh_fp_checked(l %ctx)`)
|
||||
chk = fresh()
|
||||
emit(` %${chk} =w ceql %fp, 0`)
|
||||
if (has_handler && !in_handler) {
|
||||
emit(` jnz %${chk}, @disruption_handler, @${chk}_ok`)
|
||||
} else {
|
||||
needs_exc_ret = true
|
||||
emit(` jnz %${chk}, @_exc_ret, @${chk}_ok`)
|
||||
}
|
||||
emit(`@${chk}_ok`)
|
||||
s_write(a1, `%${p}_r`)
|
||||
emit(`@${p}_done`)
|
||||
continue
|
||||
}
|
||||
if (op == "subtract") {
|
||||
emit(` %fp =l call $__sub_ss(l %ctx, l %fp, l ${text(a1)}, l ${text(a2)}, l ${text(a3)})`)
|
||||
emit_exc_check()
|
||||
lhs = s_read(a2)
|
||||
rhs = s_read(a3)
|
||||
p = fresh()
|
||||
emit(` %${p}_a_tag =l and ${lhs}, 1`)
|
||||
emit(` %${p}_b_tag =l and ${rhs}, 1`)
|
||||
emit(` %${p}_a_int =w ceql %${p}_a_tag, 0`)
|
||||
emit(` %${p}_b_int =w ceql %${p}_b_tag, 0`)
|
||||
emit(` %${p}_both_int =w and %${p}_a_int, %${p}_b_int`)
|
||||
emit(` jnz %${p}_both_int, @${p}_int, @${p}_slow`)
|
||||
emit(`@${p}_int`)
|
||||
emit(` %${p}_ai =l sar ${lhs}, 1`)
|
||||
emit(` %${p}_bi =l sar ${rhs}, 1`)
|
||||
emit(` %${p}_diff =l sub %${p}_ai, %${p}_bi`)
|
||||
emit(` %${p}_diffw =w copy %${p}_diff`)
|
||||
emit(` %${p}_diffext =l extsw %${p}_diffw`)
|
||||
emit(` %${p}_diff_ok =w ceql %${p}_diffext, %${p}_diff`)
|
||||
emit(` jnz %${p}_diff_ok, @${p}_int_store, @${p}_slow`)
|
||||
emit(`@${p}_int_store`)
|
||||
emit(` %${p}_tag =l shl %${p}_diff, 1`)
|
||||
s_write(a1, `%${p}_tag`)
|
||||
emit(` jmp @${p}_done`)
|
||||
emit(`@${p}_slow`)
|
||||
emit(` %${p}_r =l call $qbe_float_sub(l %ctx, l ${lhs}, l ${rhs})`)
|
||||
emit(` %fp =l call $cell_rt_refresh_fp_checked(l %ctx)`)
|
||||
chk = fresh()
|
||||
emit(` %${chk} =w ceql %fp, 0`)
|
||||
if (has_handler && !in_handler) {
|
||||
emit(` jnz %${chk}, @disruption_handler, @${chk}_ok`)
|
||||
} else {
|
||||
needs_exc_ret = true
|
||||
emit(` jnz %${chk}, @_exc_ret, @${chk}_ok`)
|
||||
}
|
||||
emit(`@${chk}_ok`)
|
||||
s_write(a1, `%${p}_r`)
|
||||
emit(`@${p}_done`)
|
||||
continue
|
||||
}
|
||||
if (op == "multiply") {
|
||||
emit(` %fp =l call $__mul_ss(l %ctx, l %fp, l ${text(a1)}, l ${text(a2)}, l ${text(a3)})`)
|
||||
emit_exc_check()
|
||||
lhs = s_read(a2)
|
||||
rhs = s_read(a3)
|
||||
p = fresh()
|
||||
emit(` %${p}_a_tag =l and ${lhs}, 1`)
|
||||
emit(` %${p}_b_tag =l and ${rhs}, 1`)
|
||||
emit(` %${p}_a_int =w ceql %${p}_a_tag, 0`)
|
||||
emit(` %${p}_b_int =w ceql %${p}_b_tag, 0`)
|
||||
emit(` %${p}_both_int =w and %${p}_a_int, %${p}_b_int`)
|
||||
emit(` jnz %${p}_both_int, @${p}_int, @${p}_slow`)
|
||||
emit(`@${p}_int`)
|
||||
emit(` %${p}_ai =l sar ${lhs}, 1`)
|
||||
emit(` %${p}_bi =l sar ${rhs}, 1`)
|
||||
emit(` %${p}_prod =l mul %${p}_ai, %${p}_bi`)
|
||||
emit(` %${p}_prodw =w copy %${p}_prod`)
|
||||
emit(` %${p}_prodext =l extsw %${p}_prodw`)
|
||||
emit(` %${p}_prod_ok =w ceql %${p}_prodext, %${p}_prod`)
|
||||
emit(` jnz %${p}_prod_ok, @${p}_int_store, @${p}_slow`)
|
||||
emit(`@${p}_int_store`)
|
||||
emit(` %${p}_tag =l shl %${p}_prod, 1`)
|
||||
s_write(a1, `%${p}_tag`)
|
||||
emit(` jmp @${p}_done`)
|
||||
emit(`@${p}_slow`)
|
||||
emit(` %${p}_r =l call $qbe_float_mul(l %ctx, l ${lhs}, l ${rhs})`)
|
||||
emit(` %fp =l call $cell_rt_refresh_fp_checked(l %ctx)`)
|
||||
chk = fresh()
|
||||
emit(` %${chk} =w ceql %fp, 0`)
|
||||
if (has_handler && !in_handler) {
|
||||
emit(` jnz %${chk}, @disruption_handler, @${chk}_ok`)
|
||||
} else {
|
||||
needs_exc_ret = true
|
||||
emit(` jnz %${chk}, @_exc_ret, @${chk}_ok`)
|
||||
}
|
||||
emit(`@${chk}_ok`)
|
||||
s_write(a1, `%${p}_r`)
|
||||
emit(`@${p}_done`)
|
||||
continue
|
||||
}
|
||||
if (op == "divide") {
|
||||
@@ -1003,27 +1215,93 @@ var qbe_emit = function(ir, qbe, export_name) {
|
||||
// --- Comparisons (int path, no GC) ---
|
||||
|
||||
if (op == "eq_int") {
|
||||
emit(` call $__eq_int_ss(l %fp, l ${text(a1)}, l ${text(a2)}, l ${text(a3)})`)
|
||||
lhs = s_read(a2)
|
||||
rhs = s_read(a3)
|
||||
p = fresh()
|
||||
emit(` %${p}_ai =l sar ${lhs}, 1`)
|
||||
emit(` %${p}_bi =l sar ${rhs}, 1`)
|
||||
emit(` %${p}_aiw =w copy %${p}_ai`)
|
||||
emit(` %${p}_biw =w copy %${p}_bi`)
|
||||
emit(` %${p}_cr =w ceqw %${p}_aiw, %${p}_biw`)
|
||||
emit(` %${p}_crext =l extuw %${p}_cr`)
|
||||
emit(` %${p}_sh =l shl %${p}_crext, 5`)
|
||||
emit(` %${p}_r =l or %${p}_sh, 3`)
|
||||
s_write(a1, `%${p}_r`)
|
||||
continue
|
||||
}
|
||||
if (op == "ne_int") {
|
||||
emit(` call $__ne_int_ss(l %fp, l ${text(a1)}, l ${text(a2)}, l ${text(a3)})`)
|
||||
lhs = s_read(a2)
|
||||
rhs = s_read(a3)
|
||||
p = fresh()
|
||||
emit(` %${p}_ai =l sar ${lhs}, 1`)
|
||||
emit(` %${p}_bi =l sar ${rhs}, 1`)
|
||||
emit(` %${p}_aiw =w copy %${p}_ai`)
|
||||
emit(` %${p}_biw =w copy %${p}_bi`)
|
||||
emit(` %${p}_cr =w cnew %${p}_aiw, %${p}_biw`)
|
||||
emit(` %${p}_crext =l extuw %${p}_cr`)
|
||||
emit(` %${p}_sh =l shl %${p}_crext, 5`)
|
||||
emit(` %${p}_r =l or %${p}_sh, 3`)
|
||||
s_write(a1, `%${p}_r`)
|
||||
continue
|
||||
}
|
||||
if (op == "lt_int") {
|
||||
emit(` call $__lt_int_ss(l %fp, l ${text(a1)}, l ${text(a2)}, l ${text(a3)})`)
|
||||
lhs = s_read(a2)
|
||||
rhs = s_read(a3)
|
||||
p = fresh()
|
||||
emit(` %${p}_ai =l sar ${lhs}, 1`)
|
||||
emit(` %${p}_bi =l sar ${rhs}, 1`)
|
||||
emit(` %${p}_aiw =w copy %${p}_ai`)
|
||||
emit(` %${p}_biw =w copy %${p}_bi`)
|
||||
emit(` %${p}_cr =w csltw %${p}_aiw, %${p}_biw`)
|
||||
emit(` %${p}_crext =l extuw %${p}_cr`)
|
||||
emit(` %${p}_sh =l shl %${p}_crext, 5`)
|
||||
emit(` %${p}_r =l or %${p}_sh, 3`)
|
||||
s_write(a1, `%${p}_r`)
|
||||
continue
|
||||
}
|
||||
if (op == "gt_int") {
|
||||
emit(` call $__gt_int_ss(l %fp, l ${text(a1)}, l ${text(a2)}, l ${text(a3)})`)
|
||||
lhs = s_read(a2)
|
||||
rhs = s_read(a3)
|
||||
p = fresh()
|
||||
emit(` %${p}_ai =l sar ${lhs}, 1`)
|
||||
emit(` %${p}_bi =l sar ${rhs}, 1`)
|
||||
emit(` %${p}_aiw =w copy %${p}_ai`)
|
||||
emit(` %${p}_biw =w copy %${p}_bi`)
|
||||
emit(` %${p}_cr =w csgtw %${p}_aiw, %${p}_biw`)
|
||||
emit(` %${p}_crext =l extuw %${p}_cr`)
|
||||
emit(` %${p}_sh =l shl %${p}_crext, 5`)
|
||||
emit(` %${p}_r =l or %${p}_sh, 3`)
|
||||
s_write(a1, `%${p}_r`)
|
||||
continue
|
||||
}
|
||||
if (op == "le_int") {
|
||||
emit(` call $__le_int_ss(l %fp, l ${text(a1)}, l ${text(a2)}, l ${text(a3)})`)
|
||||
lhs = s_read(a2)
|
||||
rhs = s_read(a3)
|
||||
p = fresh()
|
||||
emit(` %${p}_ai =l sar ${lhs}, 1`)
|
||||
emit(` %${p}_bi =l sar ${rhs}, 1`)
|
||||
emit(` %${p}_aiw =w copy %${p}_ai`)
|
||||
emit(` %${p}_biw =w copy %${p}_bi`)
|
||||
emit(` %${p}_cr =w cslew %${p}_aiw, %${p}_biw`)
|
||||
emit(` %${p}_crext =l extuw %${p}_cr`)
|
||||
emit(` %${p}_sh =l shl %${p}_crext, 5`)
|
||||
emit(` %${p}_r =l or %${p}_sh, 3`)
|
||||
s_write(a1, `%${p}_r`)
|
||||
continue
|
||||
}
|
||||
if (op == "ge_int") {
|
||||
emit(` call $__ge_int_ss(l %fp, l ${text(a1)}, l ${text(a2)}, l ${text(a3)})`)
|
||||
lhs = s_read(a2)
|
||||
rhs = s_read(a3)
|
||||
p = fresh()
|
||||
emit(` %${p}_ai =l sar ${lhs}, 1`)
|
||||
emit(` %${p}_bi =l sar ${rhs}, 1`)
|
||||
emit(` %${p}_aiw =w copy %${p}_ai`)
|
||||
emit(` %${p}_biw =w copy %${p}_bi`)
|
||||
emit(` %${p}_cr =w csgew %${p}_aiw, %${p}_biw`)
|
||||
emit(` %${p}_crext =l extuw %${p}_cr`)
|
||||
emit(` %${p}_sh =l shl %${p}_crext, 5`)
|
||||
emit(` %${p}_r =l or %${p}_sh, 3`)
|
||||
s_write(a1, `%${p}_r`)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -1240,39 +1518,99 @@ var qbe_emit = function(ir, qbe, export_name) {
|
||||
// --- Control flow ---
|
||||
|
||||
if (op == "jump") {
|
||||
emit(` jmp @${sanitize(a1)}`)
|
||||
j_lbl = sanitize(a1)
|
||||
j_idx = label_pos[j_lbl]
|
||||
if (j_idx != null && j_idx < instr_idx) {
|
||||
emit_backedge_branch(j_lbl)
|
||||
} else {
|
||||
emit(` jmp @${j_lbl}`)
|
||||
}
|
||||
last_was_term = true
|
||||
continue
|
||||
}
|
||||
if (op == "jump_true") {
|
||||
v = s_read(a1)
|
||||
p = fresh()
|
||||
emit(` %${p} =w call $JS_ToBool(l %ctx, l ${v})`)
|
||||
emit(` jnz %${p}, @${sanitize(a2)}, @${p}_f`)
|
||||
jt_lbl = sanitize(a2)
|
||||
jt_idx = label_pos[jt_lbl]
|
||||
jt_backedge = jt_idx != null && jt_idx < instr_idx
|
||||
emit(` %${p}_is_true =w ceql ${v}, ${text(qbe.js_true)}`)
|
||||
emit(` jnz %${p}_is_true, @${p}_take, @${p}_chk_fast`)
|
||||
emit(`@${p}_chk_fast`)
|
||||
emit(` %${p}_tag =l and ${v}, 31`)
|
||||
emit(` %${p}_is_bool =w ceql %${p}_tag, 3`)
|
||||
emit(` %${p}_is_null =w ceql %${p}_tag, 7`)
|
||||
emit(` %${p}_is_falsey =w or %${p}_is_bool, %${p}_is_null`)
|
||||
emit(` jnz %${p}_is_falsey, @${p}_f, @${p}_tb`)
|
||||
emit(`@${p}_tb`)
|
||||
emit(` %${p}_tbv =w call $JS_ToBool(l %ctx, l ${v})`)
|
||||
emit(` jnz %${p}_tbv, @${p}_take, @${p}_f`)
|
||||
emit(`@${p}_take`)
|
||||
if (jt_backedge) {
|
||||
emit_backedge_branch(jt_lbl)
|
||||
} else {
|
||||
emit(` jmp @${jt_lbl}`)
|
||||
}
|
||||
emit(`@${p}_f`)
|
||||
continue
|
||||
}
|
||||
if (op == "jump_false") {
|
||||
v = s_read(a1)
|
||||
p = fresh()
|
||||
emit(` %${p} =w call $JS_ToBool(l %ctx, l ${v})`)
|
||||
emit(` jnz %${p}, @${p}_t, @${sanitize(a2)}`)
|
||||
jf_lbl = sanitize(a2)
|
||||
jf_idx = label_pos[jf_lbl]
|
||||
jf_backedge = jf_idx != null && jf_idx < instr_idx
|
||||
emit(` %${p}_is_true =w ceql ${v}, ${text(qbe.js_true)}`)
|
||||
emit(` jnz %${p}_is_true, @${p}_t, @${p}_chk_fast`)
|
||||
emit(`@${p}_chk_fast`)
|
||||
emit(` %${p}_tag =l and ${v}, 31`)
|
||||
emit(` %${p}_is_bool =w ceql %${p}_tag, 3`)
|
||||
emit(` %${p}_is_null =w ceql %${p}_tag, 7`)
|
||||
emit(` %${p}_is_fast_false =w or %${p}_is_bool, %${p}_is_null`)
|
||||
emit(` jnz %${p}_is_fast_false, @${p}_take, @${p}_tb`)
|
||||
emit(`@${p}_tb`)
|
||||
emit(` %${p}_tbv =w call $JS_ToBool(l %ctx, l ${v})`)
|
||||
emit(` jnz %${p}_tbv, @${p}_t, @${p}_take`)
|
||||
emit(`@${p}_take`)
|
||||
if (jf_backedge) {
|
||||
emit_backedge_branch(jf_lbl)
|
||||
} else {
|
||||
emit(` jmp @${jf_lbl}`)
|
||||
}
|
||||
emit(`@${p}_t`)
|
||||
continue
|
||||
}
|
||||
if (op == "jump_null") {
|
||||
v = s_read(a1)
|
||||
p = fresh()
|
||||
jn_lbl = sanitize(a2)
|
||||
jn_idx = label_pos[jn_lbl]
|
||||
jn_backedge = jn_idx != null && jn_idx < instr_idx
|
||||
emit(` %${p} =w ceql ${v}, ${text(qbe.js_null)}`)
|
||||
emit(` jnz %${p}, @${sanitize(a2)}, @${p}_nn`)
|
||||
if (jn_backedge) {
|
||||
emit(` jnz %${p}, @${p}_bn, @${p}_nn`)
|
||||
emit(`@${p}_bn`)
|
||||
emit_backedge_branch(jn_lbl)
|
||||
} else {
|
||||
emit(` jnz %${p}, @${jn_lbl}, @${p}_nn`)
|
||||
}
|
||||
emit(`@${p}_nn`)
|
||||
continue
|
||||
}
|
||||
if (op == "jump_not_null") {
|
||||
v = s_read(a1)
|
||||
p = fresh()
|
||||
jnn_lbl = sanitize(a2)
|
||||
jnn_idx = label_pos[jnn_lbl]
|
||||
jnn_backedge = jnn_idx != null && jnn_idx < instr_idx
|
||||
emit(` %${p} =w cnel ${v}, ${text(qbe.js_null)}`)
|
||||
emit(` jnz %${p}, @${sanitize(a2)}, @${p}_n`)
|
||||
if (jnn_backedge) {
|
||||
emit(` jnz %${p}, @${p}_bn, @${p}_n`)
|
||||
emit(`@${p}_bn`)
|
||||
emit_backedge_branch(jnn_lbl)
|
||||
} else {
|
||||
emit(` jnz %${p}, @${jnn_lbl}, @${p}_n`)
|
||||
}
|
||||
emit(`@${p}_n`)
|
||||
continue
|
||||
}
|
||||
@@ -1316,26 +1654,14 @@ var qbe_emit = function(ir, qbe, export_name) {
|
||||
continue
|
||||
}
|
||||
if (op == "tail_invoke") {
|
||||
// Same as invoke — dispatch loop regular call with resume
|
||||
seg_counter = seg_counter + 1
|
||||
seg_num = seg_counter
|
||||
resume_val = seg_num * 65536 + a2
|
||||
emit(` %_tinv_addr${text(seg_num)} =l sub %fp, 8`)
|
||||
emit(` storel ${text(resume_val * 2)}, %_tinv_addr${text(seg_num)}`)
|
||||
emit(` call $cell_rt_signal_call(l %ctx, l %fp, l ${text(a1)})`)
|
||||
emit(" ret 0")
|
||||
emit(`@_seg${text(seg_num)}`)
|
||||
// Check for exception after dispatch loop resumes us
|
||||
// Tail call: hand control to dispatch loop and do not resume this segment.
|
||||
// Use 0xFFFF as ret_slot (no result writeback into current frame).
|
||||
p = fresh()
|
||||
emit(` %${p} =w call $JS_HasException(l %ctx)`)
|
||||
if (has_handler && !in_handler) {
|
||||
emit(` jnz %${p}, @disruption_handler, @${p}_ok`)
|
||||
} else {
|
||||
needs_exc_ret = true
|
||||
emit(` jnz %${p}, @_exc_ret, @${p}_ok`)
|
||||
}
|
||||
emit(`@${p}_ok`)
|
||||
last_was_term = false
|
||||
emit(` %${p}_addr =l sub %fp, 8`)
|
||||
emit(` storel ${text(65535 * 2)}, %${p}_addr`)
|
||||
emit(` call $cell_rt_signal_tail_call(l %ctx, l %fp, l ${text(a1)})`)
|
||||
emit(" ret 0")
|
||||
last_was_term = true
|
||||
continue
|
||||
}
|
||||
if (op == "goframe") {
|
||||
|
||||
Reference in New Issue
Block a user