decomposed mcode
This commit is contained in:
479
mcode.cm
479
mcode.cm
@@ -227,6 +227,463 @@ var mcode = function(ast) {
|
||||
add_instr([op, slot, label])
|
||||
}
|
||||
|
||||
// ---- Decomposed op emission helpers ----
|
||||
|
||||
// Helper: check if an AST node is a known-int literal
|
||||
var is_known_int = function(node) {
|
||||
if (node == null) { return false }
|
||||
return node.kind == "number" && is_integer(node.number)
|
||||
}
|
||||
|
||||
// Helper: check if an AST node is a known-text literal
|
||||
var is_known_text = function(node) {
|
||||
if (node == null) { return false }
|
||||
return node.kind == "text" || node.kind == "text literal"
|
||||
}
|
||||
|
||||
// Helper: check if an AST node is a known-number literal (int or float)
|
||||
var is_known_number = function(node) {
|
||||
if (node == null) { return false }
|
||||
return node.kind == "number"
|
||||
}
|
||||
|
||||
// Helper: check if an AST node is a known-bool literal
|
||||
var is_known_bool = function(node) {
|
||||
if (node == null) { return false }
|
||||
return node.kind == "true" || node.kind == "false"
|
||||
}
|
||||
|
||||
// Helper: check if an AST node is a known-null literal
|
||||
var is_known_null = function(node) {
|
||||
if (node == null) { return false }
|
||||
return node.kind == "null"
|
||||
}
|
||||
|
||||
// emit_add_decomposed: int path -> text path -> float path -> disrupt
|
||||
var emit_add_decomposed = function(dest, left, right, left_node, right_node) {
|
||||
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 not_int = null
|
||||
var not_text = null
|
||||
var done = null
|
||||
var err = null
|
||||
|
||||
// Both sides known int
|
||||
if (left_is_int && right_is_int) {
|
||||
emit_3("add_int", dest, left, right)
|
||||
return null
|
||||
}
|
||||
// Both sides known text
|
||||
if (left_is_text && right_is_text) {
|
||||
emit_3("concat", dest, left, right)
|
||||
return null
|
||||
}
|
||||
// Both sides known number (but not both int)
|
||||
if (left_is_num && right_is_num) {
|
||||
if (left_is_int && right_is_int) {
|
||||
emit_3("add_int", dest, left, right)
|
||||
} else {
|
||||
emit_3("add_float", dest, left, right)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
not_int = gen_label("add_ni")
|
||||
not_text = gen_label("add_nt")
|
||||
done = gen_label("add_done")
|
||||
err = gen_label("add_err")
|
||||
|
||||
// Int path
|
||||
t0 = alloc_slot()
|
||||
if (!left_is_int) {
|
||||
emit_2("is_int", t0, left)
|
||||
emit_jump_cond("jump_false", t0, not_int)
|
||||
}
|
||||
t1 = alloc_slot()
|
||||
if (!right_is_int) {
|
||||
emit_2("is_int", t1, right)
|
||||
emit_jump_cond("jump_false", t1, not_int)
|
||||
}
|
||||
emit_3("add_int", dest, left, right)
|
||||
emit_jump(done)
|
||||
|
||||
// Text path
|
||||
emit_label(not_int)
|
||||
if (!left_is_text) {
|
||||
emit_2("is_text", t0, left)
|
||||
emit_jump_cond("jump_false", t0, not_text)
|
||||
}
|
||||
if (!right_is_text) {
|
||||
emit_2("is_text", t1, right)
|
||||
emit_jump_cond("jump_false", t1, not_text)
|
||||
}
|
||||
emit_3("concat", dest, left, right)
|
||||
emit_jump(done)
|
||||
|
||||
// Float path
|
||||
emit_label(not_text)
|
||||
if (!left_is_num) {
|
||||
emit_2("is_num", t0, left)
|
||||
emit_jump_cond("jump_false", t0, err)
|
||||
}
|
||||
if (!right_is_num) {
|
||||
emit_2("is_num", t1, right)
|
||||
emit_jump_cond("jump_false", t1, err)
|
||||
}
|
||||
emit_3("add_float", dest, left, right)
|
||||
emit_jump(done)
|
||||
|
||||
emit_label(err)
|
||||
emit_0("disrupt")
|
||||
emit_label(done)
|
||||
return null
|
||||
}
|
||||
|
||||
// emit_numeric_binop: int path -> float path -> disrupt
|
||||
var emit_numeric_binop = function(int_op, float_op, dest, left, right, left_node, right_node) {
|
||||
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 not_int = null
|
||||
var done = null
|
||||
var err = null
|
||||
|
||||
// Both sides known int
|
||||
if (left_is_int && right_is_int) {
|
||||
emit_3(int_op, dest, left, right)
|
||||
return null
|
||||
}
|
||||
// Both sides known number (but not both int)
|
||||
if (left_is_num && right_is_num) {
|
||||
emit_3(float_op, dest, left, right)
|
||||
return null
|
||||
}
|
||||
|
||||
not_int = gen_label("num_ni")
|
||||
done = gen_label("num_done")
|
||||
err = gen_label("num_err")
|
||||
|
||||
t0 = alloc_slot()
|
||||
if (!left_is_int) {
|
||||
emit_2("is_int", t0, left)
|
||||
emit_jump_cond("jump_false", t0, not_int)
|
||||
}
|
||||
t1 = alloc_slot()
|
||||
if (!right_is_int) {
|
||||
emit_2("is_int", t1, right)
|
||||
emit_jump_cond("jump_false", t1, not_int)
|
||||
}
|
||||
emit_3(int_op, dest, left, right)
|
||||
emit_jump(done)
|
||||
|
||||
emit_label(not_int)
|
||||
if (!left_is_num) {
|
||||
emit_2("is_num", t0, left)
|
||||
emit_jump_cond("jump_false", t0, err)
|
||||
}
|
||||
if (!right_is_num) {
|
||||
emit_2("is_num", t1, right)
|
||||
emit_jump_cond("jump_false", t1, err)
|
||||
}
|
||||
emit_3(float_op, dest, left, right)
|
||||
emit_jump(done)
|
||||
|
||||
emit_label(err)
|
||||
emit_0("disrupt")
|
||||
emit_label(done)
|
||||
return null
|
||||
}
|
||||
|
||||
// emit_eq_decomposed: identical -> int -> float -> text -> null -> bool -> mismatch(false)
|
||||
var emit_eq_decomposed = function(dest, left, right, left_node, right_node) {
|
||||
var t0 = 0
|
||||
var t1 = 0
|
||||
var done = gen_label("eq_done")
|
||||
var not_int = gen_label("eq_ni")
|
||||
var not_num = gen_label("eq_nn")
|
||||
var not_text = gen_label("eq_nt")
|
||||
var not_null = gen_label("eq_nnl")
|
||||
var not_bool = gen_label("eq_nb")
|
||||
|
||||
// Identical check
|
||||
emit_3("is_identical", dest, left, right)
|
||||
emit_jump_cond("jump_true", dest, done)
|
||||
|
||||
// Int path
|
||||
t0 = alloc_slot()
|
||||
emit_2("is_int", t0, left)
|
||||
emit_jump_cond("jump_false", t0, not_int)
|
||||
t1 = alloc_slot()
|
||||
emit_2("is_int", t1, right)
|
||||
emit_jump_cond("jump_false", t1, not_int)
|
||||
emit_3("eq_int", dest, left, right)
|
||||
emit_jump(done)
|
||||
|
||||
// Float path
|
||||
emit_label(not_int)
|
||||
emit_2("is_num", t0, left)
|
||||
emit_jump_cond("jump_false", t0, not_num)
|
||||
emit_2("is_num", t1, right)
|
||||
emit_jump_cond("jump_false", t1, not_num)
|
||||
emit_3("eq_float", dest, left, right)
|
||||
emit_jump(done)
|
||||
|
||||
// Text path
|
||||
emit_label(not_num)
|
||||
emit_2("is_text", t0, left)
|
||||
emit_jump_cond("jump_false", t0, not_text)
|
||||
emit_2("is_text", t1, right)
|
||||
emit_jump_cond("jump_false", t1, not_text)
|
||||
emit_3("eq_text", dest, left, right)
|
||||
emit_jump(done)
|
||||
|
||||
// Null path
|
||||
emit_label(not_text)
|
||||
emit_2("is_null", t0, left)
|
||||
emit_jump_cond("jump_false", t0, not_null)
|
||||
emit_2("is_null", t1, right)
|
||||
emit_jump_cond("jump_false", t1, not_null)
|
||||
emit_1("true", dest)
|
||||
emit_jump(done)
|
||||
|
||||
// Bool path
|
||||
emit_label(not_null)
|
||||
emit_2("is_bool", t0, left)
|
||||
emit_jump_cond("jump_false", t0, not_bool)
|
||||
emit_2("is_bool", t1, right)
|
||||
emit_jump_cond("jump_false", t1, not_bool)
|
||||
emit_3("eq_bool", dest, left, right)
|
||||
emit_jump(done)
|
||||
|
||||
// Mismatch -> false
|
||||
emit_label(not_bool)
|
||||
emit_1("false", dest)
|
||||
emit_label(done)
|
||||
return null
|
||||
}
|
||||
|
||||
// emit_ne_decomposed: identical -> int -> float -> text -> null -> bool -> mismatch(true)
|
||||
var emit_ne_decomposed = function(dest, left, right, left_node, right_node) {
|
||||
var t0 = 0
|
||||
var t1 = 0
|
||||
var done = gen_label("ne_done")
|
||||
var not_ident = gen_label("ne_nid")
|
||||
var not_int = gen_label("ne_ni")
|
||||
var not_num = gen_label("ne_nn")
|
||||
var not_text = gen_label("ne_nt")
|
||||
var not_null = gen_label("ne_nnl")
|
||||
var not_bool = gen_label("ne_nb")
|
||||
|
||||
// Identical -> false
|
||||
emit_3("is_identical", dest, left, right)
|
||||
emit_jump_cond("jump_true", dest, not_ident)
|
||||
// If jump_true doesn't fire, dest already holds false, continue to checks
|
||||
emit_jump(not_int)
|
||||
|
||||
emit_label(not_ident)
|
||||
emit_1("false", dest)
|
||||
emit_jump(done)
|
||||
|
||||
// Int path
|
||||
emit_label(not_int)
|
||||
t0 = alloc_slot()
|
||||
emit_2("is_int", t0, left)
|
||||
emit_jump_cond("jump_false", t0, not_num)
|
||||
t1 = alloc_slot()
|
||||
emit_2("is_int", t1, right)
|
||||
emit_jump_cond("jump_false", t1, not_num)
|
||||
emit_3("ne_int", dest, left, right)
|
||||
emit_jump(done)
|
||||
|
||||
// Float path
|
||||
emit_label(not_num)
|
||||
emit_2("is_num", t0, left)
|
||||
emit_jump_cond("jump_false", t0, not_text)
|
||||
emit_2("is_num", t1, right)
|
||||
emit_jump_cond("jump_false", t1, not_text)
|
||||
emit_3("ne_float", dest, left, right)
|
||||
emit_jump(done)
|
||||
|
||||
// Text path
|
||||
emit_label(not_text)
|
||||
emit_2("is_text", t0, left)
|
||||
emit_jump_cond("jump_false", t0, not_null)
|
||||
emit_2("is_text", t1, right)
|
||||
emit_jump_cond("jump_false", t1, not_null)
|
||||
emit_3("ne_text", dest, left, right)
|
||||
emit_jump(done)
|
||||
|
||||
// Null path
|
||||
emit_label(not_null)
|
||||
emit_2("is_null", t0, left)
|
||||
emit_jump_cond("jump_false", t0, not_bool)
|
||||
emit_2("is_null", t1, right)
|
||||
emit_jump_cond("jump_false", t1, not_bool)
|
||||
emit_1("false", dest)
|
||||
emit_jump(done)
|
||||
|
||||
// Bool path
|
||||
var mismatch = gen_label("ne_mis")
|
||||
emit_label(not_bool)
|
||||
emit_2("is_bool", t0, left)
|
||||
emit_jump_cond("jump_false", t0, mismatch)
|
||||
emit_2("is_bool", t1, right)
|
||||
emit_jump_cond("jump_false", t1, mismatch)
|
||||
emit_3("ne_bool", dest, left, right)
|
||||
emit_jump(done)
|
||||
|
||||
// Mismatch -> true (ne of different types is true)
|
||||
emit_label(mismatch)
|
||||
emit_1("true", dest)
|
||||
emit_label(done)
|
||||
return null
|
||||
}
|
||||
|
||||
// emit_relational: int -> float -> text -> disrupt
|
||||
var emit_relational = function(int_op, float_op, text_op, dest, left, right, left_node, right_node) {
|
||||
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 not_int = null
|
||||
var not_num = null
|
||||
var done = null
|
||||
var err = null
|
||||
|
||||
// Both known int
|
||||
if (left_is_int && right_is_int) {
|
||||
emit_3(int_op, dest, left, right)
|
||||
return null
|
||||
}
|
||||
// Both known number
|
||||
if (left_is_num && right_is_num) {
|
||||
emit_3(float_op, dest, left, right)
|
||||
return null
|
||||
}
|
||||
// Both known text
|
||||
if (left_is_text && right_is_text) {
|
||||
emit_3(text_op, dest, left, right)
|
||||
return null
|
||||
}
|
||||
|
||||
not_int = gen_label("rel_ni")
|
||||
not_num = gen_label("rel_nn")
|
||||
done = gen_label("rel_done")
|
||||
err = gen_label("rel_err")
|
||||
|
||||
t0 = alloc_slot()
|
||||
emit_2("is_int", t0, left)
|
||||
emit_jump_cond("jump_false", t0, not_int)
|
||||
t1 = alloc_slot()
|
||||
emit_2("is_int", t1, right)
|
||||
emit_jump_cond("jump_false", t1, not_int)
|
||||
emit_3(int_op, dest, left, right)
|
||||
emit_jump(done)
|
||||
|
||||
emit_label(not_int)
|
||||
emit_2("is_num", t0, left)
|
||||
emit_jump_cond("jump_false", t0, not_num)
|
||||
emit_2("is_num", t1, right)
|
||||
emit_jump_cond("jump_false", t1, not_num)
|
||||
emit_3(float_op, dest, left, right)
|
||||
emit_jump(done)
|
||||
|
||||
emit_label(not_num)
|
||||
emit_2("is_text", t0, left)
|
||||
emit_jump_cond("jump_false", t0, err)
|
||||
emit_2("is_text", t1, right)
|
||||
emit_jump_cond("jump_false", t1, err)
|
||||
emit_3(text_op, dest, left, right)
|
||||
emit_jump(done)
|
||||
|
||||
emit_label(err)
|
||||
emit_0("disrupt")
|
||||
emit_label(done)
|
||||
return null
|
||||
}
|
||||
|
||||
// emit_neg_decomposed: int path -> float path -> disrupt
|
||||
var emit_neg_decomposed = function(dest, src, src_node) {
|
||||
var t0 = 0
|
||||
var not_int = null
|
||||
var done = null
|
||||
var err = null
|
||||
|
||||
if (is_known_int(src_node)) {
|
||||
emit_2("neg_int", dest, src)
|
||||
return null
|
||||
}
|
||||
if (is_known_number(src_node)) {
|
||||
emit_2("neg_float", dest, src)
|
||||
return null
|
||||
}
|
||||
|
||||
not_int = gen_label("neg_ni")
|
||||
done = gen_label("neg_done")
|
||||
err = gen_label("neg_err")
|
||||
|
||||
t0 = alloc_slot()
|
||||
emit_2("is_int", t0, src)
|
||||
emit_jump_cond("jump_false", t0, not_int)
|
||||
emit_2("neg_int", dest, src)
|
||||
emit_jump(done)
|
||||
|
||||
emit_label(not_int)
|
||||
emit_2("is_num", t0, src)
|
||||
emit_jump_cond("jump_false", t0, err)
|
||||
emit_2("neg_float", dest, src)
|
||||
emit_jump(done)
|
||||
|
||||
emit_label(err)
|
||||
emit_0("disrupt")
|
||||
emit_label(done)
|
||||
return null
|
||||
}
|
||||
|
||||
// Central router: maps op string to decomposition helper
|
||||
var emit_binop = function(op_str, dest, left, right, left_node, right_node) {
|
||||
if (op_str == "add") {
|
||||
emit_add_decomposed(dest, left, right, left_node, right_node)
|
||||
} else if (op_str == "subtract") {
|
||||
emit_numeric_binop("sub_int", "sub_float", dest, left, right, left_node, right_node)
|
||||
} else if (op_str == "multiply") {
|
||||
emit_numeric_binop("mul_int", "mul_float", dest, left, right, left_node, right_node)
|
||||
} else if (op_str == "divide") {
|
||||
emit_numeric_binop("div_int", "div_float", dest, left, right, left_node, right_node)
|
||||
} else if (op_str == "modulo") {
|
||||
emit_numeric_binop("mod_int", "mod_float", dest, left, right, left_node, right_node)
|
||||
} else if (op_str == "eq") {
|
||||
emit_eq_decomposed(dest, left, right, left_node, right_node)
|
||||
} else if (op_str == "ne") {
|
||||
emit_ne_decomposed(dest, left, right, left_node, right_node)
|
||||
} else if (op_str == "lt") {
|
||||
emit_relational("lt_int", "lt_float", "lt_text", dest, left, right, left_node, right_node)
|
||||
} else if (op_str == "le") {
|
||||
emit_relational("le_int", "le_float", "le_text", dest, left, right, left_node, right_node)
|
||||
} else if (op_str == "gt") {
|
||||
emit_relational("gt_int", "gt_float", "gt_text", dest, left, right, left_node, right_node)
|
||||
} else if (op_str == "ge") {
|
||||
emit_relational("ge_int", "ge_float", "ge_text", dest, left, right, left_node, right_node)
|
||||
} else {
|
||||
// Passthrough for bitwise, pow, in, etc.
|
||||
emit_3(op_str, dest, left, right)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
var emit_get_prop = function(dest, obj, prop) {
|
||||
add_instr(["load", dest, obj, prop])
|
||||
}
|
||||
@@ -463,7 +920,7 @@ var mcode = function(ast) {
|
||||
if (op == null) {
|
||||
op = "add"
|
||||
}
|
||||
emit_3(op, dest, left_slot, right_slot)
|
||||
emit_binop(op, dest, left_slot, right_slot, left, right)
|
||||
return dest
|
||||
}
|
||||
|
||||
@@ -515,7 +972,7 @@ var mcode = function(ast) {
|
||||
}
|
||||
right_slot = gen_expr(right, -1)
|
||||
dest = alloc_slot()
|
||||
emit_3(op, dest, left_slot, right_slot)
|
||||
emit_binop(op, dest, left_slot, right_slot, null, right)
|
||||
if (level == 0) {
|
||||
local = find_var(name)
|
||||
if (local >= 0) {
|
||||
@@ -538,7 +995,7 @@ var mcode = function(ast) {
|
||||
emit_get_prop(old_val, obj_slot, prop)
|
||||
right_slot = gen_expr(right, -1)
|
||||
dest = alloc_slot()
|
||||
emit_3(op, dest, old_val, right_slot)
|
||||
emit_binop(op, dest, old_val, right_slot, null, right)
|
||||
emit_set_prop(obj_slot, prop, dest)
|
||||
return dest
|
||||
} else if (left_kind == "[") {
|
||||
@@ -550,7 +1007,7 @@ var mcode = function(ast) {
|
||||
emit_get_elem(old_val, obj_slot, idx_slot)
|
||||
right_slot = gen_expr(right, -1)
|
||||
dest = alloc_slot()
|
||||
emit_3(op, dest, old_val, right_slot)
|
||||
emit_binop(op, dest, old_val, right_slot, null, right)
|
||||
emit_set_elem(obj_slot, idx_slot, dest)
|
||||
return dest
|
||||
}
|
||||
@@ -679,6 +1136,7 @@ var mcode = function(ast) {
|
||||
var arith_op = null
|
||||
var operand_kind = null
|
||||
var one_slot = 0
|
||||
var one_node = null
|
||||
var old_slot = 0
|
||||
var local = 0
|
||||
var new_slot = 0
|
||||
@@ -899,7 +1357,7 @@ var mcode = function(ast) {
|
||||
a0 = gen_expr(args_list[0], -1)
|
||||
a1 = gen_expr(args_list[1], -1)
|
||||
d = alloc_slot()
|
||||
emit_3(mop, d, a0, a1)
|
||||
emit_binop(mop, d, a0, a1, args_list[0], args_list[1])
|
||||
return d
|
||||
}
|
||||
|
||||
@@ -946,7 +1404,7 @@ var mcode = function(ast) {
|
||||
if (kind == "-unary") {
|
||||
operand_slot = gen_expr(expr.expression, -1)
|
||||
slot = alloc_slot()
|
||||
emit_2("neg", slot, operand_slot)
|
||||
emit_neg_decomposed(slot, operand_slot, expr.expression)
|
||||
return slot
|
||||
}
|
||||
if (kind == "+unary") {
|
||||
@@ -961,6 +1419,7 @@ var mcode = function(ast) {
|
||||
operand_kind = operand.kind
|
||||
one_slot = alloc_slot()
|
||||
emit_2("int", one_slot, 1)
|
||||
one_node = {kind: "number", number: 1}
|
||||
|
||||
if (operand_kind == "name") {
|
||||
name = operand.name
|
||||
@@ -983,7 +1442,7 @@ var mcode = function(ast) {
|
||||
emit_access_intrinsic(old_slot, name)
|
||||
}
|
||||
new_slot = alloc_slot()
|
||||
emit_3(arith_op, new_slot, old_slot, one_slot)
|
||||
emit_binop(arith_op, new_slot, old_slot, one_slot, null, one_node)
|
||||
if (level == 0) {
|
||||
local = find_var(name)
|
||||
if (local >= 0) {
|
||||
@@ -1003,7 +1462,7 @@ var mcode = function(ast) {
|
||||
old_slot = alloc_slot()
|
||||
emit_get_prop(old_slot, obj_slot, prop)
|
||||
new_slot = alloc_slot()
|
||||
emit_3(arith_op, new_slot, old_slot, one_slot)
|
||||
emit_binop(arith_op, new_slot, old_slot, one_slot, null, one_node)
|
||||
emit_set_prop(obj_slot, prop, new_slot)
|
||||
return postfix ? old_slot : new_slot
|
||||
} else if (operand_kind == "[") {
|
||||
@@ -1014,7 +1473,7 @@ var mcode = function(ast) {
|
||||
old_slot = alloc_slot()
|
||||
emit_get_elem(old_slot, obj_slot, idx_slot)
|
||||
new_slot = alloc_slot()
|
||||
emit_3(arith_op, new_slot, old_slot, one_slot)
|
||||
emit_binop(arith_op, new_slot, old_slot, one_slot, null, one_node)
|
||||
emit_set_elem(obj_slot, idx_slot, new_slot)
|
||||
return postfix ? old_slot : new_slot
|
||||
}
|
||||
@@ -1452,7 +1911,7 @@ var mcode = function(ast) {
|
||||
case_expr = case_node.expression
|
||||
case_val = gen_expr(case_expr, -1)
|
||||
cmp_slot = alloc_slot()
|
||||
emit_3("eq", cmp_slot, switch_val, case_val)
|
||||
emit_binop("eq", cmp_slot, switch_val, case_val, null, case_expr)
|
||||
emit_jump_cond("jump_true", cmp_slot, case_label)
|
||||
push(case_labels, case_label)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user