specialize add opcodes

This commit is contained in:
2025-12-30 15:30:23 -06:00
parent b64e35604a
commit 0bd752fc8c
3 changed files with 141 additions and 50 deletions

View File

@@ -210,6 +210,10 @@ DEF( mul, 1, 2, 1, none)
DEF( div, 1, 2, 1, none) DEF( div, 1, 2, 1, none)
DEF( mod, 1, 2, 1, none) DEF( mod, 1, 2, 1, none)
DEF( add, 1, 2, 1, none) DEF( add, 1, 2, 1, none)
DEF( add_int, 1, 2, 1, none)
DEF( add_float, 1, 2, 1, none)
DEF( add_string, 1, 2, 1, none)
DEF( add_nullnum, 1, 2, 1, none)
DEF( sub, 1, 2, 1, none) DEF( sub, 1, 2, 1, none)
DEF( pow, 1, 2, 1, none) DEF( pow, 1, 2, 1, none)
DEF( shl, 1, 2, 1, none) DEF( shl, 1, 2, 1, none)

View File

@@ -12670,6 +12670,18 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
#define CASE(op) case op #define CASE(op) case op
#define DEFAULT default #define DEFAULT default
#define BREAK break #define BREAK break
#define PATCH_OPCODE(new_op) do { \
const uint8_t *instr_ptr = pc - 1; \
uint8_t *bc = (uint8_t *)b->byte_code_buf; \
size_t instr_idx = (size_t)(instr_ptr - b->byte_code_buf); \
bc[instr_idx] = (new_op); \
} while (0)
#define SWITCH_OPCODE(new_op, target_label) do { \
PATCH_OPCODE(new_op); \
goto target_label; \
} while (0)
#define DEOPT(target_label) goto target_label
#else #else
static const void * const dispatch_table[256] = { static const void * const dispatch_table[256] = {
#define DEF(id, size, n_pop, n_push, f) && case_OP_ ## id, #define DEF(id, size, n_pop, n_push, f) && case_OP_ ## id,
@@ -12685,6 +12697,18 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
#define CASE(op) case_ ## op #define CASE(op) case_ ## op
#define DEFAULT case_default #define DEFAULT case_default
#define BREAK SWITCH(pc) #define BREAK SWITCH(pc)
#define PATCH_OPCODE(new_op) do { \
const uint8_t *instr_ptr = pc - 1; \
uint8_t *bc = (uint8_t *)b->byte_code_buf; \
size_t instr_idx = (size_t)(instr_ptr - b->byte_code_buf); \
bc[instr_idx] = (new_op); \
} while (0)
#define SWITCH_OPCODE(new_op, target_label) do { \
PATCH_OPCODE(new_op); \
goto target_label; \
} while (0)
#define DEOPT(target_label) goto target_label
#endif #endif
if (js_poll_interrupts(caller_ctx)) if (js_poll_interrupts(caller_ctx))
@@ -14275,58 +14299,120 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
goto exception; goto exception;
} }
BREAK; BREAK;
CASE(OP_add): {
CASE(OP_add): OPADD:
{
JSValue op1 = sp[-2], op2 = sp[-1], res; JSValue op1 = sp[-2], op2 = sp[-1], res;
int tag1 = JS_VALUE_GET_NORM_TAG(op1); int tag1 = JS_VALUE_GET_TAG(op1);
int tag2 = JS_VALUE_GET_NORM_TAG(op2); int tag2 = JS_VALUE_GET_TAG(op2);
/* 1) both ints? keep fast int path with overflow check */
if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
int64_t tmp = (int64_t)JS_VALUE_GET_INT(op1)
+ JS_VALUE_GET_INT(op2);
if (likely((int)tmp == tmp)) {
res = JS_NewInt32(ctx, (int)tmp);
} else {
res = __JS_NewFloat64(ctx, (double)JS_VALUE_GET_INT(op1) + (double)JS_VALUE_GET_INT(op2));
}
}
/* 2) both floats? */
else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
res = __JS_NewFloat64(ctx,
JS_VALUE_GET_FLOAT64(op1)
+ JS_VALUE_GET_FLOAT64(op2));
}
/* 3) both strings? */
else if (JS_IsString(op1) && JS_IsString(op2)) {
res = JS_ConcatString(ctx, op1, op2);
if (JS_IsException(res))
goto exception;
}
/* 4) mixed int/float? promote to float */
// TODO: Seems slow
else if ((tag1 == JS_TAG_INT && tag2 == JS_TAG_FLOAT64) ||
(tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_INT)) {
double a, b;
if(tag1 == JS_TAG_INT)
a = (double)JS_VALUE_GET_INT(op1);
else
a = JS_VALUE_GET_FLOAT64(op1);
if(tag2 == JS_TAG_INT)
b = (double)JS_VALUE_GET_INT(op2);
else
b = JS_VALUE_GET_FLOAT64(op2);
res = __JS_NewFloat64(ctx, a + b);
}
/* 5) anything else → throw */
else {
JS_ThrowTypeError(ctx, "cannot concatenate with string");
goto exception;
}
if (tag1 == JS_TAG_NULL || tag2 == JS_TAG_NULL) {
int other = (tag1 == JS_TAG_NULL) ? tag2 : tag1;
if (JS_TAG_IS_NUMBER(other) || other == JS_TAG_NULL) {
PATCH_OPCODE(OP_add_nullnum);
res = JS_NULL;
sp[-2] = res; sp[-2] = res;
sp--; sp--;
BREAK;
}
goto add_type_error;
}
if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) {
if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) {
PATCH_OPCODE(OP_add_string);
res = JS_ConcatString(ctx, op1, op2);
if (JS_IsException(res)) goto exception;
sp[-2] = res;
sp--;
BREAK;
}
goto add_type_error;
}
if (likely((tag1 | tag2) == 0)) {
int64_t tmp = (int64_t)JS_VALUE_GET_INT(op1) + (int64_t)JS_VALUE_GET_INT(op2);
if (likely((int32_t)tmp == tmp)) {
PATCH_OPCODE(OP_add_int);
res = JS_NewInt32(ctx, (int32_t)tmp);
} else {
res = __JS_NewFloat64(ctx, (double)tmp);
}
sp[-2] = res;
sp--;
BREAK;
}
if (JS_TAG_IS_NUMBER(tag1) && JS_TAG_IS_NUMBER(tag2)) {
PATCH_OPCODE(OP_add_float);
res = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) + JS_VALUE_GET_FLOAT64(op2));
sp[-2] = res;
sp--;
BREAK;
}
add_type_error:
/* throw your “illegal +” error */
goto exception;
}
CASE(OP_add_int):
{
CASE_OP_add_int_entry:
JSValue op1 = sp[-2], op2 = sp[-1];
if (likely((JS_VALUE_GET_TAG(op1) | JS_VALUE_GET_TAG(op2)) == 0)) {
int64_t tmp = (int64_t)JS_VALUE_GET_INT(op1) + (int64_t)JS_VALUE_GET_INT(op2);
if (likely((int32_t)tmp == tmp)) {
sp[-2] = JS_NewInt32(ctx, (int32_t)tmp);
sp--;
} else {
DEOPT(OPADD);
}
} else {
DEOPT(OPADD);
}
}
BREAK;
CASE(OP_add_float): {
CASE_OP_add_float_entry:
JSValue op1 = sp[-2], op2 = sp[-1], res;
if (likely(JS_VALUE_IS_BOTH_FLOAT(op1, op2))) {
res = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) + JS_VALUE_GET_FLOAT64(op2));
sp[-2] = res;
sp--;
} else {
DEOPT(OPADD);
}
}
BREAK;
CASE(OP_add_string): {
CASE_OP_add_string_entry:
JSValue op1 = sp[-2], op2 = sp[-1], res;
int t1 = JS_VALUE_GET_TAG(op1), t2 = JS_VALUE_GET_TAG(op2);
if (likely(t1 == JS_TAG_STRING && t2 == JS_TAG_STRING)) {
res = JS_ConcatString(ctx, op1, op2);
if (JS_IsException(res)) goto exception;
sp[-2] = res;
sp--;
} else {
DEOPT(OPADD);
}
}
BREAK;
CASE(OP_add_nullnum): {
CASE_OP_add_nullnum_entry:
JSValue op1 = sp[-2], op2 = sp[-1];
int t1 = JS_VALUE_GET_TAG(op1), t2 = JS_VALUE_GET_TAG(op2);
if ((t1 == JS_TAG_NULL && (JS_TAG_IS_NUMBER(t2) || t2 == JS_TAG_NULL)) ||
(t2 == JS_TAG_NULL && (JS_TAG_IS_NUMBER(t1) || t1 == JS_TAG_NULL))) {
sp[-2] = JS_NULL;
sp--;
} else {
DEOPT(OPADD);
}
} }
BREAK; BREAK;
CASE(OP_add_loc): CASE(OP_add_loc):

View File

@@ -262,7 +262,8 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
#define JS_VALUE_IS_BOTH_INT(v1, v2) ((JS_VALUE_GET_TAG(v1) | JS_VALUE_GET_TAG(v2)) == 0) #define JS_VALUE_IS_BOTH_INT(v1, v2) ((JS_VALUE_GET_TAG(v1) | JS_VALUE_GET_TAG(v2)) == 0)
#define JS_VALUE_IS_BOTH_FLOAT(v1, v2) (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v1)) && JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v2))) #define JS_VALUE_IS_BOTH_FLOAT(v1, v2) (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v1)) && JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v2)))
#define JS_TAG_IS_STRING(v1) (v1 == JS_TAG_STRING || v1 == JS_TAG_STRING_ROPE)
#define JS_TAG_IS_NUMBER(v1) (JS_TAG_IS_FLOAT64(v1) || v1 == JS_TAG_INT)
#define JS_VALUE_HAS_REF_COUNT(v) ((unsigned)JS_VALUE_GET_TAG(v) >= (unsigned)JS_TAG_FIRST) #define JS_VALUE_HAS_REF_COUNT(v) ((unsigned)JS_VALUE_GET_TAG(v) >= (unsigned)JS_TAG_FIRST)
/* special values */ /* special values */