guards in mcode

This commit is contained in:
2026-02-13 02:30:41 -06:00
parent e346348eb5
commit 4a50d0587d
14 changed files with 72485 additions and 69473 deletions

View File

@@ -173,7 +173,6 @@ Memory operations come in typed variants. The compiler selects the appropriate v
| `store_dynamic` | `obj, val, key` | Store property (dispatches at runtime) | | `store_dynamic` | `obj, val, key` | Store property (dispatches at runtime) |
| `delete` | `obj, key` | Delete property | | `delete` | `obj, key` | Delete property |
| `in` | `dest, obj, key` | Check if property exists | | `in` | `dest, obj, key` | Check if property exists |
| `typeof` | `dest, src` | Get type name as text |
| `length` | `dest, src` | Get length of array or text | | `length` | `dest, src` | Get length of array or text |
### Object and Array Construction ### Object and Array Construction
@@ -181,7 +180,7 @@ Memory operations come in typed variants. The compiler selects the appropriate v
| Instruction | Operands | Description | | Instruction | Operands | Description |
|-------------|----------|-------------| |-------------|----------|-------------|
| `record` | `dest` | Create empty record `{}` | | `record` | `dest` | Create empty record `{}` |
| `array` | `dest, n, ...elems` | Create array with `n` elements | | `array` | `dest, n` | Create empty array (elements added via `push`) |
| `push` | `arr, val` | Push value to array | | `push` | `arr, val` | Push value to array |
| `pop` | `dest, arr` | Pop value from array | | `pop` | `dest, arr` | Pop value from array |

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -782,13 +782,12 @@ var mcode = function(ast) {
var name_str = alloc_slot() var name_str = alloc_slot()
emit_const_str(name_str, prop) emit_const_str(name_str, prop)
var args_arr = alloc_slot() var args_arr = alloc_slot()
var arr_instr = ["array", args_arr, argc] add_instr(["array", args_arr, 0])
_i = 0 _i = 0
while (_i < argc) { while (_i < argc) {
push(arr_instr, args[_i]) emit_2("push", args_arr, args[_i])
_i = _i + 1 _i = _i + 1
} }
add_instr(arr_instr)
var pf = alloc_slot() var pf = alloc_slot()
emit_3("frame", pf, obj, 2) emit_3("frame", pf, obj, 2)
emit_3("setarg", pf, 0, null_slot) emit_3("setarg", pf, 0, null_slot)
@@ -836,13 +835,12 @@ var mcode = function(ast) {
var null_slot = alloc_slot() var null_slot = alloc_slot()
emit_const_null(null_slot) emit_const_null(null_slot)
var args_arr = alloc_slot() var args_arr = alloc_slot()
var arr_instr = ["array", args_arr, argc] add_instr(["array", args_arr, 0])
_i = 0 _i = 0
while (_i < argc) { while (_i < argc) {
push(arr_instr, args[_i]) emit_2("push", args_arr, args[_i])
_i = _i + 1 _i = _i + 1
} }
add_instr(arr_instr)
var pf = alloc_slot() var pf = alloc_slot()
emit_3("frame", pf, obj, 2) emit_3("frame", pf, obj, 2)
emit_3("setarg", pf, 0, null_slot) emit_3("setarg", pf, 0, null_slot)
@@ -1181,17 +1179,29 @@ var mcode = function(ast) {
var obj_slot = 0 var obj_slot = 0
var idx_expr = null var idx_expr = null
var idx_slot = 0 var idx_slot = 0
var guard_t = 0
var guard_err = null
var guard_done = null
if (cop != null) { if (cop != null) {
return gen_compound_assign(node, cop) return gen_compound_assign(node, cop)
} }
// Push syntax: arr[] = val // Push syntax: arr[] = val (guarded)
if (node.push == true) { if (node.push == true) {
arr_expr = left.left arr_expr = left.left
arr_slot = gen_expr(arr_expr, -1) arr_slot = gen_expr(arr_expr, -1)
val_slot = gen_expr(right, -1) val_slot = gen_expr(right, -1)
guard_t = alloc_slot()
guard_err = gen_label("push_err")
guard_done = gen_label("push_done")
emit_2("is_array", guard_t, arr_slot)
emit_jump_cond("jump_false", guard_t, guard_err)
emit_2("push", arr_slot, val_slot) emit_2("push", arr_slot, val_slot)
emit_jump(guard_done)
emit_label(guard_err)
emit_0("disrupt")
emit_label(guard_done)
return val_slot return val_slot
} }
@@ -1306,6 +1316,9 @@ var mcode = function(ast) {
var kname = null var kname = null
var func = null var func = null
var func_id = 0 var func_id = 0
var guard_t = 0
var guard_err = null
var guard_done = null
if (expr == null) { if (expr == null) {
return -1 return -1
@@ -1343,13 +1356,12 @@ var mcode = function(ast) {
} }
// Create array from expression results // Create array from expression results
arr_slot = alloc_slot() arr_slot = alloc_slot()
arr_instr = ["array", arr_slot, nexpr] add_instr(["array", arr_slot, 0])
_i = 0 _i = 0
while (_i < nexpr) { while (_i < nexpr) {
push(arr_instr, expr_slots[_i]) emit_2("push", arr_slot, expr_slots[_i])
_i = _i + 1 _i = _i + 1
} }
add_instr(arr_instr)
// Load format intrinsic // Load format intrinsic
fmt_func_slot = find_intrinsic("format") fmt_func_slot = find_intrinsic("format")
if (fmt_func_slot < 0) { if (fmt_func_slot < 0) {
@@ -1546,11 +1558,20 @@ var mcode = function(ast) {
return d return d
} }
} }
// 2-arg push: push(arr, val) → direct opcode // 2-arg push: push(arr, val) → guarded direct opcode
if (nargs == 2 && fname == "push") { if (nargs == 2 && fname == "push") {
a0 = gen_expr(args_list[0], -1) a0 = gen_expr(args_list[0], -1)
a1 = gen_expr(args_list[1], -1) a1 = gen_expr(args_list[1], -1)
guard_t = alloc_slot()
guard_err = gen_label("push_err")
guard_done = gen_label("push_done")
emit_2("is_array", guard_t, a0)
emit_jump_cond("jump_false", guard_t, guard_err)
emit_2("push", a0, a1) emit_2("push", a0, a1)
emit_jump(guard_done)
emit_label(guard_err)
emit_0("disrupt")
emit_label(guard_done)
return a1 return a1
} }
} }
@@ -1732,13 +1753,12 @@ var mcode = function(ast) {
_i = _i + 1 _i = _i + 1
} }
dest = alloc_slot() dest = alloc_slot()
instr = ["array", dest, count] add_instr(["array", dest, 0])
_i = 0 _i = 0
while (_i < count) { while (_i < count) {
push(instr, elem_slots[_i]) emit_2("push", dest, elem_slots[_i])
_i = _i + 1 _i = _i + 1
} }
push(s_instructions, instr)
return dest return dest
} }
@@ -1856,6 +1876,9 @@ var mcode = function(ast) {
var func = null var func = null
var func_id = 0 var func_id = 0
var dest = 0 var dest = 0
var guard_t = 0
var guard_err = null
var guard_done = null
if (stmt == null) { if (stmt == null) {
return null return null
@@ -1871,12 +1894,21 @@ var mcode = function(ast) {
right = stmt.right right = stmt.right
name = left.name name = left.name
local_slot = find_var(name) local_slot = find_var(name)
// Pop: var val = arr[] // Pop: var val = arr[] (guarded)
if (stmt.pop == true && right != null) { if (stmt.pop == true && right != null) {
arr_expr = right.left arr_expr = right.left
arr_slot = gen_expr(arr_expr, -1) arr_slot = gen_expr(arr_expr, -1)
if (local_slot >= 0) { if (local_slot >= 0) {
guard_t = alloc_slot()
guard_err = gen_label("pop_err")
guard_done = gen_label("pop_done")
emit_2("is_array", guard_t, arr_slot)
emit_jump_cond("jump_false", guard_t, guard_err)
emit_2("pop", local_slot, arr_slot) emit_2("pop", local_slot, arr_slot)
emit_jump(guard_done)
emit_label(guard_err)
emit_0("disrupt")
emit_label(guard_done)
} }
return null return null
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -987,13 +987,9 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
} }
case MACH_GETINDEX: { case MACH_GETINDEX: {
/* R(A) = R(B)[R(C)] — mcode guarantees R(C) is int */
JSValue obj = frame->slots[b]; JSValue obj = frame->slots[b];
JSValue idx = frame->slots[c]; JSValue val = JS_GetPropertyNumber(ctx, obj, JS_VALUE_GET_INT(frame->slots[c]));
JSValue val;
if (JS_IsInt(idx))
val = JS_GetPropertyNumber(ctx, obj, JS_VALUE_GET_INT(idx));
else
val = JS_GetProperty(ctx, obj, idx);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
if (JS_IsException(val)) goto disrupt; if (JS_IsException(val)) goto disrupt;
frame->slots[a] = val; frame->slots[a] = val;
@@ -1001,25 +997,12 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
} }
case MACH_SETINDEX: { case MACH_SETINDEX: {
/* R(A)[R(B)] = R(C) */ /* R(A)[R(B)] = R(C) — mcode guarantees R(B) is int */
JSValue obj = frame->slots[a]; JSValue obj = frame->slots[a];
JSValue idx = frame->slots[b];
JSValue val = frame->slots[c]; JSValue val = frame->slots[c];
int ret; JSValue r = JS_SetPropertyNumber(ctx, obj, JS_VALUE_GET_INT(frame->slots[b]), val);
if (JS_IsInt(idx)) {
JSValue r = JS_SetPropertyNumber(ctx, obj, JS_VALUE_GET_INT(idx), val);
ret = JS_IsException(r) ? -1 : 0;
} else if (mist_is_array(obj)) {
JS_ThrowTypeError(ctx, "array index must be a number");
ret = -1;
} else if (mist_is_record(obj) && !mist_is_text(idx) && !mist_is_record(idx)) {
JS_ThrowTypeError(ctx, "object key must be a string or object");
ret = -1;
} else {
ret = JS_SetProperty(ctx, obj, idx, val);
}
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
if (ret < 0) goto disrupt; if (JS_IsException(r)) goto disrupt;
break; break;
} }
@@ -1187,16 +1170,10 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
} }
case MACH_NEWARRAY: { case MACH_NEWARRAY: {
int count = b;
JSValue arr = JS_NewArray(ctx); JSValue arr = JS_NewArray(ctx);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
if (JS_IsException(arr)) { goto disrupt; } if (JS_IsException(arr)) { goto disrupt; }
/* Store array in dest immediately so GC can track it */
frame->slots[a] = arr; frame->slots[a] = arr;
for (int i = 0; i < count; i++) {
JS_SetPropertyNumber(ctx, frame->slots[a], i, frame->slots[a + 1 + i]);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
}
break; break;
} }
@@ -1216,14 +1193,9 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
} }
case MACH_PUSH: { case MACH_PUSH: {
/* push R(B) onto array R(A) */ /* push R(B) onto array R(A) — mcode guarantees R(A) is array */
JSValue arr = frame->slots[a]; JSValue arr = frame->slots[a];
JSValue val = frame->slots[b]; JSValue val = frame->slots[b];
if (!mist_is_array(arr)) {
JS_ThrowTypeError(ctx, "cannot push to non-array");
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
goto disrupt;
}
JSGCRef arr_gc; JSGCRef arr_gc;
JS_PushGCRef(ctx, &arr_gc); JS_PushGCRef(ctx, &arr_gc);
arr_gc.val = arr; arr_gc.val = arr;
@@ -1236,13 +1208,8 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
} }
case MACH_POP: { case MACH_POP: {
/* R(A) = pop last element from array R(B) */ /* R(A) = pop last element from array R(B) — mcode guarantees R(B) is array */
JSValue arr = frame->slots[b]; JSValue arr = frame->slots[b];
if (!mist_is_array(arr)) {
JS_ThrowTypeError(ctx, "cannot pop from non-array");
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
goto disrupt;
}
JSValue val = JS_ArrayPop(ctx, arr); JSValue val = JS_ArrayPop(ctx, arr);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
if (JS_IsException(val)) goto disrupt; if (JS_IsException(val)) goto disrupt;
@@ -1594,30 +1561,21 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
break; break;
} }
case MACH_LOAD_INDEX: { case MACH_LOAD_INDEX: {
/* R(A) = R(B)[R(C)] — mcode guarantees R(C) is int */
JSValue obj = frame->slots[b]; JSValue obj = frame->slots[b];
JSValue idx = frame->slots[c]; JSValue val = JS_GetPropertyNumber(ctx, obj, JS_VALUE_GET_INT(frame->slots[c]));
JSValue val;
if (JS_IsInt(idx))
val = JS_GetPropertyNumber(ctx, obj, JS_VALUE_GET_INT(idx));
else
val = JS_GetProperty(ctx, obj, idx);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
if (JS_IsException(val)) goto disrupt; if (JS_IsException(val)) goto disrupt;
frame->slots[a] = val; frame->slots[a] = val;
break; break;
} }
case MACH_STORE_INDEX: { case MACH_STORE_INDEX: {
/* R(A)[R(B)] = R(C) — mcode guarantees R(B) is int */
JSValue obj = frame->slots[a]; JSValue obj = frame->slots[a];
JSValue idx = frame->slots[b];
JSValue val = frame->slots[c]; JSValue val = frame->slots[c];
int ret; JSValue r = JS_SetPropertyNumber(ctx, obj, JS_VALUE_GET_INT(frame->slots[b]), val);
if (JS_IsInt(idx)) {
JSValue r = JS_SetPropertyNumber(ctx, obj, JS_VALUE_GET_INT(idx), val);
ret = JS_IsException(r) ? -1 : 0;
} else
ret = JS_SetProperty(ctx, obj, idx, val);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
if (ret < 0) goto disrupt; if (JS_IsException(r)) goto disrupt;
break; break;
} }
case MACH_LOAD_DYNAMIC: { case MACH_LOAD_DYNAMIC: {

View File

@@ -159,8 +159,6 @@ var streamline = function(ir, log) {
slot_types[text(instr[1])] = T_FUNCTION slot_types[text(instr[1])] = T_FUNCTION
} else if (op == "length") { } else if (op == "length") {
slot_types[text(instr[1])] = T_INT slot_types[text(instr[1])] = T_INT
} else if (op == "typeof") {
slot_types[text(instr[1])] = T_TEXT
} else if (op == "neg_int") { } else if (op == "neg_int") {
slot_types[text(instr[1])] = T_INT slot_types[text(instr[1])] = T_INT
} else if (op == "neg_float") { } else if (op == "neg_float") {

File diff suppressed because it is too large Load Diff

12
test_guards.ce Normal file
View File

@@ -0,0 +1,12 @@
// Test array guard emission and optimization
var a = [1, 2, 3]
push(a, 4)
var v = a[]
var f = function(arr) {
push(arr, 99)
var x = arr[]
return x
}
f(a)

File diff suppressed because it is too large Load Diff