diff options
| author | Mike Pall <mike> | 2010-12-05 17:12:34 +0100 |
|---|---|---|
| committer | Mike Pall <mike> | 2010-12-05 17:12:34 +0100 |
| commit | e7f8cc964e036147f0303c2653a77dfe24473dcc (patch) | |
| tree | 04710210a3280557a0528208440ba4b45341bebb /src | |
| parent | 559545eb6dc098594f957b09217ac3507b56eb3c (diff) | |
| download | luajit-e7f8cc964e036147f0303c2653a77dfe24473dcc.tar.gz luajit-e7f8cc964e036147f0303c2653a77dfe24473dcc.tar.bz2 luajit-e7f8cc964e036147f0303c2653a77dfe24473dcc.zip | |
Split off fast function recording to lj_ffrecord.c.
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile | 3 | ||||
| -rw-r--r-- | src/Makefile.dep | 21 | ||||
| -rw-r--r-- | src/lj_ffrecord.c | 803 | ||||
| -rw-r--r-- | src/lj_ffrecord.h | 17 | ||||
| -rw-r--r-- | src/lj_record.c | 864 | ||||
| -rw-r--r-- | src/lj_record.h | 26 | ||||
| -rw-r--r-- | src/ljamalg.c | 1 |
7 files changed, 899 insertions, 836 deletions
diff --git a/src/Makefile b/src/Makefile index ed9d3303..9dcb8a36 100644 --- a/src/Makefile +++ b/src/Makefile | |||
| @@ -326,7 +326,8 @@ LJCORE_O= lj_gc.o lj_err.o lj_char.o lj_bc.o lj_obj.o \ | |||
| 326 | lj_lex.o lj_parse.o \ | 326 | lj_lex.o lj_parse.o \ |
| 327 | lj_ir.o lj_opt_mem.o lj_opt_fold.o lj_opt_narrow.o \ | 327 | lj_ir.o lj_opt_mem.o lj_opt_fold.o lj_opt_narrow.o \ |
| 328 | lj_opt_dce.o lj_opt_loop.o \ | 328 | lj_opt_dce.o lj_opt_loop.o \ |
| 329 | lj_mcode.o lj_snap.o lj_record.o lj_asm.o lj_trace.o lj_gdbjit.o \ | 329 | lj_mcode.o lj_snap.o lj_record.o lj_ffrecord.o \ |
| 330 | lj_asm.o lj_trace.o lj_gdbjit.o \ | ||
| 330 | lj_ctype.o lj_cdata.o lj_cconv.o lj_cparse.o \ | 331 | lj_ctype.o lj_cdata.o lj_cconv.o lj_cparse.o \ |
| 331 | lj_lib.o lj_alloc.o lib_aux.o \ | 332 | lj_lib.o lj_alloc.o lib_aux.o \ |
| 332 | $(LJLIB_O) lib_init.o | 333 | $(LJLIB_O) lib_init.o |
diff --git a/src/Makefile.dep b/src/Makefile.dep index 145438db..9cfbd587 100644 --- a/src/Makefile.dep +++ b/src/Makefile.dep | |||
| @@ -71,6 +71,10 @@ lj_dispatch.o: lj_dispatch.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ | |||
| 71 | lj_err.o: lj_err.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_err.h \ | 71 | lj_err.o: lj_err.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_err.h \ |
| 72 | lj_errmsg.h lj_str.h lj_tab.h lj_func.h lj_state.h lj_frame.h lj_bc.h \ | 72 | lj_errmsg.h lj_str.h lj_tab.h lj_func.h lj_state.h lj_frame.h lj_bc.h \ |
| 73 | lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h lj_traceerr.h lj_vm.h | 73 | lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h lj_traceerr.h lj_vm.h |
| 74 | lj_ffrecord.o: lj_ffrecord.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ | ||
| 75 | lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_frame.h lj_bc.h lj_ff.h \ | ||
| 76 | lj_ffdef.h lj_ir.h lj_jit.h lj_iropt.h lj_trace.h lj_dispatch.h \ | ||
| 77 | lj_traceerr.h lj_record.h lj_vm.h lj_recdef.h | ||
| 74 | lj_func.o: lj_func.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ | 78 | lj_func.o: lj_func.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ |
| 75 | lj_func.h lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h lj_bc.h \ | 79 | lj_func.h lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h lj_bc.h \ |
| 76 | lj_traceerr.h lj_vm.h | 80 | lj_traceerr.h lj_vm.h |
| @@ -112,10 +116,9 @@ lj_parse.o: lj_parse.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ | |||
| 112 | lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_func.h lj_state.h \ | 116 | lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_func.h lj_state.h \ |
| 113 | lj_bc.h lj_lex.h lj_parse.h lj_vm.h lj_vmevent.h | 117 | lj_bc.h lj_lex.h lj_parse.h lj_vm.h lj_vmevent.h |
| 114 | lj_record.o: lj_record.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ | 118 | lj_record.o: lj_record.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
| 115 | lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_state.h lj_frame.h \ | 119 | lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_frame.h lj_bc.h lj_ff.h \ |
| 116 | lj_bc.h lj_ff.h lj_ffdef.h lj_ir.h lj_jit.h lj_iropt.h lj_trace.h \ | 120 | lj_ffdef.h lj_ir.h lj_jit.h lj_iropt.h lj_trace.h lj_dispatch.h \ |
| 117 | lj_dispatch.h lj_traceerr.h lj_record.h lj_snap.h lj_asm.h lj_vm.h \ | 121 | lj_traceerr.h lj_record.h lj_ffrecord.h lj_snap.h lj_vm.h |
| 118 | lj_recdef.h | ||
| 119 | lj_snap.o: lj_snap.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ | 122 | lj_snap.o: lj_snap.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ |
| 120 | lj_state.h lj_frame.h lj_bc.h lj_ir.h lj_jit.h lj_iropt.h lj_trace.h \ | 123 | lj_state.h lj_frame.h lj_bc.h lj_ir.h lj_jit.h lj_iropt.h lj_trace.h \ |
| 121 | lj_dispatch.h lj_traceerr.h lj_snap.h lj_target.h lj_target_*.h | 124 | lj_dispatch.h lj_traceerr.h lj_snap.h lj_target.h lj_target_*.h |
| @@ -148,9 +151,9 @@ ljamalg.o: ljamalg.c lua.h luaconf.h lauxlib.h lj_gc.c lj_obj.h lj_def.h \ | |||
| 148 | lj_cconv.c lj_cparse.c lj_cparse.h lj_lib.c lj_lib.h lj_ir.c lj_iropt.h \ | 151 | lj_cconv.c lj_cparse.c lj_cparse.h lj_lib.c lj_lib.h lj_ir.c lj_iropt.h \ |
| 149 | lj_opt_mem.c lj_opt_fold.c lj_folddef.h lj_opt_narrow.c lj_opt_dce.c \ | 152 | lj_opt_mem.c lj_opt_fold.c lj_folddef.h lj_opt_narrow.c lj_opt_dce.c \ |
| 150 | lj_opt_loop.c lj_snap.h lj_mcode.c lj_mcode.h lj_snap.c lj_target.h \ | 153 | lj_opt_loop.c lj_snap.h lj_mcode.c lj_mcode.h lj_snap.c lj_target.h \ |
| 151 | lj_target_*.h lj_record.c lj_record.h lj_asm.h lj_recdef.h lj_asm.c \ | 154 | lj_target_*.h lj_record.c lj_record.h lj_ffrecord.h lj_ffrecord.c \ |
| 152 | lj_trace.c lj_gdbjit.h lj_gdbjit.c lj_alloc.c lib_aux.c lib_base.c \ | 155 | lj_recdef.h lj_asm.c lj_asm.h lj_trace.c lj_gdbjit.h lj_gdbjit.c \ |
| 153 | lualib.h lj_libdef.h lib_math.c lib_string.c lib_table.c lib_io.c \ | 156 | lj_alloc.c lib_aux.c lib_base.c lualib.h lj_libdef.h lib_math.c \ |
| 154 | lib_os.c lib_package.c lib_debug.c lib_bit.c lib_jit.c lib_ffi.c \ | 157 | lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c \ |
| 155 | lib_init.c | 158 | lib_bit.c lib_jit.c lib_ffi.c lib_init.c |
| 156 | luajit.o: luajit.c lua.h luaconf.h lauxlib.h lualib.h luajit.h lj_arch.h | 159 | luajit.o: luajit.c lua.h luaconf.h lauxlib.h lualib.h luajit.h lj_arch.h |
diff --git a/src/lj_ffrecord.c b/src/lj_ffrecord.c new file mode 100644 index 00000000..0c8a41cd --- /dev/null +++ b/src/lj_ffrecord.c | |||
| @@ -0,0 +1,803 @@ | |||
| 1 | /* | ||
| 2 | ** Fast function call recorder. | ||
| 3 | ** Copyright (C) 2005-2010 Mike Pall. See Copyright Notice in luajit.h | ||
| 4 | */ | ||
| 5 | |||
| 6 | #define lj_ffrecord_c | ||
| 7 | #define LUA_CORE | ||
| 8 | |||
| 9 | #include "lj_obj.h" | ||
| 10 | |||
| 11 | #if LJ_HASJIT | ||
| 12 | |||
| 13 | #include "lj_err.h" | ||
| 14 | #include "lj_str.h" | ||
| 15 | #include "lj_tab.h" | ||
| 16 | #include "lj_frame.h" | ||
| 17 | #include "lj_bc.h" | ||
| 18 | #include "lj_ff.h" | ||
| 19 | #include "lj_ir.h" | ||
| 20 | #include "lj_jit.h" | ||
| 21 | #include "lj_iropt.h" | ||
| 22 | #include "lj_trace.h" | ||
| 23 | #include "lj_record.h" | ||
| 24 | #include "lj_dispatch.h" | ||
| 25 | #include "lj_vm.h" | ||
| 26 | |||
| 27 | /* Some local macros to save typing. Undef'd at the end. */ | ||
| 28 | #define IR(ref) (&J->cur.ir[(ref)]) | ||
| 29 | |||
| 30 | /* Pass IR on to next optimization in chain (FOLD). */ | ||
| 31 | #define emitir(ot, a, b) (lj_ir_set(J, (ot), (a), (b)), lj_opt_fold(J)) | ||
| 32 | |||
| 33 | /* -- Fast function recording handlers ------------------------------------ */ | ||
| 34 | |||
| 35 | /* Conventions for fast function call handlers: | ||
| 36 | ** | ||
| 37 | ** The argument slots start at J->base[0]. All of them are guaranteed to be | ||
| 38 | ** valid and type-specialized references. J->base[J->maxslot] is set to 0 | ||
| 39 | ** as a sentinel. The runtime argument values start at rd->argv[0]. | ||
| 40 | ** | ||
| 41 | ** In general fast functions should check for presence of all of their | ||
| 42 | ** arguments and for the correct argument types. Some simplifications | ||
| 43 | ** are allowed if the interpreter throws instead. But even if recording | ||
| 44 | ** is aborted, the generated IR must be consistent (no zero-refs). | ||
| 45 | ** | ||
| 46 | ** The number of results in rd->nres is set to 1. Handlers that return | ||
| 47 | ** a different number of results need to override it. A negative value | ||
| 48 | ** prevents return processing (e.g. for pending calls). | ||
| 49 | ** | ||
| 50 | ** Results need to be stored starting at J->base[0]. Return processing | ||
| 51 | ** moves them to the right slots later. | ||
| 52 | ** | ||
| 53 | ** The per-ffid auxiliary data is the value of the 2nd part of the | ||
| 54 | ** LJLIB_REC() annotation. This allows handling similar functionality | ||
| 55 | ** in a common handler. | ||
| 56 | */ | ||
| 57 | |||
| 58 | /* Data used by handlers to record a fast function. */ | ||
| 59 | typedef struct RecordFFData { | ||
| 60 | TValue *argv; /* Runtime argument values. */ | ||
| 61 | ptrdiff_t nres; /* Number of returned results (defaults to 1). */ | ||
| 62 | uint32_t data; /* Per-ffid auxiliary data (opcode, literal etc.). */ | ||
| 63 | } RecordFFData; | ||
| 64 | |||
| 65 | /* Type of handler to record a fast function. */ | ||
| 66 | typedef void (LJ_FASTCALL *RecordFunc)(jit_State *J, RecordFFData *rd); | ||
| 67 | |||
| 68 | /* Get runtime value of int argument. */ | ||
| 69 | static int32_t argv2int(jit_State *J, TValue *o) | ||
| 70 | { | ||
| 71 | if (!tvisnum(o) && !(tvisstr(o) && lj_str_tonum(strV(o), o))) | ||
| 72 | lj_trace_err(J, LJ_TRERR_BADTYPE); | ||
| 73 | return lj_num2bit(numV(o)); | ||
| 74 | } | ||
| 75 | |||
| 76 | /* Get runtime value of string argument. */ | ||
| 77 | static GCstr *argv2str(jit_State *J, TValue *o) | ||
| 78 | { | ||
| 79 | if (LJ_LIKELY(tvisstr(o))) { | ||
| 80 | return strV(o); | ||
| 81 | } else { | ||
| 82 | GCstr *s; | ||
| 83 | if (!tvisnum(o)) | ||
| 84 | lj_trace_err(J, LJ_TRERR_BADTYPE); | ||
| 85 | s = lj_str_fromnum(J->L, &o->n); | ||
| 86 | setstrV(J->L, o, s); | ||
| 87 | return s; | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | /* Return number of results wanted by caller. */ | ||
| 92 | static ptrdiff_t results_wanted(jit_State *J) | ||
| 93 | { | ||
| 94 | TValue *frame = J->L->base-1; | ||
| 95 | if (frame_islua(frame)) | ||
| 96 | return (ptrdiff_t)bc_b(frame_pc(frame)[-1]) - 1; | ||
| 97 | else | ||
| 98 | return -1; | ||
| 99 | } | ||
| 100 | |||
| 101 | /* Throw error for unsupported variant of fast function. */ | ||
| 102 | LJ_NORET static void recff_nyiu(jit_State *J) | ||
| 103 | { | ||
| 104 | setfuncV(J->L, &J->errinfo, J->fn); | ||
| 105 | lj_trace_err_info(J, LJ_TRERR_NYIFFU); | ||
| 106 | } | ||
| 107 | |||
| 108 | /* Fallback handler for all fast functions that are not recorded (yet). */ | ||
| 109 | static void LJ_FASTCALL recff_nyi(jit_State *J, RecordFFData *rd) | ||
| 110 | { | ||
| 111 | setfuncV(J->L, &J->errinfo, J->fn); | ||
| 112 | lj_trace_err_info(J, LJ_TRERR_NYIFF); | ||
| 113 | UNUSED(rd); | ||
| 114 | } | ||
| 115 | |||
| 116 | /* C functions can have arbitrary side-effects and are not recorded (yet). */ | ||
| 117 | static void LJ_FASTCALL recff_c(jit_State *J, RecordFFData *rd) | ||
| 118 | { | ||
| 119 | setfuncV(J->L, &J->errinfo, J->fn); | ||
| 120 | lj_trace_err_info(J, LJ_TRERR_NYICF); | ||
| 121 | UNUSED(rd); | ||
| 122 | } | ||
| 123 | |||
| 124 | /* -- Base library fast functions ----------------------------------------- */ | ||
| 125 | |||
| 126 | static void LJ_FASTCALL recff_assert(jit_State *J, RecordFFData *rd) | ||
| 127 | { | ||
| 128 | /* Arguments already specialized. The interpreter throws for nil/false. */ | ||
| 129 | rd->nres = J->maxslot; /* Pass through all arguments. */ | ||
| 130 | } | ||
| 131 | |||
| 132 | static void LJ_FASTCALL recff_type(jit_State *J, RecordFFData *rd) | ||
| 133 | { | ||
| 134 | /* Arguments already specialized. Result is a constant string. Neat, huh? */ | ||
| 135 | IRType t = tref_isinteger(J->base[0]) ? IRT_NUM : tref_type(J->base[0]); | ||
| 136 | J->base[0] = lj_ir_kstr(J, strV(&J->fn->c.upvalue[t])); | ||
| 137 | UNUSED(rd); | ||
| 138 | } | ||
| 139 | |||
| 140 | static void LJ_FASTCALL recff_getmetatable(jit_State *J, RecordFFData *rd) | ||
| 141 | { | ||
| 142 | TRef tr = J->base[0]; | ||
| 143 | if (tr) { | ||
| 144 | RecordIndex ix; | ||
| 145 | ix.tab = tr; | ||
| 146 | copyTV(J->L, &ix.tabv, &rd->argv[0]); | ||
| 147 | if (lj_record_mm_lookup(J, &ix, MM_metatable)) | ||
| 148 | J->base[0] = ix.mobj; | ||
| 149 | else | ||
| 150 | J->base[0] = ix.mt; | ||
| 151 | } /* else: Interpreter will throw. */ | ||
| 152 | } | ||
| 153 | |||
| 154 | static void LJ_FASTCALL recff_setmetatable(jit_State *J, RecordFFData *rd) | ||
| 155 | { | ||
| 156 | TRef tr = J->base[0]; | ||
| 157 | TRef mt = J->base[1]; | ||
| 158 | if (tref_istab(tr) && (tref_istab(mt) || (mt && tref_isnil(mt)))) { | ||
| 159 | TRef fref, mtref; | ||
| 160 | RecordIndex ix; | ||
| 161 | ix.tab = tr; | ||
| 162 | copyTV(J->L, &ix.tabv, &rd->argv[0]); | ||
| 163 | lj_record_mm_lookup(J, &ix, MM_metatable); /* Guard for no __metatable. */ | ||
| 164 | fref = emitir(IRT(IR_FREF, IRT_PTR), tr, IRFL_TAB_META); | ||
| 165 | mtref = tref_isnil(mt) ? lj_ir_knull(J, IRT_TAB) : mt; | ||
| 166 | emitir(IRT(IR_FSTORE, IRT_TAB), fref, mtref); | ||
| 167 | if (!tref_isnil(mt)) | ||
| 168 | emitir(IRT(IR_TBAR, IRT_TAB), tr, 0); | ||
| 169 | J->base[0] = tr; | ||
| 170 | J->needsnap = 1; | ||
| 171 | } /* else: Interpreter will throw. */ | ||
| 172 | } | ||
| 173 | |||
| 174 | static void LJ_FASTCALL recff_rawget(jit_State *J, RecordFFData *rd) | ||
| 175 | { | ||
| 176 | RecordIndex ix; | ||
| 177 | ix.tab = J->base[0]; ix.key = J->base[1]; | ||
| 178 | if (tref_istab(ix.tab) && ix.key) { | ||
| 179 | ix.val = 0; ix.idxchain = 0; | ||
| 180 | settabV(J->L, &ix.tabv, tabV(&rd->argv[0])); | ||
| 181 | copyTV(J->L, &ix.keyv, &rd->argv[1]); | ||
| 182 | J->base[0] = lj_record_idx(J, &ix); | ||
| 183 | } /* else: Interpreter will throw. */ | ||
| 184 | } | ||
| 185 | |||
| 186 | static void LJ_FASTCALL recff_rawset(jit_State *J, RecordFFData *rd) | ||
| 187 | { | ||
| 188 | RecordIndex ix; | ||
| 189 | ix.tab = J->base[0]; ix.key = J->base[1]; ix.val = J->base[2]; | ||
| 190 | if (tref_istab(ix.tab) && ix.key && ix.val) { | ||
| 191 | ix.idxchain = 0; | ||
| 192 | settabV(J->L, &ix.tabv, tabV(&rd->argv[0])); | ||
| 193 | copyTV(J->L, &ix.keyv, &rd->argv[1]); | ||
| 194 | copyTV(J->L, &ix.valv, &rd->argv[2]); | ||
| 195 | lj_record_idx(J, &ix); | ||
| 196 | /* Pass through table at J->base[0] as result. */ | ||
| 197 | } /* else: Interpreter will throw. */ | ||
| 198 | } | ||
| 199 | |||
| 200 | static void LJ_FASTCALL recff_rawequal(jit_State *J, RecordFFData *rd) | ||
| 201 | { | ||
| 202 | TRef tra = J->base[0]; | ||
| 203 | TRef trb = J->base[1]; | ||
| 204 | if (tra && trb) { | ||
| 205 | int diff = lj_record_objcmp(J, tra, trb, &rd->argv[0], &rd->argv[1]); | ||
| 206 | J->base[0] = diff ? TREF_FALSE : TREF_TRUE; | ||
| 207 | } /* else: Interpreter will throw. */ | ||
| 208 | } | ||
| 209 | |||
| 210 | /* Determine mode of select() call. */ | ||
| 211 | int32_t lj_ffrecord_select_mode(jit_State *J, TRef tr, TValue *tv) | ||
| 212 | { | ||
| 213 | if (tref_isstr(tr) && *strVdata(tv) == '#') { /* select('#', ...) */ | ||
| 214 | if (strV(tv)->len == 1) { | ||
| 215 | emitir(IRT(IR_EQ, IRT_STR), tr, lj_ir_kstr(J, strV(tv))); | ||
| 216 | } else { | ||
| 217 | TRef trptr = emitir(IRT(IR_STRREF, IRT_PTR), tr, 0); | ||
| 218 | TRef trchar = emitir(IRT(IR_XLOAD, IRT_U8), trptr, IRXLOAD_READONLY); | ||
| 219 | emitir(IRT(IR_EQ, IRT_INT), trchar, lj_ir_kint(J, '#')); | ||
| 220 | } | ||
| 221 | return 0; | ||
| 222 | } else { /* select(n, ...) */ | ||
| 223 | int32_t start = argv2int(J, tv); | ||
| 224 | if (start == 0) lj_trace_err(J, LJ_TRERR_BADTYPE); /* A bit misleading. */ | ||
| 225 | return start; | ||
| 226 | } | ||
| 227 | } | ||
| 228 | |||
| 229 | static void LJ_FASTCALL recff_select(jit_State *J, RecordFFData *rd) | ||
| 230 | { | ||
| 231 | TRef tr = J->base[0]; | ||
| 232 | if (tr) { | ||
| 233 | ptrdiff_t start = lj_ffrecord_select_mode(J, tr, &rd->argv[0]); | ||
| 234 | if (start == 0) { /* select('#', ...) */ | ||
| 235 | J->base[0] = lj_ir_kint(J, J->maxslot - 1); | ||
| 236 | } else if (tref_isk(tr)) { /* select(k, ...) */ | ||
| 237 | ptrdiff_t n = (ptrdiff_t)J->maxslot; | ||
| 238 | if (start < 0) start += n; | ||
| 239 | else if (start > n) start = n; | ||
| 240 | rd->nres = n - start; | ||
| 241 | if (start >= 1) { | ||
| 242 | ptrdiff_t i; | ||
| 243 | for (i = 0; i < n - start; i++) | ||
| 244 | J->base[i] = J->base[start+i]; | ||
| 245 | } /* else: Interpreter will throw. */ | ||
| 246 | } else { | ||
| 247 | recff_nyiu(J); | ||
| 248 | } | ||
| 249 | } /* else: Interpreter will throw. */ | ||
| 250 | } | ||
| 251 | |||
| 252 | static void LJ_FASTCALL recff_tonumber(jit_State *J, RecordFFData *rd) | ||
| 253 | { | ||
| 254 | TRef tr = J->base[0]; | ||
| 255 | if (tref_isnumber_str(tr)) { | ||
| 256 | TRef base = J->base[1]; | ||
| 257 | if (base) { | ||
| 258 | base = lj_ir_toint(J, base); | ||
| 259 | if (!tref_isk(base) || IR(tref_ref(base))->i != 10) | ||
| 260 | recff_nyiu(J); | ||
| 261 | } | ||
| 262 | if (tref_isstr(tr)) { | ||
| 263 | TValue tmp; | ||
| 264 | if (!lj_str_tonum(strV(&rd->argv[0]), &tmp)) | ||
| 265 | recff_nyiu(J); /* Would need an inverted STRTO for this case. */ | ||
| 266 | tr = emitir(IRTG(IR_STRTO, IRT_NUM), tr, 0); | ||
| 267 | } | ||
| 268 | } else { | ||
| 269 | tr = TREF_NIL; | ||
| 270 | } | ||
| 271 | J->base[0] = tr; | ||
| 272 | UNUSED(rd); | ||
| 273 | } | ||
| 274 | |||
| 275 | static TValue *recff_metacall_cp(lua_State *L, lua_CFunction dummy, void *ud) | ||
| 276 | { | ||
| 277 | jit_State *J = (jit_State *)ud; | ||
| 278 | lj_record_tailcall(J, 0, 1); | ||
| 279 | UNUSED(L); UNUSED(dummy); | ||
| 280 | return NULL; | ||
| 281 | } | ||
| 282 | |||
| 283 | static int recff_metacall(jit_State *J, RecordFFData *rd, MMS mm) | ||
| 284 | { | ||
| 285 | RecordIndex ix; | ||
| 286 | ix.tab = J->base[0]; | ||
| 287 | copyTV(J->L, &ix.tabv, &rd->argv[0]); | ||
| 288 | if (lj_record_mm_lookup(J, &ix, mm)) { /* Has metamethod? */ | ||
| 289 | int errcode; | ||
| 290 | /* Temporarily insert metamethod below object. */ | ||
| 291 | J->base[1] = J->base[0]; | ||
| 292 | J->base[0] = ix.mobj; | ||
| 293 | copyTV(J->L, &rd->argv[1], &rd->argv[0]); | ||
| 294 | copyTV(J->L, &rd->argv[0], &ix.mobjv); | ||
| 295 | /* Need to protect lj_record_tailcall because it may throw. */ | ||
| 296 | errcode = lj_vm_cpcall(J->L, NULL, J, recff_metacall_cp); | ||
| 297 | /* Always undo Lua stack changes to avoid confusing the interpreter. */ | ||
| 298 | copyTV(J->L, &rd->argv[0], &rd->argv[1]); | ||
| 299 | if (errcode) | ||
| 300 | lj_err_throw(J->L, errcode); /* Propagate errors. */ | ||
| 301 | rd->nres = -1; /* Pending call. */ | ||
| 302 | return 1; /* Tailcalled to metamethod. */ | ||
| 303 | } | ||
| 304 | return 0; | ||
| 305 | } | ||
| 306 | |||
| 307 | static void LJ_FASTCALL recff_tostring(jit_State *J, RecordFFData *rd) | ||
| 308 | { | ||
| 309 | TRef tr = J->base[0]; | ||
| 310 | if (tref_isstr(tr)) { | ||
| 311 | /* Ignore __tostring in the string base metatable. */ | ||
| 312 | /* Pass on result in J->base[0]. */ | ||
| 313 | } else if (!recff_metacall(J, rd, MM_tostring)) { | ||
| 314 | if (tref_isnumber(tr)) { | ||
| 315 | J->base[0] = emitir(IRT(IR_TOSTR, IRT_STR), tr, 0); | ||
| 316 | } else if (tref_ispri(tr)) { | ||
| 317 | J->base[0] = lj_ir_kstr(J, strV(&J->fn->c.upvalue[tref_type(tr)])); | ||
| 318 | } else { | ||
| 319 | recff_nyiu(J); | ||
| 320 | } | ||
| 321 | } | ||
| 322 | } | ||
| 323 | |||
| 324 | static void LJ_FASTCALL recff_ipairs_aux(jit_State *J, RecordFFData *rd) | ||
| 325 | { | ||
| 326 | RecordIndex ix; | ||
| 327 | ix.tab = J->base[0]; | ||
| 328 | if (tref_istab(ix.tab)) { | ||
| 329 | if (!tvisnum(&rd->argv[1])) /* No support for string coercion. */ | ||
| 330 | lj_trace_err(J, LJ_TRERR_BADTYPE); | ||
| 331 | setnumV(&ix.keyv, numV(&rd->argv[1])+(lua_Number)1); | ||
| 332 | settabV(J->L, &ix.tabv, tabV(&rd->argv[0])); | ||
| 333 | ix.val = 0; ix.idxchain = 0; | ||
| 334 | ix.key = lj_ir_toint(J, J->base[1]); | ||
| 335 | J->base[0] = ix.key = emitir(IRTI(IR_ADD), ix.key, lj_ir_kint(J, 1)); | ||
| 336 | J->base[1] = lj_record_idx(J, &ix); | ||
| 337 | rd->nres = tref_isnil(J->base[1]) ? 0 : 2; | ||
| 338 | } /* else: Interpreter will throw. */ | ||
| 339 | } | ||
| 340 | |||
| 341 | static void LJ_FASTCALL recff_ipairs(jit_State *J, RecordFFData *rd) | ||
| 342 | { | ||
| 343 | #ifdef LUAJIT_ENABLE_LUA52COMPAT | ||
| 344 | if (!recff_metacall(J, rd, MM_ipairs)) | ||
| 345 | #endif | ||
| 346 | { | ||
| 347 | TRef tab = J->base[0]; | ||
| 348 | if (tref_istab(tab)) { | ||
| 349 | J->base[0] = lj_ir_kfunc(J, funcV(&J->fn->c.upvalue[0])); | ||
| 350 | J->base[1] = tab; | ||
| 351 | J->base[2] = lj_ir_kint(J, 0); | ||
| 352 | rd->nres = 3; | ||
| 353 | } /* else: Interpreter will throw. */ | ||
| 354 | } | ||
| 355 | } | ||
| 356 | |||
| 357 | static void LJ_FASTCALL recff_pcall(jit_State *J, RecordFFData *rd) | ||
| 358 | { | ||
| 359 | if (J->maxslot >= 1) { | ||
| 360 | lj_record_call(J, 0, J->maxslot - 1); | ||
| 361 | rd->nres = -1; /* Pending call. */ | ||
| 362 | } /* else: Interpreter will throw. */ | ||
| 363 | } | ||
| 364 | |||
| 365 | static TValue *recff_xpcall_cp(lua_State *L, lua_CFunction dummy, void *ud) | ||
| 366 | { | ||
| 367 | jit_State *J = (jit_State *)ud; | ||
| 368 | lj_record_call(J, 1, J->maxslot - 2); | ||
| 369 | UNUSED(L); UNUSED(dummy); | ||
| 370 | return NULL; | ||
| 371 | } | ||
| 372 | |||
| 373 | static void LJ_FASTCALL recff_xpcall(jit_State *J, RecordFFData *rd) | ||
| 374 | { | ||
| 375 | if (J->maxslot >= 2) { | ||
| 376 | TValue argv0, argv1; | ||
| 377 | TRef tmp; | ||
| 378 | int errcode; | ||
| 379 | /* Swap function and traceback. */ | ||
| 380 | tmp = J->base[0]; J->base[0] = J->base[1]; J->base[1] = tmp; | ||
| 381 | copyTV(J->L, &argv0, &rd->argv[0]); | ||
| 382 | copyTV(J->L, &argv1, &rd->argv[1]); | ||
| 383 | copyTV(J->L, &rd->argv[0], &argv1); | ||
| 384 | copyTV(J->L, &rd->argv[1], &argv0); | ||
| 385 | /* Need to protect lj_record_call because it may throw. */ | ||
| 386 | errcode = lj_vm_cpcall(J->L, NULL, J, recff_xpcall_cp); | ||
| 387 | /* Always undo Lua stack swap to avoid confusing the interpreter. */ | ||
| 388 | copyTV(J->L, &rd->argv[0], &argv0); | ||
| 389 | copyTV(J->L, &rd->argv[1], &argv1); | ||
| 390 | if (errcode) | ||
| 391 | lj_err_throw(J->L, errcode); /* Propagate errors. */ | ||
| 392 | rd->nres = -1; /* Pending call. */ | ||
| 393 | } /* else: Interpreter will throw. */ | ||
| 394 | } | ||
| 395 | |||
| 396 | /* -- Math library fast functions ----------------------------------------- */ | ||
| 397 | |||
| 398 | static void LJ_FASTCALL recff_math_abs(jit_State *J, RecordFFData *rd) | ||
| 399 | { | ||
| 400 | TRef tr = lj_ir_tonum(J, J->base[0]); | ||
| 401 | J->base[0] = emitir(IRTN(IR_ABS), tr, lj_ir_knum_abs(J)); | ||
| 402 | UNUSED(rd); | ||
| 403 | } | ||
| 404 | |||
| 405 | /* Record rounding functions math.floor and math.ceil. */ | ||
| 406 | static void LJ_FASTCALL recff_math_round(jit_State *J, RecordFFData *rd) | ||
| 407 | { | ||
| 408 | if (!tref_isinteger(J->base[0])) /* Pass through integers unmodified. */ | ||
| 409 | J->base[0] = emitir(IRTN(IR_FPMATH), lj_ir_tonum(J, J->base[0]), rd->data); | ||
| 410 | /* Note: result is integral (or NaN/Inf), but may not fit into an integer. */ | ||
| 411 | } | ||
| 412 | |||
| 413 | /* Record unary math.* functions, mapped to IR_FPMATH opcode. */ | ||
| 414 | static void LJ_FASTCALL recff_math_unary(jit_State *J, RecordFFData *rd) | ||
| 415 | { | ||
| 416 | J->base[0] = emitir(IRTN(IR_FPMATH), lj_ir_tonum(J, J->base[0]), rd->data); | ||
| 417 | } | ||
| 418 | |||
| 419 | /* Record binary math.* functions math.atan2 and math.ldexp. */ | ||
| 420 | static void LJ_FASTCALL recff_math_binary(jit_State *J, RecordFFData *rd) | ||
| 421 | { | ||
| 422 | TRef tr = lj_ir_tonum(J, J->base[0]); | ||
| 423 | J->base[0] = emitir(IRTN(rd->data), tr, lj_ir_tonum(J, J->base[1])); | ||
| 424 | } | ||
| 425 | |||
| 426 | /* Record math.asin, math.acos, math.atan. */ | ||
| 427 | static void LJ_FASTCALL recff_math_atrig(jit_State *J, RecordFFData *rd) | ||
| 428 | { | ||
| 429 | TRef y = lj_ir_tonum(J, J->base[0]); | ||
| 430 | TRef x = lj_ir_knum_one(J); | ||
| 431 | uint32_t ffid = rd->data; | ||
| 432 | if (ffid != FF_math_atan) { | ||
| 433 | TRef tmp = emitir(IRTN(IR_MUL), y, y); | ||
| 434 | tmp = emitir(IRTN(IR_SUB), x, tmp); | ||
| 435 | tmp = emitir(IRTN(IR_FPMATH), tmp, IRFPM_SQRT); | ||
| 436 | if (ffid == FF_math_asin) { x = tmp; } else { x = y; y = tmp; } | ||
| 437 | } | ||
| 438 | J->base[0] = emitir(IRTN(IR_ATAN2), y, x); | ||
| 439 | } | ||
| 440 | |||
| 441 | static void LJ_FASTCALL recff_math_htrig(jit_State *J, RecordFFData *rd) | ||
| 442 | { | ||
| 443 | TRef tr = lj_ir_tonum(J, J->base[0]); | ||
| 444 | J->base[0] = lj_ir_call(J, rd->data, tr); | ||
| 445 | } | ||
| 446 | |||
| 447 | static void LJ_FASTCALL recff_math_modf(jit_State *J, RecordFFData *rd) | ||
| 448 | { | ||
| 449 | TRef tr = J->base[0]; | ||
| 450 | if (tref_isinteger(tr)) { | ||
| 451 | J->base[0] = tr; | ||
| 452 | J->base[1] = lj_ir_kint(J, 0); | ||
| 453 | } else { | ||
| 454 | TRef trt; | ||
| 455 | tr = lj_ir_tonum(J, tr); | ||
| 456 | trt = emitir(IRTN(IR_FPMATH), tr, IRFPM_TRUNC); | ||
| 457 | J->base[0] = trt; | ||
| 458 | J->base[1] = emitir(IRTN(IR_SUB), tr, trt); | ||
| 459 | } | ||
| 460 | rd->nres = 2; | ||
| 461 | } | ||
| 462 | |||
| 463 | static void LJ_FASTCALL recff_math_degrad(jit_State *J, RecordFFData *rd) | ||
| 464 | { | ||
| 465 | TRef tr = lj_ir_tonum(J, J->base[0]); | ||
| 466 | TRef trm = lj_ir_knum(J, numV(&J->fn->c.upvalue[0])); | ||
| 467 | J->base[0] = emitir(IRTN(IR_MUL), tr, trm); | ||
| 468 | UNUSED(rd); | ||
| 469 | } | ||
| 470 | |||
| 471 | static void LJ_FASTCALL recff_math_pow(jit_State *J, RecordFFData *rd) | ||
| 472 | { | ||
| 473 | TRef tr = lj_ir_tonum(J, J->base[0]); | ||
| 474 | if (!tref_isnumber_str(J->base[1])) | ||
| 475 | lj_trace_err(J, LJ_TRERR_BADTYPE); | ||
| 476 | J->base[0] = lj_opt_narrow_pow(J, tr, J->base[1], &rd->argv[1]); | ||
| 477 | UNUSED(rd); | ||
| 478 | } | ||
| 479 | |||
| 480 | static void LJ_FASTCALL recff_math_minmax(jit_State *J, RecordFFData *rd) | ||
| 481 | { | ||
| 482 | TRef tr = lj_ir_tonum(J, J->base[0]); | ||
| 483 | uint32_t op = rd->data; | ||
| 484 | BCReg i; | ||
| 485 | for (i = 1; J->base[i] != 0; i++) | ||
| 486 | tr = emitir(IRTN(op), tr, lj_ir_tonum(J, J->base[i])); | ||
| 487 | J->base[0] = tr; | ||
| 488 | } | ||
| 489 | |||
| 490 | static void LJ_FASTCALL recff_math_random(jit_State *J, RecordFFData *rd) | ||
| 491 | { | ||
| 492 | GCudata *ud = udataV(&J->fn->c.upvalue[0]); | ||
| 493 | TRef tr, one; | ||
| 494 | lj_ir_kgc(J, obj2gco(ud), IRT_UDATA); /* Prevent collection. */ | ||
| 495 | tr = lj_ir_call(J, IRCALL_lj_math_random_step, lj_ir_kptr(J, uddata(ud))); | ||
| 496 | one = lj_ir_knum_one(J); | ||
| 497 | tr = emitir(IRTN(IR_SUB), tr, one); | ||
| 498 | if (J->base[0]) { | ||
| 499 | TRef tr1 = lj_ir_tonum(J, J->base[0]); | ||
| 500 | if (J->base[1]) { /* d = floor(d*(r2-r1+1.0)) + r1 */ | ||
| 501 | TRef tr2 = lj_ir_tonum(J, J->base[1]); | ||
| 502 | tr2 = emitir(IRTN(IR_SUB), tr2, tr1); | ||
| 503 | tr2 = emitir(IRTN(IR_ADD), tr2, one); | ||
| 504 | tr = emitir(IRTN(IR_MUL), tr, tr2); | ||
| 505 | tr = emitir(IRTN(IR_FPMATH), tr, IRFPM_FLOOR); | ||
| 506 | tr = emitir(IRTN(IR_ADD), tr, tr1); | ||
| 507 | } else { /* d = floor(d*r1) + 1.0 */ | ||
| 508 | tr = emitir(IRTN(IR_MUL), tr, tr1); | ||
| 509 | tr = emitir(IRTN(IR_FPMATH), tr, IRFPM_FLOOR); | ||
| 510 | tr = emitir(IRTN(IR_ADD), tr, one); | ||
| 511 | } | ||
| 512 | } | ||
| 513 | J->base[0] = tr; | ||
| 514 | UNUSED(rd); | ||
| 515 | } | ||
| 516 | |||
| 517 | /* -- Bit library fast functions ------------------------------------------ */ | ||
| 518 | |||
| 519 | /* Record unary bit.tobit, bit.bnot, bit.bswap. */ | ||
| 520 | static void LJ_FASTCALL recff_bit_unary(jit_State *J, RecordFFData *rd) | ||
| 521 | { | ||
| 522 | TRef tr = lj_ir_tobit(J, J->base[0]); | ||
| 523 | J->base[0] = (rd->data == IR_TOBIT) ? tr : emitir(IRTI(rd->data), tr, 0); | ||
| 524 | } | ||
| 525 | |||
| 526 | /* Record N-ary bit.band, bit.bor, bit.bxor. */ | ||
| 527 | static void LJ_FASTCALL recff_bit_nary(jit_State *J, RecordFFData *rd) | ||
| 528 | { | ||
| 529 | TRef tr = lj_ir_tobit(J, J->base[0]); | ||
| 530 | uint32_t op = rd->data; | ||
| 531 | BCReg i; | ||
| 532 | for (i = 1; J->base[i] != 0; i++) | ||
| 533 | tr = emitir(IRTI(op), tr, lj_ir_tobit(J, J->base[i])); | ||
| 534 | J->base[0] = tr; | ||
| 535 | } | ||
| 536 | |||
| 537 | /* Record bit shifts. */ | ||
| 538 | static void LJ_FASTCALL recff_bit_shift(jit_State *J, RecordFFData *rd) | ||
| 539 | { | ||
| 540 | TRef tr = lj_ir_tobit(J, J->base[0]); | ||
| 541 | TRef tsh = lj_ir_tobit(J, J->base[1]); | ||
| 542 | if (!(rd->data < IR_BROL ? LJ_TARGET_MASKSHIFT : LJ_TARGET_MASKROT) && | ||
| 543 | !tref_isk(tsh)) | ||
| 544 | tsh = emitir(IRTI(IR_BAND), tsh, lj_ir_kint(J, 31)); | ||
| 545 | J->base[0] = emitir(IRTI(rd->data), tr, tsh); | ||
| 546 | } | ||
| 547 | |||
| 548 | /* -- String library fast functions --------------------------------------- */ | ||
| 549 | |||
| 550 | static void LJ_FASTCALL recff_string_len(jit_State *J, RecordFFData *rd) | ||
| 551 | { | ||
| 552 | J->base[0] = emitir(IRTI(IR_FLOAD), lj_ir_tostr(J, J->base[0]), IRFL_STR_LEN); | ||
| 553 | UNUSED(rd); | ||
| 554 | } | ||
| 555 | |||
| 556 | /* Handle string.byte (rd->data = 0) and string.sub (rd->data = 1). */ | ||
| 557 | static void LJ_FASTCALL recff_string_range(jit_State *J, RecordFFData *rd) | ||
| 558 | { | ||
| 559 | TRef trstr = lj_ir_tostr(J, J->base[0]); | ||
| 560 | TRef trlen = emitir(IRTI(IR_FLOAD), trstr, IRFL_STR_LEN); | ||
| 561 | TRef tr0 = lj_ir_kint(J, 0); | ||
| 562 | TRef trstart, trend; | ||
| 563 | GCstr *str = argv2str(J, &rd->argv[0]); | ||
| 564 | int32_t start, end; | ||
| 565 | if (rd->data) { /* string.sub(str, start [,end]) */ | ||
| 566 | start = argv2int(J, &rd->argv[1]); | ||
| 567 | trstart = lj_ir_toint(J, J->base[1]); | ||
| 568 | trend = J->base[2]; | ||
| 569 | if (tref_isnil(trend)) { | ||
| 570 | trend = lj_ir_kint(J, -1); | ||
| 571 | end = -1; | ||
| 572 | } else { | ||
| 573 | trend = lj_ir_toint(J, trend); | ||
| 574 | end = argv2int(J, &rd->argv[2]); | ||
| 575 | } | ||
| 576 | } else { /* string.byte(str, [,start [,end]]) */ | ||
| 577 | if (J->base[1]) { | ||
| 578 | start = argv2int(J, &rd->argv[1]); | ||
| 579 | trstart = lj_ir_toint(J, J->base[1]); | ||
| 580 | trend = J->base[2]; | ||
| 581 | if (tref_isnil(trend)) { | ||
| 582 | trend = trstart; | ||
| 583 | end = start; | ||
| 584 | } else { | ||
| 585 | trend = lj_ir_toint(J, trend); | ||
| 586 | end = argv2int(J, &rd->argv[2]); | ||
| 587 | } | ||
| 588 | } else { | ||
| 589 | trend = trstart = lj_ir_kint(J, 1); | ||
| 590 | end = start = 1; | ||
| 591 | } | ||
| 592 | } | ||
| 593 | if (end < 0) { | ||
| 594 | emitir(IRTGI(IR_LT), trend, tr0); | ||
| 595 | trend = emitir(IRTI(IR_ADD), emitir(IRTI(IR_ADD), trlen, trend), | ||
| 596 | lj_ir_kint(J, 1)); | ||
| 597 | end = end+(int32_t)str->len+1; | ||
| 598 | } else if ((MSize)end <= str->len) { | ||
| 599 | emitir(IRTGI(IR_ULE), trend, trlen); | ||
| 600 | } else { | ||
| 601 | emitir(IRTGI(IR_GT), trend, trlen); | ||
| 602 | end = (int32_t)str->len; | ||
| 603 | trend = trlen; | ||
| 604 | } | ||
| 605 | if (start < 0) { | ||
| 606 | emitir(IRTGI(IR_LT), trstart, tr0); | ||
| 607 | trstart = emitir(IRTI(IR_ADD), trlen, trstart); | ||
| 608 | start = start+(int32_t)str->len; | ||
| 609 | emitir(start < 0 ? IRTGI(IR_LT) : IRTGI(IR_GE), trstart, tr0); | ||
| 610 | if (start < 0) { | ||
| 611 | trstart = tr0; | ||
| 612 | start = 0; | ||
| 613 | } | ||
| 614 | } else { | ||
| 615 | if (start == 0) { | ||
| 616 | emitir(IRTGI(IR_EQ), trstart, tr0); | ||
| 617 | trstart = tr0; | ||
| 618 | } else { | ||
| 619 | trstart = emitir(IRTI(IR_ADD), trstart, lj_ir_kint(J, -1)); | ||
| 620 | emitir(IRTGI(IR_GE), trstart, tr0); | ||
| 621 | start--; | ||
| 622 | } | ||
| 623 | } | ||
| 624 | if (rd->data) { /* Return string.sub result. */ | ||
| 625 | if (end - start >= 0) { | ||
| 626 | /* Also handle empty range here, to avoid extra traces. */ | ||
| 627 | TRef trptr, trslen = emitir(IRTI(IR_SUB), trend, trstart); | ||
| 628 | emitir(IRTGI(IR_GE), trslen, tr0); | ||
| 629 | trptr = emitir(IRT(IR_STRREF, IRT_PTR), trstr, trstart); | ||
| 630 | J->base[0] = emitir(IRT(IR_SNEW, IRT_STR), trptr, trslen); | ||
| 631 | } else { /* Range underflow: return empty string. */ | ||
| 632 | emitir(IRTGI(IR_LT), trend, trstart); | ||
| 633 | J->base[0] = lj_ir_kstr(J, lj_str_new(J->L, strdata(str), 0)); | ||
| 634 | } | ||
| 635 | } else { /* Return string.byte result(s). */ | ||
| 636 | ptrdiff_t i, len = end - start; | ||
| 637 | if (len > 0) { | ||
| 638 | TRef trslen = emitir(IRTI(IR_SUB), trend, trstart); | ||
| 639 | emitir(IRTGI(IR_EQ), trslen, lj_ir_kint(J, (int32_t)len)); | ||
| 640 | if (J->baseslot + len > LJ_MAX_JSLOTS) | ||
| 641 | lj_trace_err_info(J, LJ_TRERR_STACKOV); | ||
| 642 | rd->nres = len; | ||
| 643 | for (i = 0; i < len; i++) { | ||
| 644 | TRef tmp = emitir(IRTI(IR_ADD), trstart, lj_ir_kint(J, (int32_t)i)); | ||
| 645 | tmp = emitir(IRT(IR_STRREF, IRT_PTR), trstr, tmp); | ||
| 646 | J->base[i] = emitir(IRT(IR_XLOAD, IRT_U8), tmp, IRXLOAD_READONLY); | ||
| 647 | } | ||
| 648 | } else { /* Empty range or range underflow: return no results. */ | ||
| 649 | emitir(IRTGI(IR_LE), trend, trstart); | ||
| 650 | rd->nres = 0; | ||
| 651 | } | ||
| 652 | } | ||
| 653 | } | ||
| 654 | |||
| 655 | /* -- Table library fast functions ---------------------------------------- */ | ||
| 656 | |||
| 657 | static void LJ_FASTCALL recff_table_getn(jit_State *J, RecordFFData *rd) | ||
| 658 | { | ||
| 659 | if (tref_istab(J->base[0])) | ||
| 660 | J->base[0] = lj_ir_call(J, IRCALL_lj_tab_len, J->base[0]); | ||
| 661 | /* else: Interpreter will throw. */ | ||
| 662 | UNUSED(rd); | ||
| 663 | } | ||
| 664 | |||
| 665 | static void LJ_FASTCALL recff_table_remove(jit_State *J, RecordFFData *rd) | ||
| 666 | { | ||
| 667 | TRef tab = J->base[0]; | ||
| 668 | rd->nres = 0; | ||
| 669 | if (tref_istab(tab)) { | ||
| 670 | if (!J->base[1] || tref_isnil(J->base[1])) { /* Simple pop: t[#t] = nil */ | ||
| 671 | TRef trlen = lj_ir_call(J, IRCALL_lj_tab_len, tab); | ||
| 672 | GCtab *t = tabV(&rd->argv[0]); | ||
| 673 | MSize len = lj_tab_len(t); | ||
| 674 | emitir(IRTGI(len ? IR_NE : IR_EQ), trlen, lj_ir_kint(J, 0)); | ||
| 675 | if (len) { | ||
| 676 | RecordIndex ix; | ||
| 677 | ix.tab = tab; | ||
| 678 | ix.key = trlen; | ||
| 679 | settabV(J->L, &ix.tabv, t); | ||
| 680 | setintV(&ix.keyv, len); | ||
| 681 | ix.idxchain = 0; | ||
| 682 | if (results_wanted(J) != 0) { /* Specialize load only if needed. */ | ||
| 683 | ix.val = 0; | ||
| 684 | J->base[0] = lj_record_idx(J, &ix); /* Load previous value. */ | ||
| 685 | rd->nres = 1; | ||
| 686 | /* Assumes ix.key/ix.tab is not modified for raw lj_record_idx(). */ | ||
| 687 | } | ||
| 688 | ix.val = TREF_NIL; | ||
| 689 | lj_record_idx(J, &ix); /* Remove value. */ | ||
| 690 | } | ||
| 691 | } else { /* Complex case: remove in the middle. */ | ||
| 692 | recff_nyiu(J); | ||
| 693 | } | ||
| 694 | } /* else: Interpreter will throw. */ | ||
| 695 | } | ||
| 696 | |||
| 697 | static void LJ_FASTCALL recff_table_insert(jit_State *J, RecordFFData *rd) | ||
| 698 | { | ||
| 699 | RecordIndex ix; | ||
| 700 | ix.tab = J->base[0]; | ||
| 701 | ix.val = J->base[1]; | ||
| 702 | rd->nres = 0; | ||
| 703 | if (tref_istab(ix.tab) && ix.val) { | ||
| 704 | if (!J->base[2]) { /* Simple push: t[#t+1] = v */ | ||
| 705 | TRef trlen = lj_ir_call(J, IRCALL_lj_tab_len, ix.tab); | ||
| 706 | GCtab *t = tabV(&rd->argv[0]); | ||
| 707 | ix.key = emitir(IRTI(IR_ADD), trlen, lj_ir_kint(J, 1)); | ||
| 708 | settabV(J->L, &ix.tabv, t); | ||
| 709 | setintV(&ix.keyv, lj_tab_len(t) + 1); | ||
| 710 | ix.idxchain = 0; | ||
| 711 | lj_record_idx(J, &ix); /* Set new value. */ | ||
| 712 | } else { /* Complex case: insert in the middle. */ | ||
| 713 | recff_nyiu(J); | ||
| 714 | } | ||
| 715 | } /* else: Interpreter will throw. */ | ||
| 716 | } | ||
| 717 | |||
| 718 | /* -- I/O library fast functions ------------------------------------------ */ | ||
| 719 | |||
| 720 | /* Get FILE* for I/O function. Any I/O error aborts recording, so there's | ||
| 721 | ** no need to encode the alternate cases for any of the guards. | ||
| 722 | */ | ||
| 723 | static TRef recff_io_fp(jit_State *J, uint32_t id) | ||
| 724 | { | ||
| 725 | TRef tr, ud, fp; | ||
| 726 | if (id) { /* io.func() */ | ||
| 727 | tr = lj_ir_kptr(J, &J2G(J)->gcroot[id]); | ||
| 728 | ud = emitir(IRT(IR_XLOAD, IRT_UDATA), tr, 0); | ||
| 729 | } else { /* fp:method() */ | ||
| 730 | ud = J->base[0]; | ||
| 731 | if (!tref_isudata(ud)) | ||
| 732 | lj_trace_err(J, LJ_TRERR_BADTYPE); | ||
| 733 | tr = emitir(IRT(IR_FLOAD, IRT_U8), ud, IRFL_UDATA_UDTYPE); | ||
| 734 | emitir(IRTGI(IR_EQ), tr, lj_ir_kint(J, UDTYPE_IO_FILE)); | ||
| 735 | } | ||
| 736 | fp = emitir(IRT(IR_FLOAD, IRT_LIGHTUD), ud, IRFL_UDATA_FILE); | ||
| 737 | emitir(IRTG(IR_NE, IRT_LIGHTUD), fp, lj_ir_knull(J, IRT_LIGHTUD)); | ||
| 738 | return fp; | ||
| 739 | } | ||
| 740 | |||
| 741 | static void LJ_FASTCALL recff_io_write(jit_State *J, RecordFFData *rd) | ||
| 742 | { | ||
| 743 | TRef fp = recff_io_fp(J, rd->data); | ||
| 744 | TRef zero = lj_ir_kint(J, 0); | ||
| 745 | TRef one = lj_ir_kint(J, 1); | ||
| 746 | ptrdiff_t i = rd->data == 0 ? 1 : 0; | ||
| 747 | for (; J->base[i]; i++) { | ||
| 748 | TRef str = lj_ir_tostr(J, J->base[i]); | ||
| 749 | TRef buf = emitir(IRT(IR_STRREF, IRT_PTR), str, zero); | ||
| 750 | TRef len = emitir(IRTI(IR_FLOAD), str, IRFL_STR_LEN); | ||
| 751 | if (tref_isk(len) && IR(tref_ref(len))->i == 1) { | ||
| 752 | TRef tr = emitir(IRT(IR_XLOAD, IRT_U8), buf, IRXLOAD_READONLY); | ||
| 753 | tr = lj_ir_call(J, IRCALL_fputc, tr, fp); | ||
| 754 | if (results_wanted(J) != 0) /* Check result only if not ignored. */ | ||
| 755 | emitir(IRTGI(IR_NE), tr, lj_ir_kint(J, -1)); | ||
| 756 | } else { | ||
| 757 | TRef tr = lj_ir_call(J, IRCALL_fwrite, buf, one, len, fp); | ||
| 758 | if (results_wanted(J) != 0) /* Check result only if not ignored. */ | ||
| 759 | emitir(IRTGI(IR_EQ), tr, len); | ||
| 760 | } | ||
| 761 | } | ||
| 762 | J->base[0] = TREF_TRUE; | ||
| 763 | } | ||
| 764 | |||
| 765 | static void LJ_FASTCALL recff_io_flush(jit_State *J, RecordFFData *rd) | ||
| 766 | { | ||
| 767 | TRef fp = recff_io_fp(J, rd->data); | ||
| 768 | TRef tr = lj_ir_call(J, IRCALL_fflush, fp); | ||
| 769 | if (results_wanted(J) != 0) /* Check result only if not ignored. */ | ||
| 770 | emitir(IRTGI(IR_EQ), tr, lj_ir_kint(J, 0)); | ||
| 771 | J->base[0] = TREF_TRUE; | ||
| 772 | } | ||
| 773 | |||
| 774 | /* -- Record calls to fast functions -------------------------------------- */ | ||
| 775 | |||
| 776 | #include "lj_recdef.h" | ||
| 777 | |||
| 778 | static uint32_t recdef_lookup(GCfunc *fn) | ||
| 779 | { | ||
| 780 | if (fn->c.ffid < sizeof(recff_idmap)/sizeof(recff_idmap[0])) | ||
| 781 | return recff_idmap[fn->c.ffid]; | ||
| 782 | else | ||
| 783 | return 0; | ||
| 784 | } | ||
| 785 | |||
| 786 | /* Record entry to a fast function or C function. */ | ||
| 787 | void lj_ffrecord_func(jit_State *J) | ||
| 788 | { | ||
| 789 | RecordFFData rd; | ||
| 790 | uint32_t m = recdef_lookup(J->fn); | ||
| 791 | rd.data = m & 0xff; | ||
| 792 | rd.nres = 1; /* Default is one result. */ | ||
| 793 | rd.argv = J->L->base; | ||
| 794 | J->base[J->maxslot] = 0; /* Mark end of arguments. */ | ||
| 795 | (recff_func[m >> 8])(J, &rd); /* Call recff_* handler. */ | ||
| 796 | if (rd.nres >= 0) | ||
| 797 | lj_record_ret(J, 0, rd.nres); | ||
| 798 | } | ||
| 799 | |||
| 800 | #undef IR | ||
| 801 | #undef emitir | ||
| 802 | |||
| 803 | #endif | ||
diff --git a/src/lj_ffrecord.h b/src/lj_ffrecord.h new file mode 100644 index 00000000..f13f5c33 --- /dev/null +++ b/src/lj_ffrecord.h | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | /* | ||
| 2 | ** Fast function call recorder. | ||
| 3 | ** Copyright (C) 2005-2010 Mike Pall. See Copyright Notice in luajit.h | ||
| 4 | */ | ||
| 5 | |||
| 6 | #ifndef _LJ_FFRECORD_H | ||
| 7 | #define _LJ_FFRECORD_H | ||
| 8 | |||
| 9 | #include "lj_obj.h" | ||
| 10 | #include "lj_jit.h" | ||
| 11 | |||
| 12 | #if LJ_HASJIT | ||
| 13 | LJ_FUNC int32_t lj_ffrecord_select_mode(jit_State *J, TRef tr, TValue *tv); | ||
| 14 | LJ_FUNC void lj_ffrecord_func(jit_State *J); | ||
| 15 | #endif | ||
| 16 | |||
| 17 | #endif | ||
diff --git a/src/lj_record.c b/src/lj_record.c index 621311c1..90c06218 100644 --- a/src/lj_record.c +++ b/src/lj_record.c | |||
| @@ -10,11 +10,9 @@ | |||
| 10 | 10 | ||
| 11 | #if LJ_HASJIT | 11 | #if LJ_HASJIT |
| 12 | 12 | ||
| 13 | #include "lj_gc.h" | ||
| 14 | #include "lj_err.h" | 13 | #include "lj_err.h" |
| 15 | #include "lj_str.h" | 14 | #include "lj_str.h" |
| 16 | #include "lj_tab.h" | 15 | #include "lj_tab.h" |
| 17 | #include "lj_state.h" | ||
| 18 | #include "lj_frame.h" | 16 | #include "lj_frame.h" |
| 19 | #include "lj_bc.h" | 17 | #include "lj_bc.h" |
| 20 | #include "lj_ff.h" | 18 | #include "lj_ff.h" |
| @@ -23,8 +21,8 @@ | |||
| 23 | #include "lj_iropt.h" | 21 | #include "lj_iropt.h" |
| 24 | #include "lj_trace.h" | 22 | #include "lj_trace.h" |
| 25 | #include "lj_record.h" | 23 | #include "lj_record.h" |
| 24 | #include "lj_ffrecord.h" | ||
| 26 | #include "lj_snap.h" | 25 | #include "lj_snap.h" |
| 27 | #include "lj_asm.h" | ||
| 28 | #include "lj_dispatch.h" | 26 | #include "lj_dispatch.h" |
| 29 | #include "lj_vm.h" | 27 | #include "lj_vm.h" |
| 30 | 28 | ||
| @@ -37,26 +35,6 @@ | |||
| 37 | /* Emit raw IR without passing through optimizations. */ | 35 | /* Emit raw IR without passing through optimizations. */ |
| 38 | #define emitir_raw(ot, a, b) (lj_ir_set(J, (ot), (a), (b)), lj_ir_emit(J)) | 36 | #define emitir_raw(ot, a, b) (lj_ir_set(J, (ot), (a), (b)), lj_ir_emit(J)) |
| 39 | 37 | ||
| 40 | /* Context for recording an indexed load/store. */ | ||
| 41 | typedef struct RecordIndex { | ||
| 42 | TValue tabv; /* Runtime value of table (or indexed object). */ | ||
| 43 | TValue keyv; /* Runtime value of key. */ | ||
| 44 | TValue valv; /* Runtime value of stored value. */ | ||
| 45 | TValue mobjv; /* Runtime value of metamethod object. */ | ||
| 46 | GCtab *mtv; /* Runtime value of metatable object. */ | ||
| 47 | cTValue *oldv; /* Runtime value of previously stored value. */ | ||
| 48 | TRef tab; /* Table (or indexed object) reference. */ | ||
| 49 | TRef key; /* Key reference. */ | ||
| 50 | TRef val; /* Value reference for a store or 0 for a load. */ | ||
| 51 | TRef mt; /* Metatable reference. */ | ||
| 52 | TRef mobj; /* Metamethod object reference. */ | ||
| 53 | int idxchain; /* Index indirections left or 0 for raw lookup. */ | ||
| 54 | } RecordIndex; | ||
| 55 | |||
| 56 | /* Forward declarations. */ | ||
| 57 | static int rec_mm_lookup(jit_State *J, RecordIndex *ix, MMS mm); | ||
| 58 | static TRef rec_idx(jit_State *J, RecordIndex *ix); | ||
| 59 | |||
| 60 | /* -- Sanity checks ------------------------------------------------------- */ | 38 | /* -- Sanity checks ------------------------------------------------------- */ |
| 61 | 39 | ||
| 62 | #ifdef LUA_USE_ASSERT | 40 | #ifdef LUA_USE_ASSERT |
| @@ -182,7 +160,7 @@ static TRef getcurrf(jit_State *J) | |||
| 182 | ** Returns 2 for two different types. | 160 | ** Returns 2 for two different types. |
| 183 | ** Comparisons between primitives always return 1 -- no caller cares about it. | 161 | ** Comparisons between primitives always return 1 -- no caller cares about it. |
| 184 | */ | 162 | */ |
| 185 | static int rec_objcmp(jit_State *J, TRef a, TRef b, cTValue *av, cTValue *bv) | 163 | int lj_record_objcmp(jit_State *J, TRef a, TRef b, cTValue *av, cTValue *bv) |
| 186 | { | 164 | { |
| 187 | int diff = !lj_obj_equal(av, bv); | 165 | int diff = !lj_obj_equal(av, bv); |
| 188 | if (!tref_isk2(a, b)) { /* Shortcut, also handles primitives. */ | 166 | if (!tref_isk2(a, b)) { /* Shortcut, also handles primitives. */ |
| @@ -492,7 +470,7 @@ static void rec_call_setup(jit_State *J, BCReg func, ptrdiff_t nargs) | |||
| 492 | if (!tref_isfunc(fbase[0])) { /* Resolve __call metamethod. */ | 470 | if (!tref_isfunc(fbase[0])) { /* Resolve __call metamethod. */ |
| 493 | ix.tab = fbase[0]; | 471 | ix.tab = fbase[0]; |
| 494 | copyTV(J->L, &ix.tabv, functv); | 472 | copyTV(J->L, &ix.tabv, functv); |
| 495 | if (!rec_mm_lookup(J, &ix, MM_call) || !tref_isfunc(ix.mobj)) | 473 | if (!lj_record_mm_lookup(J, &ix, MM_call) || !tref_isfunc(ix.mobj)) |
| 496 | lj_trace_err(J, LJ_TRERR_NOMM); | 474 | lj_trace_err(J, LJ_TRERR_NOMM); |
| 497 | for (i = ++nargs; i > 0; i--) /* Shift arguments up. */ | 475 | for (i = ++nargs; i > 0; i--) /* Shift arguments up. */ |
| 498 | fbase[i] = fbase[i-1]; | 476 | fbase[i] = fbase[i-1]; |
| @@ -508,7 +486,7 @@ static void rec_call_setup(jit_State *J, BCReg func, ptrdiff_t nargs) | |||
| 508 | } | 486 | } |
| 509 | 487 | ||
| 510 | /* Record call. */ | 488 | /* Record call. */ |
| 511 | static void rec_call(jit_State *J, BCReg func, ptrdiff_t nargs) | 489 | void lj_record_call(jit_State *J, BCReg func, ptrdiff_t nargs) |
| 512 | { | 490 | { |
| 513 | rec_call_setup(J, func, nargs); | 491 | rec_call_setup(J, func, nargs); |
| 514 | /* Bump frame. */ | 492 | /* Bump frame. */ |
| @@ -518,7 +496,7 @@ static void rec_call(jit_State *J, BCReg func, ptrdiff_t nargs) | |||
| 518 | } | 496 | } |
| 519 | 497 | ||
| 520 | /* Record tail call. */ | 498 | /* Record tail call. */ |
| 521 | static void rec_tailcall(jit_State *J, BCReg func, ptrdiff_t nargs) | 499 | void lj_record_tailcall(jit_State *J, BCReg func, ptrdiff_t nargs) |
| 522 | { | 500 | { |
| 523 | rec_call_setup(J, func, nargs); | 501 | rec_call_setup(J, func, nargs); |
| 524 | if (frame_isvarg(J->L->base - 1)) { | 502 | if (frame_isvarg(J->L->base - 1)) { |
| @@ -561,7 +539,7 @@ static int check_downrec_unroll(jit_State *J, GCproto *pt) | |||
| 561 | } | 539 | } |
| 562 | 540 | ||
| 563 | /* Record return. */ | 541 | /* Record return. */ |
| 564 | static void rec_ret(jit_State *J, BCReg rbase, ptrdiff_t gotresults) | 542 | void lj_record_ret(jit_State *J, BCReg rbase, ptrdiff_t gotresults) |
| 565 | { | 543 | { |
| 566 | TValue *frame = J->L->base - 1; | 544 | TValue *frame = J->L->base - 1; |
| 567 | ptrdiff_t i; | 545 | ptrdiff_t i; |
| @@ -672,7 +650,7 @@ static BCReg rec_mm_prep(jit_State *J, ASMFunction cont) | |||
| 672 | } | 650 | } |
| 673 | 651 | ||
| 674 | /* Record metamethod lookup. */ | 652 | /* Record metamethod lookup. */ |
| 675 | static int rec_mm_lookup(jit_State *J, RecordIndex *ix, MMS mm) | 653 | int lj_record_mm_lookup(jit_State *J, RecordIndex *ix, MMS mm) |
| 676 | { | 654 | { |
| 677 | RecordIndex mix; | 655 | RecordIndex mix; |
| 678 | GCtab *mt; | 656 | GCtab *mt; |
| @@ -706,7 +684,7 @@ nocheck: | |||
| 706 | mix.key = lj_ir_kstr(J, mmstr); | 684 | mix.key = lj_ir_kstr(J, mmstr); |
| 707 | mix.val = 0; | 685 | mix.val = 0; |
| 708 | mix.idxchain = 0; | 686 | mix.idxchain = 0; |
| 709 | ix->mobj = rec_idx(J, &mix); | 687 | ix->mobj = lj_record_idx(J, &mix); |
| 710 | return !tref_isnil(ix->mobj); /* 1 if metamethod found, 0 if not. */ | 688 | return !tref_isnil(ix->mobj); /* 1 if metamethod found, 0 if not. */ |
| 711 | } | 689 | } |
| 712 | return 0; /* No metamethod. */ | 690 | return 0; /* No metamethod. */ |
| @@ -722,11 +700,11 @@ static TRef rec_mm_arith(jit_State *J, RecordIndex *ix, MMS mm) | |||
| 722 | base[1] = ix->tab; base[2] = ix->key; | 700 | base[1] = ix->tab; base[2] = ix->key; |
| 723 | copyTV(J->L, basev+1, &ix->tabv); | 701 | copyTV(J->L, basev+1, &ix->tabv); |
| 724 | copyTV(J->L, basev+2, &ix->keyv); | 702 | copyTV(J->L, basev+2, &ix->keyv); |
| 725 | if (!rec_mm_lookup(J, ix, mm)) { /* Lookup metamethod on 1st operand. */ | 703 | if (!lj_record_mm_lookup(J, ix, mm)) { /* Lookup mm on 1st operand. */ |
| 726 | if (mm != MM_len) { | 704 | if (mm != MM_len) { |
| 727 | ix->tab = ix->key; | 705 | ix->tab = ix->key; |
| 728 | copyTV(J->L, &ix->tabv, &ix->keyv); | 706 | copyTV(J->L, &ix->tabv, &ix->keyv); |
| 729 | if (rec_mm_lookup(J, ix, mm)) /* Lookup metamethod on 2nd operand. */ | 707 | if (lj_record_mm_lookup(J, ix, mm)) /* Lookup mm on 2nd operand. */ |
| 730 | goto ok; | 708 | goto ok; |
| 731 | } | 709 | } |
| 732 | lj_trace_err(J, LJ_TRERR_NOMM); | 710 | lj_trace_err(J, LJ_TRERR_NOMM); |
| @@ -734,7 +712,7 @@ static TRef rec_mm_arith(jit_State *J, RecordIndex *ix, MMS mm) | |||
| 734 | ok: | 712 | ok: |
| 735 | base[0] = ix->mobj; | 713 | base[0] = ix->mobj; |
| 736 | copyTV(J->L, basev+0, &ix->mobjv); | 714 | copyTV(J->L, basev+0, &ix->mobjv); |
| 737 | rec_call(J, func, 2); | 715 | lj_record_call(J, func, 2); |
| 738 | return 0; /* No result yet. */ | 716 | return 0; /* No result yet. */ |
| 739 | } | 717 | } |
| 740 | 718 | ||
| @@ -748,7 +726,7 @@ static void rec_mm_callcomp(jit_State *J, RecordIndex *ix, int op) | |||
| 748 | copyTV(J->L, tv+0, &ix->mobjv); | 726 | copyTV(J->L, tv+0, &ix->mobjv); |
| 749 | copyTV(J->L, tv+1, &ix->valv); | 727 | copyTV(J->L, tv+1, &ix->valv); |
| 750 | copyTV(J->L, tv+2, &ix->keyv); | 728 | copyTV(J->L, tv+2, &ix->keyv); |
| 751 | rec_call(J, func, 2); | 729 | lj_record_call(J, func, 2); |
| 752 | } | 730 | } |
| 753 | 731 | ||
| 754 | /* Record call to equality comparison metamethod (for tab and udata only). */ | 732 | /* Record call to equality comparison metamethod (for tab and udata only). */ |
| @@ -756,7 +734,7 @@ static void rec_mm_equal(jit_State *J, RecordIndex *ix, int op) | |||
| 756 | { | 734 | { |
| 757 | ix->tab = ix->val; | 735 | ix->tab = ix->val; |
| 758 | copyTV(J->L, &ix->tabv, &ix->valv); | 736 | copyTV(J->L, &ix->tabv, &ix->valv); |
| 759 | if (rec_mm_lookup(J, ix, MM_eq)) { /* Lookup metamethod on 1st operand. */ | 737 | if (lj_record_mm_lookup(J, ix, MM_eq)) { /* Lookup mm on 1st operand. */ |
| 760 | cTValue *bv; | 738 | cTValue *bv; |
| 761 | TRef mo1 = ix->mobj; | 739 | TRef mo1 = ix->mobj; |
| 762 | TValue mo1v; | 740 | TValue mo1v; |
| @@ -772,8 +750,8 @@ static void rec_mm_equal(jit_State *J, RecordIndex *ix, int op) | |||
| 772 | } else { /* Lookup metamethod on 2nd operand and compare both. */ | 750 | } else { /* Lookup metamethod on 2nd operand and compare both. */ |
| 773 | ix->tab = ix->key; | 751 | ix->tab = ix->key; |
| 774 | copyTV(J->L, &ix->tabv, bv); | 752 | copyTV(J->L, &ix->tabv, bv); |
| 775 | if (!rec_mm_lookup(J, ix, MM_eq) || | 753 | if (!lj_record_mm_lookup(J, ix, MM_eq) || |
| 776 | rec_objcmp(J, mo1, ix->mobj, &mo1v, &ix->mobjv)) | 754 | lj_record_objcmp(J, mo1, ix->mobj, &mo1v, &ix->mobjv)) |
| 777 | return; | 755 | return; |
| 778 | } | 756 | } |
| 779 | rec_mm_callcomp(J, ix, op); | 757 | rec_mm_callcomp(J, ix, op); |
| @@ -787,7 +765,7 @@ static void rec_mm_comp(jit_State *J, RecordIndex *ix, int op) | |||
| 787 | copyTV(J->L, &ix->tabv, &ix->valv); | 765 | copyTV(J->L, &ix->tabv, &ix->valv); |
| 788 | while (1) { | 766 | while (1) { |
| 789 | MMS mm = (op & 2) ? MM_le : MM_lt; /* Try __le + __lt or only __lt. */ | 767 | MMS mm = (op & 2) ? MM_le : MM_lt; /* Try __le + __lt or only __lt. */ |
| 790 | if (rec_mm_lookup(J, ix, mm)) { /* Lookup metamethod on 1st operand. */ | 768 | if (lj_record_mm_lookup(J, ix, mm)) { /* Lookup mm on 1st operand. */ |
| 791 | cTValue *bv; | 769 | cTValue *bv; |
| 792 | TRef mo1 = ix->mobj; | 770 | TRef mo1 = ix->mobj; |
| 793 | TValue mo1v; | 771 | TValue mo1v; |
| @@ -803,8 +781,8 @@ static void rec_mm_comp(jit_State *J, RecordIndex *ix, int op) | |||
| 803 | } else { /* Lookup metamethod on 2nd operand and compare both. */ | 781 | } else { /* Lookup metamethod on 2nd operand and compare both. */ |
| 804 | ix->tab = ix->key; | 782 | ix->tab = ix->key; |
| 805 | copyTV(J->L, &ix->tabv, bv); | 783 | copyTV(J->L, &ix->tabv, bv); |
| 806 | if (!rec_mm_lookup(J, ix, mm) || | 784 | if (!lj_record_mm_lookup(J, ix, mm) || |
| 807 | rec_objcmp(J, mo1, ix->mobj, &mo1v, &ix->mobjv)) | 785 | lj_record_objcmp(J, mo1, ix->mobj, &mo1v, &ix->mobjv)) |
| 808 | goto nomatch; | 786 | goto nomatch; |
| 809 | } | 787 | } |
| 810 | rec_mm_callcomp(J, ix, op); | 788 | rec_mm_callcomp(J, ix, op); |
| @@ -943,15 +921,16 @@ static int nommstr(jit_State *J, TRef key) | |||
| 943 | } | 921 | } |
| 944 | 922 | ||
| 945 | /* Record indexed load/store. */ | 923 | /* Record indexed load/store. */ |
| 946 | static TRef rec_idx(jit_State *J, RecordIndex *ix) | 924 | TRef lj_record_idx(jit_State *J, RecordIndex *ix) |
| 947 | { | 925 | { |
| 948 | TRef xref; | 926 | TRef xref; |
| 949 | IROp xrefop, loadop; | 927 | IROp xrefop, loadop; |
| 950 | cTValue *oldv; | 928 | cTValue *oldv; |
| 951 | 929 | ||
| 952 | while (!tref_istab(ix->tab)) { /* Handle non-table lookup. */ | 930 | while (!tref_istab(ix->tab)) { /* Handle non-table lookup. */ |
| 953 | lua_assert(ix->idxchain != 0); /* Never call raw rec_idx() on non-table. */ | 931 | /* Never call raw lj_record_idx() on non-table. */ |
| 954 | if (!rec_mm_lookup(J, ix, ix->val ? MM_newindex : MM_index)) | 932 | lua_assert(ix->idxchain != 0); |
| 933 | if (!lj_record_mm_lookup(J, ix, ix->val ? MM_newindex : MM_index)) | ||
| 955 | lj_trace_err(J, LJ_TRERR_NOMM); | 934 | lj_trace_err(J, LJ_TRERR_NOMM); |
| 956 | handlemm: | 935 | handlemm: |
| 957 | if (tref_isfunc(ix->mobj)) { /* Handle metamethod call. */ | 936 | if (tref_isfunc(ix->mobj)) { /* Handle metamethod call. */ |
| @@ -965,10 +944,10 @@ static TRef rec_idx(jit_State *J, RecordIndex *ix) | |||
| 965 | if (ix->val) { | 944 | if (ix->val) { |
| 966 | base[3] = ix->val; | 945 | base[3] = ix->val; |
| 967 | copyTV(J->L, tv+3, &ix->valv); | 946 | copyTV(J->L, tv+3, &ix->valv); |
| 968 | rec_call(J, func, 3); /* mobj(tab, key, val) */ | 947 | lj_record_call(J, func, 3); /* mobj(tab, key, val) */ |
| 969 | return 0; | 948 | return 0; |
| 970 | } else { | 949 | } else { |
| 971 | rec_call(J, func, 2); /* res = mobj(tab, key) */ | 950 | lj_record_call(J, func, 2); /* res = mobj(tab, key) */ |
| 972 | return 0; /* No result yet. */ | 951 | return 0; /* No result yet. */ |
| 973 | } | 952 | } |
| 974 | } | 953 | } |
| @@ -984,7 +963,7 @@ static TRef rec_idx(jit_State *J, RecordIndex *ix) | |||
| 984 | if (ix->val) /* Better fail early. */ | 963 | if (ix->val) /* Better fail early. */ |
| 985 | lj_trace_err(J, LJ_TRERR_STORENN); | 964 | lj_trace_err(J, LJ_TRERR_STORENN); |
| 986 | if (tref_isk(ix->key)) { | 965 | if (tref_isk(ix->key)) { |
| 987 | if (ix->idxchain && rec_mm_lookup(J, ix, MM_index)) | 966 | if (ix->idxchain && lj_record_mm_lookup(J, ix, MM_index)) |
| 988 | goto handlemm; | 967 | goto handlemm; |
| 989 | return TREF_NIL; | 968 | return TREF_NIL; |
| 990 | } | 969 | } |
| @@ -1005,7 +984,7 @@ static TRef rec_idx(jit_State *J, RecordIndex *ix) | |||
| 1005 | } else { | 984 | } else { |
| 1006 | res = emitir(IRTG(loadop, t), xref, 0); | 985 | res = emitir(IRTG(loadop, t), xref, 0); |
| 1007 | } | 986 | } |
| 1008 | if (t == IRT_NIL && ix->idxchain && rec_mm_lookup(J, ix, MM_index)) | 987 | if (t == IRT_NIL && ix->idxchain && lj_record_mm_lookup(J, ix, MM_index)) |
| 1009 | goto handlemm; | 988 | goto handlemm; |
| 1010 | if (irtype_ispri(t)) res = TREF_PRI(t); /* Canonicalize primitives. */ | 989 | if (irtype_ispri(t)) res = TREF_PRI(t); /* Canonicalize primitives. */ |
| 1011 | return res; | 990 | return res; |
| @@ -1024,7 +1003,7 @@ static TRef rec_idx(jit_State *J, RecordIndex *ix) | |||
| 1024 | else if (xrefop == IR_HREF) | 1003 | else if (xrefop == IR_HREF) |
| 1025 | emitir(IRTG(oldv == niltvg(J2G(J)) ? IR_EQ : IR_NE, IRT_PTR), | 1004 | emitir(IRTG(oldv == niltvg(J2G(J)) ? IR_EQ : IR_NE, IRT_PTR), |
| 1026 | xref, lj_ir_kptr(J, niltvg(J2G(J)))); | 1005 | xref, lj_ir_kptr(J, niltvg(J2G(J)))); |
| 1027 | if (ix->idxchain && rec_mm_lookup(J, ix, MM_newindex)) { /* Metamethod? */ | 1006 | if (ix->idxchain && lj_record_mm_lookup(J, ix, MM_newindex)) { |
| 1028 | lua_assert(hasmm); | 1007 | lua_assert(hasmm); |
| 1029 | goto handlemm; | 1008 | goto handlemm; |
| 1030 | } | 1009 | } |
| @@ -1117,773 +1096,6 @@ static TRef rec_upvalue(jit_State *J, uint32_t uv, TRef val) | |||
| 1117 | } | 1096 | } |
| 1118 | } | 1097 | } |
| 1119 | 1098 | ||
| 1120 | /* -- Fast function recording handlers ------------------------------------ */ | ||
| 1121 | |||
| 1122 | /* Conventions for fast function call handlers: | ||
| 1123 | ** | ||
| 1124 | ** The argument slots start at J->base[0]. All of them are guaranteed to be | ||
| 1125 | ** valid and type-specialized references. J->base[J->maxslot] is set to 0 | ||
| 1126 | ** as a sentinel. The runtime argument values start at rd->argv[0]. | ||
| 1127 | ** | ||
| 1128 | ** In general fast functions should check for presence of all of their | ||
| 1129 | ** arguments and for the correct argument types. Some simplifications | ||
| 1130 | ** are allowed if the interpreter throws instead. But even if recording | ||
| 1131 | ** is aborted, the generated IR must be consistent (no zero-refs). | ||
| 1132 | ** | ||
| 1133 | ** The number of results in rd->nres is set to 1. Handlers that return | ||
| 1134 | ** a different number of results need to override it. A negative value | ||
| 1135 | ** prevents return processing (e.g. for pending calls). | ||
| 1136 | ** | ||
| 1137 | ** Results need to be stored starting at J->base[0]. Return processing | ||
| 1138 | ** moves them to the right slots later. | ||
| 1139 | ** | ||
| 1140 | ** The per-ffid auxiliary data is the value of the 2nd part of the | ||
| 1141 | ** LJLIB_REC() annotation. This allows handling similar functionality | ||
| 1142 | ** in a common handler. | ||
| 1143 | */ | ||
| 1144 | |||
| 1145 | /* Data used by handlers to record a fast function. */ | ||
| 1146 | typedef struct RecordFFData { | ||
| 1147 | TValue *argv; /* Runtime argument values. */ | ||
| 1148 | ptrdiff_t nres; /* Number of returned results (defaults to 1). */ | ||
| 1149 | uint32_t data; /* Per-ffid auxiliary data (opcode, literal etc.). */ | ||
| 1150 | } RecordFFData; | ||
| 1151 | |||
| 1152 | /* Type of handler to record a fast function. */ | ||
| 1153 | typedef void (LJ_FASTCALL *RecordFunc)(jit_State *J, RecordFFData *rd); | ||
| 1154 | |||
| 1155 | /* Get runtime value of int argument. */ | ||
| 1156 | static int32_t argv2int(jit_State *J, TValue *o) | ||
| 1157 | { | ||
| 1158 | if (!tvisnum(o) && !(tvisstr(o) && lj_str_tonum(strV(o), o))) | ||
| 1159 | lj_trace_err(J, LJ_TRERR_BADTYPE); | ||
| 1160 | return lj_num2bit(numV(o)); | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | /* Get runtime value of string argument. */ | ||
| 1164 | static GCstr *argv2str(jit_State *J, TValue *o) | ||
| 1165 | { | ||
| 1166 | if (LJ_LIKELY(tvisstr(o))) { | ||
| 1167 | return strV(o); | ||
| 1168 | } else { | ||
| 1169 | GCstr *s; | ||
| 1170 | if (!tvisnum(o)) | ||
| 1171 | lj_trace_err(J, LJ_TRERR_BADTYPE); | ||
| 1172 | s = lj_str_fromnum(J->L, &o->n); | ||
| 1173 | setstrV(J->L, o, s); | ||
| 1174 | return s; | ||
| 1175 | } | ||
| 1176 | } | ||
| 1177 | |||
| 1178 | /* Return number of results wanted by caller. */ | ||
| 1179 | static ptrdiff_t results_wanted(jit_State *J) | ||
| 1180 | { | ||
| 1181 | TValue *frame = J->L->base-1; | ||
| 1182 | if (frame_islua(frame)) | ||
| 1183 | return (ptrdiff_t)bc_b(frame_pc(frame)[-1]) - 1; | ||
| 1184 | else | ||
| 1185 | return -1; | ||
| 1186 | } | ||
| 1187 | |||
| 1188 | /* Throw error for unsupported variant of fast function. */ | ||
| 1189 | LJ_NORET static void recff_nyiu(jit_State *J) | ||
| 1190 | { | ||
| 1191 | setfuncV(J->L, &J->errinfo, J->fn); | ||
| 1192 | lj_trace_err_info(J, LJ_TRERR_NYIFFU); | ||
| 1193 | } | ||
| 1194 | |||
| 1195 | /* Fallback handler for all fast functions that are not recorded (yet). */ | ||
| 1196 | static void LJ_FASTCALL recff_nyi(jit_State *J, RecordFFData *rd) | ||
| 1197 | { | ||
| 1198 | setfuncV(J->L, &J->errinfo, J->fn); | ||
| 1199 | lj_trace_err_info(J, LJ_TRERR_NYIFF); | ||
| 1200 | UNUSED(rd); | ||
| 1201 | } | ||
| 1202 | |||
| 1203 | /* C functions can have arbitrary side-effects and are not recorded (yet). */ | ||
| 1204 | static void LJ_FASTCALL recff_c(jit_State *J, RecordFFData *rd) | ||
| 1205 | { | ||
| 1206 | setfuncV(J->L, &J->errinfo, J->fn); | ||
| 1207 | lj_trace_err_info(J, LJ_TRERR_NYICF); | ||
| 1208 | UNUSED(rd); | ||
| 1209 | } | ||
| 1210 | |||
| 1211 | /* -- Base library fast functions ----------------------------------------- */ | ||
| 1212 | |||
| 1213 | static void LJ_FASTCALL recff_assert(jit_State *J, RecordFFData *rd) | ||
| 1214 | { | ||
| 1215 | /* Arguments already specialized. The interpreter throws for nil/false. */ | ||
| 1216 | rd->nres = J->maxslot; /* Pass through all arguments. */ | ||
| 1217 | } | ||
| 1218 | |||
| 1219 | static void LJ_FASTCALL recff_type(jit_State *J, RecordFFData *rd) | ||
| 1220 | { | ||
| 1221 | /* Arguments already specialized. Result is a constant string. Neat, huh? */ | ||
| 1222 | IRType t = tref_isinteger(J->base[0]) ? IRT_NUM : tref_type(J->base[0]); | ||
| 1223 | J->base[0] = lj_ir_kstr(J, strV(&J->fn->c.upvalue[t])); | ||
| 1224 | UNUSED(rd); | ||
| 1225 | } | ||
| 1226 | |||
| 1227 | static void LJ_FASTCALL recff_getmetatable(jit_State *J, RecordFFData *rd) | ||
| 1228 | { | ||
| 1229 | TRef tr = J->base[0]; | ||
| 1230 | if (tr) { | ||
| 1231 | RecordIndex ix; | ||
| 1232 | ix.tab = tr; | ||
| 1233 | copyTV(J->L, &ix.tabv, &rd->argv[0]); | ||
| 1234 | if (rec_mm_lookup(J, &ix, MM_metatable)) | ||
| 1235 | J->base[0] = ix.mobj; | ||
| 1236 | else | ||
| 1237 | J->base[0] = ix.mt; | ||
| 1238 | } /* else: Interpreter will throw. */ | ||
| 1239 | } | ||
| 1240 | |||
| 1241 | static void LJ_FASTCALL recff_setmetatable(jit_State *J, RecordFFData *rd) | ||
| 1242 | { | ||
| 1243 | TRef tr = J->base[0]; | ||
| 1244 | TRef mt = J->base[1]; | ||
| 1245 | if (tref_istab(tr) && (tref_istab(mt) || (mt && tref_isnil(mt)))) { | ||
| 1246 | TRef fref, mtref; | ||
| 1247 | RecordIndex ix; | ||
| 1248 | ix.tab = tr; | ||
| 1249 | copyTV(J->L, &ix.tabv, &rd->argv[0]); | ||
| 1250 | rec_mm_lookup(J, &ix, MM_metatable); /* Guard for no __metatable field. */ | ||
| 1251 | fref = emitir(IRT(IR_FREF, IRT_PTR), tr, IRFL_TAB_META); | ||
| 1252 | mtref = tref_isnil(mt) ? lj_ir_knull(J, IRT_TAB) : mt; | ||
| 1253 | emitir(IRT(IR_FSTORE, IRT_TAB), fref, mtref); | ||
| 1254 | if (!tref_isnil(mt)) | ||
| 1255 | emitir(IRT(IR_TBAR, IRT_TAB), tr, 0); | ||
| 1256 | J->base[0] = tr; | ||
| 1257 | J->needsnap = 1; | ||
| 1258 | } /* else: Interpreter will throw. */ | ||
| 1259 | } | ||
| 1260 | |||
| 1261 | static void LJ_FASTCALL recff_rawget(jit_State *J, RecordFFData *rd) | ||
| 1262 | { | ||
| 1263 | RecordIndex ix; | ||
| 1264 | ix.tab = J->base[0]; ix.key = J->base[1]; | ||
| 1265 | if (tref_istab(ix.tab) && ix.key) { | ||
| 1266 | ix.val = 0; ix.idxchain = 0; | ||
| 1267 | settabV(J->L, &ix.tabv, tabV(&rd->argv[0])); | ||
| 1268 | copyTV(J->L, &ix.keyv, &rd->argv[1]); | ||
| 1269 | J->base[0] = rec_idx(J, &ix); | ||
| 1270 | } /* else: Interpreter will throw. */ | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | static void LJ_FASTCALL recff_rawset(jit_State *J, RecordFFData *rd) | ||
| 1274 | { | ||
| 1275 | RecordIndex ix; | ||
| 1276 | ix.tab = J->base[0]; ix.key = J->base[1]; ix.val = J->base[2]; | ||
| 1277 | if (tref_istab(ix.tab) && ix.key && ix.val) { | ||
| 1278 | ix.idxchain = 0; | ||
| 1279 | settabV(J->L, &ix.tabv, tabV(&rd->argv[0])); | ||
| 1280 | copyTV(J->L, &ix.keyv, &rd->argv[1]); | ||
| 1281 | copyTV(J->L, &ix.valv, &rd->argv[2]); | ||
| 1282 | rec_idx(J, &ix); | ||
| 1283 | /* Pass through table at J->base[0] as result. */ | ||
| 1284 | } /* else: Interpreter will throw. */ | ||
| 1285 | } | ||
| 1286 | |||
| 1287 | static void LJ_FASTCALL recff_rawequal(jit_State *J, RecordFFData *rd) | ||
| 1288 | { | ||
| 1289 | TRef tra = J->base[0]; | ||
| 1290 | TRef trb = J->base[1]; | ||
| 1291 | if (tra && trb) { | ||
| 1292 | int diff = rec_objcmp(J, tra, trb, &rd->argv[0], &rd->argv[1]); | ||
| 1293 | J->base[0] = diff ? TREF_FALSE : TREF_TRUE; | ||
| 1294 | } /* else: Interpreter will throw. */ | ||
| 1295 | } | ||
| 1296 | |||
| 1297 | /* Determine mode of select() call. */ | ||
| 1298 | static int32_t select_mode(jit_State *J, TRef tr, TValue *tv) | ||
| 1299 | { | ||
| 1300 | if (tref_isstr(tr) && *strVdata(tv) == '#') { /* select('#', ...) */ | ||
| 1301 | if (strV(tv)->len == 1) { | ||
| 1302 | emitir(IRT(IR_EQ, IRT_STR), tr, lj_ir_kstr(J, strV(tv))); | ||
| 1303 | } else { | ||
| 1304 | TRef trptr = emitir(IRT(IR_STRREF, IRT_PTR), tr, 0); | ||
| 1305 | TRef trchar = emitir(IRT(IR_XLOAD, IRT_U8), trptr, IRXLOAD_READONLY); | ||
| 1306 | emitir(IRT(IR_EQ, IRT_INT), trchar, lj_ir_kint(J, '#')); | ||
| 1307 | } | ||
| 1308 | return 0; | ||
| 1309 | } else { /* select(n, ...) */ | ||
| 1310 | int32_t start = argv2int(J, tv); | ||
| 1311 | if (start == 0) lj_trace_err(J, LJ_TRERR_BADTYPE); /* A bit misleading. */ | ||
| 1312 | return start; | ||
| 1313 | } | ||
| 1314 | } | ||
| 1315 | |||
| 1316 | static void LJ_FASTCALL recff_select(jit_State *J, RecordFFData *rd) | ||
| 1317 | { | ||
| 1318 | TRef tr = J->base[0]; | ||
| 1319 | if (tr) { | ||
| 1320 | ptrdiff_t start = select_mode(J, tr, &rd->argv[0]); | ||
| 1321 | if (start == 0) { /* select('#', ...) */ | ||
| 1322 | J->base[0] = lj_ir_kint(J, J->maxslot - 1); | ||
| 1323 | } else if (tref_isk(tr)) { /* select(k, ...) */ | ||
| 1324 | ptrdiff_t n = (ptrdiff_t)J->maxslot; | ||
| 1325 | if (start < 0) start += n; | ||
| 1326 | else if (start > n) start = n; | ||
| 1327 | rd->nres = n - start; | ||
| 1328 | if (start >= 1) { | ||
| 1329 | ptrdiff_t i; | ||
| 1330 | for (i = 0; i < n - start; i++) | ||
| 1331 | J->base[i] = J->base[start+i]; | ||
| 1332 | } /* else: Interpreter will throw. */ | ||
| 1333 | } else { | ||
| 1334 | recff_nyiu(J); | ||
| 1335 | } | ||
| 1336 | } /* else: Interpreter will throw. */ | ||
| 1337 | } | ||
| 1338 | |||
| 1339 | static void LJ_FASTCALL recff_tonumber(jit_State *J, RecordFFData *rd) | ||
| 1340 | { | ||
| 1341 | TRef tr = J->base[0]; | ||
| 1342 | if (tref_isnumber_str(tr)) { | ||
| 1343 | TRef base = J->base[1]; | ||
| 1344 | if (base) { | ||
| 1345 | base = lj_ir_toint(J, base); | ||
| 1346 | if (!tref_isk(base) || IR(tref_ref(base))->i != 10) | ||
| 1347 | recff_nyiu(J); | ||
| 1348 | } | ||
| 1349 | if (tref_isstr(tr)) { | ||
| 1350 | TValue tmp; | ||
| 1351 | if (!lj_str_tonum(strV(&rd->argv[0]), &tmp)) | ||
| 1352 | recff_nyiu(J); /* Would need an inverted STRTO for this case. */ | ||
| 1353 | tr = emitir(IRTG(IR_STRTO, IRT_NUM), tr, 0); | ||
| 1354 | } | ||
| 1355 | } else { | ||
| 1356 | tr = TREF_NIL; | ||
| 1357 | } | ||
| 1358 | J->base[0] = tr; | ||
| 1359 | UNUSED(rd); | ||
| 1360 | } | ||
| 1361 | |||
| 1362 | static TValue *recff_metacall_cp(lua_State *L, lua_CFunction dummy, void *ud) | ||
| 1363 | { | ||
| 1364 | jit_State *J = (jit_State *)ud; | ||
| 1365 | rec_tailcall(J, 0, 1); | ||
| 1366 | UNUSED(L); UNUSED(dummy); | ||
| 1367 | return NULL; | ||
| 1368 | } | ||
| 1369 | |||
| 1370 | static int recff_metacall(jit_State *J, RecordFFData *rd, MMS mm) | ||
| 1371 | { | ||
| 1372 | RecordIndex ix; | ||
| 1373 | ix.tab = J->base[0]; | ||
| 1374 | copyTV(J->L, &ix.tabv, &rd->argv[0]); | ||
| 1375 | if (rec_mm_lookup(J, &ix, mm)) { /* Has metamethod? */ | ||
| 1376 | int errcode; | ||
| 1377 | /* Temporarily insert metamethod below object. */ | ||
| 1378 | J->base[1] = J->base[0]; | ||
| 1379 | J->base[0] = ix.mobj; | ||
| 1380 | copyTV(J->L, &rd->argv[1], &rd->argv[0]); | ||
| 1381 | copyTV(J->L, &rd->argv[0], &ix.mobjv); | ||
| 1382 | /* Need to protect rec_tailcall because it may throw. */ | ||
| 1383 | errcode = lj_vm_cpcall(J->L, NULL, J, recff_metacall_cp); | ||
| 1384 | /* Always undo Lua stack changes to avoid confusing the interpreter. */ | ||
| 1385 | copyTV(J->L, &rd->argv[0], &rd->argv[1]); | ||
| 1386 | if (errcode) | ||
| 1387 | lj_err_throw(J->L, errcode); /* Propagate errors. */ | ||
| 1388 | rd->nres = -1; /* Pending call. */ | ||
| 1389 | return 1; /* Tailcalled to metamethod. */ | ||
| 1390 | } | ||
| 1391 | return 0; | ||
| 1392 | } | ||
| 1393 | |||
| 1394 | static void LJ_FASTCALL recff_tostring(jit_State *J, RecordFFData *rd) | ||
| 1395 | { | ||
| 1396 | TRef tr = J->base[0]; | ||
| 1397 | if (tref_isstr(tr)) { | ||
| 1398 | /* Ignore __tostring in the string base metatable. */ | ||
| 1399 | /* Pass on result in J->base[0]. */ | ||
| 1400 | } else if (!recff_metacall(J, rd, MM_tostring)) { | ||
| 1401 | if (tref_isnumber(tr)) { | ||
| 1402 | J->base[0] = emitir(IRT(IR_TOSTR, IRT_STR), tr, 0); | ||
| 1403 | } else if (tref_ispri(tr)) { | ||
| 1404 | J->base[0] = lj_ir_kstr(J, strV(&J->fn->c.upvalue[tref_type(tr)])); | ||
| 1405 | } else { | ||
| 1406 | recff_nyiu(J); | ||
| 1407 | } | ||
| 1408 | } | ||
| 1409 | } | ||
| 1410 | |||
| 1411 | static void LJ_FASTCALL recff_ipairs_aux(jit_State *J, RecordFFData *rd) | ||
| 1412 | { | ||
| 1413 | RecordIndex ix; | ||
| 1414 | ix.tab = J->base[0]; | ||
| 1415 | if (tref_istab(ix.tab)) { | ||
| 1416 | if (!tvisnum(&rd->argv[1])) /* No support for string coercion. */ | ||
| 1417 | lj_trace_err(J, LJ_TRERR_BADTYPE); | ||
| 1418 | setnumV(&ix.keyv, numV(&rd->argv[1])+(lua_Number)1); | ||
| 1419 | settabV(J->L, &ix.tabv, tabV(&rd->argv[0])); | ||
| 1420 | ix.val = 0; ix.idxchain = 0; | ||
| 1421 | ix.key = lj_ir_toint(J, J->base[1]); | ||
| 1422 | J->base[0] = ix.key = emitir(IRTI(IR_ADD), ix.key, lj_ir_kint(J, 1)); | ||
| 1423 | J->base[1] = rec_idx(J, &ix); | ||
| 1424 | rd->nres = tref_isnil(J->base[1]) ? 0 : 2; | ||
| 1425 | } /* else: Interpreter will throw. */ | ||
| 1426 | } | ||
| 1427 | |||
| 1428 | static void LJ_FASTCALL recff_ipairs(jit_State *J, RecordFFData *rd) | ||
| 1429 | { | ||
| 1430 | #ifdef LUAJIT_ENABLE_LUA52COMPAT | ||
| 1431 | if (!recff_metacall(J, rd, MM_ipairs)) | ||
| 1432 | #endif | ||
| 1433 | { | ||
| 1434 | TRef tab = J->base[0]; | ||
| 1435 | if (tref_istab(tab)) { | ||
| 1436 | J->base[0] = lj_ir_kfunc(J, funcV(&J->fn->c.upvalue[0])); | ||
| 1437 | J->base[1] = tab; | ||
| 1438 | J->base[2] = lj_ir_kint(J, 0); | ||
| 1439 | rd->nres = 3; | ||
| 1440 | } /* else: Interpreter will throw. */ | ||
| 1441 | } | ||
| 1442 | } | ||
| 1443 | |||
| 1444 | static void LJ_FASTCALL recff_pcall(jit_State *J, RecordFFData *rd) | ||
| 1445 | { | ||
| 1446 | if (J->maxslot >= 1) { | ||
| 1447 | rec_call(J, 0, J->maxslot - 1); | ||
| 1448 | rd->nres = -1; /* Pending call. */ | ||
| 1449 | } /* else: Interpreter will throw. */ | ||
| 1450 | } | ||
| 1451 | |||
| 1452 | static TValue *recff_xpcall_cp(lua_State *L, lua_CFunction dummy, void *ud) | ||
| 1453 | { | ||
| 1454 | jit_State *J = (jit_State *)ud; | ||
| 1455 | rec_call(J, 1, J->maxslot - 2); | ||
| 1456 | UNUSED(L); UNUSED(dummy); | ||
| 1457 | return NULL; | ||
| 1458 | } | ||
| 1459 | |||
| 1460 | static void LJ_FASTCALL recff_xpcall(jit_State *J, RecordFFData *rd) | ||
| 1461 | { | ||
| 1462 | if (J->maxslot >= 2) { | ||
| 1463 | TValue argv0, argv1; | ||
| 1464 | TRef tmp; | ||
| 1465 | int errcode; | ||
| 1466 | /* Swap function and traceback. */ | ||
| 1467 | tmp = J->base[0]; J->base[0] = J->base[1]; J->base[1] = tmp; | ||
| 1468 | copyTV(J->L, &argv0, &rd->argv[0]); | ||
| 1469 | copyTV(J->L, &argv1, &rd->argv[1]); | ||
| 1470 | copyTV(J->L, &rd->argv[0], &argv1); | ||
| 1471 | copyTV(J->L, &rd->argv[1], &argv0); | ||
| 1472 | /* Need to protect rec_call because it may throw. */ | ||
| 1473 | errcode = lj_vm_cpcall(J->L, NULL, J, recff_xpcall_cp); | ||
| 1474 | /* Always undo Lua stack swap to avoid confusing the interpreter. */ | ||
| 1475 | copyTV(J->L, &rd->argv[0], &argv0); | ||
| 1476 | copyTV(J->L, &rd->argv[1], &argv1); | ||
| 1477 | if (errcode) | ||
| 1478 | lj_err_throw(J->L, errcode); /* Propagate errors. */ | ||
| 1479 | rd->nres = -1; /* Pending call. */ | ||
| 1480 | } /* else: Interpreter will throw. */ | ||
| 1481 | } | ||
| 1482 | |||
| 1483 | /* -- Math library fast functions ----------------------------------------- */ | ||
| 1484 | |||
| 1485 | static void LJ_FASTCALL recff_math_abs(jit_State *J, RecordFFData *rd) | ||
| 1486 | { | ||
| 1487 | TRef tr = lj_ir_tonum(J, J->base[0]); | ||
| 1488 | J->base[0] = emitir(IRTN(IR_ABS), tr, lj_ir_knum_abs(J)); | ||
| 1489 | UNUSED(rd); | ||
| 1490 | } | ||
| 1491 | |||
| 1492 | /* Record rounding functions math.floor and math.ceil. */ | ||
| 1493 | static void LJ_FASTCALL recff_math_round(jit_State *J, RecordFFData *rd) | ||
| 1494 | { | ||
| 1495 | if (!tref_isinteger(J->base[0])) /* Pass through integers unmodified. */ | ||
| 1496 | J->base[0] = emitir(IRTN(IR_FPMATH), lj_ir_tonum(J, J->base[0]), rd->data); | ||
| 1497 | /* Note: result is integral (or NaN/Inf), but may not fit into an integer. */ | ||
| 1498 | } | ||
| 1499 | |||
| 1500 | /* Record unary math.* functions, mapped to IR_FPMATH opcode. */ | ||
| 1501 | static void LJ_FASTCALL recff_math_unary(jit_State *J, RecordFFData *rd) | ||
| 1502 | { | ||
| 1503 | J->base[0] = emitir(IRTN(IR_FPMATH), lj_ir_tonum(J, J->base[0]), rd->data); | ||
| 1504 | } | ||
| 1505 | |||
| 1506 | /* Record binary math.* functions math.atan2 and math.ldexp. */ | ||
| 1507 | static void LJ_FASTCALL recff_math_binary(jit_State *J, RecordFFData *rd) | ||
| 1508 | { | ||
| 1509 | TRef tr = lj_ir_tonum(J, J->base[0]); | ||
| 1510 | J->base[0] = emitir(IRTN(rd->data), tr, lj_ir_tonum(J, J->base[1])); | ||
| 1511 | } | ||
| 1512 | |||
| 1513 | /* Record math.asin, math.acos, math.atan. */ | ||
| 1514 | static void LJ_FASTCALL recff_math_atrig(jit_State *J, RecordFFData *rd) | ||
| 1515 | { | ||
| 1516 | TRef y = lj_ir_tonum(J, J->base[0]); | ||
| 1517 | TRef x = lj_ir_knum_one(J); | ||
| 1518 | uint32_t ffid = rd->data; | ||
| 1519 | if (ffid != FF_math_atan) { | ||
| 1520 | TRef tmp = emitir(IRTN(IR_MUL), y, y); | ||
| 1521 | tmp = emitir(IRTN(IR_SUB), x, tmp); | ||
| 1522 | tmp = emitir(IRTN(IR_FPMATH), tmp, IRFPM_SQRT); | ||
| 1523 | if (ffid == FF_math_asin) { x = tmp; } else { x = y; y = tmp; } | ||
| 1524 | } | ||
| 1525 | J->base[0] = emitir(IRTN(IR_ATAN2), y, x); | ||
| 1526 | } | ||
| 1527 | |||
| 1528 | static void LJ_FASTCALL recff_math_htrig(jit_State *J, RecordFFData *rd) | ||
| 1529 | { | ||
| 1530 | TRef tr = lj_ir_tonum(J, J->base[0]); | ||
| 1531 | J->base[0] = lj_ir_call(J, rd->data, tr); | ||
| 1532 | } | ||
| 1533 | |||
| 1534 | static void LJ_FASTCALL recff_math_modf(jit_State *J, RecordFFData *rd) | ||
| 1535 | { | ||
| 1536 | TRef tr = J->base[0]; | ||
| 1537 | if (tref_isinteger(tr)) { | ||
| 1538 | J->base[0] = tr; | ||
| 1539 | J->base[1] = lj_ir_kint(J, 0); | ||
| 1540 | } else { | ||
| 1541 | TRef trt; | ||
| 1542 | tr = lj_ir_tonum(J, tr); | ||
| 1543 | trt = emitir(IRTN(IR_FPMATH), tr, IRFPM_TRUNC); | ||
| 1544 | J->base[0] = trt; | ||
| 1545 | J->base[1] = emitir(IRTN(IR_SUB), tr, trt); | ||
| 1546 | } | ||
| 1547 | rd->nres = 2; | ||
| 1548 | } | ||
| 1549 | |||
| 1550 | static void LJ_FASTCALL recff_math_degrad(jit_State *J, RecordFFData *rd) | ||
| 1551 | { | ||
| 1552 | TRef tr = lj_ir_tonum(J, J->base[0]); | ||
| 1553 | TRef trm = lj_ir_knum(J, numV(&J->fn->c.upvalue[0])); | ||
| 1554 | J->base[0] = emitir(IRTN(IR_MUL), tr, trm); | ||
| 1555 | UNUSED(rd); | ||
| 1556 | } | ||
| 1557 | |||
| 1558 | static void LJ_FASTCALL recff_math_pow(jit_State *J, RecordFFData *rd) | ||
| 1559 | { | ||
| 1560 | TRef tr = lj_ir_tonum(J, J->base[0]); | ||
| 1561 | if (!tref_isnumber_str(J->base[1])) | ||
| 1562 | lj_trace_err(J, LJ_TRERR_BADTYPE); | ||
| 1563 | J->base[0] = lj_opt_narrow_pow(J, tr, J->base[1], &rd->argv[1]); | ||
| 1564 | UNUSED(rd); | ||
| 1565 | } | ||
| 1566 | |||
| 1567 | static void LJ_FASTCALL recff_math_minmax(jit_State *J, RecordFFData *rd) | ||
| 1568 | { | ||
| 1569 | TRef tr = lj_ir_tonum(J, J->base[0]); | ||
| 1570 | uint32_t op = rd->data; | ||
| 1571 | BCReg i; | ||
| 1572 | for (i = 1; J->base[i] != 0; i++) | ||
| 1573 | tr = emitir(IRTN(op), tr, lj_ir_tonum(J, J->base[i])); | ||
| 1574 | J->base[0] = tr; | ||
| 1575 | } | ||
| 1576 | |||
| 1577 | static void LJ_FASTCALL recff_math_random(jit_State *J, RecordFFData *rd) | ||
| 1578 | { | ||
| 1579 | GCudata *ud = udataV(&J->fn->c.upvalue[0]); | ||
| 1580 | TRef tr, one; | ||
| 1581 | lj_ir_kgc(J, obj2gco(ud), IRT_UDATA); /* Prevent collection. */ | ||
| 1582 | tr = lj_ir_call(J, IRCALL_lj_math_random_step, lj_ir_kptr(J, uddata(ud))); | ||
| 1583 | one = lj_ir_knum_one(J); | ||
| 1584 | tr = emitir(IRTN(IR_SUB), tr, one); | ||
| 1585 | if (J->base[0]) { | ||
| 1586 | TRef tr1 = lj_ir_tonum(J, J->base[0]); | ||
| 1587 | if (J->base[1]) { /* d = floor(d*(r2-r1+1.0)) + r1 */ | ||
| 1588 | TRef tr2 = lj_ir_tonum(J, J->base[1]); | ||
| 1589 | tr2 = emitir(IRTN(IR_SUB), tr2, tr1); | ||
| 1590 | tr2 = emitir(IRTN(IR_ADD), tr2, one); | ||
| 1591 | tr = emitir(IRTN(IR_MUL), tr, tr2); | ||
| 1592 | tr = emitir(IRTN(IR_FPMATH), tr, IRFPM_FLOOR); | ||
| 1593 | tr = emitir(IRTN(IR_ADD), tr, tr1); | ||
| 1594 | } else { /* d = floor(d*r1) + 1.0 */ | ||
| 1595 | tr = emitir(IRTN(IR_MUL), tr, tr1); | ||
| 1596 | tr = emitir(IRTN(IR_FPMATH), tr, IRFPM_FLOOR); | ||
| 1597 | tr = emitir(IRTN(IR_ADD), tr, one); | ||
| 1598 | } | ||
| 1599 | } | ||
| 1600 | J->base[0] = tr; | ||
| 1601 | UNUSED(rd); | ||
| 1602 | } | ||
| 1603 | |||
| 1604 | /* -- Bit library fast functions ------------------------------------------ */ | ||
| 1605 | |||
| 1606 | /* Record unary bit.tobit, bit.bnot, bit.bswap. */ | ||
| 1607 | static void LJ_FASTCALL recff_bit_unary(jit_State *J, RecordFFData *rd) | ||
| 1608 | { | ||
| 1609 | TRef tr = lj_ir_tobit(J, J->base[0]); | ||
| 1610 | J->base[0] = (rd->data == IR_TOBIT) ? tr : emitir(IRTI(rd->data), tr, 0); | ||
| 1611 | } | ||
| 1612 | |||
| 1613 | /* Record N-ary bit.band, bit.bor, bit.bxor. */ | ||
| 1614 | static void LJ_FASTCALL recff_bit_nary(jit_State *J, RecordFFData *rd) | ||
| 1615 | { | ||
| 1616 | TRef tr = lj_ir_tobit(J, J->base[0]); | ||
| 1617 | uint32_t op = rd->data; | ||
| 1618 | BCReg i; | ||
| 1619 | for (i = 1; J->base[i] != 0; i++) | ||
| 1620 | tr = emitir(IRTI(op), tr, lj_ir_tobit(J, J->base[i])); | ||
| 1621 | J->base[0] = tr; | ||
| 1622 | } | ||
| 1623 | |||
| 1624 | /* Record bit shifts. */ | ||
| 1625 | static void LJ_FASTCALL recff_bit_shift(jit_State *J, RecordFFData *rd) | ||
| 1626 | { | ||
| 1627 | TRef tr = lj_ir_tobit(J, J->base[0]); | ||
| 1628 | TRef tsh = lj_ir_tobit(J, J->base[1]); | ||
| 1629 | if (!(rd->data < IR_BROL ? LJ_TARGET_MASKSHIFT : LJ_TARGET_MASKROT) && | ||
| 1630 | !tref_isk(tsh)) | ||
| 1631 | tsh = emitir(IRTI(IR_BAND), tsh, lj_ir_kint(J, 31)); | ||
| 1632 | J->base[0] = emitir(IRTI(rd->data), tr, tsh); | ||
| 1633 | } | ||
| 1634 | |||
| 1635 | /* -- String library fast functions --------------------------------------- */ | ||
| 1636 | |||
| 1637 | static void LJ_FASTCALL recff_string_len(jit_State *J, RecordFFData *rd) | ||
| 1638 | { | ||
| 1639 | J->base[0] = emitir(IRTI(IR_FLOAD), lj_ir_tostr(J, J->base[0]), IRFL_STR_LEN); | ||
| 1640 | UNUSED(rd); | ||
| 1641 | } | ||
| 1642 | |||
| 1643 | /* Handle string.byte (rd->data = 0) and string.sub (rd->data = 1). */ | ||
| 1644 | static void LJ_FASTCALL recff_string_range(jit_State *J, RecordFFData *rd) | ||
| 1645 | { | ||
| 1646 | TRef trstr = lj_ir_tostr(J, J->base[0]); | ||
| 1647 | TRef trlen = emitir(IRTI(IR_FLOAD), trstr, IRFL_STR_LEN); | ||
| 1648 | TRef tr0 = lj_ir_kint(J, 0); | ||
| 1649 | TRef trstart, trend; | ||
| 1650 | GCstr *str = argv2str(J, &rd->argv[0]); | ||
| 1651 | int32_t start, end; | ||
| 1652 | if (rd->data) { /* string.sub(str, start [,end]) */ | ||
| 1653 | start = argv2int(J, &rd->argv[1]); | ||
| 1654 | trstart = lj_ir_toint(J, J->base[1]); | ||
| 1655 | trend = J->base[2]; | ||
| 1656 | if (tref_isnil(trend)) { | ||
| 1657 | trend = lj_ir_kint(J, -1); | ||
| 1658 | end = -1; | ||
| 1659 | } else { | ||
| 1660 | trend = lj_ir_toint(J, trend); | ||
| 1661 | end = argv2int(J, &rd->argv[2]); | ||
| 1662 | } | ||
| 1663 | } else { /* string.byte(str, [,start [,end]]) */ | ||
| 1664 | if (J->base[1]) { | ||
| 1665 | start = argv2int(J, &rd->argv[1]); | ||
| 1666 | trstart = lj_ir_toint(J, J->base[1]); | ||
| 1667 | trend = J->base[2]; | ||
| 1668 | if (tref_isnil(trend)) { | ||
| 1669 | trend = trstart; | ||
| 1670 | end = start; | ||
| 1671 | } else { | ||
| 1672 | trend = lj_ir_toint(J, trend); | ||
| 1673 | end = argv2int(J, &rd->argv[2]); | ||
| 1674 | } | ||
| 1675 | } else { | ||
| 1676 | trend = trstart = lj_ir_kint(J, 1); | ||
| 1677 | end = start = 1; | ||
| 1678 | } | ||
| 1679 | } | ||
| 1680 | if (end < 0) { | ||
| 1681 | emitir(IRTGI(IR_LT), trend, tr0); | ||
| 1682 | trend = emitir(IRTI(IR_ADD), emitir(IRTI(IR_ADD), trlen, trend), | ||
| 1683 | lj_ir_kint(J, 1)); | ||
| 1684 | end = end+(int32_t)str->len+1; | ||
| 1685 | } else if ((MSize)end <= str->len) { | ||
| 1686 | emitir(IRTGI(IR_ULE), trend, trlen); | ||
| 1687 | } else { | ||
| 1688 | emitir(IRTGI(IR_GT), trend, trlen); | ||
| 1689 | end = (int32_t)str->len; | ||
| 1690 | trend = trlen; | ||
| 1691 | } | ||
| 1692 | if (start < 0) { | ||
| 1693 | emitir(IRTGI(IR_LT), trstart, tr0); | ||
| 1694 | trstart = emitir(IRTI(IR_ADD), trlen, trstart); | ||
| 1695 | start = start+(int32_t)str->len; | ||
| 1696 | emitir(start < 0 ? IRTGI(IR_LT) : IRTGI(IR_GE), trstart, tr0); | ||
| 1697 | if (start < 0) { | ||
| 1698 | trstart = tr0; | ||
| 1699 | start = 0; | ||
| 1700 | } | ||
| 1701 | } else { | ||
| 1702 | if (start == 0) { | ||
| 1703 | emitir(IRTGI(IR_EQ), trstart, tr0); | ||
| 1704 | trstart = tr0; | ||
| 1705 | } else { | ||
| 1706 | trstart = emitir(IRTI(IR_ADD), trstart, lj_ir_kint(J, -1)); | ||
| 1707 | emitir(IRTGI(IR_GE), trstart, tr0); | ||
| 1708 | start--; | ||
| 1709 | } | ||
| 1710 | } | ||
| 1711 | if (rd->data) { /* Return string.sub result. */ | ||
| 1712 | if (end - start >= 0) { | ||
| 1713 | /* Also handle empty range here, to avoid extra traces. */ | ||
| 1714 | TRef trptr, trslen = emitir(IRTI(IR_SUB), trend, trstart); | ||
| 1715 | emitir(IRTGI(IR_GE), trslen, tr0); | ||
| 1716 | trptr = emitir(IRT(IR_STRREF, IRT_PTR), trstr, trstart); | ||
| 1717 | J->base[0] = emitir(IRT(IR_SNEW, IRT_STR), trptr, trslen); | ||
| 1718 | } else { /* Range underflow: return empty string. */ | ||
| 1719 | emitir(IRTGI(IR_LT), trend, trstart); | ||
| 1720 | J->base[0] = lj_ir_kstr(J, lj_str_new(J->L, strdata(str), 0)); | ||
| 1721 | } | ||
| 1722 | } else { /* Return string.byte result(s). */ | ||
| 1723 | ptrdiff_t i, len = end - start; | ||
| 1724 | if (len > 0) { | ||
| 1725 | TRef trslen = emitir(IRTI(IR_SUB), trend, trstart); | ||
| 1726 | emitir(IRTGI(IR_EQ), trslen, lj_ir_kint(J, (int32_t)len)); | ||
| 1727 | if (J->baseslot + len > LJ_MAX_JSLOTS) | ||
| 1728 | lj_trace_err_info(J, LJ_TRERR_STACKOV); | ||
| 1729 | rd->nres = len; | ||
| 1730 | for (i = 0; i < len; i++) { | ||
| 1731 | TRef tmp = emitir(IRTI(IR_ADD), trstart, lj_ir_kint(J, (int32_t)i)); | ||
| 1732 | tmp = emitir(IRT(IR_STRREF, IRT_PTR), trstr, tmp); | ||
| 1733 | J->base[i] = emitir(IRT(IR_XLOAD, IRT_U8), tmp, IRXLOAD_READONLY); | ||
| 1734 | } | ||
| 1735 | } else { /* Empty range or range underflow: return no results. */ | ||
| 1736 | emitir(IRTGI(IR_LE), trend, trstart); | ||
| 1737 | rd->nres = 0; | ||
| 1738 | } | ||
| 1739 | } | ||
| 1740 | } | ||
| 1741 | |||
| 1742 | /* -- Table library fast functions ---------------------------------------- */ | ||
| 1743 | |||
| 1744 | static void LJ_FASTCALL recff_table_getn(jit_State *J, RecordFFData *rd) | ||
| 1745 | { | ||
| 1746 | if (tref_istab(J->base[0])) | ||
| 1747 | J->base[0] = lj_ir_call(J, IRCALL_lj_tab_len, J->base[0]); | ||
| 1748 | /* else: Interpreter will throw. */ | ||
| 1749 | UNUSED(rd); | ||
| 1750 | } | ||
| 1751 | |||
| 1752 | static void LJ_FASTCALL recff_table_remove(jit_State *J, RecordFFData *rd) | ||
| 1753 | { | ||
| 1754 | TRef tab = J->base[0]; | ||
| 1755 | rd->nres = 0; | ||
| 1756 | if (tref_istab(tab)) { | ||
| 1757 | if (!J->base[1] || tref_isnil(J->base[1])) { /* Simple pop: t[#t] = nil */ | ||
| 1758 | TRef trlen = lj_ir_call(J, IRCALL_lj_tab_len, tab); | ||
| 1759 | GCtab *t = tabV(&rd->argv[0]); | ||
| 1760 | MSize len = lj_tab_len(t); | ||
| 1761 | emitir(IRTGI(len ? IR_NE : IR_EQ), trlen, lj_ir_kint(J, 0)); | ||
| 1762 | if (len) { | ||
| 1763 | RecordIndex ix; | ||
| 1764 | ix.tab = tab; | ||
| 1765 | ix.key = trlen; | ||
| 1766 | settabV(J->L, &ix.tabv, t); | ||
| 1767 | setintV(&ix.keyv, len); | ||
| 1768 | ix.idxchain = 0; | ||
| 1769 | if (results_wanted(J) != 0) { /* Specialize load only if needed. */ | ||
| 1770 | ix.val = 0; | ||
| 1771 | J->base[0] = rec_idx(J, &ix); /* Load previous value. */ | ||
| 1772 | rd->nres = 1; | ||
| 1773 | /* Assumes ix.key/ix.tab is not modified for raw rec_idx(). */ | ||
| 1774 | } | ||
| 1775 | ix.val = TREF_NIL; | ||
| 1776 | rec_idx(J, &ix); /* Remove value. */ | ||
| 1777 | } | ||
| 1778 | } else { /* Complex case: remove in the middle. */ | ||
| 1779 | recff_nyiu(J); | ||
| 1780 | } | ||
| 1781 | } /* else: Interpreter will throw. */ | ||
| 1782 | } | ||
| 1783 | |||
| 1784 | static void LJ_FASTCALL recff_table_insert(jit_State *J, RecordFFData *rd) | ||
| 1785 | { | ||
| 1786 | RecordIndex ix; | ||
| 1787 | ix.tab = J->base[0]; | ||
| 1788 | ix.val = J->base[1]; | ||
| 1789 | rd->nres = 0; | ||
| 1790 | if (tref_istab(ix.tab) && ix.val) { | ||
| 1791 | if (!J->base[2]) { /* Simple push: t[#t+1] = v */ | ||
| 1792 | TRef trlen = lj_ir_call(J, IRCALL_lj_tab_len, ix.tab); | ||
| 1793 | GCtab *t = tabV(&rd->argv[0]); | ||
| 1794 | ix.key = emitir(IRTI(IR_ADD), trlen, lj_ir_kint(J, 1)); | ||
| 1795 | settabV(J->L, &ix.tabv, t); | ||
| 1796 | setintV(&ix.keyv, lj_tab_len(t) + 1); | ||
| 1797 | ix.idxchain = 0; | ||
| 1798 | rec_idx(J, &ix); /* Set new value. */ | ||
| 1799 | } else { /* Complex case: insert in the middle. */ | ||
| 1800 | recff_nyiu(J); | ||
| 1801 | } | ||
| 1802 | } /* else: Interpreter will throw. */ | ||
| 1803 | } | ||
| 1804 | |||
| 1805 | /* -- I/O library fast functions ------------------------------------------ */ | ||
| 1806 | |||
| 1807 | /* Get FILE* for I/O function. Any I/O error aborts recording, so there's | ||
| 1808 | ** no need to encode the alternate cases for any of the guards. | ||
| 1809 | */ | ||
| 1810 | static TRef recff_io_fp(jit_State *J, uint32_t id) | ||
| 1811 | { | ||
| 1812 | TRef tr, ud, fp; | ||
| 1813 | if (id) { /* io.func() */ | ||
| 1814 | tr = lj_ir_kptr(J, &J2G(J)->gcroot[id]); | ||
| 1815 | ud = emitir(IRT(IR_XLOAD, IRT_UDATA), tr, 0); | ||
| 1816 | } else { /* fp:method() */ | ||
| 1817 | ud = J->base[0]; | ||
| 1818 | if (!tref_isudata(ud)) | ||
| 1819 | lj_trace_err(J, LJ_TRERR_BADTYPE); | ||
| 1820 | tr = emitir(IRT(IR_FLOAD, IRT_U8), ud, IRFL_UDATA_UDTYPE); | ||
| 1821 | emitir(IRTGI(IR_EQ), tr, lj_ir_kint(J, UDTYPE_IO_FILE)); | ||
| 1822 | } | ||
| 1823 | fp = emitir(IRT(IR_FLOAD, IRT_LIGHTUD), ud, IRFL_UDATA_FILE); | ||
| 1824 | emitir(IRTG(IR_NE, IRT_LIGHTUD), fp, lj_ir_knull(J, IRT_LIGHTUD)); | ||
| 1825 | return fp; | ||
| 1826 | } | ||
| 1827 | |||
| 1828 | static void LJ_FASTCALL recff_io_write(jit_State *J, RecordFFData *rd) | ||
| 1829 | { | ||
| 1830 | TRef fp = recff_io_fp(J, rd->data); | ||
| 1831 | TRef zero = lj_ir_kint(J, 0); | ||
| 1832 | TRef one = lj_ir_kint(J, 1); | ||
| 1833 | ptrdiff_t i = rd->data == 0 ? 1 : 0; | ||
| 1834 | for (; J->base[i]; i++) { | ||
| 1835 | TRef str = lj_ir_tostr(J, J->base[i]); | ||
| 1836 | TRef buf = emitir(IRT(IR_STRREF, IRT_PTR), str, zero); | ||
| 1837 | TRef len = emitir(IRTI(IR_FLOAD), str, IRFL_STR_LEN); | ||
| 1838 | if (tref_isk(len) && IR(tref_ref(len))->i == 1) { | ||
| 1839 | TRef tr = emitir(IRT(IR_XLOAD, IRT_U8), buf, IRXLOAD_READONLY); | ||
| 1840 | tr = lj_ir_call(J, IRCALL_fputc, tr, fp); | ||
| 1841 | if (results_wanted(J) != 0) /* Check result only if not ignored. */ | ||
| 1842 | emitir(IRTGI(IR_NE), tr, lj_ir_kint(J, -1)); | ||
| 1843 | } else { | ||
| 1844 | TRef tr = lj_ir_call(J, IRCALL_fwrite, buf, one, len, fp); | ||
| 1845 | if (results_wanted(J) != 0) /* Check result only if not ignored. */ | ||
| 1846 | emitir(IRTGI(IR_EQ), tr, len); | ||
| 1847 | } | ||
| 1848 | } | ||
| 1849 | J->base[0] = TREF_TRUE; | ||
| 1850 | } | ||
| 1851 | |||
| 1852 | static void LJ_FASTCALL recff_io_flush(jit_State *J, RecordFFData *rd) | ||
| 1853 | { | ||
| 1854 | TRef fp = recff_io_fp(J, rd->data); | ||
| 1855 | TRef tr = lj_ir_call(J, IRCALL_fflush, fp); | ||
| 1856 | if (results_wanted(J) != 0) /* Check result only if not ignored. */ | ||
| 1857 | emitir(IRTGI(IR_EQ), tr, lj_ir_kint(J, 0)); | ||
| 1858 | J->base[0] = TREF_TRUE; | ||
| 1859 | } | ||
| 1860 | |||
| 1861 | /* -- Record calls to fast functions -------------------------------------- */ | ||
| 1862 | |||
| 1863 | #include "lj_recdef.h" | ||
| 1864 | |||
| 1865 | static uint32_t recdef_lookup(GCfunc *fn) | ||
| 1866 | { | ||
| 1867 | if (fn->c.ffid < sizeof(recff_idmap)/sizeof(recff_idmap[0])) | ||
| 1868 | return recff_idmap[fn->c.ffid]; | ||
| 1869 | else | ||
| 1870 | return 0; | ||
| 1871 | } | ||
| 1872 | |||
| 1873 | /* Record entry to a fast function or C function. */ | ||
| 1874 | static void rec_func_ff(jit_State *J) | ||
| 1875 | { | ||
| 1876 | RecordFFData rd; | ||
| 1877 | uint32_t m = recdef_lookup(J->fn); | ||
| 1878 | rd.data = m & 0xff; | ||
| 1879 | rd.nres = 1; /* Default is one result. */ | ||
| 1880 | rd.argv = J->L->base; | ||
| 1881 | J->base[J->maxslot] = 0; /* Mark end of arguments. */ | ||
| 1882 | (recff_func[m >> 8])(J, &rd); /* Call recff_* handler. */ | ||
| 1883 | if (rd.nres >= 0) | ||
| 1884 | rec_ret(J, 0, rd.nres); | ||
| 1885 | } | ||
| 1886 | |||
| 1887 | /* -- Record calls to Lua functions --------------------------------------- */ | 1099 | /* -- Record calls to Lua functions --------------------------------------- */ |
| 1888 | 1100 | ||
| 1889 | /* Check unroll limits for calls. */ | 1101 | /* Check unroll limits for calls. */ |
| @@ -2026,7 +1238,7 @@ static void rec_varg(jit_State *J, BCReg dst, ptrdiff_t nresults) | |||
| 2026 | } else if (select_detect(J)) { /* y = select(x, ...) */ | 1238 | } else if (select_detect(J)) { /* y = select(x, ...) */ |
| 2027 | TRef tridx = J->base[dst-1]; | 1239 | TRef tridx = J->base[dst-1]; |
| 2028 | TRef tr = TREF_NIL; | 1240 | TRef tr = TREF_NIL; |
| 2029 | ptrdiff_t idx = select_mode(J, tridx, &J->L->base[dst-1]); | 1241 | ptrdiff_t idx = lj_ffrecord_select_mode(J, tridx, &J->L->base[dst-1]); |
| 2030 | if (idx < 0) goto nyivarg; | 1242 | if (idx < 0) goto nyivarg; |
| 2031 | if (idx != 0 && !tref_isinteger(tridx)) | 1243 | if (idx != 0 && !tref_isinteger(tridx)) |
| 2032 | tridx = emitir(IRTGI(IR_TOINT), tridx, IRTOINT_INDEX); | 1244 | tridx = emitir(IRTGI(IR_TOINT), tridx, IRTOINT_INDEX); |
| @@ -2231,7 +1443,7 @@ void lj_record_ins(jit_State *J) | |||
| 2231 | if (!(tref_isk2(ra, rc) && !(tref_istab(ra) || tref_isudata(ra)))) { | 1443 | if (!(tref_isk2(ra, rc) && !(tref_istab(ra) || tref_isudata(ra)))) { |
| 2232 | int diff; | 1444 | int diff; |
| 2233 | rec_comp_prep(J); | 1445 | rec_comp_prep(J); |
| 2234 | diff = rec_objcmp(J, ra, rc, rav, rcv); | 1446 | diff = lj_record_objcmp(J, ra, rc, rav, rcv); |
| 2235 | if (diff == 1 && (tref_istab(ra) || tref_isudata(ra))) { | 1447 | if (diff == 1 && (tref_istab(ra) || tref_isudata(ra))) { |
| 2236 | /* Only check __eq if different, but the same type (table or udata). */ | 1448 | /* Only check __eq if different, but the same type (table or udata). */ |
| 2237 | rec_mm_equal(J, &ix, (int)op); | 1449 | rec_mm_equal(J, &ix, (int)op); |
| @@ -2354,7 +1566,7 @@ void lj_record_ins(jit_State *J) | |||
| 2354 | settabV(J->L, &ix.tabv, tabref(J->fn->l.env)); | 1566 | settabV(J->L, &ix.tabv, tabref(J->fn->l.env)); |
| 2355 | ix.tab = emitir(IRT(IR_FLOAD, IRT_TAB), getcurrf(J), IRFL_FUNC_ENV); | 1567 | ix.tab = emitir(IRT(IR_FLOAD, IRT_TAB), getcurrf(J), IRFL_FUNC_ENV); |
| 2356 | ix.idxchain = LJ_MAX_IDXCHAIN; | 1568 | ix.idxchain = LJ_MAX_IDXCHAIN; |
| 2357 | rc = rec_idx(J, &ix); | 1569 | rc = lj_record_idx(J, &ix); |
| 2358 | break; | 1570 | break; |
| 2359 | 1571 | ||
| 2360 | case BC_TGETB: case BC_TSETB: | 1572 | case BC_TGETB: case BC_TSETB: |
| @@ -2363,7 +1575,7 @@ void lj_record_ins(jit_State *J) | |||
| 2363 | /* fallthrough */ | 1575 | /* fallthrough */ |
| 2364 | case BC_TGETV: case BC_TGETS: case BC_TSETV: case BC_TSETS: | 1576 | case BC_TGETV: case BC_TGETS: case BC_TSETV: case BC_TSETS: |
| 2365 | ix.idxchain = LJ_MAX_IDXCHAIN; | 1577 | ix.idxchain = LJ_MAX_IDXCHAIN; |
| 2366 | rc = rec_idx(J, &ix); | 1578 | rc = lj_record_idx(J, &ix); |
| 2367 | break; | 1579 | break; |
| 2368 | 1580 | ||
| 2369 | case BC_TNEW: | 1581 | case BC_TNEW: |
| @@ -2380,13 +1592,13 @@ void lj_record_ins(jit_State *J) | |||
| 2380 | J->base[ra] = getslot(J, ra-3); | 1592 | J->base[ra] = getslot(J, ra-3); |
| 2381 | J->base[ra+1] = getslot(J, ra-2); | 1593 | J->base[ra+1] = getslot(J, ra-2); |
| 2382 | J->base[ra+2] = getslot(J, ra-1); | 1594 | J->base[ra+2] = getslot(J, ra-1); |
| 2383 | { /* Have to do the actual copy now because rec_call needs the values. */ | 1595 | { /* Do the actual copy now because lj_record_call needs the values. */ |
| 2384 | TValue *b = &J->L->base[ra]; | 1596 | TValue *b = &J->L->base[ra]; |
| 2385 | copyTV(J->L, b, b-3); | 1597 | copyTV(J->L, b, b-3); |
| 2386 | copyTV(J->L, b+1, b-2); | 1598 | copyTV(J->L, b+1, b-2); |
| 2387 | copyTV(J->L, b+2, b-1); | 1599 | copyTV(J->L, b+2, b-1); |
| 2388 | } | 1600 | } |
| 2389 | rec_call(J, ra, (ptrdiff_t)rc-1); | 1601 | lj_record_call(J, ra, (ptrdiff_t)rc-1); |
| 2390 | break; | 1602 | break; |
| 2391 | 1603 | ||
| 2392 | /* L->top is set to L->base+ra+rc+NARGS-1+1. See lj_dispatch_ins(). */ | 1604 | /* L->top is set to L->base+ra+rc+NARGS-1+1. See lj_dispatch_ins(). */ |
| @@ -2394,14 +1606,14 @@ void lj_record_ins(jit_State *J) | |||
| 2394 | rc = (BCReg)(J->L->top - J->L->base) - ra; | 1606 | rc = (BCReg)(J->L->top - J->L->base) - ra; |
| 2395 | /* fallthrough */ | 1607 | /* fallthrough */ |
| 2396 | case BC_CALL: | 1608 | case BC_CALL: |
| 2397 | rec_call(J, ra, (ptrdiff_t)rc-1); | 1609 | lj_record_call(J, ra, (ptrdiff_t)rc-1); |
| 2398 | break; | 1610 | break; |
| 2399 | 1611 | ||
| 2400 | case BC_CALLMT: | 1612 | case BC_CALLMT: |
| 2401 | rc = (BCReg)(J->L->top - J->L->base) - ra; | 1613 | rc = (BCReg)(J->L->top - J->L->base) - ra; |
| 2402 | /* fallthrough */ | 1614 | /* fallthrough */ |
| 2403 | case BC_CALLT: | 1615 | case BC_CALLT: |
| 2404 | rec_tailcall(J, ra, (ptrdiff_t)rc-1); | 1616 | lj_record_tailcall(J, ra, (ptrdiff_t)rc-1); |
| 2405 | break; | 1617 | break; |
| 2406 | 1618 | ||
| 2407 | case BC_VARG: | 1619 | case BC_VARG: |
| @@ -2415,7 +1627,7 @@ void lj_record_ins(jit_State *J) | |||
| 2415 | rc = (BCReg)(J->L->top - J->L->base) - ra + 1; | 1627 | rc = (BCReg)(J->L->top - J->L->base) - ra + 1; |
| 2416 | /* fallthrough */ | 1628 | /* fallthrough */ |
| 2417 | case BC_RET: case BC_RET0: case BC_RET1: | 1629 | case BC_RET: case BC_RET0: case BC_RET1: |
| 2418 | rec_ret(J, ra, (ptrdiff_t)rc-1); | 1630 | lj_record_ret(J, ra, (ptrdiff_t)rc-1); |
| 2419 | break; | 1631 | break; |
| 2420 | 1632 | ||
| 2421 | /* -- Loops and branches ------------------------------------------------ */ | 1633 | /* -- Loops and branches ------------------------------------------------ */ |
| @@ -2483,12 +1695,12 @@ void lj_record_ins(jit_State *J) | |||
| 2483 | 1695 | ||
| 2484 | case BC_FUNCC: | 1696 | case BC_FUNCC: |
| 2485 | case BC_FUNCCW: | 1697 | case BC_FUNCCW: |
| 2486 | rec_func_ff(J); | 1698 | lj_ffrecord_func(J); |
| 2487 | break; | 1699 | break; |
| 2488 | 1700 | ||
| 2489 | default: | 1701 | default: |
| 2490 | if (op >= BC__MAX) { | 1702 | if (op >= BC__MAX) { |
| 2491 | rec_func_ff(J); | 1703 | lj_ffrecord_func(J); |
| 2492 | break; | 1704 | break; |
| 2493 | } | 1705 | } |
| 2494 | /* fallthrough */ | 1706 | /* fallthrough */ |
diff --git a/src/lj_record.h b/src/lj_record.h index 53c2f4c4..acd6de09 100644 --- a/src/lj_record.h +++ b/src/lj_record.h | |||
| @@ -10,6 +10,32 @@ | |||
| 10 | #include "lj_jit.h" | 10 | #include "lj_jit.h" |
| 11 | 11 | ||
| 12 | #if LJ_HASJIT | 12 | #if LJ_HASJIT |
| 13 | /* Context for recording an indexed load/store. */ | ||
| 14 | typedef struct RecordIndex { | ||
| 15 | TValue tabv; /* Runtime value of table (or indexed object). */ | ||
| 16 | TValue keyv; /* Runtime value of key. */ | ||
| 17 | TValue valv; /* Runtime value of stored value. */ | ||
| 18 | TValue mobjv; /* Runtime value of metamethod object. */ | ||
| 19 | GCtab *mtv; /* Runtime value of metatable object. */ | ||
| 20 | cTValue *oldv; /* Runtime value of previously stored value. */ | ||
| 21 | TRef tab; /* Table (or indexed object) reference. */ | ||
| 22 | TRef key; /* Key reference. */ | ||
| 23 | TRef val; /* Value reference for a store or 0 for a load. */ | ||
| 24 | TRef mt; /* Metatable reference. */ | ||
| 25 | TRef mobj; /* Metamethod object reference. */ | ||
| 26 | int idxchain; /* Index indirections left or 0 for raw lookup. */ | ||
| 27 | } RecordIndex; | ||
| 28 | |||
| 29 | LJ_FUNC int lj_record_objcmp(jit_State *J, TRef a, TRef b, | ||
| 30 | cTValue *av, cTValue *bv); | ||
| 31 | |||
| 32 | LJ_FUNC void lj_record_call(jit_State *J, BCReg func, ptrdiff_t nargs); | ||
| 33 | LJ_FUNC void lj_record_tailcall(jit_State *J, BCReg func, ptrdiff_t nargs); | ||
| 34 | LJ_FUNC void lj_record_ret(jit_State *J, BCReg rbase, ptrdiff_t gotresults); | ||
| 35 | |||
| 36 | LJ_FUNC int lj_record_mm_lookup(jit_State *J, RecordIndex *ix, MMS mm); | ||
| 37 | LJ_FUNC TRef lj_record_idx(jit_State *J, RecordIndex *ix); | ||
| 38 | |||
| 13 | LJ_FUNC void lj_record_ins(jit_State *J); | 39 | LJ_FUNC void lj_record_ins(jit_State *J); |
| 14 | LJ_FUNC void lj_record_setup(jit_State *J); | 40 | LJ_FUNC void lj_record_setup(jit_State *J); |
| 15 | #endif | 41 | #endif |
diff --git a/src/ljamalg.c b/src/ljamalg.c index 30547d3f..1d060960 100644 --- a/src/ljamalg.c +++ b/src/ljamalg.c | |||
| @@ -54,6 +54,7 @@ | |||
| 54 | #include "lj_mcode.c" | 54 | #include "lj_mcode.c" |
| 55 | #include "lj_snap.c" | 55 | #include "lj_snap.c" |
| 56 | #include "lj_record.c" | 56 | #include "lj_record.c" |
| 57 | #include "lj_ffrecord.c" | ||
| 57 | #include "lj_asm.c" | 58 | #include "lj_asm.c" |
| 58 | #include "lj_trace.c" | 59 | #include "lj_trace.c" |
| 59 | #include "lj_gdbjit.c" | 60 | #include "lj_gdbjit.c" |
