suite passes now with mcode->mach lowering

This commit is contained in:
2026-02-12 09:40:24 -06:00
parent 68fb440502
commit b771b2b5d8
16 changed files with 346 additions and 48 deletions

View File

@@ -275,6 +275,7 @@ static void print_usage(const char *prog)
printf(" --core <path> Set core path directly (overrides CELL_CORE)\n");
printf(" --shop <path> Set shop path (overrides CELL_SHOP)\n");
printf(" --emit-qbe Emit QBE IL (for native compilation)\n");
printf(" --dump-mach Dump MACH bytecode disassembly\n");
printf(" --test [heap_size] Run C test suite\n");
printf(" -h, --help Show this help message\n");
printf("\nEnvironment:\n");
@@ -307,6 +308,7 @@ int cell_init(int argc, char **argv)
/* Default: run script through bootstrap pipeline */
int emit_qbe = 0;
int dump_mach = 0;
int arg_start = 1;
const char *shop_override = NULL;
const char *core_override = NULL;
@@ -319,6 +321,9 @@ int cell_init(int argc, char **argv)
} else if (strcmp(argv[arg_start], "--emit-qbe") == 0) {
emit_qbe = 1;
arg_start++;
} else if (strcmp(argv[arg_start], "--dump-mach") == 0) {
dump_mach = 1;
arg_start++;
} else if (strcmp(argv[arg_start], "--shop") == 0) {
if (arg_start + 1 >= argc) {
printf("ERROR: --shop requires a path argument\n");
@@ -398,6 +403,7 @@ int cell_init(int argc, char **argv)
JS_SetPropertyStr(ctx, hidden_env, "shop_path",
shop_path ? JS_NewString(ctx, shop_path) : JS_NULL);
JS_SetPropertyStr(ctx, hidden_env, "emit_qbe", JS_NewBool(ctx, emit_qbe));
JS_SetPropertyStr(ctx, hidden_env, "dump_mach", JS_NewBool(ctx, dump_mach));
JS_SetPropertyStr(ctx, hidden_env, "actorsym", JS_DupValue(ctx, cli_rt->actor_sym_ref.val));
JS_SetPropertyStr(ctx, hidden_env, "json", js_json_use(ctx));
JS_SetPropertyStr(ctx, hidden_env, "nota", js_nota_use(ctx));

View File

@@ -1658,6 +1658,19 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
case MACH_IS_NULL:
frame->slots[a] = JS_NewBool(ctx, JS_IsNull(frame->slots[b]));
break;
case MACH_IS_FUNC:
frame->slots[a] = JS_NewBool(ctx, JS_IsFunction(frame->slots[b]));
break;
case MACH_IS_PROXY: {
JSValue v = frame->slots[b];
int is_proxy = 0;
if (JS_IsFunction(v)) {
JSFunction *fn = JS_VALUE_GET_FUNCTION(v);
is_proxy = (fn->length == 2);
}
frame->slots[a] = JS_NewBool(ctx, is_proxy);
break;
}
case MACH_TYPEOF: {
JSValue val = frame->slots[b];
const char *tname = "unknown";
@@ -1681,15 +1694,19 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
break;
}
case MACH_AND: {
int ba = JS_ToBool(ctx, frame->slots[b]);
int bb = JS_ToBool(ctx, frame->slots[c]);
frame->slots[a] = JS_NewBool(ctx, ba && bb);
JSValue left = frame->slots[b];
if (!JS_ToBool(ctx, left))
frame->slots[a] = left;
else
frame->slots[a] = frame->slots[c];
break;
}
case MACH_OR: {
int ba = JS_ToBool(ctx, frame->slots[b]);
int bb = JS_ToBool(ctx, frame->slots[c]);
frame->slots[a] = JS_NewBool(ctx, ba || bb);
JSValue left = frame->slots[b];
if (JS_ToBool(ctx, left))
frame->slots[a] = left;
else
frame->slots[a] = frame->slots[c];
break;
}
@@ -1717,12 +1734,9 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
JSValue obj = frame->slots[b];
JSValue key = code->cpool[c];
if (JS_IsFunction(obj)) {
JSFunction *fn_chk = JS_VALUE_GET_FUNCTION(obj);
if (fn_chk->length != 2) {
JS_ThrowTypeError(ctx, "cannot read property of non-proxy function");
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
goto disrupt;
}
JS_ThrowTypeError(ctx, "cannot read property of function");
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
goto disrupt;
}
JSValue val = JS_GetProperty(ctx, obj, key);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
@@ -1790,6 +1804,9 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
} else if (JS_IsArray(obj)) {
JS_ThrowTypeError(ctx, "array index must be a number");
ret = -1;
} else if (JS_IsBool(key) || JS_IsNull(key) || JS_IsArray(key) || JS_IsFunction(key)) {
JS_ThrowTypeError(ctx, "object key must be text");
ret = -1;
} else {
ret = JS_SetProperty(ctx, obj, key, val);
}
@@ -2553,6 +2570,8 @@ static MachCode *mcode_lower_func(cJSON *fobj, const char *filename) {
else if (strcmp(op, "is_text") == 0) { AB2(MACH_IS_TEXT); }
else if (strcmp(op, "is_bool") == 0) { AB2(MACH_IS_BOOL); }
else if (strcmp(op, "is_null") == 0) { AB2(MACH_IS_NULL); }
else if (strcmp(op, "is_func") == 0) { AB2(MACH_IS_FUNC); }
else if (strcmp(op, "is_proxy") == 0) { AB2(MACH_IS_PROXY); }
else if (strcmp(op, "typeof") == 0) { AB2(MACH_TYPEOF); }
/* Logical */
else if (strcmp(op, "not") == 0) { AB2(MACH_NOT); }
@@ -2571,7 +2590,15 @@ static MachCode *mcode_lower_func(cJSON *fobj, const char *filename) {
int dest = A1, obj = A2;
cJSON *key_item = cJSON_GetArrayItem(it, 3);
if (cJSON_IsString(key_item)) {
EM(MACH_ABC(MACH_LOAD_FIELD, dest, obj, ml_cpool_str(&s, key_item->valuestring)));
int ki = ml_cpool_str(&s, key_item->valuestring);
if (ki <= 255) {
EM(MACH_ABC(MACH_LOAD_FIELD, dest, obj, ki));
} else {
/* cpool index > 255: load key via LOADK, then use dynamic access */
int tmp = s.nr_slots++;
EM(MACH_ABx(MACH_LOADK, tmp, ki));
EM(MACH_ABC(MACH_LOAD_DYNAMIC, dest, obj, tmp));
}
} else {
/* key is a register — fall back to dynamic access */
int key_reg = (int)key_item->valuedouble;
@@ -2582,7 +2609,15 @@ static MachCode *mcode_lower_func(cJSON *fobj, const char *filename) {
int obj = A1, val = A2;
cJSON *key_item = cJSON_GetArrayItem(it, 3);
if (cJSON_IsString(key_item)) {
EM(MACH_ABC(MACH_STORE_FIELD, obj, ml_cpool_str(&s, key_item->valuestring), val));
int ki = ml_cpool_str(&s, key_item->valuestring);
if (ki <= 255) {
EM(MACH_ABC(MACH_STORE_FIELD, obj, ki, val));
} else {
/* cpool index > 255: load key via LOADK, then use dynamic access */
int tmp = s.nr_slots++;
EM(MACH_ABx(MACH_LOADK, tmp, ki));
EM(MACH_ABC(MACH_STORE_DYNAMIC, obj, tmp, val));
}
} else {
/* key is a register — fall back to dynamic access */
int key_reg = (int)key_item->valuedouble;
@@ -2601,7 +2636,21 @@ static MachCode *mcode_lower_func(cJSON *fobj, const char *filename) {
}
/* Delete */
else if (strcmp(op, "delete") == 0) {
ABC3(MACH_DELETEINDEX);
int dest = A1, obj = A2;
cJSON *key_item = cJSON_GetArrayItem(it, 3);
if (cJSON_IsString(key_item)) {
int ki = ml_cpool_str(&s, key_item->valuestring);
if (ki <= 255) {
EM(MACH_ABC(MACH_DELETE, dest, obj, ki));
} else {
int tmp = s.nr_slots++;
EM(MACH_ABx(MACH_LOADK, tmp, ki));
EM(MACH_ABC(MACH_DELETEINDEX, dest, obj, tmp));
}
} else {
int key_reg = (int)key_item->valuedouble;
EM(MACH_ABC(MACH_DELETEINDEX, dest, obj, key_reg));
}
}
/* Array/Object creation */
else if (strcmp(op, "array") == 0) {
@@ -3271,3 +3320,21 @@ JSValue JS_RunMachBin(JSContext *ctx, const uint8_t *data, size_t size, JSValue
return result;
}
void JS_DumpMachBin(JSContext *ctx, const uint8_t *data, size_t size, JSValue env) {
MachCode *mc = JS_DeserializeMachCode(data, size);
if (!mc) {
printf("Failed to deserialize MACH bytecode\n");
return;
}
JSGCRef env_ref;
JS_PushGCRef(ctx, &env_ref);
env_ref.val = env;
JSCodeRegister *code = JS_LoadMachCode(ctx, mc, env_ref.val);
JS_FreeMachCode(mc);
dump_register_code(ctx, code, 0);
JS_PopGCRef(ctx, &env_ref);
}

View File

@@ -595,6 +595,8 @@ typedef enum MachOpcode {
/* Misc */
MACH_IN, /* R(A) = (R(B) in R(C)) — has property (ABC) */
MACH_IS_FUNC, /* R(A) = is_function(R(B)) */
MACH_IS_PROXY, /* R(A) = is_function(R(B)) && R(B).length == 2 */
MACH_OP_COUNT
} MachOpcode;
@@ -724,6 +726,8 @@ static const char *mach_opcode_names[MACH_OP_COUNT] = {
[MACH_DISRUPT] = "disrupt",
[MACH_SET_VAR] = "set_var",
[MACH_IN] = "in",
[MACH_IS_FUNC] = "is_func",
[MACH_IS_PROXY] = "is_proxy",
};
/* Compiled register-based code (off-heap, never GC'd).

View File

@@ -995,6 +995,9 @@ struct JSCodeRegister *JS_LoadMachCode(JSContext *ctx, MachCode *mc, JSValue env
/* Deserialize and execute pre-compiled MACH binary bytecode. */
JSValue JS_RunMachBin(JSContext *ctx, const uint8_t *data, size_t size, JSValue env);
/* Dump disassembly of pre-compiled MACH binary bytecode. */
void JS_DumpMachBin(JSContext *ctx, const uint8_t *data, size_t size, JSValue env);
/* Compile mcode JSON IR to MachCode binary. */
MachCode *mach_compile_mcode(struct cJSON *mcode_json);

View File

@@ -9788,6 +9788,57 @@ static JSValue js_mach_eval_mcode (JSContext *ctx, JSValue this_val, int argc, J
return result;
}
/* mach_dump_mcode(name, mcode_json, env?) - compile mcode IR and dump bytecode disassembly */
static JSValue js_mach_dump_mcode (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
if (argc < 2 || !JS_IsText (argv[0]) || !JS_IsText (argv[1]))
return JS_ThrowTypeError (ctx, "mach_dump_mcode requires (name, mcode_json) text arguments");
const char *name = JS_ToCString (ctx, argv[0]);
if (!name) return JS_EXCEPTION;
const char *json_str = JS_ToCString (ctx, argv[1]);
if (!json_str) {
JS_FreeCString (ctx, name);
return JS_EXCEPTION;
}
cJSON *mcode = cJSON_Parse (json_str);
JS_FreeCString (ctx, json_str);
if (!mcode) {
JS_FreeCString (ctx, name);
return JS_ThrowSyntaxError (ctx, "mach_dump_mcode: failed to parse mcode JSON");
}
if (!cJSON_GetObjectItemCaseSensitive (mcode, "filename"))
cJSON_AddStringToObject (mcode, "filename", name);
MachCode *mc = mach_compile_mcode (mcode);
cJSON_Delete (mcode);
if (!mc) {
JS_FreeCString (ctx, name);
return JS_ThrowInternalError (ctx, "mach_dump_mcode: compilation failed");
}
JSValue env = (argc >= 3 && JS_IsGCObject (argv[2])) ? argv[2] : JS_NULL;
JSGCRef env_ref;
JS_PushGCRef (ctx, &env_ref);
env_ref.val = env;
/* Serialize to binary then dump */
size_t bin_size;
uint8_t *bin = JS_SerializeMachCode (mc, &bin_size);
JS_FreeMachCode (mc);
JS_DumpMachBin (ctx, bin, bin_size, env_ref.val);
sys_free (bin);
JS_PopGCRef (ctx, &env_ref);
JS_FreeCString (ctx, name);
return JS_NULL;
}
/* mach_compile_mcode_bin(name, mcode_json) - compile mcode IR to serialized binary blob */
static JSValue js_mach_compile_mcode_bin (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
if (argc < 2 || !JS_IsText (argv[0]) || !JS_IsText (argv[1]))
@@ -10920,6 +10971,7 @@ static void JS_AddIntrinsicBaseObjects (JSContext *ctx) {
/* Core functions - using GC-safe helper */
js_set_global_cfunc(ctx, "mach_load", js_mach_load, 2);
js_set_global_cfunc(ctx, "mach_eval_mcode", js_mach_eval_mcode, 3);
js_set_global_cfunc(ctx, "mach_dump_mcode", js_mach_dump_mcode, 3);
js_set_global_cfunc(ctx, "mach_compile_mcode_bin", js_mach_compile_mcode_bin, 2);
js_set_global_cfunc(ctx, "stone", js_cell_stone, 1);
js_set_global_cfunc(ctx, "length", js_cell_length, 1);