lower ops directly
This commit is contained in:
94
mcode.cm
94
mcode.cm
@@ -41,6 +41,28 @@ var mcode = function(ast) {
|
||||
length: "length"
|
||||
}
|
||||
|
||||
// Numeric intrinsic lowering maps (Tier 1 direct mcode).
|
||||
var intrinsic_num_unary_ops = {
|
||||
abs: "abs",
|
||||
sign: "sign",
|
||||
fraction: "fraction",
|
||||
integer: "integer",
|
||||
whole: "integer",
|
||||
neg: "negate"
|
||||
}
|
||||
var intrinsic_num_binary_ops = {
|
||||
modulo: "modulo",
|
||||
remainder: "remainder",
|
||||
max: "max",
|
||||
min: "min"
|
||||
}
|
||||
var intrinsic_num_place_ops = {
|
||||
floor: "floor",
|
||||
ceiling: "ceiling",
|
||||
round: "round",
|
||||
trunc: "trunc"
|
||||
}
|
||||
|
||||
// Compiler state
|
||||
var s_instructions = null
|
||||
var s_data = null
|
||||
@@ -877,6 +899,56 @@ var mcode = function(ast) {
|
||||
}
|
||||
}
|
||||
|
||||
// Intrinsic numeric helpers:
|
||||
// preserve native intrinsic behavior for bad argument types by returning null.
|
||||
var emit_intrinsic_num_unary = function(op, arg_slot) {
|
||||
var dest = alloc_slot()
|
||||
var t = alloc_slot()
|
||||
var bad = gen_label(op + "_arg_bad")
|
||||
var done = gen_label(op + "_arg_done")
|
||||
emit_2("is_num", t, arg_slot)
|
||||
emit_jump_cond("jump_false", t, bad)
|
||||
emit_2(op, dest, arg_slot)
|
||||
emit_jump(done)
|
||||
emit_label(bad)
|
||||
emit_1("null", dest)
|
||||
emit_label(done)
|
||||
return dest
|
||||
}
|
||||
|
||||
var emit_intrinsic_num_binary = function(op, left_slot, right_slot) {
|
||||
var dest = alloc_slot()
|
||||
var t0 = alloc_slot()
|
||||
var t1 = alloc_slot()
|
||||
var bad = gen_label(op + "_arg_bad")
|
||||
var done = gen_label(op + "_arg_done")
|
||||
emit_2("is_num", t0, left_slot)
|
||||
emit_jump_cond("jump_false", t0, bad)
|
||||
emit_2("is_num", t1, right_slot)
|
||||
emit_jump_cond("jump_false", t1, bad)
|
||||
emit_3(op, dest, left_slot, right_slot)
|
||||
emit_jump(done)
|
||||
emit_label(bad)
|
||||
emit_1("null", dest)
|
||||
emit_label(done)
|
||||
return dest
|
||||
}
|
||||
|
||||
var emit_intrinsic_num_place = function(op, value_slot, place_slot) {
|
||||
var dest = alloc_slot()
|
||||
var t = alloc_slot()
|
||||
var bad = gen_label(op + "_arg_bad")
|
||||
var done = gen_label(op + "_arg_done")
|
||||
emit_2("is_num", t, value_slot)
|
||||
emit_jump_cond("jump_false", t, bad)
|
||||
emit_3(op, dest, value_slot, place_slot)
|
||||
emit_jump(done)
|
||||
emit_label(bad)
|
||||
emit_1("null", dest)
|
||||
emit_label(done)
|
||||
return dest
|
||||
}
|
||||
|
||||
// Scan scope record for variable declarations
|
||||
var scan_scope = function() {
|
||||
var scope = find_scope_record(s_function_nr)
|
||||
@@ -1796,6 +1868,28 @@ var mcode = function(ast) {
|
||||
if (callee_kind == "name" && callee.intrinsic == true) {
|
||||
fname = callee.name
|
||||
nargs = args_list != null ? length(args_list) : 0
|
||||
mop = intrinsic_num_unary_ops[fname]
|
||||
if (mop != null && nargs == 1) {
|
||||
a0 = gen_expr(args_list[0], -1)
|
||||
return emit_intrinsic_num_unary(mop, a0)
|
||||
}
|
||||
mop = intrinsic_num_binary_ops[fname]
|
||||
if (mop != null && nargs == 2) {
|
||||
a0 = gen_expr(args_list[0], -1)
|
||||
a1 = gen_expr(args_list[1], -1)
|
||||
return emit_intrinsic_num_binary(mop, a0, a1)
|
||||
}
|
||||
mop = intrinsic_num_place_ops[fname]
|
||||
if (mop != null && (nargs == 1 || nargs == 2)) {
|
||||
a0 = gen_expr(args_list[0], -1)
|
||||
if (nargs == 2) {
|
||||
a1 = gen_expr(args_list[1], -1)
|
||||
} else {
|
||||
a1 = alloc_slot()
|
||||
emit_1("null", a1)
|
||||
}
|
||||
return emit_intrinsic_num_place(mop, a0, a1)
|
||||
}
|
||||
// 1-arg type check intrinsics → direct opcode
|
||||
if (nargs == 1 && sensory_ops[fname] != null) {
|
||||
a0 = gen_expr(args_list[0], -1)
|
||||
|
||||
185
qbe_emit.cm
185
qbe_emit.cm
@@ -1730,9 +1730,192 @@ var qbe_emit = function(ir, qbe, export_name) {
|
||||
p = fresh()
|
||||
lhs_d = emit_num_to_double(lhs)
|
||||
rhs_d = emit_num_to_double(rhs)
|
||||
emit(` %${p}_rd =d call $fmod(d ${lhs_d}, d ${rhs_d})`)
|
||||
emit(` %${p}_lhs_nan =w cned ${lhs_d}, ${lhs_d}`)
|
||||
emit(` %${p}_rhs_nan =w cned ${rhs_d}, ${rhs_d}`)
|
||||
emit(` %${p}_has_nan =w or %${p}_lhs_nan, %${p}_rhs_nan`)
|
||||
emit(` jnz %${p}_has_nan, @${p}_bad, @${p}_chk0`)
|
||||
emit(`@${p}_chk0`)
|
||||
emit(` %${p}_rhs0 =w ceqd ${rhs_d}, d_0.0`)
|
||||
emit(` jnz %${p}_rhs0, @${p}_bad, @${p}_calc`)
|
||||
emit(`@${p}_calc`)
|
||||
emit(` %${p}_q =d div ${lhs_d}, ${rhs_d}`)
|
||||
emit(` %${p}_qf =d call $floor(d %${p}_q)`)
|
||||
emit(` %${p}_m =d mul ${rhs_d}, %${p}_qf`)
|
||||
emit(` %${p}_rd =d sub ${lhs_d}, %${p}_m`)
|
||||
emit(` %${p}_r =l call $qbe_new_float64(l %ctx, d %${p}_rd)`)
|
||||
s_write(a1, `%${p}_r`)
|
||||
emit(` jmp @${p}_done`)
|
||||
emit(`@${p}_bad`)
|
||||
s_write(a1, text(qbe.js_null))
|
||||
emit(`@${p}_done`)
|
||||
continue
|
||||
}
|
||||
if (op == "remainder") {
|
||||
lhs = s_read(a2)
|
||||
rhs = s_read(a3)
|
||||
p = fresh()
|
||||
lhs_d = emit_num_to_double(lhs)
|
||||
rhs_d = emit_num_to_double(rhs)
|
||||
emit(` %${p}_rhs0 =w ceqd ${rhs_d}, d_0.0`)
|
||||
emit(` jnz %${p}_rhs0, @${p}_bad, @${p}_calc`)
|
||||
emit(`@${p}_calc`)
|
||||
emit(` %${p}_q =d div ${lhs_d}, ${rhs_d}`)
|
||||
emit(` %${p}_qt =d call $trunc(d %${p}_q)`)
|
||||
emit(` %${p}_m =d mul ${rhs_d}, %${p}_qt`)
|
||||
emit(` %${p}_rd =d sub ${lhs_d}, %${p}_m`)
|
||||
emit(` %${p}_r =l call $qbe_new_float64(l %ctx, d %${p}_rd)`)
|
||||
s_write(a1, `%${p}_r`)
|
||||
emit(` jmp @${p}_done`)
|
||||
emit(`@${p}_bad`)
|
||||
s_write(a1, text(qbe.js_null))
|
||||
emit(`@${p}_done`)
|
||||
continue
|
||||
}
|
||||
if (op == "max" || op == "min") {
|
||||
lhs = s_read(a2)
|
||||
rhs = s_read(a3)
|
||||
p = fresh()
|
||||
lhs_d = emit_num_to_double(lhs)
|
||||
rhs_d = emit_num_to_double(rhs)
|
||||
if (op == "max") {
|
||||
emit(` %${p}_take_l =w cgtd ${lhs_d}, ${rhs_d}`)
|
||||
} else {
|
||||
emit(` %${p}_take_l =w cltd ${lhs_d}, ${rhs_d}`)
|
||||
}
|
||||
emit(` jnz %${p}_take_l, @${p}_lhs, @${p}_rhs`)
|
||||
emit(`@${p}_lhs`)
|
||||
emit(` %${p}_rd =d copy ${lhs_d}`)
|
||||
emit(` jmp @${p}_done_math`)
|
||||
emit(`@${p}_rhs`)
|
||||
emit(` %${p}_rd =d copy ${rhs_d}`)
|
||||
emit(`@${p}_done_math`)
|
||||
emit(` %${p}_r =l call $qbe_new_float64(l %ctx, d %${p}_rd)`)
|
||||
s_write(a1, `%${p}_r`)
|
||||
continue
|
||||
}
|
||||
if (op == "abs") {
|
||||
lhs = s_read(a2)
|
||||
p = fresh()
|
||||
lhs_d = emit_num_to_double(lhs)
|
||||
emit(` %${p}_rd =d call $fabs(d ${lhs_d})`)
|
||||
emit(` %${p}_r =l call $qbe_new_float64(l %ctx, d %${p}_rd)`)
|
||||
s_write(a1, `%${p}_r`)
|
||||
continue
|
||||
}
|
||||
if (op == "sign") {
|
||||
lhs = s_read(a2)
|
||||
p = fresh()
|
||||
lhs_d = emit_num_to_double(lhs)
|
||||
emit(` %${p}_lt0 =w cltd ${lhs_d}, d_0.0`)
|
||||
emit(` jnz %${p}_lt0, @${p}_neg, @${p}_chk_pos`)
|
||||
emit(`@${p}_chk_pos`)
|
||||
emit(` %${p}_gt0 =w cgtd ${lhs_d}, d_0.0`)
|
||||
emit(` jnz %${p}_gt0, @${p}_pos, @${p}_zero`)
|
||||
emit(`@${p}_neg`)
|
||||
s_write(a1, text(-2))
|
||||
emit(` jmp @${p}_done`)
|
||||
emit(`@${p}_pos`)
|
||||
s_write(a1, text(2))
|
||||
emit(` jmp @${p}_done`)
|
||||
emit(`@${p}_zero`)
|
||||
s_write(a1, text(0))
|
||||
emit(`@${p}_done`)
|
||||
continue
|
||||
}
|
||||
if (op == "fraction") {
|
||||
lhs = s_read(a2)
|
||||
p = fresh()
|
||||
lhs_d = emit_num_to_double(lhs)
|
||||
emit(` %${p}_ti =d call $trunc(d ${lhs_d})`)
|
||||
emit(` %${p}_rd =d sub ${lhs_d}, %${p}_ti`)
|
||||
emit(` %${p}_r =l call $qbe_new_float64(l %ctx, d %${p}_rd)`)
|
||||
s_write(a1, `%${p}_r`)
|
||||
continue
|
||||
}
|
||||
if (op == "integer") {
|
||||
lhs = s_read(a2)
|
||||
p = fresh()
|
||||
lhs_d = emit_num_to_double(lhs)
|
||||
emit(` %${p}_rd =d call $trunc(d ${lhs_d})`)
|
||||
emit(` %${p}_r =l call $qbe_new_float64(l %ctx, d %${p}_rd)`)
|
||||
s_write(a1, `%${p}_r`)
|
||||
continue
|
||||
}
|
||||
if (op == "floor" || op == "ceiling" || op == "round" || op == "trunc") {
|
||||
lhs = s_read(a2)
|
||||
rhs = s_read(a3)
|
||||
p = fresh()
|
||||
lhs_d = emit_num_to_double(lhs)
|
||||
emit(` %${p}_lhs_num =w copy ${emit_is_num_w(lhs)}`)
|
||||
emit(` jnz %${p}_lhs_num, @${p}_place, @${p}_bad`)
|
||||
emit(`@${p}_place`)
|
||||
emit(` %${p}_t1 =l and ${rhs}, 1`)
|
||||
emit(` %${p}_is_int =w ceql %${p}_t1, 0`)
|
||||
emit(` jnz %${p}_is_int, @${p}_pi_int, @${p}_pi_not_int`)
|
||||
emit(`@${p}_pi_int`)
|
||||
emit(` %${p}_pil =l sar ${rhs}, 1`)
|
||||
emit(` %${p}_piw =w copy %${p}_pil`)
|
||||
emit(` jmp @${p}_pi_done`)
|
||||
emit(`@${p}_pi_not_int`)
|
||||
emit(` %${p}_t5 =l and ${rhs}, 31`)
|
||||
emit(` %${p}_is_null =w ceql %${p}_t5, 7`)
|
||||
emit(` jnz %${p}_is_null, @${p}_pi_zero, @${p}_pi_chk_bool`)
|
||||
emit(`@${p}_pi_zero`)
|
||||
emit(` %${p}_piw =w copy 0`)
|
||||
emit(` jmp @${p}_pi_done`)
|
||||
emit(`@${p}_pi_chk_bool`)
|
||||
emit(` %${p}_is_bool =w ceql %${p}_t5, 3`)
|
||||
emit(` jnz %${p}_is_bool, @${p}_pi_bool, @${p}_pi_chk_float`)
|
||||
emit(`@${p}_pi_bool`)
|
||||
emit(` %${p}_bl =l shr ${rhs}, 5`)
|
||||
emit(` %${p}_bw =w copy %${p}_bl`)
|
||||
emit(` %${p}_piw =w and %${p}_bw, 1`)
|
||||
emit(` jmp @${p}_pi_done`)
|
||||
emit(`@${p}_pi_chk_float`)
|
||||
emit(` %${p}_t3 =l and ${rhs}, 7`)
|
||||
emit(` %${p}_is_float =w ceql %${p}_t3, 5`)
|
||||
emit(` jnz %${p}_is_float, @${p}_pi_float, @${p}_bad`)
|
||||
emit(`@${p}_pi_float`)
|
||||
rhs_d = emit_num_to_double(rhs)
|
||||
emit(` %${p}_piw =w dtosi ${rhs_d}`)
|
||||
emit(`@${p}_pi_done`)
|
||||
emit(` %${p}_is_zero =w ceqw %${p}_piw, 0`)
|
||||
emit(` jnz %${p}_is_zero, @${p}_direct, @${p}_scaled`)
|
||||
emit(`@${p}_direct`)
|
||||
if (op == "floor") {
|
||||
emit(` %${p}_rd =d call $floor(d ${lhs_d})`)
|
||||
} else if (op == "ceiling") {
|
||||
emit(` %${p}_rd =d call $ceil(d ${lhs_d})`)
|
||||
} else if (op == "round") {
|
||||
emit(` %${p}_rd =d call $round(d ${lhs_d})`)
|
||||
} else {
|
||||
emit(` %${p}_rd =d call $trunc(d ${lhs_d})`)
|
||||
}
|
||||
emit(` jmp @${p}_store`)
|
||||
emit(`@${p}_scaled`)
|
||||
emit(` %${p}_pl =l extsw %${p}_piw`)
|
||||
emit(` %${p}_pd =d sltof %${p}_pl`)
|
||||
emit(` %${p}_negpd =d neg %${p}_pd`)
|
||||
emit(` %${p}_mult =d call $pow(d d_10.0, d %${p}_negpd)`)
|
||||
emit(` %${p}_sd =d mul ${lhs_d}, %${p}_mult`)
|
||||
if (op == "floor") {
|
||||
emit(` %${p}_sr =d call $floor(d %${p}_sd)`)
|
||||
} else if (op == "ceiling") {
|
||||
emit(` %${p}_sr =d call $ceil(d %${p}_sd)`)
|
||||
} else if (op == "round") {
|
||||
emit(` %${p}_sr =d call $round(d %${p}_sd)`)
|
||||
} else {
|
||||
emit(` %${p}_sr =d call $trunc(d %${p}_sd)`)
|
||||
}
|
||||
emit(` %${p}_rd =d div %${p}_sr, %${p}_mult`)
|
||||
emit(` jmp @${p}_store`)
|
||||
emit(`@${p}_bad`)
|
||||
s_write(a1, text(qbe.js_null))
|
||||
emit(` jmp @${p}_done`)
|
||||
emit(`@${p}_store`)
|
||||
emit(` %${p}_r =l call $qbe_new_float64(l %ctx, d %${p}_rd)`)
|
||||
s_write(a1, `%${p}_r`)
|
||||
emit(`@${p}_done`)
|
||||
continue
|
||||
}
|
||||
if (op == "negate") {
|
||||
|
||||
181
source/mach.c
181
source/mach.c
@@ -547,7 +547,7 @@ static JSValue reg_vm_binop(JSContext *ctx, int op, JSValue a, JSValue b) {
|
||||
return JS_NewFloat64(ctx, (double)ia / (double)ib);
|
||||
case MACH_MOD:
|
||||
if (ib == 0) return JS_NULL;
|
||||
return JS_NewInt32(ctx, ia % ib);
|
||||
return JS_NewFloat64(ctx, (double)ia - ((double)ib * floor((double)ia / (double)ib)));
|
||||
case MACH_EQ:
|
||||
return JS_NewBool(ctx, ia == ib);
|
||||
case MACH_NEQ:
|
||||
@@ -673,8 +673,9 @@ static JSValue reg_vm_binop(JSContext *ctx, int op, JSValue a, JSValue b) {
|
||||
}
|
||||
case MACH_MOD: {
|
||||
if (db == 0.0) return JS_NULL;
|
||||
double r = fmod(da, db);
|
||||
if (!isfinite(r)) return JS_NULL;
|
||||
if (isnan(da) || isnan(db)) return JS_NULL;
|
||||
if (da == 0.0) return JS_NewFloat64(ctx, 0.0);
|
||||
double r = da - (db * floor(da / db));
|
||||
return JS_NewFloat64(ctx, r);
|
||||
}
|
||||
case MACH_POW: {
|
||||
@@ -704,6 +705,34 @@ static JSValue reg_vm_binop(JSContext *ctx, int op, JSValue a, JSValue b) {
|
||||
return JS_RaiseDisrupt(ctx, "type mismatch in binary operation");
|
||||
}
|
||||
|
||||
static inline int mach_get_number(JSValue v, double *out) {
|
||||
uint32_t tag = JS_VALUE_GET_TAG(v);
|
||||
if (tag == JS_TAG_INT) {
|
||||
*out = (double)JS_VALUE_GET_INT(v);
|
||||
return 0;
|
||||
}
|
||||
if (JS_TAG_IS_FLOAT64(tag)) {
|
||||
*out = JS_VALUE_GET_FLOAT64(v);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int mach_get_place(JSContext *ctx, JSValue v, int32_t *out) {
|
||||
uint32_t tag = JS_VALUE_GET_NORM_TAG(v);
|
||||
if (tag == JS_TAG_INT || tag == JS_TAG_BOOL || tag == JS_TAG_NULL || tag == JS_TAG_FLOAT64) {
|
||||
return JS_ToInt32(ctx, out, v);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline double mach_apply_place(double d, int32_t place, double (*f)(double)) {
|
||||
if (place == 0)
|
||||
return f(d);
|
||||
double mult = pow(10.0, -(double)place);
|
||||
return f(d * mult) / mult;
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_ASAN
|
||||
void __asan_on_error(void) {
|
||||
@@ -856,6 +885,12 @@ vm_dispatch:
|
||||
DT(MACH_MUL), DT(MACH_DIV),
|
||||
DT(MACH_MOD), DT(MACH_POW),
|
||||
DT(MACH_NEG),
|
||||
DT(MACH_REMAINDER), DT(MACH_MAX),
|
||||
DT(MACH_MIN), DT(MACH_ABS),
|
||||
DT(MACH_SIGN), DT(MACH_FRACTION),
|
||||
DT(MACH_INTEGER), DT(MACH_FLOOR),
|
||||
DT(MACH_CEILING), DT(MACH_ROUND),
|
||||
DT(MACH_TRUNC),
|
||||
DT(MACH_EQ), DT(MACH_NEQ),
|
||||
DT(MACH_LT), DT(MACH_LE),
|
||||
DT(MACH_GT), DT(MACH_GE),
|
||||
@@ -1043,17 +1078,15 @@ vm_dispatch:
|
||||
}
|
||||
VM_CASE(MACH_MOD): {
|
||||
JSValue left = frame->slots[b], right = frame->slots[c];
|
||||
if (JS_VALUE_IS_BOTH_INT(left, right)) {
|
||||
int32_t ib = JS_VALUE_GET_INT(right);
|
||||
frame->slots[a] = (ib != 0) ? JS_NewInt32(ctx, JS_VALUE_GET_INT(left) % ib) : JS_NULL;
|
||||
double da, db;
|
||||
if (mach_get_number(left, &da) != 0 || mach_get_number(right, &db) != 0 || db == 0.0 ||
|
||||
isnan(da) || isnan(db)) {
|
||||
frame->slots[a] = JS_NULL;
|
||||
} else {
|
||||
double da, db, r;
|
||||
JS_ToFloat64(ctx, &da, left);
|
||||
JS_ToFloat64(ctx, &db, right);
|
||||
if (db == 0.0) { frame->slots[a] = JS_NULL; }
|
||||
else {
|
||||
r = fmod(da, db);
|
||||
frame->slots[a] = !isfinite(r) ? JS_NULL : JS_NewFloat64(ctx, r);
|
||||
if (da == 0.0) {
|
||||
frame->slots[a] = JS_NewFloat64(ctx, 0.0);
|
||||
} else {
|
||||
frame->slots[a] = JS_NewFloat64(ctx, da - (db * floor(da / db)));
|
||||
}
|
||||
}
|
||||
VM_BREAK();
|
||||
@@ -1077,6 +1110,116 @@ vm_dispatch:
|
||||
VM_BREAK();
|
||||
}
|
||||
|
||||
VM_CASE(MACH_REMAINDER): {
|
||||
JSValue left = frame->slots[b], right = frame->slots[c];
|
||||
double da, db;
|
||||
if (mach_get_number(left, &da) != 0 || mach_get_number(right, &db) != 0 || db == 0.0) {
|
||||
frame->slots[a] = JS_NULL;
|
||||
} else {
|
||||
frame->slots[a] = JS_NewFloat64(ctx, da - (trunc(da / db) * db));
|
||||
}
|
||||
VM_BREAK();
|
||||
}
|
||||
|
||||
VM_CASE(MACH_MAX): {
|
||||
JSValue left = frame->slots[b], right = frame->slots[c];
|
||||
double da, db;
|
||||
if (mach_get_number(left, &da) != 0 || mach_get_number(right, &db) != 0) {
|
||||
frame->slots[a] = JS_NULL;
|
||||
} else {
|
||||
frame->slots[a] = JS_NewFloat64(ctx, da > db ? da : db);
|
||||
}
|
||||
VM_BREAK();
|
||||
}
|
||||
|
||||
VM_CASE(MACH_MIN): {
|
||||
JSValue left = frame->slots[b], right = frame->slots[c];
|
||||
double da, db;
|
||||
if (mach_get_number(left, &da) != 0 || mach_get_number(right, &db) != 0) {
|
||||
frame->slots[a] = JS_NULL;
|
||||
} else {
|
||||
frame->slots[a] = JS_NewFloat64(ctx, da < db ? da : db);
|
||||
}
|
||||
VM_BREAK();
|
||||
}
|
||||
|
||||
VM_CASE(MACH_ABS): {
|
||||
JSValue v = frame->slots[b];
|
||||
double d;
|
||||
if (mach_get_number(v, &d) != 0) {
|
||||
frame->slots[a] = JS_NULL;
|
||||
} else {
|
||||
frame->slots[a] = JS_NewFloat64(ctx, fabs(d));
|
||||
}
|
||||
VM_BREAK();
|
||||
}
|
||||
|
||||
VM_CASE(MACH_SIGN): {
|
||||
JSValue v = frame->slots[b];
|
||||
double d;
|
||||
if (mach_get_number(v, &d) != 0) {
|
||||
frame->slots[a] = JS_NULL;
|
||||
} else if (d < 0) {
|
||||
frame->slots[a] = JS_NewInt32(ctx, -1);
|
||||
} else if (d > 0) {
|
||||
frame->slots[a] = JS_NewInt32(ctx, 1);
|
||||
} else {
|
||||
frame->slots[a] = JS_NewInt32(ctx, 0);
|
||||
}
|
||||
VM_BREAK();
|
||||
}
|
||||
|
||||
VM_CASE(MACH_FRACTION): {
|
||||
JSValue v = frame->slots[b];
|
||||
double d;
|
||||
if (mach_get_number(v, &d) != 0) {
|
||||
frame->slots[a] = JS_NULL;
|
||||
} else {
|
||||
frame->slots[a] = JS_NewFloat64(ctx, d - trunc(d));
|
||||
}
|
||||
VM_BREAK();
|
||||
}
|
||||
|
||||
VM_CASE(MACH_INTEGER): {
|
||||
JSValue v = frame->slots[b];
|
||||
double d;
|
||||
if (mach_get_number(v, &d) != 0) {
|
||||
frame->slots[a] = JS_NULL;
|
||||
} else {
|
||||
frame->slots[a] = JS_NewFloat64(ctx, trunc(d));
|
||||
}
|
||||
VM_BREAK();
|
||||
}
|
||||
|
||||
VM_CASE(MACH_FLOOR):
|
||||
VM_CASE(MACH_CEILING):
|
||||
VM_CASE(MACH_ROUND):
|
||||
VM_CASE(MACH_TRUNC): {
|
||||
JSValue v = frame->slots[b];
|
||||
JSValue pval = frame->slots[c];
|
||||
double d, r;
|
||||
int32_t place = 0;
|
||||
if (mach_get_number(v, &d) != 0) {
|
||||
frame->slots[a] = JS_NULL;
|
||||
VM_BREAK();
|
||||
}
|
||||
if (!JS_IsNull(pval) && mach_get_place(ctx, pval, &place) != 0) {
|
||||
frame->slots[a] = JS_NULL;
|
||||
VM_BREAK();
|
||||
}
|
||||
if (op == MACH_FLOOR) {
|
||||
r = mach_apply_place(d, place, floor);
|
||||
} else if (op == MACH_CEILING) {
|
||||
r = mach_apply_place(d, place, ceil);
|
||||
} else if (op == MACH_ROUND) {
|
||||
r = mach_apply_place(d, place, round);
|
||||
} else {
|
||||
r = mach_apply_place(d, place, trunc);
|
||||
}
|
||||
frame->slots[a] = JS_NewFloat64(ctx, r);
|
||||
VM_BREAK();
|
||||
}
|
||||
|
||||
/* Comparison — inline integer fast paths */
|
||||
VM_CASE(MACH_EQ): {
|
||||
JSValue left = frame->slots[b], right = frame->slots[c];
|
||||
@@ -2403,6 +2546,17 @@ static MachCode *mcode_lower_func(cJSON *fobj, const char *filename) {
|
||||
else if (strcmp(op, "modulo") == 0) { ABC3(MACH_MOD); }
|
||||
else if (strcmp(op, "pow") == 0) { ABC3(MACH_POW); }
|
||||
else if (strcmp(op, "negate") == 0) { AB2(MACH_NEG); }
|
||||
else if (strcmp(op, "remainder") == 0) { ABC3(MACH_REMAINDER); }
|
||||
else if (strcmp(op, "max") == 0) { ABC3(MACH_MAX); }
|
||||
else if (strcmp(op, "min") == 0) { ABC3(MACH_MIN); }
|
||||
else if (strcmp(op, "abs") == 0) { AB2(MACH_ABS); }
|
||||
else if (strcmp(op, "sign") == 0) { AB2(MACH_SIGN); }
|
||||
else if (strcmp(op, "fraction") == 0) { AB2(MACH_FRACTION); }
|
||||
else if (strcmp(op, "integer") == 0) { AB2(MACH_INTEGER); }
|
||||
else if (strcmp(op, "floor") == 0) { ABC3(MACH_FLOOR); }
|
||||
else if (strcmp(op, "ceiling") == 0) { ABC3(MACH_CEILING); }
|
||||
else if (strcmp(op, "round") == 0) { ABC3(MACH_ROUND); }
|
||||
else if (strcmp(op, "trunc") == 0) { ABC3(MACH_TRUNC); }
|
||||
/* Typed integer comparisons */
|
||||
else if (strcmp(op, "eq_int") == 0) { ABC3(MACH_EQ_INT); }
|
||||
else if (strcmp(op, "ne_int") == 0) { ABC3(MACH_NE_INT); }
|
||||
@@ -3107,4 +3261,3 @@ void JS_DumpMachBin(JSContext *ctx, const uint8_t *data, size_t size, JSValue en
|
||||
dump_register_code(ctx, code, 0);
|
||||
JS_PopGCRef(ctx, &env_ref);
|
||||
}
|
||||
|
||||
|
||||
@@ -487,6 +487,17 @@ typedef enum MachOpcode {
|
||||
MACH_MOD, /* R(A) = R(B) % R(C) */
|
||||
MACH_POW, /* R(A) = R(B) ** R(C) */
|
||||
MACH_NEG, /* R(A) = -R(B) */
|
||||
MACH_REMAINDER, /* R(A) = remainder(R(B), R(C)) */
|
||||
MACH_MAX, /* R(A) = max(R(B), R(C)) */
|
||||
MACH_MIN, /* R(A) = min(R(B), R(C)) */
|
||||
MACH_ABS, /* R(A) = abs(R(B)) */
|
||||
MACH_SIGN, /* R(A) = sign(R(B)) */
|
||||
MACH_FRACTION, /* R(A) = fraction(R(B)) */
|
||||
MACH_INTEGER, /* R(A) = integer(R(B)) */
|
||||
MACH_FLOOR, /* R(A) = floor(R(B), R(C)) */
|
||||
MACH_CEILING, /* R(A) = ceiling(R(B), R(C)) */
|
||||
MACH_ROUND, /* R(A) = round(R(B), R(C)) */
|
||||
MACH_TRUNC, /* R(A) = trunc(R(B), R(C)) */
|
||||
MACH__DEAD_INC, /* reserved — was MACH_INC, never emitted */
|
||||
MACH__DEAD_DEC, /* reserved — was MACH_DEC, never emitted */
|
||||
|
||||
@@ -660,6 +671,17 @@ static const char *mach_opcode_names[MACH_OP_COUNT] = {
|
||||
[MACH_MOD] = "mod",
|
||||
[MACH_POW] = "pow",
|
||||
[MACH_NEG] = "neg",
|
||||
[MACH_REMAINDER] = "remainder",
|
||||
[MACH_MAX] = "max",
|
||||
[MACH_MIN] = "min",
|
||||
[MACH_ABS] = "abs",
|
||||
[MACH_SIGN] = "sign",
|
||||
[MACH_FRACTION] = "fraction",
|
||||
[MACH_INTEGER] = "integer",
|
||||
[MACH_FLOOR] = "floor",
|
||||
[MACH_CEILING] = "ceiling",
|
||||
[MACH_ROUND] = "round",
|
||||
[MACH_TRUNC] = "trunc",
|
||||
[MACH__DEAD_INC] = "dead_inc",
|
||||
[MACH__DEAD_DEC] = "dead_dec",
|
||||
[MACH_EQ] = "eq",
|
||||
|
||||
@@ -37,7 +37,8 @@ var streamline = function(ir, log) {
|
||||
|
||||
var numeric_ops = {
|
||||
add: true, subtract: true, multiply: true,
|
||||
divide: true, modulo: true, pow: true
|
||||
divide: true, modulo: true, remainder: true,
|
||||
max: true, min: true, pow: true
|
||||
}
|
||||
var bool_result_ops = {
|
||||
eq_int: true, ne_int: true, lt_int: true, gt_int: true,
|
||||
@@ -229,7 +230,12 @@ var streamline = function(ir, log) {
|
||||
add: [2, T_NUM, 3, T_NUM],
|
||||
subtract: [2, T_NUM, 3, T_NUM], multiply: [2, T_NUM, 3, T_NUM],
|
||||
divide: [2, T_NUM, 3, T_NUM], modulo: [2, T_NUM, 3, T_NUM],
|
||||
pow: [2, T_NUM, 3, T_NUM], negate: [2, T_NUM],
|
||||
remainder: [2, T_NUM, 3, T_NUM], max: [2, T_NUM, 3, T_NUM],
|
||||
min: [2, T_NUM, 3, T_NUM], pow: [2, T_NUM, 3, T_NUM],
|
||||
negate: [2, T_NUM], abs: [2, T_NUM], sign: [2, T_NUM],
|
||||
fraction: [2, T_NUM], integer: [2, T_NUM],
|
||||
floor: [2, T_NUM], ceiling: [2, T_NUM],
|
||||
round: [2, T_NUM], trunc: [2, T_NUM],
|
||||
bitand: [2, T_INT, 3, T_INT], bitor: [2, T_INT, 3, T_INT],
|
||||
bitxor: [2, T_INT, 3, T_INT], shl: [2, T_INT, 3, T_INT],
|
||||
shr: [2, T_INT, 3, T_INT], ushr: [2, T_INT, 3, T_INT],
|
||||
@@ -332,10 +338,14 @@ var streamline = function(ir, log) {
|
||||
bitnot: [1, T_INT], bitand: [1, T_INT], bitor: [1, T_INT],
|
||||
bitxor: [1, T_INT], shl: [1, T_INT], shr: [1, T_INT], ushr: [1, T_INT],
|
||||
negate: [1, T_NUM], concat: [1, T_TEXT],
|
||||
abs: [1, T_NUM], sign: [1, T_INT], fraction: [1, T_NUM],
|
||||
integer: [1, T_NUM], floor: [1, T_NUM], ceiling: [1, T_NUM],
|
||||
round: [1, T_NUM], trunc: [1, T_NUM],
|
||||
eq: [1, T_BOOL], ne: [1, T_BOOL], lt: [1, T_BOOL],
|
||||
le: [1, T_BOOL], gt: [1, T_BOOL], ge: [1, T_BOOL], in: [1, T_BOOL],
|
||||
add: [1, T_NUM], subtract: [1, T_NUM], multiply: [1, T_NUM],
|
||||
divide: [1, T_NUM], modulo: [1, T_NUM], pow: [1, T_NUM],
|
||||
divide: [1, T_NUM], modulo: [1, T_NUM], remainder: [1, T_NUM],
|
||||
max: [1, T_NUM], min: [1, T_NUM], pow: [1, T_NUM],
|
||||
move: [1, T_UNKNOWN], load_field: [1, T_UNKNOWN],
|
||||
load_index: [1, T_UNKNOWN], load_dynamic: [1, T_UNKNOWN],
|
||||
pop: [1, T_UNKNOWN], get: [1, T_UNKNOWN],
|
||||
@@ -359,8 +369,8 @@ var streamline = function(ir, log) {
|
||||
var intrinsic_return_types = {
|
||||
abs: T_NUM, floor: T_NUM, ceiling: T_NUM,
|
||||
round: T_NUM, trunc: T_NUM, fraction: T_NUM,
|
||||
integer: T_NUM, sign: T_NUM,
|
||||
max: T_NUM, min: T_NUM
|
||||
integer: T_NUM, whole: T_NUM, sign: T_NUM,
|
||||
max: T_NUM, min: T_NUM, remainder: T_NUM, modulo: T_NUM
|
||||
}
|
||||
|
||||
var infer_slot_write_types = function(func, param_types) {
|
||||
|
||||
Reference in New Issue
Block a user