vm bytecode output

This commit is contained in:
2026-02-05 10:14:14 -06:00
parent f71f6a296b
commit 8b7af0c22a
3 changed files with 470 additions and 0 deletions

View File

@@ -465,6 +465,74 @@ int cell_init(int argc, char **argv)
return mach_json ? 0 : 1;
}
/* Check for --vmcode flag to dump linked register VM bytecode */
if (argc >= 3 && strcmp(argv[1], "--vmcode") == 0) {
const char *script_or_file = argv[2];
char *script = NULL;
char *allocated_script = NULL;
const char *filename = "<eval>";
struct stat st;
if (stat(script_or_file, &st) == 0 && S_ISREG(st.st_mode)) {
FILE *f = fopen(script_or_file, "r");
if (!f) {
printf("Failed to open file: %s\n", script_or_file);
return 1;
}
allocated_script = malloc(st.st_size + 1);
if (!allocated_script) {
fclose(f);
printf("Failed to allocate memory for script\n");
return 1;
}
size_t read_size = fread(allocated_script, 1, st.st_size, f);
fclose(f);
allocated_script[read_size] = '\0';
script = allocated_script;
filename = script_or_file;
} else {
script = (char *)script_or_file;
}
JSRuntime *rt = JS_NewRuntime();
if (!rt) {
printf("Failed to create JS runtime\n");
free(allocated_script);
return 1;
}
JSContext *ctx = JS_NewContext(rt);
if (!ctx) {
printf("Failed to create JS context\n");
JS_FreeRuntime(rt);
free(allocated_script);
return 1;
}
char *ast_json = JS_AST(ctx, script, strlen(script), filename);
if (!ast_json) {
printf("Failed to parse AST\n");
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script);
return 1;
}
char *mach_json = JS_Mach(ctx, ast_json);
free(ast_json);
if (mach_json) {
JS_DumpRegisterMach(ctx, mach_json, JS_NULL);
free(mach_json);
} else {
printf("Failed to generate machine code\n");
}
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script);
return mach_json ? 0 : 1;
}
/* Check for --mach-run flag to generate and run machine code through register VM */
if (argc >= 3 && strcmp(argv[1], "--mach-run") == 0) {
const char *script_or_file = argv[2];

View File

@@ -541,6 +541,78 @@ typedef enum MachOpcode {
MACH_OP_COUNT
} MachOpcode;
static const char *mach_opcode_names[MACH_OP_COUNT] = {
[MACH_OP_NOP] = "nop",
[MACH_OP_ACCESS] = "access",
[MACH_OP_NULL] = "null",
[MACH_OP_TRUE] = "true",
[MACH_OP_FALSE] = "false",
[MACH_OP_MOVE] = "move",
[MACH_OP_ADD] = "add",
[MACH_OP_SUB] = "sub",
[MACH_OP_MUL] = "mul",
[MACH_OP_DIV] = "div",
[MACH_OP_MOD] = "mod",
[MACH_OP_POW] = "pow",
[MACH_OP_NEG] = "neg",
[MACH_OP_INC] = "inc",
[MACH_OP_DEC] = "dec",
[MACH_OP_EQ] = "eq",
[MACH_OP_NE] = "ne",
[MACH_OP_LT] = "lt",
[MACH_OP_LE] = "le",
[MACH_OP_GT] = "gt",
[MACH_OP_GE] = "ge",
[MACH_OP_STRICT_EQ] = "strict_eq",
[MACH_OP_STRICT_NE] = "strict_ne",
[MACH_OP_NOT] = "not",
[MACH_OP_BITNOT] = "bitnot",
[MACH_OP_BITAND] = "bitand",
[MACH_OP_BITOR] = "bitor",
[MACH_OP_BITXOR] = "bitxor",
[MACH_OP_SHL] = "shl",
[MACH_OP_SHR] = "shr",
[MACH_OP_USHR] = "ushr",
[MACH_OP_LOAD_PROP] = "load_prop",
[MACH_OP_STORE_PROP] = "store_prop",
[MACH_OP_LOAD_IDX] = "load_idx",
[MACH_OP_STORE_IDX] = "store_idx",
[MACH_OP_DELETE_PROP] = "delete_prop",
[MACH_OP_DELETE_IDX] = "delete_idx",
[MACH_OP_GET] = "get",
[MACH_OP_PUT] = "put",
[MACH_OP_CAP_NAME] = "cap_name",
[MACH_OP_GET_ENV_SLOT] = "get_env_slot",
[MACH_OP_SET_ENV_SLOT] = "set_env_slot",
[MACH_OP_GET_GLOBAL_SLOT] = "get_global_slot",
[MACH_OP_SET_GLOBAL_SLOT] = "set_global_slot",
[MACH_OP_SET_VAR] = "set_var",
[MACH_OP_JUMP] = "jump",
[MACH_OP_JUMP_TRUE] = "jump_true",
[MACH_OP_JUMP_FALSE] = "jump_false",
[MACH_OP_JUMP_NOT_NULL] = "jump_not_null",
[MACH_OP_FRAME] = "frame",
[MACH_OP_SET_THIS] = "set_this",
[MACH_OP_ARG] = "arg",
[MACH_OP_INVOKE] = "invoke",
[MACH_OP_GOFRAME] = "goframe",
[MACH_OP_GOINVOKE] = "goinvoke",
[MACH_OP_MKFUNC] = "mkfunc",
[MACH_OP_MKFUNC_ARROW] = "mkfunc_arrow",
[MACH_OP_MKRECORD] = "mkrecord",
[MACH_OP_MKARRAY] = "mkarray",
[MACH_OP_MKNEW] = "mknew",
[MACH_OP_RETURN] = "return",
[MACH_OP_RETURN_UNDEF] = "return_undef",
[MACH_OP_THROW] = "throw",
[MACH_OP_TRY_BEGIN] = "try_begin",
[MACH_OP_TRY_END] = "try_end",
[MACH_OP_CATCH_BEGIN] = "catch_begin",
[MACH_OP_CATCH_END] = "catch_end",
[MACH_OP_FINALLY_BEGIN] = "finally_begin",
[MACH_OP_FINALLY_END] = "finally_end",
};
/* Compiled instruction for register VM.
Each instruction has opcode + up to 3 operands.
Operands can be register indices, constant pool indices, or literal values. */
@@ -32184,6 +32256,332 @@ done:
Register VM Public API
============================================================ */
/* Print a single constant pool value for dump output */
static void dump_cpool_value(JSContext *ctx, JSValue val) {
uint32_t tag = JS_VALUE_GET_TAG(val);
if (JS_IsPtr(val)) {
void *ptr = JS_VALUE_GET_PTR(val);
objhdr_t hdr = *(objhdr_t *)ptr;
uint8_t mist_type = objhdr_type(hdr);
if (mist_type == OBJ_TEXT) {
const char *str = JS_ToCString(ctx, val);
if (str) {
printf("\"%s\"", str);
JS_FreeCString(ctx, str);
} else {
printf("<string>");
}
return;
}
printf("<ptr type=%d>", mist_type);
return;
}
switch (tag) {
case JS_TAG_INT:
printf("%d", JS_VALUE_GET_INT(val));
break;
case JS_TAG_BOOL:
printf("%s", JS_VALUE_GET_BOOL(val) ? "true" : "false");
break;
case JS_TAG_NULL:
printf("null");
break;
case JS_TAG_SHORT_FLOAT:
printf("%g", JS_VALUE_GET_FLOAT64(val));
break;
case JS_TAG_STRING_IMM: {
const char *str = JS_ToCString(ctx, val);
if (str) {
printf("\"%s\"", str);
JS_FreeCString(ctx, str);
} else {
printf("<imm string>");
}
break;
}
default:
printf("<tag=%d>", tag);
break;
}
}
/* Internal helper to dump JSCodeRegister */
static void dump_register_code(JSContext *ctx, JSCodeRegister *code, int indent) {
char pad[64];
int pad_len = indent * 2;
if (pad_len > 60) pad_len = 60;
memset(pad, ' ', pad_len);
pad[pad_len] = '\0';
/* Function header */
const char *name = "<anonymous>";
if (!JS_IsNull(code->name)) {
const char *n = JS_ToCString(ctx, code->name);
if (n) name = n;
}
printf("%sFunction: %s\n", pad, name);
printf("%s Arity: %d, Slots: %d, Close: %d\n", pad,
code->arity, code->nr_slots, code->nr_close_slots);
if (!JS_IsNull(code->name)) {
JS_FreeCString(ctx, name);
}
/* Constant pool */
if (code->cpool_count > 0) {
printf("%s\n%sConstant Pool (%d entries):\n", pad, pad, code->cpool_count);
for (uint32_t i = 0; i < code->cpool_count; i++) {
printf("%s [%d]: ", pad, i);
dump_cpool_value(ctx, code->cpool[i]);
printf("\n");
}
}
/* Instructions */
printf("%s\n%sInstructions (%d):\n", pad, pad, code->instr_count);
for (uint32_t i = 0; i < code->instr_count; i++) {
MachInstr *instr = &code->instructions[i];
uint8_t op = instr->opcode;
const char *op_name = (op < MACH_OP_COUNT) ? mach_opcode_names[op] : "???";
printf("%s %3d: %-14s ", pad, i, op_name);
switch (op) {
/* No operands */
case MACH_OP_NOP:
case MACH_OP_RETURN_UNDEF:
case MACH_OP_TRY_END:
case MACH_OP_CATCH_END:
case MACH_OP_FINALLY_BEGIN:
case MACH_OP_FINALLY_END:
break;
/* dest only */
case MACH_OP_NULL:
case MACH_OP_TRUE:
case MACH_OP_FALSE:
printf("r%d", instr->a);
break;
/* dest, cpool_idx */
case MACH_OP_ACCESS:
printf("r%d, #%d", instr->a, instr->b);
if (instr->b >= 0 && (uint32_t)instr->b < code->cpool_count) {
printf(" ; ");
dump_cpool_value(ctx, code->cpool[instr->b]);
}
break;
/* dest, src */
case MACH_OP_MOVE:
case MACH_OP_NEG:
case MACH_OP_INC:
case MACH_OP_DEC:
case MACH_OP_NOT:
case MACH_OP_BITNOT:
printf("r%d, r%d", instr->a, instr->b);
break;
/* dest, src1, src2 */
case MACH_OP_ADD:
case MACH_OP_SUB:
case MACH_OP_MUL:
case MACH_OP_DIV:
case MACH_OP_MOD:
case MACH_OP_POW:
case MACH_OP_EQ:
case MACH_OP_NE:
case MACH_OP_LT:
case MACH_OP_LE:
case MACH_OP_GT:
case MACH_OP_GE:
case MACH_OP_STRICT_EQ:
case MACH_OP_STRICT_NE:
case MACH_OP_BITAND:
case MACH_OP_BITOR:
case MACH_OP_BITXOR:
case MACH_OP_SHL:
case MACH_OP_SHR:
case MACH_OP_USHR:
printf("r%d, r%d, r%d", instr->a, instr->b, instr->c);
break;
/* dest, obj, cpool_idx (property) */
case MACH_OP_LOAD_PROP:
printf("r%d, r%d, #%d", instr->a, instr->b, instr->c);
if (instr->c >= 0 && (uint32_t)instr->c < code->cpool_count) {
printf(" ; ");
dump_cpool_value(ctx, code->cpool[instr->c]);
}
break;
/* obj, val, cpool_idx (property) */
case MACH_OP_STORE_PROP:
printf("r%d, r%d, #%d", instr->a, instr->b, instr->c);
if (instr->c >= 0 && (uint32_t)instr->c < code->cpool_count) {
printf(" ; ");
dump_cpool_value(ctx, code->cpool[instr->c]);
}
break;
/* dest, obj, idx */
case MACH_OP_LOAD_IDX:
case MACH_OP_DELETE_IDX:
printf("r%d, r%d, r%d", instr->a, instr->b, instr->c);
break;
/* obj, idx, val */
case MACH_OP_STORE_IDX:
printf("r%d, r%d, r%d", instr->a, instr->b, instr->c);
break;
/* dest, obj, cpool_idx */
case MACH_OP_DELETE_PROP:
printf("r%d, r%d, #%d", instr->a, instr->b, instr->c);
break;
/* dest, slot, depth */
case MACH_OP_GET:
printf("r%d, slot=%d, depth=%d", instr->a, instr->b, instr->c);
break;
/* src, slot, depth */
case MACH_OP_PUT:
printf("r%d, slot=%d, depth=%d", instr->a, instr->b, instr->c);
break;
/* dest, cpool_idx (name) */
case MACH_OP_CAP_NAME:
printf("r%d, #%d", instr->a, instr->b);
if (instr->b >= 0 && (uint32_t)instr->b < code->cpool_count) {
printf(" ; ");
dump_cpool_value(ctx, code->cpool[instr->b]);
}
break;
/* dest, slot_idx */
case MACH_OP_GET_ENV_SLOT:
case MACH_OP_GET_GLOBAL_SLOT:
printf("r%d, slot=%d", instr->a, instr->b);
break;
/* src, slot_idx */
case MACH_OP_SET_ENV_SLOT:
case MACH_OP_SET_GLOBAL_SLOT:
printf("r%d, slot=%d", instr->a, instr->b);
break;
/* cpool_idx, src */
case MACH_OP_SET_VAR:
printf("#%d, r%d", instr->a, instr->b);
if (instr->a >= 0 && (uint32_t)instr->a < code->cpool_count) {
printf(" ; ");
dump_cpool_value(ctx, code->cpool[instr->a]);
}
break;
/* label (resolved to instruction index) */
case MACH_OP_JUMP:
case MACH_OP_TRY_BEGIN:
printf("@%d", instr->a);
break;
/* cond, label (resolved to instruction index) */
case MACH_OP_JUMP_TRUE:
case MACH_OP_JUMP_FALSE:
case MACH_OP_JUMP_NOT_NULL:
printf("r%d, @%d", instr->a, instr->b);
break;
/* frame_reg, func_reg, argc */
case MACH_OP_FRAME:
case MACH_OP_GOFRAME:
printf("r%d, r%d, %d", instr->a, instr->b, instr->c);
break;
/* frame_reg, this_reg */
case MACH_OP_SET_THIS:
printf("r%d, r%d", instr->a, instr->b);
break;
/* frame_reg, arg_idx, val_reg */
case MACH_OP_ARG:
printf("r%d, [%d], r%d", instr->a, instr->b, instr->c);
break;
/* frame_reg, ret_reg */
case MACH_OP_INVOKE:
printf("r%d, r%d", instr->a, instr->b);
break;
/* frame_reg */
case MACH_OP_GOINVOKE:
printf("r%d", instr->a);
break;
/* dest, func_id */
case MACH_OP_MKFUNC:
case MACH_OP_MKFUNC_ARROW:
printf("r%d, func#%d", instr->a, instr->b);
break;
/* dest, argc */
case MACH_OP_MKRECORD:
case MACH_OP_MKARRAY:
printf("r%d, %d", instr->a, instr->b);
break;
/* dest, func_reg, argc */
case MACH_OP_MKNEW:
printf("r%d, r%d, %d", instr->a, instr->b, instr->c);
break;
/* src */
case MACH_OP_RETURN:
case MACH_OP_THROW:
printf("r%d", instr->a);
break;
/* dest */
case MACH_OP_CATCH_BEGIN:
printf("r%d", instr->a);
break;
default:
printf("a=%d, b=%d, c=%d", instr->a, instr->b, instr->c);
break;
}
printf("\n");
}
/* Nested functions */
if (code->func_count > 0) {
printf("%s\n%sNested Functions (%d):\n", pad, pad, code->func_count);
for (uint32_t i = 0; i < code->func_count; i++) {
printf("%s [%d]:\n", pad, i);
if (code->functions[i]) {
dump_register_code(ctx, code->functions[i], indent + 2);
} else {
printf("%s <null>\n", pad);
}
}
}
}
/* Dump linked register VM bytecode */
void JS_DumpRegisterMach(JSContext *ctx, const char *mach_json, JSValue env) {
JSCodeRegister *code = js_link_mach(ctx, mach_json, env);
if (!code) {
printf("=== Register VM Bytecode ===\nFailed to link machine code\n=== End Register VM Bytecode ===\n");
return;
}
printf("=== Register VM Bytecode ===\n");
dump_register_code(ctx, code, 0);
printf("=== End Register VM Bytecode ===\n");
}
/* Link and execute register-based machine code */
JSValue JS_IntegrateRegister(JSContext *ctx, const char *mach_json, JSValue env) {
/* Link the machine code */

View File

@@ -1232,6 +1232,10 @@ char *JS_Mach (JSContext *ctx, const char *ast_json);
Returns result of execution, or JS_EXCEPTION on error. */
JSValue JS_IntegrateRegister (JSContext *ctx, const char *mach_json, JSValue env);
/* Dump linked register VM bytecode to stdout for debugging.
Links mach_json and prints the resulting bytecode with resolved labels. */
void JS_DumpRegisterMach (JSContext *ctx, const char *mach_json, JSValue env);
/* Integrate a CellModule with an environment and execute.
Returns callable function value, or JS_EXCEPTION on error. */
JSValue cell_module_integrate (JSContext *ctx, CellModule *mod, JSValue env);