From d75ce916d71ba4734ec2748a346c8b5081a32d7a Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Tue, 10 Feb 2026 16:37:11 -0600 Subject: [PATCH] compile optimization --- docs/spec/mcode.md | 40 ++- docs/spec/pipeline.md | 118 +++++++ fold.cm | 47 ++- fold.mach | Bin 28071 -> 30239 bytes internal/bootstrap.cm | 67 +++- internal/bootstrap.mach | Bin 6152 -> 7764 bytes mcode.cm | 199 ++++++++---- mcode.mach | Bin 33031 -> 51271 bytes parse.cm | 46 ++- parse.mach | Bin 52939 -> 54273 bytes qbe.cm | 97 +++--- qbe.mach | Bin 0 -> 40178 bytes qbe_emit.cm | 667 ++++++++++++++++++++++++++++++++++++++++ qbe_emit.mach | Bin 0 -> 28132 bytes regen.cm | 3 + source/cell.c | 6 + streamline.cm | 351 +++++++++++++++++++++ streamline.mach | Bin 0 -> 8382 bytes 18 files changed, 1528 insertions(+), 113 deletions(-) create mode 100644 docs/spec/pipeline.md create mode 100644 qbe.mach create mode 100644 qbe_emit.cm create mode 100644 qbe_emit.mach create mode 100644 streamline.cm create mode 100644 streamline.mach diff --git a/docs/spec/mcode.md b/docs/spec/mcode.md index 9f5f1712..29cfb488 100644 --- a/docs/spec/mcode.md +++ b/docs/spec/mcode.md @@ -10,12 +10,11 @@ Mcode is a JSON-based intermediate representation that can be interpreted direct ## Pipeline ``` -Source → Tokenize → Parse (AST) → Mcode (JSON) → Interpret - → Compile to Mach (planned) - → Compile to native (planned) +Source → Tokenize → Parse (AST) → Fold → Mcode (JSON) → Streamline → Interpret + → QBE → Native ``` -Mcode is produced by the `JS_Mcode` compiler pass, which emits a cJSON tree. The mcode interpreter walks this tree directly, dispatching on instruction name strings. +Mcode is produced by `mcode.cm`, which lowers the folded AST to JSON instruction arrays. The streamline optimizer (`streamline.cm`) then eliminates redundant operations. The result can be interpreted by `mcode.c`, or lowered to QBE IL by `qbe_emit.cm` for native compilation. See [Compilation Pipeline](pipeline.md) for the full overview. ## JSMCode Structure @@ -44,16 +43,37 @@ struct JSMCode { ## Instruction Format -Each instruction is a JSON array. The first element is the instruction name (string), followed by operands: +Each instruction is a JSON array. The first element is the instruction name (string), followed by operands (typically `[op, dest, ...args, line, col]`): ```json -["LOADK", 0, 42] -["ADD", 2, 0, 1] -["JMPFALSE", 3, "else_label"] -["CALL", 0, 2, 1] +["access", 3, 5, 1, 9] +["load_index", 10, 4, 9, 5, 11] +["store_dynamic", 4, 11, 12, 6, 3] +["frame", 15, 14, 1, 7, 7] +["setarg", 15, 0, 16, 7, 7] +["invoke", 15, 13, 7, 7] ``` -The instruction set mirrors the Mach VM opcodes — same operations, same register semantics, but with string dispatch instead of numeric opcodes. +### Typed Load/Store + +Memory operations come in typed variants for optimization: + +- `load_index dest, obj, idx` — array element by integer index +- `load_field dest, obj, key` — record property by string key +- `load_dynamic dest, obj, key` — unknown; dispatches at runtime +- `store_index obj, val, idx` — array element store +- `store_field obj, val, key` — record property store +- `store_dynamic obj, val, key` — unknown; dispatches at runtime + +The compiler selects the appropriate variant based on `type_tag` and `access_kind` annotations from parse and fold. + +### Decomposed Calls + +Function calls are split into separate instructions: + +- `frame dest, fn, argc` — allocate call frame +- `setarg frame, idx, val` — set argument +- `invoke frame, result` — execute the call ## Labels diff --git a/docs/spec/pipeline.md b/docs/spec/pipeline.md new file mode 100644 index 00000000..fe1240ba --- /dev/null +++ b/docs/spec/pipeline.md @@ -0,0 +1,118 @@ +--- +title: "Compilation Pipeline" +description: "Overview of the compilation stages and optimizations" +--- + +## Overview + +The compilation pipeline transforms source code through several stages, each adding information or lowering the representation toward execution. There are three execution backends: the Mach register VM (default), the Mcode interpreter (debug), and native code via QBE (experimental). + +``` +Source → Tokenize → Parse → Fold → Mach VM (default) + → Mcode → Streamline → Mcode Interpreter + → QBE → Native +``` + +## Stages + +### Tokenize (`tokenize.cm`) + +Splits source text into tokens. Handles string interpolation by re-tokenizing template literal contents. Produces a token array with position information (line, column). + +### Parse (`parse.cm`) + +Converts tokens into an AST. Also performs semantic analysis: + +- **Scope records**: For each scope (global, function), builds a record mapping variable names to their metadata: `make` (var/def/function/input), `function_nr`, `nr_uses`, `closure` flag, and `level`. +- **Type tags**: When the right-hand side of a `def` is a syntactically obvious type, stamps `type_tag` on the scope record entry. Derivable types: `"integer"`, `"number"`, `"text"`, `"array"`, `"record"`, `"function"`, `"logical"`, `"null"`. +- **Intrinsic resolution**: Names used but not locally bound are recorded in `ast.intrinsics`. Name nodes referencing intrinsics get `intrinsic: true`. +- **Access kind**: Subscript (`[`) nodes get `access_kind`: `"index"` for numeric subscripts, `"field"` for string subscripts, omitted otherwise. +- **Tail position**: Return statements where the expression is a call get `tail: true`. + +### Fold (`fold.cm`) + +Operates on the AST. Performs constant folding and type analysis: + +- **Constant folding**: Evaluates arithmetic on known constants at compile time (e.g., `5 + 10` becomes `15`). +- **Constant propagation**: Tracks `def` bindings whose values are known constants. +- **Type propagation**: Extends `type_tag` through operations. When both operands of an arithmetic op have known types, the result type is known. Propagates type tags to reference sites. +- **Intrinsic specialization**: When an intrinsic call's argument types are known, stamps a `hint` on the call node. For example, `length(x)` where x is a known array gets `hint: "array_length"`. Type checks like `is_array(known_array)` are folded to `true`. +- **Purity marking**: Stamps `pure: true` on expressions with no side effects (literals, name references, arithmetic on pure operands). +- **Dead code elimination**: Removes unreachable branches when conditions are known constants. + +### Mcode (`mcode.cm`) + +Lowers the AST to a JSON-based intermediate representation with explicit operations. Key design principle: **every type check is an explicit instruction** so downstream optimizers can see and eliminate them. + +- **Typed load/store**: Emits `load_index` (array by integer), `load_field` (record by string), or `load_dynamic` (unknown) based on type information from fold. +- **Decomposed calls**: Function calls are split into `frame` (create call frame) + `setarg` (set arguments) + `invoke` (execute call). +- **Intrinsic access**: Intrinsic functions are loaded via `access` with an intrinsic marker rather than global lookup. + +See [Mcode IR](mcode.md) for instruction format details. + +### Streamline (`streamline.cm`) + +Optimizes the Mcode IR. Operates per-function: + +- **Redundant instruction elimination**: Removes no-op patterns and redundant moves. +- **Dead code removal**: Eliminates instructions whose results are never used. +- **Type-based narrowing**: When type information is available, narrows `load_dynamic`/`store_dynamic` to typed variants. + +### QBE Emit (`qbe_emit.cm`) + +Lowers optimized Mcode IR to QBE intermediate language for native code compilation. Each Mcode function becomes a QBE function that calls into the cell runtime (`cell_rt_*` functions) for operations that require the runtime (allocation, intrinsic dispatch, etc.). + +String constants are interned in a data section. Integer constants are NaN-boxed inline. + +### QBE Macros (`qbe.cm`) + +Provides operation implementations as QBE IL templates. Each arithmetic, comparison, and type operation is defined as a function that emits the corresponding QBE instructions, handling type dispatch (integer, float, text paths) with proper guard checks. + +## Execution Backends + +### Mach VM (default) + +Binary 32-bit register VM. Used for production execution and bootstrapping. + +``` +./cell script.ce +``` + +### Mcode Interpreter + +JSON-based interpreter. Used for debugging the compilation pipeline. + +``` +./cell --mcode script.ce +``` + +### QBE Native (experimental) + +Generates QBE IL that can be compiled to native code. + +``` +./cell --emit-qbe script.ce > output.ssa +``` + +## Files + +| File | Role | +|------|------| +| `tokenize.cm` | Lexer | +| `parse.cm` | Parser + semantic analysis | +| `fold.cm` | Constant folding + type analysis | +| `mcode.cm` | AST → Mcode IR lowering | +| `streamline.cm` | Mcode IR optimizer | +| `qbe_emit.cm` | Mcode IR → QBE IL emitter | +| `qbe.cm` | QBE IL operation templates | +| `internal/bootstrap.cm` | Pipeline orchestrator | + +## Test Files + +| File | Tests | +|------|-------| +| `parse_test.ce` | Type tags, access_kind, intrinsic resolution | +| `fold_test.ce` | Type propagation, purity, intrinsic hints | +| `mcode_test.ce` | Typed load/store, decomposed calls | +| `streamline_test.ce` | Optimization counts, IR before/after | +| `qbe_test.ce` | End-to-end QBE IL generation | diff --git a/fold.cm b/fold.cm index f1ed7cc9..adc6087d 100644 --- a/fold.cm +++ b/fold.cm @@ -158,6 +158,7 @@ var fold = function(ast) { var name = null var sv = null var item = null + var rhs_target = null while (i < length(stmts)) { stmt = stmts[i] kind = stmt.kind @@ -169,6 +170,19 @@ var fold = function(ast) { register_const(fn_nr, name, stmt.right) } } + if (name != null && stmt.right != null && stmt.right.kind == "(") { + rhs_target = stmt.right.expression + if (rhs_target != null && rhs_target.intrinsic == true) { + sv = scope_var(fn_nr, name) + if (sv != null && sv.type_tag == null) { + if (rhs_target.name == "array") sv.type_tag = "array" + else if (rhs_target.name == "record") sv.type_tag = "record" + else if (rhs_target.name == "text") sv.type_tag = "text" + else if (rhs_target.name == "number") sv.type_tag = "number" + else if (rhs_target.name == "blob") sv.type_tag = "blob" + } + } + } } else if (kind == "function") { name = stmt.name if (name != null && stmt.arity != null) { @@ -320,6 +334,8 @@ var fold = function(ast) { var ar = null var akey = null var tv = null + var att = null + var arg = null // Recurse into children first (bottom-up) if (k == "+" || k == "-" || k == "*" || k == "/" || k == "%" || @@ -385,6 +401,10 @@ var fold = function(ast) { return copy_loc(expr, {kind: lit.kind, value: lit.value, number: lit.number}) } } + sv = scope_var(fn_nr, expr.name) + if (sv != null && sv.type_tag != null) { + expr.type_tag = sv.type_tag + } return expr } @@ -497,7 +517,7 @@ var fold = function(ast) { return expr } - // Call: stamp arity + // Call: stamp arity and fold intrinsic type checks if (k == "(") { target = expr.expression if (target != null && target.kind == "name" && target.level == 0) { @@ -506,6 +526,30 @@ var fold = function(ast) { if (fn_arities[akey] != null) ar = fn_arities[akey][target.name] if (ar != null) expr.arity = ar } + if (target != null && target.intrinsic == true && length(expr.list) == 1) { + arg = expr.list[0] + att = null + if (arg.type_tag != null) { + att = arg.type_tag + } else if (arg.kind == "name" && arg.level == 0) { + sv = scope_var(fn_nr, arg.name) + if (sv != null) att = sv.type_tag + } + if (att != null) { + if (target.name == "is_array") return make_bool(att == "array", expr) + if (target.name == "is_text") return make_bool(att == "text", expr) + if (target.name == "is_number") return make_bool(att == "number" || att == "integer", expr) + if (target.name == "is_integer") return make_bool(att == "integer", expr) + if (target.name == "is_function") return make_bool(att == "function", expr) + if (target.name == "is_logical") return make_bool(att == "logical", expr) + if (target.name == "is_null") return make_bool(att == "null", expr) + if (target.name == "is_object") return make_bool(att == "record", expr) + if (target.name == "length") { + if (att == "array") expr.hint = "array_length" + else if (att == "text") expr.hint = "text_length" + } + } + } return expr } @@ -525,6 +569,7 @@ var fold = function(ast) { if (k == "var" || k == "def") { stmt.right = fold_expr(stmt.right, fn_nr) + if (is_pure(stmt.right)) stmt.pure = true return stmt } if (k == "var_list") { diff --git a/fold.mach b/fold.mach index 886da2910f5c43121dd8c80f7df865e1560cc04c..3b4ec01373ec7038bb7cefa1335de2eb2a1fc408 100644 GIT binary patch literal 30239 zcmc(o378#KnXpe)-MUL}_jcdg=_H-Ibvn0iI^F5)fLS4cs6apkR8R;>heSG^(CL6E ztEkMQqB4r2!lX<^$>eQ)wyBpk|`TKd^ z)K_Qu&QfRj>eRU>N-5=o{YxS2J2bUd?gNDeQGj@x}lr}xYrI5d64m-u@pckk80;iE_P zpNB&^^i;j(sjwcZ(VFLl#hRKx%|ymi6KP*f^aX07lu{G9P)!7BH8Ge`6WOeq=*+3{ zex=5%gk9XBCJLQu!tYZ4GBQ`jt=z39mh~vV7r8gD{0i4fK}{^}Reld)x?l-zJ$=gW z=Gs+M6H7{JqPt&BELx<(+EVhPys*ARm4`gvTdve--S@&;T~$24=24n@-4nlHwC;Jm zwN$-X%Mh<$5z*r3I}S+&r`Yc#wTvg3#$2As$9>ff+3{} zwfa!AmZFYA>S#cPLwWM$JH0CDo^T)kgl^ zrux-M_@4?-fL&@(U5x*w$j{+=6N@|Cf=b7}XwG{Yfzo0%qMgJrBUkd`wH-nP;W>AHR7QPj9secXf zu&9M^2ci0Q&<%r{?-0iF{h&_?KOpY^r4j!SzaJ7m$&Z2#^`l_1dN9aADdwRdqaGsO zLxdN8LR`yFgQ60CL7s(Qp+gv;?RpS`{cGtUa&zf{>AkvpOdmaX@#Ktd__LGGnl-}A z(MeyT@7q0fWKug#ADx<7gp1td125GKe3hb`u7a{paw!jYKQ)58O8E&nNXV BNtO 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 diff --git a/internal/bootstrap.cm b/internal/bootstrap.cm index 2f0984e8..fabcea7b 100644 --- a/internal/bootstrap.cm +++ b/internal/bootstrap.cm @@ -41,11 +41,17 @@ var boot_env = {use: use_basic} var tokenize_mod = boot_load("tokenize", boot_env) var parse_mod = boot_load("parse", boot_env) var fold_mod = boot_load("fold", boot_env) +use_cache['tokenize'] = tokenize_mod +use_cache['parse'] = parse_mod +use_cache['fold'] = fold_mod // Optionally load mcode compiler module var mcode_mod = null +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 @@ -55,6 +61,9 @@ function check_mach_stale() { ["parse.cm", "parse.mach"], ["fold.cm", "fold.mach"], ["mcode.cm", "mcode.mach"], + ["streamline.cm", "streamline.mach"], + ["qbe.cm", "qbe.mach"], + ["qbe_emit.cm", "qbe_emit.mach"], ["internal/bootstrap.cm", "internal/bootstrap.mach"], ["internal/engine.cm", "internal/engine.mach"] ] @@ -118,26 +127,78 @@ function analyze(src, filename) { return ast } +// Load a module from .mach bytecode, falling back to source compilation +function load_module(name, env) { + var mach_path = core_path + '/' + name + ".mach" + var data = null + var src_path = null + var src = null + var ast = null + if (fd.is_file(mach_path)) { + data = fd.slurp(mach_path) + return mach_load(data, env) + } + src_path = core_path + '/' + name + ".cm" + src = text(fd.slurp(src_path)) + ast = analyze(src, src_path) + return mach_eval_ast(name, json.encode(ast), 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 + } +} + // Run AST through either mcode or mach pipeline function run_ast(name, ast, env) { var compiled = null + var optimized = null + var qbe_il = null if (use_mcode) { compiled = mcode_mod(ast) - return mcode_run(name, json.encode(compiled), env) + 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) } return mach_eval_ast(name, json.encode(ast), env) } // use() with ƿit pipeline for .cm modules function use_fn(path) { - var file_path = path + '.cm' + var file_path = null + var mach_path = null + var data = null var script = null var ast = null var result = null if (use_cache[path]) return use_cache[path] - // Check CWD first, then core_path + // Try .mach bytecode first (CWD then core_path) + mach_path = path + '.mach' + if (!fd.is_file(mach_path)) + mach_path = core_path + '/' + path + '.mach' + if (fd.is_file(mach_path)) { + data = fd.slurp(mach_path) + result = mach_load(data, {use: use_fn}) + use_cache[path] = result + return result + } + + // Try .cm source (CWD then core_path) + file_path = path + '.cm' if (!fd.is_file(file_path)) file_path = core_path + '/' + path + '.cm' diff --git a/internal/bootstrap.mach b/internal/bootstrap.mach index 452430bbbf068f43ec3a3760684fe401df88651e..0a18f9cd5afb0bf36d3931baa4c94cd599227e10 100644 GIT binary patch literal 7764 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 param functions) + var _bp_dest = 0 + var _bp_left = 0 + var _bp_right = 0 + var _bp_ln = null + var _bp_rn = null + // State save/restore for nested function compilation var save_state = function() { return { @@ -260,15 +267,19 @@ var mcode = function(ast) { } // emit_add_decomposed: int path -> text path -> float path -> disrupt - var emit_add_decomposed = function(dest, left, right, left_node, right_node) { + // reads _bp_dest, _bp_left, _bp_right, _bp_ln, _bp_rn from closure + var emit_add_decomposed = function() { + var dest = _bp_dest + var left = _bp_left + var right = _bp_right var t0 = 0 var t1 = 0 - var left_is_int = is_known_int(left_node) - var left_is_text = is_known_text(left_node) - var left_is_num = is_known_number(left_node) - var right_is_int = is_known_int(right_node) - var right_is_text = is_known_text(right_node) - var right_is_num = is_known_number(right_node) + var left_is_int = is_known_int(_bp_ln) + var left_is_text = is_known_text(_bp_ln) + var left_is_num = is_known_number(_bp_ln) + var right_is_int = is_known_int(_bp_rn) + var right_is_text = is_known_text(_bp_rn) + var right_is_num = is_known_number(_bp_rn) var not_int = null var not_text = null var done = null @@ -346,13 +357,17 @@ var mcode = function(ast) { } // emit_numeric_binop: int path -> float path -> disrupt - var emit_numeric_binop = function(int_op, float_op, dest, left, right, left_node, right_node) { + // reads _bp_dest, _bp_left, _bp_right, _bp_ln, _bp_rn from closure + var emit_numeric_binop = function(int_op, float_op) { + var dest = _bp_dest + var left = _bp_left + var right = _bp_right var t0 = 0 var t1 = 0 - var left_is_int = is_known_int(left_node) - var left_is_num = is_known_number(left_node) - var right_is_int = is_known_int(right_node) - var right_is_num = is_known_number(right_node) + var left_is_int = is_known_int(_bp_ln) + var left_is_num = is_known_number(_bp_ln) + var right_is_int = is_known_int(_bp_rn) + var right_is_num = is_known_number(_bp_rn) var not_int = null var done = null var err = null @@ -404,7 +419,11 @@ var mcode = function(ast) { } // emit_eq_decomposed: identical -> int -> float -> text -> null -> bool -> mismatch(false) - var emit_eq_decomposed = function(dest, left, right, left_node, right_node) { + // reads _bp_dest, _bp_left, _bp_right from closure + var emit_eq_decomposed = function() { + var dest = _bp_dest + var left = _bp_left + var right = _bp_right var t0 = 0 var t1 = 0 var done = gen_label("eq_done") @@ -472,7 +491,11 @@ var mcode = function(ast) { } // emit_ne_decomposed: identical -> int -> float -> text -> null -> bool -> mismatch(true) - var emit_ne_decomposed = function(dest, left, right, left_node, right_node) { + // reads _bp_dest, _bp_left, _bp_right from closure + var emit_ne_decomposed = function() { + var dest = _bp_dest + var left = _bp_left + var right = _bp_right var t0 = 0 var t1 = 0 var done = gen_label("ne_done") @@ -549,15 +572,19 @@ var mcode = function(ast) { } // emit_relational: int -> float -> text -> disrupt - var emit_relational = function(int_op, float_op, text_op, dest, left, right, left_node, right_node) { + // reads _bp_dest, _bp_left, _bp_right, _bp_ln, _bp_rn from closure + var emit_relational = function(int_op, float_op, text_op) { + var dest = _bp_dest + var left = _bp_left + var right = _bp_right var t0 = 0 var t1 = 0 - var left_is_int = is_known_int(left_node) - var left_is_num = is_known_number(left_node) - var left_is_text = is_known_text(left_node) - var right_is_int = is_known_int(right_node) - var right_is_num = is_known_number(right_node) - var right_is_text = is_known_text(right_node) + var left_is_int = is_known_int(_bp_ln) + var left_is_num = is_known_number(_bp_ln) + var left_is_text = is_known_text(_bp_ln) + var right_is_int = is_known_int(_bp_rn) + var right_is_num = is_known_number(_bp_rn) + var right_is_text = is_known_text(_bp_rn) var not_int = null var not_num = null var done = null @@ -654,29 +681,33 @@ var mcode = function(ast) { } // Central router: maps op string to decomposition helper - var emit_binop = function(op_str, dest, left, right, left_node, right_node) { + // Sets _bp_* closure vars then calls helper with reduced args + var emit_binop = function(op_str, dest, left, right) { + _bp_dest = dest + _bp_left = left + _bp_right = right if (op_str == "add") { - emit_add_decomposed(dest, left, right, left_node, right_node) + emit_add_decomposed() } else if (op_str == "subtract") { - emit_numeric_binop("sub_int", "sub_float", dest, left, right, left_node, right_node) + emit_numeric_binop("sub_int", "sub_float") } else if (op_str == "multiply") { - emit_numeric_binop("mul_int", "mul_float", dest, left, right, left_node, right_node) + emit_numeric_binop("mul_int", "mul_float") } else if (op_str == "divide") { - emit_numeric_binop("div_int", "div_float", dest, left, right, left_node, right_node) + emit_numeric_binop("div_int", "div_float") } else if (op_str == "modulo") { - emit_numeric_binop("mod_int", "mod_float", dest, left, right, left_node, right_node) + emit_numeric_binop("mod_int", "mod_float") } else if (op_str == "eq") { - emit_eq_decomposed(dest, left, right, left_node, right_node) + emit_eq_decomposed() } else if (op_str == "ne") { - emit_ne_decomposed(dest, left, right, left_node, right_node) + emit_ne_decomposed() } else if (op_str == "lt") { - emit_relational("lt_int", "lt_float", "lt_text", dest, left, right, left_node, right_node) + emit_relational("lt_int", "lt_float", "lt_text") } else if (op_str == "le") { - emit_relational("le_int", "le_float", "le_text", dest, left, right, left_node, right_node) + emit_relational("le_int", "le_float", "le_text") } else if (op_str == "gt") { - emit_relational("gt_int", "gt_float", "gt_text", dest, left, right, left_node, right_node) + emit_relational("gt_int", "gt_float", "gt_text") } else if (op_str == "ge") { - emit_relational("ge_int", "ge_float", "ge_text", dest, left, right, left_node, right_node) + emit_relational("ge_int", "ge_float", "ge_text") } else { // Passthrough for bitwise, pow, in, etc. emit_3(op_str, dest, left, right) @@ -685,19 +716,31 @@ var mcode = function(ast) { } var emit_get_prop = function(dest, obj, prop) { - add_instr(["load", dest, obj, prop]) + add_instr(["load_field", dest, obj, prop]) } var emit_set_prop = function(obj, prop, val) { - add_instr(["store", obj, val, prop]) + add_instr(["store_field", obj, val, prop]) } - var emit_get_elem = function(dest, obj, idx) { - emit_3("load", dest, obj, idx) + var emit_get_elem = function(dest, obj, idx, access_kind) { + if (access_kind == "index") { + emit_3("load_index", dest, obj, idx) + } else if (access_kind == "field") { + emit_3("load_field", dest, obj, idx) + } else { + emit_3("load_dynamic", dest, obj, idx) + } } - var emit_set_elem = function(obj, idx, val) { - emit_3("store", obj, val, idx) + var emit_set_elem = function(obj, idx, val, access_kind) { + if (access_kind == "index") { + emit_3("store_index", obj, val, idx) + } else if (access_kind == "field") { + emit_3("store_field", obj, val, idx) + } else { + emit_3("store_dynamic", obj, val, idx) + } } var emit_call = function(dest, func_slot, args) { @@ -718,23 +761,37 @@ var mcode = function(ast) { } var emit_call_method = function(dest, obj, prop, args) { - var instr = ["callmethod", dest, obj, prop] + var method_slot = alloc_slot() + add_instr(["load_field", method_slot, obj, prop]) + var argc = length(args) + var frame_slot = alloc_slot() + emit_3("frame", frame_slot, method_slot, argc) + emit_3("setarg", frame_slot, 0, obj) + var arg_idx = 1 var _i = 0 - while (_i < length(args)) { - push(instr, args[_i]) + while (_i < argc) { + emit_3("setarg", frame_slot, arg_idx, args[_i]) + arg_idx = arg_idx + 1 _i = _i + 1 } - add_instr(instr) + emit_2("invoke", frame_slot, dest) } var emit_call_method_dyn = function(dest, obj, key_reg, args) { - var instr = ["callmethod_dyn", dest, obj, key_reg] + var method_slot = alloc_slot() + emit_3("load_dynamic", method_slot, obj, key_reg) + var argc = length(args) + var frame_slot = alloc_slot() + emit_3("frame", frame_slot, method_slot, argc) + emit_3("setarg", frame_slot, 0, obj) + var arg_idx = 1 var _i = 0 - while (_i < length(args)) { - push(instr, args[_i]) + while (_i < argc) { + emit_3("setarg", frame_slot, arg_idx, args[_i]) + arg_idx = arg_idx + 1 _i = _i + 1 } - add_instr(instr) + emit_2("invoke", frame_slot, dest) } var emit_go_call = function(func_slot, args) { @@ -920,7 +977,9 @@ var mcode = function(ast) { if (op == null) { op = "add" } - emit_binop(op, dest, left_slot, right_slot, left, right) + _bp_ln = left + _bp_rn = right + emit_binop(op, dest, left_slot, right_slot) return dest } @@ -972,7 +1031,9 @@ var mcode = function(ast) { } right_slot = gen_expr(right, -1) dest = alloc_slot() - emit_binop(op, dest, left_slot, right_slot, null, right) + _bp_ln = null + _bp_rn = right + emit_binop(op, dest, left_slot, right_slot) if (level == 0) { local = find_var(name) if (local >= 0) { @@ -995,7 +1056,9 @@ var mcode = function(ast) { emit_get_prop(old_val, obj_slot, prop) right_slot = gen_expr(right, -1) dest = alloc_slot() - emit_binop(op, dest, old_val, right_slot, null, right) + _bp_ln = null + _bp_rn = right + emit_binop(op, dest, old_val, right_slot) emit_set_prop(obj_slot, prop, dest) return dest } else if (left_kind == "[") { @@ -1004,11 +1067,13 @@ var mcode = function(ast) { obj_slot = gen_expr(obj, -1) idx_slot = gen_expr(idx_expr, -1) old_val = alloc_slot() - emit_get_elem(old_val, obj_slot, idx_slot) + emit_get_elem(old_val, obj_slot, idx_slot, left.access_kind) right_slot = gen_expr(right, -1) dest = alloc_slot() - emit_binop(op, dest, old_val, right_slot, null, right) - emit_set_elem(obj_slot, idx_slot, dest) + _bp_ln = null + _bp_rn = right + emit_binop(op, dest, old_val, right_slot) + emit_set_elem(obj_slot, idx_slot, dest, left.access_kind) return dest } return -1 @@ -1081,7 +1146,7 @@ var mcode = function(ast) { idx_expr = left.right obj_slot = gen_expr(obj, -1) idx_slot = gen_expr(idx_expr, -1) - emit_set_elem(obj_slot, idx_slot, val_slot) + emit_set_elem(obj_slot, idx_slot, val_slot, left.access_kind) } return val_slot } @@ -1301,7 +1366,7 @@ var mcode = function(ast) { obj_slot = gen_expr(obj, -1) idx_slot = gen_expr(idx, -1) slot = alloc_slot() - emit_get_elem(slot, obj_slot, idx_slot) + emit_get_elem(slot, obj_slot, idx_slot, expr.access_kind) return slot } @@ -1357,7 +1422,9 @@ var mcode = function(ast) { a0 = gen_expr(args_list[0], -1) a1 = gen_expr(args_list[1], -1) d = alloc_slot() - emit_binop(mop, d, a0, a1, args_list[0], args_list[1]) + _bp_ln = args_list[0] + _bp_rn = args_list[1] + emit_binop(mop, d, a0, a1) return d } @@ -1442,7 +1509,9 @@ var mcode = function(ast) { emit_access_intrinsic(old_slot, name) } new_slot = alloc_slot() - emit_binop(arith_op, new_slot, old_slot, one_slot, null, one_node) + _bp_ln = null + _bp_rn = one_node + emit_binop(arith_op, new_slot, old_slot, one_slot) if (level == 0) { local = find_var(name) if (local >= 0) { @@ -1462,7 +1531,9 @@ var mcode = function(ast) { old_slot = alloc_slot() emit_get_prop(old_slot, obj_slot, prop) new_slot = alloc_slot() - emit_binop(arith_op, new_slot, old_slot, one_slot, null, one_node) + _bp_ln = null + _bp_rn = one_node + emit_binop(arith_op, new_slot, old_slot, one_slot) emit_set_prop(obj_slot, prop, new_slot) return postfix ? old_slot : new_slot } else if (operand_kind == "[") { @@ -1471,10 +1542,12 @@ var mcode = function(ast) { obj_slot = gen_expr(obj, -1) idx_slot = gen_expr(idx_expr, -1) old_slot = alloc_slot() - emit_get_elem(old_slot, obj_slot, idx_slot) + emit_get_elem(old_slot, obj_slot, idx_slot, operand.access_kind) new_slot = alloc_slot() - emit_binop(arith_op, new_slot, old_slot, one_slot, null, one_node) - emit_set_elem(obj_slot, idx_slot, new_slot) + _bp_ln = null + _bp_rn = one_node + emit_binop(arith_op, new_slot, old_slot, one_slot) + emit_set_elem(obj_slot, idx_slot, new_slot, operand.access_kind) return postfix ? old_slot : new_slot } } @@ -1911,7 +1984,9 @@ var mcode = function(ast) { case_expr = case_node.expression case_val = gen_expr(case_expr, -1) cmp_slot = alloc_slot() - emit_binop("eq", cmp_slot, switch_val, case_val, null, case_expr) + _bp_ln = null + _bp_rn = case_expr + emit_binop("eq", cmp_slot, switch_val, case_val) emit_jump_cond("jump_true", cmp_slot, case_label) push(case_labels, case_label) } diff --git a/mcode.mach b/mcode.mach index e3fdf4a94ba77753b945105b065cd848240c6adf..f5227053df5540e34b380cbdd5ecdbd0727cfea6 100644 GIT binary patch literal 51271 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 Mj@jHajCeN*zhhZ_Pyhe` literal 33031 zcmd6w3w&Kgwg2~dBsoc%$4Q!3o21!onx-dhPajYUwgmw}c}OeBL$8E1JuQK}NYX+p z^6)}I5MPLZ6|wX}k;j!sxq?uHLV=1<6wu49fLNt~JhUiqMd<(go4seBlarhR-uw4+ z)6cInvu4e#HM3{0H8XqfIgaBvQDFb_i1c*!Elov;g!SKw!BkirrwsP>hJ)-F6gSL& zk&u%JheCOwu#+V1M#4@T#io%Ja>ju201>Ce`D-i|AY^zP=NLl!7YO(?6eGW5>C&(` z+rpALxMIoBKu710Ax$@=o)z6gUH#pw3@H)Lhihrq%C4m;Lr5AzPv6oN-F=2I*$|d= z4R!Q-6}OmHLFgMW3};$~Gkn5{MvuXhyTfYm{P})zz$6zgG`+K8@Zn*xc13A%bs(B_(8 zx)&t8An65@U879Gr_T4N3q9)m1zxz2P^$V#*H5;1)HA){M86DGXpAhR4vi5S?-A6u z%4qS6(UVG^NpW2tI=i=T$cQ=1E7{%G;g$IO7d#1VUi+teLBb1?UNG4UTD;&)FF4Wb zmHBj>r_ute(t>a?GMA@%+xrLlmJf9F7>16)p$8#PS6eC27!E}o25)0HLKr2?Bg`i( zAdC?f5*85_6OJJ)AuJ^=?20^!U=>; zgf8Jk!byb9gp&zd2wNi4)iCYfWk!gg;h)gG> z?#K*6WJQvM$cW4&M90W1LUfDFCZvs#IfS$^vKJw3jO^R)jjnR+iIeF+DJ&+KcqX!Y9bM#qtRy{_ zconE7UIS{0*Evpd92g&RlJyugE={sA>Le%RImxDcC+QYA$%!#1IjPV|HWxX`$;D2x zWsH-YQsN|AOP%D@GAEfRcam*!CpoRcNlveHk~79S$z+w2oLTK8XVo~#*|kn`PMwq7 zYn+qZd%TmJTkjgMB8lB|66P)CJO-}M-u9KWU(Mc|t*EqL>AA`HWPs5eY6X9{re}x;J-*Y4X z6}|#&>_qJECnMvXW3b1cjnp{DVuw$NOmNzvuOq!a(%}4!_<`8CgYx6ftp(-I8L=v7 zSF8wB55r!VFa62%+P`3u(g-DtfpG@gyLtzQ23B+qb@lZQhOs@)FoK-kVEf9BfkESx zOhis;uzl#{uEF-fZoDDJ%Nd>;Z0{Xt?-*D&;Kn-T~Ic^GNYE02T|1i|hBT(c>8vAJ!I0{?7 z2t^lz&w!(ma10IpES+#H@#ARB@ig!R;-7<8uz$K+?O$-`>UQQr=vrqDbIn)G{WyHQ zE9KH=waR7D<1+uM3`e4^tQicAu#G%v-i{V0p=AeNt%JD55;R^)i!5L@c0~iw{skJ! zWEf-7&)Pz>98?VqNN-1vw<~N3BThqJe$>s+bIJ^zp%sldtg%df z%b#}d7N63(b z-=w?+;>_?;SJ}V7f|y%QIsF5vWnE`rpt$>tNg2Yk8_$T(?pTuQ#@DUr9ZC&oLFE{V zCMU8R4jq%vJtcFe`zsL*h4Se{(`hV|?S1qe8uT5emmZo3CkMEk&bR`lEvVHfEn)UA zP@0S3nY~^%p^R%@Hqh78uK9p;G6_8Mik@C;+PJ+#YE^lrH6{%`oK4sDxr*!KVH}}{ z?{k~~0J?_;LJ#X`mY_d%pVYK}ikq!6MNGFEORbcd`x#5@O(o1UWBANsn1W>q%cv`g#+OJ3l1-L()H_trluS&h`YC zz_e$iqUlkhwx(6YI{PZ!jL4f2c{3s{_9&8OI%WdQZl5=mHurj8e;|Pd&!A(p*Qkql zD-v$yrpSaOs3J|4?Vrl0_cL_~vL%??qC>ZdjD$4=GZ^oQh$rg~WZi+RJD8AULNe4> zs@47l@^tTv+9FS$`G1KtHQqTN^Mhx0V-aVDf0wD1cOm&MB;SSPyYM!dkVFa5U19$M z>56=ybfZJ346F;kTYwHRbYP#$Es8ob@}tJvN{>zGu?amkp~oil*u-_sgyjFTblHq9 zo6%)6x@<<5&FGQ|>7r3EYQLDPqTIfIbH{cp>0FxCPwF~z|FTgOT%3?U%}^(WVj*Wj zenHG7WS;L#D2Nrhg~W>@&V*QD(ToVwj|_9E4txmv@(?q^hgdaxm_XRhuj&oY;tmAW^5HXWiX zzO18rFg1pVj6kcdAjU>}tj6g@!l4#=*P~&5b&EpNm3@-iUw8_$^AsNbDXy}`ub}-3 z+OHU<7SegomRGIG(6x6(cehbmx=K|UTs?Nt7_$a#>Al+aR?(HDK%j{iTaw z=Z|!~S?$SqYyP2;;^k@NU_faGOOmi|rabOAyE1%*k@5!4I-egaZb2V@Z6{j1`dW%VyZQP$|+@EM% z8coEToIlez4|RkVlZXp5hXGY`H_QxLHDh?g(pS#jz#ryPS*_nf>$lK4jV9tv&f941 zp^h+(J!-h8u9wXTOwIpRpZ^{0|Bm))G!bud-a~s2b%Yj^hzm1^VUI1!(dXtWV441r z9uJ2Kop30gfhOWj*t9V|>Il=2e(E@$(gc4>n|a+tBHn)MVEd`PeW&;8wl$WmJGE@6 z-Ha5?tht}P>|pH^P%`fNfv)8z`-L=rFjaV#z{v2jeS(IavD0(RHla7K95OeaaVzQ~ zRbVcT4-yF*MWc z4;y7=;aTos@MPso%J@Fh1|G_q?lXmEnfil5Uc>s>FI7Q9Hg?(+M3~WD^Ac4OG=&Ul zs!^007)a|l(-d3UH8`-Mf9P>?Lo{5oESE;PMNISyv7E6&;`rcV(nX{Tc_PH)S9c8Q zV$wy$#NjVtsyc>r@fhOpmy#|aJ*I>>{AHv|NtcwG?RqVJwfU##fo?^f(^`RltH>ve z5u!vT;aFr>A+NgFNmQ`H#0|6ow_=RXUy{MkW>il8GM~SkurfPUK)(Uj5fT0qZW5=4dBq|~yw}OYBmDN72hPFja+uX|hQOfZ=sY>n7EGL>* zE>~Z<6?KkV$y2S05c0+o7eDbzo{bIDt$q)>HF$C>OGYV^OE15SxrQUJ$wboYcmFvgVk6xeJu{e$UCdLEqLQurBv!YOQ zfMOnK7l&E`j3IwaXi9(*@=HRK1C(NLOUbt=Bfl)v8lXH>>BMJ_GjN}L2?Td2sH25&MHb7m5^ zn1yb$$e)GWSv-RjW<#4Dss_!V)nG5m?M*q0x$w-T++51drHrr-w0$VI59RiuoUm^w z5AYa=YqBpo?u(AXe$e-$Z-oWK7tjV_Au=rXr@sA>u|IvdKV^jjpdEmW1CVh5^$Q15 z#^NCI52DOLlsO1~;b3S7Q|4gG91Op32r>^ruS1|8g6@ZeCK?!Na{Kg8AnpTa1>=M7LmV*GK(m)2!3HPw8fNJOqs>-3r8dK zX!JT7{f}mB9nIJhj)8s*Itj-TKNg*YS90lZtw1 zA2g{j4;JmD;(tDckl$zK((Z3eEn@oHc~I~OGjR{Vs?<$*VElz6M452u@3FvBrN4gSOkj>(j8cQizQf)C0GQD zPSTxN4vVFDuM`$w8J408i|?V9u+}*Ri*pJxPQmJ&f(1GSt0$ZaP3VQ+1CIyc?}NV& zdY_5&I1s&rerUoV-em|Yw}Nt~Gq}$nZn288tKeBh*;U9ARzq8j?A6q<8l8nRDfcDH zS)2vWS(H1Aa%WLSI2+p8lslVpXH!Nv7i)JeR_KDF38HeDO=Z z^qicbr^bXgJ~TGlfc)ka*yJn0cd?n@#Sd7l#SgA!_HXe$(%&O(aTR{!Dpq3_-zWWj z(iT^fzM8bfHQ45Bu*uh8)33pvUxTd|)+#dVb@;Rm&^N%p0Y9(-y@czb zT@UZ|l)s)8tMEg3Z-9OSyf?sm1AM}b&~AkHMtE<8Pq+!%P4L|W-%aRo6M4eT&~L`C z2)7Wwg}xAOC4MXXx59rbbqcpZyN!Bpqug!ODclb2cKB|G?{;{EjnFp2w-LUL@CbK6 zy92&E@PT*GH+SIog#Up4AIKK&B7PULg`W`r3H(1{4)YUMufpBX?xxLwgv$hoK9PkbVT2!lT3= zMW*m`;y;J)=hXLe>JlD<_84UzqrS(eOL!dGc)5M>K|LIVb!LLaF3cB!X=oZ{QW?sQQS@0LkD-w`K znOF3tym^H@AMxIfRXoMLFgVfJ=4W69dq#Q=GE=S^6|r!8@}jLnd8 z97>#tg(g9!UdduR*U;F0gf^TP=gwkrIIn0Puh#~)dHso1x(%8@W$Gb*=_?(W2;|hE z(U)cUa>=)R>ca^iLcaK|{H8s~XFJyPi{y(hmwd}7`Ah^pu>In<@+a*K+^CfL;HjukIsxtwvp#)T(S^2IOt=pCPG_!H%f+j8Z3e0p@#1~k$64U?Q_ z!E+e#=Wx&#&tv4DXRFZSw@h??i<7W;f%FTcEq;e{`5n&NVh8CRq%D3=`uC(QcH%sC z;xu+L;n;~&+R3CxcnSJT@V>;v=OrdR!Yj~T!5Im!5`Pt4h1ZC`MjfxAtMEGM*WnTV zO#ILA2)mdJy-E5_c;93)@+R#R{sQeU$odQN|AIpl{tE9~(BFdhEqLF8Pk0;J+wi^( z@7wSR??8J8zIWhzhjzR}p77t${~H~IzZ3sEItcF(e-Hlm;D3)gh4-PoPrdI`?tSVM zc0=0@-){JJ!y`EIf_&-Vgn3{aZZQZ!3$d6E@x?=kPk2J%W`i*FFh)kuH-33sa3P;~ zKK%K7`ja0XZ%_cOfO-q4w}5(u7_=CCG5BKe35C!K;VXo%5FVijS`mCj@D=gtP*Hf2 zK{51VWDBLlOOY*<5if(k4E{3e6w0BMQ*Sx-mQ$w?hZct~4qqG|p#oY3d=>Cjz#~*b ztAwwT?+}Htq{kvtsDZbJKC!4JuNEDII^uPdtE2up>K4X98%LRO)IW~8h4IkF!#5tj z@$d-s(CXotP_r||?at4hpW5FFCtIB6i(8jQ58tb2Z@!d0n-W}0@D!djYgj#ncQxH8 z=czm{&rEg7;b+f2a7BDftvz+^4d=1OH~Z{*+KpFW#PI~0ef=u-_p5knU&Y={6;INu z32WHHsl~3RQ*ctitV9i(hf+ z!1ODp4m<%jvMgUN`Ib-pJhIDdI&Nw@=F(yY=^5e40g|MXVZP4xF_SM> zXHwo`7K3*-n1h+z3$wZp3o#G1gtg8*?!9^FF^>-c=kd|sJWRB(FEn93{2povYn=t~ zFMz(l#2X2l4HiNZJ`TT!I>I{VK===Yejx2S5Ho)uCSEuQns5kTWF87W$ujU$ls%k% z+9NRSM_|&AK&K=4AW=9H+L7FZN21%2^oQ_ic#nd96ud{ldlY=aB4~@?T?FqU_=LsK z7Q?p~`eJw%hnoyO1N}4fi*O9_W9TR0v&26O|7WT9v+xVYLOYi7$5QXH)F~VX?Kt?3 zgYP(agyW$d58v_d9S@Ii0<;t0I|05E=;ss26Fvw1bI2C{h4{Z9TlfO;FTno=`t1w2 zP~k*qCsOZ;lsl0+g?4D|@U_F&4v%mWw3FaF3BHry5jvoCz}LYb?0}~u>>4bAz69BV zwhxvfThP;r6#T+6;>(~3%Sl@>j2&5P`5yZhxLWMxD6UqXd2pc9dqYl+(0@>DoIz^2 zd5)k}uz3=#x?x7M4DXA((A%suSa1$b*GImM7QtfsAA~2BHV!)yu%^V;G#qWj~D@$YKEFG62s2hGrl0JX{xiYr=PF>m+tYAqwXQ|;;4E1HEVv%AWfg5#D({2 z=Uz??@X`E|u3p|7kqzLarQlhj;@<78(!qgCVj~f-DW0|h{vk|aA{sC$o@Vk7W->MP z>C-Q7dlItGBqPFEN`az0e9Y(tZblDMG@`+f_NIG3kSk zy)--n>>J?Qn80siUM((z|1#2-F>k&sJR8gd3&AJAq5$8a+;`x!xSVpAlfImCms9R? z%3V&m%PDs`f28V-V16)JdYba~6p0evHyPmS^ zDZ8Gs>nXdQvg;|kp0XDIM%jO(ti`pIy_T}qQubQPUQ5|)DSIttuchp@l(o2yve!}8 zVgqG2P<8`lH&Av1Wj9cE17$Z*b^~QCZh-d&rU5tLnr~okegp1UxCu9E!7wy)UHLWp z7r0SH7^6(@=AkD8o}&v0c$W^hiKK6uy2zXhj$ z3oW~a)z&R+%-zDuDHE`KB(0?AAV&_SjHj12nq%`_=10%;1B$$qUsI=`TMsCk}kq)EIK!a`5KyZ5#QYu7dY|4qT*I9 zZdgn?`Zk9}k85`S*r_WU<4he+%t*@9&k9>SYF zjKz6`yhqq(wAhLz+6sLu_xo0MR<>ffw&Dpae#W%&Q5Gt~W7Pc^*NUyr{J6eofD&VN9G6qvTS;+t-DuO z)h*(ExWZ5<9?XrjEM`sxUGu#OTv?3-I>l+(#Z_Z2sr*ENXB~gR6#N(ACeQ-f41P)P zJxQ-TNv}RhFJ|H?Dtd~%r|A8s$g_Bw9ua;`kN%n-wonuH+>?x=jGk;?x~g}iz1LlI zYHC&cKq|Pg{;ziEb~@f;oc9C^>tq0fw>e}Uf|4cI|r@g$d%UHje#O0WD zdWRf#UW8%I2kQy!l`(*Pu{m~@-ceGP-4U|lzPnJ z7Ku*f7*^hJ=C;A;VLam;`Oqrk$Wp@F*+E7zyElBgpw~htj43Hi#4vhPFBqf8t>Z1H zwA7OyjcUWfiVqvv%--s>2=*D-h&Z!miP#0U^}QTHy& z?}FEYOJE%QsBTrn?9rpgV#W_HPYvm0-hMN0OFK-u)5)BrjXENA|9VSgnWkJr&>z5_ z@BI*-b(StvFuJTuO^+t7EF(v5(M*5x*u&#+D7OG3n$PHyn?;+nJ0&4wT8Pm!pR;=I z(=8BP?y}H#tGvWK!mSo%xK;R#TcsZ3R+008Zj}#E3cV5b7Nf=DZLZ+kjD)wDG``J< z65e41zQYK!_-{sz#oyrn8&~{q)o^8xf7l>H0rmWXDQ zv5n*%0y)}|VHynPAT`bPMjjX`N3x6qA++J+Rg$%y(2&bGQ^lNGp|Q$o3f|nE^ZMqI z&OTB>K3sPP~(P%mCGR&a6j6GwYC74d$K>=Krd>tG9o}kTG3GD^80vMo}|C#l<`F zyvPt#?Z`Whe`KVtu{~>24xgtve$FCN_P8&5=)W6b^ii`t8O4rra|#Nlh}aWQLX3Fv zNl{7Afs-1c_Cbm}2IE=G=r?PKz0LZ-#l1Sk>|jEhVjj>aU1-v+oV;Y#5N|N5{q=-g zv`$S(^P_(;=R=!1ef}NXCH{IvUy$%wv2S3jO?^RKg}89!yT$B!j^SlW)+KInxzkWo ztU>G7i6JiL#Wb@6SYh6F2=UfK0WN<+QSlfCq|;bbS!nd!;@vfqxDzHZYAu>!YG$Nb zOy=$prf~O6;oM>i9e6nvS|U>7v=L6@{Nm|^GZ@V?sB;E)Rwk0rXQJp#?yNMLiBAhK z3!d4?-wW(bd@lTR;ZI{c@ks&pfoC4P`@y##@;%fOdiWUan2(+dxH}iZw-CJ+VjUK8 z-mFLUr{l!HgP2V^}zML!)ze;!639mciG#^LmfhiXELiNrk|!L|1=o-mD1(^tYG z>b5{!?mnX}{%Jw>Oj&arlKMC80cARjnGrcySsF{x(K+&XFb|LQzf68zU^@ zunCQk2x$oZq{+n-WgdI?IlRW_XtRfALSd%C=b03Ip0me(=Z^?Ob%q1?O!l9 zcu&=Drw*1>gG@UW$vUM4|I80pn8ul@?&V7UnR)=4`IVrE`|od@ixJ{|w#U(6vftCZ`6{0}rq1a1ewT#l zbWp8%sCN#0nvyhxF=Vt_$75=+>*QP+W&4)9jIw0w&=qG4gBwHET2yN$R;p6a<0i*{ z+~k(94Nyd#Od}jNhV%%^E#Va=Xd`)fifwo?t{7$+U>Vsq)oJ9Fenfd}9Q}{v@mA+Y zY9EiX{zvk7%w^(yREn}Kk&XlewKj>c>os+n6gbp7|Fg`+Oq{>+eQrvXQ$Xkwa>cW6`k!NuV z@l!!JhNXvTR1Z8o@b_T+gKH*EyzC@mI z7J0(C$UB$%Ext_rU%`3EJ0Je@;W?i=&ZoSv2HG0ZYa&y?bb|}1??UJo!h0ck7s4le z1=?506D}f8_!{!QM*S9Fhv)0`_17sUd;^|u;4HrpX$ zv$zuaS`6Y^c-G>O*J2dcMtEcunFaPS_#W-CUTVd}bLD^`kbO zWSs3gx0>nlmVNLmH|)=tQ>{2z%?a^F1S@d!D>rU6y;8%g>s8e?tyLYtd#Y}AzQbzX zr(;a$Y(aEBsWbE`{D_g4-o4u^U+nYnr~sa^rcP!)yd&#WW7KMJ0JUtc@lI!EU6qE9 z*|{&nVS73)x|YZVTlnweP*hS|LL zTjB7y$8pEA@~Fc2WwfOgd7e(0y0cymbjNYk@=6rCx9A;Xzie*Z^=_}^X6Vj!a;qp@ zgHf)f?mA}X)fi&){%PF2i`=qMb?ch&>ould#t-WkuW$GatD5%{YIv`=R`0BO{psap zwi%wftTvnXe<@eP_uPJa+$vu-uON7`>vPKXWbl4NAfHd8K9YPsx2l$W-sjDdAF5&o z%(as<`ZtW0a4kkrxDE#-Y=Cw>=^qlmf$&BQ-c1si)V3W z7SB=kIm%f)j}E`3Jug829rf>^T|2neJ8*nEBCQ6$N1s2y^CEdK5`U34yhxj0M8=Ca zw-<44!cOvcQg$cucakT(glvnKk?}IJEnY$PAECVpUIVWq{|)MRgF47clWQvc1^L2Tl(%5Go0Z9^pTd~arsTN- z#~b+7EDwye=Ws~g09?URqNk%jEWgH2BgoX_IeR%?#Lw&)9PC=|Jpv1UKg2k;*=DK0 z9+c;Wu(f*Wz)J*_*QKQK%Ziu0z_{skle@ z(>0XLwY+i7qaRM~=HYgxC)IR7Q)ul0Sx?H|w8M9A!TFHGJt-fi;q}Um)t2YL^Rce9 zUdeoELViPaO#wHHZ(*~&o;)R<#{ZH;mR09Ws;n%pvA&!+>C~`mYUIqxpqGsB*lu-6 zmbX^gdTaKys8e5TjP%xcks7?J|7A*xmJ+5eTUK73=QhT};ic<}0#DY$+{McEydwv? zX6he&`J&kXOOraz+mmOPXEtr)tv%DW>~g%p$CoSKS0Fqjkta5?)E~TwH%b}PzA8MO zDaV&D!Iw5(pUTs`%bhO#-N$=aYvFzF{`a}7h22;&{ib0EgrikXgx@=iM&tZcAs-$_ z%Xnp?!ikYCBwhrq7``!}1e8VDlZ0kb&Tl4`NBJNy+8SUiJRYhEJycPq8q|PVHbiSF zS4&=PlwWcKb)j4iza@*%#D^f6Qjj|kMVp=f_E~s$)wY$BR(-e3w2JR z>=fE+(Mp+C+SwZAx8jg*F_n6zMjHYo;7g!KBI<%E!!V8V(co^zuUz~lh?khc$IEap*WUugS*kAeA= zn@_pes}y~MqU82ic(g&Lc=tcy1L1Cu?}3roHs6pP16(B;%LVw8S{MuWN9K4N9$JhW|p6aSV2A&e(Ep zhKvUcgGMG0!mcR?qaxNYX^*T_V{&UX9}gl=PA&*yaj+fZk6r>$ri`(29Xx z^Qgc-xYt;8?+4#xxq}~a)7nPvDCid>esHMA{`!FTe*4nYGNU{@viuU8-^~!cgs#LA zv^&*wN|!!;ah%gnrj>@Uw9j0fW&E_PT+QI=T{^=-J8|7yF|!fowJLB#dwkz&AT`ME z2%Di}ZeyPmdS(^t*t@k9KtRzCpok z?`5A4N)ZXpT>H7q1R8k2pv@yKG_$=HPh5ku3IEZ!`5rUQ<4Wq%Dakq^xH4W*sdE)Q zT7g+(mK$1&Ph$hBl6Bx%)_|2f%`tfs0$ud^za}eQnVNuZ>f?MY9VOv%;%_3(8qaUT z@beT|pXoeB&QjzHerK$yAKlEEi>${gc^Wg8rzHOQiY?peAEUq3i?vvw*9n1qw*spl z==Evzz#CNJrA+-iIiil1L)8zWvu=Yv#eAcxH1CuTOY@v_SQ^h|>XDv!D*wzwQ|D}3 zC+o3cWg4>5yh}bTe?nH8=d8o>o3hewR+{(Ahw)F!N;hYvC!2ID=V^{;1Jg6p7AB)3 z^49URt)eCCT20~HM?8IHi@DA%nuX~aarA>z(F%R4p?hiZd1=E_k>L7zdlluCW2;&# z_1$oYQ+>IC;%!c2MN!-pPkb8j>3*CaV5|t6_;loG+%(2#RC-~u(jCiw2jBYUQ(t3z zW~Ey>i~QM@jTLh!gREd&uoq(L4pCny?#2`5>`=`xR>j5|td3PW!WUy~K!PvD%AK>w zI~&?L{Ick|q`yok{A+BSa~|pQ!5V&j^a4J^`U>%j;s0uk&xT@b77~6v#-<>3eUtRJ z_!R6?%6=PM2EGIT<&?dGGFMXmyTsQb>-)U4B>VuLb-c;BE><7l8u+e*b{%CbHt=1@ z^~kuMve%+`;c)TdRW{~ zefOiM#b)#q9zeze$Vj7=_{;zglK&w2X-pxW46p?`TVizv4^f}+Fl8R5Od6AkPY>`2 z`HzsFMuPb409(o5N`4v%;=E<*<7edmjQlj3iBAjgDEW_)pGGtBX#svt{?Ey`c#JkY zMjM33(O-DN$CHeSCmCzP(~Jkbr+r` z{VeHc;S+w#b$WsE_yX7Ih1i4)v=A3&8T<~O-@zm7AijgL!taUy9@-xmQ^JerDC|V` gPR5qSOI&x0Kcf2|Nn5;%POs8#;SKb$;P!R?AHm~I(EtDd diff --git a/parse.cm b/parse.cm index 77262d1f..addb6868 100644 --- a/parse.cm +++ b/parse.cm @@ -1493,6 +1493,22 @@ var parse = function(tokens, src, filename, tokenizer) { return functino_names[name] == true } + var derive_type_tag = function(expr) { + if (expr == null) return null + var k = expr.kind + if (k == "array") return "array" + if (k == "record") return "record" + if (k == "function") return "function" + if (k == "text" || k == "text literal") return "text" + if (k == "number") { + if (is_integer(expr.number)) return "integer" + return "number" + } + if (k == "true" || k == "false") return "logical" + if (k == "null") return "null" + return null + } + var _assign_kinds = { assign: true, "+=": true, "-=": true, "*=": true, "/=": true, "%=": true, "<<=": true, ">>=": true, ">>>=": true, @@ -1517,7 +1533,8 @@ var parse = function(tokens, src, filename, tokenizer) { function_nr: v.function_nr, nr_uses: v.nr_uses, closure: v.closure == 1, - level: 0 + level: 0, + type_tag: v.type_tag } slots = slots + 1 if (v.closure) close_slots = close_slots + 1 @@ -1648,13 +1665,26 @@ var parse = function(tokens, src, filename, tokenizer) { return null } + if (kind == "[") { + sem_check_expr(scope, expr.left) + sem_check_expr(scope, expr.right) + if (expr.right != null) { + if (expr.right.kind == "number" && is_integer(expr.right.number)) { + expr.access_kind = "index" + } else if (expr.right.kind == "text") { + expr.access_kind = "field" + } + } + return null + } + if (kind == "," || kind == "+" || kind == "-" || kind == "*" || kind == "/" || kind == "%" || kind == "==" || kind == "!=" || kind == "<" || kind == ">" || kind == "<=" || kind == ">=" || kind == "&&" || kind == "||" || kind == "&" || kind == "|" || kind == "^" || kind == "<<" || kind == ">>" || kind == ">>>" || kind == "**" || kind == "in" || - kind == "." || kind == "[") { + kind == ".") { sem_check_expr(scope, expr.left) sem_check_expr(scope, expr.right) return null @@ -1763,6 +1793,7 @@ var parse = function(tokens, src, filename, tokenizer) { if (r.level > 0) r.v.closure = 1 } else { expr.level = -1 + expr.intrinsic = true sem_add_intrinsic(name) } } @@ -1786,6 +1817,7 @@ var parse = function(tokens, src, filename, tokenizer) { var pname = null var def_val = null var sr = null + var tt = null if (kind == "var_list") { i = 0 @@ -1825,6 +1857,13 @@ var parse = function(tokens, src, filename, tokenizer) { } } sem_check_expr(scope, stmt.right) + if (name != null) { + tt = derive_type_tag(stmt.right) + if (tt != null) { + existing = sem_find_var(scope, name) + if (existing != null) existing.type_tag = tt + } + } return null } @@ -1902,6 +1941,9 @@ var parse = function(tokens, src, filename, tokenizer) { if (kind == "return" || kind == "go") { sem_check_expr(scope, stmt.expression) + if (stmt.expression != null && stmt.expression.kind == "(") { + stmt.tail = true + } return null } diff --git a/parse.mach b/parse.mach index d5dce8b25d314bd59e057066317277fb1e76800a..9e259f42347aedf8ab417b87e603737bb3e3989e 100644 GIT binary patch delta 10642 zcmb7~dwdnuoxsn`&CJ}q@7$XJf!v#1AR#0qApwN&3<(b{)<<=<#ZFf<3eWCckrmNO|wp!gj6rbx#i*FZStzxyU`XEv(N zS$Sp7R?3{CROj5Ws&n2r)j4~->YUT0I(wUyHNRT5&R2%j*QQ$gDChIjuT*uP+uzcc zP*z{Bvij$9%`l8UrSc1uvKFRP>jLTvmCE!lSlH5Eqp}O;A8k@k`SmJgpf{|u+uygK zrQcEc{;&9*8y2SeTKi4I=r@$z*Vo_t;m}aCiShoF%FREzzis|tRr1}0SEt^#MylUf zl_7PMLQ_Z=r7$PtFv`Q!Z>@@uI!a-J&pTGR3K&T#j0>4eIa$4nUO*jXo%)?;)vG;L zqbj#YsS2pFvuc{1SI5~ks=}#J|L2TSZZhGMPF5;SO>%_N)Fv%eo2>DvqtEE1163iT zC zGE+^Nu4fLcIkj-(mT{g`)~(#I&a4Q9#?=GsR<2#P+O%m{bKY6YSFSb75C_gZd0@@D zm8+;aQVnHaf9-iIO^e3UmaRT_rGI(#*{cRtU_>sjIdApq8I)3~P8cOE2~(K~rP{#D znyjD_gA1mAK_v|Tf+{g%ZD!b(h_xlcwi3T>0!CGd6}w&%Ubn-xq{=58l`S#s)`Ve* z6DpDPf=&rL>|7c%Ru(f>9$v4AwN?0SI+965xYKfDvV|7B4^bbtO}pPYu|4yr2fq+7@NE6-nTO9eb&hPR`YNQhi`5)Dhg+9URsePC+p7t z+RBsHEn8J+xNWS_QFv!_mr-BHUN_&#mfFdjlPsKcU9+GD(93Ya!y%E&M7zB zs7p>iw5lQN^9S<#N7n5}LQL^p&UC6dFT!qJpB58)2p^CRSLT=qOuo6zn{AaEDqCh&wkAzOX5=CEh#2FL$EoSKKLdIQb0*Bf z^*Od%WG**#9-~JzA~&jDa@otl(rY&qnU5V!pY4^ZJ{0>%cAvcnzC4V6@+T~?OCbX} zmyAseP7O4Qh5HBorj+IImeEiFdSPeH<;x}%T}yddG5I<%R%O! zGX5dvA3_5wSD#wA>(*-qmu#75RGX!~+Pbv^Yt9{5@j+qJZOfZ_8r*a;k>)r`x#^Zv znHrxm=2I#z}|S`*sNHrYK}x(=;)d@(tFCAv5dJ zkWPhzSSs(jDx0A_ok_L2hGC^#)so^6Or=%M%%qyx@glvNP|svnO4a%uL~Ki`!dY7% zJ!L|Q$?8s^Q_>COmY6&-Cb!1qwwOH0m&54y3r?c<+QTkY@%SyBpfLXSed--j4_(E@ z&d^59em0D1o)O*25_6a2!%bDYEvaPGFOf033LEbn?*#!)i8((tCQpmWhlHcqk(IRR zdmMQ*J?xWGU+4i2G^8_y-`p{#dq$|0K8RMC%BJY;q^h%(j_FUJ9e0j3dJ22*oYFC~ zrZtr?tnw6{jH;YDD|O(Zvxn#rvP@UzSqZr+ z38O4mR=Pw<-i?%@?CR=N^UFg+=*)}eWLo;pwcK*a_dLIEl>d4A`G?X(dQVAAc&iqme6VtQ_?V_9t$Fti<72h8K6GECOp6S&G#dC({ zQXIV0_T;BA#w6tF`|(eBtNCZL5io%=U=!Dc%W+D$62~GMku{yjnz`yqk5l|gxC(`c zMr2JVvSuD~K!N@djmVl#WX(L}fbY;hB99#4eTQd1&3EY((TJ?+MApnh4!D~B5o3`x z-N*sg&_{DEx8z!G$+g@P;d{2@@iPLXl2zX)b;4$pG}obY9eu9jx^O-9>#1Mw%UN{; z^&6<)KwbEE^o5(Sp}CnpH`C{4t_!y?7jET|=2n|;54SQYG+S7LTX=5S!jju!PxQGB zLz=A^7Ve@gq7hlsi5ze@eIlBW1GeGJHk{dpBilTF?!ln&LliXsiHG;%;k`H)=RO>~ z4+rnV!TWGfxF0>u1K1J%i#E+qu=OCe9>i9hhgkaFLp(D6l%@ADh9Ac8!x$DG!GPvx z7!-aN&*9B;tX<6xGO+^# zJ6PMouh=Mljn}`%sW>~)+e!9z675cVs?YN{pm~YwFJV{nGIn0U&MVj{;!$~(j;~_q zRSdm~A>lO)XY|57T)Go3GWc4=3U0vP0DwZ?Ks|h zMEf48dXIbl9+OG<9g3O{FeH3Po8}MH|A>Q{kBICeBGr71Gs0f%3ZF2r@G1R19BVBW+oq!st}(}=W46XjG-PV3TV0+JdC}n+(QW3TOgpA{ehiFF zl1ET0>$tZ6(C0eX$%~ySV#mrjN^DpO*AFD+Cb*7$H{rCnj`ZuyrakghnNNH_}%GH$DW^0YITulY~34=bf z{a`Y@jElW6t14X;I=>`v-W%pUk9~iU_kOnU{=m*Rk0$N%hUZ62s?!OlmN&eXSTNOo zFqR_!hRP)zSGjzqO_aOM+{b+*Ow{r~S>x2ITKarZr2fEiD_pTN11HIVJV)!>U~`T4 z*dS2vAos~smTTh>)3TU4-cS8W#2ZtTUUztU(_x>f`}Of(27-x~Vd91J&KoxIG6$M? zF-4iNfANGR@)|QEw?XDj_%;$aP*b$y4WIcn2XY|FKy4UZ<9^YRk;%b?Gs_#4FaIVh`W_zW-oFZpN#hp3$;CwoWWpLQw*!CEl zSIsXRoIm8Sj8M?aYM=XF7dq1;nK`&K6@1r7o1>%K!qLelGCEt%Eb}*(a@IUsqQ6ed znW>f-#K{WTu||s%&Fpf4BT-wdJ|=S1zg{o(gJ!_f#=kHF75k(=>qi_on+v1&m)x)n z5v?0VTOvWYeAVIaVGZb{GoI}E?2x=LHpZ4(J~SAe2P!=_Y%5pe)5cVbS5TQq!N$Vt zJ=>|p1XG!D`#3uO^Ypx!V$(i~&7tD3Lu20wq|b~4PTpv;^7$#+I|7}I+0yEI@x7UE zQGTa$RMz2byttDcXwRRx-%gkcn+hq92lIApY)79BBeae;T?UO^Oirup;W>~i9NxowoL?v<%74G3w{4~ zF;A+LXJz=tATv}SwS11j>$!4~$ujbDXRkkb>35LH%i!(NLN3od^xO*@q;UAiNb~0)V&tLVBqpoS-`K-}NL)|d) zPN~ZC9G7<*V4}}h^u}`jD-p(HNSJ`EX`wDmWH3!D4zxP_alvUR(#BvBdE~sBgd>x1 zc#_iyy|B1Q2mLi(2c0{do+6znb~+;=2mJlrX$$G04Cr2oZ7j^(=WH$-(D6Yn^(8RvK= z-tkPl>!wE3(cQz4!@jmU%njBCyyx-*FG3?dWG#PFFoa;DP^Jy-*3kdQ8kHIfsVBtaFL zvC{-Ipl=x8z?p9_sOFojBH6o^*1nobc- z$N|?d@HGs44cQT{W#DVcMnp5RFt^C}h+gGfB3b&vm+=ia!cJyvXFV0rlO)LvDxZKHG_2^8^`sg7Kap;3pifNj-^oPvPBD zco*ks(*HC@pJx3&jU&P{7}7k8-m^?T&Hu6kK1a0AVLZ+b`t4wn?;uM%*o1^%;keIi zgy)$!&$C1{FW}(|w7r0m@FJ&`@Dfo9FLVEV2H$<`G{cxvIOg%`{BC-@!I)Lp`uJtu zF1+4_!CmaxySS&joI{53I-b7Clg?WhdYiQWmPp^B{tko0d6(<&^5sd`jiV8Kea6$*(ageZWEa-;Fe(UCiKZQ0l<{mqEe zxbGrvoAujvzwPkbPQRVww{!ir%WvoT?R>vo;J4j=+tZ!xZg51l zCnfY=A@qepWEZ7Hwr`rq_D&bsg)>BUaicK$D@1d@(2Ri=(L6xg&(EL`6$8=1%s@mK z1B-<**v~ah(*}e{4+&um+oE}h<6$AR+jIY`e$h!t0Au)j2viFq9dz zM0#+Q+qq%b9%vraHEmE6=D@&U)0y${CY}C+wn+A`9BkGZ;8yrCok)O%Y*!gOcQTRMdX zcG75?NbV;)Qd&vw)Q+oiBRkXD?A(nzJG8poo}B}^(>oiT*T%x7~l2a*b$1SF7~p~I(#*2Bh$jT0LuwmAb@e7b1^Vy06kVmOk-$+(j2D$V){ zOfh3(iaAwO5kNHoC~D{{sWoduq>O#Tby2_NE3--y{x%V*W}1+G^;{PN1iU42zMrANNW}}6Y6X;nx~bU z*oL=&6mm*rsa@8rfrdQo*im$tPLb$9v4i4vm`mVO6X>K~k~wA(#32cNa8aHvoODt1 zE@lwLJkHHSPcffbNfyvY(Zg}iYWV{Ti85d&vd1Ef^qCe^LBeG*fdtecx8~_5(0*+7 zld(%~WOqs%&b__7A$RBQ$8z`Vxxa-}v6x|NeMXdf6VtV3Th}=59V^&pYoDBHGo42n!FFNPlcOOy7Lwy3xiTa(@;%+$ zuJ0Xod~dyzz4^zw^se(%w}6PwbagX!xr|Wh7%DfV0M#lsrS_SXI zXtPYar8%l;QM)3Vu?qudalIwCVl3G=TjfAZts%KBBxgf%yDMjG(>0ZQYJXK54?AVZ z*vwWR(`#p^uM)d}O0h`TdP{?4S0u_TT~;Xf>CtK0oZOd3=d{hOY_=nsQDW0cD^KcO z_WAuLL?TL-FOIe~&lAb`{K{l}K}4#`(+zgCB2i!)dXz{|eRN94ZzqsB_X)EXArYA7Onj@%A&jl2Fq~ZoV_DD<3wx-Dm zKW+Dl8Bwh@5i=Lc2okorD5)>9Y5S7;VmlaB{eSGOXqwe75{7PiWiTj1vrKrt5j9-* z@mfT9PeuoGXUFQxhU_Lo2+bZJPwJ()mPcxxCCq36N#vwh zN*15sd_WR8DVA}5IWyrW;bP{4PtpDf=PqHMUBSe@k{NOpdY5wT)69+|%nKvDMMjtv z6_+!wT+Tdkx!ELf#1)*lf{Eq|CK}0~qA0lvLjh@IkI!=cbMy~LBDdwant)aljA9MJ zt>NvnhACa~c>?utD#co|SWA>^alV!*cCC5w1g^oEWF7t1(Qh67*73$!XAb9CkCXLO zXua8(X9MRppr`l(dXhgU5XE&4C$59*$yoANWEYS^R%DSCeaIePqJKaNS&>CnEJpUY zf&Kw0;oN|NN0wHx7}?{mQ3yyQD>{%pzD%Eh6tW_VtXPcfaU=bG9E52hI*~nYqEkQ$ z*<&N)Wg}(XNYx}ab9^($H@k8|+(H#44*H5s^w~t8P4tm$=6Ey5n@vX^B*d+pxRn#P zx^hB%1p|_=VnlHpeQu-AZS;}c&g*syZ^|t!U&3r<)Nkc&xRp`9)tu$>ci2(Ig04$didV(eaw-HWj>-{o=pE(?)ejQ0C5d>@AI!?5Ij^c4?cPqK$| ziiga&Ble;Y&@A@hWFHyrW7)ZnD(qvq8RmODyOMtTIZC!isnk*K z`BBD_g3?VNdci0{r7AztS~0K0VdM2eqdMDhj(-k>Uyw}{*)cg60c z)}Cv6Vt-DnsmYyv;+EXXzpTpL{p2Y`tVej`meZC3iweC2ih*xDdKnY} zZ)M$*D@`p}6QGlh>T)F$@fINEYEj6_g*9?qk2J*MMpP%T*rYOu`;$L`(;@j8KSaKnDI(e zR2B&{7H3_@5}E!Lc7!E)g!NmbI1$r|y`@-@)N?wNgVs3NRTi+aE2&IWYQ@QTsk<?@|QRic2TH$BvtSHZ=i$}|EK}RScp$pDF*?>Ck5cBks3c=0KrK0_GuYAbxw9<+wN6F zE0$x(EAbR>Q&Gw$CU9Og$vM8AQu&xvozFRjyS>3#>W=;(m}>uaxWQc5dBsOLuMi36 zBc1cXl*efDJq?1X@q_7>mAP;*a(flkYlGVol}}BrlPKnSiWXp`*xxqG$Euj;+mHsm zEl^Cal|eKyA$ftg+b8#2=(sKj)V*FWkNg_R2&&fLpL0s&R&1^j(}Wxx-m~p)9kT{gtB9p0IZykFAEh^KlHPP*Z_=1>+|B$s z^Y(cMWDxxf-~$ZKn6AmAU)(;-pUR;E@Z>8}9x+!bZJ3QKa58MGnT@uR27;=g?SD z$grFyXSsp#E-3JK`kn#9w!LXtZgIVxzNas@xePk>wmUICPSEW4%j8?K>f8b8$Zsj( z^3(AGk0g&zbMW}E{|~ZkK6heq?KEFtUg2{9p9|&Z0EhQQ+HUc`2UxRZgbCYfP3oe}zEOdX3{2?3P|AF7Y z|8ng77R9ryuzts)<@d;naTYYf;=74eI|0oq5*mL+(5wcS<)T|r7AGDB{INh{ajYoh zSW;xg^AvM;i>ItHr;Ohy# zp5W^VKFm~y3sdvVLRR!4kK{>EZb_2A%_veBPmy7Y3{zy7va&wCw320crdyVnPNLIE zR5F8vB+g7SRb=Rtp;Lwql38?+%%+c`l|HTXY2~`4jUcj&lPuYXX=j|YGfvtGx}BsX z9oSLKL2s@VbNH#9Ko^Q#D0ZRPg_2|*ii!m&F5tSNn?GIka65XaOqgE!^;&h1h8CBF z+(N}-5?gFJ=XF0(wH;Hb!mCn6)xY~({OK^D!DwkOEVPFC)aJ3RwD{&QO6|Pp{YLzt& zI$bWsmEtm7U52a6@FW@Gc!U&3@FlsNI!Hd_p!6A^D=E~K467@xMpyuY6S#_CuEMF} zvy5@c=WrCzj_h$Y{jcWQ)!0+4;oKU|t>K)+xrWHDA+l?TQnHTYbsVpA<+S)S6xLIL z^;96t1}dJf^)Oq|-GZ$x4Baiv3X-kpDZYjs$=5lj*yfP+HnQGE#*%N)LGllD zQv4&wcM?=UEpj8whEC}AxtP}DoAkenU;>)Oc7ooHkL?Wh?F6?SJxBg1zQuzl`8FBe zP3ODGFw8v!bq`Oxw)D8lW>?AmFd?88O3iDm=#gSqc z721WvU1Th|kKpbnxcdn%%mbAF0gOJtBl!S4ET z<6cJYUdptWX+*M*$QAn;IQtp(icta_<=iMbl81RaNXE!g@(8z2!D7Pwk0g9_@c!ja z?kF7_Ji0{F7Us4bo6qmTV+~qQ?zLlIa}JQr0URA*GCsh)K48t8z++_a1WTDGaqzF) zj;E-=L5>gNJj@}kAF`&p{E$QfYLOdZ4s=7m&!x1NI`T*Sn^lqLX&gL*gJ*CM<}ejF zOaOtS@fPm?>Y3s96|30dPmSZ!tIhAMbE>j5YO`;9G++LQ2YncEB+Is zFJSZqjD|Um(c|2X<5cfB*-2i+sN!V?xa1YakmSDzKym_mC$Q&{6ep?~EGOVqIxFN& F`+uS4`9uH! diff --git a/qbe.cm b/qbe.cm index 6dbc6947..fa95e688 100644 --- a/qbe.cm +++ b/qbe.cm @@ -13,6 +13,11 @@ def js_true = 35 def js_exception = 15 def js_empty_text = 27 +// Shared closure vars for functions with >4 params +var _qop = null +var _qop2 = null +var _qflags = null + def int32_min = -2147483648 def int32_max = 2147483647 def mantissa_mask = 4503599627370495 @@ -398,18 +403,20 @@ var mod = function(p, ctx, a, b) { // ============================================================ // Helper: generate comparison for a given op string and int comparison QBE op -// null_true: whether null==null returns true (eq, le, ge) or false (ne, lt, gt) -var cmp = function(p, ctx, a, b, int_cmp_op, float_cmp_op_id, is_eq, is_ne, null_true) { +// reads _qflags = {int_cmp_op, float_id, is_eq, is_ne, null_true} from closure +var cmp = function(p, ctx, a, b) { + var int_cmp_op = _qflags.int_cmp_op + var float_cmp_op_id = _qflags.float_id var eq_only = 0 - if (is_eq || is_ne) { + var mismatch_val = js_false + var null_val = js_false + if (_qflags.is_eq || _qflags.is_ne) { eq_only = 1 } - var mismatch_val = js_false - if (is_ne) { + if (_qflags.is_ne) { mismatch_val = js_true } - var null_val = js_false - if (null_true) { + if (_qflags.null_true) { null_val = js_true } return `@${p}.start @@ -485,27 +492,32 @@ var cmp = function(p, ctx, a, b, int_cmp_op, float_cmp_op_id, is_eq, is_ne, null // MACH_EQ=0, NEQ=1, LT=2, LE=3, GT=4, GE=5 // null_true: eq, le, ge return true for null==null; ne, lt, gt return false var eq = function(p, ctx, a, b) { - return cmp(p, ctx, a, b, "ceqw", 0, true, false, true) + _qflags = {int_cmp_op: "ceqw", float_id: 0, is_eq: true, is_ne: false, null_true: true} + return cmp(p, ctx, a, b) } var ne = function(p, ctx, a, b) { - return cmp(p, ctx, a, b, "cnew", 1, false, true, false) + _qflags = {int_cmp_op: "cnew", float_id: 1, is_eq: false, is_ne: true, null_true: false} + return cmp(p, ctx, a, b) } var lt = function(p, ctx, a, b) { - return cmp(p, ctx, a, b, "csltw", 2, false, false, false) + _qflags = {int_cmp_op: "csltw", float_id: 2, is_eq: false, is_ne: false, null_true: false} + return cmp(p, ctx, a, b) } var le = function(p, ctx, a, b) { - return cmp(p, ctx, a, b, "cslew", 3, false, false, true) + _qflags = {int_cmp_op: "cslew", float_id: 3, is_eq: false, is_ne: false, null_true: true} + return cmp(p, ctx, a, b) } var gt = function(p, ctx, a, b) { - return cmp(p, ctx, a, b, "csgtw", 4, false, false, false) + _qflags = {int_cmp_op: "csgtw", float_id: 4, is_eq: false, is_ne: false, null_true: false} + return cmp(p, ctx, a, b) } var ge = function(p, ctx, a, b) { - return cmp(p, ctx, a, b, "csgew", 5, false, false, true) + _qflags = {int_cmp_op: "csgew", float_id: 5, is_eq: false, is_ne: false, null_true: true} } // ============================================================ @@ -627,7 +639,9 @@ var bnot = function(p, ctx, v) { // Both operands must be numeric. Int fast path, float -> convert to int32. // ============================================================ -var bitwise_op = function(p, ctx, a, b, qbe_op) { +// reads _qop from closure +var bitwise_op = function(p, ctx, a, b) { + var qbe_op = _qop return `@${p}.start %${p}.at =l and ${a}, 1 %${p}.bt =l and ${b}, 1 @@ -654,19 +668,24 @@ var bitwise_op = function(p, ctx, a, b, qbe_op) { } var band = function(p, ctx, a, b) { - return bitwise_op(p, ctx, a, b, "and") + _qop = "and" + return bitwise_op(p, ctx, a, b) } var bor = function(p, ctx, a, b) { - return bitwise_op(p, ctx, a, b, "or") + _qop = "or" + return bitwise_op(p, ctx, a, b) } var bxor = function(p, ctx, a, b) { - return bitwise_op(p, ctx, a, b, "xor") + _qop = "xor" + return bitwise_op(p, ctx, a, b) } // Shift ops: mask shift amount to 5 bits (& 31) -var shift_op = function(p, ctx, a, b, qbe_op) { +// reads _qop from closure +var shift_op = function(p, ctx, a, b) { + var qbe_op = _qop return `@${p}.start %${p}.at =l and ${a}, 1 %${p}.bt =l and ${b}, 1 @@ -694,15 +713,18 @@ var shift_op = function(p, ctx, a, b, qbe_op) { } var shl = function(p, ctx, a, b) { - return shift_op(p, ctx, a, b, "shl") + _qop = "shl" + return shift_op(p, ctx, a, b) } var shr = function(p, ctx, a, b) { - return shift_op(p, ctx, a, b, "sar") + _qop = "sar" + return shift_op(p, ctx, a, b) } var ushr = function(p, ctx, a, b) { - return shift_op(p, ctx, a, b, "shr") + _qop = "shr" + return shift_op(p, ctx, a, b) } // ============================================================ @@ -898,7 +920,9 @@ var gt_int = function(p, ctx, a, b) { return cmp_int(p, a, b, "csgtw") } var ge_int = function(p, ctx, a, b) { return cmp_int(p, a, b, "csgew") } // --- Comparisons (float path) --- -var cmp_float = function(p, ctx, a, b, op_id) { +// reads _qop from closure (op_id) +var cmp_float = function(p, ctx, a, b) { + var op_id = _qop return ` %${p}.fcr =w call $qbe_float_cmp(l ${ctx}, w ${op_id}, l ${a}, l ${b}) %${p}.fcrext =l extuw %${p}.fcr %${p}.fsh =l shl %${p}.fcrext, 5 @@ -906,15 +930,18 @@ var cmp_float = function(p, ctx, a, b, op_id) { ` } -var eq_float = function(p, ctx, a, b) { return cmp_float(p, ctx, a, b, 0) } -var ne_float = function(p, ctx, a, b) { return cmp_float(p, ctx, a, b, 1) } -var lt_float = function(p, ctx, a, b) { return cmp_float(p, ctx, a, b, 2) } -var le_float = function(p, ctx, a, b) { return cmp_float(p, ctx, a, b, 3) } -var gt_float = function(p, ctx, a, b) { return cmp_float(p, ctx, a, b, 4) } -var ge_float = function(p, ctx, a, b) { return cmp_float(p, ctx, a, b, 5) } +var eq_float = function(p, ctx, a, b) { _qop = 0; return cmp_float(p, ctx, a, b) } +var ne_float = function(p, ctx, a, b) { _qop = 1; return cmp_float(p, ctx, a, b) } +var lt_float = function(p, ctx, a, b) { _qop = 2; return cmp_float(p, ctx, a, b) } +var le_float = function(p, ctx, a, b) { _qop = 3; return cmp_float(p, ctx, a, b) } +var gt_float = function(p, ctx, a, b) { _qop = 4; return cmp_float(p, ctx, a, b) } +var ge_float = function(p, ctx, a, b) { _qop = 5; return cmp_float(p, ctx, a, b) } // --- Comparisons (text path) --- -var cmp_text = function(p, ctx, a, b, qbe_op, eq_only) { +// reads _qop (qbe_op) and _qop2 (eq_only) from closure +var cmp_text = function(p, ctx, a, b) { + var qbe_op = _qop + var eq_only = _qop2 return ` %${p}.scmp =w call $js_string_compare_value(l ${ctx}, l ${a}, l ${b}, w ${eq_only}) %${p}.tcr =w ${qbe_op} %${p}.scmp, 0 %${p}.tcrext =l extuw %${p}.tcr @@ -923,12 +950,12 @@ var cmp_text = function(p, ctx, a, b, qbe_op, eq_only) { ` } -var eq_text = function(p, ctx, a, b) { return cmp_text(p, ctx, a, b, "ceqw", 1) } -var ne_text = function(p, ctx, a, b) { return cmp_text(p, ctx, a, b, "cnew", 1) } -var lt_text = function(p, ctx, a, b) { return cmp_text(p, ctx, a, b, "csltw", 0) } -var le_text = function(p, ctx, a, b) { return cmp_text(p, ctx, a, b, "cslew", 0) } -var gt_text = function(p, ctx, a, b) { return cmp_text(p, ctx, a, b, "csgtw", 0) } -var ge_text = function(p, ctx, a, b) { return cmp_text(p, ctx, a, b, "csgew", 0) } +var eq_text = function(p, ctx, a, b) { _qop = "ceqw"; _qop2 = 1; return cmp_text(p, ctx, a, b) } +var ne_text = function(p, ctx, a, b) { _qop = "cnew"; _qop2 = 1; return cmp_text(p, ctx, a, b) } +var lt_text = function(p, ctx, a, b) { _qop = "csltw"; _qop2 = 0; return cmp_text(p, ctx, a, b) } +var le_text = function(p, ctx, a, b) { _qop = "cslew"; _qop2 = 0; return cmp_text(p, ctx, a, b) } +var gt_text = function(p, ctx, a, b) { _qop = "csgtw"; _qop2 = 0; return cmp_text(p, ctx, a, b) } +var ge_text = function(p, ctx, a, b) { _qop = "csgew"; _qop2 = 0; return cmp_text(p, ctx, a, b) } // --- Comparisons (bool path) --- var eq_bool = function(p, a, b) { diff --git a/qbe.mach b/qbe.mach new file mode 100644 index 0000000000000000000000000000000000000000..fd0219c9e33bcd2a0480f844d8b07610bdc11a48 GIT binary patch literal 40178 zcmeI5d2n6Vec#W+1Bi|LCQ=lyAQIFtE+R#VwpRNx7K-J8fDHm90E&_o zHjC3N-CJwR?o49Gc4w0O(LZV@@l5{dOs4Hjlj-tbr`y!s?KV+ctBu*u_bm6_3*Z5d z6a|SgALq-voqNvjoO{l1JHK;aj4?k3!mr55<({UUMB#`0fBky=WL+co zKR-V_b7gwEuF0KGj7-m$!-IvnE9JWN?m_vrvGS#bso9yj4gSW(OAD_LFO*+fs9VRK zsrlilnT5J$cRF+B;%Ir!=EDP9fC}S$bar;y=E->_PoKl3g}J&`pV`HWE_45$ElB4V z$_ut65v+5YVR#TS(1VHT*^!0*-nteNF3hHvN;*@%n&zQ%n}-V4MR-q*H!?n6r{nyU zQG2|2W!fIcr(Si}v*UGjJTG6ihnccHOfR^DySm`6E|l#XGvy2RI5jioaw?D2)sw^Y z%E zHFs_`Y=}-qC2{O-&~LbhC-Kw3xWF)x%i4W{E@yY!rex%*R9S2PO`28k+?g0}QA2jhZ51Dx1K@;yh zWa6d{6E`0=ac`%Ix5p-K?=tb>l8LuI58$liW{P&zAs7|P8ZH^;(Fcr!udTM8_!2c(`TZj z`BPES@??~>9*mOpk4DLcd!uCIeNnP$N0e;d6eU|e9VJ`0M9H?!DB1o(lSJ zIj}!U?mrYI4;+t@2MArC@D2XNq2LU z^z=qa@AfF^Ymbut!%;G@ElLKTh?1e_qvS|Slsp_o$s@a>mXdF3$GybU)`E-0ud1q<@Ti z$4PmH>ltu`yndYHm$>&9xqO@RKjPjWbN$aK>4%`L&}yClGvFQY-wQ3~&%plye*x;? z>5X7B!r~A(2}TeqUjSdK+iHG|<8N~B>l}Z#ZnODEoc~ks&$<3Lb(_q4pt-oo>;(^l zb6~W%#asZ>;4*j>ya9fyxY7I^&%ey^P4E_Y8@vPF1;0X?uNF6(uYunNUkBd+-vqw{ zzQw!W2EPyf0Q@2N4)`PR$KX$k+s(f!Hkp52gtp=a^KUu+JMt9%eQ~SBe<-$^?^4!( z> z^VP$LKpiN8da$m($uxo{&|KeaTEKd+f$NPNH*wqyws5|!e!bZac7UDWF0c#SUBA)n z0r!BtAOiP-`|4ZGe$WmMfcwD%;6ZS(euFs#I>2Gj31Uzpzi!Y2dO;uP2ZLY;JY2se z#k2HRNn@!j;pbH#-L`tl#O&Nf#84O4ZEXkM=(^Uq(Dvwb8?v_TjncJ_wo;33cVBCJ z^lIB!`SLUuJ*4Sq>uOQ94NV2p&|GI4S~#{AOlM=g*(dqk!1cODs!>;HDAt=pl&j%W zxWRwCVym!CC;nHZ3ctYPP=(U6b?BU$T2zga>Y!@4wRP7f7YuYbr=-M|?hn|mH9lCG)JCyo9Zk4FYW86?%hR2jXZdJI6 z@6f%r1EN=>+yAOC%C{N(o>5L+ytw?Z?v(wihgq)AjgB_z)7JL+nJ+q~>5_G{9ebni zTIcKKd7Z&C$0olpjP2QS>>$R;JKW$)$I4$dy{^ z4hdCQ32B63J3c%)K4-7tHk4RtP=i0+n{nVp`V zy=t#~n=QP4sZ2$>q0K#XfmMLxaIV^gW~E-y8ak~yh~6h&$_Yy;cD4zLs41$KemJinXc9&iuX3nFkYXao0w zePBOm2M56Y-~sR;cnBN>hd>873_3v!xe6;)#8+hBJ@_vfZ$u zDEi7M-&b(+w#sWYo* zKdKY|tJ1TNa^Lsttg@4}r>%me&Y4xPNbC&KoPMQ#Y4xj#x!H?y59^XIW-qPR78yz( z3XkmvrVCAFn6C6JXR=v6>#8l+F<$j_lDdv2hBn?de|2GYLj8{JGj)|GPMX`^?L@zN zb*6mVNkvi^l6Jt+vD4vNf}r%N8+6jJ?5)a>Rli0p`eJ!*Hq$cd;y$6Qb=NrR>prPf znjm#T@7W8w`ows7YpqkYr#h;BHF)4U zN#^P+(wX#|ZfhSNKJ)zWv*oKN-K_M3(@w3%>)d_qp%%?OH#9S1S{Q+?Mbp_-H2WI3 zxsH2{P3B+&Zsqz0Jmfd-RwfrC5A|nt;(t{>)OSgDOg{1Xg^@X@IKit%QtKaDgIJ}r zjaKgWruX?qOkhiDr=JbBIYPHQAelX?T%ACJUHX&~Vem^`jm|Di29a+i_sqiZrICe6 zB)*l~;nE6VNyHJ`Z>*gqW!cMo`1sUl<%#^;{t3j)UvW+#r@=zh)QG5)#GV?pqe_x` z+DYX3=>?4~tAJj?9VGqJjXX-9bguIG3oZ?9Lf%T#>(kI0v#(0BJ1v&NrxDm>N2!xj zX;Vo2&c13TIVIC}!?UlJ=TLR5e?$%sd+!VKLGVHRh1x)_j=8LMBk>CnmPGkF=5iyB z7a(a)Dh{X83{vI+MM~=$%KJ?qE4V%u(PcBRVi&&;3e>bypd@xf=?Gk9U$y%qemmM^ zL|Cai{*YS3>Lrr7!HGhPWxItd7E(us(S5sw3wU;W2#5 zKI)M^w#4>nh9xu^^?B09Ogz6ZH#KuXwd#{v%dua|n2se&xNEk4sPxU&4}GB=H9|j> z*$Pnbq6T}`M(QZ@{yyId{ZMIZw_@snkeW_nYwc6JChMo!Ly2m!0s3&rGi|Ve(A>PL z<{6*{R=*DoHa;~!cjeN8)O~7Hd1M=Fpf}T4L^-*Dj$yP!)n-6rUdMia6usmVy7bF% z!Z3-~u)fGd6`Xcs{gR2Qr4}D)t;>zH%UFEv_ZBt=ms|V3!q!{ia&vkXl3|IS1ua(3 zf|f_2|1oeB90QMoC%}{7I5@%kCrN*bv`=w;8k_-7bMGw2XSn|?$8+FQ;M3qa@H}_{ zyvX}6k@jWMeFnX*`JCCOSe-_mH8q=q>yS_l>&zjf*3C?Ou9fcfNhn7`ToU~$o%mlB ziT-WwfAS^z_|(LN6=lwG1z)XEr0d0w)ns}|^wA1+QDOPsMk?9kbtlPBGPr+qgcJhi1OnzH0_Kzi%wu;149>FM>u56y-Rbm*eKI&1TLe9 zYVx5*;h+~+6%HZMM=Kme#YZR{(&Fz3hpYs+N#Q_8y(1j3DsmRg9pP}Jf?&DyK{&|z z4ZkXu!#}t!eSyp_x+ojDJv5!SfJ;eEF6$wzxG6QCC*Pq@Pg-<0(pn~JWd!cSd}eqK zNZ_WeZElr0MmvpHc>(_(8k`c_>@;)7I+-q;Vfpe^DC>r@snP7*r$VNbNGhg+rRG?{ zq;*sF+PjU)uZ@h!86xjtc8loPsJnB}mPIvCuhA zm#9n!%N;m1Vsp3ibbR5=!wO+C5fp(o?x~mGZ``({EP`?8Q_fie%ghGg`*ZG4i~(}1 zxI=OF1-fMzEOCd*?-#5X`;98z&}S>oy`JE%gVx}PtDZXN3Y1GeXE*iJx}iil_xfqs zkf*wy^@hp=82p0j1o8k@j3e$;>?ZjEE01h@;|g5PZlZTs=bl{0a_(tB>nM6DLanX& z`WNACH)l5?c6MQd?BF=iH+X*0~4W z*15<1Wu1Gx`vPfSB;8B0xt_UWbKS7TwVa@>{hq|I?6oI!;(t{{^2}}N%*#trxaKEC zB#+Myv*7iE$YY;FDvZUDR}sZa=JOZyA}YD8j?21@N_i4;*2N|Ydx*baH7f<$sVyedIrLu)jPzI2vI zB7Bh`L543UNceEp#?pi~0tC;WJBnU6apzi!0eSlf5*WFS#(|kviNZWWHIP>bmD)WL2L22-Csb;QM zGiAG^UYh-|-CjXcXnsxNyDVn^ViWuq0GH_oyz7k-g@C@tM7&oZ#a(69-7!nA@@ z*l0+iBZ4XAA{t~wjV+EYcKn_iQqsQYyDC zAfyzZ_q@}k^nAC%>1AO$dn?Vc!gnbG2@?y0uMy0kokX1(&(&zT8 z!qF}JwU#NITzR`7kBTr7*G@CBmRKN9*=Ab8&DXe=aKYy3jLqVdX;X)=RM}*zj>pVw$VwY^!AgqFCqg6i zIR$5oj@XXxXd)1U8zExJMq{{GCi|j|F?LyssL||;A_}j93w5#~7kHrQ0U>r&84quGHopjOI_90DuSkuxAV@T%e_NQX)4hH*mP=qhBUkB}oa(SpR1}o#KGVeENzpyiX^~Hy% z0cEBie8^Wr5j=GLA!-pG+cq1bg(qC|I~psqe@F@`A49TU;b=oXRAtDAstkqDnjz>S zi$yMvAqeBHEu_>1RTJ4SQ|f{)_fqI!EX}BVn^KqP%72_v7mez_PN~ZeXED#pFX?F)S&{&wiqi+zo<~!u{;X)O~%Wk`|=cp{YAm z-}xCz^75q_=u4Fpx34tmw<^iqcX4W-mH%Tdl#nW=DO^iR6wUKxDivgFqg9(1eS35sS*-VDkdz%FG)O+ z2y!)kI1t^dbfPePWyz_9+*ek_pnEI*%J}`#5m-{pAa6MC=QwUjp@ZQoOH`!|V!twy zs=}#D$SOK@NzOm}%GXd++FJ^H_cpb-V`EF9z*a%+4TZk;b)c1JEv^136`|El==iSqly#Y{4?J7qU(`8pV;>Ae4GNiSu97((ED#xZ9T;)i9c9kRf zvC8qryfW*mgj|tN&MU*U^~!Key)u-W3%R2FG~|kOFHw(|ITCW!@L7<6^Wew8=fEpq z82lwL0!GPijN>@@lsQg-3t$pVfzN|4fN5|MLtv)SG|ckOCDLE!-W-?*3*ZWP6`U!^{OjpJVj ze*^qYz{0qOzYYEl_`BfC;7#yWgK2mh17+jSn|-Y)(CcaE4I9nDRzl4PnLfmNK82dS zTi9;#i-p}r_@%-Y^UH;u=2zJA=vNDk8SLWR;nxbA%vX38@Ehdun_LI{R$=oBCPD4q zojWOibX$O~X$?m$I}JZ?ILhj?d%B_HU;SVf% z?-w?LEnp|uor7vG6MDs#VDG(K-4X>_q|UcM(YHXozpkw%{+yQhb6UcogQL*zah#*@ zVv6@@iT7xU_h^atXo>e|i7ZxY34O>hqUmpWQ`F^}B6JFOtgR`2KvVpHrf@jSQ5f(z z!BKcQ#rrhH`!vP-G{yTg#rrfx7Bx1-t?w1ZWt+l{m)e@*hcv|xX$ptK9EAao6C8z? zQ(UJhuG19PX^QJK#dVq@i`tsv!maNW^~*NJjr58l`+*kg?)Es$Q5f(z!BKcQMLlD@ zzHTGf0(OGkIfSN=Yc2e;Ow$dJ3m>#sXcN;s*KAm}DYCsnx<(V#ZKApkhdBxZ9w#^o zFXy0Ys0v!k7k<7KMuB&!WEo*o z$Ws}-H&_)JZ}~B8Rudjun+QWQv!ohBEw~o!QmmY-4s#+bsm2_m#EVmGaA18{)|`Q` zDI5>lrG~7%6c8GqiWpn z_A|WGMVT08olOtjvK5gZSCv%Ep+-h=<&(oGMqaCxQH;EnGK!JcR$&yG1hp~>Hc=dJ zbDIn}MgapDF4+we7~40-FG@GYFZ|X9mRSTQ`w{UAHm3fN`Nh^d{NmPdpdU?s@v%`X zEWdDDm#j6v@EfLl9QXyh#(c>9;*NfCi(HX`U&v(?epL*Sukc(dEj?_MS&z!32s3Rj z%s#=)r=13II%Nn6Lc5lpT~Yf{V)b^I>%bP7DI@JG(;R5AykP}2T6K1Y1I?PWVR9oo zwE;33JO{fwJ^X5LLbqOud5{p@jE zl^tQthXO5pKUBALffja=2pv`45f|XE9+}-TMqw8IHI0$coVSB6nF&mC`>$jJlU$62 zlmcFK<_Bdjp5wL4JpAMA;sv+7Bfoe-o`x_AC_QZvo4K3}A&fG|sZY;WVj?gn+_bb@ z4nEeW`eq+paA>mbhIfX4bp9SkK_-5RztO2mw zx><*YduS_$m!jgNQTznErd^$yFAu*#tg41N(WrKq+r4CT?cz7n9b^XR*i3c|X{&xG z9vGf>Uq_}S{l1RXH`XO>&a=Tg=};%XHl`;T>Z@<8OFoB4*TMTMY^>YROMZQ@kbcq+ zkaiF@($Z=6$rWn<>_Ce-*vQl;e`eqilR9!o{tSQJqT2Aj-S3MhRhcx>7Hak{A>k6W zb4o~c=Cy|S)Qv06YyCRTXW=UrJ2>tFdqG$K`7Bm3k1PZX+b^0tCAM- zP*;@}TB?wJ=%mHsHDmLWQxglwiXJB`{9-YLgOeHk&JzxCwymTFeR#40{%>UkJiaC` zI5NP>3gke{yIsJBCvnV$Cz0O(GGmbRL!><-nbEbP%=o=eu*?WDPwZJmu7#{Xag^6727@1Y8*U% z>$4R{k599v!zbO1Y%Rx6TIiht6y)s3DIYB2qO2h%^Jz2z786MdH1ZYQ@{xKur#nB|5xp;c8hjw(Cn zxIa#bPLtbdr{ar6ugRpge}*#MW=Fc6cF?iqxNUZw*+fTfVZWUZ8mBK?I^E(;bfn!~ zwmGou$La`^)9BT5@-E&)M+UyHx2$?gg^HGMH@|s~9J=1OZiCHoo9D=W_XKIHpX}y2 z+VID1o}<;Ke)Ak`_7zoD7M?nlRXYf1^BnvK;S0&GU%G}L6><}>Dl00ijc((5QFL2Y zWo_TX<}M%dA6Hwt>TX~VBR0UllX+XY;D^Cy7y|8;{%+ zugYvXqUc$>s@`onQk7XPu~5aV9LsS7(;KKThhU&OE@fFoJ0EJT2ViIOJ-5|hQD0U^ zR$GN%m5$8{y&!?J(b+lwNp`jpe(FQB`D#ldvn78OnB8BA%Z7zk!m{C_&b0wMcb*OU z%&e0)60iB-Yl&AM?kqP-F8Ep^)(2nHZ&zgJxKp# zogZ-XWdjFz-Uc$xkIp9kQrsE^tK1AH&c34#D+fjme+sCFWKMmobb!jjc>lK>ZAJ>5=9z=;a5d8$%9#abo-SkX)V^~&tBEQl=_w-dzrt!lM?=sbfSAg+jFea z{NGLMf#qfGxW#{e*jmp%GAcK-l8=j$QsBa(WOe3ZpQIePJ(-Zies2q+Gu_mRpUHM+ zw-4___%7Yu0^u9>wm>V$#(heq&221$E}*Koz(PuQv7dKS)rvqsbZ$T_FEbC(unpsG z1*1-XF}t&Q>n#-|SzS#MI-o0Rn?WxYvRZ&FqktCi)Jbh75Tmcrhmu(v4e zEed;!!rr2=ENU#Qwwk?7VQ*8|+Z6UTg}qH-S=3e->vn6Y**g^W4u!o#Vee4bI~0~h zjfK@#vv(=%T?%`b!rrB@cPT83P?-LhcKB7{I={$s$Mjd(?@0Vp8@L6v&P8GPI75d0 z(yfl*K4GUL83I{80r$!8a6H0wy*xW;VeWs;KOp>@BiV(A}>V zH<=q5*Q=#f`&Jo?dL(vGs06sZyi$rk_r@&coE{B?m ztgoraZ&0Me5st^fDR35?%b}(s8)_=@n-uAAgyV5=3Y-P!atK9A3Wr}6?(h$J?zuzl zyK617|18@GJITT*!k$`a2KJANX7|&AFD%_n3*SY$mlp1e{8m~xF&6KnmC-oRRQx_# zyQqm+30qb@#P~{i z{@vnc^LxcDx0JnP*$+ij8}~UC!|M8;=M)+&BY23quV0Dxe5)86hoc;Y)8IMqB1pjJ za;S+GRFIpY<=fEWaFnBP8axMH1PS>9i%;J`*jhH`x7>8pVg)`uJ@Dex=Udf?0Vo-5zre*#RVjPZf6wZL>!AsygcqNA# ziMhF!`3}T59OEdQ0ndY%zvuHuL={q#AD9{{f?03%Q(2C5TOpanYU%k zBupc3CBgKv$`5)`&PH-esH}+u|8o}77Rt>2N$IS^vNH9B^}`?U{IDI3YMq;Is+sbg Ordl=%|4y$m=KljH^9?ru literal 0 HcmV?d00001 diff --git a/qbe_emit.cm b/qbe_emit.cm new file mode 100644 index 00000000..bf94d71c --- /dev/null +++ b/qbe_emit.cm @@ -0,0 +1,667 @@ +// qbe_emit.cm — mcode IR → QBE IL compiler +// Takes mcode IR (from mcode.cm) and uses qbe.cm macros to produce +// a complete QBE IL program ready for the qbe compiler. +// qbe module is passed via env as 'qbe' + +var qbe_emit = function(ir, qbe) { + var out = [] + var data_out = [] + var str_table = {} + var str_id = 0 + var uid = 0 + + // ============================================================ + // Output helpers + // ============================================================ + + var emit = function(s) { + push(out, s) + } + + var fresh = function() { + uid = uid + 1 + return "u" + text(uid) + } + + var s = function(n) { + return "%s" + text(n) + } + + var sanitize = function(lbl) { + var r = replace(lbl, ".", "_") + r = replace(r, "-", "_") + r = replace(r, " ", "_") + r = replace(r, "/", "_") + r = replace(r, "<", "") + r = replace(r, ">", "") + r = replace(r, "(", "") + r = replace(r, ")", "") + return r + } + + // ============================================================ + // String interning — emit data section entries + // ============================================================ + + var intern_str = function(val) { + if (str_table[val] != null) return str_table[val] + var label = "$d_str_" + text(str_id) + str_id = str_id + 1 + var escaped = replace(val, "\\", "\\\\") + escaped = replace(escaped, "\"", "\\\"") + var line = "data " + label + ' = ' + '{ b "' + escaped + '", b 0 }' + push(data_out, line) + str_table[val] = label + return label + } + + // ============================================================ + // Extract property name from mcode operand + // ============================================================ + + var prop_name = function(a) { + if (is_text(a)) return a + if (is_object(a)) { + if (a.name != null) return a.name + if (a.value != null) return a.value + } + return null + } + + // ============================================================ + // Compile one function's instructions + // ============================================================ + + var compile_fn = function(fn, fn_idx, is_main) { + var instrs = fn.instructions + var nr_slots = fn.nr_slots + var nr_args = fn.nr_args + var name = is_main ? "cell_main" : "cell_fn_" + text(fn_idx) + name = sanitize(name) + var i = 0 + var instr = null + var op = null + var a1 = null + var a2 = null + var a3 = null + var a4 = null + var p = null + var pn = null + var sl = null + var fop_id = 0 + + // Function signature: (ctx, frame_ptr) → JSValue + emit(`export function l $${name}(l %ctx, l %fp) {`) + emit("@entry") + + // Load all slots from frame into SSA variables + // Each slot is a JSValue (8 bytes) at fp + slot*8 + var off = 0 + i = 0 + while (i < nr_slots) { + off = i * 8 + emit(` %p${text(i)} =l add %fp, ${text(off)}`) + emit(` ${s(i)} =l loadl %p${text(i)}`) + i = i + 1 + } + + // Walk instructions + i = 0 + while (i < length(instrs)) { + instr = instrs[i] + i = i + 1 + + // Labels are plain strings + if (is_text(instr)) { + emit("@" + sanitize(instr)) + continue + } + + op = instr[0] + a1 = instr[1] + a2 = instr[2] + a3 = instr[3] + + // --- Constants --- + + if (op == "int") { + emit(` ${s(a1)} =l copy ${text(a2 * 2)}`) + continue + } + if (op == "null") { + emit(` ${s(a1)} =l copy ${text(qbe.js_null)}`) + continue + } + if (op == "true") { + emit(` ${s(a1)} =l copy ${text(qbe.js_true)}`) + continue + } + if (op == "false") { + emit(` ${s(a1)} =l copy ${text(qbe.js_false)}`) + continue + } + if (op == "access") { + if (is_number(a2)) { + if (is_integer(a2)) { + emit(` ${s(a1)} =l copy ${text(a2 * 2)}`) + } else { + emit(` ${s(a1)} =l call $__JS_NewFloat64(l %ctx, d d_${text(a2)})`) + } + } else if (is_text(a2)) { + sl = intern_str(a2) + emit(` ${s(a1)} =l call $JS_NewString(l %ctx, l ${sl})`) + } else if (is_object(a2)) { + if (a2.make == "intrinsic") { + sl = intern_str(a2.name) + emit(` ${s(a1)} =l call $cell_rt_get_intrinsic(l %ctx, l ${sl})`) + } else if (a2.kind == "number") { + if (a2.number != null && is_integer(a2.number)) { + emit(` ${s(a1)} =l copy ${text(a2.number * 2)}`) + } else if (a2.number != null) { + emit(` ${s(a1)} =l call $__JS_NewFloat64(l %ctx, d d_${text(a2.number)})`) + } else { + emit(` ${s(a1)} =l copy ${text(qbe.js_null)}`) + } + } else if (a2.kind == "text") { + sl = intern_str(a2.value) + emit(` ${s(a1)} =l call $JS_NewString(l %ctx, l ${sl})`) + } else if (a2.kind == "true") { + emit(` ${s(a1)} =l copy ${text(qbe.js_true)}`) + } else if (a2.kind == "false") { + emit(` ${s(a1)} =l copy ${text(qbe.js_false)}`) + } else if (a2.kind == "null") { + emit(` ${s(a1)} =l copy ${text(qbe.js_null)}`) + } else { + emit(` ${s(a1)} =l copy ${text(qbe.js_null)}`) + } + } else { + emit(` ${s(a1)} =l copy ${text(qbe.js_null)}`) + } + continue + } + + // --- Movement --- + + if (op == "move") { + emit(` ${s(a1)} =l copy ${s(a2)}`) + continue + } + + // --- Arithmetic (int path) — use qbe.cm macros --- + + if (op == "add_int") { + p = fresh() + emit(qbe.add_int(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "sub_int") { + p = fresh() + emit(qbe.sub_int(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "mul_int") { + p = fresh() + emit(qbe.mul_int(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "div_int") { + p = fresh() + emit(qbe.div_int(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "mod_int") { + p = fresh() + emit(qbe.mod_int(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + + // --- Arithmetic (float path) --- + + if (op == "add_float") { + p = fresh() + emit(qbe.add_float(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "sub_float") { + p = fresh() + emit(qbe.sub_float(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "mul_float") { + p = fresh() + emit(qbe.mul_float(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "div_float") { + p = fresh() + emit(qbe.div_float(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "mod_float") { + p = fresh() + emit(qbe.mod_float(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + + // --- String concat --- + + if (op == "concat") { + p = fresh() + emit(qbe.concat(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + + // --- Type checks — use qbe.cm macros --- + + if (op == "is_int") { + p = fresh() + emit(qbe.is_int(p, s(a2))) + emit(qbe.new_bool(p + ".r", "%" + p)) + emit(` ${s(a1)} =l copy %${p}.r`) + continue + } + if (op == "is_text") { + p = fresh() + emit(qbe.is_imm_text(p, s(a2))) + emit(qbe.new_bool(p + ".r", "%" + p)) + emit(` ${s(a1)} =l copy %${p}.r`) + continue + } + if (op == "is_num") { + p = fresh() + emit(qbe.is_number(p, s(a2))) + emit(qbe.new_bool(p + ".r", "%" + p)) + emit(` ${s(a1)} =l copy %${p}.r`) + continue + } + if (op == "is_bool") { + p = fresh() + emit(qbe.is_bool(p, s(a2))) + emit(qbe.new_bool(p + ".r", "%" + p)) + emit(` ${s(a1)} =l copy %${p}.r`) + continue + } + if (op == "is_null") { + p = fresh() + emit(qbe.is_null(p, s(a2))) + emit(qbe.new_bool(p + ".r", "%" + p)) + emit(` ${s(a1)} =l copy %${p}.r`) + continue + } + if (op == "is_identical") { + p = fresh() + emit(qbe.is_identical(p, s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + + // --- Comparisons (int path) --- + + if (op == "eq_int") { + p = fresh() + emit(qbe.eq_int(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "ne_int") { + p = fresh() + emit(qbe.ne_int(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "lt_int") { + p = fresh() + emit(qbe.lt_int(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "gt_int") { + p = fresh() + emit(qbe.gt_int(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "le_int") { + p = fresh() + emit(qbe.le_int(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "ge_int") { + p = fresh() + emit(qbe.ge_int(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + + // --- Comparisons (float/text/bool) --- + + if (op == "eq_float") { + p = fresh() + emit(qbe.eq_float(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "ne_float") { + p = fresh() + emit(qbe.ne_float(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "lt_float" || op == "gt_float" || op == "le_float" || op == "ge_float") { + p = fresh() + fop_id = 0 + if (op == "lt_float") fop_id = 2 + else if (op == "le_float") fop_id = 3 + else if (op == "gt_float") fop_id = 4 + else if (op == "ge_float") fop_id = 5 + emit(qbe.cmp_float != null ? cmp_float(p, "%ctx", s(a2), s(a3), fop_id) : ` %${p} =l call $qbe_float_cmp(l %ctx, w ${text(fop_id)}, l ${s(a2)}, l ${s(a3)})`) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "eq_text") { + p = fresh() + emit(qbe.eq_text(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "ne_text") { + p = fresh() + emit(qbe.ne_text(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "lt_text" || op == "gt_text" || op == "le_text" || op == "ge_text") { + p = fresh() + emit(` ${s(a1)} =l call $cell_rt_${op}(l %ctx, l ${s(a2)}, l ${s(a3)})`) + continue + } + if (op == "eq_bool") { + p = fresh() + emit(qbe.eq_bool(p, s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "ne_bool") { + p = fresh() + emit(qbe.ne_bool(p, s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "eq_tol" || op == "ne_tol") { + emit(` ${s(a1)} =l call $cell_rt_${op}(l %ctx, l ${s(a2)}, l ${s(a3)})`) + continue + } + + // --- Boolean ops --- + + if (op == "not") { + p = fresh() + emit(qbe.lnot(p, "%ctx", s(a2))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "and") { + emit(` ${s(a1)} =l and ${s(a2)}, ${s(a3)}`) + continue + } + if (op == "or") { + emit(` ${s(a1)} =l or ${s(a2)}, ${s(a3)}`) + continue + } + + // --- Bitwise ops — use qbe.cm macros --- + + if (op == "bitnot") { + p = fresh() + emit(qbe.bnot(p, "%ctx", s(a2))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "bitand") { + p = fresh() + emit(qbe.band(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "bitor") { + p = fresh() + emit(qbe.bor(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "bitxor") { + p = fresh() + emit(qbe.bxor(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "shl") { + p = fresh() + emit(qbe.shl(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "shr") { + p = fresh() + emit(qbe.shr(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + if (op == "ushr") { + p = fresh() + emit(qbe.ushr(p, "%ctx", s(a2), s(a3))) + emit(` ${s(a1)} =l copy %${p}`) + continue + } + + // --- Property access — runtime calls --- + + if (op == "load_field") { + pn = prop_name(a3) + if (pn != null) { + sl = intern_str(pn) + emit(` ${s(a1)} =l call $cell_rt_load_field(l %ctx, l ${s(a2)}, l ${sl})`) + } else { + emit(` ${s(a1)} =l call $cell_rt_load_dynamic(l %ctx, l ${s(a2)}, l ${s(a3)})`) + } + continue + } + if (op == "load_index") { + emit(` ${s(a1)} =l call $cell_rt_load_index(l %ctx, l ${s(a2)}, l ${s(a3)})`) + continue + } + if (op == "load_dynamic") { + emit(` ${s(a1)} =l call $cell_rt_load_dynamic(l %ctx, l ${s(a2)}, l ${s(a3)})`) + continue + } + if (op == "store_field") { + pn = prop_name(a3) + if (pn != null) { + sl = intern_str(pn) + emit(` call $cell_rt_store_field(l %ctx, l ${s(a1)}, l ${s(a2)}, l ${sl})`) + } else { + emit(` call $cell_rt_store_dynamic(l %ctx, l ${s(a1)}, l ${s(a2)}, l ${s(a3)})`) + } + continue + } + if (op == "store_index") { + emit(` call $cell_rt_store_index(l %ctx, l ${s(a1)}, l ${s(a2)}, l ${s(a3)})`) + continue + } + if (op == "store_dynamic") { + emit(` call $cell_rt_store_dynamic(l %ctx, l ${s(a1)}, l ${s(a2)}, l ${s(a3)})`) + continue + } + + // --- Closure access --- + + if (op == "get") { + emit(` ${s(a1)} =l call $cell_rt_get_closure(l %ctx, l %fp, l ${text(a2)}, l ${text(a3)})`) + continue + } + if (op == "put") { + emit(` call $cell_rt_put_closure(l %ctx, l %fp, l ${s(a1)}, l ${text(a2)}, l ${text(a3)})`) + continue + } + + // --- Control flow --- + + if (op == "jump") { + emit(` jmp @${sanitize(a1)}`) + continue + } + if (op == "jump_true") { + p = fresh() + emit(` %${p} =w call $JS_ToBool(l %ctx, l ${s(a1)})`) + emit(` jnz %${p}, @${sanitize(a2)}, @${p}_f`) + emit(`@${p}_f`) + continue + } + if (op == "jump_false") { + p = fresh() + emit(` %${p} =w call $JS_ToBool(l %ctx, l ${s(a1)})`) + emit(` jnz %${p}, @${p}_t, @${sanitize(a2)}`) + emit(`@${p}_t`) + continue + } + if (op == "jump_null") { + p = fresh() + emit(` %${p} =w ceql ${s(a1)}, ${text(qbe.js_null)}`) + emit(` jnz %${p}, @${sanitize(a2)}, @${p}_nn`) + emit(`@${p}_nn`) + continue + } + if (op == "jump_not_null") { + p = fresh() + emit(` %${p} =w cnel ${s(a1)}, ${text(qbe.js_null)}`) + emit(` jnz %${p}, @${sanitize(a2)}, @${p}_n`) + emit(`@${p}_n`) + continue + } + if (op == "wary_true") { + p = fresh() + emit(` %${p} =w call $JS_ToBool(l %ctx, l ${s(a1)})`) + emit(` jnz %${p}, @${sanitize(a2)}, @${p}_f`) + emit(`@${p}_f`) + continue + } + if (op == "wary_false") { + p = fresh() + emit(` %${p} =w call $JS_ToBool(l %ctx, l ${s(a1)})`) + emit(` jnz %${p}, @${p}_t, @${sanitize(a2)}`) + emit(`@${p}_t`) + continue + } + + // --- Function calls --- + + if (op == "frame") { + emit(` ${s(a1)} =l call $cell_rt_frame(l %ctx, l ${s(a2)}, l ${text(a3)})`) + continue + } + if (op == "setarg") { + emit(` call $cell_rt_setarg(l ${s(a1)}, l ${text(a2)}, l ${s(a3)})`) + continue + } + if (op == "invoke") { + emit(` ${s(a2)} =l call $cell_rt_invoke(l %ctx, l ${s(a1)})`) + continue + } + if (op == "goframe") { + emit(` ${s(a1)} =l call $cell_rt_goframe(l %ctx, l ${s(a2)}, l ${text(a3)})`) + continue + } + if (op == "goinvoke") { + emit(` call $cell_rt_goinvoke(l %ctx, l ${s(a1)})`) + continue + } + + // --- Function object creation --- + + if (op == "function") { + emit(` ${s(a1)} =l call $cell_rt_make_function(l %ctx, l ${text(a2)})`) + continue + } + + // --- Array push/pop --- + + if (op == "push") { + emit(` call $cell_rt_push(l %ctx, l ${s(a1)}, l ${s(a2)})`) + continue + } + if (op == "pop") { + emit(` ${s(a1)} =l call $cell_rt_pop(l %ctx, l ${s(a2)})`) + continue + } + + // --- Misc --- + + if (op == "return") { + emit(` ret ${s(a1)}`) + continue + } + if (op == "disrupt") { + emit(` call $cell_rt_disrupt(l %ctx)`) + emit(` ret ${text(qbe.js_null)}`) + continue + } + if (op == "delete") { + emit(` ${s(a1)} =l call $cell_rt_delete(l %ctx, l ${s(a2)}, l ${s(a3)})`) + continue + } + if (op == "typeof") { + emit(` ${s(a1)} =l call $cell_rt_typeof(l %ctx, l ${s(a2)})`) + continue + } + + // --- Unknown opcode --- + emit(` # unknown: ${op}`) + } + + emit("}") + emit("") + } + + // ============================================================ + // Main: compile all functions then main + // ============================================================ + + var fi = 0 + while (fi < length(ir.functions)) { + compile_fn(ir.functions[fi], fi, false) + fi = fi + 1 + } + + compile_fn(ir.main, -1, true) + + // Assemble: data section first, then function bodies + var result = [] + var di = 0 + while (di < length(data_out)) { + push(result, data_out[di]) + di = di + 1 + } + if (length(data_out) > 0) push(result, "") + + di = 0 + while (di < length(out)) { + push(result, out[di]) + di = di + 1 + } + + return text(result, "\n") +} + +return qbe_emit diff --git a/qbe_emit.mach b/qbe_emit.mach new file mode 100644 index 0000000000000000000000000000000000000000..a3bccfc254561e8ad1e488a5998bff075eff3857 GIT binary patch literal 28132 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 f2jYr?= argc) { printf("ERROR: --shop requires a path argument\n"); @@ -416,6 +421,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, "use_mcode", JS_NewBool(ctx, use_mcode)); + 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)); JS_SetPropertyStr(ctx, hidden_env, "nota", js_nota_use(ctx)); diff --git a/streamline.cm b/streamline.cm new file mode 100644 index 00000000..2606395f --- /dev/null +++ b/streamline.cm @@ -0,0 +1,351 @@ +// streamline.cm — mcode IR optimizer +// Single forward pass: type inference + strength reduction + +var streamline = function(ir) { + // Type constants + var T_UNKNOWN = "unknown" + var T_INT = "int" + var T_FLOAT = "float" + var T_NUM = "num" + var T_TEXT = "text" + var T_BOOL = "bool" + var T_NULL = "null" + + // Integer arithmetic ops that produce integer results + var int_result_ops = { + add_int: true, sub_int: true, mul_int: true, + div_int: true, mod_int: true + } + + // Float arithmetic ops that produce float results + var float_result_ops = { + add_float: true, sub_float: true, mul_float: true, + div_float: true, mod_float: true + } + + // Comparison ops that produce bool results + var bool_result_ops = { + eq_int: true, ne_int: true, lt_int: true, gt_int: true, + le_int: true, ge_int: true, + eq_float: true, ne_float: true, lt_float: true, gt_float: true, + le_float: true, ge_float: true, + eq_text: true, ne_text: true, lt_text: true, gt_text: true, + le_text: true, ge_text: true, + eq_bool: true, ne_bool: true, + eq_tol: true, ne_tol: true, + not: true, and: true, or: true, + is_int: true, is_text: true, is_num: true, + is_bool: true, is_null: true, is_identical: true + } + + // Type check opcodes and what type they verify + var type_check_map = { + is_int: T_INT, + is_text: T_TEXT, + is_num: T_NUM, + is_bool: T_BOOL, + is_null: T_NULL + } + + // Determine the type of an access literal value + var access_value_type = function(val) { + if (is_number(val)) { + if (is_integer(val)) { + return T_INT + } + return T_FLOAT + } + if (is_text(val)) { + return T_TEXT + } + return T_UNKNOWN + } + + // Update slot_types for an instruction (shared tracking logic) + var track_types = function(slot_types, instr) { + var op = instr[0] + var src_type = null + + if (op == "access") { + slot_types[text(instr[1])] = access_value_type(instr[2]) + } else if (op == "int") { + slot_types[text(instr[1])] = T_INT + } else if (op == "true" || op == "false") { + slot_types[text(instr[1])] = T_BOOL + } else if (op == "null") { + slot_types[text(instr[1])] = T_NULL + } else if (op == "move") { + src_type = slot_types[text(instr[2])] + if (src_type != null) { + slot_types[text(instr[1])] = src_type + } else { + slot_types[text(instr[1])] = T_UNKNOWN + } + } else if (int_result_ops[op] == true) { + slot_types[text(instr[1])] = T_INT + } else if (float_result_ops[op] == true) { + slot_types[text(instr[1])] = T_FLOAT + } else if (op == "concat") { + slot_types[text(instr[1])] = T_TEXT + } else if (bool_result_ops[op] == true) { + slot_types[text(instr[1])] = T_BOOL + } else if (op == "load_field" || op == "load_index" || op == "load_dynamic") { + slot_types[text(instr[1])] = T_UNKNOWN + } else if (op == "invoke") { + slot_types[text(instr[2])] = T_UNKNOWN + } else if (op == "pop" || op == "get" || op == "function") { + slot_types[text(instr[1])] = T_UNKNOWN + } else if (op == "typeof") { + slot_types[text(instr[1])] = T_TEXT + } else if (op == "neg_int") { + slot_types[text(instr[1])] = T_INT + } else if (op == "neg_float") { + slot_types[text(instr[1])] = T_FLOAT + } else if (op == "bitnot" || op == "bitand" || op == "bitor" || + op == "bitxor" || op == "shl" || op == "shr" || op == "ushr") { + slot_types[text(instr[1])] = T_INT + } + return null + } + + // Check if a slot has a known type (with T_NUM subsumption) + var slot_is = function(slot_types, slot, typ) { + var known = slot_types[text(slot)] + if (known == null) { + return false + } + if (known == typ) { + return true + } + if (typ == T_NUM && (known == T_INT || known == T_FLOAT)) { + return true + } + return false + } + + // Optimize a single function's instructions + var optimize_function = function(func) { + var instructions = func.instructions + var num_instr = 0 + var slot_types = null + var nop_counter = 0 + var i = 0 + var instr = null + var op = null + var dest = 0 + var src = 0 + var checked_type = null + var next = null + var next_op = null + var target_label = null + var src_known = null + var jlen = 0 + var j = 0 + var peek = null + + if (instructions == null || length(instructions) == 0) { + return null + } + + num_instr = length(instructions) + slot_types = {} + + // Peephole optimization pass: type tracking + strength reduction + i = 0 + while (i < num_instr) { + instr = instructions[i] + + // Labels are join points: clear all type info (conservative) + if (is_text(instr)) { + slot_types = {} + i = i + 1 + continue + } + + if (!is_array(instr)) { + i = i + 1 + continue + } + + op = instr[0] + + // --- Peephole: type-check + jump where we know the type --- + if (type_check_map[op] != null && i + 1 < num_instr) { + dest = instr[1] + src = instr[2] + checked_type = type_check_map[op] + next = instructions[i + 1] + + if (is_array(next)) { + next_op = next[0] + + // Pattern: is_ t, x -> jump_false t, label + if (next_op == "jump_false" && next[1] == dest) { + target_label = next[2] + + if (slot_is(slot_types, src, checked_type)) { + // Known match: check always true, never jumps — eliminate both + nop_counter = nop_counter + 1 + instructions[i] = "_nop_" + text(nop_counter) + nop_counter = nop_counter + 1 + instructions[i + 1] = "_nop_" + text(nop_counter) + slot_types[text(dest)] = T_BOOL + i = i + 2 + continue + } + + src_known = slot_types[text(src)] + if (src_known != null && src_known != T_UNKNOWN && src_known != checked_type) { + // Check for T_NUM subsumption: INT and FLOAT match T_NUM + if (checked_type == T_NUM && (src_known == T_INT || src_known == T_FLOAT)) { + // Actually matches — eliminate both + nop_counter = nop_counter + 1 + instructions[i] = "_nop_" + text(nop_counter) + nop_counter = nop_counter + 1 + instructions[i + 1] = "_nop_" + text(nop_counter) + slot_types[text(dest)] = T_BOOL + i = i + 2 + continue + } + // Known mismatch: always jumps — nop the check, rewrite jump + nop_counter = nop_counter + 1 + instructions[i] = "_nop_" + text(nop_counter) + jlen = length(next) + instructions[i + 1] = ["jump", target_label, next[jlen - 2], next[jlen - 1]] + slot_types[text(dest)] = T_UNKNOWN + i = i + 2 + continue + } + + // Unknown: can't eliminate, but narrow type on fallthrough + slot_types[text(dest)] = T_BOOL + slot_types[text(src)] = checked_type + i = i + 2 + continue + } + + // Pattern: is_ t, x -> jump_true t, label + if (next_op == "jump_true" && next[1] == dest) { + target_label = next[2] + + if (slot_is(slot_types, src, checked_type)) { + // Known match: always true, always jumps — nop check, rewrite to jump + nop_counter = nop_counter + 1 + instructions[i] = "_nop_" + text(nop_counter) + jlen = length(next) + instructions[i + 1] = ["jump", target_label, next[jlen - 2], next[jlen - 1]] + slot_types[text(dest)] = T_BOOL + i = i + 2 + continue + } + + src_known = slot_types[text(src)] + if (src_known != null && src_known != T_UNKNOWN && src_known != checked_type) { + if (checked_type == T_NUM && (src_known == T_INT || src_known == T_FLOAT)) { + // Actually matches T_NUM — always jumps + nop_counter = nop_counter + 1 + instructions[i] = "_nop_" + text(nop_counter) + jlen = length(next) + instructions[i + 1] = ["jump", target_label, next[jlen - 2], next[jlen - 1]] + slot_types[text(dest)] = T_BOOL + i = i + 2 + continue + } + // Known mismatch: never jumps — eliminate both + nop_counter = nop_counter + 1 + instructions[i] = "_nop_" + text(nop_counter) + nop_counter = nop_counter + 1 + instructions[i + 1] = "_nop_" + text(nop_counter) + slot_types[text(dest)] = T_BOOL + i = i + 2 + continue + } + + // Unknown: can't optimize + slot_types[text(dest)] = T_BOOL + i = i + 2 + continue + } + } + + // Standalone type check (no jump following): just track the result + slot_types[text(dest)] = T_BOOL + i = i + 1 + continue + } + + // --- Strength reduction: load_dynamic / store_dynamic --- + if (op == "load_dynamic") { + if (slot_is(slot_types, instr[3], T_TEXT)) { + instr[0] = "load_field" + } else if (slot_is(slot_types, instr[3], T_INT)) { + instr[0] = "load_index" + } + slot_types[text(instr[1])] = T_UNKNOWN + i = i + 1 + continue + } + if (op == "store_dynamic") { + if (slot_is(slot_types, instr[3], T_TEXT)) { + instr[0] = "store_field" + } else if (slot_is(slot_types, instr[3], T_INT)) { + instr[0] = "store_index" + } + i = i + 1 + continue + } + + // --- Standard type tracking --- + track_types(slot_types, instr) + + i = i + 1 + } + + // Second pass: remove dead jumps (jump to the immediately next label) + i = 0 + while (i < num_instr) { + instr = instructions[i] + if (is_array(instr) && instr[0] == "jump") { + target_label = instr[1] + // Check if the very next non-nop item is that label + j = i + 1 + while (j < num_instr) { + peek = instructions[j] + if (is_text(peek)) { + if (peek == target_label) { + nop_counter = nop_counter + 1 + instructions[i] = "_nop_" + text(nop_counter) + } + break + } + if (is_array(peek)) { + break + } + j = j + 1 + } + } + i = i + 1 + } + + return null + } + + // Process main function + if (ir.main != null) { + optimize_function(ir.main) + } + + // Process all sub-functions + var fi = 0 + if (ir.functions != null) { + fi = 0 + while (fi < length(ir.functions)) { + optimize_function(ir.functions[fi]) + fi = fi + 1 + } + } + + return ir +} + +return streamline diff --git a/streamline.mach b/streamline.mach new file mode 100644 index 0000000000000000000000000000000000000000..3ccf6a767cc0efd1a36f02d46d845d06711d5ccf GIT binary patch literal 8382 zcmd6sdu(J?8Nkn(nLCe8+nLUt>2urZ>`rHg>2|wM9*cy42n(`6V0kUz+HGf-LU+1s zTV(YQMaB2q1QR7eP0)k{#XzDVqF{nCB4~VTBKU{8@qs43@D=LsJCC_7EsMrzoaEQ< zJLi1oJLi1&-1E3qN~sw5UkA#<;_>Fx(Ye`HQ*z@lsg)XsE$9hVPzQC|K$a3p;D4(< z>?Avu;?Gj+*4F&%S}}1q+ggkT$is8dVz}F|*Yc<&!Q%d4XR#BEES}% z!_Y^*rGl)eAvK^js4mA4QaH_V2J#F%GG@aNa!8HP8%r4}Oghd*o=Z;7QAU>4DE+ex zBZV!Fvyo@hi*Cm-a#(F7Z4rG~ZK7?<5OPRWiHxO$6v~D%>?|9QLc>tQpJf;+Y&SHB z={R+^P(Fd&ikyU}?oik09_U}i zwb)&&2cTxS4ja#n)DdLI&>+(PR&PJDV40=Fu0w~K$75nZimjx1grr-UWWkbMtip;C zFr^zYt?RKgX}uyHQ`MNxj$?z&h^(*%M)syW>B>7q%8t2<3A&5v>eEPMeyut=s2s zZJKT4*!(dgk2DtvYqYi-yTud7n)8RvvuHJsn2ki%f6?uBCtJy(*+toVoGROooFT|{ zI}q9(s4~2To9$k;aPx69rX`eT$!9LAay%B8OdREcc(NX3RVAsFPHR<8#nO$W*432m z)@&~2bS%?IX&nS9HL8Iw@1 z&elL1R_{1fdv@GcN&W1L?1-(O7d}20i(Tgm-(4&8?ibZxF|U06v!s6RRCad!lVZn) z_R?{(^KDG*dQN3$<31#I1!HGp60XpTEA-L|y?2G)w?gkXdOb+4H;+ue{LMx7>)|h%M-z^U*CgQU50DM^w=*uc5x@Aa)0_i>RVoUW;AC7`kT{x@C&`Q`C>B zqFWA8-!qNfG>{e@mc!V2j$n5LyND{fgb+5=$2V*BI@X# zGtfO3qFZjEy<2E6qKaNsbDS1Dynf~$FNYofTYXxPV^sTyAge@$*EV3QTe+p=%$5>U z3HMr)j@Q}V;x1iH>bza50)?Ixds$}CGrq33^*&^2xQ+AYZA@R!afaks;NT-Hktf`a zF5JP?jM#+k*^O>Fft~OMLgA6&%V0N%;(n_`!39#8#iL z@u?H$NOa56(PIY>o6{6gQ3qS|#}2jwYZS3CXHsKoPGE8kUUhgn^;xS6^97DQ&4d21 zm2KzP!&#Izr?Ve3;$b=Ca1@bVat>EIkIpG8hONe<<}w#!>~esLaWRf@iI(AU4p*A< zpseL`dCP^#;^r_=l_DqOQc^XFT<$nJ=fYyC9>!EGWX*?4NO_TRE_MN#6Vfhi_JsLZ zJ$$P!XOj5}_CYaYZ0cdc+E6c<&xf@?EiYG!Tp~-9(?%&Qs(Kh#+Lu4KysT-boXv#Q zl1`V4rEH9T1xcKV~iXHT~TLry2e8llFX#3mQ zi4S5%yn^0N)hKD5q@Sb7JpG}4%6)%T3A^6Nbr#nJ5W_eFi^`ZxN(hb;PQVlCsn^Mz&FkJ~j;)_gv($a^nkujiSy_l7OX>u6i>0|g2?%}9&FJGMQr7h3L>Dwo$Cwvmy`#2!oM;})5DawRTQ+_`_g$MBS03S>r z;II}kfi7$}dd3l!oaZQfg=hB! zXX_{U=qC?^9pnU>dOp-E#%HYKz$_!e>d4&xWTi_Le9+=h(!9?#@^+$B8UCsvjkax3gz!FA|s z`2q3!0r9ick#+S$Vki8F`2B?W@Dtjw)Q~mx40+FxXBk6|sb`rx!cUniKf`ClR&-&P z;pdD+ILY`<<{{DXKe6Hh_u3W(gc3Qe96 z)1!_tD$hu1KPXZy(IhEm`0~tN+s3B{rgUvw5>MoUM)64VE0?S6^ zM%BYV41|Kts9vO}5Bomq_36!yaipamyMEo_A!uu^q%#*nr&z`X5n4 z=ievT2qW}i6zQqZhYEeD&