Something went wrong. Try again.
javascript for 🐜's, a tiny runtime with big ambitions antjs.org
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030#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)));}