vm bytecode output
This commit is contained in:
@@ -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];
|
||||
|
||||
398
source/quickjs.c
398
source/quickjs.c
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user