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) |
| `delete` | `obj, key` | Delete property |
| `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 |
### Object and Array Construction
@@ -181,7 +180,7 @@ Memory operations come in typed variants. The compiler selects the appropriate v
| Instruction | Operands | Description |
|-------------|----------|-------------|
| `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 |
| `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()
emit_const_str(name_str, prop)
var args_arr = alloc_slot()
var arr_instr = ["array", args_arr, argc]
add_instr(["array", args_arr, 0])
_i = 0
while (_i < argc) {
push(arr_instr, args[_i])
emit_2("push", args_arr, args[_i])
_i = _i + 1
}
add_instr(arr_instr)
var pf = alloc_slot()
emit_3("frame", pf, obj, 2)
emit_3("setarg", pf, 0, null_slot)
@@ -836,13 +835,12 @@ var mcode = function(ast) {
var null_slot = alloc_slot()
emit_const_null(null_slot)
var args_arr = alloc_slot()
var arr_instr = ["array", args_arr, argc]
add_instr(["array", args_arr, 0])
_i = 0
while (_i < argc) {
push(arr_instr, args[_i])
emit_2("push", args_arr, args[_i])
_i = _i + 1
}
add_instr(arr_instr)
var pf = alloc_slot()
emit_3("frame", pf, obj, 2)
emit_3("setarg", pf, 0, null_slot)
@@ -1181,17 +1179,29 @@ var mcode = function(ast) {
var obj_slot = 0
var idx_expr = null
var idx_slot = 0
var guard_t = 0
var guard_err = null
var guard_done = null
if (cop != null) {
return gen_compound_assign(node, cop)
}
// Push syntax: arr[] = val
// Push syntax: arr[] = val (guarded)
if (node.push == true) {
arr_expr = left.left
arr_slot = gen_expr(arr_expr, -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_jump(guard_done)
emit_label(guard_err)
emit_0("disrupt")
emit_label(guard_done)
return val_slot
}
@@ -1306,6 +1316,9 @@ var mcode = function(ast) {
var kname = null
var func = null
var func_id = 0
var guard_t = 0
var guard_err = null
var guard_done = null
if (expr == null) {
return -1
@@ -1343,13 +1356,12 @@ var mcode = function(ast) {
}
// Create array from expression results
arr_slot = alloc_slot()
arr_instr = ["array", arr_slot, nexpr]
add_instr(["array", arr_slot, 0])
_i = 0
while (_i < nexpr) {
push(arr_instr, expr_slots[_i])
emit_2("push", arr_slot, expr_slots[_i])
_i = _i + 1
}
add_instr(arr_instr)
// Load format intrinsic
fmt_func_slot = find_intrinsic("format")
if (fmt_func_slot < 0) {
@@ -1546,11 +1558,20 @@ var mcode = function(ast) {
return d
}
}
// 2-arg push: push(arr, val) → direct opcode
// 2-arg push: push(arr, val) → guarded direct opcode
if (nargs == 2 && fname == "push") {
a0 = gen_expr(args_list[0], -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_jump(guard_done)
emit_label(guard_err)
emit_0("disrupt")
emit_label(guard_done)
return a1
}
}
@@ -1732,13 +1753,12 @@ var mcode = function(ast) {
_i = _i + 1
}
dest = alloc_slot()
instr = ["array", dest, count]
add_instr(["array", dest, 0])
_i = 0
while (_i < count) {
push(instr, elem_slots[_i])
emit_2("push", dest, elem_slots[_i])
_i = _i + 1
}
push(s_instructions, instr)
return dest
}
@@ -1856,6 +1876,9 @@ var mcode = function(ast) {
var func = null
var func_id = 0
var dest = 0
var guard_t = 0
var guard_err = null
var guard_done = null
if (stmt == null) {
return null
@@ -1871,12 +1894,21 @@ var mcode = function(ast) {
right = stmt.right
name = left.name
local_slot = find_var(name)
// Pop: var val = arr[]
// Pop: var val = arr[] (guarded)
if (stmt.pop == true && right != null) {
arr_expr = right.left
arr_slot = gen_expr(arr_expr, -1)
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_jump(guard_done)
emit_label(guard_err)
emit_0("disrupt")
emit_label(guard_done)
}
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: {
/* R(A) = R(B)[R(C)] — mcode guarantees R(C) is int */
JSValue obj = frame->slots[b];
JSValue idx = 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);
JSValue val = JS_GetPropertyNumber(ctx, obj, JS_VALUE_GET_INT(frame->slots[c]));
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
if (JS_IsException(val)) goto disrupt;
frame->slots[a] = val;
@@ -1001,25 +997,12 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
}
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 idx = frame->slots[b];
JSValue val = frame->slots[c];
int ret;
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);
}
JSValue r = JS_SetPropertyNumber(ctx, obj, JS_VALUE_GET_INT(frame->slots[b]), val);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
if (ret < 0) goto disrupt;
if (JS_IsException(r)) goto disrupt;
break;
}
@@ -1187,16 +1170,10 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
}
case MACH_NEWARRAY: {
int count = b;
JSValue arr = JS_NewArray(ctx);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
if (JS_IsException(arr)) { goto disrupt; }
/* Store array in dest immediately so GC can track it */
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;
}
@@ -1216,14 +1193,9 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
}
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 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;
JS_PushGCRef(ctx, &arr_gc);
arr_gc.val = arr;
@@ -1236,13 +1208,8 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
}
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];
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);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
if (JS_IsException(val)) goto disrupt;
@@ -1594,30 +1561,21 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
break;
}
case MACH_LOAD_INDEX: {
/* R(A) = R(B)[R(C)] — mcode guarantees R(C) is int */
JSValue obj = frame->slots[b];
JSValue idx = 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);
JSValue val = JS_GetPropertyNumber(ctx, obj, JS_VALUE_GET_INT(frame->slots[c]));
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
if (JS_IsException(val)) goto disrupt;
frame->slots[a] = val;
break;
}
case MACH_STORE_INDEX: {
/* R(A)[R(B)] = R(C) — mcode guarantees R(B) is int */
JSValue obj = frame->slots[a];
JSValue idx = frame->slots[b];
JSValue val = frame->slots[c];
int ret;
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);
JSValue r = JS_SetPropertyNumber(ctx, obj, JS_VALUE_GET_INT(frame->slots[b]), val);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
if (ret < 0) goto disrupt;
if (JS_IsException(r)) goto disrupt;
break;
}
case MACH_LOAD_DYNAMIC: {

View File

@@ -159,8 +159,6 @@ var streamline = function(ir, log) {
slot_types[text(instr[1])] = T_FUNCTION
} else if (op == "length") {
slot_types[text(instr[1])] = T_INT
} 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") {

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