This commit is contained in:
2026-02-11 14:41:37 -06:00
parent c1910ee1db
commit 8a84be65e1
9 changed files with 1325 additions and 43 deletions

20
dump_mcode.cm Normal file
View File

@@ -0,0 +1,20 @@
var fd = use("fd")
var json = use("json")
var tokenize = use("tokenize")
var parse = use("parse")
var fold = use("fold")
var mcode = use("mcode")
var streamline = use("streamline")
var name = args[0]
var src = text(fd.slurp(name))
var tok = tokenize(src, name)
var ast = parse(tok.tokens, src, name, tokenize)
var folded = fold(ast)
var compiled = mcode(folded)
var optimized = streamline(compiled)
var out = json.encode(optimized)
var f = fd.open("/tmp/mcode_dump.json", "w")
fd.write(f, out)
fd.close(f)
print("wrote /tmp/mcode_dump.json")

View File

@@ -1,5 +1,5 @@
// Hidden vars come from env:
// CLI mode (cell_init): os, args, core_path, shop_path, use_mcode
// CLI mode (cell_init): os, args, core_path, shop_path, emit_qbe
// Actor spawn (script_startup): os, json, nota, wota, actorsym, init, core_path, shop_path
// args[0] = script name, args[1..] = user args
var load_internal = os.load_internal
@@ -45,14 +45,11 @@ use_cache['tokenize'] = tokenize_mod
use_cache['parse'] = parse_mod
use_cache['fold'] = fold_mod
// Optionally load mcode compiler module
var mcode_mod = null
// Always load mcode compiler module
var mcode_mod = boot_load("mcode", boot_env)
use_cache['mcode'] = mcode_mod
var streamline_mod = null
var qbe_emit_mod = null
if (use_mcode) {
mcode_mod = boot_load("mcode", boot_env)
use_cache['mcode'] = mcode_mod
}
// Warn if any .cm source is newer than its .mach bytecode
function check_mach_stale() {
@@ -146,33 +143,26 @@ function load_module(name, env) {
// Load optimization pipeline modules (needs analyze to be defined)
var qbe_macros = null
if (use_mcode) {
streamline_mod = load_module("streamline", boot_env)
use_cache['streamline'] = streamline_mod
if (emit_qbe) {
qbe_macros = load_module("qbe", boot_env)
qbe_emit_mod = load_module("qbe_emit", boot_env)
use_cache['qbe'] = qbe_macros
use_cache['qbe_emit'] = qbe_emit_mod
}
streamline_mod = load_module("streamline", boot_env)
use_cache['streamline'] = streamline_mod
if (emit_qbe) {
qbe_macros = load_module("qbe", boot_env)
qbe_emit_mod = load_module("qbe_emit", boot_env)
use_cache['qbe'] = qbe_macros
use_cache['qbe_emit'] = qbe_emit_mod
}
// Run AST through either mcode or mach pipeline
// Run AST through mcode pipeline → register VM
function run_ast(name, ast, env) {
var compiled = null
var optimized = null
var compiled = mcode_mod(ast)
var optimized = streamline_mod(compiled)
var qbe_il = null
if (use_mcode) {
compiled = mcode_mod(ast)
optimized = streamline_mod(compiled)
if (emit_qbe) {
qbe_il = qbe_emit_mod(optimized, qbe_macros)
print(qbe_il)
return null
}
return mcode_run(name, json.encode(optimized), env)
if (emit_qbe) {
qbe_il = qbe_emit_mod(optimized, qbe_macros)
print(qbe_il)
return null
}
return mach_eval_ast(name, json.encode(ast), env)
return mach_eval_mcode(name, json.encode(optimized), env)
}
// use() with ƿit pipeline for .cm modules

Binary file not shown.

View File

@@ -211,9 +211,9 @@ void script_startup(cell_rt *prt)
JS_SetPropertyStr(js, hidden_env, "init", JS_NULL);
}
// Set args and use_mcode to null/false for actor spawn (not CLI mode)
// Set args to null for actor spawn (not CLI mode)
JS_SetPropertyStr(js, hidden_env, "args", JS_NULL);
JS_SetPropertyStr(js, hidden_env, "use_mcode", JS_NewBool(js, 0));
/* use_mcode no longer needed — new bootstrap always uses mcode pipeline */
if (core_path)
JS_SetPropertyStr(js, hidden_env, "core_path", JS_NewString(js, core_path));
@@ -290,7 +290,7 @@ static void print_usage(const char *prog)
printf("Options:\n");
printf(" --core <path> Set core path directly (overrides CELL_CORE)\n");
printf(" --shop <path> Set shop path (overrides CELL_SHOP)\n");
printf(" --mcode <script> [args] Run through mcode compilation pipeline\n");
printf(" --emit-qbe Emit QBE IL (for native compilation)\n");
printf(" --test [heap_size] Run C test suite\n");
printf(" -h, --help Show this help message\n");
printf("\nEnvironment:\n");
@@ -322,7 +322,6 @@ int cell_init(int argc, char **argv)
}
/* Default: run script through bootstrap pipeline */
int use_mcode = 0;
int emit_qbe = 0;
int arg_start = 1;
const char *shop_override = NULL;
@@ -331,10 +330,9 @@ int cell_init(int argc, char **argv)
// Parse flags (order-independent)
while (arg_start < argc && argv[arg_start][0] == '-') {
if (strcmp(argv[arg_start], "--mcode") == 0) {
use_mcode = 1;
/* --mcode is now always on; accept and ignore for compat */
arg_start++;
} else if (strcmp(argv[arg_start], "--emit-qbe") == 0) {
use_mcode = 1; // QBE requires mcode pipeline
emit_qbe = 1;
arg_start++;
} else if (strcmp(argv[arg_start], "--shop") == 0) {
@@ -420,7 +418,7 @@ int cell_init(int argc, char **argv)
JS_SetPropertyStr(ctx, hidden_env, "core_path", JS_NewString(ctx, core_path));
JS_SetPropertyStr(ctx, hidden_env, "shop_path",
shop_path ? JS_NewString(ctx, shop_path) : JS_NULL);
JS_SetPropertyStr(ctx, hidden_env, "use_mcode", JS_NewBool(ctx, use_mcode));
/* use_mcode no longer needed — new bootstrap always uses mcode pipeline */
JS_SetPropertyStr(ctx, hidden_env, "emit_qbe", JS_NewBool(ctx, emit_qbe));
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));

File diff suppressed because it is too large Load Diff

View File

@@ -464,6 +464,8 @@ typedef struct { uint16_t line; uint16_t col; } MachLineEntry;
#define MACH_GET_sJ(i) ((int32_t)((i) & 0xFFFFFF00) >> 8)
typedef enum MachOpcode {
/* === Legacy opcodes (used by existing .mach files) === */
/* Constants & Loading */
MACH_LOADK, /* R(A) = K(Bx) — load from constant pool (ABx) */
MACH_LOADI, /* R(A) = (int16_t)sBx — load small integer (AsBx) */
@@ -474,7 +476,7 @@ typedef enum MachOpcode {
/* Movement */
MACH_MOVE, /* R(A) = R(B) */
/* Arithmetic (ABC) */
/* Generic arithmetic (ABC) — used by legacy .mach */
MACH_ADD, /* R(A) = R(B) + R(C) */
MACH_SUB, /* R(A) = R(B) - R(C) */
MACH_MUL, /* R(A) = R(B) * R(C) */
@@ -485,7 +487,7 @@ typedef enum MachOpcode {
MACH_INC, /* R(A) = R(B) + 1 */
MACH_DEC, /* R(A) = R(B) - 1 */
/* Comparison (ABC) */
/* Generic comparison (ABC) — used by legacy .mach */
MACH_EQ, /* R(A) = (R(B) == R(C)) */
MACH_NEQ, /* R(A) = (R(B) != R(C)) */
MACH_LT, /* R(A) = (R(B) < R(C)) */
@@ -493,7 +495,7 @@ typedef enum MachOpcode {
MACH_GT, /* R(A) = (R(B) > R(C)) */
MACH_GE, /* R(A) = (R(B) >= R(C)) */
/* Logical/Bitwise */
/* Logical/Bitwise — used by legacy .mach */
MACH_LNOT, /* R(A) = !R(B) */
MACH_BNOT, /* R(A) = ~R(B) */
MACH_BAND, /* R(A) = R(B) & R(C) */
@@ -503,7 +505,7 @@ typedef enum MachOpcode {
MACH_SHR, /* R(A) = R(B) >> R(C) */
MACH_USHR, /* R(A) = R(B) >>> R(C) */
/* Property access */
/* Property access — used by legacy .mach */
MACH_GETFIELD, /* R(A) = R(B)[K(C)] — named property */
MACH_SETFIELD, /* R(A)[K(B)] = R(C) — named property */
MACH_GETINDEX, /* R(A) = R(B)[R(C)] — computed property */
@@ -524,12 +526,12 @@ typedef enum MachOpcode {
MACH_JMPFALSE, /* if !R(A): pc += sBx — (iAsBx format) */
MACH_JMPNULL, /* if R(A)==null: pc += sBx */
/* Function calls — Lua-style consecutive registers */
/* Function calls — Lua-style consecutive registers (legacy .mach) */
MACH_CALL, /* Call R(A) with B args R(A+1)..R(A+B), C=0 discard, C=1 keep result in R(A) */
MACH_RETURN, /* Return R(A) */
MACH_RETNIL, /* Return null */
/* Object/array creation */
/* Object/array creation — legacy .mach */
MACH_NEWOBJECT, /* R(A) = {} */
MACH_NEWARRAY, /* R(A) = new array, B = element count in R(A+1)..R(A+B) */
MACH_CLOSURE, /* R(A) = closure(functions[Bx]) (ABx) */
@@ -551,10 +553,112 @@ typedef enum MachOpcode {
MACH_NOP,
/* === New mcode-derived opcodes (1:1 mapping to mcode IR) === */
/* Typed integer arithmetic (ABC) */
MACH_ADD_INT, /* R(A) = R(B) + R(C) — int, overflow → float */
MACH_SUB_INT, /* R(A) = R(B) - R(C) — int */
MACH_MUL_INT, /* R(A) = R(B) * R(C) — int */
MACH_DIV_INT, /* R(A) = R(B) / R(C) — int */
MACH_MOD_INT, /* R(A) = R(B) % R(C) — int */
MACH_NEG_INT, /* R(A) = -R(B) — int (AB) */
/* Typed float arithmetic (ABC) */
MACH_ADD_FLOAT, /* R(A) = R(B) + R(C) — float */
MACH_SUB_FLOAT, /* R(A) = R(B) - R(C) — float */
MACH_MUL_FLOAT, /* R(A) = R(B) * R(C) — float */
MACH_DIV_FLOAT, /* R(A) = R(B) / R(C) — float */
MACH_MOD_FLOAT, /* R(A) = R(B) % R(C) — float */
MACH_NEG_FLOAT, /* R(A) = -R(B) — float (AB) */
/* 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 */
/* Special comparisons */
MACH_IS_IDENTICAL, /* R(A) = (R(B) === R(C)) — identity check (ABC) */
/* Type checks (AB) */
MACH_IS_INT, /* R(A) = is_int(R(B)) */
MACH_IS_NUM, /* R(A) = is_num(R(B)) */
MACH_IS_TEXT, /* R(A) = is_text(R(B)) */
MACH_IS_BOOL, /* R(A) = is_bool(R(B)) */
MACH_IS_NULL, /* R(A) = is_null(R(B)) */
MACH_TYPEOF, /* R(A) = typeof(R(B)) */
/* Logical (mcode-style) */
MACH_NOT, /* R(A) = !R(B) — boolean not (AB) */
MACH_AND, /* R(A) = R(B) && R(C) (ABC) */
MACH_OR, /* R(A) = R(B) || R(C) (ABC) */
/* Bitwise (mcode names) */
MACH_BITNOT, /* R(A) = ~R(B) (AB) */
MACH_BITAND, /* R(A) = R(B) & R(C) (ABC) */
MACH_BITOR, /* R(A) = R(B) | R(C) (ABC) */
MACH_BITXOR, /* R(A) = R(B) ^ R(C) (ABC) */
/* Property access (mcode names) */
MACH_LOAD_FIELD, /* R(A) = R(B).K(C) — named property (ABC) */
MACH_STORE_FIELD, /* R(A).K(B) = R(C) — named property (ABC) */
MACH_LOAD_INDEX, /* R(A) = R(B)[R(C)] — integer index (ABC) */
MACH_STORE_INDEX, /* R(A)[R(B)] = R(C) — integer index (ABC) */
MACH_LOAD_DYNAMIC, /* R(A) = R(B)[R(C)] — dynamic key (ABC) */
MACH_STORE_DYNAMIC, /* R(A)[R(B)] = R(C) — dynamic key (ABC) */
/* Object/Array creation (mcode names) */
MACH_NEWRECORD, /* R(A) = {} — new empty record (A only) */
/* Decomposed function calls (mcode-style) */
MACH_FRAME, /* R(A) = frame(R(B), C) — alloc call frame (ABC) */
MACH_SETARG, /* frame R(A)[B] = R(C) — set arg in frame (ABC) */
MACH_INVOKE, /* R(B) = invoke(R(A)) — call frame, result in R(B) (AB) */
MACH_GOFRAME, /* R(A) = goframe(R(B), C) — async frame (ABC) */
MACH_GOINVOKE, /* goinvoke(R(A)) — async invoke, no result (A only) */
/* Control flow */
MACH_JMPNOTNULL, /* if R(A)!=null: pc += sBx (iAsBx) */
/* Error handling */
MACH_DISRUPT, /* trigger disruption (A only) */
/* Variable storage */
MACH_SET_VAR, /* env/global[K(Bx)] = R(A) — store to var (ABx) */
/* Misc */
MACH_IN, /* R(A) = (R(B) in R(C)) — has property (ABC) */
MACH_OP_COUNT
} MachOpcode;
static const char *mach_opcode_names[MACH_OP_COUNT] = {
/* Legacy */
[MACH_LOADK] = "loadk",
[MACH_LOADI] = "loadi",
[MACH_LOADNULL] = "loadnull",
@@ -614,6 +718,70 @@ static const char *mach_opcode_names[MACH_OP_COUNT] = {
[MACH_EQ_TOL] = "eq_tol",
[MACH_NEQ_TOL] = "neq_tol",
[MACH_NOP] = "nop",
/* Mcode-derived */
[MACH_ADD_INT] = "add_int",
[MACH_SUB_INT] = "sub_int",
[MACH_MUL_INT] = "mul_int",
[MACH_DIV_INT] = "div_int",
[MACH_MOD_INT] = "mod_int",
[MACH_NEG_INT] = "neg_int",
[MACH_ADD_FLOAT] = "add_float",
[MACH_SUB_FLOAT] = "sub_float",
[MACH_MUL_FLOAT] = "mul_float",
[MACH_DIV_FLOAT] = "div_float",
[MACH_MOD_FLOAT] = "mod_float",
[MACH_NEG_FLOAT] = "neg_float",
[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_IS_IDENTICAL] = "is_identical",
[MACH_IS_INT] = "is_int",
[MACH_IS_NUM] = "is_num",
[MACH_IS_TEXT] = "is_text",
[MACH_IS_BOOL] = "is_bool",
[MACH_IS_NULL] = "is_null",
[MACH_TYPEOF] = "typeof",
[MACH_NOT] = "not",
[MACH_AND] = "and",
[MACH_OR] = "or",
[MACH_BITNOT] = "bitnot",
[MACH_BITAND] = "bitand",
[MACH_BITOR] = "bitor",
[MACH_BITXOR] = "bitxor",
[MACH_LOAD_FIELD] = "load_field",
[MACH_STORE_FIELD] = "store_field",
[MACH_LOAD_INDEX] = "load_index",
[MACH_STORE_INDEX] = "store_index",
[MACH_LOAD_DYNAMIC] = "load_dynamic",
[MACH_STORE_DYNAMIC] = "store_dynamic",
[MACH_NEWRECORD] = "newrecord",
[MACH_FRAME] = "frame",
[MACH_SETARG] = "setarg",
[MACH_INVOKE] = "invoke",
[MACH_GOFRAME] = "goframe",
[MACH_GOINVOKE] = "goinvoke",
[MACH_JMPNOTNULL] = "jmpnotnull",
[MACH_DISRUPT] = "disrupt",
[MACH_SET_VAR] = "set_var",
[MACH_IN] = "in",
};
/* Compiled register-based code (off-heap, never GC'd).

View File

@@ -1106,6 +1106,9 @@ JSValue JS_RunMach (JSContext *ctx, const char *ast_json, JSValue env);
/* Deserialize and execute pre-compiled MACH binary bytecode. */
JSValue JS_RunMachBin(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);
/* Execute MCODE from cJSON tree. Takes ownership of root. */
JSValue JS_CallMcodeTree (JSContext *ctx, struct cJSON *root);

View File

@@ -10249,6 +10249,55 @@ static JSValue js_mcode_run (JSContext *ctx, JSValue this_val, int argc, JSValue
return result;
}
/* mach_eval_mcode(name, mcode_json, env?) - compile mcode IR and run via register VM */
static JSValue js_mach_eval_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_eval_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_eval_mcode: failed to parse mcode JSON");
}
/* Set filename on the mcode root if not present */
if (!cJSON_GetObjectItemCaseSensitive (mcode, "filename"))
cJSON_AddStringToObject (mcode, "filename", name);
/* Compile mcode IR → MachCode binary */
MachCode *mc = mach_compile_mcode (mcode);
cJSON_Delete (mcode);
if (!mc) {
JS_FreeCString (ctx, name);
return JS_ThrowInternalError (ctx, "mach_eval_mcode: compilation failed");
}
JSValue env = (argc >= 3 && JS_IsObject (argv[2])) ? argv[2] : JS_NULL;
JSGCRef env_ref;
JS_PushGCRef (ctx, &env_ref);
env_ref.val = env;
JSCodeRegister *code = JS_LoadMachCode (ctx, mc, env_ref.val);
JS_FreeMachCode (mc);
JSValue result = JS_CallRegisterVM (ctx, code, ctx->global_obj, 0, NULL, env_ref.val, JS_NULL);
JS_PopGCRef (ctx, &env_ref);
JS_FreeCString (ctx, name);
return result;
}
/* ============================================================================
* stone() function - deep freeze with blob support
* ============================================================================
@@ -11353,6 +11402,7 @@ static void JS_AddIntrinsicBaseObjects (JSContext *ctx) {
js_set_global_cfunc(ctx, "mcode_run", js_mcode_run, 3);
js_set_global_cfunc(ctx, "mach_compile_ast", js_mach_compile_ast, 2);
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, "stone", js_cell_stone, 1);
js_set_global_cfunc(ctx, "length", js_cell_length, 1);
js_set_global_cfunc(ctx, "call", js_cell_call, 3);

42
test_parse_boot.cm Normal file
View File

@@ -0,0 +1,42 @@
var fd = use("fd")
var json = use("json")
var tokenize = use("tokenize")
var parse = use("parse")
var fold = use("fold")
var src = text(fd.slurp("internal/bootstrap.cm"))
var tok_result = tokenize(src, "bootstrap.cm")
var ast = parse(tok_result.tokens, src, "bootstrap.cm", tokenize)
var i = 0
var folded = null
var ast_json = null
var f = null
var bytecode = null
var has_errors = ast.errors != null && length(ast.errors) > 0
if (has_errors) {
print("PARSE ERRORS:")
while (i < length(ast.errors)) {
print(text(ast.errors[i].line) + ":" + text(ast.errors[i].column) + " " + ast.errors[i].message)
i = i + 1
}
} else {
print("Parse OK")
print(" statements: " + text(length(ast.statements)))
if (ast.functions != null) {
print(" functions: " + text(length(ast.functions)))
} else {
print(" functions: null")
}
folded = fold(ast)
ast_json = json.encode(folded)
f = fd.open("/tmp/bootstrap_ast.json", "w")
fd.write(f, ast_json)
fd.close(f)
print("Wrote AST to /tmp/bootstrap_ast.json")
bytecode = mach_compile_ast("bootstrap", ast_json)
print("Bytecode size: " + text(length(bytecode)))
f = fd.open("/tmp/bootstrap_test.mach", "w")
fd.write(f, bytecode)
fd.close(f)
print("Wrote bytecode to /tmp/bootstrap_test.mach")
}