Something went wrong. Try again.
MIRROR: javascript for 🐜's, a tiny runtime with big ambitions ant.themackabu.dev
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274#include "jit_internal.h"#include "silver/feedback.h"
// TODO: opcode.hstatic bool jit_op_inline_side_effect(sv_op_t op) { switch (op) { case OP_PUT_FIELD: case OP_LOAD_STABLE_BUILTIN: case OP_CALL: case OP_CALL_METHOD: case OP_CALL_STABLE_BUILTIN: case OP_TAIL_CALL: case OP_TAIL_CALL_METHOD: return true; default: return false; }}
// TODO: opcode.hstatic bool jit_op_inline_restarts_callee_on_guard_failure(sv_op_t op) { switch (op) { case OP_CHECK_CTOR: case OP_GET_GLOBAL: case OP_GET_ELEM: case OP_BAND: case OP_BOR: case OP_BXOR: case OP_SHL: case OP_SHR: case OP_USHR: return true; default: return false; }}
// TODO: opcode.hstatic bool jit_op_inline_after_effect(sv_op_t op) { switch (op) { case OP_RETURN: case OP_RETURN_UNDEF: case OP_POP: case OP_DUP: case OP_DUP2: case OP_NIP: case OP_INSERT2: case OP_INSERT3: case OP_GET_LOCAL: case OP_GET_LOCAL8: case OP_PUT_LOCAL: case OP_PUT_LOCAL8: case OP_SET_LOCAL: case OP_SET_LOCAL8: case OP_GET_ARG: case OP_PUT_ARG: case OP_SET_ARG: case OP_GET_FIELD: case OP_GET_FIELD2: case OP_GET_FIELD_OPT: case OP_GET_LENGTH: case OP_CONST: case OP_CONST8: case OP_CONST_I8: case OP_UNDEF: case OP_NULL: case OP_TRUE: case OP_FALSE: case OP_THIS: case OP_IS_UNDEF: case OP_IS_NULL: case OP_IS_UNDEF_OR_NULL: case OP_SEQ: case OP_SNE: case OP_NOP: case OP_LINE_NUM: case OP_COL_NUM: case OP_LABEL: return true; default: return false; }}
static bool jit_inline_receiver_is_this(const sv_func_t *f, int off) { uint8_t marks[JIT_INLINE_MAX_BYTECODE + 1]; int sp = 0; for (int o = 0; o < off;) { const uint8_t *ip = f->code + o; sv_op_t op = (sv_op_t)*ip; if (sv_op_flags[op] & (SV_OPF_JIT_BRANCH32 | SV_OPF_JIT_BRANCH8)) return false; int pops, pushes; if (!sv_op_stack_effect(f, ip, &pops, &pushes) || pops > sp || sp - pops + pushes > JIT_INLINE_MAX_BYTECODE) return false; switch (op) { case OP_THIS: marks[sp++] = 1; break; case OP_DUP: marks[sp] = marks[sp - 1]; sp++; break; case OP_INSERT2: { uint8_t val = marks[sp - 1]; marks[sp - 1] = marks[sp - 2]; marks[sp - 2] = val; marks[sp++] = val; break; } default: sp -= pops; memset(marks + sp, 0, (size_t)pushes); sp += pushes; break; } o += sv_op_size[op]; } return sp >= 2 && marks[sp - 2];}
static bool jit_inline_note_instruction(const sv_func_t *f, int off, int size, uint8_t *boundaries) { boundaries[off] |= 1; const uint8_t *ip = f->code + off; uint16_t flags = sv_op_flags[*ip]; if (!(flags & (SV_OPF_JIT_BRANCH32 | SV_OPF_JIT_BRANCH8))) return true;
int delta = (flags & SV_OPF_JIT_BRANCH32) ? sv_get_i32(ip + 1) : sv_get_i8(ip + 1); int next = off + size; if (delta < 0 || delta >= f->code_len - next) return false; boundaries[next + delta] |= 2; return true;}
static bool jit_inline_targets_aligned(const uint8_t *boundaries, int len) { for (int off = 0; off < len; off++) if (boundaries[off] == 2) return false; return true;}
bool jit_inlineable(sv_func_t *f) { if (!f) return false; if (jit_compile_owner && f->realm != jit_compile_owner->realm) return false; if (f->is_async || f->is_generator) return false; // derived ctors need the super-rebound `this` returned from RETURN/ // RETURN_UNDEF (see the main emission); the inline path doesn't model it if (f->is_derived_ctor) return false; if (!f->code || f->code_len <= 0 || f->code_len > JIT_INLINE_MAX_BYTECODE) return false;
uint8_t boundaries[JIT_INLINE_MAX_BYTECODE] = {0}; uint8_t *ip = f->code; uint8_t *end = f->code + f->code_len; bool seen_effect = false;
while (ip < end) { sv_op_t op = (sv_op_t)*ip; if (op >= OP__COUNT) return false; int sz = sv_op_size[op]; if (sz == 0 || sz > end - ip) return false;
uint16_t flags = sv_op_flags[op]; if ((flags & SV_OPF_JIT_INLINEABLE) == 0) return false; if (!jit_inline_note_instruction(f, (int)(ip - f->code), sz, boundaries)) return false;
if (op == OP_PUT_ARG || op == OP_SET_ARG) { uint16_t idx = sv_get_u16(ip + 1); if (idx >= f->param_count || idx >= SV_JIT_ARGS_BUF_CAP) return false; } if ((flags & SV_OPF_JIT_INLINE_ARGC) != 0 && sv_get_u16(ip + 1) > SV_JIT_ARGS_BUF_CAP) return false; if (op == OP_CALL_STABLE_BUILTIN && sv_get_u16(ip + 2) > SV_JIT_ARGS_BUF_CAP) return false;
// OP_SPECIAL_OBJ(SV_SPECIAL_ARGUMENTS) materializes `arguments`. keep these functions on // the interpreter until JIT routes a real per-call activation/object // with matching lifetime and semantics. if (op == OP_SPECIAL_OBJ && sv_get_u8(ip + 1) == SV_SPECIAL_ARGUMENTS) return false;
// These guards branch to the shared slow path, which invokes the whole // callee. Never emit them after an operation that may already have had an // observable effect. if (seen_effect && jit_op_inline_restarts_callee_on_guard_failure(op)) return false;
if (seen_effect) if (op != OP_JMP && !jit_op_inline_after_effect(op) && !jit_op_inline_side_effect(op)) return false;
if (jit_op_inline_side_effect(op)) seen_effect = true; ip += sz; }
return jit_inline_targets_aligned(boundaries, f->code_len);}
// Track params, locals and stack slots: uint8_t 0 = no alias, 1 = may alias a reused object.// Join predecessor states with |=; depths < 0 marks unreachable instructions.// Backedges are rejected, so bytecode order is topological and one pass reaches a fixpoint.// Reject escaping aliases and unmodelled opcodes (default: return false) conservatively.// Reuse requires effect-free bodies: inline read helpers bail before getters/proxies run.// Restarting the callee with fresh objects then preserves any observable object identity.static bool jit_analyze_empty_object_reuse(sv_func_t *f) { if (f->obj_site_count == 0) return false; int local_base = f->param_count; int stack_base = local_base + f->max_locals; int slots = stack_base + f->max_stack; if (slots <= 0 || slots > JIT_INLINE_MAX_BYTECODE || !f->code || f->code_len <= 0 || f->code_len > JIT_INLINE_MAX_BYTECODE) return false;
uint8_t boundaries[JIT_INLINE_MAX_BYTECODE] = {0}; for (int off = 0; off < f->code_len;) { sv_op_t op = (sv_op_t)f->code[off]; if (op >= OP__COUNT) return false; int size = sv_op_size[op]; if (!size || off + size > f->code_len || jit_op_inline_side_effect(op)) return false; if (!jit_inline_note_instruction(f, off, size, boundaries)) return false; off += size; } if (!jit_inline_targets_aligned(boundaries, f->code_len)) return false; uint8_t states[(f->code_len + 1) * slots]; int depths[f->code_len + 1]; memset(states, 0, sizeof(states)); for (int i = 0; i <= f->code_len; i++) depths[i] = -1; depths[0] = 0; bool has_object = false; for (int off = 0; off < f->code_len;) { const uint8_t *ip = f->code + off; sv_op_t op = (sv_op_t)*ip; int size = sv_op_size[op]; int sp = depths[off]; if (sp < 0) { off += size; continue; } int pops, pushes; if (!sv_op_stack_effect(f, ip, &pops, &pushes) || sp < pops || sp - pops + pushes > f->max_stack) return false; uint8_t *state = states + off * slots; uint8_t *stack = state + stack_base; int idx; bool terminal = false; switch (op) { case OP_OBJECT: { sv_obj_site_cache_t *site = sv_obj_site_for_offset(f, (uint32_t)off); if (!site || site->key_count) return false; stack[sp] = 1; has_object = true; break; } case OP_GET_ARG: idx = sv_get_u16(ip + 1); if (idx >= f->param_count) return false; stack[sp] = state[idx]; break; case OP_PUT_ARG: case OP_SET_ARG: idx = sv_get_u16(ip + 1); if (idx >= f->param_count) return false; state[idx] = stack[sp - 1]; break; case OP_GET_LOCAL: case OP_GET_LOCAL8: idx = op == OP_GET_LOCAL ? sv_get_u16(ip + 1) : ip[1]; if (idx >= f->max_locals) return false; stack[sp] = state[local_base + idx]; break; case OP_SET_LOCAL_UNDEF: idx = sv_get_u16(ip + 1); if (idx >= f->max_locals) return false; state[local_base + idx] = 0; break; case OP_PUT_LOCAL: case OP_SET_LOCAL: case OP_PUT_LOCAL8: case OP_SET_LOCAL8: idx = op == OP_PUT_LOCAL || op == OP_SET_LOCAL ? sv_get_u16(ip + 1) : ip[1]; if (idx >= f->max_locals) return false; state[local_base + idx] = stack[sp - 1]; break; case OP_DUP: stack[sp] = stack[sp - 1]; break; case OP_NIP: stack[sp - 2] = stack[sp - 1]; break; case OP_GET_FIELD: case OP_GET_FIELD_OPT: case OP_GET_LENGTH: case OP_NOT: case OP_IS_UNDEF: case OP_IS_NULL: case OP_IS_UNDEF_OR_NULL: case OP_IS_PRIMITIVE_TYPE: stack[sp - 1] = 0; break; case OP_GET_FIELD2: stack[sp] = 0; break; case OP_RETURN: if (stack[sp - 1]) return false; terminal = true; break; case OP_RETURN_UNDEF: terminal = true; break; case OP_JMP: case OP_JMP_TRUE: case OP_JMP_FALSE: case OP_JMP_TRUE8: case OP_JMP_FALSE8: case OP_JMP_TRUE_PEEK: case OP_JMP_FALSE_PEEK: case OP_JMP_NOT_NULLISH: case OP_POP: case OP_NOP: case OP_LINE_NUM: case OP_COL_NUM: case OP_LABEL: break; case OP_CONST: case OP_CONST8: case OP_CONST_I8: case OP_UNDEF: case OP_NULL: case OP_TRUE: case OP_FALSE: case OP_THIS: case OP_GET_UPVAL: case OP_GET_GLOBAL: stack[sp] = 0; break; default: return false; } sp += pushes - pops; int next = off + size; int successors[2] = {next, -1}; uint16_t flags = sv_op_flags[op]; if (flags & (SV_OPF_JIT_BRANCH32 | SV_OPF_JIT_BRANCH8)) { int target = next + ((flags & SV_OPF_JIT_BRANCH32) ? sv_get_i32(ip + 1) : sv_get_i8(ip + 1)); if (target <= off || target >= f->code_len || !(boundaries[target] & 1)) return false; if (op == OP_JMP) successors[0] = target; else successors[1] = target; } for (int i = 0; !terminal && i < 2; i++) { int target = successors[i]; if (target < 0) continue; if (depths[target] >= 0 && depths[target] != sp) return false; depths[target] = sp; uint8_t *dest = states + target * slots; for (int j = 0; j < slots; j++) dest[j] |= state[j]; } off = next; } return has_object;}
static bool jit_inline_can_reuse_empty_objects(sv_func_t *f) { if (!f->jit_inline_reuse_checked) { f->jit_inline_reuse_empty = jit_analyze_empty_object_reuse(f); f->jit_inline_reuse_checked = true; } return f->jit_inline_reuse_empty;}
static void mir_emit_inline_read_guard( MIR_context_t ctx, MIR_item_t fn, MIR_reg_t value, MIR_reg_t result, MIR_reg_t scratch, MIR_label_t slow, MIR_label_t join, MIR_label_t ok) { MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, value), MIR_new_uint_op(ctx, SV_JIT_BAILOUT))); 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_BNE, MIR_new_label_op(ctx, ok), MIR_new_reg_op(ctx, scratch), MIR_new_uint_op(ctx, JIT_ERR_TAG))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, result), MIR_new_reg_op(ctx, value))); MIR_append_insn(ctx, fn, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, join)));}
static MIR_label_t inl_label_for_offset(MIR_context_t ctx, inl_label_map_t *lm, int bc_off, int sp) { for (int i = 0; i < lm->count; i++) if (lm->entries[i].bc_off == bc_off) { if (lm->entries[i].sp < 0) lm->entries[i].sp = sp; return lm->entries[i].label; } if (lm->count >= INL_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->entries[lm->count].sp = sp; lm->count++; return lbl;}
static MIR_label_t inl_label_lookup(inl_label_map_t *lm, int bc_off, int *out_sp) { for (int i = 0; i < lm->count; i++) if (lm->entries[i].bc_off == bc_off) { if (out_sp) *out_sp = lm->entries[i].sp; return lm->entries[i].label; } return NULL;}
static bool jit_starts_numeric_const(sv_func_t *func, uint8_t *ip, uint8_t *end, int *out_size) { if (!func || !ip || ip >= end) return false; sv_op_t op = (sv_op_t)*ip; int sz = sv_op_size[op]; if (sz == 0 || ip + sz > end) return false; if (out_size) *out_size = sz;
switch (op) { case OP_CONST_I8: return true; case OP_CONST: { uint32_t idx = sv_get_u32(ip + 1); return idx < (uint32_t)func->const_count && vtype(func->constants[idx]) == kTypeNumber; } case OP_CONST8: { uint8_t idx = sv_get_u8(ip + 1); return (uint32_t)idx < (uint32_t)func->const_count && vtype(func->constants[idx]) == kTypeNumber; } default: return false; }}
bool jit_has_immediate_numeric_local_init(sv_func_t *func, uint8_t *ip, uint8_t *end, uint16_t local_idx) { int const_size = 0; if (!jit_starts_numeric_const(func, ip, end, &const_size)) return false; uint8_t *put_ip = ip + const_size; if (put_ip >= end) return false;
sv_op_t put_op = (sv_op_t)*put_ip; int put_size = sv_op_size[put_op]; if (put_size == 0 || put_ip + put_size > end) return false;
if (put_op == OP_PUT_LOCAL || put_op == OP_SET_LOCAL) return sv_get_u16(put_ip + 1) == local_idx; if (put_op == OP_PUT_LOCAL8 || put_op == OP_SET_LOCAL8) return local_idx <= UINT8_MAX && sv_get_u8(put_ip + 1) == (uint8_t)local_idx; return false;}
static bool jit_inline_reader_leaf(sv_func_t *func) { if (!jit_inlineable(func) || func->code_len > 64 || func->max_locals > 16 || func->max_stack > 16 || func->gc_const_slot_count || func->obj_site_count) return false; for (const uint8_t *ip = func->code, *end = ip + func->code_len; ip < end; ip += sv_op_size[*ip]) { switch (*ip) { case OP_THIS: case OP_GET_ARG: case OP_GET_UPVAL: case OP_GET_LOCAL: case OP_GET_LOCAL8: case OP_PUT_LOCAL: case OP_PUT_LOCAL8: case OP_SET_LOCAL: case OP_SET_LOCAL8: case OP_GET_FIELD: case OP_GET_FIELD2: case OP_GET_FIELD_OPT: case OP_GET_LENGTH: case OP_GET_ELEM: case OP_GET_GLOBAL: case OP_CONST: case OP_CONST8: case OP_CONST_I8: case OP_UNDEF: case OP_NULL: case OP_TRUE: case OP_FALSE: case OP_DUP: case OP_POP: case OP_NIP: case OP_SEQ: case OP_SNE: case OP_IS_UNDEF: case OP_IS_NULL: case OP_IS_UNDEF_OR_NULL: case OP_JMP: case OP_JMP_TRUE: case OP_JMP_FALSE: case OP_JMP_TRUE8: case OP_JMP_FALSE8: case OP_JMP_TRUE_PEEK: case OP_JMP_FALSE_PEEK: case OP_JMP_NOT_NULLISH: case OP_RETURN: case OP_RETURN_UNDEF: case OP_NOP: case OP_LINE_NUM: case OP_COL_NUM: case OP_LABEL: break; case OP_EQ: case OP_NE: { const uint8_t *feedback = sv_func_type_feedback(func); uint8_t seen = feedback ? feedback[ip - func->code] : 0; if (!sv_tfb_specialization_ready(seen) || (seen & SV_TFB_CLASS_MASK) != SV_TFB_NUM) return false; break; } default: return false; } } return true;}
void jit_emit_inline_body( MIR_context_t ctx, MIR_item_t jit_func, ant_t *js, sv_func_t *callee, MIR_reg_t *arg_regs, int caller_argc, const uint8_t *arg_num, const MIR_reg_t *arg_d, MIR_reg_t result, MIR_label_t slow, MIR_label_t join, MIR_reg_t r_bool, MIR_reg_t *p_d_slot, int id, int *p_reg_site, MIR_reg_t r_inl_closure, MIR_reg_t r_inl_this, MIR_reg_t r_inl_new_target, MIR_reg_t r_inl_super, MIR_reg_t r_vm, MIR_reg_t r_js, MIR_reg_t r_ic_epoch, MIR_item_t helper2_proto, MIR_item_t imp_seq, MIR_item_t imp_sne, MIR_item_t imp_eq, MIR_item_t imp_ne, MIR_item_t gf_proto, MIR_item_t imp_get_field_inline, MIR_item_t special_obj_proto, MIR_item_t imp_special_obj, const jit_inline_ext_t *ext ) { sv_code_unit_make_immortal(callee); if (!callee->is_strict && !callee->is_arrow) { bool uses_this = false; for (uint8_t *ip = callee->code; ip < callee->code + callee->code_len; ip += sv_op_size[*ip]) if (*ip == OP_THIS) { uses_this = true; break; } if (uses_this) { MIR_label_t receiver_ready = MIR_new_label(ctx); const uint8_t object_types[] = { kTypeObject, kTypeArray, kTypeFunction, kTypeBuiltin, kTypePromise, kTypeGenerator, }; MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_UBLE, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, r_inl_this), MIR_new_uint_op(ctx, NANBOX_PREFIX))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_URSH, MIR_new_reg_op(ctx, r_bool), MIR_new_reg_op(ctx, r_inl_this), MIR_new_uint_op(ctx, NANBOX_TYPE_SHIFT))); for (size_t i = 0; i < sizeof(object_types); i++) MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, receiver_ready), MIR_new_reg_op(ctx, r_bool), MIR_new_uint_op(ctx, (NANBOX_PREFIX >> NANBOX_TYPE_SHIFT) | object_types[i]))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, slow))); MIR_append_insn(ctx, jit_func, receiver_ready); } } int reader_budget = 128; int inl_max_stack = callee->max_stack + JIT_VSTACK_SLACK; MIR_reg_t inl_vs[inl_max_stack]; for (int i = 0; i < inl_max_stack; i++) { char rn[32]; snprintf(rn, sizeof(rn), "inl%d_s%d", id, i); inl_vs[i] = MIR_new_func_reg(ctx, jit_func->u.func, MIR_JSVAL, rn); } int isp = 0;
MIR_reg_t inl_d[inl_max_stack]; uint8_t inl_num[inl_max_stack]; for (int i = 0; i < inl_max_stack; i++) { char rn[32]; snprintf(rn, sizeof(rn), "inl%d_dt%d", id, i); inl_d[i] = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, rn); inl_num[i] = 0; } uint8_t inl_known[inl_max_stack]; memset(inl_known, 0, sizeof(inl_known));
#define INL_ENSURE_D_SLOT() \ do { \ if (!*p_d_slot) { \ *p_d_slot = MIR_new_func_reg(ctx, jit_func->u.func, \ MIR_T_I64, "d_slot_inl"); \ MIR_append_insn(ctx, jit_func, \ MIR_new_insn(ctx, MIR_ALLOCA, \ MIR_new_reg_op(ctx, *p_d_slot), \ MIR_new_uint_op(ctx, 8))); \ } \ } while (0)#define INL_FLUSH_SLOT(k) \ do { \ int _fk = (k); \ if (inl_num[_fk]) { \ INL_ENSURE_D_SLOT(); \ mir_d_to_i64(ctx, jit_func, inl_vs[_fk], inl_d[_fk], *p_d_slot); \ inl_num[_fk] = 0; \ } \ } while (0)#define INL_FLUSH_ALL() \ do { \ for (int _fa = 0; _fa < isp; _fa++) \ INL_FLUSH_SLOT(_fa); \ } while (0)
int inl_n_args = callee->param_count < SV_JIT_ARGS_BUF_CAP ? callee->param_count : SV_JIT_ARGS_BUF_CAP; MIR_reg_t inl_params[inl_n_args > 0 ? inl_n_args : 1]; memset(inl_params, 0, sizeof(inl_params)); for (uint8_t *scan = callee->code; scan < callee->code + callee->code_len; scan += sv_op_size[*scan]) { if (*scan != OP_PUT_ARG && *scan != OP_SET_ARG) continue; uint16_t idx = sv_get_u16(scan + 1); ANT_ASSERT(idx < inl_n_args, "invalid inline parameter index"); if (inl_params[idx]) continue; char rn[32]; snprintf(rn, sizeof(rn), "inl%d_arg%u", id, (unsigned)idx); inl_params[idx] = MIR_new_func_reg(ctx, jit_func->u.func, MIR_JSVAL, rn); if (idx >= caller_argc) { mir_load_imm(ctx, jit_func, inl_params[idx], mkval(kTypeUndefined, 0)); } else if (arg_num && arg_num[idx]) { INL_ENSURE_D_SLOT(); mir_d_to_i64(ctx, jit_func, inl_params[idx], arg_d[idx], *p_d_slot); } else MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, inl_params[idx]), MIR_new_reg_op(ctx, arg_regs[idx]))); }
int inl_n_locals = callee->max_locals; MIR_reg_t inl_locals[inl_n_locals > 0 ? inl_n_locals : 1]; for (int i = 0; i < inl_n_locals; i++) { char rn[32]; snprintf(rn, sizeof(rn), "inl%d_l%d", id, i); inl_locals[i] = MIR_new_func_reg(ctx, jit_func->u.func, MIR_JSVAL, rn); mir_load_imm(ctx, jit_func, inl_locals[i], mkval(kTypeUndefined, 0)); }
MIR_reg_t inl_undef = 0; { const uint8_t *scan = callee->code; const uint8_t *scan_end = callee->code + callee->code_len; while (scan < scan_end) { sv_op_t sop = (sv_op_t)*scan; if (sop == OP_CALL || sop == OP_CALL_METHOD || sop == OP_TAIL_CALL || sop == OP_TAIL_CALL_METHOD) { char rn[32]; snprintf(rn, sizeof(rn), "inl%d_undef", id); inl_undef = MIR_new_func_reg(ctx, jit_func->u.func, MIR_JSVAL, rn); mir_load_imm(ctx, jit_func, inl_undef, mkval(kTypeUndefined, 0)); break; } int ssz = sv_op_size[sop]; if (ssz <= 0) break; scan += ssz; } }
bool reuse_empty_objects = jit_inline_can_reuse_empty_objects(callee); int inl_arith = 0; int inl_upval_n = 0;
inl_label_map_t inl_lm = {.count = 0};
uint8_t *code_base = callee->code; uint8_t *ip = callee->code; uint8_t *end = callee->code + callee->code_len; bool seen_effect = false; int *inl_depth = sv_func_stack_depth_map(callee);
jit_call_tagging_t *tagging = jit_call_tagging; if (tagging && tagging->inl.depth++ < SV_JIT_INLINE_FRAMES_MAX) tagging->inl.frames[tagging->inl.depth - 1] = (sv_jit_frame_t){ callee, 0 }; int tag_depth = tagging ? tagging->inl.depth : 0;
while (ip < end) { sv_op_t op = (sv_op_t)*ip; int sz = sv_op_size[op]; int inl_bc_off = (int)(ip - code_base); if (tagging && tag_depth <= SV_JIT_INLINE_FRAMES_MAX) tagging->inl.frames[tag_depth - 1].bc_off = inl_bc_off;
if (inl_depth && inl_depth[inl_bc_off] < 0) { ip += sz; continue; }
int label_sp = -1; MIR_label_t target_lbl = inl_label_lookup(&inl_lm, inl_bc_off, &label_sp); if (target_lbl) { INL_FLUSH_ALL(); MIR_append_insn(ctx, jit_func, target_lbl); if (inl_depth) isp = inl_depth[inl_bc_off]; else if (label_sp >= 0) isp = label_sp; memset(inl_num, 0, (size_t)inl_max_stack); }
switch (op) { case OP_GET_ARG: { uint16_t idx = sv_get_u16(ip + 1); if (idx < inl_n_args && inl_params[idx]) { inl_num[isp] = 0; MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, inl_vs[isp++]), MIR_new_reg_op(ctx, inl_params[idx]))); break; } if ((int)idx < caller_argc && arg_num && arg_num[idx]) { MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_DMOV, MIR_new_reg_op(ctx, inl_d[isp]), MIR_new_reg_op(ctx, arg_d[idx]))); inl_num[isp++] = 1; break; } inl_num[isp] = 0; MIR_reg_t dst = inl_vs[isp++]; if ((int)idx < caller_argc) MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, dst), MIR_new_reg_op(ctx, arg_regs[idx]))); else mir_load_imm(ctx, jit_func, dst, mkval(kTypeUndefined, 0)); break; }
case OP_PUT_ARG: case OP_SET_ARG: { INL_FLUSH_SLOT(isp - 1); uint16_t idx = sv_get_u16(ip + 1); ANT_ASSERT(idx < inl_n_args && inl_params[idx], "invalid inline parameter store"); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, inl_params[idx]), MIR_new_reg_op(ctx, inl_vs[isp - 1]))); if (op == OP_PUT_ARG) inl_num[--isp] = 0; break; }
case OP_OBJECT: { INL_FLUSH_ALL(); sv_obj_site_cache_t *site = sv_obj_site_for_offset(callee, (uint32_t)inl_bc_off); bool reuse_site = reuse_empty_objects && site && site->key_count == 0; MIR_reg_t dst = inl_vs[isp++]; inl_num[isp - 1] = 0; MIR_label_t allocated = MIR_new_label(ctx); if (reuse_site) { mir_load_const_slot(ctx, jit_func, dst, &site->literal_template); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_URSH, MIR_new_reg_op(ctx, r_bool), MIR_new_reg_op(ctx, dst), MIR_new_uint_op(ctx, NANBOX_TYPE_SHIFT))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, allocated), MIR_new_reg_op(ctx, r_bool), MIR_new_uint_op(ctx, NANBOX_TOBJ_TAG))); } MIR_append_insn(ctx, jit_func, MIR_new_call_insn(ctx, 7, MIR_new_ref_op(ctx, ext->object_proto), MIR_new_ref_op(ctx, ext->imp_object), MIR_new_reg_op(ctx, dst), MIR_new_reg_op(ctx, r_vm), MIR_new_reg_op(ctx, r_js), MIR_new_uint_op(ctx, (uintptr_t)callee), MIR_new_uint_op(ctx, (uintptr_t)site))); MIR_label_t ok = MIR_new_label(ctx); mir_emit_inline_read_guard(ctx, jit_func, dst, result, r_bool, slow, join, ok); MIR_append_insn(ctx, jit_func, ok); if (reuse_site) { char rn[40]; snprintf(rn, sizeof(rn), "inl%d_empty_site%d", id, inl_bc_off); MIR_reg_t slot = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, rn); mir_load_imm(ctx, jit_func, slot, (uintptr_t)&site->literal_template); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_mem_op(ctx, MIR_JSVAL, 0, slot, 0, 1), MIR_new_reg_op(ctx, dst))); } MIR_append_insn(ctx, jit_func, allocated); break; }
case OP_CONST_I8: { double d = (double)(int8_t)sv_get_i8(ip + 1); union { double d; uint64_t u; } u = {d}; mir_load_imm(ctx, jit_func, inl_vs[isp++], u.u); break; } case OP_CONST: { uint32_t idx = sv_get_u32(ip + 1); ANT_ASSERT(idx < (uint32_t)callee->const_count, "invalid inline constant index"); ant_value_t cv = callee->constants[idx]; MIR_reg_t dst = inl_vs[isp++]; if (jit_const_is_heap(cv)) mir_load_const_slot(ctx, jit_func, dst, &callee->constants[idx]); else mir_load_imm(ctx, jit_func, dst, cv); break; } case OP_CONST8: { uint8_t idx = sv_get_u8(ip + 1); ANT_ASSERT((uint32_t)idx < (uint32_t)callee->const_count, "invalid inline constant index"); ant_value_t cv = callee->constants[idx]; MIR_reg_t dst = inl_vs[isp++]; if (jit_const_is_heap(cv)) mir_load_const_slot(ctx, jit_func, dst, &callee->constants[idx]); else mir_load_imm(ctx, jit_func, dst, cv); break; } case OP_UNDEF: mir_load_imm(ctx, jit_func, inl_vs[isp++], mkval(kTypeUndefined, 0)); break; case OP_NULL: mir_load_imm(ctx, jit_func, inl_vs[isp++], mkval(kTypeNull, 0)); break; case OP_TRUE: mir_load_imm(ctx, jit_func, inl_vs[isp++], js_true); break; case OP_FALSE: mir_load_imm(ctx, jit_func, inl_vs[isp++], js_false); break;
case OP_THIS: { MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, inl_vs[isp++]), MIR_new_reg_op(ctx, r_inl_this))); break; }
case OP_CHECK_CTOR: MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, r_inl_new_target), MIR_new_uint_op(ctx, mkval(kTypeUndefined, 0)))); break;
case OP_SET_LOCAL_UNDEF: { uint16_t idx = sv_get_u16(ip + 1); ANT_ASSERT(idx < inl_n_locals, "invalid inline local initialization"); mir_load_imm(ctx, jit_func, inl_locals[idx], SV_TDZ); break; }
case OP_GET_LOCAL: { uint16_t idx = sv_get_u16(ip + 1); ANT_ASSERT((uint32_t)idx < (uint32_t)inl_n_locals, "invalid inline local index"); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, inl_vs[isp++]), MIR_new_reg_op(ctx, inl_locals[idx]))); break; } case OP_GET_LOCAL8: { uint8_t idx = sv_get_u8(ip + 1); ANT_ASSERT((uint32_t)idx < (uint32_t)inl_n_locals, "invalid inline local index"); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, inl_vs[isp++]), MIR_new_reg_op(ctx, inl_locals[idx]))); break; } case OP_PUT_LOCAL: { INL_FLUSH_SLOT(isp - 1); uint16_t idx = sv_get_u16(ip + 1); ANT_ASSERT((uint32_t)idx < (uint32_t)inl_n_locals, "invalid inline local index"); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, inl_locals[idx]), MIR_new_reg_op(ctx, inl_vs[--isp]))); break; } case OP_PUT_LOCAL8: { INL_FLUSH_SLOT(isp - 1); uint8_t idx = sv_get_u8(ip + 1); ANT_ASSERT((uint32_t)idx < (uint32_t)inl_n_locals, "invalid inline local index"); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, inl_locals[idx]), MIR_new_reg_op(ctx, inl_vs[--isp]))); break; } case OP_SET_LOCAL: { INL_FLUSH_SLOT(isp - 1); uint16_t idx = sv_get_u16(ip + 1); ANT_ASSERT((uint32_t)idx < (uint32_t)inl_n_locals, "invalid inline local index"); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, inl_locals[idx]), MIR_new_reg_op(ctx, inl_vs[isp - 1]))); break; } case OP_SET_LOCAL8: { INL_FLUSH_SLOT(isp - 1); uint8_t idx = sv_get_u8(ip + 1); ANT_ASSERT((uint32_t)idx < (uint32_t)inl_n_locals, "invalid inline local index"); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, inl_locals[idx]), MIR_new_reg_op(ctx, inl_vs[isp - 1]))); break; }
case OP_GET_UPVAL: { ANT_ASSERT(r_inl_closure != 0, "inline upvalue access requires a closure"); uint16_t idx = sv_get_u16(ip + 1); int un = inl_upval_n++; char rn_uvs[32], rn_uv[32], rn_loc[32]; snprintf(rn_uvs, sizeof(rn_uvs), "inl%d_uvs%d", id, un); snprintf(rn_uv, sizeof(rn_uv), "inl%d_uv%d", id, un); snprintf(rn_loc, sizeof(rn_loc), "inl%d_uvl%d", id, un);
MIR_reg_t r_uvs = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, rn_uvs); MIR_reg_t r_uv = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, rn_uv); MIR_reg_t r_loc = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, rn_loc); MIR_reg_t dst = inl_vs[isp++];
MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, r_uvs), MIR_new_mem_op(ctx, MIR_T_P, (MIR_disp_t)offsetof(sv_closure_t, upvalues), r_inl_closure, 0, 1))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, r_uv), MIR_new_mem_op(ctx, MIR_T_P, (MIR_disp_t)((int)idx * (int)sizeof(sv_upvalue_t *)), r_uvs, 0, 1))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, r_loc), MIR_new_mem_op(ctx, MIR_T_P, (MIR_disp_t)offsetof(sv_upvalue_t, location), r_uv, 0, 1))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, dst), MIR_new_mem_op(ctx, MIR_JSVAL, 0, r_loc, 0, 1))); if (jit_upvalue_may_tdz(callee, idx)) MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, slow), MIR_new_reg_op(ctx, dst), MIR_new_uint_op(ctx, SV_TDZ))); if (jit_upvalue_is_builder_target(callee, idx)) mir_emit_branch_if_string_builder(ctx, jit_func, dst, r_bool, slow); break; }
case OP_POP: isp--; inl_num[isp] = 0; break; case OP_DUP: { INL_FLUSH_SLOT(isp - 1); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, inl_vs[isp]), MIR_new_reg_op(ctx, inl_vs[isp - 1]))); inl_known[isp] = inl_known[isp - 1]; isp++; break; } case OP_DUP2: { ANT_ASSERT(isp >= 2, "invalid inline stack depth"); INL_FLUSH_SLOT(isp - 1); INL_FLUSH_SLOT(isp - 2); MIR_reg_t ra = inl_vs[isp - 2]; MIR_reg_t rb = inl_vs[isp - 1]; MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, inl_vs[isp]), MIR_new_reg_op(ctx, ra))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, inl_vs[isp + 1]), MIR_new_reg_op(ctx, rb))); isp += 2; break; }
case OP_NIP: { ANT_ASSERT(isp >= 2, "invalid inline stack depth"); INL_FLUSH_SLOT(isp - 1); inl_num[isp - 2] = 0; MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, inl_vs[isp - 2]), MIR_new_reg_op(ctx, inl_vs[isp - 1]))); isp--; break; }
case OP_INSERT2: { ANT_ASSERT(isp >= 2, "invalid inline stack depth"); INL_FLUSH_ALL(); char tn[32]; snprintf(tn, sizeof(tn), "inl%d_ins2t_%d", id, inl_bc_off); MIR_reg_t r_t = MIR_new_func_reg(ctx, jit_func->u.func, MIR_JSVAL, tn); MIR_reg_t r_a = inl_vs[isp - 1]; MIR_reg_t r_obj = inl_vs[isp - 2]; MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, r_t), MIR_new_reg_op(ctx, r_a))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, inl_vs[isp - 1]), MIR_new_reg_op(ctx, r_obj))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, inl_vs[isp - 2]), MIR_new_reg_op(ctx, r_t))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, inl_vs[isp]), MIR_new_reg_op(ctx, r_t))); isp++; break; } case OP_INSERT3: { ANT_ASSERT(isp >= 3, "invalid inline stack depth"); INL_FLUSH_ALL(); char tn[32]; snprintf(tn, sizeof(tn), "inl%d_ins3t_%d", id, inl_bc_off); MIR_reg_t r_t = MIR_new_func_reg(ctx, jit_func->u.func, MIR_JSVAL, tn); MIR_reg_t r_a = inl_vs[isp - 1]; MIR_reg_t r_prop = inl_vs[isp - 2]; MIR_reg_t r_obj = inl_vs[isp - 3]; MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, r_t), MIR_new_reg_op(ctx, r_a))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, inl_vs[isp - 1]), MIR_new_reg_op(ctx, r_prop))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, inl_vs[isp - 2]), MIR_new_reg_op(ctx, r_obj))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, inl_vs[isp - 3]), MIR_new_reg_op(ctx, r_t))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, inl_vs[isp]), MIR_new_reg_op(ctx, r_t))); isp++; break; }
case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV: case OP_ADD_NUM: case OP_SUB_NUM: case OP_MUL_NUM: case OP_DIV_NUM: { int str_bc_off = (int)(ip - callee->code); uint8_t fb = sv_func_type_feedback(callee) ? sv_func_type_feedback(callee)[str_bc_off] : 0; bool fb_str_only = op == OP_ADD && fb && !(fb & ~SV_TFB_STR); if (fb_str_only) { INL_FLUSH_SLOT(isp - 1); INL_FLUSH_SLOT(isp - 2); MIR_reg_t rr = inl_vs[--isp]; MIR_reg_t rl = inl_vs[--isp]; MIR_reg_t rd = inl_vs[isp++]; inl_num[isp - 1] = 0; mir_emit_string_concat_fastpath( ctx, jit_func, r_js, rl, rr, rd, slow, id, str_bc_off, false); break; }
if (inl_num[isp - 1] && inl_num[isp - 2]) { MIR_insn_code_t dop; switch (op) { case OP_ADD: case OP_ADD_NUM: dop = MIR_DADD; break; case OP_SUB: case OP_SUB_NUM: dop = MIR_DSUB; break; case OP_MUL: case OP_MUL_NUM: dop = MIR_DMUL; break; default: dop = MIR_DDIV; break; } isp -= 2; MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, dop, MIR_new_reg_op(ctx, inl_d[isp]), MIR_new_reg_op(ctx, inl_d[isp]), MIR_new_reg_op(ctx, inl_d[isp + 1]))); inl_num[isp++] = 1; inl_num[isp] = 0; break; } INL_FLUSH_SLOT(isp - 1); INL_FLUSH_SLOT(isp - 2); MIR_reg_t rr = inl_vs[--isp]; MIR_reg_t rl = inl_vs[--isp]; MIR_reg_t rd = inl_vs[isp++]; inl_num[isp - 1] = 0;
if (!(op == OP_ADD_NUM || op == OP_SUB_NUM || op == OP_MUL_NUM || op == OP_DIV_NUM)) { mir_emit_is_num_guard(ctx, jit_func, r_bool, rl, slow); mir_emit_is_num_guard(ctx, jit_func, r_bool, rr, slow); }
if (!*p_d_slot) { *p_d_slot = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, "d_slot_inl"); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_ALLOCA, MIR_new_reg_op(ctx, *p_d_slot), MIR_new_uint_op(ctx, 8))); }
int an = inl_arith++; char d1[32], d2[32], d3[32]; snprintf(d1, sizeof(d1), "inl%d_fd1_%d", id, an); snprintf(d2, sizeof(d2), "inl%d_fd2_%d", id, an); snprintf(d3, sizeof(d3), "inl%d_fd3_%d", id, an); MIR_reg_t fd1 = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, d1); MIR_reg_t fd2 = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, d2); MIR_reg_t fd3 = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, d3);
mir_i64_to_d(ctx, jit_func, fd1, rl, *p_d_slot); mir_i64_to_d(ctx, jit_func, fd2, rr, *p_d_slot);
MIR_insn_code_t mir_op; switch (op) { case OP_ADD: case OP_ADD_NUM: mir_op = MIR_DADD; break; case OP_SUB: case OP_SUB_NUM: mir_op = MIR_DSUB; break; case OP_MUL: case OP_MUL_NUM: mir_op = MIR_DMUL; break; default: mir_op = MIR_DDIV; break; } MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, mir_op, MIR_new_reg_op(ctx, fd3), MIR_new_reg_op(ctx, fd1), MIR_new_reg_op(ctx, fd2))); mir_d_to_i64(ctx, jit_func, rd, fd3, *p_d_slot); break; }
case OP_MOD: { for (int k = isp - 2; k < isp; k++) { if (inl_num[k]) continue; INL_ENSURE_D_SLOT(); mir_emit_is_num_guard(ctx, jit_func, r_bool, inl_vs[k], slow); mir_i64_to_d(ctx, jit_func, inl_d[k], inl_vs[k], *p_d_slot); inl_num[k] = 1; } isp--; mir_emit_mod_numbers(ctx, jit_func, p_reg_site, ext->fmod_proto, ext->imp_fmod, inl_d[isp - 1], inl_d[isp], inl_d[isp - 1]); inl_num[isp] = 0; break; }
case OP_NEG: { if (inl_num[isp - 1]) { MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_DNEG, MIR_new_reg_op(ctx, inl_d[isp - 1]), MIR_new_reg_op(ctx, inl_d[isp - 1]))); break; }
MIR_reg_t rs = inl_vs[isp - 1]; mir_emit_is_num_guard(ctx, jit_func, r_bool, rs, slow);
if (!*p_d_slot) { *p_d_slot = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, "d_slot_inl"); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_ALLOCA, MIR_new_reg_op(ctx, *p_d_slot), MIR_new_uint_op(ctx, 8))); }
int nn = inl_arith++; char nd1[32], nd2[32]; snprintf(nd1, sizeof(nd1), "inl%d_neg1_%d", id, nn); snprintf(nd2, sizeof(nd2), "inl%d_neg2_%d", id, nn); MIR_reg_t fd1 = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, nd1); MIR_reg_t fd2 = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, nd2); mir_i64_to_d(ctx, jit_func, fd1, rs, *p_d_slot); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_DNEG, MIR_new_reg_op(ctx, fd2), MIR_new_reg_op(ctx, fd1))); mir_d_to_i64(ctx, jit_func, rs, fd2, *p_d_slot); break; }
case OP_LT: case OP_LE: case OP_GT: case OP_GE: { if (inl_num[isp - 1] && inl_num[isp - 2]) { MIR_insn_code_t dcmp; switch (op) { case OP_LT: dcmp = MIR_DLT; break; case OP_LE: dcmp = MIR_DLE; break; case OP_GT: dcmp = MIR_DGT; break; default: dcmp = MIR_DGE; break; } isp -= 2; MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, dcmp, MIR_new_reg_op(ctx, r_bool), MIR_new_reg_op(ctx, inl_d[isp]), MIR_new_reg_op(ctx, inl_d[isp + 1]))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_OR, MIR_new_reg_op(ctx, inl_vs[isp]), MIR_new_uint_op(ctx, js_false), MIR_new_reg_op(ctx, r_bool))); inl_num[isp++] = 0; inl_num[isp] = 0; break; } INL_FLUSH_SLOT(isp - 1); INL_FLUSH_SLOT(isp - 2); MIR_reg_t rr = inl_vs[--isp]; MIR_reg_t rl = inl_vs[--isp]; MIR_reg_t rd = inl_vs[isp++]; inl_num[isp - 1] = 0;
mir_emit_is_num_guard(ctx, jit_func, r_bool, rl, slow); mir_emit_is_num_guard(ctx, jit_func, r_bool, rr, slow);
if (!*p_d_slot) { *p_d_slot = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, "d_slot_inl"); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_ALLOCA, MIR_new_reg_op(ctx, *p_d_slot), MIR_new_uint_op(ctx, 8))); }
int cn = inl_arith++; char cd1[32], cd2[32]; snprintf(cd1, sizeof(cd1), "inl%d_cd1_%d", id, cn); snprintf(cd2, sizeof(cd2), "inl%d_cd2_%d", id, cn); MIR_reg_t fd1 = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, cd1); MIR_reg_t fd2 = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, cd2); mir_i64_to_d(ctx, jit_func, fd1, rl, *p_d_slot); mir_i64_to_d(ctx, jit_func, fd2, rr, *p_d_slot);
MIR_insn_code_t cmp_op; const char *cmp_name; switch (op) { case OP_LT: cmp_op = MIR_DLT; cmp_name = "lt"; break; case OP_LE: cmp_op = MIR_DLE; cmp_name = "le"; break; case OP_GT: cmp_op = MIR_DGT; cmp_name = "gt"; break; default: cmp_op = MIR_DGE; cmp_name = "ge"; break; }
char cmp_rn[32]; snprintf(cmp_rn, sizeof(cmp_rn), "inl%d_%s_%d", id, cmp_name, cn); MIR_reg_t r_tmp = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, cmp_rn); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, cmp_op, MIR_new_reg_op(ctx, r_tmp), MIR_new_reg_op(ctx, fd1), MIR_new_reg_op(ctx, fd2))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_OR, MIR_new_reg_op(ctx, rd), MIR_new_uint_op(ctx, js_false), MIR_new_reg_op(ctx, r_tmp))); break; }
case OP_SEQ: case OP_SNE: case OP_EQ: case OP_NE: { uint8_t feedback = sv_func_type_feedback(callee) ? sv_func_type_feedback(callee)[inl_bc_off] : 0; bool reader_coercion = ext->reader_only && (op == OP_EQ || op == OP_NE); if (reader_coercion || (sv_tfb_specialization_ready(feedback) && (feedback & SV_TFB_CLASS_MASK) == SV_TFB_NUM)) { int right_idx = isp - 1; int left_idx = isp - 2; MIR_reg_t rr = inl_vs[right_idx]; MIR_reg_t rl = inl_vs[left_idx]; MIR_label_t eq_slow = MIR_new_label(ctx); MIR_label_t eq_done = MIR_new_label(ctx); INL_ENSURE_D_SLOT(); if (!inl_num[left_idx]) { mir_emit_is_num_guard( ctx, jit_func, r_bool, rl, eq_slow); mir_i64_to_d( ctx, jit_func, inl_d[left_idx], rl, *p_d_slot); } if (!inl_num[right_idx]) { mir_emit_is_num_guard( ctx, jit_func, r_bool, rr, eq_slow); mir_i64_to_d( ctx, jit_func, inl_d[right_idx], rr, *p_d_slot); } isp -= 2; MIR_reg_t rd = inl_vs[isp++]; mir_emit_numeric_equality( ctx, jit_func, inl_d[left_idx], inl_d[right_idx], rd, r_bool, op == OP_NE || op == OP_SNE); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, eq_done))); MIR_append_insn(ctx, jit_func, eq_slow); if (reader_coercion) { MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, slow))); } else { MIR_item_t helper = op == OP_SEQ ? imp_seq : op == OP_EQ ? imp_eq : op == OP_NE ? imp_ne : imp_sne; if (inl_num[left_idx]) mir_d_to_i64( ctx, jit_func, rl, inl_d[left_idx], *p_d_slot); if (inl_num[right_idx]) mir_d_to_i64( ctx, jit_func, rr, inl_d[right_idx], *p_d_slot); mir_call_helper2(ctx, jit_func, rd, helper2_proto, helper, r_vm, r_js, rl, rr); } MIR_append_insn(ctx, jit_func, eq_done); inl_num[isp - 1] = 0; inl_num[isp] = 0; } else { INL_FLUSH_SLOT(isp - 1); INL_FLUSH_SLOT(isp - 2); MIR_reg_t rr = inl_vs[--isp]; MIR_reg_t rl = inl_vs[--isp]; MIR_reg_t rd = inl_vs[isp++]; MIR_item_t helper = op == OP_SEQ ? imp_seq : op == OP_EQ ? imp_eq : op == OP_NE ? imp_ne : imp_sne; if (op == OP_SEQ || op == OP_SNE) { MIR_label_t eq_slow = MIR_new_label(ctx); MIR_label_t eq_done = MIR_new_label(ctx); mir_emit_strict_tagged_equality( ctx, jit_func, rl, rr, rd, r_bool, op == OP_SNE, (feedback & SV_TFB_STR) != 0, eq_slow, eq_done); MIR_append_insn(ctx, jit_func, eq_slow); mir_call_helper2(ctx, jit_func, rd, helper2_proto, helper, r_vm, r_js, rl, rr); MIR_append_insn(ctx, jit_func, eq_done); } else { mir_call_helper2(ctx, jit_func, rd, helper2_proto, helper, r_vm, r_js, rl, rr); } } break; }
case OP_NOT: { INL_FLUSH_SLOT(isp - 1); MIR_reg_t value = inl_vs[isp - 1]; MIR_label_t falsy = MIR_new_label(ctx); MIR_label_t done = MIR_new_label(ctx); mir_emit_truthy_branch(ctx, jit_func, value, r_bool, r_js, ext->truthy_proto, ext->imp_is_truthy, true, falsy); mir_load_imm(ctx, jit_func, value, js_false); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, done))); MIR_append_insn(ctx, jit_func, falsy); mir_load_imm(ctx, jit_func, value, js_true); MIR_append_insn(ctx, jit_func, done); break; }
case OP_IS_UNDEF: case OP_IS_NULL: { INL_FLUSH_SLOT(isp - 1); MIR_reg_t rs = inl_vs[isp - 1]; uint64_t cmp_val = (op == OP_IS_UNDEF) ? mkval(kTypeUndefined, 0) : mkval(kTypeNull, 0); MIR_label_t is_true = MIR_new_label(ctx); MIR_label_t is_done = MIR_new_label(ctx); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, is_true), MIR_new_reg_op(ctx, rs), MIR_new_uint_op(ctx, cmp_val))); mir_load_imm(ctx, jit_func, rs, js_false); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, is_done))); MIR_append_insn(ctx, jit_func, is_true); mir_load_imm(ctx, jit_func, rs, js_true); MIR_append_insn(ctx, jit_func, is_done); break; }
case OP_IS_UNDEF_OR_NULL: { INL_FLUSH_SLOT(isp - 1); MIR_reg_t rs = inl_vs[isp - 1]; MIR_label_t is_true = MIR_new_label(ctx); MIR_label_t is_done = MIR_new_label(ctx); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, is_true), MIR_new_reg_op(ctx, rs), MIR_new_uint_op(ctx, mkval(kTypeUndefined, 0)))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, is_true), MIR_new_reg_op(ctx, rs), MIR_new_uint_op(ctx, mkval(kTypeNull, 0)))); mir_load_imm(ctx, jit_func, rs, js_false); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, is_done))); MIR_append_insn(ctx, jit_func, is_true); mir_load_imm(ctx, jit_func, rs, js_true); MIR_append_insn(ctx, jit_func, is_done); break; }
case OP_IS_PRIMITIVE_TYPE: { INL_FLUSH_ALL(); mir_emit_primitive_type_test(ctx, jit_func, inl_vs[isp - 1], r_bool, ip[1]); break; }
case OP_JMP: { INL_FLUSH_ALL(); int target = inl_bc_off + sz + sv_get_i32(ip + 1); MIR_label_t lbl = inl_label_for_offset(ctx, &inl_lm, target, isp); ANT_ASSERT(lbl != NULL, "inline label map exhausted"); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, lbl))); break; }
case OP_JMP_NOT_NULLISH: { INL_FLUSH_ALL(); MIR_reg_t cond = inl_vs[isp - 1]; int target = inl_bc_off + sz + sv_get_i32(ip + 1); MIR_label_t lbl = inl_label_for_offset(ctx, &inl_lm, target, isp); ANT_ASSERT(lbl != NULL, "inline label map exhausted"); MIR_label_t done = MIR_new_label(ctx); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, done), MIR_new_reg_op(ctx, cond), MIR_new_uint_op(ctx, mkval(kTypeNull, 0)))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, lbl), MIR_new_reg_op(ctx, cond), MIR_new_uint_op(ctx, mkval(kTypeUndefined, 0)))); MIR_append_insn(ctx, jit_func, done); break; }
case OP_JMP_TRUE_PEEK: case OP_JMP_FALSE_PEEK: case OP_JMP_TRUE: case OP_JMP_FALSE: case OP_JMP_TRUE8: case OP_JMP_FALSE8: { INL_FLUSH_ALL(); bool is_peek = (op == OP_JMP_TRUE_PEEK || op == OP_JMP_FALSE_PEEK); MIR_reg_t cond = is_peek ? inl_vs[isp - 1] : inl_vs[--isp]; bool short_op = (op == OP_JMP_TRUE8 || op == OP_JMP_FALSE8); bool is_false_branch = (op == OP_JMP_FALSE || op == OP_JMP_FALSE8 || op == OP_JMP_FALSE_PEEK); int target = inl_bc_off + sz + (short_op ? (int8_t)sv_get_i8(ip + 1) : sv_get_i32(ip + 1)); MIR_label_t lbl = inl_label_for_offset(ctx, &inl_lm, target, isp); ANT_ASSERT(lbl != NULL, "inline label map exhausted");
mir_emit_truthy_branch(ctx, jit_func, cond, r_bool, r_js, ext->truthy_proto, ext->imp_is_truthy, is_false_branch, lbl); break; }
case OP_GET_FIELD: { INL_FLUSH_SLOT(isp - 1); uint32_t idx = sv_get_u32(ip + 1); ANT_ASSERT(idx < (uint32_t)callee->atom_count, "invalid inline atom index"); sv_atom_t *atom = &callee->atoms[idx]; MIR_reg_t obj = inl_vs[--isp]; MIR_reg_t dst = inl_vs[isp++]; inl_known[isp - 1] = isp >= 2 ? jit_math_field_builtin(inl_known[isp - 2], atom->str, atom->len) : 0; uint16_t gf_ic_idx = sv_get_u16(ip + 5); MIR_label_t gf_done = MIR_new_label(ctx); MIR_label_t gf_slowl = MIR_new_label(ctx); bool gf_fast = r_ic_epoch != 0 && mir_emit_get_field_ic_fastpath( ctx, jit_func, js, callee, -(id * 100000 + inl_bc_off + 1), gf_ic_idx, atom, obj, dst, gf_slowl, r_ic_epoch, NULL, false); if (gf_fast) { MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, gf_done))); MIR_append_insn(ctx, jit_func, gf_slowl); } MIR_append_insn(ctx, jit_func, MIR_new_call_insn(ctx, 8, MIR_new_ref_op(ctx, gf_proto), MIR_new_ref_op(ctx, seen_effect ? ext->imp_get_field : imp_get_field_inline), MIR_new_reg_op(ctx, dst), MIR_new_reg_op(ctx, r_js), MIR_new_reg_op(ctx, obj), MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)atom), MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)callee), MIR_new_int_op(ctx, (int64_t)inl_bc_off))); mir_emit_inline_read_guard( ctx, jit_func, dst, result, r_bool, slow, join, gf_done); MIR_append_insn(ctx, jit_func, gf_done); break; } case OP_GET_FIELD_OPT: { INL_FLUSH_SLOT(isp - 1); uint32_t idx = sv_get_u32(ip + 1); ANT_ASSERT(idx < (uint32_t)callee->atom_count, "invalid inline atom index"); sv_atom_t *atom = &callee->atoms[idx]; MIR_reg_t obj = inl_vs[--isp]; MIR_reg_t dst = inl_vs[isp++]; MIR_label_t nullish = MIR_new_label(ctx); MIR_label_t no_err = MIR_new_label(ctx); MIR_label_t value_ok = MIR_new_label(ctx); MIR_label_t gf_slow = MIR_new_label(ctx); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, nullish), MIR_new_reg_op(ctx, obj), MIR_new_uint_op(ctx, mkval(kTypeNull, 0)))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, nullish), MIR_new_reg_op(ctx, obj), MIR_new_uint_op(ctx, mkval(kTypeUndefined, 0)))); uint16_t gf_ic_idx = sv_get_u16(ip + 5); bool gf_fast = r_ic_epoch != 0 && mir_emit_get_field_ic_fastpath( ctx, jit_func, js, callee, -(id * 100000 + inl_bc_off + 1), gf_ic_idx, atom, obj, dst, gf_slow, r_ic_epoch, NULL, false); if (gf_fast) { MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, no_err))); MIR_append_insn(ctx, jit_func, gf_slow); } MIR_append_insn(ctx, jit_func, MIR_new_call_insn(ctx, 8, MIR_new_ref_op(ctx, gf_proto), MIR_new_ref_op(ctx, seen_effect ? ext->imp_get_field : imp_get_field_inline), MIR_new_reg_op(ctx, dst), MIR_new_reg_op(ctx, r_js), MIR_new_reg_op(ctx, obj), MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)atom), MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)callee), MIR_new_int_op(ctx, (int64_t)inl_bc_off))); mir_emit_inline_read_guard( ctx, jit_func, dst, result, r_bool, slow, join, value_ok); MIR_append_insn(ctx, jit_func, value_ok); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, no_err))); MIR_append_insn(ctx, jit_func, nullish); mir_load_imm(ctx, jit_func, dst, mkval(kTypeUndefined, 0)); MIR_append_insn(ctx, jit_func, no_err); break; } case OP_GET_FIELD2: { INL_FLUSH_SLOT(isp - 1); uint32_t idx = sv_get_u32(ip + 1); ANT_ASSERT(idx < (uint32_t)callee->atom_count, "invalid inline atom index"); sv_atom_t *atom = &callee->atoms[idx]; MIR_reg_t obj = inl_vs[isp - 1]; MIR_reg_t dst = inl_vs[isp++]; inl_known[isp - 1] = jit_math_field_builtin(inl_known[isp - 2], atom->str, atom->len); MIR_label_t value_ok = MIR_new_label(ctx); MIR_label_t gf_slow = MIR_new_label(ctx); uint16_t gf_ic_idx = sv_get_u16(ip + 5); bool gf_fast = r_ic_epoch != 0 && mir_emit_get_field_ic_fastpath( ctx, jit_func, js, callee, -(id * 100000 + inl_bc_off + 1), gf_ic_idx, atom, obj, dst, gf_slow, r_ic_epoch, NULL, false); if (gf_fast) { MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, value_ok))); MIR_append_insn(ctx, jit_func, gf_slow); } MIR_append_insn(ctx, jit_func, MIR_new_call_insn(ctx, 8, MIR_new_ref_op(ctx, gf_proto), MIR_new_ref_op(ctx, seen_effect ? ext->imp_get_field : imp_get_field_inline), MIR_new_reg_op(ctx, dst), MIR_new_reg_op(ctx, r_js), MIR_new_reg_op(ctx, obj), MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)atom), MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)callee), MIR_new_int_op(ctx, (int64_t)inl_bc_off))); mir_emit_inline_read_guard( ctx, jit_func, dst, result, r_bool, slow, join, value_ok); MIR_append_insn(ctx, jit_func, value_ok); break; } case OP_GET_GLOBAL: { uint32_t idx = sv_get_u32(ip + 1); ANT_ASSERT(idx < (uint32_t)callee->atom_count, "invalid inline atom index"); sv_atom_t *gg_atom = &callee->atoms[idx]; MIR_reg_t dst = inl_vs[isp++]; inl_known[isp - 1] = gg_atom->len == 4 && memcmp(gg_atom->str, "Math", 4) == 0 ? JIT_BUILTIN_MATH : JIT_BUILTIN_UNKNOWN; int gg_site = -(id * 100000 + inl_bc_off + 1); bool gg_fast = r_ic_epoch != 0 && mir_emit_get_global_ic_fastpath( ctx, jit_func, js, callee, gg_site, r_js, dst, slow, r_ic_epoch, ip, NULL, NULL); if (!gg_fast) MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, slow))); break; }
case OP_LOAD_STABLE_BUILTIN: { uint8_t stable_kind = sv_get_u8(ip + 1); ANT_ASSERT( stable_kind == SV_STABLE_BUILTIN_PROMISE_RESOLVE, "invalid inline stable-builtin load"); MIR_reg_t stable_this = inl_vs[isp++]; MIR_reg_t stable_fn = inl_vs[isp++]; int stable_site = -(id * 100000 + inl_bc_off + 1); mir_emit_load_stable_builtin( ctx, jit_func, js, r_js, stable_kind, stable_this, stable_fn, ext->r_args_buf, ext->stable_load_proto, ext->imp_load_stable_builtin, stable_site); MIR_label_t stable_ok = MIR_new_label(ctx); mir_emit_inline_read_guard( ctx, jit_func, stable_fn, result, r_bool, slow, join, stable_ok); MIR_append_insn(ctx, jit_func, stable_ok); break; }
case OP_PUT_FIELD: { INL_FLUSH_SLOT(isp - 1); INL_FLUSH_SLOT(isp - 2); uint32_t pf_idx = sv_get_u32(ip + 1); ANT_ASSERT(pf_idx < (uint32_t)callee->atom_count, "invalid inline atom index"); sv_atom_t *pf_atom = &callee->atoms[pf_idx]; uint16_t pf_ic_idx = sv_get_u16(ip + 5); sv_ic_entry_t *pf_ic = NULL; if (callee->ic_slots && pf_ic_idx != UINT16_MAX && pf_ic_idx < callee->ic_count) pf_ic = &callee->ic_slots[pf_ic_idx]; MIR_reg_t pf_val = inl_vs[--isp]; MIR_reg_t pf_obj = inl_vs[--isp]; MIR_label_t pf_slow = MIR_new_label(ctx); MIR_label_t pf_ok = MIR_new_label(ctx); int pf_name_off = -(id * 100000 + inl_bc_off + 1); bool pf_fast = r_ic_epoch != 0 && mir_emit_put_field_ic_fastpath( ctx, jit_func, js, callee, pf_name_off, pf_ic_idx, pf_atom, r_js, pf_obj, pf_val, pf_slow, r_ic_epoch, ext->shape_transition_proto, ext->imp_shape_transition, ext->imp_protector_store, ext->remember_obj_proto, ext->imp_remember_obj, ext->fresh_this && jit_inline_receiver_is_this(callee, inl_bc_off)); if (pf_fast) { MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, pf_ok))); MIR_append_insn(ctx, jit_func, pf_slow); } MIR_append_insn(ctx, jit_func, MIR_new_call_insn(ctx, 10, MIR_new_ref_op(ctx, ext->put_field_proto), MIR_new_ref_op(ctx, ext->imp_put_field), MIR_new_reg_op(ctx, result), MIR_new_reg_op(ctx, r_vm), MIR_new_reg_op(ctx, r_js), MIR_new_reg_op(ctx, pf_obj), MIR_new_reg_op(ctx, pf_val), MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)pf_atom), MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)pf_ic), MIR_new_int_op(ctx, callee->is_strict))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_URSH, MIR_new_reg_op(ctx, r_bool), MIR_new_reg_op(ctx, result), MIR_new_int_op(ctx, NANBOX_TYPE_SHIFT))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, pf_ok), MIR_new_reg_op(ctx, r_bool), MIR_new_uint_op(ctx, JIT_ERR_TAG))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, join))); MIR_append_insn(ctx, jit_func, pf_ok); break; }
case OP_GET_ELEM: { INL_FLUSH_SLOT(isp - 2); uint8_t feedback = sv_func_type_feedback(callee) ? sv_func_type_feedback(callee)[inl_bc_off] : 0; bool specialize = sv_tfb_specialization_ready(feedback); bool typed_seen = sv_tfb_elem_saw_typed_array(feedback); bool key_is_num = inl_num[isp - 1] != 0; MIR_reg_t key_double = inl_d[isp - 1];
if (specialize) { INL_ENSURE_D_SLOT(); MIR_reg_t ge_key = inl_vs[--isp]; MIR_reg_t ge_obj = inl_vs[--isp]; (void)inl_vs[isp++]; int index_site = mir_next_reg_site(p_reg_site); int element_site = mir_next_reg_site(p_reg_site); MIR_reg_t index = mir_emit_array_index_guard( ctx, jit_func, ge_key, key_double, key_is_num, *p_d_slot, slow, index_site); MIR_label_t ge_typed = typed_seen ? MIR_new_label(ctx) : slow; MIR_label_t ge_loaded = MIR_new_label(ctx); (void)mir_emit_dense_element_guard( ctx, jit_func, ge_obj, index, r_bool, JIT_ELEMENT_NUMERIC_READ, ge_typed, element_site, NULL); mir_i64_to_d( ctx, jit_func, inl_d[isp - 1], r_bool, *p_d_slot); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, ge_loaded))); if (typed_seen) { MIR_append_insn(ctx, jit_func, ge_typed); mir_emit_typed_array_read( ctx, jit_func, ge_obj, index, inl_d[isp - 1], slow, ge_loaded, element_site, sv_tfb_elem_type(callee, inl_bc_off)); } MIR_append_insn(ctx, jit_func, ge_loaded); inl_num[isp - 1] = 1; inl_num[isp] = 0; break; }
INL_FLUSH_SLOT(isp - 1); MIR_reg_t ge_key = inl_vs[--isp]; MIR_reg_t ge_obj = inl_vs[--isp]; MIR_reg_t ge_dst = inl_vs[isp++]; inl_num[isp - 1] = 0; MIR_label_t ge_ok = MIR_new_label(ctx); MIR_label_t ge_slow = MIR_new_label(ctx); INL_ENSURE_D_SLOT(); MIR_label_t ge_typed = typed_seen ? MIR_new_label(ctx) : ge_slow; MIR_reg_t ge_index = mir_emit_array_index_guard( ctx, jit_func, ge_key, key_double, key_is_num, *p_d_slot, ge_slow, mir_next_reg_site(p_reg_site)); int ge_site = mir_next_reg_site(p_reg_site); (void)mir_emit_dense_element_guard( ctx, jit_func, ge_obj, ge_index, r_bool, JIT_ELEMENT_READ, ge_typed, ge_site, NULL); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, ge_dst), MIR_new_reg_op(ctx, r_bool))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, ge_ok))); if (typed_seen) { MIR_label_t ge_typed_loaded = MIR_new_label(ctx); char ge_name[48]; snprintf(ge_name, sizeof(ge_name), "inl_ta_number_%d", ge_site); MIR_reg_t ge_number = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, ge_name); MIR_append_insn(ctx, jit_func, ge_typed); mir_emit_typed_array_read( ctx, jit_func, ge_obj, ge_index, ge_number, ge_slow, ge_typed_loaded, ge_site, sv_tfb_elem_type(callee, inl_bc_off)); MIR_append_insn(ctx, jit_func, ge_typed_loaded); mir_d_to_i64(ctx, jit_func, ge_dst, ge_number, *p_d_slot); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, ge_ok))); } MIR_append_insn(ctx, jit_func, ge_slow); MIR_append_insn(ctx, jit_func, MIR_new_call_insn(ctx, 6, MIR_new_ref_op(ctx, ext->get_elem_inline_proto), MIR_new_ref_op(ctx, ext->imp_get_elem_inline), MIR_new_reg_op(ctx, ge_dst), MIR_new_reg_op(ctx, r_js), MIR_new_reg_op(ctx, ge_obj), MIR_new_reg_op(ctx, ge_key))); mir_emit_inline_read_guard( ctx, jit_func, ge_dst, result, r_bool, slow, join, ge_ok); MIR_append_insn(ctx, jit_func, ge_ok); break; }
case OP_GET_LENGTH: { INL_FLUSH_SLOT(isp - 1); MIR_reg_t gl_obj = inl_vs[--isp]; MIR_reg_t gl_dst = inl_vs[isp++]; MIR_label_t gl_ok = MIR_new_label(ctx); INL_ENSURE_D_SLOT(); mir_emit_get_length( ctx, jit_func, js, gl_obj, gl_dst, r_js, *p_d_slot, ext->get_length_proto, seen_effect ? ext->imp_get_length : ext->imp_get_length_inline, false, id, inl_bc_off, NULL, NULL); mir_emit_inline_read_guard( ctx, jit_func, gl_dst, result, r_bool, slow, join, gl_ok); MIR_append_insn(ctx, jit_func, gl_ok); break; }
case OP_BAND: case OP_BOR: case OP_BXOR: case OP_SHL: case OP_SHR: case OP_USHR: { uint8_t feedback = sv_func_type_feedback(callee) ? sv_func_type_feedback(callee)[inl_bc_off] : 0; bool specialize = sv_tfb_specialization_ready(feedback); bool l_is_num = inl_num[isp - 2] != 0; bool r_is_num = inl_num[isp - 1] != 0;
if (specialize) { INL_ENSURE_D_SLOT(); MIR_reg_t bw_rr = inl_vs[--isp]; MIR_reg_t bw_rl = inl_vs[--isp]; MIR_reg_t bw_rd = inl_vs[isp++]; int left_site = mir_next_reg_site(p_reg_site); int right_site = mir_next_reg_site(p_reg_site); int result_site = mir_next_reg_site(p_reg_site); MIR_reg_t left = mir_emit_word32_guard( ctx, jit_func, bw_rl, inl_d[isp - 1], l_is_num, false, *p_d_slot, slow, left_site); MIR_reg_t right = mir_emit_word32_guard( ctx, jit_func, bw_rr, inl_d[isp], r_is_num, false, *p_d_slot, slow, right_site); mir_emit_word32_binary( ctx, jit_func, op, left, right, bw_rd, inl_d[isp - 1], true, result_site); inl_num[isp - 1] = 1; inl_num[isp] = 0; break; }
INL_FLUSH_SLOT(isp - 1); INL_FLUSH_SLOT(isp - 2); mir_emit_is_num_guard(ctx, jit_func, r_bool, inl_vs[isp - 1], slow); mir_emit_is_num_guard(ctx, jit_func, r_bool, inl_vs[isp - 2], slow); MIR_reg_t bw_rr = inl_vs[--isp]; MIR_reg_t bw_rl = inl_vs[--isp]; MIR_reg_t bw_rd = inl_vs[isp++]; inl_num[isp - 1] = 0; MIR_item_t bw_imp; switch (op) { case OP_BAND: bw_imp = ext->imp_band; break; case OP_BOR: bw_imp = ext->imp_bor; break; case OP_BXOR: bw_imp = ext->imp_bxor; break; case OP_SHL: bw_imp = ext->imp_shl; break; case OP_SHR: bw_imp = ext->imp_shr; break; default: bw_imp = ext->imp_ushr; break; } mir_call_helper2(ctx, jit_func, bw_rd, helper2_proto, bw_imp, r_vm, r_js, bw_rl, bw_rr); break; }
case OP_CALL_STABLE_BUILTIN: { int stable_kind = ip[1]; uint16_t stable_argc = sv_get_u16(ip + 2); ANT_ASSERT(stable_argc <= SV_JIT_ARGS_BUF_CAP, "nested stable-builtin call escaped feasibility"); ANT_ASSERT(isp >= (int)stable_argc + 2, "invalid inline stable-builtin call stack depth"); INL_FLUSH_ALL();
MIR_reg_t stable_arg0 = stable_argc > 0 ? inl_vs[isp - (int)stable_argc] : 0; bool stable_args_prepared = !( stable_kind == SV_STABLE_BUILTIN_PROMISE_RESOLVE && stable_argc == 1); for (int i = (int)stable_argc - 1; i >= 0; i--) if (stable_args_prepared) MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_mem_op(ctx, MIR_JSVAL, (MIR_disp_t)(i * (int)sizeof(ant_value_t)), ext->r_args_buf, 0, 1), MIR_new_reg_op(ctx, inl_vs[isp - (int)stable_argc + i]))); isp -= (int)stable_argc; MIR_reg_t stable_fn = inl_vs[--isp]; MIR_reg_t stable_this = inl_vs[--isp]; MIR_reg_t stable_dst = inl_vs[isp++];
int stable_site = -(id * 100000 + inl_bc_off + 1); mir_emit_call_stable_builtin( ctx, jit_func, js, r_vm, r_js, stable_kind, stable_fn, stable_this, stable_arg0, ext->r_args_buf, stable_argc, stable_dst, ext->stable_call_proto, ext->imp_call_stable_builtin, false, stable_args_prepared, stable_site);
MIR_label_t stable_ok = MIR_new_label(ctx); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_URSH, MIR_new_reg_op(ctx, r_bool), MIR_new_reg_op(ctx, stable_dst), MIR_new_int_op(ctx, NANBOX_TYPE_SHIFT))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, stable_ok), MIR_new_reg_op(ctx, r_bool), MIR_new_uint_op(ctx, JIT_ERR_TAG))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, result), MIR_new_reg_op(ctx, stable_dst))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, join))); MIR_append_insn(ctx, jit_func, stable_ok); break; }
case OP_CALL: case OP_TAIL_CALL: case OP_CALL_METHOD: case OP_TAIL_CALL_METHOD: { bool nc_method = (op == OP_CALL_METHOD || op == OP_TAIL_CALL_METHOD); bool nc_tail = (op == OP_TAIL_CALL || op == OP_TAIL_CALL_METHOD); uint16_t nc_argc = sv_get_u16(ip + 1); ANT_ASSERT(nc_argc <= SV_JIT_ARGS_BUF_CAP, "nested inline call escaped feasibility"); ANT_ASSERT(isp >= (int)nc_argc + (nc_method ? 2 : 1), "invalid inline call stack depth"); INL_FLUSH_ALL();
MIR_label_t math_done = NULL; uint8_t nc_known = nc_method ? inl_known[isp - (int)nc_argc - 1] : 0; if (nc_known >= JIT_BUILTIN_MATH_FN) { ant_math_intrinsic_t kind = (ant_math_intrinsic_t)(nc_known - JIT_BUILTIN_MATH_FN); if (nc_argc == (kind >= ANT_MATH_FIRST_BINARY ? 2 : 1)) { INL_ENSURE_D_SLOT(); math_done = mir_emit_math_call(ctx, jit_func, &(jit_math_call_t){ .kind = kind, .result = nc_tail ? result : inl_vs[isp - (int)nc_argc - 2], .callee = inl_vs[isp - (int)nc_argc - 1], .a = inl_vs[isp - (int)nc_argc], .b = nc_argc == 2 ? inl_vs[isp - 1] : 0, .r_js = r_js, .r_d_slot = *p_d_slot, .scratch = r_bool, .math1_proto = ext->math1_proto, .imp_math = ext->imp_math, .site = mir_next_reg_site(p_reg_site), }); } }
MIR_reg_t nc_args[nc_argc ? nc_argc : 1]; for (int i = 0; i < (int)nc_argc; i++) nc_args[i] = inl_vs[isp - (int)nc_argc + i]; for (int i = (int)nc_argc - 1; i >= 0; i--) MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_mem_op(ctx, MIR_JSVAL, (MIR_disp_t)(i * (int)sizeof(ant_value_t)), ext->r_args_buf, 0, 1), MIR_new_reg_op(ctx, inl_vs[isp - (int)nc_argc + i]))); isp -= (int)nc_argc; MIR_reg_t nc_fn = inl_vs[--isp]; MIR_reg_t nc_this = nc_method ? inl_vs[--isp] : inl_undef; MIR_reg_t nc_dst = nc_tail ? result : inl_vs[isp++];
MIR_label_t dv_generic = MIR_new_label(ctx); MIR_label_t dv_done = MIR_new_label(ctx); { int dv_off = (int)(ip - callee->code); char dv_rn[48]; snprintf(dv_rn, sizeof(dv_rn), "inl%d_dv%d_cl", id, dv_off); MIR_reg_t r_dv_cl = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, dv_rn); snprintf(dv_rn, sizeof(dv_rn), "inl%d_dv%d_fn", id, dv_off); MIR_reg_t r_dv_fn = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, dv_rn); snprintf(dv_rn, sizeof(dv_rn), "inl%d_dv%d_jp", id, dv_off); MIR_reg_t r_dv_jp = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, dv_rn); snprintf(dv_rn, sizeof(dv_rn), "inl%d_dv%d_this", id, dv_off); MIR_reg_t r_dv_this = MIR_new_func_reg(ctx, jit_func->u.func, MIR_JSVAL, dv_rn); snprintf(dv_rn, sizeof(dv_rn), "inl%d_dv%d_sup", id, dv_off); MIR_reg_t r_dv_sup = MIR_new_func_reg(ctx, jit_func->u.func, MIR_JSVAL, dv_rn); snprintf(dv_rn, sizeof(dv_rn), "inl%d_dv%d_bnd", id, dv_off); MIR_reg_t r_dv_bound = MIR_new_func_reg(ctx, jit_func->u.func, MIR_JSVAL, dv_rn);
MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, dv_generic), MIR_new_reg_op(ctx, nc_fn), MIR_new_reg_op(ctx, r_inl_super))); mir_emit_get_closure(ctx, jit_func, r_dv_cl, nc_fn, r_bool, dv_generic); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, r_bool), MIR_new_mem_op(ctx, MIR_T_U32, (MIR_disp_t)offsetof(sv_closure_t, call_flags), r_dv_cl, 0, 1))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_AND, MIR_new_reg_op(ctx, r_bool), MIR_new_reg_op(ctx, r_bool), MIR_new_uint_op(ctx, (uint64_t)SV_CALL_HAS_BOUND_ARGS))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, dv_generic), MIR_new_reg_op(ctx, r_bool), MIR_new_uint_op(ctx, 0))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, r_dv_fn), MIR_new_mem_op(ctx, MIR_T_P, (MIR_disp_t)offsetof(sv_closure_t, func), r_dv_cl, 0, 1))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, r_dv_sup), MIR_new_mem_op(ctx, MIR_T_I64, (MIR_disp_t)offsetof(sv_closure_t, super_val), r_dv_cl, 0, 1))); mir_emit_resolve_call_this(ctx, jit_func, r_dv_this, r_dv_cl, nc_this, r_bool, r_dv_bound); sv_func_t *reader = sv_tfb_get_call_target(callee, dv_off); if (ext->next_inline_id && jit_inline_reader_leaf(reader) && reader->code_len <= reader_budget) { sv_code_unit_make_immortal(reader); reader_budget -= reader->code_len; MIR_label_t dispatch = MIR_new_label(ctx); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, dispatch), MIR_new_reg_op(ctx, r_dv_fn), MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)reader))); int reader_id = (*ext->next_inline_id)++; jit_inline_ext_t reader_ext = *ext; reader_ext.reader_only = true; reader_ext.fresh_this = false; jit_emit_inline_body(ctx, jit_func, js, reader, nc_args, nc_argc, NULL, NULL, nc_dst, dv_generic, dv_done, r_bool, p_d_slot, reader_id, p_reg_site, r_dv_cl, r_dv_this, inl_undef, r_dv_sup, r_vm, r_js, r_ic_epoch, helper2_proto, imp_seq, imp_sne, imp_eq, imp_ne, gf_proto, imp_get_field_inline, special_obj_proto, imp_special_obj, &reader_ext); MIR_append_insn(ctx, jit_func, dispatch); } MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, dv_generic), MIR_new_reg_op(ctx, r_dv_fn), MIR_new_int_op(ctx, 0))); mir_emit_realm_guard(ctx, jit_func, r_dv_fn, r_dv_jp, js, dv_generic); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, r_dv_jp), MIR_new_mem_op(ctx, MIR_T_P, (MIR_disp_t)offsetof(sv_func_t, jit_code), r_dv_fn, 0, 1))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_BEQ, MIR_new_label_op(ctx, dv_generic), MIR_new_reg_op(ctx, r_dv_jp), MIR_new_int_op(ctx, 0))); MIR_append_insn(ctx, jit_func, MIR_new_call_insn(ctx, 10, MIR_new_ref_op(ctx, ext->self_proto), MIR_new_reg_op(ctx, r_dv_jp), MIR_new_reg_op(ctx, nc_dst), MIR_new_reg_op(ctx, r_vm), MIR_new_reg_op(ctx, r_dv_this), MIR_new_uint_op(ctx, mkval(kTypeUndefined, 0)), MIR_new_reg_op(ctx, r_dv_sup), MIR_new_reg_op(ctx, ext->r_args_buf), MIR_new_int_op(ctx, (int64_t)nc_argc), MIR_new_reg_op(ctx, r_dv_cl))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, dv_done))); } MIR_append_insn(ctx, jit_func, dv_generic); { MIR_label_t dv_slow = MIR_new_label(ctx); char dv_prefix[24]; snprintf(dv_prefix, sizeof(dv_prefix), "inl%d_", id); int dv_site = (int)(ip - callee->code); if (sv_tfb_call_site_may_call_builtin(callee, dv_site)) mir_emit_builtin_call_fast( ctx, jit_func, js, dv_prefix, dv_site, r_js, r_bool, ext->cfunc_proto, ext->native_finish_proto, ext->imp_native_finish, nc_fn, nc_this, ext->r_args_buf, nc_argc, nc_dst, dv_slow, dv_done); else mir_emit_builtin_call_watch(ctx, jit_func, js, dv_prefix, dv_site, r_bool, nc_fn, callee, dv_site); MIR_append_insn(ctx, jit_func, dv_slow); }
if (nc_method) { MIR_append_insn(ctx, jit_func, MIR_new_call_insn(ctx, 12, MIR_new_ref_op(ctx, ext->call_method_proto), MIR_new_ref_op(ctx, ext->imp_call_method), MIR_new_reg_op(ctx, nc_dst), MIR_new_reg_op(ctx, r_vm), MIR_new_reg_op(ctx, r_js), MIR_new_reg_op(ctx, nc_fn), MIR_new_reg_op(ctx, nc_this), MIR_new_reg_op(ctx, ext->r_args_buf), MIR_new_int_op(ctx, (int64_t)nc_argc), MIR_new_reg_op(ctx, inl_undef), MIR_new_reg_op(ctx, inl_undef), MIR_new_int_op(ctx, 0))); } else { MIR_append_insn(ctx, jit_func, MIR_new_call_insn(ctx, 9, MIR_new_ref_op(ctx, ext->call_proto), MIR_new_ref_op(ctx, ext->imp_call), MIR_new_reg_op(ctx, nc_dst), MIR_new_reg_op(ctx, r_vm), MIR_new_reg_op(ctx, r_js), MIR_new_reg_op(ctx, nc_fn), MIR_new_reg_op(ctx, nc_this), MIR_new_reg_op(ctx, ext->r_args_buf), MIR_new_int_op(ctx, (int64_t)nc_argc))); } MIR_append_insn(ctx, jit_func, dv_done);
if (nc_tail) { MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, join))); } else { MIR_label_t nc_ok = MIR_new_label(ctx); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_URSH, MIR_new_reg_op(ctx, r_bool), MIR_new_reg_op(ctx, nc_dst), MIR_new_int_op(ctx, NANBOX_TYPE_SHIFT))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_BNE, MIR_new_label_op(ctx, nc_ok), MIR_new_reg_op(ctx, r_bool), MIR_new_uint_op(ctx, JIT_ERR_TAG))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, result), MIR_new_reg_op(ctx, nc_dst))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, join))); MIR_append_insn(ctx, jit_func, nc_ok); } if (math_done) { MIR_append_insn(ctx, jit_func, math_done); if (nc_tail) MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, join))); } if (!nc_tail) inl_known[isp - 1] = JIT_BUILTIN_UNKNOWN; break; }
case OP_RETURN: { INL_FLUSH_SLOT(isp - 1); MIR_reg_t ret = inl_vs[--isp]; MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, result), MIR_new_reg_op(ctx, ret))); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, join))); break; } case OP_RETURN_UNDEF: mir_load_imm(ctx, jit_func, result, mkval(kTypeUndefined, 0)); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, join))); break;
case OP_SPECIAL_OBJ: { INL_FLUSH_ALL(); uint8_t which = sv_get_u8(ip + 1); MIR_reg_t dst = inl_vs[isp++]; if (which == SV_SPECIAL_NEW_TARGET) { MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, dst), MIR_new_reg_op(ctx, r_inl_new_target))); } else if (which == SV_SPECIAL_SUPER) { MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, dst), MIR_new_reg_op(ctx, r_inl_super))); } else if (which == SV_SPECIAL_ARGC) { mir_load_imm(ctx, jit_func, dst, tov((double)caller_argc)); } else if (which == SV_SPECIAL_MODULE_IMPORT) { MIR_append_insn(ctx, jit_func, MIR_new_call_insn(ctx, 6, MIR_new_ref_op(ctx, special_obj_proto), MIR_new_ref_op(ctx, imp_special_obj), MIR_new_reg_op(ctx, dst), MIR_new_reg_op(ctx, r_vm), MIR_new_reg_op(ctx, r_js), MIR_new_int_op(ctx, (int64_t)which))); } else { mir_load_imm(ctx, jit_func, dst, mkval(kTypeUndefined, 0)); } break; }
case OP_NOP: case OP_LINE_NUM: case OP_COL_NUM: case OP_LABEL: break;
default: ANT_ASSERT(false, "inlineable opcode has no emitter"); } if (jit_op_inline_side_effect(op)) seen_effect = true; ip += sz; } free(inl_depth); if (tagging) tagging->inl.depth--; ANT_ASSERT(!reuse_empty_objects || !seen_effect, "empty object reuse requires an effect-free inline body");}