asserts only for frame gets
This commit is contained in:
566
qbe_emit.cm
566
qbe_emit.cm
@@ -41,49 +41,6 @@ ${sw("w", "%fp2", "%dest", result_var)}
|
||||
// Category A: Pure QBE helpers (no C calls)
|
||||
// ============================================================
|
||||
|
||||
// ============================================================
|
||||
// Category B: Non-allocating C call helpers
|
||||
// ============================================================
|
||||
|
||||
// Type checks via C (no ctx needed except is_proxy)
|
||||
var tc_ops = [
|
||||
["is_stone", "JS_IsStone", false],
|
||||
["is_proxy", "cell_rt_is_proxy", true]
|
||||
]
|
||||
var i = 0
|
||||
var tc_name = null
|
||||
var tc_cfn = null
|
||||
var tc_ctx = null
|
||||
while (i < length(tc_ops)) {
|
||||
tc_name = tc_ops[i][0]
|
||||
tc_cfn = tc_ops[i][1]
|
||||
tc_ctx = tc_ops[i][2]
|
||||
if (tc_ctx) {
|
||||
h[] = `export function $__${tc_name}_ss(l %ctx, l %fp, l %dest, l %src) ${lb}
|
||||
@entry
|
||||
${sr("a", "%src")}
|
||||
%cr =w call $${tc_cfn}(l %ctx, l %a)
|
||||
%crext =l extuw %cr
|
||||
%sh =l shl %crext, 5
|
||||
%r =l or %sh, 3
|
||||
${sw("w", "%fp", "%dest", "%r")}
|
||||
ret
|
||||
}`
|
||||
} else {
|
||||
h[] = `export function $__${tc_name}_ss(l %fp, l %dest, l %src) ${lb}
|
||||
@entry
|
||||
${sr("a", "%src")}
|
||||
%cr =w call $${tc_cfn}(l %a)
|
||||
%crext =l extuw %cr
|
||||
%sh =l shl %crext, 5
|
||||
%r =l or %sh, 3
|
||||
${sw("w", "%fp", "%dest", "%r")}
|
||||
ret
|
||||
}`
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
|
||||
// is_record: pointer + header type check (OBJ_RECORD=3), chase forwards
|
||||
h[] = `export function $__is_record_ss(l %fp, l %dest, l %src) ${lb}
|
||||
@entry
|
||||
@@ -179,7 +136,7 @@ ${sw("w", "%fp", "%dest", "%r")}
|
||||
|
||||
// eq_tol, ne_tol (return tagged JSValue directly) — needs tolerance (3rd value)
|
||||
var tol_ops = ["eq_tol", "ne_tol"]
|
||||
i = 0
|
||||
var i = 0
|
||||
while (i < length(tol_ops)) {
|
||||
h[] = `export function $__${tol_ops[i]}_ss(l %ctx, l %fp, l %dest, l %s1, l %s2, l %s3) ${lb}
|
||||
@entry
|
||||
@@ -266,25 +223,6 @@ ${sw("w", "%fp", "%dest", "%r")}
|
||||
ret
|
||||
}`
|
||||
|
||||
// and, or (return tagged JSValue directly)
|
||||
h[] = `export function $__and_ss(l %ctx, l %fp, l %dest, l %s1, l %s2) ${lb}
|
||||
@entry
|
||||
${sr("a", "%s1")}
|
||||
${sr("b", "%s2")}
|
||||
%r =l call $cell_rt_and(l %ctx, l %a, l %b)
|
||||
${sw("w", "%fp", "%dest", "%r")}
|
||||
ret
|
||||
}`
|
||||
|
||||
h[] = `export function $__or_ss(l %ctx, l %fp, l %dest, l %s1, l %s2) ${lb}
|
||||
@entry
|
||||
${sr("a", "%s1")}
|
||||
${sr("b", "%s2")}
|
||||
%r =l call $cell_rt_or(l %ctx, l %a, l %b)
|
||||
${sw("w", "%fp", "%dest", "%r")}
|
||||
ret
|
||||
}`
|
||||
|
||||
// Bitwise unary/binary ops: mirror MACH mcode op behavior via JS_ToInt32 coercion.
|
||||
h[] = `export function $__bnot_ss(l %ctx, l %fp, l %dest, l %src) ${lb}
|
||||
@entry
|
||||
@@ -325,7 +263,7 @@ ${sw("w", "%fp", "%dest", "%r")}
|
||||
// Category C: Allocating helpers (return fp or 0)
|
||||
// ============================================================
|
||||
|
||||
// concat allocates; keep refresh path
|
||||
// concat allocates; runtime helper mirrors MACH self-assign fast path
|
||||
h[] = `export function l $__concat_ss(l %ctx, l %fp, l %dest, l %s1, l %s2) ${lb}
|
||||
@entry
|
||||
${sr("a", "%s1")}
|
||||
@@ -1098,6 +1036,8 @@ var qbe_emit = function(ir, qbe, export_name) {
|
||||
var text_arg_slot = 0
|
||||
var text_dest_slot = 0
|
||||
var cmp_id = 0
|
||||
var depth = 0
|
||||
var d = 0
|
||||
|
||||
// Pre-scan: count invoke/tail_invoke points to assign segment numbers.
|
||||
// Must skip dead code (instructions after terminators) the same way
|
||||
@@ -1234,14 +1174,7 @@ var qbe_emit = function(ir, qbe, export_name) {
|
||||
|
||||
// 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}`)
|
||||
}
|
||||
emit(` jmp @${target_label}`)
|
||||
}
|
||||
|
||||
// Inline JS_ToBool equivalent for hot branch paths.
|
||||
@@ -1894,7 +1827,24 @@ var qbe_emit = function(ir, qbe, export_name) {
|
||||
}
|
||||
if (op == "stone_text") {
|
||||
v = s_read(a1)
|
||||
emit(` call $cell_rt_stone_text(l ${v})`)
|
||||
p = fresh()
|
||||
emit(` %${p}_ptag =l and ${v}, 7`)
|
||||
emit(` %${p}_is_ptr =w ceql %${p}_ptag, 1`)
|
||||
emit(` jnz %${p}_is_ptr, @${p}_ptr, @${p}_done`)
|
||||
emit(`@${p}_ptr`)
|
||||
emit(` %${p}_ptr =l and ${v}, -8`)
|
||||
emit(` %${p}_hdr =l loadl %${p}_ptr`)
|
||||
emit(` %${p}_ht =l and %${p}_hdr, 7`)
|
||||
emit(` %${p}_is_text =w ceql %${p}_ht, 2`)
|
||||
emit(` jnz %${p}_is_text, @${p}_stone_chk, @${p}_done`)
|
||||
emit(`@${p}_stone_chk`)
|
||||
emit(` %${p}_s =l and %${p}_hdr, 8`)
|
||||
emit(` %${p}_is_stone =w cnel %${p}_s, 0`)
|
||||
emit(` jnz %${p}_is_stone, @${p}_done, @${p}_set`)
|
||||
emit(`@${p}_set`)
|
||||
emit(` %${p}_new_hdr =l or %${p}_hdr, 8`)
|
||||
emit(` storel %${p}_new_hdr, %${p}_ptr`)
|
||||
emit(`@${p}_done`)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -1955,19 +1905,279 @@ var qbe_emit = function(ir, qbe, export_name) {
|
||||
continue
|
||||
}
|
||||
if (op == "is_stone") {
|
||||
emit(` call $__is_stone_ss(l %fp, l ${text(a1)}, l ${text(a2)})`)
|
||||
v = s_read(a2)
|
||||
p = fresh()
|
||||
emit(` %${p}_ptag =l and ${v}, 7`)
|
||||
emit(` %${p}_is_ptr =w ceql %${p}_ptag, 1`)
|
||||
emit(` jnz %${p}_is_ptr, @${p}_ptr, @${p}_yes`)
|
||||
emit(`@${p}_ptr`)
|
||||
emit(` %${p}_ptr =l and ${v}, -8`)
|
||||
emit(` %${p}_hdr =l loadl %${p}_ptr`)
|
||||
emit(`@${p}_chase`)
|
||||
emit(` %${p}_ht =l and %${p}_hdr, 7`)
|
||||
emit(` %${p}_is_fwd =w ceql %${p}_ht, 7`)
|
||||
emit(` jnz %${p}_is_fwd, @${p}_follow, @${p}_chk`)
|
||||
emit(`@${p}_follow`)
|
||||
emit(` %${p}_ptr =l shr %${p}_hdr, 3`)
|
||||
emit(` %${p}_hdr =l loadl %${p}_ptr`)
|
||||
emit(` jmp @${p}_chase`)
|
||||
emit(`@${p}_chk`)
|
||||
emit(` %${p}_s =l and %${p}_hdr, 8`)
|
||||
emit(` %${p}_w =w cnel %${p}_s, 0`)
|
||||
emit(` jmp @${p}_done`)
|
||||
emit(`@${p}_yes`)
|
||||
emit(` %${p}_w =w copy 1`)
|
||||
emit(`@${p}_done`)
|
||||
s_write(a1, emit_pack_bool_js(`%${p}_w`))
|
||||
continue
|
||||
}
|
||||
if (op == "is_proxy") {
|
||||
emit(` call $__is_proxy_ss(l %ctx, l %fp, l ${text(a1)}, l ${text(a2)})`)
|
||||
v = s_read(a2)
|
||||
p = fresh()
|
||||
emit(` %${p}_ptag =l and ${v}, 7`)
|
||||
emit(` %${p}_is_ptr =w ceql %${p}_ptag, 1`)
|
||||
emit(` jnz %${p}_is_ptr, @${p}_ptr, @${p}_no`)
|
||||
emit(`@${p}_ptr`)
|
||||
emit(` %${p}_ptr =l and ${v}, -8`)
|
||||
emit(` %${p}_hdr =l loadl %${p}_ptr`)
|
||||
emit(`@${p}_chase`)
|
||||
emit(` %${p}_ht =l and %${p}_hdr, 7`)
|
||||
emit(` %${p}_is_fwd =w ceql %${p}_ht, 7`)
|
||||
emit(` jnz %${p}_is_fwd, @${p}_follow, @${p}_chk`)
|
||||
emit(`@${p}_follow`)
|
||||
emit(` %${p}_ptr =l shr %${p}_hdr, 3`)
|
||||
emit(` %${p}_hdr =l loadl %${p}_ptr`)
|
||||
emit(` jmp @${p}_chase`)
|
||||
emit(`@${p}_chk`)
|
||||
emit(` %${p}_is_fn =w ceql %${p}_ht, 4`)
|
||||
emit(` jnz %${p}_is_fn, @${p}_len_chk, @${p}_no`)
|
||||
emit(`@${p}_len_chk`)
|
||||
emit(` %${p}_len_p =l add %${p}_ptr, 16`)
|
||||
emit(` %${p}_len_q =l loadl %${p}_len_p`)
|
||||
emit(` %${p}_len =l and %${p}_len_q, 65535`)
|
||||
emit(` %${p}_w =w ceql %${p}_len, 2`)
|
||||
emit(` jmp @${p}_done`)
|
||||
emit(`@${p}_no`)
|
||||
emit(` %${p}_w =w copy 0`)
|
||||
emit(`@${p}_done`)
|
||||
s_write(a1, emit_pack_bool_js(`%${p}_w`))
|
||||
continue
|
||||
}
|
||||
if (op == "is_blob" || op == "is_data" || op == "is_fit" ||
|
||||
op == "is_char" || op == "is_digit" || op == "is_letter" ||
|
||||
if (op == "is_blob") {
|
||||
v = s_read(a2)
|
||||
p = fresh()
|
||||
emit(` %${p}_ptag =l and ${v}, 7`)
|
||||
emit(` %${p}_is_ptr =w ceql %${p}_ptag, 1`)
|
||||
emit(` jnz %${p}_is_ptr, @${p}_ptr, @${p}_no`)
|
||||
emit(`@${p}_ptr`)
|
||||
emit(` %${p}_ptr =l and ${v}, -8`)
|
||||
emit(` %${p}_hdr =l loadl %${p}_ptr`)
|
||||
emit(`@${p}_chase`)
|
||||
emit(` %${p}_ht =l and %${p}_hdr, 7`)
|
||||
emit(` %${p}_is_fwd =w ceql %${p}_ht, 7`)
|
||||
emit(` jnz %${p}_is_fwd, @${p}_follow, @${p}_chk`)
|
||||
emit(`@${p}_follow`)
|
||||
emit(` %${p}_ptr =l shr %${p}_hdr, 3`)
|
||||
emit(` %${p}_hdr =l loadl %${p}_ptr`)
|
||||
emit(` jmp @${p}_chase`)
|
||||
emit(`@${p}_chk`)
|
||||
emit(` %${p}_w =w ceql %${p}_ht, 1`)
|
||||
emit(` jmp @${p}_done`)
|
||||
emit(`@${p}_no`)
|
||||
emit(` %${p}_w =w copy 0`)
|
||||
emit(`@${p}_done`)
|
||||
s_write(a1, emit_pack_bool_js(`%${p}_w`))
|
||||
continue
|
||||
}
|
||||
if (op == "is_data") {
|
||||
v = s_read(a2)
|
||||
p = fresh()
|
||||
emit(` %${p}_ptag =l and ${v}, 7`)
|
||||
emit(` %${p}_is_ptr =w ceql %${p}_ptag, 1`)
|
||||
emit(` jnz %${p}_is_ptr, @${p}_ptr, @${p}_no`)
|
||||
emit(`@${p}_ptr`)
|
||||
emit(` %${p}_ptr =l and ${v}, -8`)
|
||||
emit(` %${p}_hdr =l loadl %${p}_ptr`)
|
||||
emit(`@${p}_chase`)
|
||||
emit(` %${p}_ht =l and %${p}_hdr, 7`)
|
||||
emit(` %${p}_is_fwd =w ceql %${p}_ht, 7`)
|
||||
emit(` jnz %${p}_is_fwd, @${p}_follow, @${p}_chk`)
|
||||
emit(`@${p}_follow`)
|
||||
emit(` %${p}_ptr =l shr %${p}_hdr, 3`)
|
||||
emit(` %${p}_hdr =l loadl %${p}_ptr`)
|
||||
emit(` jmp @${p}_chase`)
|
||||
emit(`@${p}_chk`)
|
||||
emit(` %${p}_is_arr =w ceql %${p}_ht, 0`)
|
||||
emit(` %${p}_is_fn =w ceql %${p}_ht, 4`)
|
||||
emit(` %${p}_is_blob =w ceql %${p}_ht, 1`)
|
||||
emit(` %${p}_bad =w or %${p}_is_arr, %${p}_is_fn`)
|
||||
emit(` %${p}_bad =w or %${p}_bad, %${p}_is_blob`)
|
||||
emit(` %${p}_w =w ceqw %${p}_bad, 0`)
|
||||
emit(` jmp @${p}_done`)
|
||||
emit(`@${p}_no`)
|
||||
emit(` %${p}_w =w copy 0`)
|
||||
emit(`@${p}_done`)
|
||||
s_write(a1, emit_pack_bool_js(`%${p}_w`))
|
||||
continue
|
||||
}
|
||||
if (op == "is_fit") {
|
||||
v = s_read(a2)
|
||||
p = fresh()
|
||||
emit(` %${p}_tag =l and ${v}, 1`)
|
||||
emit(` %${p}_is_int =w ceql %${p}_tag, 0`)
|
||||
emit(` jnz %${p}_is_int, @${p}_yes, @${p}_sfloat_chk`)
|
||||
emit(`@${p}_sfloat_chk`)
|
||||
emit(` %${p}_ptag =l and ${v}, 7`)
|
||||
emit(` %${p}_is_sfloat =w ceql %${p}_ptag, 5`)
|
||||
emit(` jnz %${p}_is_sfloat, @${p}_sfloat, @${p}_no`)
|
||||
emit(`@${p}_sfloat`)
|
||||
lhs_d = emit_num_to_double(v)
|
||||
emit(` %${p}_ti =d call $trunc(d ${lhs_d})`)
|
||||
emit(` %${p}_eqi =w ceqd ${lhs_d}, %${p}_ti`)
|
||||
emit(` %${p}_ad =d call $fabs(d ${lhs_d})`)
|
||||
emit(` %${p}_ltlim =w cltd %${p}_ad, d_9007199254740992.0`)
|
||||
emit(` %${p}_eqlim =w ceqd %${p}_ad, d_9007199254740992.0`)
|
||||
emit(` %${p}_lim =w or %${p}_ltlim, %${p}_eqlim`)
|
||||
emit(` %${p}_w =w and %${p}_eqi, %${p}_lim`)
|
||||
emit(` jmp @${p}_done`)
|
||||
emit(`@${p}_yes`)
|
||||
emit(` %${p}_w =w copy 1`)
|
||||
emit(` jmp @${p}_done`)
|
||||
emit(`@${p}_no`)
|
||||
emit(` %${p}_w =w copy 0`)
|
||||
emit(`@${p}_done`)
|
||||
s_write(a1, emit_pack_bool_js(`%${p}_w`))
|
||||
continue
|
||||
}
|
||||
if (op == "is_char") {
|
||||
v = s_read(a2)
|
||||
p = fresh()
|
||||
emit(` %${p}_imm =l and ${v}, 31`)
|
||||
emit(` %${p}_is_imm =w ceql %${p}_imm, 11`)
|
||||
emit(` jnz %${p}_is_imm, @${p}_imm_path, @${p}_ptr_chk`)
|
||||
emit(`@${p}_imm_path`)
|
||||
emit(` %${p}_ilen =l shr ${v}, 5`)
|
||||
emit(` %${p}_ilen =l and %${p}_ilen, 7`)
|
||||
emit(` %${p}_w =w ceql %${p}_ilen, 1`)
|
||||
emit(` jmp @${p}_done`)
|
||||
emit(`@${p}_ptr_chk`)
|
||||
emit(` %${p}_ptag =l and ${v}, 7`)
|
||||
emit(` %${p}_is_ptr =w ceql %${p}_ptag, 1`)
|
||||
emit(` jnz %${p}_is_ptr, @${p}_ptr, @${p}_no`)
|
||||
emit(`@${p}_ptr`)
|
||||
emit(` %${p}_ptr =l and ${v}, -8`)
|
||||
emit(` %${p}_hdr =l loadl %${p}_ptr`)
|
||||
emit(`@${p}_chase`)
|
||||
emit(` %${p}_ht =l and %${p}_hdr, 7`)
|
||||
emit(` %${p}_is_fwd =w ceql %${p}_ht, 7`)
|
||||
emit(` jnz %${p}_is_fwd, @${p}_follow, @${p}_chk`)
|
||||
emit(`@${p}_follow`)
|
||||
emit(` %${p}_ptr =l shr %${p}_hdr, 3`)
|
||||
emit(` %${p}_hdr =l loadl %${p}_ptr`)
|
||||
emit(` jmp @${p}_chase`)
|
||||
emit(`@${p}_chk`)
|
||||
emit(` %${p}_is_text =w ceql %${p}_ht, 2`)
|
||||
emit(` jnz %${p}_is_text, @${p}_len_chk, @${p}_no`)
|
||||
emit(`@${p}_len_chk`)
|
||||
emit(` %${p}_len_p =l add %${p}_ptr, 8`)
|
||||
emit(` %${p}_len_l =l loadl %${p}_len_p`)
|
||||
emit(` %${p}_w =w ceql %${p}_len_l, 1`)
|
||||
emit(` jmp @${p}_done`)
|
||||
emit(`@${p}_no`)
|
||||
emit(` %${p}_w =w copy 0`)
|
||||
emit(`@${p}_done`)
|
||||
s_write(a1, emit_pack_bool_js(`%${p}_w`))
|
||||
continue
|
||||
}
|
||||
if (op == "is_digit" || op == "is_letter" ||
|
||||
op == "is_lower" || op == "is_upper" || op == "is_ws") {
|
||||
v = s_read(a2)
|
||||
p = fresh()
|
||||
emit(` %${p}_w =w call $cell_rt_${op}(l ${v})`)
|
||||
emit(` %${p}_imm =l and ${v}, 31`)
|
||||
emit(` %${p}_is_imm =w ceql %${p}_imm, 11`)
|
||||
emit(` jnz %${p}_is_imm, @${p}_imm_len, @${p}_ptr_chk`)
|
||||
emit(`@${p}_imm_len`)
|
||||
emit(` %${p}_ilen =l shr ${v}, 5`)
|
||||
emit(` %${p}_ilen =l and %${p}_ilen, 7`)
|
||||
emit(` %${p}_imm_one =w ceql %${p}_ilen, 1`)
|
||||
emit(` jnz %${p}_imm_one, @${p}_imm_char, @${p}_no`)
|
||||
emit(`@${p}_imm_char`)
|
||||
emit(` %${p}_ch_l =l shr ${v}, 8`)
|
||||
emit(` %${p}_ch_l =l and %${p}_ch_l, 255`)
|
||||
emit(` %${p}_ch_w =w copy %${p}_ch_l`)
|
||||
emit(` jmp @${p}_pred`)
|
||||
emit(`@${p}_ptr_chk`)
|
||||
emit(` %${p}_ptag =l and ${v}, 7`)
|
||||
emit(` %${p}_is_ptr =w ceql %${p}_ptag, 1`)
|
||||
emit(` jnz %${p}_is_ptr, @${p}_ptr, @${p}_no`)
|
||||
emit(`@${p}_ptr`)
|
||||
emit(` %${p}_ptr =l and ${v}, -8`)
|
||||
emit(` %${p}_hdr =l loadl %${p}_ptr`)
|
||||
emit(`@${p}_chase`)
|
||||
emit(` %${p}_ht =l and %${p}_hdr, 7`)
|
||||
emit(` %${p}_is_fwd =w ceql %${p}_ht, 7`)
|
||||
emit(` jnz %${p}_is_fwd, @${p}_follow, @${p}_text_chk`)
|
||||
emit(`@${p}_follow`)
|
||||
emit(` %${p}_ptr =l shr %${p}_hdr, 3`)
|
||||
emit(` %${p}_hdr =l loadl %${p}_ptr`)
|
||||
emit(` jmp @${p}_chase`)
|
||||
emit(`@${p}_text_chk`)
|
||||
emit(` %${p}_is_text =w ceql %${p}_ht, 2`)
|
||||
emit(` jnz %${p}_is_text, @${p}_text_len, @${p}_no`)
|
||||
emit(`@${p}_text_len`)
|
||||
emit(` %${p}_len_p =l add %${p}_ptr, 8`)
|
||||
emit(` %${p}_len_l =l loadl %${p}_len_p`)
|
||||
emit(` %${p}_text_one =w ceql %${p}_len_l, 1`)
|
||||
emit(` jnz %${p}_text_one, @${p}_text_char, @${p}_no`)
|
||||
emit(`@${p}_text_char`)
|
||||
emit(` %${p}_pack_p =l add %${p}_ptr, 24`)
|
||||
emit(` %${p}_pack =l loadl %${p}_pack_p`)
|
||||
emit(` %${p}_ch_l =l shr %${p}_pack, 32`)
|
||||
emit(` %${p}_ch_l =l and %${p}_ch_l, 255`)
|
||||
emit(` %${p}_ch_w =w copy %${p}_ch_l`)
|
||||
emit(`@${p}_pred`)
|
||||
if (op == "is_digit") {
|
||||
emit(` %${p}_lt_0 =w csltw %${p}_ch_w, 48`)
|
||||
emit(` %${p}_ge_0 =w ceqw %${p}_lt_0, 0`)
|
||||
emit(` %${p}_lt_9 =w csltw %${p}_ch_w, 58`)
|
||||
emit(` %${p}_w =w and %${p}_ge_0, %${p}_lt_9`)
|
||||
} else if (op == "is_lower") {
|
||||
emit(` %${p}_lt_a =w csltw %${p}_ch_w, 97`)
|
||||
emit(` %${p}_ge_a =w ceqw %${p}_lt_a, 0`)
|
||||
emit(` %${p}_lt_z =w csltw %${p}_ch_w, 123`)
|
||||
emit(` %${p}_w =w and %${p}_ge_a, %${p}_lt_z`)
|
||||
} else if (op == "is_upper") {
|
||||
emit(` %${p}_lt_A =w csltw %${p}_ch_w, 65`)
|
||||
emit(` %${p}_ge_A =w ceqw %${p}_lt_A, 0`)
|
||||
emit(` %${p}_lt_Z =w csltw %${p}_ch_w, 91`)
|
||||
emit(` %${p}_w =w and %${p}_ge_A, %${p}_lt_Z`)
|
||||
} else if (op == "is_letter") {
|
||||
emit(` %${p}_lt_A =w csltw %${p}_ch_w, 65`)
|
||||
emit(` %${p}_ge_A =w ceqw %${p}_lt_A, 0`)
|
||||
emit(` %${p}_lt_Z =w csltw %${p}_ch_w, 91`)
|
||||
emit(` %${p}_is_upper =w and %${p}_ge_A, %${p}_lt_Z`)
|
||||
emit(` %${p}_lt_a =w csltw %${p}_ch_w, 97`)
|
||||
emit(` %${p}_ge_a =w ceqw %${p}_lt_a, 0`)
|
||||
emit(` %${p}_lt_z =w csltw %${p}_ch_w, 123`)
|
||||
emit(` %${p}_is_lower =w and %${p}_ge_a, %${p}_lt_z`)
|
||||
emit(` %${p}_w =w or %${p}_is_upper, %${p}_is_lower`)
|
||||
} else {
|
||||
emit(` %${p}_is_sp =w ceqw %${p}_ch_w, 32`)
|
||||
emit(` %${p}_is_tb =w ceqw %${p}_ch_w, 9`)
|
||||
emit(` %${p}_is_nl =w ceqw %${p}_ch_w, 10`)
|
||||
emit(` %${p}_is_cr =w ceqw %${p}_ch_w, 13`)
|
||||
emit(` %${p}_is_ff =w ceqw %${p}_ch_w, 12`)
|
||||
emit(` %${p}_is_vt =w ceqw %${p}_ch_w, 11`)
|
||||
emit(` %${p}_w =w or %${p}_is_sp, %${p}_is_tb`)
|
||||
emit(` %${p}_w =w or %${p}_w, %${p}_is_nl`)
|
||||
emit(` %${p}_w =w or %${p}_w, %${p}_is_cr`)
|
||||
emit(` %${p}_w =w or %${p}_w, %${p}_is_ff`)
|
||||
emit(` %${p}_w =w or %${p}_w, %${p}_is_vt`)
|
||||
}
|
||||
emit(` jmp @${p}_done`)
|
||||
emit(`@${p}_no`)
|
||||
emit(` %${p}_w =w copy 0`)
|
||||
emit(`@${p}_done`)
|
||||
s_write(a1, emit_pack_bool_js(`%${p}_w`))
|
||||
continue
|
||||
}
|
||||
@@ -1999,6 +2209,33 @@ var qbe_emit = function(ir, qbe, export_name) {
|
||||
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}_aiw =w copy %${p}_ai`)
|
||||
emit(` %${p}_biw =w copy %${p}_bi`)
|
||||
if (op == "eq") {
|
||||
emit(` %${p}_w =w ceqw %${p}_aiw, %${p}_biw`)
|
||||
} else if (op == "ne") {
|
||||
emit(` %${p}_w =w cnew %${p}_aiw, %${p}_biw`)
|
||||
} else if (op == "lt") {
|
||||
emit(` %${p}_w =w csltw %${p}_aiw, %${p}_biw`)
|
||||
} else if (op == "le") {
|
||||
emit(` %${p}_w =w cslew %${p}_aiw, %${p}_biw`)
|
||||
} else if (op == "gt") {
|
||||
emit(` %${p}_w =w csgtw %${p}_aiw, %${p}_biw`)
|
||||
} else {
|
||||
emit(` %${p}_w =w csgew %${p}_aiw, %${p}_biw`)
|
||||
}
|
||||
s_write(a1, emit_pack_bool_js(`%${p}_w`))
|
||||
emit(` jmp @${p}_done`)
|
||||
emit(`@${p}_slow`)
|
||||
cmp_id = 0
|
||||
if (op == "eq") cmp_id = 0
|
||||
else if (op == "ne") cmp_id = 1
|
||||
@@ -2016,6 +2253,7 @@ var qbe_emit = function(ir, qbe, export_name) {
|
||||
}
|
||||
emit(`@${p}_ok`)
|
||||
s_write(a1, `%${p}_r`)
|
||||
emit(`@${p}_done`)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -2032,11 +2270,31 @@ var qbe_emit = function(ir, qbe, export_name) {
|
||||
continue
|
||||
}
|
||||
if (op == "and") {
|
||||
emit(` call $__and_ss(l %ctx, l %fp, l ${text(a1)}, l ${text(a2)}, l ${text(a3)})`)
|
||||
lhs = s_read(a2)
|
||||
rhs = s_read(a3)
|
||||
p = fresh()
|
||||
truthy = emit_truthy_w(lhs)
|
||||
emit(` jnz ${truthy}, @${p}_t, @${p}_f`)
|
||||
emit(`@${p}_t`)
|
||||
s_write(a1, rhs)
|
||||
emit(` jmp @${p}_done`)
|
||||
emit(`@${p}_f`)
|
||||
s_write(a1, lhs)
|
||||
emit(`@${p}_done`)
|
||||
continue
|
||||
}
|
||||
if (op == "or") {
|
||||
emit(` call $__or_ss(l %ctx, l %fp, l ${text(a1)}, l ${text(a2)}, l ${text(a3)})`)
|
||||
lhs = s_read(a2)
|
||||
rhs = s_read(a3)
|
||||
p = fresh()
|
||||
truthy = emit_truthy_w(lhs)
|
||||
emit(` jnz ${truthy}, @${p}_t, @${p}_f`)
|
||||
emit(`@${p}_t`)
|
||||
s_write(a1, lhs)
|
||||
emit(` jmp @${p}_done`)
|
||||
emit(`@${p}_f`)
|
||||
s_write(a1, rhs)
|
||||
emit(`@${p}_done`)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -2210,8 +2468,66 @@ var qbe_emit = function(ir, qbe, export_name) {
|
||||
}
|
||||
if (op == "store_index") {
|
||||
// IR: ["store_index", obj, val, idx]
|
||||
lhs = s_read(a1)
|
||||
rhs = s_read(a2)
|
||||
v = s_read(a3)
|
||||
p = fresh()
|
||||
emit(` %${p}_idx_tag =l and ${v}, 1`)
|
||||
emit(` %${p}_idx_is_int =w ceql %${p}_idx_tag, 0`)
|
||||
emit(` jnz %${p}_idx_is_int, @${p}_idx_ok, @${p}_slow`)
|
||||
emit(`@${p}_idx_ok`)
|
||||
emit(` %${p}_idx_l =l sar ${v}, 1`)
|
||||
emit(` %${p}_idx_w =w copy %${p}_idx_l`)
|
||||
emit(` %${p}_idx_neg =w csltw %${p}_idx_w, 0`)
|
||||
emit(` jnz %${p}_idx_neg, @${p}_slow, @${p}_arr_ptr_chk`)
|
||||
emit(`@${p}_arr_ptr_chk`)
|
||||
emit(` %${p}_ptag =l and ${lhs}, 7`)
|
||||
emit(` %${p}_is_ptr =w ceql %${p}_ptag, 1`)
|
||||
emit(` jnz %${p}_is_ptr, @${p}_arr_ptr, @${p}_slow`)
|
||||
emit(`@${p}_arr_ptr`)
|
||||
emit(` %${p}_arr_ptr =l and ${lhs}, -8`)
|
||||
emit(` %${p}_arr_hdr =l loadl %${p}_arr_ptr`)
|
||||
emit(`@${p}_arr_chase`)
|
||||
emit(` %${p}_arr_ty =l and %${p}_arr_hdr, 7`)
|
||||
emit(` %${p}_arr_is_fwd =w ceql %${p}_arr_ty, 7`)
|
||||
emit(` jnz %${p}_arr_is_fwd, @${p}_arr_follow, @${p}_arr_chk`)
|
||||
emit(`@${p}_arr_follow`)
|
||||
emit(` %${p}_arr_ptr =l shr %${p}_arr_hdr, 3`)
|
||||
emit(` %${p}_arr_hdr =l loadl %${p}_arr_ptr`)
|
||||
emit(` jmp @${p}_arr_chase`)
|
||||
emit(`@${p}_arr_chk`)
|
||||
emit(` %${p}_arr_is_array =w ceql %${p}_arr_ty, 0`)
|
||||
emit(` jnz %${p}_arr_is_array, @${p}_arr_stone_chk, @${p}_slow`)
|
||||
emit(`@${p}_arr_stone_chk`)
|
||||
emit(` %${p}_arr_stone =l and %${p}_arr_hdr, 8`)
|
||||
emit(` %${p}_arr_is_stone =w cnel %${p}_arr_stone, 0`)
|
||||
emit(` jnz %${p}_arr_is_stone, @${p}_slow, @${p}_cap_chk`)
|
||||
emit(`@${p}_cap_chk`)
|
||||
emit(` %${p}_cap_l =l shr %${p}_arr_hdr, 8`)
|
||||
emit(` %${p}_cap_w =w copy %${p}_cap_l`)
|
||||
emit(` %${p}_in_cap =w csltw %${p}_idx_w, %${p}_cap_w`)
|
||||
emit(` jnz %${p}_in_cap, @${p}_len_chk, @${p}_slow`)
|
||||
emit(`@${p}_len_chk`)
|
||||
emit(` %${p}_len_p =l add %${p}_arr_ptr, 8`)
|
||||
emit(` %${p}_len_l =l loadl %${p}_len_p`)
|
||||
emit(` %${p}_len_w =w copy %${p}_len_l`)
|
||||
emit(` %${p}_need_len =w csgew %${p}_idx_w, %${p}_len_w`)
|
||||
emit(` jnz %${p}_need_len, @${p}_bump_len, @${p}_store`)
|
||||
emit(`@${p}_bump_len`)
|
||||
emit(` %${p}_next_len_w =w add %${p}_idx_w, 1`)
|
||||
emit(` %${p}_next_len_l =l extsw %${p}_next_len_w`)
|
||||
emit(` storel %${p}_next_len_l, %${p}_len_p`)
|
||||
emit(`@${p}_store`)
|
||||
emit(` %${p}_idx2_l =l extsw %${p}_idx_w`)
|
||||
emit(` %${p}_idx2_off =l shl %${p}_idx2_l, 3`)
|
||||
emit(` %${p}_vals_p =l add %${p}_arr_ptr, 16`)
|
||||
emit(` %${p}_item_p =l add %${p}_vals_p, %${p}_idx2_off`)
|
||||
emit(` storel ${rhs}, %${p}_item_p`)
|
||||
emit(` jmp @${p}_done`)
|
||||
emit(`@${p}_slow`)
|
||||
emit(` %fp =l call $__store_index_ss(l %ctx, l %fp, l ${text(a1)}, l ${text(a2)}, l ${text(a3)})`)
|
||||
emit_exc_check()
|
||||
emit(`@${p}_done`)
|
||||
continue
|
||||
}
|
||||
if (op == "store_dynamic") {
|
||||
@@ -2240,15 +2556,53 @@ var qbe_emit = function(ir, qbe, export_name) {
|
||||
|
||||
if (op == "get") {
|
||||
// mcode: get(dest, slot, depth) — a2=slot, a3=depth
|
||||
p = fresh()
|
||||
emit(` %${p} =l call $cell_rt_get_closure(l %ctx, l %fp, l ${text(a3)}, l ${text(a2)})`)
|
||||
s_write(a1, `%${p}`)
|
||||
depth = a3
|
||||
if (depth == 0) {
|
||||
v = s_read(a2)
|
||||
s_write(a1, v)
|
||||
} else {
|
||||
p = fresh()
|
||||
emit(` %${p}_fp =l copy %fp`)
|
||||
d = 0
|
||||
while (d < depth) {
|
||||
emit(` %${p}_fn_p_${text(d)} =l sub %${p}_fp, 24`)
|
||||
emit(` %${p}_fn_${text(d)} =l loadl %${p}_fn_p_${text(d)}`)
|
||||
emit(` %${p}_fn_ptr_${text(d)} =l and %${p}_fn_${text(d)}, -8`)
|
||||
emit(` %${p}_outer_p_${text(d)} =l add %${p}_fn_ptr_${text(d)}, 40`)
|
||||
emit(` %${p}_outer_${text(d)} =l loadl %${p}_outer_p_${text(d)}`)
|
||||
emit(` %${p}_outer_ptr_${text(d)} =l and %${p}_outer_${text(d)}, -8`)
|
||||
emit(` %${p}_fp =l add %${p}_outer_ptr_${text(d)}, 32`)
|
||||
d = d + 1
|
||||
}
|
||||
emit(` %${p}_slotp =l add %${p}_fp, ${text(a2 * 8)}`)
|
||||
emit(` %${p}_val =l loadl %${p}_slotp`)
|
||||
s_write(a1, `%${p}_val`)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if (op == "put") {
|
||||
// mcode: put(val, slot, depth) — a2=slot, a3=depth
|
||||
v = s_read(a1)
|
||||
emit(` call $cell_rt_put_closure(l %ctx, l %fp, l ${v}, l ${text(a3)}, l ${text(a2)})`)
|
||||
depth = a3
|
||||
if (depth == 0) {
|
||||
s_write(a2, v)
|
||||
} else {
|
||||
p = fresh()
|
||||
emit(` %${p}_fp =l copy %fp`)
|
||||
d = 0
|
||||
while (d < depth) {
|
||||
emit(` %${p}_fn_p_${text(d)} =l sub %${p}_fp, 24`)
|
||||
emit(` %${p}_fn_${text(d)} =l loadl %${p}_fn_p_${text(d)}`)
|
||||
emit(` %${p}_fn_ptr_${text(d)} =l and %${p}_fn_${text(d)}, -8`)
|
||||
emit(` %${p}_outer_p_${text(d)} =l add %${p}_fn_ptr_${text(d)}, 40`)
|
||||
emit(` %${p}_outer_${text(d)} =l loadl %${p}_outer_p_${text(d)}`)
|
||||
emit(` %${p}_outer_ptr_${text(d)} =l and %${p}_outer_${text(d)}, -8`)
|
||||
emit(` %${p}_fp =l add %${p}_outer_ptr_${text(d)}, 32`)
|
||||
d = d + 1
|
||||
}
|
||||
emit(` %${p}_slotp =l add %${p}_fp, ${text(a2 * 8)}`)
|
||||
emit(` storel ${v}, %${p}_slotp`)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -2401,8 +2755,52 @@ var qbe_emit = function(ir, qbe, export_name) {
|
||||
// --- Array push/pop [G] ---
|
||||
|
||||
if (op == "push") {
|
||||
lhs = s_read(a1)
|
||||
rhs = s_read(a2)
|
||||
p = fresh()
|
||||
emit(` %${p}_ptag =l and ${lhs}, 7`)
|
||||
emit(` %${p}_is_ptr =w ceql %${p}_ptag, 1`)
|
||||
emit(` jnz %${p}_is_ptr, @${p}_arr_ptr, @${p}_slow`)
|
||||
emit(`@${p}_arr_ptr`)
|
||||
emit(` %${p}_arr_ptr =l and ${lhs}, -8`)
|
||||
emit(` %${p}_arr_hdr =l loadl %${p}_arr_ptr`)
|
||||
emit(`@${p}_arr_chase`)
|
||||
emit(` %${p}_arr_ty =l and %${p}_arr_hdr, 7`)
|
||||
emit(` %${p}_arr_is_fwd =w ceql %${p}_arr_ty, 7`)
|
||||
emit(` jnz %${p}_arr_is_fwd, @${p}_arr_follow, @${p}_arr_chk`)
|
||||
emit(`@${p}_arr_follow`)
|
||||
emit(` %${p}_arr_ptr =l shr %${p}_arr_hdr, 3`)
|
||||
emit(` %${p}_arr_hdr =l loadl %${p}_arr_ptr`)
|
||||
emit(` jmp @${p}_arr_chase`)
|
||||
emit(`@${p}_arr_chk`)
|
||||
emit(` %${p}_arr_is_array =w ceql %${p}_arr_ty, 0`)
|
||||
emit(` jnz %${p}_arr_is_array, @${p}_arr_stone_chk, @${p}_slow`)
|
||||
emit(`@${p}_arr_stone_chk`)
|
||||
emit(` %${p}_arr_stone =l and %${p}_arr_hdr, 8`)
|
||||
emit(` %${p}_arr_is_stone =w cnel %${p}_arr_stone, 0`)
|
||||
emit(` jnz %${p}_arr_is_stone, @${p}_slow, @${p}_lens`)
|
||||
emit(`@${p}_lens`)
|
||||
emit(` %${p}_len_p =l add %${p}_arr_ptr, 8`)
|
||||
emit(` %${p}_len_l =l loadl %${p}_len_p`)
|
||||
emit(` %${p}_len_w =w copy %${p}_len_l`)
|
||||
emit(` %${p}_cap_l =l shr %${p}_arr_hdr, 8`)
|
||||
emit(` %${p}_cap_w =w copy %${p}_cap_l`)
|
||||
emit(` %${p}_in_cap =w csltw %${p}_len_w, %${p}_cap_w`)
|
||||
emit(` jnz %${p}_in_cap, @${p}_store, @${p}_slow`)
|
||||
emit(`@${p}_store`)
|
||||
emit(` %${p}_idx_l =l extsw %${p}_len_w`)
|
||||
emit(` %${p}_idx_off =l shl %${p}_idx_l, 3`)
|
||||
emit(` %${p}_vals_p =l add %${p}_arr_ptr, 16`)
|
||||
emit(` %${p}_item_p =l add %${p}_vals_p, %${p}_idx_off`)
|
||||
emit(` storel ${rhs}, %${p}_item_p`)
|
||||
emit(` %${p}_next_len_w =w add %${p}_len_w, 1`)
|
||||
emit(` %${p}_next_len_l =l extsw %${p}_next_len_w`)
|
||||
emit(` storel %${p}_next_len_l, %${p}_len_p`)
|
||||
emit(` jmp @${p}_done`)
|
||||
emit(`@${p}_slow`)
|
||||
emit(` %fp =l call $__push_ss(l %ctx, l %fp, l ${text(a1)}, l ${text(a2)})`)
|
||||
emit_exc_check()
|
||||
emit(`@${p}_done`)
|
||||
continue
|
||||
}
|
||||
if (op == "pop") {
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
*/
|
||||
|
||||
#include "quickjs-internal.h"
|
||||
#include <assert.h>
|
||||
|
||||
/* ============================================================
|
||||
Mach VM instruction definitions (private to mach.c)
|
||||
@@ -2018,21 +2019,12 @@ vm_dispatch:
|
||||
int depth = b;
|
||||
JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function);
|
||||
JSFrameRegister *target = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.cell.outer_frame);
|
||||
if (!target) {
|
||||
fprintf(stderr, "GETUP: NULL outer_frame at depth 0! pc=%d a=%d depth=%d slot=%d nr_slots=%d instr=0x%08x\n",
|
||||
pc-1, a, depth, c, code->nr_slots, instr);
|
||||
result = JS_RaiseDisrupt(ctx, "GETUP: NULL outer_frame");
|
||||
goto disrupt;
|
||||
}
|
||||
assert(depth > 0);
|
||||
assert(target != NULL);
|
||||
for (int d = 1; d < depth; d++) {
|
||||
fn = JS_VALUE_GET_FUNCTION(target->function);
|
||||
JSFrameRegister *next = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.cell.outer_frame);
|
||||
if (!next) {
|
||||
fprintf(stderr, "GETUP: NULL outer_frame at depth %d! pc=%d a=%d depth=%d slot=%d nr_slots=%d instr=0x%08x\n",
|
||||
d, pc-1, a, depth, c, code->nr_slots, instr);
|
||||
result = JS_RaiseDisrupt(ctx, "GETUP: NULL outer_frame at depth %d", d);
|
||||
goto disrupt;
|
||||
}
|
||||
assert(next != NULL);
|
||||
target = next;
|
||||
}
|
||||
stone_mutable_text(target->slots[c]);
|
||||
@@ -2045,22 +2037,14 @@ vm_dispatch:
|
||||
int depth = b;
|
||||
JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function);
|
||||
JSFrameRegister *target = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.cell.outer_frame);
|
||||
assert(depth > 0);
|
||||
assert(target != NULL);
|
||||
for (int d = 1; d < depth; d++) {
|
||||
fn = JS_VALUE_GET_FUNCTION(target->function);
|
||||
target = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.cell.outer_frame);
|
||||
assert(target != NULL);
|
||||
}
|
||||
{
|
||||
uint64_t tcap = objhdr_cap56(target->header);
|
||||
if ((unsigned)c >= tcap) {
|
||||
fprintf(stderr, "MACH_SETUP OOB: slot=%d >= target_cap=%llu depth=%d "
|
||||
"cur_fn=%s (%s) pc=%u\n",
|
||||
c, (unsigned long long)tcap, depth,
|
||||
code->name_cstr ? code->name_cstr : "?",
|
||||
code->filename_cstr ? code->filename_cstr : "?", pc - 1);
|
||||
fflush(stderr);
|
||||
VM_BREAK();
|
||||
}
|
||||
}
|
||||
assert((unsigned)c < objhdr_cap56(target->header));
|
||||
target->slots[c] = frame->slots[a];
|
||||
VM_BREAK();
|
||||
}
|
||||
|
||||
@@ -150,7 +150,6 @@ JSValue qbe_bitwise_xor(JSContext *ctx, JSValue a, JSValue b) {
|
||||
/* Concat helper matching MACH_CONCAT semantics exactly. */
|
||||
JSValue cell_rt_concat(JSContext *ctx, JSValue left, JSValue right, int self_assign) {
|
||||
if (self_assign) {
|
||||
/* Self-assign pattern: slot[a] = slot[a] + slot[c]. */
|
||||
if (JS_IsPtr(left)) {
|
||||
JSText *s = (JSText *)chase(left);
|
||||
int slen = (int)s->length;
|
||||
@@ -159,21 +158,18 @@ JSValue cell_rt_concat(JSContext *ctx, JSValue left, JSValue right, int self_ass
|
||||
if (objhdr_type(s->hdr) == OBJ_TEXT
|
||||
&& !(s->hdr & OBJHDR_S_MASK)
|
||||
&& slen + rlen <= cap) {
|
||||
/* In-place append, no allocation. */
|
||||
for (int i = 0; i < rlen; i++)
|
||||
string_put(s, slen + i, js_string_value_get(right, i));
|
||||
s->length = slen + rlen;
|
||||
return left;
|
||||
}
|
||||
}
|
||||
/* Allocate with growth factor, leave unstoned. */
|
||||
JSValue res = JS_ConcatStringGrow(ctx, left, right);
|
||||
if (JS_IsException(res))
|
||||
return JS_EXCEPTION;
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Different target: exact-fit stoned path. */
|
||||
JSValue res = JS_ConcatString(ctx, left, right);
|
||||
if (JS_IsException(res))
|
||||
return JS_EXCEPTION;
|
||||
@@ -468,52 +464,6 @@ JSValue cell_rt_get_intrinsic_lit(JSContext *ctx, int64_t lit_idx) {
|
||||
return cell_rt_get_intrinsic_key(ctx, key);
|
||||
}
|
||||
|
||||
/* --- Closure access ---
|
||||
Walk the outer_frame chain on JSFunction (JS_FUNC_KIND_NATIVE).
|
||||
The frame's function field links to the JSFunction, whose
|
||||
u.native.outer_frame points to the enclosing frame.
|
||||
GC traces outer_frame naturally — no registry needed. */
|
||||
|
||||
/* Get the outer frame's slots from a frame pointer.
|
||||
The frame's function must be JS_FUNC_KIND_NATIVE. */
|
||||
static JSValue *get_outer_frame_slots(JSValue *fp) {
|
||||
/* fp points to frame->slots[0]; frame header is before it */
|
||||
JSFrameRegister *frame = (JSFrameRegister *)((char *)fp - offsetof(JSFrameRegister, slots));
|
||||
if (JS_IsNull(frame->function))
|
||||
return NULL;
|
||||
JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function);
|
||||
if (fn->kind != JS_FUNC_KIND_NATIVE)
|
||||
return NULL;
|
||||
JSValue outer = fn->u.cell.outer_frame;
|
||||
if (JS_IsNull(outer))
|
||||
return NULL;
|
||||
JSFrameRegister *outer_frame = (JSFrameRegister *)JS_VALUE_GET_PTR(outer);
|
||||
return (JSValue *)outer_frame->slots;
|
||||
}
|
||||
|
||||
JSValue cell_rt_get_closure(JSContext *ctx, void *fp, int64_t depth,
|
||||
int64_t slot) {
|
||||
(void)ctx;
|
||||
JSValue *frame = (JSValue *)fp;
|
||||
for (int64_t d = 0; d < depth; d++) {
|
||||
frame = get_outer_frame_slots(frame);
|
||||
if (!frame)
|
||||
return JS_NULL;
|
||||
}
|
||||
return frame[slot];
|
||||
}
|
||||
|
||||
void cell_rt_put_closure(JSContext *ctx, void *fp, JSValue val, int64_t depth,
|
||||
int64_t slot) {
|
||||
(void)ctx;
|
||||
JSValue *frame = (JSValue *)fp;
|
||||
for (int64_t d = 0; d < depth; d++) {
|
||||
frame = get_outer_frame_slots(frame);
|
||||
if (!frame) return;
|
||||
}
|
||||
frame[slot] = val;
|
||||
}
|
||||
|
||||
/* --- GC-managed AOT frame stack ---
|
||||
Each native dispatch loop pushes a GC ref so the GC can find and
|
||||
update the current frame pointer when it moves objects.
|
||||
@@ -662,18 +612,6 @@ typedef JSValue (*cell_compiled_fn)(JSContext *ctx, void *fp);
|
||||
to call another function (instead of recursing via C stack).
|
||||
============================================================ */
|
||||
|
||||
/* Poll pause state on taken backward jumps (AOT backedges).
|
||||
MACH can suspend/resume a register VM frame at pc granularity; native AOT
|
||||
does not currently have an equivalent resume point, so we acknowledge timer
|
||||
pauses by clearing pause_flag and continuing the current turn. */
|
||||
int cell_rt_check_backedge(JSContext *ctx) {
|
||||
int pf = atomic_load_explicit(&ctx->pause_flag, memory_order_relaxed);
|
||||
if (pf >= 1) {
|
||||
atomic_store_explicit(&ctx->pause_flag, 0, memory_order_relaxed);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cell_rt_signal_call(JSContext *ctx, void *fp, int64_t frame_slot) {
|
||||
NativeRTState *st = native_state(ctx);
|
||||
if (!st) return;
|
||||
@@ -1194,101 +1132,6 @@ JSValue cell_rt_ne_tol(JSContext *ctx, JSValue a, JSValue b, JSValue tol) {
|
||||
return JS_NewBool(ctx, !cell_rt_tol_eq_inner(ctx, a, b, tol));
|
||||
}
|
||||
|
||||
/* --- Extended type checks and text stoning --- */
|
||||
|
||||
void cell_rt_stone_text(JSValue v) {
|
||||
stone_mutable_text(v);
|
||||
}
|
||||
|
||||
int cell_rt_is_blob(JSValue v) {
|
||||
return mist_is_blob(v);
|
||||
}
|
||||
|
||||
int cell_rt_is_data(JSValue v) {
|
||||
return mist_is_gc_object(v) && !mist_is_array(v)
|
||||
&& !mist_is_function(v) && !mist_is_blob(v);
|
||||
}
|
||||
|
||||
int cell_rt_is_fit(JSValue v) {
|
||||
if (JS_IsInt(v))
|
||||
return 1;
|
||||
if (JS_IsShortFloat(v)) {
|
||||
double d = JS_VALUE_GET_FLOAT64(v);
|
||||
return isfinite(d) && trunc(d) == d && fabs(d) <= 9007199254740992.0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cell_rt_is_char(JSValue v) {
|
||||
if (MIST_IsImmediateASCII(v))
|
||||
return MIST_GetImmediateASCIILen(v) == 1;
|
||||
if (mist_is_text(v))
|
||||
return js_string_value_len(v) == 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cell_rt_is_digit(JSValue v) {
|
||||
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
|
||||
int ch = MIST_GetImmediateASCIIChar(v, 0);
|
||||
return ch >= '0' && ch <= '9';
|
||||
}
|
||||
if (mist_is_text(v) && js_string_value_len(v) == 1) {
|
||||
uint32_t ch = js_string_value_get(v, 0);
|
||||
return ch >= '0' && ch <= '9';
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cell_rt_is_letter(JSValue v) {
|
||||
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
|
||||
int ch = MIST_GetImmediateASCIIChar(v, 0);
|
||||
return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
|
||||
}
|
||||
if (mist_is_text(v) && js_string_value_len(v) == 1) {
|
||||
uint32_t ch = js_string_value_get(v, 0);
|
||||
return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cell_rt_is_lower(JSValue v) {
|
||||
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
|
||||
int ch = MIST_GetImmediateASCIIChar(v, 0);
|
||||
return ch >= 'a' && ch <= 'z';
|
||||
}
|
||||
if (mist_is_text(v) && js_string_value_len(v) == 1) {
|
||||
uint32_t ch = js_string_value_get(v, 0);
|
||||
return ch >= 'a' && ch <= 'z';
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cell_rt_is_upper(JSValue v) {
|
||||
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
|
||||
int ch = MIST_GetImmediateASCIIChar(v, 0);
|
||||
return ch >= 'A' && ch <= 'Z';
|
||||
}
|
||||
if (mist_is_text(v) && js_string_value_len(v) == 1) {
|
||||
uint32_t ch = js_string_value_get(v, 0);
|
||||
return ch >= 'A' && ch <= 'Z';
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cell_rt_is_ws(JSValue v) {
|
||||
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
|
||||
int ch = MIST_GetImmediateASCIIChar(v, 0);
|
||||
return ch == ' ' || ch == '\t' || ch == '\n'
|
||||
|| ch == '\r' || ch == '\f' || ch == '\v';
|
||||
}
|
||||
if (mist_is_text(v) && js_string_value_len(v) == 1) {
|
||||
uint32_t ch = js_string_value_get(v, 0);
|
||||
return ch == ' ' || ch == '\t' || ch == '\n'
|
||||
|| ch == '\r' || ch == '\f' || ch == '\v';
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cell_rt_is_actor(JSContext *ctx, JSValue v) {
|
||||
int result = 0;
|
||||
if (mist_is_record(v) && !JS_IsNull(ctx->actor_sym))
|
||||
@@ -1296,25 +1139,6 @@ int cell_rt_is_actor(JSContext *ctx, JSValue v) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/* --- Type check: is_proxy (function with arity 2) --- */
|
||||
|
||||
int cell_rt_is_proxy(JSContext *ctx, JSValue v) {
|
||||
(void)ctx;
|
||||
if (!JS_IsFunction(v)) return 0;
|
||||
JSFunction *fn = JS_VALUE_GET_FUNCTION(v);
|
||||
return fn->length == 2;
|
||||
}
|
||||
|
||||
/* --- Short-circuit and/or (non-allocating) --- */
|
||||
|
||||
JSValue cell_rt_and(JSContext *ctx, JSValue left, JSValue right) {
|
||||
return JS_ToBool(ctx, left) ? right : left;
|
||||
}
|
||||
|
||||
JSValue cell_rt_or(JSContext *ctx, JSValue left, JSValue right) {
|
||||
return JS_ToBool(ctx, left) ? left : right;
|
||||
}
|
||||
|
||||
/* --- Exception checking ---
|
||||
After potentially-throwing runtime calls, QBE-generated code needs to
|
||||
check for pending exceptions and branch to the disruption handler. */
|
||||
|
||||
Reference in New Issue
Block a user