#include "jit_internal.h" #include "../silver/ops/literals.h" #include "silver/feedback.h" #include "modules/buffer.h" static_assert( kTypeObject == 0 && kTypeString == 1 && kTypeArray == 2 && kTypeFunction == 3 && kTypeBuiltin == 4 && kTypePromise == 5 && kTypeGenerator == 6, "truthiness requires a contiguous object/string tag range" ); static_assert(kTypeNull == kTypeUndefined + 1 && kTypeBool == kTypeNull + 1, "truthiness requires consecutive Undefined, Null, and Boolean tags"); static_assert( offsetof(ant_flat_string_t, len) == 0 && offsetof(ant_rope_heap_t, len) == 0 && offsetof(ant_string_builder_t, len) == 0 && sizeof(ant_offset_t) == sizeof(uint64_t), "string truthiness requires the shared length prefix" ); void mir_emit_truthy_branch( MIR_context_t ctx, MIR_item_t fn, MIR_reg_t value, MIR_reg_t scratch, MIR_reg_t r_js, MIR_item_t truthy_proto, MIR_item_t imp_is_truthy, bool is_false_branch, MIR_label_t target) { MIR_label_t truthy = is_false_branch ? MIR_new_label(ctx) : target; MIR_label_t falsy = is_false_branch ? target : MIR_new_label(ctx); MIR_label_t done = is_false_branch ? truthy : falsy; MIR_label_t numeric = MIR_new_label(ctx); MIR_label_t string = MIR_new_label(ctx); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, truthy), MIR_new_reg_op(ctx, value), MIR_new_uint_op(ctx, js_true))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_SUB, MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, value), MIR_new_uint_op(ctx, mkval(kTypeUndefined, 0)))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_UBLE, MIR_new_label_op(ctx, falsy), MIR_new_reg_op(ctx, scratch), MIR_new_uint_op(ctx, (uint64_t)(kTypeBool - kTypeUndefined) << NANBOX_TYPE_SHIFT))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_URSH, MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, value), MIR_new_uint_op(ctx, NANBOX_TYPE_SHIFT))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, truthy), MIR_new_reg_op(ctx, scratch), MIR_new_uint_op(ctx, NANBOX_PREFIX >> NANBOX_TYPE_SHIFT))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_UBLE, MIR_new_label_op(ctx, numeric), MIR_new_reg_op(ctx, value), MIR_new_uint_op(ctx, NANBOX_PREFIX))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_SUB, MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, scratch), MIR_new_uint_op(ctx, (NANBOX_PREFIX >> NANBOX_TYPE_SHIFT) | kTypeArray))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_UBLE, MIR_new_label_op(ctx, truthy), MIR_new_reg_op(ctx, scratch), MIR_new_uint_op(ctx, kTypeGenerator - kTypeArray))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, string), MIR_new_reg_op(ctx, scratch), MIR_new_int_op(ctx, kTypeString - kTypeArray))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, truthy), MIR_new_reg_op(ctx, scratch), MIR_new_uint_op(ctx, kTypeSymbol - kTypeArray))); MIR_append_insn(ctx, fn, MIR_new_call_insn(ctx, 5, MIR_new_ref_op(ctx, truthy_proto), MIR_new_ref_op(ctx, imp_is_truthy), MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, r_js), MIR_new_reg_op(ctx, value))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, is_false_branch ? MIR_BEQ : MIR_BNE, MIR_new_label_op(ctx, target), MIR_new_reg_op(ctx, scratch), MIR_new_int_op(ctx, 0))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, done))); MIR_append_insn(ctx, fn, string); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_AND, MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, value), MIR_new_uint_op(ctx, STR_HEAP_TAG_MASK))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_UBGT, MIR_new_label_op(ctx, falsy), MIR_new_reg_op(ctx, scratch), MIR_new_uint_op(ctx, STR_HEAP_TAG_BUILDER))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_AND, MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, value), MIR_new_uint_op(ctx, NANBOX_DATA_MASK & ~STR_HEAP_TAG_MASK))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, falsy), MIR_new_reg_op(ctx, scratch), MIR_new_int_op(ctx, 0))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_ADD, MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, MIR_reg(ctx, "cage_base", fn->u.func)))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, scratch), MIR_new_mem_op(ctx, MIR_T_U64, 0, scratch, 0, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, is_false_branch ? MIR_BEQ : MIR_BNE, MIR_new_label_op(ctx, target), MIR_new_reg_op(ctx, scratch), MIR_new_int_op(ctx, 0))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, done))); MIR_append_insn(ctx, fn, numeric); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_LSH, MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, value), MIR_new_uint_op(ctx, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, falsy), MIR_new_reg_op(ctx, scratch), MIR_new_uint_op(ctx, 0))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_UBGT, MIR_new_label_op(ctx, falsy), MIR_new_reg_op(ctx, scratch), MIR_new_uint_op(ctx, UINT64_C(0xffe0000000000000)))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, truthy))); MIR_append_insn(ctx, fn, done); } bool vstack_alloc(jit_vstack_t *vs, int max) { size_t n = (size_t)max; *vs = (jit_vstack_t){ .max = max }; vs->regs = calloc(n, sizeof(MIR_reg_t)); vs->d_regs = calloc(n, sizeof(MIR_reg_t)); vs->known_func = calloc(n, sizeof(sv_func_t *)); vs->slot_type = calloc(n, sizeof(uint8_t)); vs->known_const = calloc(n, sizeof(uint64_t)); vs->has_const = calloc(n, sizeof(bool)); vs->known_bool = calloc(n, sizeof(uint8_t)); vs->known_builtin = calloc(n, sizeof(uint8_t)); vs->known_str = calloc(n, sizeof(*vs->known_str)); vs->integer_range = calloc(n, sizeof(jit_integer_range_t)); if ( vs->regs && vs->d_regs && vs->known_func && vs->slot_type && vs->known_const && vs->has_const && vs->known_bool && vs->known_builtin && vs->known_str && vs->integer_range ) return true; vstack_free(vs); return false; } void vstack_free(jit_vstack_t *vs) { free(vs->regs); free(vs->d_regs); free(vs->known_func); free(vs->slot_type); free(vs->known_const); free(vs->has_const); free(vs->known_bool); free(vs->known_builtin); free(vs->known_str); free(vs->integer_range); vs->regs = vs->d_regs = NULL; vs->known_func = NULL; vs->slot_type = vs->known_bool = vs->known_builtin = NULL; vs->known_const = NULL; vs->has_const = NULL; vs->known_str = NULL; vs->integer_range = NULL; } void vstack_forget_facts(jit_vstack_t *vs) { size_t n = (size_t)vs->max; memset(vs->slot_type, SLOT_BOXED, n); memset(vs->known_func, 0, n * sizeof(sv_func_t *)); memset(vs->has_const, 0, n * sizeof(bool)); memset(vs->known_bool, 0, n); memset(vs->known_builtin, 0, n); memset(vs->known_str, 0, n * sizeof(*vs->known_str)); } jit_value_info_t vstack_value_info(const jit_vstack_t *vs, int idx) { jit_value_info_t info = {0}; if (vs->known_func) info.known_func = vs->known_func[idx]; if (vs->has_const && vs->has_const[idx]) { info.has_const = true; info.known_const = vs->known_const[idx]; } if (vs->known_bool) info.known_bool = vs->known_bool[idx]; if (vs->known_builtin) info.known_builtin = vs->known_builtin[idx]; if (vs->known_str) info.known_str = vs->known_str[idx]; if (vs->integer_range) info.integer_range = vs->integer_range[idx]; return info; } void vstack_set_value_info( jit_vstack_t *vs, int idx, jit_value_info_t info) { if (vs->known_func) vs->known_func[idx] = info.known_func; if (vs->has_const) { vs->has_const[idx] = info.has_const; if (info.has_const) vs->known_const[idx] = info.known_const; } if (vs->known_bool) vs->known_bool[idx] = info.known_bool; if (vs->known_builtin) vs->known_builtin[idx] = info.known_builtin; if (vs->known_str) vs->known_str[idx] = info.known_str; if (vs->integer_range) vs->integer_range[idx] = info.integer_range; } void vstack_clear_value_info(jit_vstack_t *vs, int idx) { vstack_set_value_info(vs, idx, (jit_value_info_t){0}); } static inline bool vstack_check_push(jit_vstack_t *vs) { if (vs->sp < vs->max) return true; vs->overflow = true; return false; } MIR_reg_t vstack_push(jit_vstack_t *vs) { if (!vstack_check_push(vs)) return vs->regs[vs->max - 1]; vstack_clear_value_info(vs, vs->sp); if (vs->slot_type) vs->slot_type[vs->sp] = SLOT_BOXED; return vs->regs[vs->sp++]; } MIR_reg_t vstack_push_const(jit_vstack_t *vs, uint64_t val) { if (!vstack_check_push(vs)) return vs->regs[vs->max - 1]; vstack_clear_value_info(vs, vs->sp); if (vs->slot_type) vs->slot_type[vs->sp] = SLOT_BOXED; if (vs->has_const) { vs->has_const[vs->sp] = true; vs->known_const[vs->sp] = val; } return vs->regs[vs->sp++]; } MIR_reg_t vstack_pop(jit_vstack_t *vs) { if (vs->sp <= 0) { vs->overflow = true; return vs->regs[0]; } return vs->regs[--vs->sp]; } MIR_reg_t vstack_top(jit_vstack_t *vs) { return vs->regs[vs->sp - 1]; } jit_integer_range_t jit_word_range(sv_op_t op, jit_integer_range_t left, jit_integer_range_t right) { jit_integer_range_t full = {INT32_MIN, INT32_MAX, true}; if (op == OP_BAND) { if (right.known && right.min == right.max && right.min >= 0 && right.max <= INT32_MAX) return (jit_integer_range_t){0, right.max, true}; if (left.known && left.min == left.max && left.min >= 0 && left.max <= INT32_MAX) return (jit_integer_range_t){0, left.max, true}; } if (op == OP_USHR) full = (jit_integer_range_t){0, UINT32_MAX, true}; if (!right.known || right.min != right.max) return full; unsigned shift = (unsigned)right.min & 31u; if (op == OP_SHR) { if (!left.known || left.min < INT32_MIN || left.max > INT32_MAX) left = full; return (jit_integer_range_t){left.min >> shift, left.max >> shift, true}; } if (op == OP_USHR) { int64_t maximum = left.known && left.min >= 0 ? left.max : UINT32_MAX; return (jit_integer_range_t){0, maximum >> shift, true}; } if (op == OP_SHL && left.known && left.min >= 0 && left.max <= (INT32_MAX >> shift)) return (jit_integer_range_t){left.min << shift, left.max << shift, true}; return full; } static jit_integer_range_t jit_number_integer_range(double number) { if (number < INT32_MIN || number > UINT32_MAX || !isfinite(number) || trunc(number) != number || (number == 0 && signbit(number))) return (jit_integer_range_t){0}; return (jit_integer_range_t){(int64_t)number, (int64_t)number, true}; } static jit_integer_range_t jit_constant_integer_range(sv_func_t *func, uint8_t *ip) { if (!ip) return (jit_integer_range_t){0}; if (*ip == OP_CONST_I8) return jit_number_integer_range(sv_get_i8(ip + 1)); if (*ip == OP_CONST || *ip == OP_CONST8) { uint32_t index = *ip == OP_CONST ? sv_get_u32(ip + 1) : sv_get_u8(ip + 1); if (index < (uint32_t)func->const_count && vtype(func->constants[index]) == kTypeNumber) return jit_number_integer_range(tod(func->constants[index])); } return (jit_integer_range_t){0}; } void jit_emit_integer_constant( MIR_context_t ctx, MIR_item_t fn, jit_vstack_t *vs, MIR_reg_t dst, double number) { jit_integer_range_t range = jit_number_integer_range(number); if (!range.known) return; MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, dst), MIR_new_uint_op(ctx, (uint64_t)range.min))); vs->slot_type[vs->sp - 1] = SLOT_I32; vs->integer_range[vs->sp - 1] = range; } void jit_entry_integer_ranges( sv_func_t *func, jit_integer_range_t *ranges, int n_locals, int param_count) { enum { UNASSIGNED, ASSIGNED, REJECTED }; uint8_t *state = calloc((size_t)n_locals, sizeof(*state)); if (!state) return; bool before_branch = true; uint8_t *previous = NULL; uint8_t *before_previous = NULL; uint8_t *end = func->code + func->code_len; for (uint8_t *ip = func->code; ip < end;) { sv_op_t op = (sv_op_t)*ip; int size = sv_op_size[op]; if (!size || ip + size > end || op == OP_PUT_LOCAL_CHK) { memset(ranges, 0, (size_t)n_locals * sizeof(*ranges)); break; } uint16_t flags = sv_op_flags[op]; if (flags & (SV_OPF_JIT_BRANCH32 | SV_OPF_JIT_BRANCH8)) before_branch = false; int index = -1; switch (op) { case OP_PUT_LOCAL: case OP_SET_LOCAL: case OP_PUT_LOCAL8: case OP_SET_LOCAL8: { bool short_op = (op == OP_PUT_LOCAL8 || op == OP_SET_LOCAL8); index = short_op ? sv_get_u8(ip + 1) : sv_get_u16(ip + 1); if (index >= n_locals) break; if (!before_branch || state[index] != UNASSIGNED) { state[index] = REJECTED; break; } state[index] = ASSIGNED; ranges[index] = jit_constant_integer_range(func, previous); if (!previous) break; sv_op_t prev_op = (sv_op_t)*previous; bool word_op = prev_op == OP_BAND || prev_op == OP_BOR || prev_op == OP_BXOR || prev_op == OP_SHL || prev_op == OP_SHR || prev_op == OP_USHR; if (!word_op) break; uint8_t *type_feedback = sv_func_type_feedback(func); uint8_t feedback = type_feedback ? type_feedback[previous - func->code] : 0; if (sv_tfb_specialization_ready(feedback)) ranges[index] = jit_word_range( prev_op, (jit_integer_range_t){0}, jit_constant_integer_range(func, before_previous)); break; } case OP_INC_LOCAL: case OP_DEC_LOCAL: case OP_ADD_LOCAL: index = sv_get_u8(ip + 1); if (index < n_locals) state[index] = REJECTED; break; case OP_SET_LOCAL_UNDEF: index = sv_get_u16(ip + 1); if (index < n_locals && (state[index] != UNASSIGNED || !before_branch)) state[index] = REJECTED; break; default: if (flags & SV_OPF_BUILDER_TARGET) { index = (int)sv_get_u16(ip + 1) - param_count; if (index >= 0 && index < n_locals) state[index] = REJECTED; } break; } if (index >= 0 && index < n_locals && state[index] == REJECTED) ranges[index] = (jit_integer_range_t){0}; before_previous = previous; previous = ip; ip += size; } free(state); } static bool jit_reads_slot(const uint8_t *ip, bool arg, int index) { if (!ip) return false; if (arg) return *ip == OP_GET_ARG && sv_get_u16(ip + 1) == index; if (*ip == OP_GET_LOCAL) return sv_get_u16(ip + 1) == index; return *ip == OP_GET_LOCAL8 && sv_get_u8(ip + 1) == index; } static int jit_constant_step(sv_func_t *func, uint8_t *ip) { jit_integer_range_t k = jit_constant_integer_range(func, ip); return k.known && k.min != 0 && k.min >= -JIT_COUNTER_STEP_MAX && k.min <= JIT_COUNTER_STEP_MAX ? (int)k.min : 0; } static int jit_counter_step( sv_func_t *func, const uint8_t *target, const uint8_t *store, uint8_t *const back[3], bool arg, int index) { uint8_t *p1 = back[0], *p2 = back[1], *p3 = back[2]; if (!p1 || target[store - func->code] || target[p1 - func->code]) return 0; switch (*p1) { case OP_INC: case OP_POST_INC: return jit_reads_slot(p2, arg, index) ? 1 : 0; case OP_DEC: case OP_POST_DEC: return jit_reads_slot(p2, arg, index) ? -1 : 0; case OP_ADD: case OP_ADD_NUM: case OP_SUB: case OP_SUB_NUM: { if (!p2 || !p3 || target[p2 - func->code]) return 0; bool sub = *p1 == OP_SUB || *p1 == OP_SUB_NUM; if (jit_reads_slot(p3, arg, index)) { int k = jit_constant_step(func, p2); return sub ? -k : k; } return !sub && jit_reads_slot(p2, arg, index) ? jit_constant_step(func, p3) : 0; } default: return 0; } } void jit_induction_locals(sv_func_t *func, uint8_t *kinds, int n_locals, int param_count) { enum { UNSEEN, NONNEG, SIGNED, REJECTED }; uint8_t *state = calloc((size_t)n_locals, 1); uint8_t *target = calloc((size_t)func->code_len + 1, 1); int *copy_dst = NULL, *copy_src = NULL; int copies = 0, copy_cap = 0; if (!state || !target) goto none; uint8_t *end = func->code + func->code_len; for (uint8_t *ip = func->code; ip < end;) { sv_op_t op = (sv_op_t)*ip; int size = sv_op_size[op]; if (!size || ip + size > end) goto none; uint16_t flags = sv_op_flags[op]; int64_t to = -1; if (flags & SV_OPF_JIT_BRANCH32) to = (ip - func->code) + size + sv_get_i32(ip + 1); else if (flags & SV_OPF_JIT_BRANCH8) to = (ip - func->code) + size + sv_get_i8(ip + 1); if (to >= 0 && to <= func->code_len) target[to] = 1; ip += size; } uint8_t *back[3] = {0}; for (uint8_t *ip = func->code; ip < end;) { sv_op_t op = (sv_op_t)*ip; int size = sv_op_size[op]; if (!size || ip + size > end || op == OP_PUT_LOCAL_CHK) goto none; uint8_t *previous = back[0]; int index = -1; uint8_t kind = REJECTED; switch (op) { case OP_PUT_LOCAL: case OP_SET_LOCAL: case OP_PUT_LOCAL8: case OP_SET_LOCAL8: { bool short_op = op == OP_PUT_LOCAL8 || op == OP_SET_LOCAL8; index = short_op ? sv_get_u8(ip + 1) : sv_get_u16(ip + 1); if (index >= n_locals || !previous || target[ip - func->code]) break; int step = jit_counter_step(func, target, ip, back, false, index); if (step) { kind = step > 0 ? UNSEEN : SIGNED; break; } jit_integer_range_t constant = jit_constant_integer_range(func, previous); if (constant.known) { kind = constant.min >= 0 ? NONNEG : SIGNED; break; } sv_op_t prev_op = (sv_op_t)*previous; if (prev_op != OP_GET_LOCAL && prev_op != OP_GET_LOCAL8) break; int source = prev_op == OP_GET_LOCAL8 ? sv_get_u8(previous + 1) : sv_get_u16(previous + 1); if (source >= n_locals) break; if (copies == copy_cap) { copy_cap = copy_cap ? copy_cap * 2 : 16; int *dst = realloc(copy_dst, (size_t)copy_cap * sizeof(int)); if (dst) copy_dst = dst; int *src = realloc(copy_src, (size_t)copy_cap * sizeof(int)); if (src) copy_src = src; if (!dst || !src) goto none; } copy_dst[copies] = index; copy_src[copies++] = source; kind = UNSEEN; break; } case OP_INC_LOCAL: index = sv_get_u8(ip + 1); kind = UNSEEN; break; case OP_DEC_LOCAL: index = sv_get_u8(ip + 1); kind = SIGNED; break; case OP_ADD_LOCAL: { index = sv_get_u8(ip + 1); int step = previous && !target[ip - func->code] ? jit_constant_step(func, previous) : 0; if (step) kind = step > 0 ? UNSEEN : SIGNED; break; } case OP_YIELD_STAR_INIT: case OP_YIELD_STAR_NEXT: case OP_YIELD_STAR_THROW: case OP_YIELD_STAR_RETURN: index = sv_get_u16(ip + 1); break; default: if (sv_op_flags[op] & SV_OPF_BUILDER_TARGET) index = (int)sv_get_u16(ip + 1) - param_count; break; } if (index >= 0 && index < n_locals && kind > state[index]) state[index] = kind; back[2] = back[1]; back[1] = back[0]; back[0] = ip; ip += size; } // a copy is as good as its source; repeat until no copy weakens a local for (bool changed = true; changed;) { changed = false; for (int i = 0; i < copies; i++) { uint8_t from = state[copy_src[i]] == UNSEEN ? REJECTED : state[copy_src[i]]; if (from > state[copy_dst[i]]) { state[copy_dst[i]] = from; changed = true; } } } for (int i = 0; i < n_locals; i++) kinds[i] = state[i] == NONNEG ? JIT_INDUCTION_NONNEG : state[i] == SIGNED ? JIT_INDUCTION_SIGNED : 0; none: free(copy_dst); free(copy_src); free(target); free(state); } uint8_t jit_induction_params(sv_func_t *func, int param_count) { if (func->has_dynamic_eval || param_count <= 0) return 0; uint8_t *target = calloc((size_t)func->code_len + 1, 1); if (!target) return 0; uint8_t *end = func->code + func->code_len; uint8_t ok = 0, bad = 0; for (uint8_t *ip = func->code; ip < end;) { sv_op_t op = (sv_op_t)*ip; int size = sv_op_size[op]; if (!size || ip + size > end) goto none; uint16_t flags = sv_op_flags[op]; int64_t to = -1; if (flags & SV_OPF_JIT_BRANCH32) to = (ip - func->code) + size + sv_get_i32(ip + 1); else if (flags & SV_OPF_JIT_BRANCH8) to = (ip - func->code) + size + sv_get_i8(ip + 1); if (to >= 0 && to <= func->code_len) target[to] = 1; if (op == OP_SPECIAL_OBJ && sv_get_u8(ip + 1) == SV_SPECIAL_ARGUMENTS) goto none; ip += size; } uint8_t *back[3] = {0}; for (uint8_t *ip = func->code; ip < end; ip += sv_op_size[*ip]) { sv_op_t op = (sv_op_t)*ip; int index = -1; bool counted = false; if (op == OP_PUT_ARG || op == OP_SET_ARG) { index = sv_get_u16(ip + 1); counted = jit_counter_step(func, target, ip, back, true, index) != 0; } else if (sv_op_flags[op] & SV_OPF_BUILDER_TARGET) index = sv_get_u16(ip + 1); if (index >= 0 && index < param_count && index < JIT_PARAM_HOIST_CAP) { if (counted) ok |= (uint8_t)(1u << index); else bad |= (uint8_t)(1u << index); } back[2] = back[1]; back[1] = back[0]; back[0] = ip; } free(target); return ok & (uint8_t)~(bad | func->jit_param_counters_off); none: free(target); return 0; } bool jit_emit_integer_arithmetic( MIR_context_t ctx, MIR_item_t fn, jit_vstack_t *vs, sv_op_t op) { int li = vs->sp - 2, ri = vs->sp - 1; if (vs->slot_type[li] != SLOT_I32 || vs->slot_type[ri] != SLOT_I32) return false; jit_integer_range_t l = vs->integer_range[li], r = vs->integer_range[ri]; if (!l.known || !r.known) return false; __int128 lo, hi; MIR_insn_code_t code; if (op == OP_ADD || op == OP_ADD_NUM) { code = MIR_ADD; lo = (__int128)l.min + r.min; hi = (__int128)l.max + r.max; } else if (op == OP_SUB || op == OP_SUB_NUM) { code = MIR_SUB; lo = (__int128)l.min - r.max; hi = (__int128)l.max - r.min; } else if (op == OP_MOD) { if (l.min < 0 || (r.min <= 0 && r.max >= 0)) return false; code = MIR_MOD; int64_t divisor = r.max > -r.min ? r.max : -r.min; lo = 0; hi = divisor - 1 < l.max ? divisor - 1 : l.max; } else { if ((l.min < 0 && r.min <= 0 && r.max >= 0) || (r.min < 0 && l.min <= 0 && l.max >= 0)) return false; code = MIR_MUL; __int128 products[] = {(__int128)l.min * r.min, (__int128)l.min * r.max, (__int128)l.max * r.min, (__int128)l.max * r.max}; lo = hi = products[0]; for (int i = 1; i < 4; i++) { if (products[i] < lo) lo = products[i]; if (products[i] > hi) hi = products[i]; } } if (lo < INT32_MIN || hi > UINT32_MAX) return false; MIR_reg_t right = vstack_pop(vs), left = vstack_pop(vs), dst = vstack_push(vs); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, code, MIR_new_reg_op(ctx, dst), MIR_new_reg_op(ctx, left), MIR_new_reg_op(ctx, right))); vs->slot_type[vs->sp - 1] = SLOT_I32; vs->integer_range[vs->sp - 1] = (jit_integer_range_t){(int64_t)lo, (int64_t)hi, true}; return true; } MIR_label_t label_for_offset(MIR_context_t ctx, jit_label_map_t *lm, int bc_off) { if (bc_off < 0 || bc_off >= lm->index_len) return NULL; int at = lm->index[bc_off] - 1; if (at >= 0) return lm->entries[at].label; if (lm->count >= MAX_LABELS) return NULL; MIR_label_t lbl = MIR_new_label(ctx); lm->entries[lm->count].bc_off = bc_off; lm->entries[lm->count].label = lbl; lm->index[bc_off] = ++lm->count; return lbl; } int jit_constant_object_span(sv_func_t *func, sv_obj_site_cache_t *site, jit_label_map_t *lm) { if (!site || !site->key_atoms || !site->key_count) return 0; uint8_t *begin = func->code + site->bc_off + sv_op_size[OP_OBJECT]; uint8_t *ip = begin, *end = func->code + func->code_len; for (uint16_t i = 0; i < site->key_count; i++) { ant_value_t value; int size = sv_literal_constant_at(func, ip, &value); if (!size) return 0; ip += size; if (ip + sv_op_size[OP_DEFINE_SLOT] > end || *ip != OP_DEFINE_SLOT || sv_get_u32(ip + 1) != site->key_atoms[i] || sv_get_u16(ip + 5) != i) return 0; ip += sv_op_size[OP_DEFINE_SLOT]; } for (int off = (int)(begin - func->code); off < (int)(ip - func->code); off++) if (jit_label_at(lm, off) >= 0) return 0; return (int)(ip - begin); } void mir_emit_branch_if_object( MIR_context_t ctx, MIR_item_t fn, MIR_reg_t value, MIR_reg_t scratch, MIR_label_t target) { MIR_label_t not_object = MIR_new_label(ctx); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_UBLE, MIR_new_label_op(ctx, not_object), MIR_new_reg_op(ctx, value), MIR_new_uint_op(ctx, NANBOX_PREFIX))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_URSH, MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, value), MIR_new_int_op(ctx, NANBOX_TYPE_SHIFT))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_AND, MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, scratch), MIR_new_int_op(ctx, NANBOX_TYPE_MASK))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_LSH, MIR_new_reg_op(ctx, scratch), MIR_new_int_op(ctx, 1), MIR_new_reg_op(ctx, scratch))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_AND, MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, scratch), MIR_new_int_op(ctx, (int64_t)T_OBJECT_MASK))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BT, MIR_new_label_op(ctx, target), MIR_new_reg_op(ctx, scratch))); MIR_append_insn(ctx, fn, not_object); } void mir_emit_decode_ref( MIR_context_t ctx, MIR_item_t fn, MIR_reg_t dst, MIR_reg_t value) { MIR_reg_t base = MIR_reg(ctx, "cage_base", fn->u.func); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_AND, MIR_new_reg_op(ctx, dst), MIR_new_reg_op(ctx, value), MIR_new_uint_op(ctx, NANBOX_DATA_MASK))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_ADD, MIR_new_reg_op(ctx, dst), MIR_new_reg_op(ctx, dst), MIR_new_reg_op(ctx, base))); } static void mir_emit_string_flat_ptr( MIR_context_t ctx, MIR_item_t fn, MIR_reg_t dst, MIR_reg_t value, MIR_reg_t tag, MIR_label_t other, MIR_label_t slow) { MIR_label_t flat = MIR_new_label(ctx); mir_emit_decode_ref(ctx, fn, dst, value); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_AND, MIR_new_reg_op(ctx, tag), MIR_new_reg_op(ctx, dst), MIR_new_uint_op(ctx, STR_HEAP_TAG_MASK))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, flat), MIR_new_reg_op(ctx, tag), MIR_new_uint_op(ctx, STR_HEAP_TAG_FLAT))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, other), MIR_new_reg_op(ctx, tag), MIR_new_uint_op(ctx, STR_HEAP_TAG_ROPE))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_AND, MIR_new_reg_op(ctx, dst), MIR_new_reg_op(ctx, dst), MIR_new_uint_op(ctx, ~STR_HEAP_TAG_MASK))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, dst), MIR_new_mem_op(ctx, MIR_JSVAL, offsetof(ant_rope_heap_t, cached), dst, 0, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_URSH, MIR_new_reg_op(ctx, tag), MIR_new_reg_op(ctx, dst), MIR_new_uint_op(ctx, NANBOX_TYPE_SHIFT))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, tag), MIR_new_uint_op(ctx, JIT_STR_TAG))); mir_emit_decode_ref(ctx, fn, dst, dst); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_AND, MIR_new_reg_op(ctx, tag), MIR_new_reg_op(ctx, dst), MIR_new_uint_op(ctx, STR_HEAP_TAG_MASK))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, tag), MIR_new_uint_op(ctx, STR_HEAP_TAG_FLAT))); MIR_append_insn(ctx, fn, flat); } void mir_emit_cage_offset( MIR_context_t ctx, MIR_item_t fn, MIR_reg_t dst, MIR_reg_t ptr) { MIR_reg_t base = MIR_reg(ctx, "cage_base", fn->u.func); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_SUB, MIR_new_reg_op(ctx, dst), MIR_new_reg_op(ctx, ptr), MIR_new_reg_op(ctx, base))); } void mir_i64_to_d(MIR_context_t ctx, MIR_item_t fn, MIR_reg_t dst_d, MIR_reg_t src_i64, MIR_reg_t slot) { #if SV_JIT_HAS_BITCAST (void)slot; MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_I2DB, MIR_new_reg_op(ctx, dst_d), MIR_new_reg_op(ctx, src_i64))); #else MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_mem_op(ctx, MIR_T_I64, 0, slot, 0, 1), MIR_new_reg_op(ctx, src_i64))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_DMOV, MIR_new_reg_op(ctx, dst_d), MIR_new_mem_op(ctx, MIR_T_D, 0, slot, 0, 1))); #endif } void mir_d_to_i64_non_nan(MIR_context_t ctx, MIR_item_t fn, MIR_reg_t dst_i64, MIR_reg_t src_d, MIR_reg_t slot) { #if SV_JIT_HAS_BITCAST (void)slot; MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_D2IB, MIR_new_reg_op(ctx, dst_i64), MIR_new_reg_op(ctx, src_d))); #else MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_DMOV, MIR_new_mem_op(ctx, MIR_T_D, 0, slot, 0, 1), MIR_new_reg_op(ctx, src_d))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, dst_i64), MIR_new_mem_op(ctx, MIR_T_I64, 0, slot, 0, 1))); #endif } void mir_d_to_i64(MIR_context_t ctx, MIR_item_t fn, MIR_reg_t dst_i64, MIR_reg_t src_d, MIR_reg_t slot) { MIR_label_t done = MIR_new_label(ctx); mir_d_to_i64_non_nan(ctx, fn, dst_i64, src_d, slot); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_UBLE, MIR_new_label_op(ctx, done), MIR_new_reg_op(ctx, dst_i64), MIR_new_uint_op(ctx, NANBOX_PREFIX))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, dst_i64), MIR_new_uint_op(ctx, UINT64_C(0x7FF8000000000000)))); MIR_append_insn(ctx, fn, done); } void mir_emit_get_length( MIR_context_t ctx, MIR_item_t fn, ant_t *js, MIR_reg_t obj, MIR_reg_t dst, MIR_reg_t r_js, MIR_reg_t r_d_slot, MIR_item_t get_length_proto, MIR_item_t imp_get_length, bool builder_slot, int owner_id, int bc_off, MIR_label_t slow_out, MIR_label_t done_out) { char tag_name[48], ptr_name[48], len_name[48], dbl_name[48]; snprintf(tag_name, sizeof(tag_name), "gl_tag_%d_%d", owner_id, bc_off); snprintf(ptr_name, sizeof(ptr_name), "gl_ptr_%d_%d", owner_id, bc_off); snprintf(len_name, sizeof(len_name), "gl_len_%d_%d", owner_id, bc_off); snprintf(dbl_name, sizeof(dbl_name), "gl_dbl_%d_%d", owner_id, bc_off); MIR_reg_t tag = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, tag_name); MIR_reg_t ptr = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, ptr_name); MIR_reg_t len = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, len_name); MIR_reg_t dbl = MIR_new_func_reg(ctx, fn->u.func, MIR_T_D, dbl_name); MIR_label_t array = MIR_new_label(ctx); MIR_label_t ascii = MIR_new_label(ctx); MIR_label_t encode = MIR_new_label(ctx); MIR_label_t slow = slow_out ? slow_out : MIR_new_label(ctx); MIR_label_t done = done_out ? done_out : MIR_new_label(ctx); MIR_label_t builder = builder_slot ? MIR_new_label(ctx) : slow; MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_URSH, MIR_new_reg_op(ctx, tag), MIR_new_reg_op(ctx, obj), MIR_new_uint_op(ctx, NANBOX_TYPE_SHIFT))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, array), MIR_new_reg_op(ctx, tag), MIR_new_uint_op(ctx, NANBOX_TARR_TAG))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, tag), MIR_new_uint_op(ctx, JIT_STR_TAG))); mir_emit_string_flat_ptr(ctx, fn, ptr, obj, tag, builder, slow); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, len), MIR_new_mem_op(ctx, MIR_T_U64, (MIR_disp_t)offsetof(ant_flat_string_t, meta), ptr, 0, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_AND, MIR_new_reg_op(ctx, tag), MIR_new_reg_op(ctx, len), MIR_new_uint_op(ctx, STR_META_ASCII_MASK))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, ascii), MIR_new_reg_op(ctx, tag), MIR_new_uint_op(ctx, (uint64_t)STR_ASCII_YES << STR_META_ASCII_SHIFT))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_AND, MIR_new_reg_op(ctx, len), MIR_new_reg_op(ctx, len), MIR_new_uint_op(ctx, STR_META_UTF16_MASK))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, len), MIR_new_uint_op(ctx, STR_UTF16_LEN_UNKNOWN))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, encode))); MIR_append_insn(ctx, fn, ascii); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, len), MIR_new_mem_op(ctx, MIR_T_U64, (MIR_disp_t)offsetof(ant_flat_string_t, len), ptr, 0, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, encode))); if (builder_slot) { MIR_append_insn(ctx, fn, builder); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, tag), MIR_new_uint_op(ctx, STR_HEAP_TAG_BUILDER))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_AND, MIR_new_reg_op(ctx, ptr), MIR_new_reg_op(ctx, ptr), MIR_new_uint_op(ctx, ~STR_HEAP_TAG_MASK))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, tag), MIR_new_mem_op(ctx, MIR_T_U8, (MIR_disp_t)offsetof(ant_string_builder_t, ascii_state), ptr, 0, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, tag), MIR_new_uint_op(ctx, STR_ASCII_YES))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, len), MIR_new_mem_op(ctx, MIR_T_U64, (MIR_disp_t)offsetof(ant_string_builder_t, len), ptr, 0, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, encode))); } MIR_append_insn(ctx, fn, array); mir_emit_decode_ref(ctx, fn, ptr, obj); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, tag), MIR_new_mem_op(ctx, MIR_T_U8, (MIR_disp_t)ANT_OBJECT_FLAGS_HIGH_BYTE, ptr, 0, 1))); ant_shape_t *arguments_shapes[2] = {0}; if (js) { ant_value_t templates[2] = {js->builtins.arguments_iter_template, js->builtins.sloppy_arguments_iter_template}; for (int i = 0; i < 2; i++) if (vtype(templates[i]) == kTypeObject) arguments_shapes[i] = js_obj_ptr(templates[i])->shape; } MIR_label_t arguments = arguments_shapes[0] || arguments_shapes[1] ? MIR_new_label(ctx) : slow; MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_UBGE, MIR_new_label_op(ctx, arguments), MIR_new_reg_op(ctx, tag), MIR_new_uint_op(ctx, ANT_OBJECT_FLAG_ARGUMENTS >> 8))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, len), MIR_new_mem_op(ctx, MIR_T_U32, (MIR_disp_t)offsetof(ant_object_t, u.array.len), ptr, 0, 1))); MIR_append_insn(ctx, fn, encode); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_UI2D, MIR_new_reg_op(ctx, dbl), MIR_new_reg_op(ctx, len))); mir_d_to_i64_non_nan(ctx, fn, dst, dbl, r_d_slot); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, done))); if (arguments != slow) { MIR_label_t template_hit = MIR_new_label(ctx); MIR_append_insn(ctx, fn, arguments); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, tag), MIR_new_mem_op(ctx, MIR_T_P, (MIR_disp_t)offsetof(ant_object_t, shape), ptr, 0, 1))); for (int i = 0; i < 2; i++) { if (!arguments_shapes[i]) continue; mir_load_imm(ctx, fn, len, (uint64_t)(uintptr_t)arguments_shapes[i]); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, template_hit), MIR_new_reg_op(ctx, tag), MIR_new_reg_op(ctx, len))); } MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, slow))); MIR_append_insn(ctx, fn, template_hit); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, dst), MIR_new_mem_op(ctx, MIR_JSVAL, (MIR_disp_t)(offsetof(ant_object_t, inobj) + ANT_ARGUMENTS_SLOT_LENGTH * sizeof(ant_value_t)), ptr, 0, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, done))); } if (slow_out) return; MIR_append_insn(ctx, fn, slow); MIR_append_insn(ctx, fn, MIR_new_call_insn(ctx, 5, MIR_new_ref_op(ctx, get_length_proto), MIR_new_ref_op(ctx, imp_get_length), MIR_new_reg_op(ctx, dst), MIR_new_reg_op(ctx, r_js), MIR_new_reg_op(ctx, obj))); MIR_append_insn(ctx, fn, done); } static void mir_emit_i32_to_num(MIR_context_t ctx, MIR_item_t fn, MIR_reg_t number, MIR_reg_t integer) { MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_I2D, MIR_new_reg_op(ctx, number), MIR_new_reg_op(ctx, integer))); } void mir_emit_slot_boxed(MIR_context_t ctx, MIR_item_t fn, MIR_reg_t boxed, MIR_reg_t number, uint8_t slot_type, MIR_reg_t d_slot) { if (slot_type == SLOT_I32) { mir_emit_i32_to_num(ctx, fn, number, boxed); mir_d_to_i64_non_nan(ctx, fn, boxed, number, d_slot); } else if (slot_type == SLOT_NUM) mir_d_to_i64(ctx, fn, boxed, number, d_slot); } void vstack_ensure_boxed(jit_vstack_t *vs, int idx, MIR_context_t ctx, MIR_item_t fn, MIR_reg_t d_slot) { if (!vs->slot_type || vs->slot_type[idx] == SLOT_BOXED) return; jit_integer_range_t range = vs->integer_range ? vs->integer_range[idx] : (jit_integer_range_t){0}; if (vs->slot_type[idx] == SLOT_I32 && range.known && range.min == range.max) { mir_load_imm(ctx, fn, vs->regs[idx], tov((double)range.min)); } else { mir_emit_slot_boxed( ctx, fn, vs->regs[idx], vs->d_regs[idx], vs->slot_type[idx], d_slot); } vs->slot_type[idx] = SLOT_BOXED; } void vstack_ensure_num(jit_vstack_t *vs, int idx, MIR_context_t ctx, MIR_item_t fn, MIR_reg_t d_slot) { if (!vs->slot_type || vs->slot_type[idx] == SLOT_NUM) return; if (vs->slot_type[idx] == SLOT_I32) mir_emit_i32_to_num(ctx, fn, vs->d_regs[idx], vs->regs[idx]); else mir_i64_to_d(ctx, fn, vs->d_regs[idx], vs->regs[idx], d_slot); vs->slot_type[idx] = SLOT_NUM; } bool vstack_prepare_num(jit_vstack_t *vs, int idx, MIR_context_t ctx, MIR_item_t fn, MIR_reg_t d_slot) { if (!vs->slot_type) return false; if (vs->slot_type[idx] == SLOT_I32) vstack_ensure_num(vs, idx, ctx, fn, d_slot); return vs->slot_type[idx] == SLOT_NUM; } void vstack_flush_to_boxed(jit_vstack_t *vs, MIR_context_t ctx, MIR_item_t fn, MIR_reg_t d_slot) { if (!vs->slot_type) return; for (int i = 0; i < vs->sp; i++) vstack_ensure_boxed(vs, i, ctx, fn, d_slot); } void vstack_rebox_binop_operands( jit_vstack_t *vs, MIR_context_t ctx, MIR_item_t fn, uint8_t lhs_type, uint8_t rhs_type, MIR_reg_t d_slot) { mir_emit_slot_boxed(ctx, fn, vs->regs[vs->sp - 1], vs->d_regs[vs->sp - 1], lhs_type, d_slot); mir_emit_slot_boxed(ctx, fn, vs->regs[vs->sp], vs->d_regs[vs->sp], rhs_type, d_slot); } bool jit_const_is_heap(ant_value_t cv) { uint8_t t = vtype(cv); return ((1u << t) & ((1u << kTypeObject) | (1u << kTypeString) | (1u << kTypeArray) | (1u << kTypePromise) | (1u << kTypeBigInt) | (1u << kTypeGenerator) | (1u << kTypeSymbol))) != 0; } void mir_load_const_slot(MIR_context_t ctx, MIR_item_t fn, MIR_reg_t dst, ant_value_t *slot) { mir_load_imm(ctx, fn, dst, (uint64_t)(uintptr_t)slot); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, dst), MIR_new_mem_op(ctx, MIR_T_I64, 0, dst, 0, 1))); } void mir_call_helper2(MIR_context_t ctx, MIR_item_t fn, MIR_reg_t dst, MIR_item_t proto, MIR_item_t func_item, MIR_reg_t vm_reg, MIR_reg_t js_reg, MIR_reg_t arg0, MIR_reg_t arg1) { MIR_append_insn(ctx, fn, MIR_new_call_insn(ctx, 7, MIR_new_ref_op(ctx, proto), MIR_new_ref_op(ctx, func_item), MIR_new_reg_op(ctx, dst), MIR_new_reg_op(ctx, vm_reg), MIR_new_reg_op(ctx, js_reg), MIR_new_reg_op(ctx, arg0), MIR_new_reg_op(ctx, arg1))); } void mir_emit_is_num_guard(MIR_context_t ctx, MIR_item_t fn, MIR_reg_t r_bool, MIR_reg_t v, MIR_label_t slow) { (void)r_bool; MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_UBGT, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, v), MIR_new_uint_op(ctx, NANBOX_PREFIX))); } int mir_next_reg_site(int *next_site) { ANT_ASSERT(next_site != NULL, "missing MIR register site counter"); return (*next_site)++; } static MIR_reg_t mir_emit_integer_conversion_guard( MIR_context_t ctx, MIR_item_t fn, MIR_reg_t boxed, MIR_reg_t known_double, bool is_known_double, MIR_reg_t d_slot, double minimum, double maximum, MIR_label_t slow, int site, bool exact) { char double_name[48], integer_name[48]; snprintf(double_name, sizeof(double_name), "spec_d_%d", site); snprintf(integer_name, sizeof(integer_name), "spec_i_%d", site); MIR_reg_t number = known_double; if (!is_known_double) { number = MIR_new_func_reg(ctx, fn->u.func, MIR_T_D, double_name); mir_emit_is_num_guard(ctx, fn, 0, boxed, slow); mir_i64_to_d(ctx, fn, number, boxed, d_slot); } MIR_reg_t integer = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, integer_name); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_DBNE, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, number), MIR_new_reg_op(ctx, number))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_DBLT, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, number), MIR_new_double_op(ctx, minimum))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_DBGT, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, number), MIR_new_double_op(ctx, maximum))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_D2I, MIR_new_reg_op(ctx, integer), MIR_new_reg_op(ctx, number))); if (!exact) return integer; char roundtrip_name[48]; snprintf(roundtrip_name, sizeof(roundtrip_name), "spec_rt_%d", site); MIR_reg_t roundtrip = MIR_new_func_reg(ctx, fn->u.func, MIR_T_D, roundtrip_name); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_I2D, MIR_new_reg_op(ctx, roundtrip), MIR_new_reg_op(ctx, integer))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_DBNE, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, number), MIR_new_reg_op(ctx, roundtrip))); return integer; } MIR_reg_t mir_emit_exact_integer_guard( MIR_context_t ctx, MIR_item_t fn, MIR_reg_t boxed, MIR_reg_t known_double, bool is_known_double, MIR_reg_t d_slot, double minimum, double maximum, MIR_label_t slow, int site) { return mir_emit_integer_conversion_guard( ctx, fn, boxed, known_double, is_known_double, d_slot, minimum, maximum, slow, site, true); } void mir_emit_primitive_type_test( MIR_context_t ctx, MIR_item_t fn, MIR_reg_t value, MIR_reg_t scratch, uint8_t type) { if (type == kTypeNumber) { MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_ULE, MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, value), MIR_new_uint_op(ctx, NANBOX_PREFIX))); } else { MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_URSH, MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, value), MIR_new_uint_op(ctx, NANBOX_TYPE_SHIFT))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_EQ, MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, scratch), MIR_new_uint_op(ctx, (NANBOX_PREFIX >> NANBOX_TYPE_SHIFT) | type))); } MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_OR, MIR_new_reg_op(ctx, value), MIR_new_reg_op(ctx, scratch), MIR_new_uint_op(ctx, js_false))); } static void mir_emit_char_code_at_core( MIR_context_t ctx, MIR_item_t fn, MIR_reg_t callee, MIR_reg_t string, MIR_reg_t index, MIR_reg_t result, MIR_reg_t js, MIR_reg_t d_slot, MIR_label_t slow, MIR_label_t done, int site) { char name[48]; MIR_reg_t regs[3]; for (int i = 0; i < 3; i++) { snprintf(name, sizeof(name), "char_%d_%d", site, i); regs[i] = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, name); } MIR_reg_t tag = regs[0], ptr = regs[1], len = regs[2]; snprintf(name, sizeof(name), "char_double_%d", site); MIR_reg_t number = MIR_new_func_reg(ctx, fn->u.func, MIR_T_D, name); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_URSH, MIR_new_reg_op(ctx, tag), MIR_new_reg_op(ctx, callee), MIR_new_uint_op(ctx, NANBOX_TYPE_SHIFT))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, tag), MIR_new_uint_op(ctx, (NANBOX_PREFIX >> NANBOX_TYPE_SHIFT) | kTypeBuiltin))); mir_emit_decode_ref(ctx, fn, ptr, callee); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, tag), MIR_new_mem_op(ctx, MIR_T_P, offsetof(ant_cfunc_meta_t, fn), ptr, 0, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, tag), MIR_new_uint_op(ctx, (uintptr_t)builtin_string_charCodeAt))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_URSH, MIR_new_reg_op(ctx, tag), MIR_new_reg_op(ctx, string), MIR_new_uint_op(ctx, NANBOX_TYPE_SHIFT))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, tag), MIR_new_uint_op(ctx, JIT_STR_TAG))); mir_emit_string_flat_ptr(ctx, fn, ptr, string, tag, slow, slow); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, tag), MIR_new_mem_op(ctx, MIR_T_U64, offsetof(ant_flat_string_t, meta), ptr, 0, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_AND, MIR_new_reg_op(ctx, tag), MIR_new_reg_op(ctx, tag), MIR_new_uint_op(ctx, STR_META_ASCII_MASK))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, tag), MIR_new_uint_op(ctx, (uint64_t)STR_ASCII_YES << STR_META_ASCII_SHIFT))); MIR_reg_t integer = mir_emit_exact_integer_guard( ctx, fn, index, 0, false, d_slot, 0, INT32_MAX, slow, site); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, len), MIR_new_mem_op(ctx, MIR_T_U64, offsetof(ant_flat_string_t, len), ptr, 0, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_UBGE, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, integer), MIR_new_reg_op(ctx, len))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, tag), MIR_new_mem_op(ctx, MIR_T_U32, offsetof(ant_t, vm_exec_depth), js, 0, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, tag), MIR_new_int_op(ctx, 0))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, tag), MIR_new_mem_op(ctx, MIR_T_U8, offsetof(ant_flat_string_t, bytes), ptr, integer, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_I2D, MIR_new_reg_op(ctx, number), MIR_new_reg_op(ctx, tag))); mir_d_to_i64_non_nan(ctx, fn, result, number, d_slot); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, done))); } void mir_emit_char_code_at( MIR_context_t ctx, MIR_item_t fn, MIR_reg_t target, MIR_reg_t receiver, MIR_reg_t args, int argc, MIR_reg_t result, MIR_reg_t js, MIR_reg_t d_slot, MIR_label_t slow, MIR_label_t done, int site) { char name[48]; MIR_reg_t regs[5]; const char *labels[5] = { "tag", "ptr", "callee", "string", "index" }; for (int i = 0; i < 5; i++) { snprintf(name, sizeof(name), "char_call_%s_%d", labels[i], site); regs[i] = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, name); } MIR_reg_t tag = regs[0], ptr = regs[1], callee = regs[2], string = regs[3], index = regs[4]; MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, callee), MIR_new_reg_op(ctx, target))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, string), MIR_new_reg_op(ctx, receiver))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, index), MIR_new_mem_op(ctx, MIR_JSVAL, 0, args, 0, 1))); if (argc >= 2) { MIR_label_t core = MIR_new_label(ctx); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_URSH, MIR_new_reg_op(ctx, tag), MIR_new_reg_op(ctx, target), MIR_new_uint_op(ctx, NANBOX_TYPE_SHIFT))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, core), MIR_new_reg_op(ctx, tag), MIR_new_uint_op(ctx, (NANBOX_PREFIX >> NANBOX_TYPE_SHIFT) | kTypeFunction))); mir_emit_decode_ref(ctx, fn, ptr, target); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, tag), MIR_new_mem_op(ctx, MIR_T_U32, offsetof(sv_closure_t, call_flags), ptr, 0, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_AND, MIR_new_reg_op(ctx, tag), MIR_new_reg_op(ctx, tag), MIR_new_uint_op(ctx, SV_CALL_IS_UNCURRY | SV_CALL_HAS_BOUND_ARGS))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, core), MIR_new_reg_op(ctx, tag), MIR_new_uint_op(ctx, SV_CALL_IS_UNCURRY))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, callee), MIR_new_mem_op(ctx, MIR_JSVAL, offsetof(sv_closure_t, bound_this), ptr, 0, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, string), MIR_new_mem_op(ctx, MIR_JSVAL, 0, args, 0, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, index), MIR_new_mem_op(ctx, MIR_JSVAL, sizeof(ant_value_t), args, 0, 1))); MIR_append_insn(ctx, fn, core); } mir_emit_char_code_at_core(ctx, fn, callee, string, index, result, js, d_slot, slow, done, site); } MIR_reg_t mir_emit_word32_guard( MIR_context_t ctx, MIR_item_t fn, MIR_reg_t boxed, MIR_reg_t known_double, bool is_known_double, bool is_known_i32, MIR_reg_t d_slot, MIR_label_t slow, int site) { if (is_known_i32) return boxed; char name[48]; MIR_reg_t bits = boxed; MIR_reg_t number = known_double; if (is_known_double) { snprintf(name, sizeof(name), "word_bits_%d", site); bits = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, name); mir_d_to_i64_non_nan(ctx, fn, bits, number, d_slot); } snprintf(name, sizeof(name), "word_magnitude_%d", site); MIR_reg_t magnitude = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, name); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_LSH, MIR_new_reg_op(ctx, magnitude), MIR_new_reg_op(ctx, bits), MIR_new_int_op(ctx, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_UBGE, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, magnitude), MIR_new_uint_op(ctx, tov(0x1p63) << 1))); if (!is_known_double) { snprintf(name, sizeof(name), "word_double_%d", site); number = MIR_new_func_reg(ctx, fn->u.func, MIR_T_D, name); mir_i64_to_d(ctx, fn, number, bits, d_slot); } snprintf(name, sizeof(name), "word_integer_%d", site); MIR_reg_t integer = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, name); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_D2I, MIR_new_reg_op(ctx, integer), MIR_new_reg_op(ctx, number))); return integer; } MIR_reg_t mir_emit_array_index_guard( MIR_context_t ctx, MIR_item_t fn, MIR_reg_t boxed, MIR_reg_t known_double, bool is_known_double, MIR_reg_t d_slot, MIR_label_t slow, int site) { char name[48]; MIR_reg_t bits = boxed; MIR_reg_t number = known_double; if (is_known_double) { snprintf(name, sizeof(name), "index_bits_%d", site); bits = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, name); mir_d_to_i64_non_nan(ctx, fn, bits, number, d_slot); } MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_UBGT, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, bits), MIR_new_uint_op(ctx, tov((double)UINT32_MAX - 1.0)))); if (!is_known_double) { snprintf(name, sizeof(name), "index_double_%d", site); number = MIR_new_func_reg(ctx, fn->u.func, MIR_T_D, name); mir_i64_to_d(ctx, fn, number, bits, d_slot); } snprintf(name, sizeof(name), "index_integer_%d", site); MIR_reg_t integer = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, name); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_D2I, MIR_new_reg_op(ctx, integer), MIR_new_reg_op(ctx, number))); snprintf(name, sizeof(name), "index_roundtrip_%d", site); MIR_reg_t roundtrip = MIR_new_func_reg(ctx, fn->u.func, MIR_T_D, name); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_I2D, MIR_new_reg_op(ctx, roundtrip), MIR_new_reg_op(ctx, integer))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_DBNE, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, number), MIR_new_reg_op(ctx, roundtrip))); return integer; } MIR_reg_t mir_emit_known_array_index_guard( MIR_context_t ctx, MIR_item_t fn, MIR_reg_t boxed, MIR_reg_t integer, MIR_label_t slow, MIR_reg_t d_slot, int site, MIR_reg_t cached_key, MIR_reg_t cached_index) { if (!integer) { MIR_label_t hit = MIR_new_label(ctx); mir_emit_is_num_guard(ctx, fn, 0, boxed, slow); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, hit), MIR_new_reg_op(ctx, boxed), MIR_new_reg_op(ctx, cached_key))); MIR_reg_t checked = mir_emit_array_index_guard( ctx, fn, boxed, 0, false, d_slot, slow, site); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, cached_key), MIR_new_reg_op(ctx, boxed))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, cached_index), MIR_new_reg_op(ctx, checked))); MIR_append_insn(ctx, fn, hit); char name[48]; snprintf(name, sizeof(name), "checked_index_%d", site); MIR_reg_t result = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, name); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, result), MIR_new_reg_op(ctx, cached_index))); return result; } return integer; } MIR_reg_t mir_emit_dense_element_guard( MIR_context_t ctx, MIR_item_t fn, MIR_reg_t object, MIR_reg_t index, MIR_reg_t value, jit_element_access_t access, MIR_label_t slow, int site, jit_dense_element_t *element) { bool writable = access == JIT_ELEMENT_WRITE; char tag_name[48], ptr_name[48], flags_name[48]; char data_name[48], len_name[48]; snprintf(tag_name, sizeof(tag_name), "elem_tag_%d", site); snprintf(ptr_name, sizeof(ptr_name), "elem_ptr_%d", site); snprintf(flags_name, sizeof(flags_name), "elem_flags_%d", site); snprintf(data_name, sizeof(data_name), "elem_data_%d", site); snprintf(len_name, sizeof(len_name), "elem_len_%d", site); MIR_reg_t tag = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, tag_name); MIR_reg_t ptr = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, ptr_name); MIR_reg_t flags = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, flags_name); MIR_reg_t data = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, data_name); MIR_reg_t len = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, len_name); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_URSH, MIR_new_reg_op(ctx, tag), MIR_new_reg_op(ctx, object), MIR_new_uint_op(ctx, NANBOX_TYPE_SHIFT))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, tag), MIR_new_uint_op(ctx, NANBOX_TARR_TAG))); mir_emit_decode_ref(ctx, fn, ptr, object); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, flags), MIR_new_mem_op(ctx, MIR_T_U16, (MIR_disp_t)offsetof(ant_object_t, flags), ptr, 0, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_AND, MIR_new_reg_op(ctx, tag), MIR_new_reg_op(ctx, flags), MIR_new_uint_op(ctx, ANT_OBJECT_FLAG_EXOTIC | ANT_OBJECT_FLAG_FAST_ARRAY | ANT_OBJECT_FLAG_DENSE_LENGTH_FITS | (writable ? ANT_OBJECT_FLAG_FROZEN : 0)))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, tag), MIR_new_uint_op(ctx, ANT_OBJECT_FLAG_FAST_ARRAY | ANT_OBJECT_FLAG_DENSE_LENGTH_FITS))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, data), MIR_new_mem_op(ctx, MIR_T_P, (MIR_disp_t)offsetof(ant_object_t, u.array.data), ptr, 0, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, data), MIR_new_int_op(ctx, 0))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, len), MIR_new_mem_op(ctx, MIR_T_U32, (MIR_disp_t)offsetof(ant_object_t, u.array.len), ptr, 0, 1))); MIR_label_t past_end = element && element->past_end ? element->past_end : slow; MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_UBGE, MIR_new_label_op(ctx, past_end), MIR_new_reg_op(ctx, index), MIR_new_reg_op(ctx, len))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, value), MIR_new_mem_op(ctx, MIR_JSVAL, 0, data, index, sizeof(ant_value_t)))); if (access == JIT_ELEMENT_NUMERIC_READ) { mir_emit_is_num_guard(ctx, fn, 0, value, slow); } else if (access == JIT_ELEMENT_READ) { MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_UBGE, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, value), MIR_new_uint_op(ctx, ANT_SENTINEL_TAG))); } if (element) { element->ptr = ptr; element->flags = flags; element->len = len; } return data; } static const struct { TypedArrayType type; MIR_type_t mem; MIR_scale_t scale; } typed_array_kinds[] = { { TYPED_ARRAY_UINT8, MIR_T_U8, 1 }, { TYPED_ARRAY_FLOAT64, MIR_T_D, 8 }, { TYPED_ARRAY_INT32, MIR_T_I32, 4 }, { TYPED_ARRAY_FLOAT32, MIR_T_F, 4 }, { TYPED_ARRAY_UINT8_CLAMPED, MIR_T_U8, 1 }, { TYPED_ARRAY_INT8, MIR_T_I8, 1 }, { TYPED_ARRAY_UINT16, MIR_T_U16, 2 }, { TYPED_ARRAY_INT16, MIR_T_I16, 2 }, { TYPED_ARRAY_UINT32, MIR_T_U32, 4 }, }; #define TA_INSN(...) MIR_append_insn(ctx, fn, MIR_new_insn(ctx, __VA_ARGS__)) #define TA_REG(r) MIR_new_reg_op(ctx, r) #define TA_LBL(l) MIR_new_label_op(ctx, l) #define TA_MEM(type, off, base) MIR_new_mem_op(ctx, type, (MIR_disp_t)(off), base, 0, 1) typedef struct { MIR_reg_t type, t, data; } jit_typed_array_t; static jit_typed_array_t mir_emit_typed_array_guard( MIR_context_t ctx, MIR_item_t fn, MIR_reg_t object, MIR_reg_t index, MIR_label_t slow, int site) { char name[48]; jit_typed_array_t ta; snprintf(name, sizeof(name), "ta_type_%d", site); ta.type = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, name); snprintf(name, sizeof(name), "ta_t_%d", site); ta.t = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, name); snprintf(name, sizeof(name), "ta_data_%d", site); ta.data = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, name); MIR_reg_t ptr = ta.type, t = ta.t, data = ta.data; TA_INSN(MIR_URSH, TA_REG(t), TA_REG(object), MIR_new_uint_op(ctx, NANBOX_TYPE_SHIFT)); TA_INSN(MIR_BNE, TA_LBL(slow), TA_REG(t), MIR_new_uint_op(ctx, NANBOX_TOBJ_TAG)); mir_emit_decode_ref(ctx, fn, ptr, object); TA_INSN(MIR_MOV, TA_REG(t), TA_MEM(MIR_T_U32, offsetof(ant_object_t, native_tag), ptr)); TA_INSN(MIR_BNE, TA_LBL(slow), TA_REG(t), MIR_new_uint_op(ctx, BUFFER_TYPEDARRAY_NATIVE_TAG)); TA_INSN(MIR_MOV, TA_REG(t), TA_MEM(MIR_T_P, offsetof(ant_object_t, exotic_ops), ptr)); TA_INSN(MIR_BNE, TA_LBL(slow), TA_REG(t), MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)buffer_typedarray_exotic_ops())); TA_INSN(MIR_MOV, TA_REG(ptr), TA_MEM(MIR_T_P, offsetof(ant_object_t, native_ptr), ptr)); TA_INSN(MIR_MOV, TA_REG(t), TA_MEM(MIR_T_U64, offsetof(TypedArrayData, length), ptr)); TA_INSN(MIR_UBGE, TA_LBL(slow), TA_REG(index), TA_REG(t)); TA_INSN(MIR_MOV, TA_REG(data), TA_MEM(MIR_T_P, offsetof(TypedArrayData, buffer), ptr)); TA_INSN(MIR_BEQ, TA_LBL(slow), TA_REG(data), MIR_new_int_op(ctx, 0)); TA_INSN(MIR_MOV, TA_REG(t), TA_MEM(MIR_T_U8, offsetof(ArrayBufferData, is_detached), data)); TA_INSN(MIR_BNE, TA_LBL(slow), TA_REG(t), MIR_new_int_op(ctx, 0)); TA_INSN(MIR_MOV, TA_REG(data), TA_MEM(MIR_T_P, offsetof(ArrayBufferData, data), data)); TA_INSN(MIR_MOV, TA_REG(t), TA_MEM(MIR_T_U64, offsetof(TypedArrayData, byte_offset), ptr)); TA_INSN(MIR_ADD, TA_REG(data), TA_REG(data), TA_REG(t)); TA_INSN(MIR_MOV, TA_REG(ptr), TA_MEM(MIR_T_U32, offsetof(TypedArrayData, type), ptr)); return ta; } static int typed_array_kind_index(int type) { for (size_t i = 0; i < sizeof(typed_array_kinds) / sizeof(typed_array_kinds[0]); i++) if ((int)typed_array_kinds[i].type == type) return (int)i; return -1; } static void typed_array_emit_load( MIR_context_t ctx, MIR_item_t fn, jit_typed_array_t ta, MIR_reg_t index, MIR_reg_t number, MIR_reg_t single, size_t kind, MIR_label_t done) { MIR_type_t mem = typed_array_kinds[kind].mem; MIR_op_t element = MIR_new_mem_op(ctx, mem, 0, ta.data, index, typed_array_kinds[kind].scale); if (mem == MIR_T_D) { TA_INSN(MIR_DMOV, TA_REG(number), element); } else if (mem == MIR_T_F) { TA_INSN(MIR_FMOV, TA_REG(single), element); TA_INSN(MIR_F2D, TA_REG(number), TA_REG(single)); } else { TA_INSN(MIR_MOV, TA_REG(ta.t), element); TA_INSN(MIR_I2D, TA_REG(number), TA_REG(ta.t)); } TA_INSN(MIR_JMP, TA_LBL(done)); } void mir_emit_typed_array_read( MIR_context_t ctx, MIR_item_t fn, MIR_reg_t object, MIR_reg_t index, MIR_reg_t number, MIR_label_t slow, MIR_label_t done, int site, int first_type) { jit_typed_array_t ta = mir_emit_typed_array_guard(ctx, fn, object, index, slow, site); char name[48]; snprintf(name, sizeof(name), "ta_f_%d", site); MIR_reg_t single = MIR_new_func_reg(ctx, fn->u.func, MIR_T_F, name); int first = typed_array_kind_index(first_type); for (int pass = first >= 0 ? 0 : 1; pass < 2; pass++) for (size_t i = 0; i < sizeof(typed_array_kinds) / sizeof(typed_array_kinds[0]); i++) { if (pass == 0 ? (int)i != first : (int)i == first) continue; MIR_label_t next = MIR_new_label(ctx); TA_INSN(MIR_BNE, TA_LBL(next), TA_REG(ta.type), MIR_new_uint_op(ctx, typed_array_kinds[i].type)); typed_array_emit_load(ctx, fn, ta, index, number, single, i, done); MIR_append_insn(ctx, fn, next); } TA_INSN(MIR_JMP, TA_LBL(slow)); } static void typed_array_emit_store( MIR_context_t ctx, MIR_item_t fn, jit_typed_array_t ta, MIR_reg_t index, MIR_reg_t number, MIR_reg_t single, size_t kind, MIR_label_t done) { MIR_type_t mem = typed_array_kinds[kind].mem; MIR_op_t element = MIR_new_mem_op(ctx, mem, 0, ta.data, index, typed_array_kinds[kind].scale); if (mem == MIR_T_D) { TA_INSN(MIR_DMOV, element, TA_REG(number)); } else if (mem == MIR_T_F) { TA_INSN(MIR_D2F, TA_REG(single), TA_REG(number)); TA_INSN(MIR_FMOV, element, TA_REG(single)); } else { TA_INSN(MIR_MOV, element, TA_REG(ta.t)); } TA_INSN(MIR_JMP, TA_LBL(done)); } static void typed_array_emit_integer( MIR_context_t ctx, MIR_item_t fn, jit_typed_array_t ta, MIR_reg_t number, MIR_reg_t round, bool clamped, MIR_label_t slow) { TA_INSN(MIR_D2I, TA_REG(ta.t), TA_REG(number)); TA_INSN(MIR_I2D, TA_REG(round), TA_REG(ta.t)); TA_INSN(MIR_DBNE, TA_LBL(slow), TA_REG(number), TA_REG(round)); if (clamped) TA_INSN(MIR_UBGT, TA_LBL(slow), TA_REG(ta.t), MIR_new_uint_op(ctx, UINT8_MAX)); } void mir_emit_typed_array_write( MIR_context_t ctx, MIR_item_t fn, MIR_reg_t object, MIR_reg_t index, MIR_reg_t value, MIR_reg_t d_slot, MIR_label_t slow, MIR_label_t done, int site, int first_type) { TA_INSN(MIR_UBGT, TA_LBL(slow), TA_REG(value), MIR_new_uint_op(ctx, NANBOX_PREFIX)); jit_typed_array_t ta = mir_emit_typed_array_guard(ctx, fn, object, index, slow, site); char name[48]; snprintf(name, sizeof(name), "ta_num_%d", site); MIR_reg_t number = MIR_new_func_reg(ctx, fn->u.func, MIR_T_D, name); snprintf(name, sizeof(name), "ta_round_%d", site); MIR_reg_t round = MIR_new_func_reg(ctx, fn->u.func, MIR_T_D, name); snprintf(name, sizeof(name), "ta_f_%d", site); MIR_reg_t single = MIR_new_func_reg(ctx, fn->u.func, MIR_T_F, name); MIR_label_t integer = MIR_new_label(ctx); mir_i64_to_d(ctx, fn, number, value, d_slot); int first = typed_array_kind_index(first_type); if (first >= 0) { MIR_type_t mem = typed_array_kinds[first].mem; MIR_label_t next = MIR_new_label(ctx); TA_INSN(MIR_BNE, TA_LBL(next), TA_REG(ta.type), MIR_new_uint_op(ctx, typed_array_kinds[first].type)); if (mem != MIR_T_D && mem != MIR_T_F) typed_array_emit_integer(ctx, fn, ta, number, round, typed_array_kinds[first].type == TYPED_ARRAY_UINT8_CLAMPED, slow); typed_array_emit_store(ctx, fn, ta, index, number, single, (size_t)first, done); MIR_append_insn(ctx, fn, next); } for (size_t i = 0; i < sizeof(typed_array_kinds) / sizeof(typed_array_kinds[0]); i++) { MIR_type_t mem = typed_array_kinds[i].mem; if ((mem != MIR_T_D && mem != MIR_T_F) || (int)i == first) continue; MIR_label_t next = MIR_new_label(ctx); TA_INSN(MIR_BNE, TA_LBL(next), TA_REG(ta.type), MIR_new_uint_op(ctx, typed_array_kinds[i].type)); typed_array_emit_store(ctx, fn, ta, index, number, single, i, done); MIR_append_insn(ctx, fn, next); } TA_INSN(MIR_D2I, TA_REG(ta.t), TA_REG(number)); TA_INSN(MIR_I2D, TA_REG(round), TA_REG(ta.t)); TA_INSN(MIR_DBNE, TA_LBL(slow), TA_REG(number), TA_REG(round)); TA_INSN(MIR_BNE, TA_LBL(integer), TA_REG(ta.type), MIR_new_uint_op(ctx, TYPED_ARRAY_UINT8_CLAMPED)); TA_INSN(MIR_UBGT, TA_LBL(slow), TA_REG(ta.t), MIR_new_uint_op(ctx, UINT8_MAX)); MIR_append_insn(ctx, fn, integer); for (size_t i = 0; i < sizeof(typed_array_kinds) / sizeof(typed_array_kinds[0]); i++) { MIR_type_t mem = typed_array_kinds[i].mem; if (mem == MIR_T_D || mem == MIR_T_F || (int)i == first) continue; MIR_label_t next = MIR_new_label(ctx); TA_INSN(MIR_BNE, TA_LBL(next), TA_REG(ta.type), MIR_new_uint_op(ctx, typed_array_kinds[i].type)); typed_array_emit_store(ctx, fn, ta, index, number, single, i, done); MIR_append_insn(ctx, fn, next); } TA_INSN(MIR_JMP, TA_LBL(slow)); } #undef TA_INSN #undef TA_REG #undef TA_LBL #undef TA_MEM uint8_t jit_math_field_builtin(uint8_t receiver, const char *name, uint32_t len) { if (receiver != JIT_BUILTIN_MATH) return JIT_BUILTIN_UNKNOWN; for (int i = 0; i < ANT_MATH_INTRINSIC_COUNT; i++) if (strlen(ant_math_intrinsic_names[i]) == len && memcmp(name, ant_math_intrinsic_names[i], len) == 0) return (uint8_t)(JIT_BUILTIN_MATH_FN + i); return JIT_BUILTIN_UNKNOWN; } MIR_label_t mir_emit_math_call(MIR_context_t ctx, MIR_item_t fn, const jit_math_call_t *call) { bool binary = call->kind >= ANT_MATH_FIRST_BINARY; MIR_label_t slow = MIR_new_label(ctx); MIR_label_t done = MIR_new_label(ctx); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, call->scratch), MIR_new_mem_op(ctx, MIR_JSVAL, (MIR_disp_t)(offsetof(ant_t, sym.math_fns) + (size_t)call->kind * sizeof(ant_value_t)), call->r_js, 0, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, call->callee), MIR_new_reg_op(ctx, call->scratch))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_UBGT, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, call->a), MIR_new_uint_op(ctx, NANBOX_PREFIX))); if (binary) MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_UBGT, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, call->b), MIR_new_uint_op(ctx, NANBOX_PREFIX))); char name[48]; #define REG(var, type, tag) \ snprintf(name, sizeof(name), "math_" tag "_%d", call->site); \ MIR_reg_t var = MIR_new_func_reg(ctx, fn->u.func, type, name) #define INSN(...) MIR_append_insn(ctx, fn, MIR_new_insn(ctx, __VA_ARGS__)) #define R(reg) MIR_new_reg_op(ctx, reg) #define U(value) MIR_new_uint_op(ctx, value) #define L(label) MIR_new_label_op(ctx, label) const uint64_t abs_mask = UINT64_C(0x7fffffffffffffff), inf_bits = UINT64_C(0x7ff0000000000000); switch (call->kind) { case ANT_MATH_ABS: INSN(MIR_AND, R(call->result), R(call->a), U(abs_mask)); break; case ANT_MATH_SIGN: { REG(t, MIR_T_I64, "t"); MIR_label_t keep = MIR_new_label(ctx), negative = MIR_new_label(ctx); INSN(MIR_AND, R(t), R(call->a), U(abs_mask)); INSN(MIR_BEQ, L(keep), R(t), MIR_new_int_op(ctx, 0)); INSN(MIR_UBGT, L(keep), R(t), U(inf_bits)); INSN(MIR_BLT, L(negative), R(call->a), MIR_new_int_op(ctx, 0)); INSN(MIR_MOV, R(call->result), U(UINT64_C(0x3ff0000000000000))); INSN(MIR_JMP, L(done)); MIR_append_insn(ctx, fn, negative); INSN(MIR_MOV, R(call->result), U(UINT64_C(0xbff0000000000000))); INSN(MIR_JMP, L(done)); MIR_append_insn(ctx, fn, keep); INSN(MIR_MOV, R(call->result), R(call->a)); break; } case ANT_MATH_MAX: case ANT_MATH_MIN: { bool max = call->kind == ANT_MATH_MAX; REG(da, MIR_T_D, "a"); REG(db, MIR_T_D, "b"); MIR_label_t pick_a = MIR_new_label(ctx), pick_b = MIR_new_label(ctx), tie = MIR_new_label(ctx); mir_i64_to_d(ctx, fn, da, call->a, call->r_d_slot); mir_i64_to_d(ctx, fn, db, call->b, call->r_d_slot); INSN(max ? MIR_DBGT : MIR_DBLT, L(pick_b), R(db), R(da)); INSN(max ? MIR_DBLT : MIR_DBGT, L(pick_a), R(db), R(da)); INSN(MIR_DBEQ, L(tie), R(db), R(da)); INSN(MIR_MOV, R(call->result), U(UINT64_C(0x7ff8000000000000))); INSN(MIR_JMP, L(done)); MIR_append_insn(ctx, fn, tie); INSN(max ? MIR_AND : MIR_OR, R(call->result), R(call->a), R(call->b)); INSN(MIR_JMP, L(done)); MIR_append_insn(ctx, fn, pick_b); INSN(MIR_MOV, R(call->result), R(call->b)); INSN(MIR_JMP, L(done)); MIR_append_insn(ctx, fn, pick_a); INSN(MIR_MOV, R(call->result), R(call->a)); break; } case ANT_MATH_IMUL: { REG(ia, MIR_T_I64, "ia"); REG(ib, MIR_T_I64, "ib"); REG(d, MIR_T_D, "d"); INSN(MIR_AND, R(ia), R(call->a), U(abs_mask)); INSN(MIR_UBGE, L(slow), R(ia), U(UINT64_C(0x43e0000000000000))); INSN(MIR_AND, R(ib), R(call->b), U(abs_mask)); INSN(MIR_UBGE, L(slow), R(ib), U(UINT64_C(0x43e0000000000000))); mir_i64_to_d(ctx, fn, d, call->a, call->r_d_slot); INSN(MIR_D2I, R(ia), R(d)); mir_i64_to_d(ctx, fn, d, call->b, call->r_d_slot); INSN(MIR_D2I, R(ib), R(d)); INSN(MIR_MUL, R(ia), R(ia), R(ib)); INSN(MIR_EXT32, R(ia), R(ia)); INSN(MIR_I2D, R(d), R(ia)); mir_d_to_i64_non_nan(ctx, fn, call->result, d, call->r_d_slot); break; } default: { REG(da, MIR_T_D, "a"); REG(dr, MIR_T_D, "r"); mir_i64_to_d(ctx, fn, da, call->a, call->r_d_slot); MIR_append_insn(ctx, fn, MIR_new_call_insn(ctx, 4, MIR_new_ref_op(ctx, call->math1_proto), MIR_new_ref_op(ctx, call->imp_math[call->kind]), R(dr), R(da))); mir_d_to_i64(ctx, fn, call->result, dr, call->r_d_slot); break; } } #undef REG #undef INSN #undef R #undef U #undef L MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, done))); MIR_append_insn(ctx, fn, slow); return done; } void mir_emit_array_add_guard( MIR_context_t ctx, MIR_item_t fn, MIR_reg_t r_js, MIR_reg_t ptr, MIR_reg_t flags, MIR_label_t slow, int site) { char name[48]; snprintf(name, sizeof(name), "add_t_%d", site); MIR_reg_t t = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, name); snprintf(name, sizeof(name), "add_ap_%d", site); MIR_reg_t ap = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, name); #define LOAD(dst, type, disp, base) \ MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, dst), \ MIR_new_mem_op(ctx, type, (MIR_disp_t)(disp), base, 0, 1))) #define BRANCH(code, a, b) \ MIR_append_insn(ctx, fn, MIR_new_insn(ctx, code, MIR_new_label_op(ctx, slow), a, b)) MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_AND, MIR_new_reg_op(ctx, t), MIR_new_reg_op(ctx, flags), MIR_new_uint_op(ctx, ANT_OBJECT_FLAG_EXTENSIBLE | ANT_OBJECT_FLAG_SEALED | ANT_OBJECT_FLAG_FROZEN | ANT_OBJECT_FLAG_EXOTIC))); BRANCH(MIR_BNE, MIR_new_reg_op(ctx, t), MIR_new_uint_op(ctx, ANT_OBJECT_FLAG_EXTENSIBLE)); LOAD(t, MIR_JSVAL, offsetof(ant_object_t, proto), ptr); LOAD(ap, MIR_JSVAL, offsetof(ant_t, sym.array_proto), r_js); BRANCH(MIR_BNE, MIR_new_reg_op(ctx, t), MIR_new_reg_op(ctx, ap)); LOAD(t, MIR_T_U32, offsetof(ant_t, ic.epoch), r_js); LOAD(ap, MIR_T_U32, offsetof(ant_t, array_chain_plain_epoch), r_js); BRANCH(MIR_BNE, MIR_new_reg_op(ctx, t), MIR_new_reg_op(ctx, ap)); LOAD(ap, MIR_JSVAL, offsetof(ant_t, sym.array_proto), r_js); mir_emit_decode_ref(ctx, fn, ap, ap); LOAD(t, MIR_T_U32, offsetof(ant_object_t, u.array.len), ap); BRANCH(MIR_BNE, MIR_new_reg_op(ctx, t), MIR_new_int_op(ctx, 0)); #undef LOAD #undef BRANCH } void mir_emit_word32_binary( MIR_context_t ctx, MIR_item_t fn, sv_op_t op, MIR_reg_t left, MIR_reg_t right, MIR_reg_t result, MIR_reg_t result_double, bool materialize_double, int site) { char shift_name[48]; snprintf(shift_name, sizeof(shift_name), "word_shift_%d", site); MIR_reg_t shift = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, shift_name); MIR_insn_code_t operation; switch (op) { case OP_BAND: operation = MIR_ANDS; break; case OP_BOR: operation = MIR_ORS; break; case OP_BXOR: operation = MIR_XORS; break; case OP_SHL: operation = MIR_LSHS; break; case OP_SHR: operation = MIR_RSHS; break; default: operation = MIR_URSHS; break; } if (op == OP_SHL || op == OP_SHR || op == OP_USHR) { MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_ANDS, MIR_new_reg_op(ctx, shift), MIR_new_reg_op(ctx, right), MIR_new_int_op(ctx, 0x1f))); right = shift; } MIR_append_insn(ctx, fn, MIR_new_insn(ctx, operation, MIR_new_reg_op(ctx, result), MIR_new_reg_op(ctx, left), MIR_new_reg_op(ctx, right))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, op == OP_USHR ? MIR_UEXT32 : MIR_EXT32, MIR_new_reg_op(ctx, result), MIR_new_reg_op(ctx, result))); if (materialize_double) MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_I2D, MIR_new_reg_op(ctx, result_double), MIR_new_reg_op(ctx, result))); } void mir_emit_numeric_equality( MIR_context_t ctx, MIR_item_t fn, MIR_reg_t left, MIR_reg_t right, MIR_reg_t result, MIR_reg_t scratch, bool invert) { MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_DEQ, MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, left), MIR_new_reg_op(ctx, right))); if (invert) MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_XOR, MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, scratch), MIR_new_int_op(ctx, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_OR, MIR_new_reg_op(ctx, result), MIR_new_uint_op(ctx, js_false), MIR_new_reg_op(ctx, scratch))); } static void mir_emit_string_equality( MIR_context_t ctx, MIR_item_t fn, MIR_reg_t left, MIR_reg_t right, MIR_reg_t scratch, bool inline_long_strings, MIR_label_t equal, MIR_label_t not_equal, MIR_label_t slow) { MIR_reg_t lp = MIR_reg(ctx, "str_eq_l", fn->u.func); MIR_reg_t rp = MIR_reg(ctx, "str_eq_r", fn->u.func); MIR_reg_t len = MIR_reg(ctx, "str_eq_n", fn->u.func); MIR_reg_t word = MIR_reg(ctx, "str_eq_w", fn->u.func); MIR_label_t long_strings = inline_long_strings ? MIR_new_label(ctx) : slow; MIR_label_t word_loop = MIR_new_label(ctx); MIR_label_t last_word = MIR_new_label(ctx); if (inline_long_strings) { mir_emit_string_flat_ptr(ctx, fn, lp, left, scratch, slow, slow); mir_emit_string_flat_ptr(ctx, fn, rp, right, scratch, slow, slow); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, equal), MIR_new_reg_op(ctx, lp), MIR_new_reg_op(ctx, rp))); } else { MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_OR, MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, left), MIR_new_reg_op(ctx, right))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_AND, MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, scratch), MIR_new_uint_op(ctx, STR_HEAP_TAG_MASK))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, scratch), MIR_new_int_op(ctx, 0))); mir_emit_decode_ref(ctx, fn, lp, left); mir_emit_decode_ref(ctx, fn, rp, right); } MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, len), MIR_new_mem_op(ctx, MIR_T_I64, offsetof(ant_flat_string_t, len), lp, 0, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, scratch), MIR_new_mem_op(ctx, MIR_T_I64, offsetof(ant_flat_string_t, len), rp, 0, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, not_equal), MIR_new_reg_op(ctx, len), MIR_new_reg_op(ctx, scratch))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_UBGT, MIR_new_label_op(ctx, long_strings), MIR_new_reg_op(ctx, len), MIR_new_uint_op(ctx, 8))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, equal), MIR_new_reg_op(ctx, len), MIR_new_uint_op(ctx, 0))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, lp), MIR_new_mem_op(ctx, MIR_T_I64, offsetof(ant_flat_string_t, bytes), lp, 0, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, rp), MIR_new_mem_op(ctx, MIR_T_I64, offsetof(ant_flat_string_t, bytes), rp, 0, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_XOR, MIR_new_reg_op(ctx, lp), MIR_new_reg_op(ctx, lp), MIR_new_reg_op(ctx, rp))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_LSH, MIR_new_reg_op(ctx, len), MIR_new_reg_op(ctx, len), MIR_new_uint_op(ctx, 3))); mir_load_imm(ctx, fn, scratch, 64); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_SUB, MIR_new_reg_op(ctx, len), MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, len))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_LSH, MIR_new_reg_op(ctx, lp), MIR_new_reg_op(ctx, lp), MIR_new_reg_op(ctx, len))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, not_equal), MIR_new_reg_op(ctx, lp), MIR_new_int_op(ctx, 0))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, equal))); if (!inline_long_strings) return; MIR_append_insn(ctx, fn, long_strings); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_ADD, MIR_new_reg_op(ctx, lp), MIR_new_reg_op(ctx, lp), MIR_new_uint_op(ctx, offsetof(ant_flat_string_t, bytes)))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_ADD, MIR_new_reg_op(ctx, rp), MIR_new_reg_op(ctx, rp), MIR_new_uint_op(ctx, offsetof(ant_flat_string_t, bytes)))); MIR_append_insn(ctx, fn, word_loop); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_UBLE, MIR_new_label_op(ctx, last_word), MIR_new_reg_op(ctx, len), MIR_new_uint_op(ctx, 8))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, scratch), MIR_new_mem_op(ctx, MIR_T_I64, 0, lp, 0, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, word), MIR_new_mem_op(ctx, MIR_T_I64, 0, rp, 0, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, not_equal), MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, word))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_ADD, MIR_new_reg_op(ctx, lp), MIR_new_reg_op(ctx, lp), MIR_new_uint_op(ctx, 8))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_ADD, MIR_new_reg_op(ctx, rp), MIR_new_reg_op(ctx, rp), MIR_new_uint_op(ctx, 8))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_SUB, MIR_new_reg_op(ctx, len), MIR_new_reg_op(ctx, len), MIR_new_uint_op(ctx, 8))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, word_loop))); MIR_append_insn(ctx, fn, last_word); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_ADD, MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, lp), MIR_new_reg_op(ctx, len))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, scratch), MIR_new_mem_op(ctx, MIR_T_I64, -8, scratch, 0, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_ADD, MIR_new_reg_op(ctx, word), MIR_new_reg_op(ctx, rp), MIR_new_reg_op(ctx, len))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, word), MIR_new_mem_op(ctx, MIR_T_I64, -8, word, 0, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, not_equal), MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, word))); } static void mir_emit_const_word_check( MIR_context_t ctx, MIR_item_t fn, MIR_reg_t ptr, MIR_reg_t scratch, const ant_flat_string_t *lit, ant_offset_t off, uint64_t mask, MIR_label_t not_equal) { uint64_t word = 0; memcpy(&word, lit->bytes + off, lit->len - off < 8 ? (size_t)(lit->len - off) : 8); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, scratch), MIR_new_mem_op(ctx, MIR_T_I64, (MIR_disp_t)(offsetof(ant_flat_string_t, bytes) + off), ptr, 0, 1))); if (mask != UINT64_MAX) MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_AND, MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, scratch), MIR_new_uint_op(ctx, mask))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, not_equal), MIR_new_reg_op(ctx, scratch), MIR_new_uint_op(ctx, word))); } void mir_emit_const_string_equality( MIR_context_t ctx, MIR_item_t fn, MIR_reg_t value, MIR_reg_t literal, const ant_flat_string_t *lit, MIR_reg_t result, MIR_reg_t scratch, bool invert, MIR_label_t slow, MIR_label_t done) { MIR_label_t equal = MIR_new_label(ctx); MIR_label_t not_equal = MIR_new_label(ctx); MIR_reg_t ptr = MIR_reg(ctx, "str_eq_l", fn->u.func); ant_offset_t len = lit->len; MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, equal), MIR_new_reg_op(ctx, value), MIR_new_reg_op(ctx, literal))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_URSH, MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, value), MIR_new_uint_op(ctx, NANBOX_TYPE_SHIFT))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, not_equal), MIR_new_reg_op(ctx, scratch), MIR_new_uint_op(ctx, JIT_STR_TAG))); mir_emit_string_flat_ptr(ctx, fn, ptr, value, scratch, slow, slow); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, scratch), MIR_new_mem_op(ctx, MIR_T_I64, offsetof(ant_flat_string_t, len), ptr, 0, 1))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, not_equal), MIR_new_reg_op(ctx, scratch), MIR_new_uint_op(ctx, (uint64_t)len))); if (len > 0 && len < 8) mir_emit_const_word_check(ctx, fn, ptr, scratch, lit, 0, (UINT64_C(1) << (len * 8)) - 1, not_equal); else if (len >= 8) { ant_offset_t off = 0; for (; off + 8 <= len; off += 8) mir_emit_const_word_check(ctx, fn, ptr, scratch, lit, off, UINT64_MAX, not_equal); if (off < len) mir_emit_const_word_check(ctx, fn, ptr, scratch, lit, len - 8, UINT64_MAX, not_equal); } MIR_append_insn(ctx, fn, equal); mir_load_imm(ctx, fn, result, invert ? js_false : js_true); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, done))); MIR_append_insn(ctx, fn, not_equal); mir_load_imm(ctx, fn, result, invert ? js_true : js_false); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, done))); } void mir_emit_strict_tagged_equality( MIR_context_t ctx, MIR_item_t fn, MIR_reg_t left, MIR_reg_t right, MIR_reg_t result, MIR_reg_t scratch, bool invert, bool inline_long_strings, MIR_label_t slow, MIR_label_t done) { MIR_label_t left_number = MIR_new_label(ctx); MIR_label_t strings = MIR_new_label(ctx); MIR_label_t equal = MIR_new_label(ctx); MIR_label_t not_equal = MIR_new_label(ctx); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_UBLE, MIR_new_label_op(ctx, left_number), MIR_new_reg_op(ctx, left), MIR_new_uint_op(ctx, NANBOX_PREFIX))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_UBLE, MIR_new_label_op(ctx, not_equal), MIR_new_reg_op(ctx, right), MIR_new_uint_op(ctx, NANBOX_PREFIX))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, equal), MIR_new_reg_op(ctx, left), MIR_new_reg_op(ctx, right))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_XOR, MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, left), MIR_new_reg_op(ctx, right))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_AND, MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, scratch), MIR_new_uint_op(ctx, NANBOX_TYPE_MASK << NANBOX_TYPE_SHIFT))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, not_equal), MIR_new_reg_op(ctx, scratch), MIR_new_int_op(ctx, 0))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_URSH, MIR_new_reg_op(ctx, scratch), MIR_new_reg_op(ctx, left), MIR_new_uint_op(ctx, NANBOX_TYPE_SHIFT))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, strings), MIR_new_reg_op(ctx, scratch), MIR_new_uint_op(ctx, JIT_STR_TAG))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, scratch), MIR_new_uint_op(ctx, (NANBOX_PREFIX >> NANBOX_TYPE_SHIFT) | (uint64_t)kTypeBigInt))); MIR_append_insn(ctx, fn, not_equal); mir_load_imm(ctx, fn, result, invert ? js_true : js_false); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, done))); MIR_append_insn(ctx, fn, left_number); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_UBGT, MIR_new_label_op(ctx, not_equal), MIR_new_reg_op(ctx, right), MIR_new_uint_op(ctx, NANBOX_PREFIX))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, slow))); MIR_append_insn(ctx, fn, strings); mir_emit_string_equality(ctx, fn, left, right, scratch, inline_long_strings, equal, not_equal, slow); MIR_append_insn(ctx, fn, equal); mir_load_imm(ctx, fn, result, invert ? js_false : js_true); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, done))); }