1058 lines
29 KiB
Plaintext
1058 lines
29 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
|
|
|
|
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
|
|
@${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
|
|
@${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 $__JS_NewFloat64(l ${ctx}, d ${d})
|
|
`
|
|
}
|
|
|
|
// ============================================================
|
|
// Arithmetic — add(p, ctx, a, b)
|
|
// Int fast path inline, text concat and float as C calls.
|
|
// Jumps to @disrupt on type mismatch.
|
|
// ============================================================
|
|
|
|
var add = function(p, ctx, a, b) {
|
|
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}.sum =l add %${p}.ia, %${p}.ib
|
|
%${p}.lo =w csltl %${p}.sum, ${int32_min}
|
|
%${p}.hi =w csgtl %${p}.sum, ${int32_max}
|
|
%${p}.ov =w or %${p}.lo, %${p}.hi
|
|
jnz %${p}.ov, @${p}.int_overflow, @${p}.int_ok
|
|
@${p}.int_ok
|
|
%${p}.rw =w copy %${p}.sum
|
|
%${p}.rext =l extuw %${p}.rw
|
|
%${p} =l shl %${p}.rext, 1
|
|
jmp @${p}.done
|
|
@${p}.int_overflow
|
|
%${p}.fd =d sltof %${p}.sum
|
|
%${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.fd)
|
|
jmp @${p}.done
|
|
@${p}.not_both_int
|
|
%${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}.chk_num
|
|
@${p}.text_path
|
|
%${p} =l call $JS_ConcatString(l ${ctx}, l ${a}, l ${b})
|
|
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}.float_path, @disrupt
|
|
@${p}.float_path
|
|
%${p} =l call $qbe_float_add(l ${ctx}, l ${a}, l ${b})
|
|
@${p}.done
|
|
`
|
|
}
|
|
|
|
var sub = function(p, ctx, a, b) {
|
|
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}.diff =l sub %${p}.ia, %${p}.ib
|
|
%${p}.lo =w csltl %${p}.diff, ${int32_min}
|
|
%${p}.hi =w csgtl %${p}.diff, ${int32_max}
|
|
%${p}.ov =w or %${p}.lo, %${p}.hi
|
|
jnz %${p}.ov, @${p}.int_overflow, @${p}.int_ok
|
|
@${p}.int_ok
|
|
%${p}.rw =w copy %${p}.diff
|
|
%${p}.rext =l extuw %${p}.rw
|
|
%${p} =l shl %${p}.rext, 1
|
|
jmp @${p}.done
|
|
@${p}.int_overflow
|
|
%${p}.fd =d sltof %${p}.diff
|
|
%${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.fd)
|
|
jmp @${p}.done
|
|
@${p}.not_both_int
|
|
%${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}.float_path, @disrupt
|
|
@${p}.float_path
|
|
%${p} =l call $qbe_float_sub(l ${ctx}, l ${a}, l ${b})
|
|
@${p}.done
|
|
`
|
|
}
|
|
|
|
var mul = function(p, ctx, a, b) {
|
|
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}.prod =l mul %${p}.ia, %${p}.ib
|
|
%${p}.lo =w csltl %${p}.prod, ${int32_min}
|
|
%${p}.hi =w csgtl %${p}.prod, ${int32_max}
|
|
%${p}.ov =w or %${p}.lo, %${p}.hi
|
|
jnz %${p}.ov, @${p}.int_overflow, @${p}.int_ok
|
|
@${p}.int_ok
|
|
%${p}.rw =w copy %${p}.prod
|
|
%${p}.rext =l extuw %${p}.rw
|
|
%${p} =l shl %${p}.rext, 1
|
|
jmp @${p}.done
|
|
@${p}.int_overflow
|
|
%${p}.fd =d sltof %${p}.prod
|
|
%${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.fd)
|
|
jmp @${p}.done
|
|
@${p}.not_both_int
|
|
%${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}.float_path, @disrupt
|
|
@${p}.float_path
|
|
%${p} =l call $qbe_float_mul(l ${ctx}, l ${a}, l ${b})
|
|
@${p}.done
|
|
`
|
|
}
|
|
|
|
var div = function(p, ctx, a, b) {
|
|
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 =w copy 0
|
|
%${p}.tmp =l sar ${a}, 1
|
|
%${p}.ia =w copy %${p}.tmp
|
|
%${p}.ib =w copy 0
|
|
%${p}.tmp2 =l sar ${b}, 1
|
|
%${p}.ib =w copy %${p}.tmp2
|
|
%${p}.div0 =w ceqw %${p}.ib, 0
|
|
jnz %${p}.div0, @${p}.ret_null, @${p}.chk_exact
|
|
@${p}.ret_null
|
|
%${p} =l copy ${js_null}
|
|
jmp @${p}.done
|
|
@${p}.chk_exact
|
|
%${p}.rem =w rem %${p}.ia, %${p}.ib
|
|
%${p}.exact =w ceqw %${p}.rem, 0
|
|
jnz %${p}.exact, @${p}.int_div, @${p}.int_to_float
|
|
@${p}.int_div
|
|
%${p}.q =w div %${p}.ia, %${p}.ib
|
|
%${p}.qext =l extuw %${p}.q
|
|
%${p} =l shl %${p}.qext, 1
|
|
jmp @${p}.done
|
|
@${p}.int_to_float
|
|
%${p}.da =d swtof %${p}.ia
|
|
%${p}.db =d swtof %${p}.ib
|
|
%${p}.dr =d div %${p}.da, %${p}.db
|
|
%${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.dr)
|
|
jmp @${p}.done
|
|
@${p}.not_both_int
|
|
%${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}.float_path, @disrupt
|
|
@${p}.float_path
|
|
%${p} =l call $qbe_float_div(l ${ctx}, l ${a}, l ${b})
|
|
@${p}.done
|
|
`
|
|
}
|
|
|
|
var mod = function(p, ctx, a, b) {
|
|
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 =w copy 0
|
|
%${p}.tmp =l sar ${a}, 1
|
|
%${p}.ia =w copy %${p}.tmp
|
|
%${p}.ib =w copy 0
|
|
%${p}.tmp2 =l sar ${b}, 1
|
|
%${p}.ib =w copy %${p}.tmp2
|
|
%${p}.div0 =w ceqw %${p}.ib, 0
|
|
jnz %${p}.div0, @${p}.ret_null, @${p}.do_mod
|
|
@${p}.ret_null
|
|
%${p} =l copy ${js_null}
|
|
jmp @${p}.done
|
|
@${p}.do_mod
|
|
%${p}.r =w rem %${p}.ia, %${p}.ib
|
|
%${p}.rext =l extuw %${p}.r
|
|
%${p} =l shl %${p}.rext, 1
|
|
jmp @${p}.done
|
|
@${p}.not_both_int
|
|
%${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}.float_path, @disrupt
|
|
@${p}.float_path
|
|
%${p} =l call $qbe_float_mod(l ${ctx}, l ${a}, l ${b})
|
|
@${p}.done
|
|
`
|
|
}
|
|
|
|
// ============================================================
|
|
// 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
|
|
// null_true: whether null==null returns true (eq, le, ge) or false (ne, lt, gt)
|
|
var cmp = function(p, ctx, a, b, int_cmp_op, float_cmp_op_id, is_eq, is_ne, null_true) {
|
|
var eq_only = 0
|
|
if (is_eq || is_ne) {
|
|
eq_only = 1
|
|
}
|
|
var mismatch_val = js_false
|
|
if (is_ne) {
|
|
mismatch_val = js_true
|
|
}
|
|
var null_val = js_false
|
|
if (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}
|
|
@${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) {
|
|
return cmp(p, ctx, a, b, "ceqw", 0, true, false, true)
|
|
}
|
|
|
|
var ne = function(p, ctx, a, b) {
|
|
return cmp(p, ctx, a, b, "cnew", 1, false, true, false)
|
|
}
|
|
|
|
var lt = function(p, ctx, a, b) {
|
|
return cmp(p, ctx, a, b, "csltw", 2, false, false, false)
|
|
}
|
|
|
|
var le = function(p, ctx, a, b) {
|
|
return cmp(p, ctx, a, b, "cslew", 3, false, false, true)
|
|
}
|
|
|
|
var gt = function(p, ctx, a, b) {
|
|
return cmp(p, ctx, a, b, "csgtw", 4, false, false, false)
|
|
}
|
|
|
|
var ge = function(p, ctx, a, b) {
|
|
return cmp(p, ctx, a, b, "csgew", 5, false, false, true)
|
|
}
|
|
|
|
// ============================================================
|
|
// Unary Ops
|
|
// ============================================================
|
|
|
|
// neg(p, ctx, v) — negate. Int fast path (INT32_MIN edge case), else C call.
|
|
var neg = function(p, ctx, v) {
|
|
return `@${p}.start
|
|
%${p}.tag =l and ${v}, 1
|
|
%${p}.is_int =w ceql %${p}.tag, 0
|
|
jnz %${p}.is_int, @${p}.int_path, @${p}.float_path
|
|
@${p}.int_path
|
|
%${p}.sl =l sar ${v}, 1
|
|
%${p}.iw =w copy %${p}.sl
|
|
%${p}.is_min =w ceqw %${p}.iw, ${int32_min}
|
|
jnz %${p}.is_min, @${p}.min_overflow, @${p}.int_ok
|
|
@${p}.min_overflow
|
|
%${p}.fd =d swtof %${p}.iw
|
|
%${p}.fdn =d neg %${p}.fd
|
|
%${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.fdn)
|
|
jmp @${p}.done
|
|
@${p}.int_ok
|
|
%${p}.ni =w sub 0, %${p}.iw
|
|
%${p}.niext =l extuw %${p}.ni
|
|
%${p} =l shl %${p}.niext, 1
|
|
jmp @${p}.done
|
|
@${p}.float_path
|
|
%${p} =l call $qbe_float_neg(l ${ctx}, l ${v})
|
|
@${p}.done
|
|
`
|
|
}
|
|
|
|
// inc(p, ctx, v) — increment. Int fast path (INT32_MAX edge case), else C call.
|
|
var inc = function(p, ctx, v) {
|
|
return `@${p}.start
|
|
%${p}.tag =l and ${v}, 1
|
|
%${p}.is_int =w ceql %${p}.tag, 0
|
|
jnz %${p}.is_int, @${p}.int_path, @${p}.float_path
|
|
@${p}.int_path
|
|
%${p}.sl =l sar ${v}, 1
|
|
%${p}.iw =w copy %${p}.sl
|
|
%${p}.is_max =w ceqw %${p}.iw, ${int32_max}
|
|
jnz %${p}.is_max, @${p}.max_overflow, @${p}.int_ok
|
|
@${p}.max_overflow
|
|
%${p}.fd =d swtof %${p}.iw
|
|
%${p}.fd1 =d add %${p}.fd, d_1.0
|
|
%${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.fd1)
|
|
jmp @${p}.done
|
|
@${p}.int_ok
|
|
%${p}.ni =w add %${p}.iw, 1
|
|
%${p}.niext =l extuw %${p}.ni
|
|
%${p} =l shl %${p}.niext, 1
|
|
jmp @${p}.done
|
|
@${p}.float_path
|
|
%${p} =l call $qbe_float_inc(l ${ctx}, l ${v})
|
|
@${p}.done
|
|
`
|
|
}
|
|
|
|
// dec(p, ctx, v) — decrement. Int fast path (INT32_MIN edge case), else C call.
|
|
var dec = function(p, ctx, v) {
|
|
return `@${p}.start
|
|
%${p}.tag =l and ${v}, 1
|
|
%${p}.is_int =w ceql %${p}.tag, 0
|
|
jnz %${p}.is_int, @${p}.int_path, @${p}.float_path
|
|
@${p}.int_path
|
|
%${p}.sl =l sar ${v}, 1
|
|
%${p}.iw =w copy %${p}.sl
|
|
%${p}.is_min =w ceqw %${p}.iw, ${int32_min}
|
|
jnz %${p}.is_min, @${p}.min_overflow, @${p}.int_ok
|
|
@${p}.min_overflow
|
|
%${p}.fd =d swtof %${p}.iw
|
|
%${p}.fd1 =d sub %${p}.fd, d_1.0
|
|
%${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.fd1)
|
|
jmp @${p}.done
|
|
@${p}.int_ok
|
|
%${p}.ni =w sub %${p}.iw, 1
|
|
%${p}.niext =l extuw %${p}.ni
|
|
%${p} =l shl %${p}.niext, 1
|
|
jmp @${p}.done
|
|
@${p}.float_path
|
|
%${p} =l call $qbe_float_dec(l ${ctx}, l ${v})
|
|
@${p}.done
|
|
`
|
|
}
|
|
|
|
// 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. Convert to int32, ~, re-tag.
|
|
var bnot = function(p, ctx, v) {
|
|
return `@${p}.start
|
|
%${p}.tag =l and ${v}, 1
|
|
%${p}.is_int =w ceql %${p}.tag, 0
|
|
jnz %${p}.is_int, @${p}.int_path, @${p}.slow_path
|
|
@${p}.int_path
|
|
%${p}.sl =l sar ${v}, 1
|
|
%${p}.iw =w copy %${p}.sl
|
|
%${p}.nw =w xor %${p}.iw, -1
|
|
%${p}.nex =l extuw %${p}.nw
|
|
%${p} =l shl %${p}.nex, 1
|
|
jmp @${p}.done
|
|
@${p}.slow_path
|
|
%${p} =l call $qbe_bnot(l ${ctx}, l ${v})
|
|
@${p}.done
|
|
`
|
|
}
|
|
|
|
// ============================================================
|
|
// Bitwise Ops — band, bor, bxor, shl, shr, ushr
|
|
// Both operands must be numeric. Int fast path, float -> convert to int32.
|
|
// ============================================================
|
|
|
|
var bitwise_op = function(p, ctx, a, b, qbe_op) {
|
|
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}.slow_path, @${p}.int_path
|
|
@${p}.int_path
|
|
%${p}.ia =l sar ${a}, 1
|
|
%${p}.iaw =w copy %${p}.ia
|
|
%${p}.ib =l sar ${b}, 1
|
|
%${p}.ibw =w copy %${p}.ib
|
|
%${p}.rw =w ${qbe_op} %${p}.iaw, %${p}.ibw
|
|
%${p}.rext =l extuw %${p}.rw
|
|
%${p} =l shl %${p}.rext, 1
|
|
jmp @${p}.done
|
|
@${p}.slow_path
|
|
%${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}.float_to_int, @disrupt
|
|
@${p}.float_to_int
|
|
%${p} =l call $qbe_bitwise_${qbe_op}(l ${ctx}, l ${a}, l ${b})
|
|
@${p}.done
|
|
`
|
|
}
|
|
|
|
var band = function(p, ctx, a, b) {
|
|
return bitwise_op(p, ctx, a, b, "and")
|
|
}
|
|
|
|
var bor = function(p, ctx, a, b) {
|
|
return bitwise_op(p, ctx, a, b, "or")
|
|
}
|
|
|
|
var bxor = function(p, ctx, a, b) {
|
|
return bitwise_op(p, ctx, a, b, "xor")
|
|
}
|
|
|
|
// Shift ops: mask shift amount to 5 bits (& 31)
|
|
var shift_op = function(p, ctx, a, b, qbe_op) {
|
|
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}.slow_path, @${p}.int_path
|
|
@${p}.int_path
|
|
%${p}.ia =l sar ${a}, 1
|
|
%${p}.iaw =w copy %${p}.ia
|
|
%${p}.ib =l sar ${b}, 1
|
|
%${p}.ibw =w copy %${p}.ib
|
|
%${p}.sh =w and %${p}.ibw, 31
|
|
%${p}.rw =w ${qbe_op} %${p}.iaw, %${p}.sh
|
|
%${p}.rext =l extuw %${p}.rw
|
|
%${p} =l shl %${p}.rext, 1
|
|
jmp @${p}.done
|
|
@${p}.slow_path
|
|
%${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}.float_to_int, @disrupt
|
|
@${p}.float_to_int
|
|
%${p} =l call $qbe_shift_${qbe_op}(l ${ctx}, l ${a}, l ${b})
|
|
@${p}.done
|
|
`
|
|
}
|
|
|
|
var shl = function(p, ctx, a, b) {
|
|
return shift_op(p, ctx, a, b, "shl")
|
|
}
|
|
|
|
var shr = function(p, ctx, a, b) {
|
|
return shift_op(p, ctx, a, b, "sar")
|
|
}
|
|
|
|
var ushr = function(p, ctx, a, b) {
|
|
return shift_op(p, ctx, a, b, "shr")
|
|
}
|
|
|
|
// ============================================================
|
|
// Decomposed per-type-path operations
|
|
// These map directly to the new IR ops emitted by mcode.cm.
|
|
// ============================================================
|
|
|
|
// --- Arithmetic (int path) ---
|
|
// add_int: assume both operands are tagged ints. Overflow -> float.
|
|
var add_int = function(p, ctx, a, b) {
|
|
return ` %${p}.ia =l sar ${a}, 1
|
|
%${p}.ib =l sar ${b}, 1
|
|
%${p}.sum =l add %${p}.ia, %${p}.ib
|
|
%${p}.lo =w csltl %${p}.sum, ${int32_min}
|
|
%${p}.hi =w csgtl %${p}.sum, ${int32_max}
|
|
%${p}.ov =w or %${p}.lo, %${p}.hi
|
|
jnz %${p}.ov, @${p}.ov, @${p}.ok
|
|
@${p}.ok
|
|
%${p}.rw =w copy %${p}.sum
|
|
%${p}.rext =l extuw %${p}.rw
|
|
%${p} =l shl %${p}.rext, 1
|
|
jmp @${p}.done
|
|
@${p}.ov
|
|
%${p}.fd =d sltof %${p}.sum
|
|
%${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.fd)
|
|
@${p}.done
|
|
`
|
|
}
|
|
|
|
var sub_int = function(p, ctx, a, b) {
|
|
return ` %${p}.ia =l sar ${a}, 1
|
|
%${p}.ib =l sar ${b}, 1
|
|
%${p}.diff =l sub %${p}.ia, %${p}.ib
|
|
%${p}.lo =w csltl %${p}.diff, ${int32_min}
|
|
%${p}.hi =w csgtl %${p}.diff, ${int32_max}
|
|
%${p}.ov =w or %${p}.lo, %${p}.hi
|
|
jnz %${p}.ov, @${p}.ov, @${p}.ok
|
|
@${p}.ok
|
|
%${p}.rw =w copy %${p}.diff
|
|
%${p}.rext =l extuw %${p}.rw
|
|
%${p} =l shl %${p}.rext, 1
|
|
jmp @${p}.done
|
|
@${p}.ov
|
|
%${p}.fd =d sltof %${p}.diff
|
|
%${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.fd)
|
|
@${p}.done
|
|
`
|
|
}
|
|
|
|
var mul_int = function(p, ctx, a, b) {
|
|
return ` %${p}.ia =l sar ${a}, 1
|
|
%${p}.ib =l sar ${b}, 1
|
|
%${p}.prod =l mul %${p}.ia, %${p}.ib
|
|
%${p}.lo =w csltl %${p}.prod, ${int32_min}
|
|
%${p}.hi =w csgtl %${p}.prod, ${int32_max}
|
|
%${p}.ov =w or %${p}.lo, %${p}.hi
|
|
jnz %${p}.ov, @${p}.ov, @${p}.ok
|
|
@${p}.ok
|
|
%${p}.rw =w copy %${p}.prod
|
|
%${p}.rext =l extuw %${p}.rw
|
|
%${p} =l shl %${p}.rext, 1
|
|
jmp @${p}.done
|
|
@${p}.ov
|
|
%${p}.fd =d sltof %${p}.prod
|
|
%${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.fd)
|
|
@${p}.done
|
|
`
|
|
}
|
|
|
|
var div_int = function(p, ctx, a, b) {
|
|
return ` %${p}.ia =w copy 0
|
|
%${p}.tmp =l sar ${a}, 1
|
|
%${p}.ia =w copy %${p}.tmp
|
|
%${p}.ib =w copy 0
|
|
%${p}.tmp2 =l sar ${b}, 1
|
|
%${p}.ib =w copy %${p}.tmp2
|
|
%${p}.div0 =w ceqw %${p}.ib, 0
|
|
jnz %${p}.div0, @${p}.null, @${p}.chk
|
|
@${p}.null
|
|
%${p} =l copy ${js_null}
|
|
jmp @${p}.done
|
|
@${p}.chk
|
|
%${p}.rem =w rem %${p}.ia, %${p}.ib
|
|
%${p}.exact =w ceqw %${p}.rem, 0
|
|
jnz %${p}.exact, @${p}.idiv, @${p}.fdiv
|
|
@${p}.idiv
|
|
%${p}.q =w div %${p}.ia, %${p}.ib
|
|
%${p}.qext =l extuw %${p}.q
|
|
%${p} =l shl %${p}.qext, 1
|
|
jmp @${p}.done
|
|
@${p}.fdiv
|
|
%${p}.da =d swtof %${p}.ia
|
|
%${p}.db =d swtof %${p}.ib
|
|
%${p}.dr =d div %${p}.da, %${p}.db
|
|
%${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.dr)
|
|
@${p}.done
|
|
`
|
|
}
|
|
|
|
var mod_int = function(p, ctx, a, b) {
|
|
return ` %${p}.ia =w copy 0
|
|
%${p}.tmp =l sar ${a}, 1
|
|
%${p}.ia =w copy %${p}.tmp
|
|
%${p}.ib =w copy 0
|
|
%${p}.tmp2 =l sar ${b}, 1
|
|
%${p}.ib =w copy %${p}.tmp2
|
|
%${p}.div0 =w ceqw %${p}.ib, 0
|
|
jnz %${p}.div0, @${p}.null, @${p}.do_mod
|
|
@${p}.null
|
|
%${p} =l copy ${js_null}
|
|
jmp @${p}.done
|
|
@${p}.do_mod
|
|
%${p}.r =w rem %${p}.ia, %${p}.ib
|
|
%${p}.rext =l extuw %${p}.r
|
|
%${p} =l shl %${p}.rext, 1
|
|
@${p}.done
|
|
`
|
|
}
|
|
|
|
var neg_int = function(p, ctx, v) {
|
|
return ` %${p}.sl =l sar ${v}, 1
|
|
%${p}.iw =w copy %${p}.sl
|
|
%${p}.is_min =w ceqw %${p}.iw, ${int32_min}
|
|
jnz %${p}.is_min, @${p}.ov, @${p}.ok
|
|
@${p}.ov
|
|
%${p}.fd =d swtof %${p}.iw
|
|
%${p}.fdn =d neg %${p}.fd
|
|
%${p} =l call $__JS_NewFloat64(l ${ctx}, d %${p}.fdn)
|
|
jmp @${p}.done
|
|
@${p}.ok
|
|
%${p}.ni =w sub 0, %${p}.iw
|
|
%${p}.niext =l extuw %${p}.ni
|
|
%${p} =l shl %${p}.niext, 1
|
|
@${p}.done
|
|
`
|
|
}
|
|
|
|
// --- Arithmetic (float path) ---
|
|
var add_float = function(p, ctx, a, b) {
|
|
return ` %${p} =l call $qbe_float_add(l ${ctx}, l ${a}, l ${b})
|
|
`
|
|
}
|
|
|
|
var sub_float = function(p, ctx, a, b) {
|
|
return ` %${p} =l call $qbe_float_sub(l ${ctx}, l ${a}, l ${b})
|
|
`
|
|
}
|
|
|
|
var mul_float = function(p, ctx, a, b) {
|
|
return ` %${p} =l call $qbe_float_mul(l ${ctx}, l ${a}, l ${b})
|
|
`
|
|
}
|
|
|
|
var div_float = function(p, ctx, a, b) {
|
|
return ` %${p} =l call $qbe_float_div(l ${ctx}, l ${a}, l ${b})
|
|
`
|
|
}
|
|
|
|
var mod_float = function(p, ctx, a, b) {
|
|
return ` %${p} =l call $qbe_float_mod(l ${ctx}, l ${a}, l ${b})
|
|
`
|
|
}
|
|
|
|
var neg_float = function(p, ctx, v) {
|
|
return ` %${p} =l call $qbe_float_neg(l ${ctx}, l ${v})
|
|
`
|
|
}
|
|
|
|
// --- 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) ---
|
|
var cmp_float = function(p, ctx, a, b, op_id) {
|
|
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) { return cmp_float(p, ctx, a, b, 0) }
|
|
var ne_float = function(p, ctx, a, b) { return cmp_float(p, ctx, a, b, 1) }
|
|
var lt_float = function(p, ctx, a, b) { return cmp_float(p, ctx, a, b, 2) }
|
|
var le_float = function(p, ctx, a, b) { return cmp_float(p, ctx, a, b, 3) }
|
|
var gt_float = function(p, ctx, a, b) { return cmp_float(p, ctx, a, b, 4) }
|
|
var ge_float = function(p, ctx, a, b) { return cmp_float(p, ctx, a, b, 5) }
|
|
|
|
// --- Comparisons (text path) ---
|
|
var cmp_text = function(p, ctx, a, b, qbe_op, eq_only) {
|
|
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) { return cmp_text(p, ctx, a, b, "ceqw", 1) }
|
|
var ne_text = function(p, ctx, a, b) { return cmp_text(p, ctx, a, b, "cnew", 1) }
|
|
var lt_text = function(p, ctx, a, b) { return cmp_text(p, ctx, a, b, "csltw", 0) }
|
|
var le_text = function(p, ctx, a, b) { return cmp_text(p, ctx, a, b, "cslew", 0) }
|
|
var gt_text = function(p, ctx, a, b) { return cmp_text(p, ctx, a, b, "csgtw", 0) }
|
|
var ge_text = function(p, ctx, a, b) { return cmp_text(p, ctx, a, b, "csgew", 0) }
|
|
|
|
// --- 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,
|
|
// decomposed arithmetic (int path)
|
|
add_int: add_int,
|
|
sub_int: sub_int,
|
|
mul_int: mul_int,
|
|
div_int: div_int,
|
|
mod_int: mod_int,
|
|
neg_int: neg_int,
|
|
// decomposed arithmetic (float path)
|
|
add_float: add_float,
|
|
sub_float: sub_float,
|
|
mul_float: mul_float,
|
|
div_float: div_float,
|
|
mod_float: mod_float,
|
|
neg_float: neg_float,
|
|
// 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
|
|
}
|