faster mach bytecode generation

This commit is contained in:
2026-02-07 15:49:09 -06:00
parent 1a925371d3
commit 13a6f6c79d
5 changed files with 105 additions and 69 deletions

View File

@@ -58,7 +58,7 @@ static:
# Bootstrap: build cell from scratch using meson (only needed once)
# Also installs core scripts to ~/.cell/core
bootstrap:
meson setup build_bootstrap -Dbuildtype=debug -Db_sanitize=address
meson setup build_bootstrap -Dbuildtype=debugoptimized
meson compile -C build_bootstrap
cp build_bootstrap/cell .
cp build_bootstrap/libcell_runtime.dylib .

View File

@@ -105,15 +105,11 @@ static char* load_core_file(const char *filename, size_t *out_size) {
return data;
}
static int print_json_errors(const char *json) {
if (!json) return 0;
cJSON *root = cJSON_Parse(json);
static int print_tree_errors(cJSON *root) {
if (!root) return 0;
cJSON *errors = cJSON_GetObjectItemCaseSensitive(root, "errors");
if (!cJSON_IsArray(errors) || cJSON_GetArraySize(errors) == 0) {
cJSON_Delete(root);
if (!cJSON_IsArray(errors) || cJSON_GetArraySize(errors) == 0)
return 0;
}
const char *filename = "<unknown>";
cJSON *fname = cJSON_GetObjectItemCaseSensitive(root, "filename");
if (cJSON_IsString(fname))
@@ -130,10 +126,18 @@ static int print_json_errors(const char *json) {
else if (msg)
fprintf(stderr, "%s: error: %s\n", filename, msg);
}
cJSON_Delete(root);
return 1;
}
static int print_json_errors(const char *json) {
if (!json) return 0;
cJSON *root = cJSON_Parse(json);
if (!root) return 0;
int result = print_tree_errors(root);
cJSON_Delete(root);
return result;
}
// Get the core path for use by scripts
const char* cell_get_core_path(void) {
return core_path;
@@ -399,17 +403,13 @@ int cell_init(int argc, char **argv)
script = (char *)script_or_file;
}
char *json = JS_AST(script, strlen(script), filename);
if (json) {
int has_errors = print_json_errors(json);
cJSON *root = cJSON_Parse(json);
free(json);
if (root) {
char *pretty = cJSON_Print(root);
cJSON_Delete(root);
printf("%s\n", pretty);
free(pretty);
}
cJSON *ast = JS_ASTTree(script, strlen(script), filename);
if (ast) {
int has_errors = print_tree_errors(ast);
char *pretty = cJSON_Print(ast);
cJSON_Delete(ast);
printf("%s\n", pretty);
free(pretty);
free(allocated_script);
return has_errors ? 1 : 0;
} else {
@@ -647,15 +647,15 @@ int cell_init(int argc, char **argv)
script = (char *)script_or_file;
}
char *ast_json = JS_AST(script, strlen(script), filename);
if (!ast_json) {
cJSON *ast = JS_ASTTree(script, strlen(script), filename);
if (!ast) {
printf("Failed to parse AST\n");
free(allocated_script);
return 1;
}
if (print_json_errors(ast_json)) {
free(ast_json);
if (print_tree_errors(ast)) {
cJSON_Delete(ast);
free(allocated_script);
return 1;
}
@@ -663,18 +663,18 @@ int cell_init(int argc, char **argv)
JSRuntime *rt = JS_NewRuntime();
if (!rt) {
printf("Failed to create JS runtime\n");
free(ast_json); free(allocated_script);
cJSON_Delete(ast); free(allocated_script);
return 1;
}
JSContext *ctx = JS_NewContext(rt);
if (!ctx) {
printf("Failed to create JS context\n");
free(ast_json); JS_FreeRuntime(rt); free(allocated_script);
cJSON_Delete(ast); JS_FreeRuntime(rt); free(allocated_script);
return 1;
}
JS_DumpMach(ctx, ast_json, JS_NULL);
free(ast_json);
JS_DumpMachTree(ctx, ast, JS_NULL);
cJSON_Delete(ast);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
@@ -711,15 +711,15 @@ int cell_init(int argc, char **argv)
script = (char *)script_or_file;
}
char *ast_json = JS_AST(script, strlen(script), filename);
if (!ast_json) {
cJSON *ast = JS_ASTTree(script, strlen(script), filename);
if (!ast) {
printf("Failed to parse AST\n");
free(allocated_script);
return 1;
}
if (print_json_errors(ast_json)) {
free(ast_json);
if (print_tree_errors(ast)) {
cJSON_Delete(ast);
free(allocated_script);
return 1;
}
@@ -727,18 +727,18 @@ int cell_init(int argc, char **argv)
JSRuntime *rt = JS_NewRuntime();
if (!rt) {
printf("Failed to create JS runtime\n");
free(ast_json); free(allocated_script);
cJSON_Delete(ast); free(allocated_script);
return 1;
}
JSContext *ctx = JS_NewContext(rt);
if (!ctx) {
printf("Failed to create JS context\n");
free(ast_json); JS_FreeRuntime(rt); free(allocated_script);
cJSON_Delete(ast); JS_FreeRuntime(rt); free(allocated_script);
return 1;
}
JSValue result = JS_RunMach(ctx, ast_json, JS_NULL);
free(ast_json);
JSValue result = JS_RunMachTree(ctx, ast, JS_NULL);
cJSON_Delete(ast);
int exit_code = 0;
if (JS_IsException(result)) {

View File

@@ -31153,7 +31153,7 @@ static void ast_semantic_check (cJSON *ast, cJSON **errors_out,
*intrinsics_out = intr_arr;
}
char *JS_AST (const char *source, size_t len, const char *filename) {
cJSON *JS_ASTTree (const char *source, size_t len, const char *filename) {
ASTParseState s;
memset (&s, 0, sizeof (s));
@@ -31205,10 +31205,14 @@ char *JS_AST (const char *source, size_t len, const char *filename) {
cJSON_AddItemToObject (ast, "errors", sem_errors);
}
/* Convert to JSON string */
return ast;
}
char *JS_AST (const char *source, size_t len, const char *filename) {
cJSON *ast = JS_ASTTree (source, len, filename);
if (!ast) return NULL;
char *json = cJSON_PrintUnformatted (ast);
cJSON_Delete (ast);
return json;
}
@@ -33021,8 +33025,7 @@ static MachCode *mach_compile_program(MachCompState *cs, cJSON *ast) {
}
/* Public API: compile AST JSON to MachCode (context-free) */
MachCode *JS_CompileMach(const char *ast_json) {
cJSON *ast = cJSON_Parse(ast_json);
MachCode *JS_CompileMachTree(cJSON *ast) {
if (!ast) return NULL;
MachCompState cs = {0};
@@ -33036,6 +33039,13 @@ MachCode *JS_CompileMach(const char *ast_json) {
sys_free(cs.vars[i].name);
sys_free(cs.vars);
return code;
}
MachCode *JS_CompileMach(const char *ast_json) {
cJSON *ast = cJSON_Parse(ast_json);
if (!ast) return NULL;
MachCode *code = JS_CompileMachTree(ast);
cJSON_Delete(ast);
return code;
}
@@ -37386,9 +37396,9 @@ static void dump_register_code(JSContext *ctx, JSCodeRegister *code, int indent)
}
}
/* Dump MACH bytecode to stdout for debugging. Takes AST JSON. */
void JS_DumpMach(JSContext *ctx, const char *ast_json, JSValue env) {
MachCode *mc = JS_CompileMach(ast_json);
/* Dump MACH bytecode to stdout for debugging. Takes AST cJSON tree. */
void JS_DumpMachTree(JSContext *ctx, cJSON *ast, JSValue env) {
MachCode *mc = JS_CompileMachTree(ast);
if (!mc) {
printf("=== MACH Bytecode ===\nFailed to compile\n=== End MACH Bytecode ===\n");
return;
@@ -37400,9 +37410,19 @@ void JS_DumpMach(JSContext *ctx, const char *ast_json, JSValue env) {
printf("=== End MACH Bytecode ===\n");
}
/* Compile and execute MACH bytecode. Takes AST JSON. */
JSValue JS_RunMach(JSContext *ctx, const char *ast_json, JSValue env) {
MachCode *mc = JS_CompileMach(ast_json);
void JS_DumpMach(JSContext *ctx, const char *ast_json, JSValue env) {
cJSON *ast = cJSON_Parse(ast_json);
if (!ast) {
printf("=== MACH Bytecode ===\nFailed to parse\n=== End MACH Bytecode ===\n");
return;
}
JS_DumpMachTree(ctx, ast, env);
cJSON_Delete(ast);
}
/* Compile and execute MACH bytecode. Takes AST cJSON tree. */
JSValue JS_RunMachTree(JSContext *ctx, cJSON *ast, JSValue env) {
MachCode *mc = JS_CompileMachTree(ast);
if (!mc) {
return JS_ThrowSyntaxError(ctx, "failed to compile AST to MACH bytecode");
}
@@ -37411,3 +37431,13 @@ JSValue JS_RunMach(JSContext *ctx, const char *ast_json, JSValue env) {
JSValue result = JS_CallRegisterVM(ctx, code, ctx->global_obj, 0, NULL, env, JS_NULL);
return result;
}
JSValue JS_RunMach(JSContext *ctx, const char *ast_json, JSValue env) {
cJSON *ast = cJSON_Parse(ast_json);
if (!ast) {
return JS_ThrowSyntaxError(ctx, "failed to parse AST JSON");
}
JSValue result = JS_RunMachTree(ctx, ast, env);
cJSON_Delete(ast);
return result;
}

View File

@@ -1218,37 +1218,43 @@ CellModule *cell_module_from_bytecode (JSContext *ctx, JSFunctionBytecode *main_
Returns allocated CellModule (caller must free with cell_module_free), or NULL on error. */
CellModule *JS_CompileModule (JSContext *ctx, const char *input, size_t input_len, const char *filename);
/* Parse source code and return AST as cJSON tree.
Caller must call cJSON_Delete() on result. */
struct cJSON *JS_ASTTree (const char *source, size_t len, const char *filename);
/* Parse source code and return AST as JSON string.
Returns malloc'd JSON string (caller must free), or NULL on error.
No JSContext needed — pure string transformation. */
Returns malloc'd JSON string (caller must free), or NULL on error. */
char *JS_AST (const char *source, size_t len, const char *filename);
/* Tokenize source code and return token array as JSON string.
Returns malloc'd JSON string (caller must free), or NULL on error.
No JSContext needed — pure string transformation. */
Returns malloc'd JSON string (caller must free), or NULL on error. */
char *JS_Tokenize (const char *source, size_t len, const char *filename);
/* Compiled bytecode (context-free, serializable) */
typedef struct MachCode MachCode;
/* Compile AST JSON to context-free MachCode.
Returns MachCode* (caller must free with JS_FreeMachCode), or NULL on error. */
/* Compile AST cJSON tree to context-free MachCode. */
MachCode *JS_CompileMachTree(struct cJSON *ast);
/* Compile AST JSON string to context-free MachCode. */
MachCode *JS_CompileMach(const char *ast_json);
/* Free a compiled MachCode tree. */
void JS_FreeMachCode(MachCode *mc);
/* Load compiled MachCode into a JSContext, materializing JSValues.
Returns JSCodeRegister* linked and ready for execution. */
/* Load compiled MachCode into a JSContext, materializing JSValues. */
struct JSCodeRegister *JS_LoadMachCode(JSContext *ctx, MachCode *mc, JSValue env);
/* Dump MACH bytecode to stdout for debugging. Takes AST JSON.
Internally compiles, loads, and dumps binary bytecode. */
/* Dump MACH bytecode to stdout. Takes AST cJSON tree. */
void JS_DumpMachTree (JSContext *ctx, struct cJSON *ast, JSValue env);
/* Dump MACH bytecode to stdout. Takes AST JSON string. */
void JS_DumpMach (JSContext *ctx, const char *ast_json, JSValue env);
/* Compile and execute MACH bytecode. Takes AST JSON.
Internally compiles, loads, and executes.
Returns result of execution, or JS_EXCEPTION on error. */
/* Compile and execute MACH bytecode from AST cJSON tree. */
JSValue JS_RunMachTree (JSContext *ctx, struct cJSON *ast, JSValue env);
/* Compile and execute MACH bytecode from AST JSON string. */
JSValue JS_RunMach (JSContext *ctx, const char *ast_json, JSValue env);
/* Compile AST JSON to MCODE JSON (string-based IR).

View File

@@ -2393,22 +2393,22 @@ TEST(ast_sem_nested_function_scope) {
TEST(mach_compile_basic) {
const char *src = "var x = 1; x = x + 1";
char *ast_json = JS_AST(src, strlen(src), "<test>");
ASSERT_MSG(ast_json != NULL, "JS_AST returned NULL");
MachCode *mc = JS_CompileMach(ast_json);
free(ast_json);
ASSERT_MSG(mc != NULL, "JS_CompileMach returned NULL");
cJSON *ast = JS_ASTTree(src, strlen(src), "<test>");
ASSERT_MSG(ast != NULL, "JS_ASTTree returned NULL");
MachCode *mc = JS_CompileMachTree(ast);
cJSON_Delete(ast);
ASSERT_MSG(mc != NULL, "JS_CompileMachTree returned NULL");
JS_FreeMachCode(mc);
return 1;
}
TEST(mach_compile_function) {
const char *src = "function f(x) { return x + 1 }";
char *ast_json = JS_AST(src, strlen(src), "<test>");
ASSERT_MSG(ast_json != NULL, "JS_AST returned NULL");
MachCode *mc = JS_CompileMach(ast_json);
free(ast_json);
ASSERT_MSG(mc != NULL, "JS_CompileMach returned NULL");
cJSON *ast = JS_ASTTree(src, strlen(src), "<test>");
ASSERT_MSG(ast != NULL, "JS_ASTTree returned NULL");
MachCode *mc = JS_CompileMachTree(ast);
cJSON_Delete(ast);
ASSERT_MSG(mc != NULL, "JS_CompileMachTree returned NULL");
JS_FreeMachCode(mc);
return 1;
}