guards in mcode
This commit is contained in:
@@ -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 |
|
||||||
|
|
||||||
|
|||||||
5707
fold.cm.mcode
5707
fold.cm.mcode
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
62
mcode.cm
62
mcode.cm
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
37487
mcode.cm.mcode
37487
mcode.cm.mcode
File diff suppressed because it is too large
Load Diff
37099
parse.cm.mcode
37099
parse.cm.mcode
File diff suppressed because it is too large
Load Diff
1306
qbe.cm.mcode
1306
qbe.cm.mcode
File diff suppressed because it is too large
Load Diff
7779
qbe_emit.cm.mcode
7779
qbe_emit.cm.mcode
File diff suppressed because it is too large
Load Diff
@@ -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: {
|
||||||
|
|||||||
@@ -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") {
|
||||||
|
|||||||
33293
streamline.cm.mcode
33293
streamline.cm.mcode
File diff suppressed because it is too large
Load Diff
12
test_guards.ce
Normal file
12
test_guards.ce
Normal 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)
|
||||||
14720
tokenize.cm.mcode
14720
tokenize.cm.mcode
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user