Merge branch 'optimize_mcode'
This commit is contained in:
435
source/mach.c
435
source/mach.c
@@ -212,34 +212,7 @@ typedef enum MachOpcode {
|
||||
|
||||
/* Text */
|
||||
MACH_CONCAT, /* R(A) = R(B) ++ R(C) — string concatenation */
|
||||
|
||||
/* Typed integer comparisons (ABC) */
|
||||
MACH_EQ_INT, /* R(A) = (R(B) == R(C)) — int */
|
||||
MACH_NE_INT, /* R(A) = (R(B) != R(C)) — int */
|
||||
MACH_LT_INT, /* R(A) = (R(B) < R(C)) — int */
|
||||
MACH_LE_INT, /* R(A) = (R(B) <= R(C)) — int */
|
||||
MACH_GT_INT, /* R(A) = (R(B) > R(C)) — int */
|
||||
MACH_GE_INT, /* R(A) = (R(B) >= R(C)) — int */
|
||||
|
||||
/* Typed float comparisons (ABC) */
|
||||
MACH_EQ_FLOAT, /* R(A) = (R(B) == R(C)) — float */
|
||||
MACH_NE_FLOAT, /* R(A) = (R(B) != R(C)) — float */
|
||||
MACH_LT_FLOAT, /* R(A) = (R(B) < R(C)) — float */
|
||||
MACH_LE_FLOAT, /* R(A) = (R(B) <= R(C)) — float */
|
||||
MACH_GT_FLOAT, /* R(A) = (R(B) > R(C)) — float */
|
||||
MACH_GE_FLOAT, /* R(A) = (R(B) >= R(C)) — float */
|
||||
|
||||
/* Typed text comparisons (ABC) */
|
||||
MACH_EQ_TEXT, /* R(A) = (R(B) == R(C)) — text */
|
||||
MACH_NE_TEXT, /* R(A) = (R(B) != R(C)) — text */
|
||||
MACH_LT_TEXT, /* R(A) = (R(B) < R(C)) — text */
|
||||
MACH_LE_TEXT, /* R(A) = (R(B) <= R(C)) — text */
|
||||
MACH_GT_TEXT, /* R(A) = (R(B) > R(C)) — text */
|
||||
MACH_GE_TEXT, /* R(A) = (R(B) >= R(C)) — text */
|
||||
|
||||
/* Typed bool comparisons (ABC) */
|
||||
MACH_EQ_BOOL, /* R(A) = (R(B) == R(C)) — bool */
|
||||
MACH_NE_BOOL, /* R(A) = (R(B) != R(C)) — bool */
|
||||
MACH_STONE_TEXT, /* stone(R(A)) — freeze mutable text before escape */
|
||||
|
||||
/* Special comparisons */
|
||||
MACH_IS_IDENTICAL, /* R(A) = (R(B) === R(C)) — identity check (ABC) */
|
||||
@@ -296,6 +269,18 @@ typedef enum MachOpcode {
|
||||
MACH_IS_STONE, /* R(A) = is_stone(R(B)) */
|
||||
MACH_LENGTH, /* R(A) = length(R(B)) — array/text/blob length */
|
||||
MACH_IS_PROXY, /* R(A) = is_function(R(B)) && R(B).length == 2 */
|
||||
MACH_IS_BLOB, /* R(A) = is_blob(R(B)) */
|
||||
MACH_IS_DATA, /* R(A) = is_data(R(B)) — plain record, not array/func/blob */
|
||||
MACH_IS_TRUE, /* R(A) = (R(B) === true) */
|
||||
MACH_IS_FALSE, /* R(A) = (R(B) === false) */
|
||||
MACH_IS_FIT, /* R(A) = is_fit(R(B)) — safe integer */
|
||||
MACH_IS_CHAR, /* R(A) = is_character(R(B)) — single char text */
|
||||
MACH_IS_DIGIT, /* R(A) = is_digit(R(B)) */
|
||||
MACH_IS_LETTER, /* R(A) = is_letter(R(B)) */
|
||||
MACH_IS_LOWER, /* R(A) = is_lower(R(B)) */
|
||||
MACH_IS_UPPER, /* R(A) = is_upper(R(B)) */
|
||||
MACH_IS_WS, /* R(A) = is_whitespace(R(B)) */
|
||||
MACH_IS_ACTOR, /* R(A) = is_actor(R(B)) — has actor_sym property */
|
||||
|
||||
MACH_OP_COUNT
|
||||
} MachOpcode;
|
||||
@@ -372,26 +357,7 @@ static const char *mach_opcode_names[MACH_OP_COUNT] = {
|
||||
[MACH_NOP] = "nop",
|
||||
/* Mcode-derived */
|
||||
[MACH_CONCAT] = "concat",
|
||||
[MACH_EQ_INT] = "eq_int",
|
||||
[MACH_NE_INT] = "ne_int",
|
||||
[MACH_LT_INT] = "lt_int",
|
||||
[MACH_LE_INT] = "le_int",
|
||||
[MACH_GT_INT] = "gt_int",
|
||||
[MACH_GE_INT] = "ge_int",
|
||||
[MACH_EQ_FLOAT] = "eq_float",
|
||||
[MACH_NE_FLOAT] = "ne_float",
|
||||
[MACH_LT_FLOAT] = "lt_float",
|
||||
[MACH_LE_FLOAT] = "le_float",
|
||||
[MACH_GT_FLOAT] = "gt_float",
|
||||
[MACH_GE_FLOAT] = "ge_float",
|
||||
[MACH_EQ_TEXT] = "eq_text",
|
||||
[MACH_NE_TEXT] = "ne_text",
|
||||
[MACH_LT_TEXT] = "lt_text",
|
||||
[MACH_LE_TEXT] = "le_text",
|
||||
[MACH_GT_TEXT] = "gt_text",
|
||||
[MACH_GE_TEXT] = "ge_text",
|
||||
[MACH_EQ_BOOL] = "eq_bool",
|
||||
[MACH_NE_BOOL] = "ne_bool",
|
||||
[MACH_STONE_TEXT] = "stone_text",
|
||||
[MACH_IS_IDENTICAL] = "is_identical",
|
||||
[MACH_IS_INT] = "is_int",
|
||||
[MACH_IS_NUM] = "is_num",
|
||||
@@ -427,6 +393,18 @@ static const char *mach_opcode_names[MACH_OP_COUNT] = {
|
||||
[MACH_IS_STONE] = "is_stone",
|
||||
[MACH_LENGTH] = "length",
|
||||
[MACH_IS_PROXY] = "is_proxy",
|
||||
[MACH_IS_BLOB] = "is_blob",
|
||||
[MACH_IS_DATA] = "is_data",
|
||||
[MACH_IS_TRUE] = "is_true",
|
||||
[MACH_IS_FALSE] = "is_false",
|
||||
[MACH_IS_FIT] = "is_fit",
|
||||
[MACH_IS_CHAR] = "is_char",
|
||||
[MACH_IS_DIGIT] = "is_digit",
|
||||
[MACH_IS_LETTER] = "is_letter",
|
||||
[MACH_IS_LOWER] = "is_lower",
|
||||
[MACH_IS_UPPER] = "is_upper",
|
||||
[MACH_IS_WS] = "is_ws",
|
||||
[MACH_IS_ACTOR] = "is_actor",
|
||||
};
|
||||
|
||||
/* ---- Compile-time constant pool entry ---- */
|
||||
@@ -1080,10 +1058,6 @@ static JSValue reg_vm_binop(JSContext *ctx, int op, JSValue a, JSValue b) {
|
||||
}
|
||||
}
|
||||
|
||||
/* String concat for ADD */
|
||||
if (op == MACH_ADD && mist_is_text(a) && mist_is_text(b))
|
||||
return JS_ConcatString(ctx, a, b);
|
||||
|
||||
/* Comparison ops allow mixed types — return false for mismatches */
|
||||
if (op >= MACH_EQ && op <= MACH_GE) {
|
||||
/* Fast path: identical values (chase pointers for forwarded objects) */
|
||||
@@ -1142,7 +1116,10 @@ static JSValue reg_vm_binop(JSContext *ctx, int op, JSValue a, JSValue b) {
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
/* Different types: EQ→false, NEQ→true, others→false */
|
||||
/* Different types for ordering comparisons: disrupt */
|
||||
if (op >= MACH_LT && op <= MACH_GE)
|
||||
return JS_RaiseDisrupt(ctx, "cannot compare: operands must be same type");
|
||||
/* EQ/NEQ with different types: false/true */
|
||||
if (op == MACH_NEQ) return JS_NewBool(ctx, 1);
|
||||
return JS_NewBool(ctx, 0);
|
||||
}
|
||||
@@ -1422,17 +1399,7 @@ vm_dispatch:
|
||||
DT(MACH_HASPROP), DT(MACH_REGEXP),
|
||||
DT(MACH_EQ_TOL), DT(MACH_NEQ_TOL),
|
||||
DT(MACH_NOP),
|
||||
DT(MACH_CONCAT),
|
||||
DT(MACH_EQ_INT), DT(MACH_NE_INT),
|
||||
DT(MACH_LT_INT), DT(MACH_LE_INT),
|
||||
DT(MACH_GT_INT), DT(MACH_GE_INT),
|
||||
DT(MACH_EQ_FLOAT), DT(MACH_NE_FLOAT),
|
||||
DT(MACH_LT_FLOAT), DT(MACH_LE_FLOAT),
|
||||
DT(MACH_GT_FLOAT), DT(MACH_GE_FLOAT),
|
||||
DT(MACH_EQ_TEXT), DT(MACH_NE_TEXT),
|
||||
DT(MACH_LT_TEXT), DT(MACH_LE_TEXT),
|
||||
DT(MACH_GT_TEXT), DT(MACH_GE_TEXT),
|
||||
DT(MACH_EQ_BOOL), DT(MACH_NE_BOOL),
|
||||
DT(MACH_CONCAT), DT(MACH_STONE_TEXT),
|
||||
DT(MACH_IS_IDENTICAL),
|
||||
DT(MACH_IS_INT), DT(MACH_IS_NUM),
|
||||
DT(MACH_IS_TEXT), DT(MACH_IS_BOOL),
|
||||
@@ -1453,6 +1420,12 @@ vm_dispatch:
|
||||
DT(MACH_IS_ARRAY), DT(MACH_IS_FUNC),
|
||||
DT(MACH_IS_RECORD), DT(MACH_IS_STONE),
|
||||
DT(MACH_LENGTH), DT(MACH_IS_PROXY),
|
||||
DT(MACH_IS_BLOB), DT(MACH_IS_DATA),
|
||||
DT(MACH_IS_TRUE), DT(MACH_IS_FALSE),
|
||||
DT(MACH_IS_FIT), DT(MACH_IS_CHAR),
|
||||
DT(MACH_IS_DIGIT), DT(MACH_IS_LETTER),
|
||||
DT(MACH_IS_LOWER), DT(MACH_IS_UPPER),
|
||||
DT(MACH_IS_WS), DT(MACH_IS_ACTOR),
|
||||
};
|
||||
#pragma GCC diagnostic pop
|
||||
#undef DT
|
||||
@@ -2062,6 +2035,7 @@ vm_dispatch:
|
||||
}
|
||||
target = next;
|
||||
}
|
||||
stone_mutable_text(target->slots[c]);
|
||||
frame->slots[a] = target->slots[c];
|
||||
VM_BREAK();
|
||||
}
|
||||
@@ -2171,6 +2145,7 @@ vm_dispatch:
|
||||
}
|
||||
|
||||
VM_CASE(MACH_RETURN):
|
||||
stone_mutable_text(frame->slots[a]);
|
||||
result = frame->slots[a];
|
||||
if (!JS_IsPtr(frame->caller)) goto done;
|
||||
{
|
||||
@@ -2338,81 +2313,46 @@ vm_dispatch:
|
||||
|
||||
/* === New mcode-derived opcodes === */
|
||||
|
||||
/* Text concatenation */
|
||||
/* Text concatenation — with in-place append fast path for s = s + x */
|
||||
VM_CASE(MACH_CONCAT): {
|
||||
JSValue res = JS_ConcatString(ctx, frame->slots[b], frame->slots[c]);
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||
if (JS_IsException(res)) goto disrupt;
|
||||
frame->slots[a] = res;
|
||||
VM_BREAK();
|
||||
}
|
||||
|
||||
/* Typed integer comparisons */
|
||||
VM_CASE(MACH_EQ_INT):
|
||||
frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[b]) == JS_VALUE_GET_INT(frame->slots[c]));
|
||||
VM_BREAK();
|
||||
VM_CASE(MACH_NE_INT):
|
||||
frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[b]) != JS_VALUE_GET_INT(frame->slots[c]));
|
||||
VM_BREAK();
|
||||
VM_CASE(MACH_LT_INT):
|
||||
frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[b]) < JS_VALUE_GET_INT(frame->slots[c]));
|
||||
VM_BREAK();
|
||||
VM_CASE(MACH_LE_INT):
|
||||
frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[b]) <= JS_VALUE_GET_INT(frame->slots[c]));
|
||||
VM_BREAK();
|
||||
VM_CASE(MACH_GT_INT):
|
||||
frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[b]) > JS_VALUE_GET_INT(frame->slots[c]));
|
||||
VM_BREAK();
|
||||
VM_CASE(MACH_GE_INT):
|
||||
frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[b]) >= JS_VALUE_GET_INT(frame->slots[c]));
|
||||
VM_BREAK();
|
||||
|
||||
/* Typed float comparisons */
|
||||
VM_CASE(MACH_EQ_FLOAT): VM_CASE(MACH_NE_FLOAT):
|
||||
VM_CASE(MACH_LT_FLOAT): VM_CASE(MACH_LE_FLOAT):
|
||||
VM_CASE(MACH_GT_FLOAT): VM_CASE(MACH_GE_FLOAT): {
|
||||
double da, db;
|
||||
JS_ToFloat64(ctx, &da, frame->slots[b]);
|
||||
JS_ToFloat64(ctx, &db, frame->slots[c]);
|
||||
int r;
|
||||
switch (op) {
|
||||
case MACH_EQ_FLOAT: r = (da == db); break;
|
||||
case MACH_NE_FLOAT: r = (da != db); break;
|
||||
case MACH_LT_FLOAT: r = (da < db); break;
|
||||
case MACH_LE_FLOAT: r = (da <= db); break;
|
||||
case MACH_GT_FLOAT: r = (da > db); break;
|
||||
case MACH_GE_FLOAT: r = (da >= db); break;
|
||||
default: r = 0; break;
|
||||
if (a == b) {
|
||||
/* Self-assign pattern: slot[a] = slot[a] + slot[c] */
|
||||
JSValue left = frame->slots[a];
|
||||
JSValue right = frame->slots[c];
|
||||
/* Inline fast path: mutable heap text with enough capacity */
|
||||
if (JS_IsPtr(left)) {
|
||||
JSText *s = (JSText *)chase(left);
|
||||
int slen = (int)s->length;
|
||||
int rlen = js_string_value_len(right);
|
||||
int cap = (int)objhdr_cap56(s->hdr);
|
||||
if (objhdr_type(s->hdr) == OBJ_TEXT
|
||||
&& !(s->hdr & OBJHDR_S_MASK)
|
||||
&& slen + rlen <= cap) {
|
||||
/* Append in-place — zero allocation, no GC possible */
|
||||
for (int i = 0; i < rlen; i++)
|
||||
string_put(s, slen + i, js_string_value_get(right, i));
|
||||
s->length = slen + rlen;
|
||||
VM_BREAK();
|
||||
}
|
||||
}
|
||||
/* Slow path: allocate with growth factor, leave unstoned */
|
||||
JSValue res = JS_ConcatStringGrow(ctx, frame->slots[b], frame->slots[c]);
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||
if (JS_IsException(res)) goto disrupt;
|
||||
frame->slots[a] = res;
|
||||
} else {
|
||||
/* Different target: use existing exact-fit stoned path */
|
||||
JSValue res = JS_ConcatString(ctx, frame->slots[b], frame->slots[c]);
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||
if (JS_IsException(res)) goto disrupt;
|
||||
frame->slots[a] = res;
|
||||
}
|
||||
frame->slots[a] = JS_NewBool(ctx, r);
|
||||
VM_BREAK();
|
||||
}
|
||||
|
||||
/* Typed text comparisons */
|
||||
VM_CASE(MACH_EQ_TEXT): VM_CASE(MACH_NE_TEXT):
|
||||
VM_CASE(MACH_LT_TEXT): VM_CASE(MACH_LE_TEXT):
|
||||
VM_CASE(MACH_GT_TEXT): VM_CASE(MACH_GE_TEXT): {
|
||||
int cmp = js_string_compare_value(ctx, frame->slots[b], frame->slots[c], FALSE);
|
||||
int r;
|
||||
switch (op) {
|
||||
case MACH_EQ_TEXT: r = (cmp == 0); break;
|
||||
case MACH_NE_TEXT: r = (cmp != 0); break;
|
||||
case MACH_LT_TEXT: r = (cmp < 0); break;
|
||||
case MACH_LE_TEXT: r = (cmp <= 0); break;
|
||||
case MACH_GT_TEXT: r = (cmp > 0); break;
|
||||
case MACH_GE_TEXT: r = (cmp >= 0); break;
|
||||
default: r = 0; break;
|
||||
}
|
||||
frame->slots[a] = JS_NewBool(ctx, r);
|
||||
VM_BREAK();
|
||||
}
|
||||
|
||||
/* Typed bool comparisons */
|
||||
VM_CASE(MACH_EQ_BOOL):
|
||||
frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_BOOL(frame->slots[b]) == JS_VALUE_GET_BOOL(frame->slots[c]));
|
||||
VM_BREAK();
|
||||
VM_CASE(MACH_NE_BOOL):
|
||||
frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_BOOL(frame->slots[b]) != JS_VALUE_GET_BOOL(frame->slots[c]));
|
||||
/* Stone mutable text — compiler-emitted at escape points */
|
||||
VM_CASE(MACH_STONE_TEXT):
|
||||
stone_mutable_text(frame->slots[a]);
|
||||
VM_BREAK();
|
||||
|
||||
/* Identity check */
|
||||
@@ -2476,6 +2416,123 @@ vm_dispatch:
|
||||
frame->slots[a] = JS_NewBool(ctx, is_proxy);
|
||||
VM_BREAK();
|
||||
}
|
||||
VM_CASE(MACH_IS_BLOB):
|
||||
frame->slots[a] = JS_NewBool(ctx, mist_is_blob(frame->slots[b]));
|
||||
VM_BREAK();
|
||||
VM_CASE(MACH_IS_DATA): {
|
||||
JSValue v = frame->slots[b];
|
||||
int result = 0;
|
||||
if (mist_is_gc_object(v) && !mist_is_array(v)
|
||||
&& !mist_is_function(v) && !mist_is_blob(v))
|
||||
result = 1;
|
||||
frame->slots[a] = JS_NewBool(ctx, result);
|
||||
VM_BREAK();
|
||||
}
|
||||
VM_CASE(MACH_IS_TRUE):
|
||||
frame->slots[a] = JS_NewBool(ctx, frame->slots[b] == JS_TRUE);
|
||||
VM_BREAK();
|
||||
VM_CASE(MACH_IS_FALSE):
|
||||
frame->slots[a] = JS_NewBool(ctx, frame->slots[b] == JS_FALSE);
|
||||
VM_BREAK();
|
||||
VM_CASE(MACH_IS_FIT): {
|
||||
JSValue v = frame->slots[b];
|
||||
int result = 0;
|
||||
if (JS_IsInt(v)) {
|
||||
result = 1;
|
||||
} else if (JS_IsShortFloat(v)) {
|
||||
double d = JS_VALUE_GET_FLOAT64(v);
|
||||
result = (isfinite(d) && trunc(d) == d && fabs(d) <= 9007199254740992.0);
|
||||
}
|
||||
frame->slots[a] = JS_NewBool(ctx, result);
|
||||
VM_BREAK();
|
||||
}
|
||||
VM_CASE(MACH_IS_CHAR): {
|
||||
JSValue v = frame->slots[b];
|
||||
int result = 0;
|
||||
if (MIST_IsImmediateASCII(v))
|
||||
result = (MIST_GetImmediateASCIILen(v) == 1);
|
||||
else if (mist_is_text(v))
|
||||
result = (js_string_value_len(v) == 1);
|
||||
frame->slots[a] = JS_NewBool(ctx, result);
|
||||
VM_BREAK();
|
||||
}
|
||||
VM_CASE(MACH_IS_DIGIT): {
|
||||
JSValue v = frame->slots[b];
|
||||
int result = 0;
|
||||
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
|
||||
int ch = MIST_GetImmediateASCIIChar(v, 0);
|
||||
result = (ch >= '0' && ch <= '9');
|
||||
} else if (mist_is_text(v) && js_string_value_len(v) == 1) {
|
||||
uint32_t ch = js_string_value_get(v, 0);
|
||||
result = (ch >= '0' && ch <= '9');
|
||||
}
|
||||
frame->slots[a] = JS_NewBool(ctx, result);
|
||||
VM_BREAK();
|
||||
}
|
||||
VM_CASE(MACH_IS_LETTER): {
|
||||
JSValue v = frame->slots[b];
|
||||
int result = 0;
|
||||
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
|
||||
int ch = MIST_GetImmediateASCIIChar(v, 0);
|
||||
result = ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'));
|
||||
} else if (mist_is_text(v) && js_string_value_len(v) == 1) {
|
||||
uint32_t ch = js_string_value_get(v, 0);
|
||||
result = ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'));
|
||||
}
|
||||
frame->slots[a] = JS_NewBool(ctx, result);
|
||||
VM_BREAK();
|
||||
}
|
||||
VM_CASE(MACH_IS_LOWER): {
|
||||
JSValue v = frame->slots[b];
|
||||
int result = 0;
|
||||
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
|
||||
int ch = MIST_GetImmediateASCIIChar(v, 0);
|
||||
result = (ch >= 'a' && ch <= 'z');
|
||||
} else if (mist_is_text(v) && js_string_value_len(v) == 1) {
|
||||
uint32_t ch = js_string_value_get(v, 0);
|
||||
result = (ch >= 'a' && ch <= 'z');
|
||||
}
|
||||
frame->slots[a] = JS_NewBool(ctx, result);
|
||||
VM_BREAK();
|
||||
}
|
||||
VM_CASE(MACH_IS_UPPER): {
|
||||
JSValue v = frame->slots[b];
|
||||
int result = 0;
|
||||
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
|
||||
int ch = MIST_GetImmediateASCIIChar(v, 0);
|
||||
result = (ch >= 'A' && ch <= 'Z');
|
||||
} else if (mist_is_text(v) && js_string_value_len(v) == 1) {
|
||||
uint32_t ch = js_string_value_get(v, 0);
|
||||
result = (ch >= 'A' && ch <= 'Z');
|
||||
}
|
||||
frame->slots[a] = JS_NewBool(ctx, result);
|
||||
VM_BREAK();
|
||||
}
|
||||
VM_CASE(MACH_IS_WS): {
|
||||
JSValue v = frame->slots[b];
|
||||
int result = 0;
|
||||
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
|
||||
int ch = MIST_GetImmediateASCIIChar(v, 0);
|
||||
result = (ch == ' ' || ch == '\t' || ch == '\n'
|
||||
|| ch == '\r' || ch == '\f' || ch == '\v');
|
||||
} else if (mist_is_text(v) && js_string_value_len(v) == 1) {
|
||||
uint32_t ch = js_string_value_get(v, 0);
|
||||
result = (ch == ' ' || ch == '\t' || ch == '\n'
|
||||
|| ch == '\r' || ch == '\f' || ch == '\v');
|
||||
}
|
||||
frame->slots[a] = JS_NewBool(ctx, result);
|
||||
VM_BREAK();
|
||||
}
|
||||
VM_CASE(MACH_IS_ACTOR): {
|
||||
JSValue v = frame->slots[b];
|
||||
int result = 0;
|
||||
if (mist_is_record(v) && !JS_IsNull(ctx->actor_sym)) {
|
||||
result = JS_HasPropertyKey(ctx, v, ctx->actor_sym) > 0;
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||
}
|
||||
frame->slots[a] = JS_NewBool(ctx, result);
|
||||
VM_BREAK();
|
||||
}
|
||||
/* Logical */
|
||||
VM_CASE(MACH_NOT): {
|
||||
int bval = JS_ToBool(ctx, frame->slots[b]);
|
||||
@@ -2622,7 +2679,15 @@ vm_dispatch:
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||
goto disrupt;
|
||||
}
|
||||
int nr = c + 2; /* argc + this + func overhead */
|
||||
JSFunction *fn = JS_VALUE_GET_FUNCTION(func_val);
|
||||
int nr;
|
||||
if (fn->kind == JS_FUNC_KIND_REGISTER) {
|
||||
JSCodeRegister *fn_code = JS_VALUE_GET_CODE(fn->u.cell.code)->u.reg.code;
|
||||
nr = fn_code->nr_slots;
|
||||
if (nr < c + 2) nr = c + 2; /* safety: never smaller than argc+2 */
|
||||
} else {
|
||||
nr = c + 2;
|
||||
}
|
||||
JSFrameRegister *call_frame = alloc_frame_register(ctx, nr);
|
||||
if (!call_frame) {
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||
@@ -2631,6 +2696,7 @@ vm_dispatch:
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||
func_val = frame->slots[b]; /* re-read after GC */
|
||||
call_frame->function = func_val;
|
||||
call_frame->address = JS_NewInt32(ctx, c); /* store actual argc */
|
||||
frame->slots[a] = JS_MKPTR(call_frame);
|
||||
VM_BREAK();
|
||||
}
|
||||
@@ -2643,36 +2709,19 @@ vm_dispatch:
|
||||
VM_CASE(MACH_INVOKE): {
|
||||
/* A=frame_slot, B=result_slot */
|
||||
JSFrameRegister *fr = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->slots[a]);
|
||||
int nr = (int)objhdr_cap56(fr->header);
|
||||
int c_argc = (nr >= 2) ? nr - 2 : 0;
|
||||
int c_argc = JS_VALUE_GET_INT(fr->address); /* actual argc stored by FRAME */
|
||||
JSValue fn_val = fr->function;
|
||||
JSFunction *fn = JS_VALUE_GET_FUNCTION(fn_val);
|
||||
if (!mach_check_call_arity(ctx, fn, c_argc))
|
||||
goto disrupt;
|
||||
|
||||
if (fn->kind == JS_FUNC_KIND_REGISTER) {
|
||||
/* Register function: switch frames inline (fast path) */
|
||||
JSCodeRegister *fn_code = JS_VALUE_GET_CODE(FN_READ_CODE(fn))->u.reg.code;
|
||||
JSFrameRegister *new_frame = alloc_frame_register(ctx, fn_code->nr_slots);
|
||||
if (!new_frame) {
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||
goto disrupt;
|
||||
}
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||
fr = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->slots[a]);
|
||||
fn_val = fr->function;
|
||||
fn = JS_VALUE_GET_FUNCTION(fn_val);
|
||||
fn_code = JS_VALUE_GET_CODE(FN_READ_CODE(fn))->u.reg.code;
|
||||
new_frame->function = fn_val;
|
||||
/* Copy this + args from call frame to new frame */
|
||||
int copy_count = (c_argc < fn_code->arity) ? c_argc : fn_code->arity;
|
||||
new_frame->slots[0] = fr->slots[0]; /* this */
|
||||
for (int i = 0; i < copy_count; i++)
|
||||
new_frame->slots[1 + i] = fr->slots[1 + i];
|
||||
/* Register function: FRAME already allocated nr_slots — just switch */
|
||||
JSCodeRegister *fn_code = JS_VALUE_GET_CODE(fn->u.cell.code)->u.reg.code;
|
||||
/* Save return info */
|
||||
frame->address = JS_NewInt32(ctx, (pc << 16) | b);
|
||||
new_frame->caller = JS_MKPTR(frame);
|
||||
frame = new_frame;
|
||||
fr->caller = JS_MKPTR(frame);
|
||||
frame = fr;
|
||||
frame_ref.val = JS_MKPTR(frame);
|
||||
code = fn_code;
|
||||
env = fn->u.cell.env_record;
|
||||
@@ -2716,8 +2765,7 @@ vm_dispatch:
|
||||
VM_CASE(MACH_GOINVOKE): {
|
||||
/* Tail call: replace current frame with callee */
|
||||
JSFrameRegister *fr = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->slots[a]);
|
||||
int nr = (int)objhdr_cap56(fr->header);
|
||||
int c_argc = (nr >= 2) ? nr - 2 : 0;
|
||||
int c_argc = JS_VALUE_GET_INT(fr->address); /* actual argc stored by FRAME */
|
||||
JSValue fn_val = fr->function;
|
||||
JSFunction *fn = JS_VALUE_GET_FUNCTION(fn_val);
|
||||
if (!mach_check_call_arity(ctx, fn, c_argc))
|
||||
@@ -2742,25 +2790,10 @@ vm_dispatch:
|
||||
env = fn->u.cell.env_record;
|
||||
pc = code->entry_point;
|
||||
} else {
|
||||
/* SLOW PATH: callee needs more slots, must allocate */
|
||||
JSFrameRegister *new_frame = alloc_frame_register(ctx, fn_code->nr_slots);
|
||||
if (!new_frame) {
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||
goto disrupt;
|
||||
}
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||
fr = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->slots[a]);
|
||||
fn_val = fr->function;
|
||||
fn = JS_VALUE_GET_FUNCTION(fn_val);
|
||||
fn_code = JS_VALUE_GET_CODE(FN_READ_CODE(fn))->u.reg.code;
|
||||
new_frame->function = fn_val;
|
||||
int copy_count = (c_argc < fn_code->arity) ? c_argc : fn_code->arity;
|
||||
new_frame->slots[0] = fr->slots[0]; /* this */
|
||||
for (int i = 0; i < copy_count; i++)
|
||||
new_frame->slots[1 + i] = fr->slots[1 + i];
|
||||
new_frame->caller = frame->caller;
|
||||
/* SLOW PATH: GOFRAME already allocated nr_slots — use fr directly */
|
||||
fr->caller = frame->caller;
|
||||
frame->caller = JS_NULL;
|
||||
frame = new_frame;
|
||||
frame = fr;
|
||||
frame_ref.val = JS_MKPTR(frame);
|
||||
code = fn_code;
|
||||
env = fn->u.cell.env_record;
|
||||
@@ -3014,10 +3047,10 @@ static MachCode *mcode_lower_func(cJSON *fobj, const char *filename) {
|
||||
if (s.nr_slots > 255) {
|
||||
cJSON *nm_chk = cJSON_GetObjectItemCaseSensitive(fobj, "name");
|
||||
const char *fn_name = nm_chk ? cJSON_GetStringValue(nm_chk) : "<anonymous>";
|
||||
fprintf(stderr, "ERROR: function '%s' has %d slots (max 255). "
|
||||
fprintf(stderr, "FATAL: function '%s' has %d slots (max 255). "
|
||||
"Ensure the streamline optimizer ran before mach compilation.\n",
|
||||
fn_name, s.nr_slots);
|
||||
return NULL;
|
||||
abort();
|
||||
}
|
||||
int dis_raw = (int)cJSON_GetNumberValue(
|
||||
cJSON_GetObjectItemCaseSensitive(fobj, "disruption_pc"));
|
||||
@@ -3084,6 +3117,7 @@ static MachCode *mcode_lower_func(cJSON *fobj, const char *filename) {
|
||||
else if (strcmp(op, "move") == 0) { AB2(MACH_MOVE); }
|
||||
/* Text */
|
||||
else if (strcmp(op, "concat") == 0) { ABC3(MACH_CONCAT); }
|
||||
else if (strcmp(op, "stone_text") == 0) { EM(MACH_ABC(MACH_STONE_TEXT, A1, 0, 0)); }
|
||||
/* Generic arithmetic */
|
||||
else if (strcmp(op, "add") == 0) { ABC3(MACH_ADD); }
|
||||
else if (strcmp(op, "subtract") == 0) { ABC3(MACH_SUB); }
|
||||
@@ -3103,30 +3137,13 @@ static MachCode *mcode_lower_func(cJSON *fobj, const char *filename) {
|
||||
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); }
|
||||
else if (strcmp(op, "lt_int") == 0) { ABC3(MACH_LT_INT); }
|
||||
else if (strcmp(op, "le_int") == 0) { ABC3(MACH_LE_INT); }
|
||||
else if (strcmp(op, "gt_int") == 0) { ABC3(MACH_GT_INT); }
|
||||
else if (strcmp(op, "ge_int") == 0) { ABC3(MACH_GE_INT); }
|
||||
/* Typed float comparisons */
|
||||
else if (strcmp(op, "eq_float") == 0) { ABC3(MACH_EQ_FLOAT); }
|
||||
else if (strcmp(op, "ne_float") == 0) { ABC3(MACH_NE_FLOAT); }
|
||||
else if (strcmp(op, "lt_float") == 0) { ABC3(MACH_LT_FLOAT); }
|
||||
else if (strcmp(op, "le_float") == 0) { ABC3(MACH_LE_FLOAT); }
|
||||
else if (strcmp(op, "gt_float") == 0) { ABC3(MACH_GT_FLOAT); }
|
||||
else if (strcmp(op, "ge_float") == 0) { ABC3(MACH_GE_FLOAT); }
|
||||
/* Typed text comparisons */
|
||||
else if (strcmp(op, "eq_text") == 0) { ABC3(MACH_EQ_TEXT); }
|
||||
else if (strcmp(op, "ne_text") == 0) { ABC3(MACH_NE_TEXT); }
|
||||
else if (strcmp(op, "lt_text") == 0) { ABC3(MACH_LT_TEXT); }
|
||||
else if (strcmp(op, "le_text") == 0) { ABC3(MACH_LE_TEXT); }
|
||||
else if (strcmp(op, "gt_text") == 0) { ABC3(MACH_GT_TEXT); }
|
||||
else if (strcmp(op, "ge_text") == 0) { ABC3(MACH_GE_TEXT); }
|
||||
/* Typed bool comparisons */
|
||||
else if (strcmp(op, "eq_bool") == 0) { ABC3(MACH_EQ_BOOL); }
|
||||
else if (strcmp(op, "ne_bool") == 0) { ABC3(MACH_NE_BOOL); }
|
||||
/* Generic comparisons */
|
||||
else if (strcmp(op, "eq") == 0) { ABC3(MACH_EQ); }
|
||||
else if (strcmp(op, "ne") == 0) { ABC3(MACH_NEQ); }
|
||||
else if (strcmp(op, "lt") == 0) { ABC3(MACH_LT); }
|
||||
else if (strcmp(op, "le") == 0) { ABC3(MACH_LE); }
|
||||
else if (strcmp(op, "gt") == 0) { ABC3(MACH_GT); }
|
||||
else if (strcmp(op, "ge") == 0) { ABC3(MACH_GE); }
|
||||
/* Special comparisons */
|
||||
else if (strcmp(op, "is_identical") == 0) { ABC3(MACH_IS_IDENTICAL); }
|
||||
else if (strcmp(op, "eq_tol") == 0) {
|
||||
@@ -3161,6 +3178,18 @@ static MachCode *mcode_lower_func(cJSON *fobj, const char *filename) {
|
||||
else if (strcmp(op, "is_stone") == 0) { AB2(MACH_IS_STONE); }
|
||||
else if (strcmp(op, "length") == 0) { AB2(MACH_LENGTH); }
|
||||
else if (strcmp(op, "is_proxy") == 0) { AB2(MACH_IS_PROXY); }
|
||||
else if (strcmp(op, "is_blob") == 0) { AB2(MACH_IS_BLOB); }
|
||||
else if (strcmp(op, "is_data") == 0) { AB2(MACH_IS_DATA); }
|
||||
else if (strcmp(op, "is_true") == 0) { AB2(MACH_IS_TRUE); }
|
||||
else if (strcmp(op, "is_false") == 0) { AB2(MACH_IS_FALSE); }
|
||||
else if (strcmp(op, "is_fit") == 0) { AB2(MACH_IS_FIT); }
|
||||
else if (strcmp(op, "is_char") == 0) { AB2(MACH_IS_CHAR); }
|
||||
else if (strcmp(op, "is_digit") == 0) { AB2(MACH_IS_DIGIT); }
|
||||
else if (strcmp(op, "is_letter") == 0) { AB2(MACH_IS_LETTER); }
|
||||
else if (strcmp(op, "is_lower") == 0) { AB2(MACH_IS_LOWER); }
|
||||
else if (strcmp(op, "is_upper") == 0) { AB2(MACH_IS_UPPER); }
|
||||
else if (strcmp(op, "is_ws") == 0) { AB2(MACH_IS_WS); }
|
||||
else if (strcmp(op, "is_actor") == 0) { AB2(MACH_IS_ACTOR); }
|
||||
/* Logical */
|
||||
else if (strcmp(op, "not") == 0) { AB2(MACH_NOT); }
|
||||
else if (strcmp(op, "and") == 0) { ABC3(MACH_AND); }
|
||||
|
||||
Reference in New Issue
Block a user