616 lines
17 KiB
Plaintext
616 lines
17 KiB
Plaintext
// QBE IL Macro Layer
|
|
// Maps mcode VM operations to QBE intermediate language fragments.
|
|
// Each macro returns a backtick template string of QBE IL.
|
|
// Convention: p = unique prefix for temporaries/labels, result in %{p}
|
|
|
|
// ============================================================
|
|
// Constants
|
|
// ============================================================
|
|
|
|
def js_null = 7
|
|
def js_false = 3
|
|
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
|
|
|
|
// ============================================================
|
|
// Type Checks — each takes (p, v), produces %{p} as w (0 or 1)
|
|
// ============================================================
|
|
|
|
var is_int = function(p, v) {
|
|
return ` %${p}.t =l and ${v}, 1
|
|
%${p} =w ceql %${p}.t, 0
|
|
`
|
|
}
|
|
|
|
var is_number = function(p, v) {
|
|
return ` %${p}.t1 =l and ${v}, 1
|
|
%${p}.ii =w ceql %${p}.t1, 0
|
|
%${p}.t2 =l and ${v}, 7
|
|
%${p}.fi =w ceql %${p}.t2, 5
|
|
%${p} =w or %${p}.ii, %${p}.fi
|
|
`
|
|
}
|
|
|
|
var is_null = function(p, v) {
|
|
return ` %${p}.t =l and ${v}, 31
|
|
%${p} =w ceql %${p}.t, 7
|
|
`
|
|
}
|
|
|
|
var is_bool = function(p, v) {
|
|
return ` %${p}.t =l and ${v}, 31
|
|
%${p} =w ceql %${p}.t, 3
|
|
`
|
|
}
|
|
|
|
var is_exception = function(p, v) {
|
|
return ` %${p}.t =l and ${v}, 31
|
|
%${p} =w ceql %${p}.t, 15
|
|
`
|
|
}
|
|
|
|
var is_ptr = function(p, v) {
|
|
return ` %${p}.t =l and ${v}, 7
|
|
%${p} =w ceql %${p}.t, 1
|
|
`
|
|
}
|
|
|
|
var is_imm_text = function(p, v) {
|
|
return ` %${p}.t =l and ${v}, 31
|
|
%${p} =w ceql %${p}.t, 27
|
|
`
|
|
}
|
|
|
|
var is_text = function(p, v) {
|
|
return ` %${p}.imm =l and ${v}, 31
|
|
%${p}.is_imm =w ceql %${p}.imm, 27
|
|
jnz %${p}.is_imm, @${p}.yes, @${p}.chk_ptr
|
|
@${p}.chk_ptr
|
|
%${p}.ptag =l and ${v}, 7
|
|
%${p}.is_ptr =w ceql %${p}.ptag, 1
|
|
jnz %${p}.is_ptr, @${p}.load_hdr, @${p}.no
|
|
@${p}.load_hdr
|
|
%${p}.ptr =l and ${v}, -8
|
|
%${p}.hdr =l loadl %${p}.ptr
|
|
@${p}.chase
|
|
%${p}.ht =l and %${p}.hdr, 7
|
|
%${p}.is_fwd =w ceql %${p}.ht, 7
|
|
jnz %${p}.is_fwd, @${p}.follow, @${p}.chk_type
|
|
@${p}.follow
|
|
%${p}.ptr =l shr %${p}.hdr, 3
|
|
%${p}.hdr =l loadl %${p}.ptr
|
|
jmp @${p}.chase
|
|
@${p}.chk_type
|
|
%${p} =w ceql %${p}.ht, 2
|
|
jmp @${p}.done
|
|
@${p}.yes
|
|
%${p} =w copy 1
|
|
jmp @${p}.done
|
|
@${p}.no
|
|
%${p} =w copy 0
|
|
jmp @${p}.done
|
|
@${p}.done
|
|
`
|
|
}
|
|
|
|
// ============================================================
|
|
// Value Extraction
|
|
// ============================================================
|
|
|
|
// get_int(p, v) — extract int32 as w from tagged int. Result: %{p}
|
|
var get_int = function(p, v) {
|
|
return ` %${p}.sl =l sar ${v}, 1
|
|
%${p} =w copy %${p}.sl
|
|
`
|
|
}
|
|
|
|
// get_bool(p, v) — extract bool as w. Result: %{p}
|
|
var get_bool = function(p, v) {
|
|
return ` %${p}.sl =l shr ${v}, 5
|
|
%${p} =w and %${p}.sl, 1
|
|
`
|
|
}
|
|
|
|
// get_ptr(p, v) — extract pointer as l. Result: %{p}
|
|
var get_ptr = function(p, v) {
|
|
return ` %${p} =l and ${v}, -8
|
|
`
|
|
}
|
|
|
|
// get_float64(p, v) — decode short float to d. Result: %{p} as d
|
|
// Caller must handle zero check (sexp == 0 -> 0.0) before calling.
|
|
var get_float64 = function(p, v) {
|
|
return ` %${p}.sign =l shr ${v}, 63
|
|
%${p}.sexp =l shr ${v}, 55
|
|
%${p}.sexp =l and %${p}.sexp, 255
|
|
%${p}.mant =l shr ${v}, 3
|
|
%${p}.mant =l and %${p}.mant, ${mantissa_mask}
|
|
%${p}.dexp =l sub %${p}.sexp, 127
|
|
%${p}.dexp =l add %${p}.dexp, 1023
|
|
%${p}.s63 =l shl %${p}.sign, 63
|
|
%${p}.e52 =l shl %${p}.dexp, 52
|
|
%${p}.bits =l or %${p}.s63, %${p}.e52
|
|
%${p}.bits =l or %${p}.bits, %${p}.mant
|
|
%${p} =d cast %${p}.bits
|
|
`
|
|
}
|
|
|
|
// to_float64(p, v) — convert any numeric value (int or short float) to d.
|
|
// Result: %{p} as d
|
|
var to_float64 = function(p, v) {
|
|
return ` %${p}.tag =l and ${v}, 1
|
|
%${p}.is_int =w ceql %${p}.tag, 0
|
|
jnz %${p}.is_int, @${p}.from_int, @${p}.from_float
|
|
@${p}.from_int
|
|
%${p}.isl =l sar ${v}, 1
|
|
%${p}.iw =w copy %${p}.isl
|
|
%${p} =d swtof %${p}.iw
|
|
jmp @${p}.done
|
|
@${p}.from_float
|
|
%${p}.fsexp =l shr ${v}, 55
|
|
%${p}.fsexp =l and %${p}.fsexp, 255
|
|
%${p}.is_zero =w ceql %${p}.fsexp, 0
|
|
jnz %${p}.is_zero, @${p}.fzero, @${p}.fdecode
|
|
@${p}.fzero
|
|
%${p} =d copy d_0.0
|
|
jmp @${p}.done
|
|
@${p}.fdecode
|
|
%${p}.fsign =l shr ${v}, 63
|
|
%${p}.fmant =l shr ${v}, 3
|
|
%${p}.fmant =l and %${p}.fmant, ${mantissa_mask}
|
|
%${p}.fdexp =l sub %${p}.fsexp, 127
|
|
%${p}.fdexp =l add %${p}.fdexp, 1023
|
|
%${p}.fs63 =l shl %${p}.fsign, 63
|
|
%${p}.fe52 =l shl %${p}.fdexp, 52
|
|
%${p}.fbits =l or %${p}.fs63, %${p}.fe52
|
|
%${p}.fbits =l or %${p}.fbits, %${p}.fmant
|
|
%${p} =d cast %${p}.fbits
|
|
jmp @${p}.done
|
|
@${p}.done
|
|
`
|
|
}
|
|
|
|
// ============================================================
|
|
// Value Creation
|
|
// ============================================================
|
|
|
|
// new_int(p, i) — tag int32 w as JSValue l. Result: %{p}
|
|
var new_int = function(p, i) {
|
|
return ` %${p}.ext =l extuw ${i}
|
|
%${p} =l shl %${p}.ext, 1
|
|
`
|
|
}
|
|
|
|
// new_bool(p, b) — tag bool w as JSValue l. Result: %{p}
|
|
var new_bool = function(p, b) {
|
|
return ` %${p}.ext =l extuw ${b}
|
|
%${p}.sh =l shl %${p}.ext, 5
|
|
%${p} =l or %${p}.sh, 3
|
|
`
|
|
}
|
|
|
|
// new_float64 — C call to __JS_NewFloat64(ctx, val). Result: %{p}
|
|
var new_float64 = function(p, ctx, d) {
|
|
return ` %${p} =l call $qbe_new_float64(l ${ctx}, d ${d})
|
|
`
|
|
}
|
|
|
|
// ============================================================
|
|
// Arithmetic — add/sub/mul/div/mod(p, ctx, a, b)
|
|
// Simple C call wrappers. Type dispatch is handled in mcode.cm.
|
|
// ============================================================
|
|
|
|
var add = function(p, ctx, a, b) {
|
|
return ` %${p} =l call $qbe_float_add(l ${ctx}, l ${a}, l ${b})
|
|
`
|
|
}
|
|
|
|
var sub = function(p, ctx, a, b) {
|
|
return ` %${p} =l call $qbe_float_sub(l ${ctx}, l ${a}, l ${b})
|
|
`
|
|
}
|
|
|
|
var mul = function(p, ctx, a, b) {
|
|
return ` %${p} =l call $qbe_float_mul(l ${ctx}, l ${a}, l ${b})
|
|
`
|
|
}
|
|
|
|
var div = function(p, ctx, a, b) {
|
|
return ` %${p} =l call $qbe_float_div(l ${ctx}, l ${a}, l ${b})
|
|
`
|
|
}
|
|
|
|
var mod = function(p, ctx, a, b) {
|
|
return ` %${p} =l call $qbe_float_mod(l ${ctx}, l ${a}, l ${b})
|
|
`
|
|
}
|
|
|
|
// ============================================================
|
|
// Comparisons — eq, ne, lt, le, gt, ge
|
|
// Each takes (p, ctx, a, b), produces %{p} as l (tagged JSValue bool)
|
|
// ============================================================
|
|
|
|
// Helper: generate comparison for a given op string and int comparison QBE op
|
|
// 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
|
|
var mismatch_val = js_false
|
|
var null_val = js_false
|
|
if (_qflags.is_eq || _qflags.is_ne) {
|
|
eq_only = 1
|
|
}
|
|
if (_qflags.is_ne) {
|
|
mismatch_val = js_true
|
|
}
|
|
if (_qflags.null_true) {
|
|
null_val = js_true
|
|
}
|
|
return `@${p}.start
|
|
%${p}.at =l and ${a}, 1
|
|
%${p}.bt =l and ${b}, 1
|
|
%${p}.not_int =l or %${p}.at, %${p}.bt
|
|
jnz %${p}.not_int, @${p}.not_both_int, @${p}.int_path
|
|
@${p}.int_path
|
|
%${p}.ia =l sar ${a}, 1
|
|
%${p}.ib =l sar ${b}, 1
|
|
%${p}.iw =w copy %${p}.ia
|
|
%${p}.ibw =w copy %${p}.ib
|
|
%${p}.cr =w ${int_cmp_op} %${p}.iw, %${p}.ibw
|
|
%${p}.crext =l extuw %${p}.cr
|
|
%${p}.sh =l shl %${p}.crext, 5
|
|
%${p} =l or %${p}.sh, 3
|
|
jmp @${p}.done
|
|
@${p}.not_both_int
|
|
%${p}.a_tag5 =l and ${a}, 31
|
|
%${p}.b_tag5 =l and ${b}, 31
|
|
%${p}.a_is_null =w ceql %${p}.a_tag5, 7
|
|
%${p}.b_is_null =w ceql %${p}.b_tag5, 7
|
|
%${p}.both_null =w and %${p}.a_is_null, %${p}.b_is_null
|
|
jnz %${p}.both_null, @${p}.null_path, @${p}.chk_bool
|
|
@${p}.null_path
|
|
%${p} =l copy ${null_val}
|
|
jmp @${p}.done
|
|
@${p}.chk_bool
|
|
%${p}.a_is_bool =w ceql %${p}.a_tag5, 3
|
|
%${p}.b_is_bool =w ceql %${p}.b_tag5, 3
|
|
%${p}.both_bool =w and %${p}.a_is_bool, %${p}.b_is_bool
|
|
jnz %${p}.both_bool, @${p}.bool_path, @${p}.chk_num
|
|
@${p}.bool_path
|
|
%${p}.ba =l shr ${a}, 5
|
|
%${p}.baw =w and %${p}.ba, 1
|
|
%${p}.bb =l shr ${b}, 5
|
|
%${p}.bbw =w and %${p}.bb, 1
|
|
%${p}.bcr =w ${int_cmp_op} %${p}.baw, %${p}.bbw
|
|
%${p}.bcrext =l extuw %${p}.bcr
|
|
%${p}.bsh =l shl %${p}.bcrext, 5
|
|
%${p} =l or %${p}.bsh, 3
|
|
jmp @${p}.done
|
|
@${p}.chk_num
|
|
%${p}.a_is_num =w call $JS_IsNumber(l ${a})
|
|
%${p}.b_is_num =w call $JS_IsNumber(l ${b})
|
|
%${p}.both_num =w and %${p}.a_is_num, %${p}.b_is_num
|
|
jnz %${p}.both_num, @${p}.num_path, @${p}.chk_text
|
|
@${p}.num_path
|
|
%${p}.fcr =w call $qbe_float_cmp(l ${ctx}, w ${float_cmp_op_id}, l ${a}, l ${b})
|
|
%${p}.fcrext =l extuw %${p}.fcr
|
|
%${p}.fsh =l shl %${p}.fcrext, 5
|
|
%${p} =l or %${p}.fsh, 3
|
|
jmp @${p}.done
|
|
@${p}.chk_text
|
|
%${p}.a_is_text =w call $JS_IsText(l ${a})
|
|
%${p}.b_is_text =w call $JS_IsText(l ${b})
|
|
%${p}.both_text =w and %${p}.a_is_text, %${p}.b_is_text
|
|
jnz %${p}.both_text, @${p}.text_path, @${p}.mismatch
|
|
@${p}.text_path
|
|
%${p}.scmp =w call $js_string_compare_value(l ${ctx}, l ${a}, l ${b}, w ${eq_only})
|
|
%${p}.tcr =w ${int_cmp_op} %${p}.scmp, 0
|
|
%${p}.tcrext =l extuw %${p}.tcr
|
|
%${p}.tsh =l shl %${p}.tcrext, 5
|
|
%${p} =l or %${p}.tsh, 3
|
|
jmp @${p}.done
|
|
@${p}.mismatch
|
|
%${p} =l copy ${mismatch_val}
|
|
jmp @${p}.done
|
|
@${p}.done
|
|
`
|
|
}
|
|
|
|
// Comparison op IDs matching MACH_EQ..MACH_GE order for qbe_float_cmp
|
|
// 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) {
|
|
_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) {
|
|
_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) {
|
|
_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) {
|
|
_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) {
|
|
_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) {
|
|
_qflags = {int_cmp_op: "csgew", float_id: 5, is_eq: false, is_ne: false, null_true: true}
|
|
return cmp(p, ctx, a, b)
|
|
}
|
|
|
|
// ============================================================
|
|
// Unary Ops
|
|
// ============================================================
|
|
|
|
// neg(p, ctx, v) — negate via C call (type guards in mcode)
|
|
var neg = function(p, ctx, v) {
|
|
return ` %${p} =l call $qbe_float_neg(l ${ctx}, l ${v})
|
|
`
|
|
}
|
|
|
|
// inc(p, ctx, v) — increment via C call (type guards in mcode)
|
|
var inc = function(p, ctx, v) {
|
|
return ` %${p} =l call $qbe_float_inc(l ${ctx}, l ${v})
|
|
`
|
|
}
|
|
|
|
// dec(p, ctx, v) — decrement via C call (type guards in mcode)
|
|
var dec = function(p, ctx, v) {
|
|
return ` %${p} =l call $qbe_float_dec(l ${ctx}, l ${v})
|
|
`
|
|
}
|
|
|
|
// lnot(p, ctx, v) — logical not. C call to JS_ToBool, then negate inline.
|
|
var lnot = function(p, ctx, v) {
|
|
return ` %${p}.bval =w call $JS_ToBool(l ${ctx}, l ${v})
|
|
%${p}.neg =w ceqw %${p}.bval, 0
|
|
%${p}.nex =l extuw %${p}.neg
|
|
%${p}.sh =l shl %${p}.nex, 5
|
|
%${p} =l or %${p}.sh, 3
|
|
`
|
|
}
|
|
|
|
// bnot(p, ctx, v) — bitwise not via C call
|
|
var bnot = function(p, ctx, v) {
|
|
return ` %${p} =l call $qbe_bnot(l ${ctx}, l ${v})
|
|
`
|
|
}
|
|
|
|
// ============================================================
|
|
// Bitwise Ops — band, bor, bxor, shl, shr, ushr
|
|
// Both operands must be numeric. Int fast path, float -> convert to int32.
|
|
// ============================================================
|
|
|
|
var band = function(p, ctx, a, b) {
|
|
return ` %${p} =l call $qbe_bitwise_and(l ${ctx}, l ${a}, l ${b})
|
|
`
|
|
}
|
|
|
|
var bor = function(p, ctx, a, b) {
|
|
return ` %${p} =l call $qbe_bitwise_or(l ${ctx}, l ${a}, l ${b})
|
|
`
|
|
}
|
|
|
|
var bxor = function(p, ctx, a, b) {
|
|
return ` %${p} =l call $qbe_bitwise_xor(l ${ctx}, l ${a}, l ${b})
|
|
`
|
|
}
|
|
|
|
var shl = function(p, ctx, a, b) {
|
|
return ` %${p} =l call $qbe_shift_shl(l ${ctx}, l ${a}, l ${b})
|
|
`
|
|
}
|
|
|
|
var shr = function(p, ctx, a, b) {
|
|
return ` %${p} =l call $qbe_shift_sar(l ${ctx}, l ${a}, l ${b})
|
|
`
|
|
}
|
|
|
|
var ushr = function(p, ctx, a, b) {
|
|
return ` %${p} =l call $qbe_shift_shr(l ${ctx}, l ${a}, l ${b})
|
|
`
|
|
}
|
|
|
|
// ============================================================
|
|
// Decomposed per-type-path operations
|
|
// These map directly to the new IR ops emitted by mcode.cm.
|
|
// ============================================================
|
|
|
|
// --- Text concat ---
|
|
var concat = function(p, ctx, a, b) {
|
|
return ` %${p} =l call $JS_ConcatString(l ${ctx}, l ${a}, l ${b})
|
|
`
|
|
}
|
|
|
|
// --- Comparisons (int path) ---
|
|
var cmp_int = function(p, a, b, qbe_op) {
|
|
return ` %${p}.ia =l sar ${a}, 1
|
|
%${p}.ib =l sar ${b}, 1
|
|
%${p}.iaw =w copy %${p}.ia
|
|
%${p}.ibw =w copy %${p}.ib
|
|
%${p}.cr =w ${qbe_op} %${p}.iaw, %${p}.ibw
|
|
%${p}.crext =l extuw %${p}.cr
|
|
%${p}.sh =l shl %${p}.crext, 5
|
|
%${p} =l or %${p}.sh, 3
|
|
`
|
|
}
|
|
|
|
var eq_int = function(p, ctx, a, b) { return cmp_int(p, a, b, "ceqw") }
|
|
var ne_int = function(p, ctx, a, b) { return cmp_int(p, a, b, "cnew") }
|
|
var lt_int = function(p, ctx, a, b) { return cmp_int(p, a, b, "csltw") }
|
|
var le_int = function(p, ctx, a, b) { return cmp_int(p, a, b, "cslew") }
|
|
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) ---
|
|
// 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
|
|
%${p} =l or %${p}.fsh, 3
|
|
`
|
|
}
|
|
|
|
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) ---
|
|
// 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
|
|
%${p}.tsh =l shl %${p}.tcrext, 5
|
|
%${p} =l or %${p}.tsh, 3
|
|
`
|
|
}
|
|
|
|
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) {
|
|
return ` %${p}.cr =w ceql ${a}, ${b}
|
|
%${p}.crext =l extuw %${p}.cr
|
|
%${p}.sh =l shl %${p}.crext, 5
|
|
%${p} =l or %${p}.sh, 3
|
|
`
|
|
}
|
|
|
|
var ne_bool = function(p, a, b) {
|
|
return ` %${p}.cr =w cnel ${a}, ${b}
|
|
%${p}.crext =l extuw %${p}.cr
|
|
%${p}.sh =l shl %${p}.crext, 5
|
|
%${p} =l or %${p}.sh, 3
|
|
`
|
|
}
|
|
|
|
// --- Type guard: is_identical ---
|
|
var is_identical = function(p, a, b) {
|
|
return ` %${p}.cr =w ceql ${a}, ${b}
|
|
%${p}.crext =l extuw %${p}.cr
|
|
%${p}.sh =l shl %${p}.crext, 5
|
|
%${p} =l or %${p}.sh, 3
|
|
`
|
|
}
|
|
|
|
// ============================================================
|
|
// Module export
|
|
// ============================================================
|
|
|
|
return {
|
|
// constants
|
|
js_null: js_null,
|
|
js_false: js_false,
|
|
js_true: js_true,
|
|
js_exception: js_exception,
|
|
js_empty_text: js_empty_text,
|
|
// type checks
|
|
is_int: is_int,
|
|
is_number: is_number,
|
|
is_null: is_null,
|
|
is_bool: is_bool,
|
|
is_exception: is_exception,
|
|
is_ptr: is_ptr,
|
|
is_imm_text: is_imm_text,
|
|
is_text: is_text,
|
|
// value extraction
|
|
get_int: get_int,
|
|
get_bool: get_bool,
|
|
get_ptr: get_ptr,
|
|
get_float64: get_float64,
|
|
to_float64: to_float64,
|
|
// value creation
|
|
new_int: new_int,
|
|
new_bool: new_bool,
|
|
new_float64: new_float64,
|
|
// arithmetic
|
|
add: add,
|
|
sub: sub,
|
|
mul: mul,
|
|
div: div,
|
|
mod: mod,
|
|
// comparisons
|
|
eq: eq,
|
|
ne: ne,
|
|
lt: lt,
|
|
le: le,
|
|
gt: gt,
|
|
ge: ge,
|
|
// unary
|
|
neg: neg,
|
|
inc: inc,
|
|
dec: dec,
|
|
lnot: lnot,
|
|
bnot: bnot,
|
|
// bitwise
|
|
band: band,
|
|
bor: bor,
|
|
bxor: bxor,
|
|
shl: shl,
|
|
shr: shr,
|
|
ushr: ushr,
|
|
// text concat
|
|
concat: concat,
|
|
// decomposed comparisons (int)
|
|
eq_int: eq_int,
|
|
ne_int: ne_int,
|
|
lt_int: lt_int,
|
|
le_int: le_int,
|
|
gt_int: gt_int,
|
|
ge_int: ge_int,
|
|
// decomposed comparisons (float)
|
|
eq_float: eq_float,
|
|
ne_float: ne_float,
|
|
lt_float: lt_float,
|
|
le_float: le_float,
|
|
gt_float: gt_float,
|
|
ge_float: ge_float,
|
|
// decomposed comparisons (text)
|
|
eq_text: eq_text,
|
|
ne_text: ne_text,
|
|
lt_text: lt_text,
|
|
le_text: le_text,
|
|
gt_text: gt_text,
|
|
ge_text: ge_text,
|
|
// decomposed comparisons (bool)
|
|
eq_bool: eq_bool,
|
|
ne_bool: ne_bool,
|
|
// type guard
|
|
is_identical: is_identical
|
|
}
|