| 1 | /* |
| 2 | ** SSA IR (Intermediate Representation) emitter. |
| 3 | ** Copyright (C) 2005-2021 Mike Pall. See Copyright Notice in luajit.h |
| 4 | */ |
| 5 | |
| 6 | #define lj_ir_c |
| 7 | #define LUA_CORE |
| 8 | |
| 9 | /* For pointers to libc/libm functions. */ |
| 10 | #include <stdio.h> |
| 11 | #include <math.h> |
| 12 | |
| 13 | #include "lj_obj.h" |
| 14 | |
| 15 | #if LJ_HASJIT |
| 16 | |
| 17 | #include "lj_gc.h" |
| 18 | #include "lj_buf.h" |
| 19 | #include "lj_str.h" |
| 20 | #include "lj_tab.h" |
| 21 | #include "lj_ir.h" |
| 22 | #include "lj_jit.h" |
| 23 | #include "lj_ircall.h" |
| 24 | #include "lj_iropt.h" |
| 25 | #include "lj_trace.h" |
| 26 | #if LJ_HASFFI |
| 27 | #include "lj_ctype.h" |
| 28 | #include "lj_cdata.h" |
| 29 | #include "lj_carith.h" |
| 30 | #endif |
| 31 | #include "lj_vm.h" |
| 32 | #include "lj_strscan.h" |
| 33 | #include "lj_strfmt.h" |
| 34 | #include "lj_prng.h" |
| 35 | |
| 36 | /* Some local macros to save typing. Undef'd at the end. */ |
| 37 | #define IR(ref) (&J->cur.ir[(ref)]) |
| 38 | #define fins (&J->fold.ins) |
| 39 | |
| 40 | /* Pass IR on to next optimization in chain (FOLD). */ |
| 41 | #define emitir(ot, a, b) (lj_ir_set(J, (ot), (a), (b)), lj_opt_fold(J)) |
| 42 | |
| 43 | /* -- IR tables ----------------------------------------------------------- */ |
| 44 | |
| 45 | /* IR instruction modes. */ |
| 46 | LJ_DATADEF const uint8_t lj_ir_mode[IR__MAX+1] = { |
| 47 | IRDEF(IRMODE) |
| 48 | 0 |
| 49 | }; |
| 50 | |
| 51 | /* IR type sizes. */ |
| 52 | LJ_DATADEF const uint8_t lj_ir_type_size[IRT__MAX+1] = { |
| 53 | #define IRTSIZE(name, size) size, |
| 54 | IRTDEF(IRTSIZE) |
| 55 | #undef IRTSIZE |
| 56 | 0 |
| 57 | }; |
| 58 | |
| 59 | /* C call info for CALL* instructions. */ |
| 60 | LJ_DATADEF const CCallInfo lj_ir_callinfo[] = { |
| 61 | #define IRCALLCI(cond, name, nargs, kind, type, flags) \ |
| 62 | { (ASMFunction)IRCALLCOND_##cond(name), \ |
| 63 | (nargs)|(CCI_CALL_##kind)|(IRT_##type<<CCI_OTSHIFT)|(flags) }, |
| 64 | IRCALLDEF(IRCALLCI) |
| 65 | #undef IRCALLCI |
| 66 | { NULL, 0 } |
| 67 | }; |
| 68 | |
| 69 | /* -- IR emitter ---------------------------------------------------------- */ |
| 70 | |
| 71 | /* Grow IR buffer at the top. */ |
| 72 | void LJ_FASTCALL lj_ir_growtop(jit_State *J) |
| 73 | { |
| 74 | IRIns *baseir = J->irbuf + J->irbotlim; |
| 75 | MSize szins = J->irtoplim - J->irbotlim; |
| 76 | if (szins) { |
| 77 | baseir = (IRIns *)lj_mem_realloc(J->L, baseir, szins*sizeof(IRIns), |
| 78 | 2*szins*sizeof(IRIns)); |
| 79 | J->irtoplim = J->irbotlim + 2*szins; |
| 80 | } else { |
| 81 | baseir = (IRIns *)lj_mem_realloc(J->L, NULL, 0, LJ_MIN_IRSZ*sizeof(IRIns)); |
| 82 | J->irbotlim = REF_BASE - LJ_MIN_IRSZ/4; |
| 83 | J->irtoplim = J->irbotlim + LJ_MIN_IRSZ; |
| 84 | } |
| 85 | J->cur.ir = J->irbuf = baseir - J->irbotlim; |
| 86 | } |
| 87 | |
| 88 | /* Grow IR buffer at the bottom or shift it up. */ |
| 89 | static void lj_ir_growbot(jit_State *J) |
| 90 | { |
| 91 | IRIns *baseir = J->irbuf + J->irbotlim; |
| 92 | MSize szins = J->irtoplim - J->irbotlim; |
| 93 | lj_assertJ(szins != 0, "zero IR size" ); |
| 94 | lj_assertJ(J->cur.nk == J->irbotlim || J->cur.nk-1 == J->irbotlim, |
| 95 | "unexpected IR growth" ); |
| 96 | if (J->cur.nins + (szins >> 1) < J->irtoplim) { |
| 97 | /* More than half of the buffer is free on top: shift up by a quarter. */ |
| 98 | MSize ofs = szins >> 2; |
| 99 | memmove(baseir + ofs, baseir, (J->cur.nins - J->irbotlim)*sizeof(IRIns)); |
| 100 | J->irbotlim -= ofs; |
| 101 | J->irtoplim -= ofs; |
| 102 | J->cur.ir = J->irbuf = baseir - J->irbotlim; |
| 103 | } else { |
| 104 | /* Double the buffer size, but split the growth amongst top/bottom. */ |
| 105 | IRIns *newbase = lj_mem_newt(J->L, 2*szins*sizeof(IRIns), IRIns); |
| 106 | MSize ofs = szins >= 256 ? 128 : (szins >> 1); /* Limit bottom growth. */ |
| 107 | memcpy(newbase + ofs, baseir, (J->cur.nins - J->irbotlim)*sizeof(IRIns)); |
| 108 | lj_mem_free(G(J->L), baseir, szins*sizeof(IRIns)); |
| 109 | J->irbotlim -= ofs; |
| 110 | J->irtoplim = J->irbotlim + 2*szins; |
| 111 | J->cur.ir = J->irbuf = newbase - J->irbotlim; |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | /* Emit IR without any optimizations. */ |
| 116 | TRef LJ_FASTCALL lj_ir_emit(jit_State *J) |
| 117 | { |
| 118 | IRRef ref = lj_ir_nextins(J); |
| 119 | IRIns *ir = IR(ref); |
| 120 | IROp op = fins->o; |
| 121 | ir->prev = J->chain[op]; |
| 122 | J->chain[op] = (IRRef1)ref; |
| 123 | ir->o = op; |
| 124 | ir->op1 = fins->op1; |
| 125 | ir->op2 = fins->op2; |
| 126 | J->guardemit.irt |= fins->t.irt; |
| 127 | return TREF(ref, irt_t((ir->t = fins->t))); |
| 128 | } |
| 129 | |
| 130 | /* Emit call to a C function. */ |
| 131 | TRef lj_ir_call(jit_State *J, IRCallID id, ...) |
| 132 | { |
| 133 | const CCallInfo *ci = &lj_ir_callinfo[id]; |
| 134 | uint32_t n = CCI_NARGS(ci); |
| 135 | TRef tr = TREF_NIL; |
| 136 | va_list argp; |
| 137 | va_start(argp, id); |
| 138 | if ((ci->flags & CCI_L)) n--; |
| 139 | if (n > 0) |
| 140 | tr = va_arg(argp, IRRef); |
| 141 | while (n-- > 1) |
| 142 | tr = emitir(IRT(IR_CARG, IRT_NIL), tr, va_arg(argp, IRRef)); |
| 143 | va_end(argp); |
| 144 | if (CCI_OP(ci) == IR_CALLS) |
| 145 | J->needsnap = 1; /* Need snapshot after call with side effect. */ |
| 146 | return emitir(CCI_OPTYPE(ci), tr, id); |
| 147 | } |
| 148 | |
| 149 | /* Load field of type t from GG_State + offset. Must be 32 bit aligned. */ |
| 150 | TRef lj_ir_ggfload(jit_State *J, IRType t, uintptr_t ofs) |
| 151 | { |
| 152 | lj_assertJ((ofs & 3) == 0, "unaligned GG_State field offset" ); |
| 153 | ofs >>= 2; |
| 154 | lj_assertJ(ofs >= IRFL__MAX && ofs <= 0x3ff, |
| 155 | "GG_State field offset breaks 10 bit FOLD key limit" ); |
| 156 | lj_ir_set(J, IRT(IR_FLOAD, t), REF_NIL, ofs); |
| 157 | return lj_opt_fold(J); |
| 158 | } |
| 159 | |
| 160 | /* -- Interning of constants ---------------------------------------------- */ |
| 161 | |
| 162 | /* |
| 163 | ** IR instructions for constants are kept between J->cur.nk >= ref < REF_BIAS. |
| 164 | ** They are chained like all other instructions, but grow downwards. |
| 165 | ** The are interned (like strings in the VM) to facilitate reference |
| 166 | ** comparisons. The same constant must get the same reference. |
| 167 | */ |
| 168 | |
| 169 | /* Get ref of next IR constant and optionally grow IR. |
| 170 | ** Note: this may invalidate all IRIns *! |
| 171 | */ |
| 172 | static LJ_AINLINE IRRef ir_nextk(jit_State *J) |
| 173 | { |
| 174 | IRRef ref = J->cur.nk; |
| 175 | if (LJ_UNLIKELY(ref <= J->irbotlim)) lj_ir_growbot(J); |
| 176 | J->cur.nk = --ref; |
| 177 | return ref; |
| 178 | } |
| 179 | |
| 180 | /* Get ref of next 64 bit IR constant and optionally grow IR. |
| 181 | ** Note: this may invalidate all IRIns *! |
| 182 | */ |
| 183 | static LJ_AINLINE IRRef ir_nextk64(jit_State *J) |
| 184 | { |
| 185 | IRRef ref = J->cur.nk - 2; |
| 186 | lj_assertJ(J->state != LJ_TRACE_ASM, "bad JIT state" ); |
| 187 | if (LJ_UNLIKELY(ref < J->irbotlim)) lj_ir_growbot(J); |
| 188 | J->cur.nk = ref; |
| 189 | return ref; |
| 190 | } |
| 191 | |
| 192 | #if LJ_GC64 |
| 193 | #define ir_nextkgc ir_nextk64 |
| 194 | #else |
| 195 | #define ir_nextkgc ir_nextk |
| 196 | #endif |
| 197 | |
| 198 | /* Intern int32_t constant. */ |
| 199 | TRef LJ_FASTCALL lj_ir_kint(jit_State *J, int32_t k) |
| 200 | { |
| 201 | IRIns *ir, *cir = J->cur.ir; |
| 202 | IRRef ref; |
| 203 | for (ref = J->chain[IR_KINT]; ref; ref = cir[ref].prev) |
| 204 | if (cir[ref].i == k) |
| 205 | goto found; |
| 206 | ref = ir_nextk(J); |
| 207 | ir = IR(ref); |
| 208 | ir->i = k; |
| 209 | ir->t.irt = IRT_INT; |
| 210 | ir->o = IR_KINT; |
| 211 | ir->prev = J->chain[IR_KINT]; |
| 212 | J->chain[IR_KINT] = (IRRef1)ref; |
| 213 | found: |
| 214 | return TREF(ref, IRT_INT); |
| 215 | } |
| 216 | |
| 217 | /* Intern 64 bit constant, given by its 64 bit pattern. */ |
| 218 | TRef lj_ir_k64(jit_State *J, IROp op, uint64_t u64) |
| 219 | { |
| 220 | IRIns *ir, *cir = J->cur.ir; |
| 221 | IRRef ref; |
| 222 | IRType t = op == IR_KNUM ? IRT_NUM : IRT_I64; |
| 223 | for (ref = J->chain[op]; ref; ref = cir[ref].prev) |
| 224 | if (ir_k64(&cir[ref])->u64 == u64) |
| 225 | goto found; |
| 226 | ref = ir_nextk64(J); |
| 227 | ir = IR(ref); |
| 228 | ir[1].tv.u64 = u64; |
| 229 | ir->t.irt = t; |
| 230 | ir->o = op; |
| 231 | ir->op12 = 0; |
| 232 | ir->prev = J->chain[op]; |
| 233 | J->chain[op] = (IRRef1)ref; |
| 234 | found: |
| 235 | return TREF(ref, t); |
| 236 | } |
| 237 | |
| 238 | /* Intern FP constant, given by its 64 bit pattern. */ |
| 239 | TRef lj_ir_knum_u64(jit_State *J, uint64_t u64) |
| 240 | { |
| 241 | return lj_ir_k64(J, IR_KNUM, u64); |
| 242 | } |
| 243 | |
| 244 | /* Intern 64 bit integer constant. */ |
| 245 | TRef lj_ir_kint64(jit_State *J, uint64_t u64) |
| 246 | { |
| 247 | return lj_ir_k64(J, IR_KINT64, u64); |
| 248 | } |
| 249 | |
| 250 | /* Check whether a number is int and return it. -0 is NOT considered an int. */ |
| 251 | static int numistrueint(lua_Number n, int32_t *kp) |
| 252 | { |
| 253 | int32_t k = lj_num2int(n); |
| 254 | if (n == (lua_Number)k) { |
| 255 | if (kp) *kp = k; |
| 256 | if (k == 0) { /* Special check for -0. */ |
| 257 | TValue tv; |
| 258 | setnumV(&tv, n); |
| 259 | if (tv.u32.hi != 0) |
| 260 | return 0; |
| 261 | } |
| 262 | return 1; |
| 263 | } |
| 264 | return 0; |
| 265 | } |
| 266 | |
| 267 | /* Intern number as int32_t constant if possible, otherwise as FP constant. */ |
| 268 | TRef lj_ir_knumint(jit_State *J, lua_Number n) |
| 269 | { |
| 270 | int32_t k; |
| 271 | if (numistrueint(n, &k)) |
| 272 | return lj_ir_kint(J, k); |
| 273 | else |
| 274 | return lj_ir_knum(J, n); |
| 275 | } |
| 276 | |
| 277 | /* Intern GC object "constant". */ |
| 278 | TRef lj_ir_kgc(jit_State *J, GCobj *o, IRType t) |
| 279 | { |
| 280 | IRIns *ir, *cir = J->cur.ir; |
| 281 | IRRef ref; |
| 282 | lj_assertJ(!isdead(J2G(J), o), "interning of dead GC object" ); |
| 283 | for (ref = J->chain[IR_KGC]; ref; ref = cir[ref].prev) |
| 284 | if (ir_kgc(&cir[ref]) == o) |
| 285 | goto found; |
| 286 | ref = ir_nextkgc(J); |
| 287 | ir = IR(ref); |
| 288 | /* NOBARRIER: Current trace is a GC root. */ |
| 289 | ir->op12 = 0; |
| 290 | setgcref(ir[LJ_GC64].gcr, o); |
| 291 | ir->t.irt = (uint8_t)t; |
| 292 | ir->o = IR_KGC; |
| 293 | ir->prev = J->chain[IR_KGC]; |
| 294 | J->chain[IR_KGC] = (IRRef1)ref; |
| 295 | found: |
| 296 | return TREF(ref, t); |
| 297 | } |
| 298 | |
| 299 | /* Allocate GCtrace constant placeholder (no interning). */ |
| 300 | TRef lj_ir_ktrace(jit_State *J) |
| 301 | { |
| 302 | IRRef ref = ir_nextkgc(J); |
| 303 | IRIns *ir = IR(ref); |
| 304 | lj_assertJ(irt_toitype_(IRT_P64) == LJ_TTRACE, "mismatched type mapping" ); |
| 305 | ir->t.irt = IRT_P64; |
| 306 | ir->o = LJ_GC64 ? IR_KNUM : IR_KNULL; /* Not IR_KGC yet, but same size. */ |
| 307 | ir->op12 = 0; |
| 308 | ir->prev = 0; |
| 309 | return TREF(ref, IRT_P64); |
| 310 | } |
| 311 | |
| 312 | /* Intern pointer constant. */ |
| 313 | TRef lj_ir_kptr_(jit_State *J, IROp op, void *ptr) |
| 314 | { |
| 315 | IRIns *ir, *cir = J->cur.ir; |
| 316 | IRRef ref; |
| 317 | #if LJ_64 && !LJ_GC64 |
| 318 | lj_assertJ((void *)(uintptr_t)u32ptr(ptr) == ptr, "out-of-range GC pointer" ); |
| 319 | #endif |
| 320 | for (ref = J->chain[op]; ref; ref = cir[ref].prev) |
| 321 | if (ir_kptr(&cir[ref]) == ptr) |
| 322 | goto found; |
| 323 | #if LJ_GC64 |
| 324 | ref = ir_nextk64(J); |
| 325 | #else |
| 326 | ref = ir_nextk(J); |
| 327 | #endif |
| 328 | ir = IR(ref); |
| 329 | ir->op12 = 0; |
| 330 | setmref(ir[LJ_GC64].ptr, ptr); |
| 331 | ir->t.irt = IRT_PGC; |
| 332 | ir->o = op; |
| 333 | ir->prev = J->chain[op]; |
| 334 | J->chain[op] = (IRRef1)ref; |
| 335 | found: |
| 336 | return TREF(ref, IRT_PGC); |
| 337 | } |
| 338 | |
| 339 | /* Intern typed NULL constant. */ |
| 340 | TRef lj_ir_knull(jit_State *J, IRType t) |
| 341 | { |
| 342 | IRIns *ir, *cir = J->cur.ir; |
| 343 | IRRef ref; |
| 344 | for (ref = J->chain[IR_KNULL]; ref; ref = cir[ref].prev) |
| 345 | if (irt_t(cir[ref].t) == t) |
| 346 | goto found; |
| 347 | ref = ir_nextk(J); |
| 348 | ir = IR(ref); |
| 349 | ir->i = 0; |
| 350 | ir->t.irt = (uint8_t)t; |
| 351 | ir->o = IR_KNULL; |
| 352 | ir->prev = J->chain[IR_KNULL]; |
| 353 | J->chain[IR_KNULL] = (IRRef1)ref; |
| 354 | found: |
| 355 | return TREF(ref, t); |
| 356 | } |
| 357 | |
| 358 | /* Intern key slot. */ |
| 359 | TRef lj_ir_kslot(jit_State *J, TRef key, IRRef slot) |
| 360 | { |
| 361 | IRIns *ir, *cir = J->cur.ir; |
| 362 | IRRef2 op12 = IRREF2((IRRef1)key, (IRRef1)slot); |
| 363 | IRRef ref; |
| 364 | /* Const part is not touched by CSE/DCE, so 0-65535 is ok for IRMlit here. */ |
| 365 | lj_assertJ(tref_isk(key) && slot == (IRRef)(IRRef1)slot, |
| 366 | "out-of-range key/slot" ); |
| 367 | for (ref = J->chain[IR_KSLOT]; ref; ref = cir[ref].prev) |
| 368 | if (cir[ref].op12 == op12) |
| 369 | goto found; |
| 370 | ref = ir_nextk(J); |
| 371 | ir = IR(ref); |
| 372 | ir->op12 = op12; |
| 373 | ir->t.irt = IRT_P32; |
| 374 | ir->o = IR_KSLOT; |
| 375 | ir->prev = J->chain[IR_KSLOT]; |
| 376 | J->chain[IR_KSLOT] = (IRRef1)ref; |
| 377 | found: |
| 378 | return TREF(ref, IRT_P32); |
| 379 | } |
| 380 | |
| 381 | /* -- Access to IR constants ---------------------------------------------- */ |
| 382 | |
| 383 | /* Copy value of IR constant. */ |
| 384 | void lj_ir_kvalue(lua_State *L, TValue *tv, const IRIns *ir) |
| 385 | { |
| 386 | UNUSED(L); |
| 387 | lj_assertL(ir->o != IR_KSLOT, "unexpected KSLOT" ); /* Common mistake. */ |
| 388 | switch (ir->o) { |
| 389 | case IR_KPRI: setpriV(tv, irt_toitype(ir->t)); break; |
| 390 | case IR_KINT: setintV(tv, ir->i); break; |
| 391 | case IR_KGC: setgcV(L, tv, ir_kgc(ir), irt_toitype(ir->t)); break; |
| 392 | case IR_KPTR: case IR_KKPTR: |
| 393 | setnumV(tv, (lua_Number)(uintptr_t)ir_kptr(ir)); |
| 394 | break; |
| 395 | case IR_KNULL: setintV(tv, 0); break; |
| 396 | case IR_KNUM: setnumV(tv, ir_knum(ir)->n); break; |
| 397 | #if LJ_HASFFI |
| 398 | case IR_KINT64: { |
| 399 | GCcdata *cd = lj_cdata_new_(L, CTID_INT64, 8); |
| 400 | *(uint64_t *)cdataptr(cd) = ir_kint64(ir)->u64; |
| 401 | setcdataV(L, tv, cd); |
| 402 | break; |
| 403 | } |
| 404 | #endif |
| 405 | default: lj_assertL(0, "bad IR constant op %d" , ir->o); break; |
| 406 | } |
| 407 | } |
| 408 | |
| 409 | /* -- Convert IR operand types -------------------------------------------- */ |
| 410 | |
| 411 | /* Convert from string to number. */ |
| 412 | TRef LJ_FASTCALL lj_ir_tonumber(jit_State *J, TRef tr) |
| 413 | { |
| 414 | if (!tref_isnumber(tr)) { |
| 415 | if (tref_isstr(tr)) |
| 416 | tr = emitir(IRTG(IR_STRTO, IRT_NUM), tr, 0); |
| 417 | else |
| 418 | lj_trace_err(J, LJ_TRERR_BADTYPE); |
| 419 | } |
| 420 | return tr; |
| 421 | } |
| 422 | |
| 423 | /* Convert from integer or string to number. */ |
| 424 | TRef LJ_FASTCALL lj_ir_tonum(jit_State *J, TRef tr) |
| 425 | { |
| 426 | if (!tref_isnum(tr)) { |
| 427 | if (tref_isinteger(tr)) |
| 428 | tr = emitir(IRTN(IR_CONV), tr, IRCONV_NUM_INT); |
| 429 | else if (tref_isstr(tr)) |
| 430 | tr = emitir(IRTG(IR_STRTO, IRT_NUM), tr, 0); |
| 431 | else |
| 432 | lj_trace_err(J, LJ_TRERR_BADTYPE); |
| 433 | } |
| 434 | return tr; |
| 435 | } |
| 436 | |
| 437 | /* Convert from integer or number to string. */ |
| 438 | TRef LJ_FASTCALL lj_ir_tostr(jit_State *J, TRef tr) |
| 439 | { |
| 440 | if (!tref_isstr(tr)) { |
| 441 | if (!tref_isnumber(tr)) |
| 442 | lj_trace_err(J, LJ_TRERR_BADTYPE); |
| 443 | tr = emitir(IRT(IR_TOSTR, IRT_STR), tr, |
| 444 | tref_isnum(tr) ? IRTOSTR_NUM : IRTOSTR_INT); |
| 445 | } |
| 446 | return tr; |
| 447 | } |
| 448 | |
| 449 | /* -- Miscellaneous IR ops ------------------------------------------------ */ |
| 450 | |
| 451 | /* Evaluate numeric comparison. */ |
| 452 | int lj_ir_numcmp(lua_Number a, lua_Number b, IROp op) |
| 453 | { |
| 454 | switch (op) { |
| 455 | case IR_EQ: return (a == b); |
| 456 | case IR_NE: return (a != b); |
| 457 | case IR_LT: return (a < b); |
| 458 | case IR_GE: return (a >= b); |
| 459 | case IR_LE: return (a <= b); |
| 460 | case IR_GT: return (a > b); |
| 461 | case IR_ULT: return !(a >= b); |
| 462 | case IR_UGE: return !(a < b); |
| 463 | case IR_ULE: return !(a > b); |
| 464 | case IR_UGT: return !(a <= b); |
| 465 | default: lj_assertX(0, "bad IR op %d" , op); return 0; |
| 466 | } |
| 467 | } |
| 468 | |
| 469 | /* Evaluate string comparison. */ |
| 470 | int lj_ir_strcmp(GCstr *a, GCstr *b, IROp op) |
| 471 | { |
| 472 | int res = lj_str_cmp(a, b); |
| 473 | switch (op) { |
| 474 | case IR_LT: return (res < 0); |
| 475 | case IR_GE: return (res >= 0); |
| 476 | case IR_LE: return (res <= 0); |
| 477 | case IR_GT: return (res > 0); |
| 478 | default: lj_assertX(0, "bad IR op %d" , op); return 0; |
| 479 | } |
| 480 | } |
| 481 | |
| 482 | /* Rollback IR to previous state. */ |
| 483 | void lj_ir_rollback(jit_State *J, IRRef ref) |
| 484 | { |
| 485 | IRRef nins = J->cur.nins; |
| 486 | while (nins > ref) { |
| 487 | IRIns *ir; |
| 488 | nins--; |
| 489 | ir = IR(nins); |
| 490 | J->chain[ir->o] = ir->prev; |
| 491 | } |
| 492 | J->cur.nins = nins; |
| 493 | } |
| 494 | |
| 495 | #undef IR |
| 496 | #undef fins |
| 497 | #undef emitir |
| 498 | |
| 499 | #endif |
| 500 | |