diff options
Diffstat (limited to 'src/lj_dispatch.c')
-rw-r--r-- | src/lj_dispatch.c | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/src/lj_dispatch.c b/src/lj_dispatch.c new file mode 100644 index 00000000..d2fce2e0 --- /dev/null +++ b/src/lj_dispatch.c | |||
@@ -0,0 +1,284 @@ | |||
1 | /* | ||
2 | ** Instruction dispatch handling. | ||
3 | ** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h | ||
4 | */ | ||
5 | |||
6 | #define lj_dispatch_c | ||
7 | #define LUA_CORE | ||
8 | |||
9 | #include "lj_obj.h" | ||
10 | #include "lj_err.h" | ||
11 | #include "lj_state.h" | ||
12 | #include "lj_frame.h" | ||
13 | #include "lj_bc.h" | ||
14 | #if LJ_HASJIT | ||
15 | #include "lj_jit.h" | ||
16 | #endif | ||
17 | #include "lj_trace.h" | ||
18 | #include "lj_dispatch.h" | ||
19 | #include "lj_vm.h" | ||
20 | #include "luajit.h" | ||
21 | |||
22 | /* -- Dispatch table management ------------------------------------------- */ | ||
23 | |||
24 | /* Initialize instruction dispatch table and hot counters. */ | ||
25 | void lj_dispatch_init(GG_State *GG) | ||
26 | { | ||
27 | uint32_t i; | ||
28 | ASMFunction *disp = GG->dispatch; | ||
29 | for (i = 0; i < BC__MAX; i++) | ||
30 | disp[GG_DISP_STATIC+i] = disp[i] = makeasmfunc(lj_vm_op_ofs[i]); | ||
31 | /* The JIT engine is off by default. luaopen_jit() turns it on. */ | ||
32 | disp[BC_FORL] = disp[BC_IFORL]; | ||
33 | disp[BC_ITERL] = disp[BC_IITERL]; | ||
34 | disp[BC_LOOP] = disp[BC_ILOOP]; | ||
35 | } | ||
36 | |||
37 | /* Update dispatch table depending on various flags. */ | ||
38 | void lj_dispatch_update(global_State *g) | ||
39 | { | ||
40 | uint8_t oldmode = g->dispatchmode; | ||
41 | uint8_t mode = 0; | ||
42 | #if LJ_HASJIT | ||
43 | mode |= (G2J(g)->flags & JIT_F_ON) ? 1 : 0; | ||
44 | mode |= G2J(g)->state != LJ_TRACE_IDLE ? 6 : 0; | ||
45 | #endif | ||
46 | mode |= (g->hookmask & HOOK_EVENTMASK) ? 2 : 0; | ||
47 | if (oldmode != mode) { /* Mode changed? */ | ||
48 | ASMFunction *disp = G2GG(g)->dispatch; | ||
49 | ASMFunction f_forl, f_iterl, f_loop; | ||
50 | g->dispatchmode = mode; | ||
51 | if ((mode & 5) == 1) { /* Hotcount if JIT is on, but not when recording. */ | ||
52 | f_forl = makeasmfunc(lj_vm_op_ofs[BC_FORL]); | ||
53 | f_iterl = makeasmfunc(lj_vm_op_ofs[BC_ITERL]); | ||
54 | f_loop = makeasmfunc(lj_vm_op_ofs[BC_LOOP]); | ||
55 | } else { /* Otherwise use the non-hotcounting instructions. */ | ||
56 | f_forl = disp[GG_DISP_STATIC+BC_IFORL]; | ||
57 | f_iterl = disp[GG_DISP_STATIC+BC_IITERL]; | ||
58 | f_loop = disp[GG_DISP_STATIC+BC_ILOOP]; | ||
59 | } | ||
60 | /* Set static loop ins first (may be copied below). */ | ||
61 | disp[GG_DISP_STATIC+BC_FORL] = f_forl; | ||
62 | disp[GG_DISP_STATIC+BC_ITERL] = f_iterl; | ||
63 | disp[GG_DISP_STATIC+BC_LOOP] = f_loop; | ||
64 | if ((oldmode & 6) != (mode & 6)) { /* Need to change whole table? */ | ||
65 | if ((mode & 6) == 0) { /* No hooks and no recording? */ | ||
66 | /* Copy static dispatch table to dynamic dispatch table. */ | ||
67 | memcpy(&disp[0], &disp[GG_DISP_STATIC], sizeof(ASMFunction)*BC__MAX); | ||
68 | } else { | ||
69 | /* The recording dispatch also checks for hooks. */ | ||
70 | ASMFunction f = (mode & 6) == 6 ? lj_vm_record : lj_vm_hook; | ||
71 | uint32_t i; | ||
72 | for (i = 0; i < BC__MAX; i++) | ||
73 | disp[i] = f; | ||
74 | } | ||
75 | } else if ((mode & 6) == 0) { /* Fix dynamic loop ins unless overriden. */ | ||
76 | disp[BC_FORL] = f_forl; | ||
77 | disp[BC_ITERL] = f_iterl; | ||
78 | disp[BC_LOOP] = f_loop; | ||
79 | } | ||
80 | } | ||
81 | } | ||
82 | |||
83 | /* -- JIT mode setting ---------------------------------------------------- */ | ||
84 | |||
85 | #if LJ_HASJIT | ||
86 | /* Set JIT mode for a single prototype. */ | ||
87 | static void setptmode(global_State *g, GCproto *pt, int mode) | ||
88 | { | ||
89 | if ((mode & LUAJIT_MODE_ON)) { /* (Re-)enable JIT compilation. */ | ||
90 | pt->flags &= ~PROTO_NO_JIT; | ||
91 | lj_trace_reenableproto(pt); /* Unpatch all ILOOP etc. bytecodes. */ | ||
92 | } else { /* Flush and/or disable JIT compilation. */ | ||
93 | if (!(mode & LUAJIT_MODE_FLUSH)) | ||
94 | pt->flags |= PROTO_NO_JIT; | ||
95 | lj_trace_flushproto(g, pt); /* Flush all traces of prototype. */ | ||
96 | } | ||
97 | } | ||
98 | |||
99 | /* Recursively set the JIT mode for all children of a prototype. */ | ||
100 | static void setptmode_all(global_State *g, GCproto *pt, int mode) | ||
101 | { | ||
102 | ptrdiff_t i; | ||
103 | for (i = -(ptrdiff_t)pt->sizekgc; i < 0; i++) { | ||
104 | GCobj *o = gcref(pt->k.gc[i]); | ||
105 | if (o->gch.gct == ~LJ_TPROTO) { | ||
106 | setptmode(g, gco2pt(o), mode); | ||
107 | setptmode_all(g, gco2pt(o), mode); | ||
108 | } | ||
109 | } | ||
110 | } | ||
111 | #endif | ||
112 | |||
113 | /* Public API function: control the JIT engine. */ | ||
114 | int luaJIT_setmode(lua_State *L, int idx, int mode) | ||
115 | { | ||
116 | global_State *g = G(L); | ||
117 | int mm = mode & LUAJIT_MODE_MASK; | ||
118 | lj_trace_abort(g); /* Abort recording on any state change. */ | ||
119 | /* Avoid pulling the rug from under our own feet. */ | ||
120 | if ((g->hookmask & HOOK_GC)) | ||
121 | lj_err_caller(L, LJ_ERR_NOGCMM); | ||
122 | switch (mm) { | ||
123 | #if LJ_HASJIT | ||
124 | case LUAJIT_MODE_ENGINE: | ||
125 | if ((mode & LUAJIT_MODE_FLUSH)) { | ||
126 | lj_trace_flushall(L); | ||
127 | } else { | ||
128 | if ((mode & LUAJIT_MODE_ON)) | ||
129 | G2J(g)->flags |= (uint32_t)JIT_F_ON; | ||
130 | else | ||
131 | G2J(g)->flags &= ~(uint32_t)JIT_F_ON; | ||
132 | lj_dispatch_update(g); | ||
133 | } | ||
134 | break; | ||
135 | case LUAJIT_MODE_FUNC: | ||
136 | case LUAJIT_MODE_ALLFUNC: | ||
137 | case LUAJIT_MODE_ALLSUBFUNC: { | ||
138 | cTValue *tv = idx == 0 ? frame_prev(L->base-1) : | ||
139 | idx > 0 ? L->base + (idx-1) : L->top + idx; | ||
140 | GCproto *pt; | ||
141 | if ((idx == 0 || tvisfunc(tv)) && isluafunc(&gcval(tv)->fn)) | ||
142 | pt = funcproto(&gcval(tv)->fn); /* Cannot use funcV() for frame slot. */ | ||
143 | else if (tvisproto(tv)) | ||
144 | pt = protoV(tv); | ||
145 | else | ||
146 | return 0; /* Failed. */ | ||
147 | if (mm != LUAJIT_MODE_ALLSUBFUNC) | ||
148 | setptmode(g, pt, mode); | ||
149 | if (mm != LUAJIT_MODE_FUNC) | ||
150 | setptmode_all(g, pt, mode); | ||
151 | break; | ||
152 | } | ||
153 | case LUAJIT_MODE_TRACE: | ||
154 | if (!(mode & LUAJIT_MODE_FLUSH)) | ||
155 | return 0; /* Failed. */ | ||
156 | lj_trace_flush(G2J(g), idx); | ||
157 | break; | ||
158 | #else | ||
159 | case LUAJIT_MODE_ENGINE: | ||
160 | case LUAJIT_MODE_FUNC: | ||
161 | case LUAJIT_MODE_ALLFUNC: | ||
162 | case LUAJIT_MODE_ALLSUBFUNC: | ||
163 | UNUSED(idx); | ||
164 | if ((mode & LUAJIT_MODE_ON)) | ||
165 | return 0; /* Failed. */ | ||
166 | break; | ||
167 | #endif | ||
168 | default: | ||
169 | return 0; /* Failed. */ | ||
170 | } | ||
171 | return 1; /* OK. */ | ||
172 | } | ||
173 | |||
174 | /* Enforce (dynamic) linker error for version mismatches. See luajit.c. */ | ||
175 | LUA_API void LUAJIT_VERSION_SYM(void) | ||
176 | { | ||
177 | } | ||
178 | |||
179 | /* -- Hooks --------------------------------------------------------------- */ | ||
180 | |||
181 | /* This function can be called asynchronously (e.g. during a signal). */ | ||
182 | LUA_API int lua_sethook(lua_State *L, lua_Hook func, int mask, int count) | ||
183 | { | ||
184 | global_State *g = G(L); | ||
185 | mask &= HOOK_EVENTMASK; | ||
186 | if (func == NULL || mask == 0) { mask = 0; func = NULL; } /* Consistency. */ | ||
187 | g->hookf = func; | ||
188 | g->hookcount = g->hookcstart = (int32_t)count; | ||
189 | g->hookmask = (uint8_t)((g->hookmask & ~HOOK_EVENTMASK) | mask); | ||
190 | lj_trace_abort(g); /* Abort recording on any hook change. */ | ||
191 | lj_dispatch_update(g); | ||
192 | return 1; | ||
193 | } | ||
194 | |||
195 | LUA_API lua_Hook lua_gethook(lua_State *L) | ||
196 | { | ||
197 | return G(L)->hookf; | ||
198 | } | ||
199 | |||
200 | LUA_API int lua_gethookmask(lua_State *L) | ||
201 | { | ||
202 | return G(L)->hookmask & HOOK_EVENTMASK; | ||
203 | } | ||
204 | |||
205 | LUA_API int lua_gethookcount(lua_State *L) | ||
206 | { | ||
207 | return (int)G(L)->hookcstart; | ||
208 | } | ||
209 | |||
210 | /* Call a hook. */ | ||
211 | static void callhook(lua_State *L, int event, BCLine line) | ||
212 | { | ||
213 | global_State *g = G(L); | ||
214 | lua_Hook hookf = g->hookf; | ||
215 | if (hookf && !hook_active(g)) { | ||
216 | lua_Debug ar; | ||
217 | lj_trace_abort(g); /* Abort recording on any hook call. */ | ||
218 | ar.event = event; | ||
219 | ar.currentline = line; | ||
220 | ar.i_ci = cast_int((L->base-1) - L->stack); /* Top frame, nextframe=NULL. */ | ||
221 | lj_state_checkstack(L, 1+LUA_MINSTACK); | ||
222 | hook_enter(g); | ||
223 | hookf(L, &ar); | ||
224 | lua_assert(hook_active(g)); | ||
225 | hook_leave(g); | ||
226 | } | ||
227 | } | ||
228 | |||
229 | /* -- Instruction dispatch callbacks -------------------------------------- */ | ||
230 | |||
231 | /* Calculate number of used stack slots in the current frame. */ | ||
232 | static BCReg cur_topslot(GCproto *pt, const BCIns *pc, uint32_t nres) | ||
233 | { | ||
234 | BCIns ins = pc[-1]; | ||
235 | for (;;) { | ||
236 | switch (bc_op(ins)) { | ||
237 | case BC_UCLO: ins = pc[bc_j(ins)]; break; | ||
238 | case BC_CALLM: | ||
239 | case BC_CALLMT: return bc_a(ins) + bc_c(ins) + nres-1+1; | ||
240 | case BC_RETM: return bc_a(ins) + bc_d(ins) + nres-1; | ||
241 | case BC_TSETM: return bc_a(ins) + nres-1; | ||
242 | default: return pt->framesize; | ||
243 | } | ||
244 | } | ||
245 | } | ||
246 | |||
247 | /* Instruction dispatch callback for instr/line hooks or when recording. */ | ||
248 | void lj_dispatch_ins(lua_State *L, const BCIns *pc, uint32_t nres) | ||
249 | { | ||
250 | GCfunc *fn = curr_func(L); | ||
251 | GCproto *pt = funcproto(fn); | ||
252 | BCReg slots = cur_topslot(pt, pc, nres); | ||
253 | global_State *g = G(L); | ||
254 | const BCIns *oldpc = cframe_Lpc(L); | ||
255 | cframe_Lpc(L) = pc; | ||
256 | L->top = L->base + slots; /* Fix top. */ | ||
257 | #if LJ_HASJIT | ||
258 | { | ||
259 | jit_State *J = G2J(g); | ||
260 | if (J->state != LJ_TRACE_IDLE) { | ||
261 | J->L = L; | ||
262 | J->pc = pc-1; | ||
263 | J->fn = fn; | ||
264 | J->pt = pt; | ||
265 | lj_trace_ins(J); | ||
266 | } | ||
267 | } | ||
268 | #endif | ||
269 | if ((g->hookmask & LUA_MASKCOUNT) && g->hookcount == 0) { | ||
270 | g->hookcount = g->hookcstart; | ||
271 | callhook(L, LUA_HOOKCOUNT, -1); | ||
272 | } | ||
273 | if ((g->hookmask & LUA_MASKLINE) && pt->lineinfo) { | ||
274 | BCPos npc = (BCPos)(pc - pt->bc)-1; | ||
275 | BCPos opc = (BCPos)(oldpc - pt->bc)-1; | ||
276 | BCLine line = pt->lineinfo[npc]; | ||
277 | if (npc == 0 || pc <= oldpc || | ||
278 | opc >= pt->sizebc || line != pt->lineinfo[opc]) { | ||
279 | L->top = L->base + slots; /* Fix top again after instruction hook. */ | ||
280 | callhook(L, LUA_HOOKLINE, line); | ||
281 | } | ||
282 | } | ||
283 | } | ||
284 | |||