/src/php-src/ext/opcache/jit/zend_jit.c
Line | Count | Source |
1 | | /* |
2 | | +----------------------------------------------------------------------+ |
3 | | | Zend JIT | |
4 | | +----------------------------------------------------------------------+ |
5 | | | Copyright (c) The PHP Group | |
6 | | +----------------------------------------------------------------------+ |
7 | | | This source file is subject to version 3.01 of the PHP license, | |
8 | | | that is bundled with this package in the file LICENSE, and is | |
9 | | | available through the world-wide-web at the following url: | |
10 | | | https://www.php.net/license/3_01.txt | |
11 | | | If you did not receive a copy of the PHP license and are unable to | |
12 | | | obtain it through the world-wide-web, please send a note to | |
13 | | | license@php.net so we can mail you a copy immediately. | |
14 | | +----------------------------------------------------------------------+ |
15 | | | Authors: Dmitry Stogov <dmitry@php.net> | |
16 | | +----------------------------------------------------------------------+ |
17 | | */ |
18 | | |
19 | | #include "main/php.h" |
20 | | #include "main/SAPI.h" |
21 | | #include "php_version.h" |
22 | | #include <ZendAccelerator.h> |
23 | | #include "zend_shared_alloc.h" |
24 | | #include "Zend/zend_execute.h" |
25 | | #include "Zend/zend_vm.h" |
26 | | #include "Zend/zend_exceptions.h" |
27 | | #include "Zend/zend_constants.h" |
28 | | #include "Zend/zend_closures.h" |
29 | | #include "Zend/zend_ini.h" |
30 | | #include "Zend/zend_observer.h" |
31 | | #include "zend_smart_str.h" |
32 | | #include "jit/zend_jit.h" |
33 | | |
34 | | #ifdef HAVE_JIT |
35 | | |
36 | | #include "Optimizer/zend_func_info.h" |
37 | | #include "Optimizer/zend_ssa.h" |
38 | | #include "Optimizer/zend_inference.h" |
39 | | #include "Optimizer/zend_call_graph.h" |
40 | | #include "Optimizer/zend_dump.h" |
41 | | #include "Optimizer/zend_worklist.h" |
42 | | |
43 | | #include "jit/zend_jit_internal.h" |
44 | | |
45 | | #ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP |
46 | | #include <pthread.h> |
47 | | #endif |
48 | | |
49 | | #ifdef ZTS |
50 | | int jit_globals_id; |
51 | | size_t jit_globals_offset; |
52 | | #else |
53 | | zend_jit_globals jit_globals; |
54 | | #endif |
55 | | |
56 | | //#define CONTEXT_THREADED_JIT |
57 | | #define ZEND_JIT_USE_RC_INFERENCE |
58 | | |
59 | | #ifdef ZEND_JIT_USE_RC_INFERENCE |
60 | 0 | # define ZEND_SSA_RC_INFERENCE_FLAG ZEND_SSA_RC_INFERENCE |
61 | 0 | # define RC_MAY_BE_1(info) (((info) & (MAY_BE_RC1|MAY_BE_REF)) != 0) |
62 | 0 | # define RC_MAY_BE_N(info) (((info) & (MAY_BE_RCN|MAY_BE_REF)) != 0) |
63 | | #else |
64 | | # define ZEND_SSA_RC_INFERENCE_FLAG 0 |
65 | | # define RC_MAY_BE_1(info) 1 |
66 | | # define RC_MAY_BE_N(info) 1 |
67 | | #endif |
68 | | |
69 | 0 | #define JIT_PREFIX "JIT$" |
70 | | #define JIT_STUB_PREFIX "JIT$$" |
71 | 0 | #define TRACE_PREFIX "TRACE-" |
72 | | |
73 | | bool zend_jit_startup_ok = false; |
74 | | |
75 | | zend_ulong zend_jit_profile_counter = 0; |
76 | | int zend_jit_profile_counter_rid = -1; |
77 | | |
78 | | int16_t zend_jit_hot_counters[ZEND_HOT_COUNTERS_COUNT]; |
79 | | |
80 | | const zend_op *zend_jit_halt_op = NULL; |
81 | | #ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP |
82 | | static int zend_write_protect = 1; |
83 | | #endif |
84 | | |
85 | | static void *dasm_buf = NULL; |
86 | | static void *dasm_end = NULL; |
87 | | static void **dasm_ptr = NULL; |
88 | | |
89 | | static size_t dasm_size = 0; |
90 | | |
91 | | static zend_long jit_bisect_pos = 0; |
92 | | |
93 | | static zend_vm_opcode_handler_t zend_jit_runtime_jit_handler = NULL; |
94 | | static zend_vm_opcode_handler_t zend_jit_profile_jit_handler = NULL; |
95 | | static zend_vm_opcode_handler_t zend_jit_func_hot_counter_handler = NULL; |
96 | | static zend_vm_opcode_handler_t zend_jit_loop_hot_counter_handler = NULL; |
97 | | static zend_vm_opcode_handler_t zend_jit_func_trace_counter_handler = NULL; |
98 | | static zend_vm_opcode_handler_t zend_jit_ret_trace_counter_handler = NULL; |
99 | | static zend_vm_opcode_handler_t zend_jit_loop_trace_counter_handler = NULL; |
100 | | |
101 | | #if ZEND_VM_KIND == ZEND_VM_KIND_CALL || ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL |
102 | | static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend_runtime_jit(ZEND_OPCODE_HANDLER_ARGS); |
103 | | #else |
104 | | static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_runtime_jit(ZEND_OPCODE_HANDLER_ARGS); |
105 | | #endif |
106 | | |
107 | | static int zend_jit_trace_op_len(const zend_op *opline); |
108 | | static int zend_jit_trace_may_exit(const zend_op_array *op_array, const zend_op *opline); |
109 | | static uint32_t _zend_jit_trace_get_exit_point(const zend_op *to_opline, uint32_t flags ZEND_FILE_LINE_DC); |
110 | 0 | #define zend_jit_trace_get_exit_point(to_opline, flags) _zend_jit_trace_get_exit_point(to_opline, flags ZEND_FILE_LINE_CC) |
111 | | static const void *zend_jit_trace_get_exit_addr(uint32_t n); |
112 | | static void zend_jit_trace_add_code(const void *start, uint32_t size); |
113 | | static zend_string *zend_jit_func_name(const zend_op_array *op_array); |
114 | | |
115 | | static bool zend_jit_needs_arg_dtor(const zend_function *func, uint32_t arg_num, zend_call_info *call_info); |
116 | | static bool zend_jit_supported_binary_op(uint8_t op, uint32_t op1_info, uint32_t op2_info); |
117 | | |
118 | 0 | static bool dominates(const zend_basic_block *blocks, int a, int b) { |
119 | 0 | while (blocks[b].level > blocks[a].level) { |
120 | 0 | b = blocks[b].idom; |
121 | 0 | } |
122 | 0 | return a == b; |
123 | 0 | } |
124 | | |
125 | | static bool zend_ssa_is_last_use(const zend_op_array *op_array, const zend_ssa *ssa, int var, int use) |
126 | 0 | { |
127 | 0 | int next_use; |
128 | |
|
129 | 0 | if (ssa->vars[var].phi_use_chain) { |
130 | 0 | zend_ssa_phi *phi = ssa->vars[var].phi_use_chain; |
131 | 0 | do { |
132 | 0 | if (!ssa->vars[phi->ssa_var].no_val) { |
133 | 0 | return 0; |
134 | 0 | } |
135 | 0 | phi = zend_ssa_next_use_phi(ssa, var, phi); |
136 | 0 | } while (phi); |
137 | 0 | } |
138 | | |
139 | 0 | if (ssa->cfg.blocks[ssa->cfg.map[use]].loop_header > 0 |
140 | 0 | || (ssa->cfg.blocks[ssa->cfg.map[use]].flags & ZEND_BB_LOOP_HEADER)) { |
141 | 0 | int b = ssa->cfg.map[use]; |
142 | 0 | int prev_use = ssa->vars[var].use_chain; |
143 | 0 | int def_block; |
144 | |
|
145 | 0 | if (ssa->vars[var].definition >= 0) { |
146 | 0 | def_block =ssa->cfg.map[ssa->vars[var].definition]; |
147 | 0 | } else { |
148 | 0 | ZEND_ASSERT(ssa->vars[var].definition_phi); |
149 | 0 | def_block = ssa->vars[var].definition_phi->block; |
150 | 0 | } |
151 | 0 | if (dominates(ssa->cfg.blocks, def_block, |
152 | 0 | (ssa->cfg.blocks[b].flags & ZEND_BB_LOOP_HEADER) ? b : ssa->cfg.blocks[b].loop_header)) { |
153 | 0 | return 0; |
154 | 0 | } |
155 | | |
156 | 0 | while (prev_use >= 0 && prev_use != use) { |
157 | 0 | if (b != ssa->cfg.map[prev_use] |
158 | 0 | && dominates(ssa->cfg.blocks, b, ssa->cfg.map[prev_use]) |
159 | 0 | && !zend_ssa_is_no_val_use(op_array->opcodes + prev_use, ssa->ops + prev_use, var)) { |
160 | 0 | return 0; |
161 | 0 | } |
162 | 0 | prev_use = zend_ssa_next_use(ssa->ops, var, prev_use); |
163 | 0 | } |
164 | 0 | } |
165 | | |
166 | 0 | next_use = zend_ssa_next_use(ssa->ops, var, use); |
167 | 0 | if (next_use < 0) { |
168 | 0 | return 1; |
169 | 0 | } else if (zend_ssa_is_no_val_use(op_array->opcodes + next_use, ssa->ops + next_use, var)) { |
170 | 0 | return 1; |
171 | 0 | } |
172 | 0 | return 0; |
173 | 0 | } |
174 | | |
175 | | static int zend_jit_is_constant_cmp_long_long(const zend_op *opline, |
176 | | zend_ssa_range *op1_range, |
177 | | zend_jit_addr op1_addr, |
178 | | zend_ssa_range *op2_range, |
179 | | zend_jit_addr op2_addr, |
180 | | bool *result) |
181 | 0 | { |
182 | 0 | zend_long op1_min; |
183 | 0 | zend_long op1_max; |
184 | 0 | zend_long op2_min; |
185 | 0 | zend_long op2_max; |
186 | |
|
187 | 0 | if (op1_range) { |
188 | 0 | op1_min = op1_range->min; |
189 | 0 | op1_max = op1_range->max; |
190 | 0 | } else if (Z_MODE(op1_addr) == IS_CONST_ZVAL) { |
191 | 0 | ZEND_ASSERT(Z_TYPE_P(Z_ZV(op1_addr)) == IS_LONG); |
192 | 0 | op1_min = op1_max = Z_LVAL_P(Z_ZV(op1_addr)); |
193 | 0 | } else { |
194 | 0 | return 0; |
195 | 0 | } |
196 | | |
197 | 0 | if (op2_range) { |
198 | 0 | op2_min = op2_range->min; |
199 | 0 | op2_max = op2_range->max; |
200 | 0 | } else if (Z_MODE(op2_addr) == IS_CONST_ZVAL) { |
201 | 0 | ZEND_ASSERT(Z_TYPE_P(Z_ZV(op2_addr)) == IS_LONG); |
202 | 0 | op2_min = op2_max = Z_LVAL_P(Z_ZV(op2_addr)); |
203 | 0 | } else { |
204 | 0 | return 0; |
205 | 0 | } |
206 | | |
207 | 0 | switch (opline->opcode) { |
208 | 0 | case ZEND_IS_EQUAL: |
209 | 0 | case ZEND_IS_IDENTICAL: |
210 | 0 | case ZEND_CASE: |
211 | 0 | case ZEND_CASE_STRICT: |
212 | 0 | if (op1_min == op1_max && op2_min == op2_max && op1_min == op2_min) { |
213 | 0 | *result = true; |
214 | 0 | return 1; |
215 | 0 | } else if (op1_max < op2_min || op1_min > op2_max) { |
216 | 0 | *result = false; |
217 | 0 | return 1; |
218 | 0 | } |
219 | 0 | return 0; |
220 | 0 | case ZEND_IS_NOT_EQUAL: |
221 | 0 | case ZEND_IS_NOT_IDENTICAL: |
222 | 0 | if (op1_min == op1_max && op2_min == op2_max && op1_min == op2_min) { |
223 | 0 | *result = false; |
224 | 0 | return 1; |
225 | 0 | } else if (op1_max < op2_min || op1_min > op2_max) { |
226 | 0 | *result = true; |
227 | 0 | return 1; |
228 | 0 | } |
229 | 0 | return 0; |
230 | 0 | case ZEND_IS_SMALLER: |
231 | 0 | if (op1_max < op2_min) { |
232 | 0 | *result = true; |
233 | 0 | return 1; |
234 | 0 | } else if (op1_min >= op2_max) { |
235 | 0 | *result = false; |
236 | 0 | return 1; |
237 | 0 | } |
238 | 0 | return 0; |
239 | 0 | case ZEND_IS_SMALLER_OR_EQUAL: |
240 | 0 | if (op1_max <= op2_min) { |
241 | 0 | *result = true; |
242 | 0 | return 1; |
243 | 0 | } else if (op1_min > op2_max) { |
244 | 0 | *result = false; |
245 | 0 | return 1; |
246 | 0 | } |
247 | 0 | return 0; |
248 | 0 | default: |
249 | 0 | ZEND_UNREACHABLE(); |
250 | 0 | } |
251 | 0 | return 0; |
252 | 0 | } |
253 | | |
254 | | #define ADVANCE_SSA_OP(ssa_op, offset) \ |
255 | 0 | do { \ |
256 | 0 | if (ssa_op) ssa_op += offset; \ |
257 | 0 | } while (0) |
258 | | |
259 | | static int zend_jit_needs_call_chain(zend_call_info *call_info, uint32_t b, const zend_op_array *op_array, zend_ssa *ssa, const zend_ssa_op *ssa_op, const zend_op *opline, int call_level, zend_jit_trace_rec *trace) |
260 | 0 | { |
261 | 0 | int skip; |
262 | |
|
263 | 0 | if (trace) { |
264 | 0 | zend_jit_trace_rec *p = trace; |
265 | |
|
266 | 0 | ADVANCE_SSA_OP(ssa_op, 1); |
267 | 0 | while (1) { |
268 | 0 | if (p->op == ZEND_JIT_TRACE_VM) { |
269 | 0 | switch (p->opline->opcode) { |
270 | 0 | case ZEND_SEND_ARRAY: |
271 | 0 | case ZEND_SEND_USER: |
272 | 0 | case ZEND_SEND_UNPACK: |
273 | 0 | case ZEND_INIT_FCALL: |
274 | 0 | case ZEND_INIT_METHOD_CALL: |
275 | 0 | case ZEND_INIT_STATIC_METHOD_CALL: |
276 | 0 | case ZEND_INIT_PARENT_PROPERTY_HOOK_CALL: |
277 | 0 | case ZEND_INIT_FCALL_BY_NAME: |
278 | 0 | case ZEND_INIT_NS_FCALL_BY_NAME: |
279 | 0 | case ZEND_INIT_DYNAMIC_CALL: |
280 | 0 | case ZEND_NEW: |
281 | 0 | case ZEND_INIT_USER_CALL: |
282 | 0 | case ZEND_FAST_CALL: |
283 | 0 | case ZEND_JMP: |
284 | 0 | case ZEND_JMPZ: |
285 | 0 | case ZEND_JMPNZ: |
286 | 0 | case ZEND_JMPZ_EX: |
287 | 0 | case ZEND_JMPNZ_EX: |
288 | 0 | case ZEND_FE_RESET_R: |
289 | 0 | case ZEND_FE_RESET_RW: |
290 | 0 | case ZEND_JMP_SET: |
291 | 0 | case ZEND_COALESCE: |
292 | 0 | case ZEND_JMP_NULL: |
293 | 0 | case ZEND_ASSERT_CHECK: |
294 | 0 | case ZEND_CATCH: |
295 | 0 | case ZEND_DECLARE_ANON_CLASS: |
296 | 0 | case ZEND_FE_FETCH_R: |
297 | 0 | case ZEND_FE_FETCH_RW: |
298 | 0 | case ZEND_BIND_INIT_STATIC_OR_JMP: |
299 | 0 | case ZEND_JMP_FRAMELESS: |
300 | 0 | return 1; |
301 | 0 | case ZEND_DO_ICALL: |
302 | 0 | case ZEND_DO_UCALL: |
303 | 0 | case ZEND_DO_FCALL_BY_NAME: |
304 | 0 | case ZEND_DO_FCALL: |
305 | 0 | case ZEND_CALLABLE_CONVERT: |
306 | 0 | return 0; |
307 | 0 | case ZEND_SEND_VAL: |
308 | 0 | case ZEND_SEND_VAR: |
309 | 0 | case ZEND_SEND_VAL_EX: |
310 | 0 | case ZEND_SEND_VAR_EX: |
311 | 0 | case ZEND_SEND_FUNC_ARG: |
312 | 0 | case ZEND_SEND_REF: |
313 | 0 | case ZEND_SEND_VAR_NO_REF: |
314 | 0 | case ZEND_SEND_VAR_NO_REF_EX: |
315 | | /* skip */ |
316 | 0 | break; |
317 | 0 | default: |
318 | 0 | if (zend_may_throw(opline, ssa_op, op_array, ssa)) { |
319 | 0 | return 1; |
320 | 0 | } |
321 | 0 | } |
322 | 0 | ADVANCE_SSA_OP(ssa_op, zend_jit_trace_op_len(opline)); |
323 | 0 | } else if (p->op == ZEND_JIT_TRACE_ENTER || |
324 | 0 | p->op == ZEND_JIT_TRACE_BACK || |
325 | 0 | p->op == ZEND_JIT_TRACE_END) { |
326 | 0 | return 1; |
327 | 0 | } |
328 | 0 | p++; |
329 | 0 | } |
330 | 0 | } |
331 | | |
332 | 0 | if (!call_info) { |
333 | 0 | const zend_op *end = op_array->opcodes + op_array->last; |
334 | |
|
335 | 0 | opline++; |
336 | 0 | ADVANCE_SSA_OP(ssa_op, 1); |
337 | 0 | skip = (call_level == 1); |
338 | 0 | while (opline != end) { |
339 | 0 | if (!skip) { |
340 | 0 | if (zend_may_throw(opline, ssa_op, op_array, ssa)) { |
341 | 0 | return 1; |
342 | 0 | } |
343 | 0 | } |
344 | 0 | switch (opline->opcode) { |
345 | 0 | case ZEND_SEND_VAL: |
346 | 0 | case ZEND_SEND_VAR: |
347 | 0 | case ZEND_SEND_VAL_EX: |
348 | 0 | case ZEND_SEND_VAR_EX: |
349 | 0 | case ZEND_SEND_FUNC_ARG: |
350 | 0 | case ZEND_SEND_REF: |
351 | 0 | case ZEND_SEND_VAR_NO_REF: |
352 | 0 | case ZEND_SEND_VAR_NO_REF_EX: |
353 | 0 | skip = 0; |
354 | 0 | break; |
355 | 0 | case ZEND_SEND_ARRAY: |
356 | 0 | case ZEND_SEND_USER: |
357 | 0 | case ZEND_SEND_UNPACK: |
358 | 0 | case ZEND_INIT_FCALL: |
359 | 0 | case ZEND_INIT_METHOD_CALL: |
360 | 0 | case ZEND_INIT_STATIC_METHOD_CALL: |
361 | 0 | case ZEND_INIT_PARENT_PROPERTY_HOOK_CALL: |
362 | 0 | case ZEND_INIT_FCALL_BY_NAME: |
363 | 0 | case ZEND_INIT_NS_FCALL_BY_NAME: |
364 | 0 | case ZEND_INIT_DYNAMIC_CALL: |
365 | 0 | case ZEND_NEW: |
366 | 0 | case ZEND_INIT_USER_CALL: |
367 | 0 | case ZEND_FAST_CALL: |
368 | 0 | case ZEND_JMP: |
369 | 0 | case ZEND_JMPZ: |
370 | 0 | case ZEND_JMPNZ: |
371 | 0 | case ZEND_JMPZ_EX: |
372 | 0 | case ZEND_JMPNZ_EX: |
373 | 0 | case ZEND_FE_RESET_R: |
374 | 0 | case ZEND_FE_RESET_RW: |
375 | 0 | case ZEND_JMP_SET: |
376 | 0 | case ZEND_COALESCE: |
377 | 0 | case ZEND_JMP_NULL: |
378 | 0 | case ZEND_ASSERT_CHECK: |
379 | 0 | case ZEND_CATCH: |
380 | 0 | case ZEND_DECLARE_ANON_CLASS: |
381 | 0 | case ZEND_FE_FETCH_R: |
382 | 0 | case ZEND_FE_FETCH_RW: |
383 | 0 | case ZEND_BIND_INIT_STATIC_OR_JMP: |
384 | 0 | case ZEND_JMP_FRAMELESS: |
385 | 0 | return 1; |
386 | 0 | case ZEND_DO_ICALL: |
387 | 0 | case ZEND_DO_UCALL: |
388 | 0 | case ZEND_DO_FCALL_BY_NAME: |
389 | 0 | case ZEND_DO_FCALL: |
390 | 0 | case ZEND_CALLABLE_CONVERT: |
391 | 0 | end = opline; |
392 | 0 | if (end - op_array->opcodes >= ssa->cfg.blocks[b].start + ssa->cfg.blocks[b].len) { |
393 | | /* INIT_FCALL and DO_FCALL in different BasicBlocks */ |
394 | 0 | return 1; |
395 | 0 | } |
396 | 0 | return 0; |
397 | 0 | } |
398 | 0 | opline++; |
399 | 0 | ADVANCE_SSA_OP(ssa_op, 1); |
400 | 0 | } |
401 | | |
402 | 0 | return 1; |
403 | 0 | } else { |
404 | 0 | const zend_op *end = call_info->caller_call_opline; |
405 | | |
406 | | /* end may be null if an opcode like EXIT is part of the argument list. */ |
407 | 0 | if (!end || end - op_array->opcodes >= ssa->cfg.blocks[b].start + ssa->cfg.blocks[b].len) { |
408 | | /* INIT_FCALL and DO_FCALL in different BasicBlocks */ |
409 | 0 | return 1; |
410 | 0 | } |
411 | | |
412 | 0 | opline++; |
413 | 0 | ADVANCE_SSA_OP(ssa_op, 1); |
414 | 0 | skip = (call_level == 1); |
415 | 0 | while (opline != end) { |
416 | 0 | if (skip) { |
417 | 0 | switch (opline->opcode) { |
418 | 0 | case ZEND_SEND_VAL: |
419 | 0 | case ZEND_SEND_VAR: |
420 | 0 | case ZEND_SEND_VAL_EX: |
421 | 0 | case ZEND_SEND_VAR_EX: |
422 | 0 | case ZEND_SEND_FUNC_ARG: |
423 | 0 | case ZEND_SEND_REF: |
424 | 0 | case ZEND_SEND_VAR_NO_REF: |
425 | 0 | case ZEND_SEND_VAR_NO_REF_EX: |
426 | 0 | skip = 0; |
427 | 0 | break; |
428 | 0 | case ZEND_SEND_ARRAY: |
429 | 0 | case ZEND_SEND_USER: |
430 | 0 | case ZEND_SEND_UNPACK: |
431 | 0 | return 1; |
432 | 0 | } |
433 | 0 | } else { |
434 | 0 | if (zend_may_throw(opline, ssa_op, op_array, ssa)) { |
435 | 0 | return 1; |
436 | 0 | } |
437 | 0 | } |
438 | 0 | opline++; |
439 | 0 | ADVANCE_SSA_OP(ssa_op, 1); |
440 | 0 | } |
441 | | |
442 | 0 | return 0; |
443 | 0 | } |
444 | 0 | } |
445 | | |
446 | | static uint32_t skip_valid_arguments(const zend_op_array *op_array, zend_ssa *ssa, const zend_call_info *call_info) |
447 | 0 | { |
448 | 0 | uint32_t num_args = 0; |
449 | 0 | zend_function *func = call_info->callee_func; |
450 | | |
451 | | /* It's okay to handle prototypes here, because they can only increase the accepted arguments. |
452 | | * Anything legal for the parent method is also legal for the parent method. */ |
453 | 0 | while (num_args < call_info->num_args) { |
454 | 0 | zend_arg_info *arg_info = func->op_array.arg_info + num_args; |
455 | |
|
456 | 0 | if (ZEND_TYPE_IS_SET(arg_info->type)) { |
457 | 0 | if (ZEND_TYPE_IS_ONLY_MASK(arg_info->type)) { |
458 | 0 | zend_op *opline = call_info->arg_info[num_args].opline; |
459 | 0 | zend_ssa_op *ssa_op = ssa->ops ? &ssa->ops[opline - op_array->opcodes] : NULL; |
460 | 0 | uint32_t type_mask = ZEND_TYPE_PURE_MASK(arg_info->type); |
461 | 0 | if ((OP1_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) & ~type_mask) { |
462 | 0 | break; |
463 | 0 | } |
464 | 0 | } else { |
465 | 0 | break; |
466 | 0 | } |
467 | 0 | } |
468 | 0 | num_args++; |
469 | 0 | } |
470 | 0 | return num_args; |
471 | 0 | } |
472 | | |
473 | | static uint32_t zend_ssa_cv_info(const zend_op_array *op_array, zend_ssa *ssa, uint32_t var) |
474 | 0 | { |
475 | 0 | uint32_t j, info; |
476 | |
|
477 | 0 | if (ssa->vars && ssa->var_info) { |
478 | 0 | info = ssa->var_info[var].type; |
479 | 0 | for (j = op_array->last_var; j < ssa->vars_count; j++) { |
480 | 0 | if (ssa->vars[j].var == var) { |
481 | 0 | info |= ssa->var_info[j].type; |
482 | 0 | } |
483 | 0 | } |
484 | 0 | } else { |
485 | 0 | info = MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_UNDEF | |
486 | 0 | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; |
487 | 0 | } |
488 | |
|
489 | 0 | #ifdef ZEND_JIT_USE_RC_INFERENCE |
490 | | /* Refcount may be increased by RETURN opcode */ |
491 | 0 | if ((info & MAY_BE_RC1) && !(info & MAY_BE_RCN)) { |
492 | 0 | for (j = 0; j < ssa->cfg.blocks_count; j++) { |
493 | 0 | if ((ssa->cfg.blocks[j].flags & ZEND_BB_REACHABLE) && |
494 | 0 | ssa->cfg.blocks[j].len > 0) { |
495 | 0 | const zend_op *opline = op_array->opcodes + ssa->cfg.blocks[j].start + ssa->cfg.blocks[j].len - 1; |
496 | |
|
497 | 0 | if (opline->opcode == ZEND_RETURN) { |
498 | 0 | if (opline->op1_type == IS_CV && opline->op1.var == EX_NUM_TO_VAR(var)) { |
499 | 0 | info |= MAY_BE_RCN; |
500 | 0 | break; |
501 | 0 | } |
502 | 0 | } |
503 | 0 | } |
504 | 0 | } |
505 | 0 | } |
506 | 0 | #endif |
507 | |
|
508 | 0 | return info; |
509 | 0 | } |
510 | | |
511 | | static bool zend_jit_may_avoid_refcounting(const zend_op *opline, uint32_t op1_info) |
512 | 0 | { |
513 | 0 | switch (opline->opcode) { |
514 | 0 | case ZEND_FETCH_OBJ_FUNC_ARG: |
515 | 0 | if (!JIT_G(current_frame) || |
516 | 0 | !JIT_G(current_frame)->call->func || |
517 | 0 | !TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) { |
518 | 0 | return 0; |
519 | 0 | } |
520 | | /* break missing intentionally */ |
521 | 0 | case ZEND_FETCH_OBJ_R: |
522 | 0 | case ZEND_FETCH_OBJ_IS: |
523 | 0 | if ((op1_info & MAY_BE_OBJECT) |
524 | 0 | && opline->op2_type == IS_CONST |
525 | 0 | && Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) == IS_STRING |
526 | 0 | && Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] != '\0') { |
527 | 0 | return 1; |
528 | 0 | } |
529 | 0 | break; |
530 | 0 | case ZEND_FETCH_DIM_FUNC_ARG: |
531 | 0 | if (!JIT_G(current_frame) || |
532 | 0 | !JIT_G(current_frame)->call->func || |
533 | 0 | !TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) { |
534 | 0 | return 0; |
535 | 0 | } |
536 | | /* break missing intentionally */ |
537 | 0 | case ZEND_FETCH_DIM_R: |
538 | 0 | case ZEND_FETCH_DIM_IS: |
539 | 0 | return 1; |
540 | 0 | case ZEND_ISSET_ISEMPTY_DIM_OBJ: |
541 | 0 | if (!(opline->extended_value & ZEND_ISEMPTY)) { |
542 | 0 | return 1; |
543 | 0 | } |
544 | 0 | break; |
545 | 0 | } |
546 | 0 | return 0; |
547 | 0 | } |
548 | | |
549 | | static bool zend_jit_is_persistent_constant(zval *key, uint32_t flags) |
550 | 0 | { |
551 | 0 | zval *zv; |
552 | 0 | zend_constant *c = NULL; |
553 | | |
554 | | /* null/true/false are resolved during compilation, so don't check for them here. */ |
555 | 0 | zv = zend_hash_find_known_hash(EG(zend_constants), Z_STR_P(key)); |
556 | 0 | if (zv) { |
557 | 0 | c = (zend_constant*)Z_PTR_P(zv); |
558 | 0 | } else if (flags & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) { |
559 | 0 | key++; |
560 | 0 | zv = zend_hash_find_known_hash(EG(zend_constants), Z_STR_P(key)); |
561 | 0 | if (zv) { |
562 | 0 | c = (zend_constant*)Z_PTR_P(zv); |
563 | 0 | } |
564 | 0 | } |
565 | 0 | return c && (ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT); |
566 | 0 | } |
567 | | |
568 | | static zend_class_entry* zend_get_known_class(const zend_op_array *op_array, const zend_op *opline, uint8_t op_type, znode_op op) |
569 | 0 | { |
570 | 0 | zend_class_entry *ce = NULL; |
571 | |
|
572 | 0 | if (op_type == IS_CONST) { |
573 | 0 | zval *zv = RT_CONSTANT(opline, op); |
574 | 0 | zend_string *class_name; |
575 | |
|
576 | 0 | ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING); |
577 | 0 | class_name = Z_STR_P(zv); |
578 | 0 | ce = zend_lookup_class_ex(class_name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); |
579 | 0 | if (ce && (ce->type == ZEND_INTERNAL_CLASS || ce->info.user.filename != op_array->filename)) { |
580 | 0 | ce = NULL; |
581 | 0 | } |
582 | 0 | } else { |
583 | 0 | ZEND_ASSERT(op_type == IS_UNUSED); |
584 | 0 | if ((op.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) { |
585 | 0 | ce = op_array->scope; |
586 | 0 | } else { |
587 | 0 | ZEND_ASSERT((op.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT); |
588 | 0 | ce = op_array->scope; |
589 | 0 | if (ce) { |
590 | 0 | if (ce->parent) { |
591 | 0 | ce = ce->parent; |
592 | 0 | if (ce->type == ZEND_INTERNAL_CLASS || ce->info.user.filename != op_array->filename) { |
593 | 0 | ce = NULL; |
594 | 0 | } |
595 | 0 | } else { |
596 | 0 | ce = NULL; |
597 | 0 | } |
598 | 0 | } |
599 | 0 | } |
600 | 0 | } |
601 | |
|
602 | 0 | return ce; |
603 | 0 | } |
604 | | |
605 | | static zend_property_info* zend_get_known_property_info(const zend_op_array *op_array, zend_class_entry *ce, zend_string *member, bool on_this, zend_string *filename) |
606 | 0 | { |
607 | 0 | zend_property_info *info = NULL; |
608 | |
|
609 | 0 | if ((on_this && (op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) || |
610 | 0 | !ce || |
611 | 0 | !(ce->ce_flags & ZEND_ACC_LINKED) || |
612 | 0 | (ce->ce_flags & ZEND_ACC_TRAIT) || |
613 | 0 | ce->create_object) { |
614 | 0 | return NULL; |
615 | 0 | } |
616 | | |
617 | 0 | if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) { |
618 | 0 | if (ce->info.user.filename != filename) { |
619 | | /* class declaration might be changed independently */ |
620 | 0 | return NULL; |
621 | 0 | } |
622 | | |
623 | 0 | if (ce->parent) { |
624 | 0 | zend_class_entry *parent = ce->parent; |
625 | |
|
626 | 0 | do { |
627 | 0 | if (parent->type == ZEND_INTERNAL_CLASS) { |
628 | 0 | break; |
629 | 0 | } else if (parent->info.user.filename != filename) { |
630 | | /* some of parents class declarations might be changed independently */ |
631 | | /* TODO: this check may be not enough, because even |
632 | | * in the same it's possible to conditionally define |
633 | | * few classes with the same name, and "parent" may |
634 | | * change from request to request. |
635 | | */ |
636 | 0 | return NULL; |
637 | 0 | } |
638 | 0 | parent = parent->parent; |
639 | 0 | } while (parent); |
640 | 0 | } |
641 | 0 | } |
642 | | |
643 | | // TODO: Treat property hooks more precisely. |
644 | 0 | info = (zend_property_info*)zend_hash_find_ptr(&ce->properties_info, member); |
645 | 0 | if (info == NULL || |
646 | 0 | !IS_VALID_PROPERTY_OFFSET(info->offset) || |
647 | 0 | (info->flags & ZEND_ACC_STATIC) || |
648 | 0 | info->hooks) { |
649 | 0 | return NULL; |
650 | 0 | } |
651 | | |
652 | 0 | if (info->flags & ZEND_ACC_PUBLIC) { |
653 | 0 | return info; |
654 | 0 | } else if (on_this) { |
655 | 0 | if (ce == info->ce) { |
656 | 0 | if (ce == op_array->scope) { |
657 | 0 | return info; |
658 | 0 | } else { |
659 | 0 | return NULL; |
660 | 0 | } |
661 | 0 | } else if ((info->flags & ZEND_ACC_PROTECTED) |
662 | 0 | && instanceof_function_slow(ce, info->ce)) { |
663 | 0 | return info; |
664 | 0 | } |
665 | 0 | } |
666 | | |
667 | 0 | return NULL; |
668 | 0 | } |
669 | | |
670 | | static bool zend_may_be_dynamic_property(zend_class_entry *ce, zend_string *member, bool on_this, const zend_op_array *op_array) |
671 | 0 | { |
672 | 0 | zend_property_info *info; |
673 | |
|
674 | 0 | if (!ce || (ce->ce_flags & ZEND_ACC_TRAIT) || (op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) { |
675 | 0 | return 1; |
676 | 0 | } |
677 | | |
678 | 0 | if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) { |
679 | 0 | if (ce->info.user.filename != op_array->filename) { |
680 | | /* class declaration might be changed independently */ |
681 | 0 | return 1; |
682 | 0 | } |
683 | 0 | } |
684 | | |
685 | | // TODO: Treat property hooks more precisely. |
686 | 0 | info = (zend_property_info*)zend_hash_find_ptr(&ce->properties_info, member); |
687 | 0 | if (info == NULL || |
688 | 0 | !IS_VALID_PROPERTY_OFFSET(info->offset) || |
689 | 0 | (info->flags & ZEND_ACC_STATIC) || |
690 | 0 | info->hooks) { |
691 | 0 | return 1; |
692 | 0 | } |
693 | | |
694 | 0 | if (!(info->flags & ZEND_ACC_PUBLIC) && |
695 | 0 | (!on_this || info->ce != ce)) { |
696 | 0 | return 1; |
697 | 0 | } |
698 | | |
699 | 0 | return 0; |
700 | 0 | } |
701 | | |
702 | | static bool zend_jit_class_may_be_modified(const zend_class_entry *ce, const zend_op_array *called_from) |
703 | 0 | { |
704 | 0 | uint32_t i; |
705 | |
|
706 | 0 | if (ce->type == ZEND_INTERNAL_CLASS) { |
707 | | #ifdef _WIN32 |
708 | | /* ASLR */ |
709 | | return 1; |
710 | | #else |
711 | 0 | return 0; |
712 | 0 | #endif |
713 | 0 | } else if (ce->type == ZEND_USER_CLASS) { |
714 | 0 | if (ce->ce_flags & ZEND_ACC_PRELOADED) { |
715 | 0 | return 0; |
716 | 0 | } |
717 | 0 | if (ce->info.user.filename == called_from->filename) { |
718 | 0 | if (ce->parent |
719 | 0 | && (!(ce->ce_flags & ZEND_ACC_LINKED) |
720 | 0 | || zend_jit_class_may_be_modified(ce->parent, called_from))) { |
721 | 0 | return 1; |
722 | 0 | } |
723 | 0 | if (ce->num_interfaces) { |
724 | 0 | if (!(ce->ce_flags & ZEND_ACC_LINKED)) { |
725 | 0 | return 1; |
726 | 0 | } |
727 | 0 | for (i = 0; i < ce->num_interfaces; i++) { |
728 | 0 | if (zend_jit_class_may_be_modified(ce->interfaces[i], called_from)) { |
729 | 0 | return 1; |
730 | 0 | } |
731 | 0 | } |
732 | 0 | } |
733 | 0 | if (ce->num_traits) { |
734 | 0 | if (!(ce->ce_flags & ZEND_ACC_LINKED)) { |
735 | 0 | return 1; |
736 | 0 | } |
737 | 0 | for (i=0; i < ce->num_traits; i++) { |
738 | 0 | zend_class_entry *trait = zend_fetch_class_by_name(ce->trait_names[i].name, |
739 | 0 | ce->trait_names[i].lc_name, |
740 | 0 | ZEND_FETCH_CLASS_TRAIT | ZEND_FETCH_CLASS_NO_AUTOLOAD | ZEND_FETCH_CLASS_SILENT); |
741 | 0 | if (!trait || zend_jit_class_may_be_modified(trait, called_from)) { |
742 | 0 | return 1; |
743 | 0 | } |
744 | 0 | } |
745 | 0 | } |
746 | 0 | return 0; |
747 | 0 | } |
748 | 0 | } |
749 | 0 | return 1; |
750 | 0 | } |
751 | | |
752 | | static bool zend_jit_may_be_modified(const zend_function *func, const zend_op_array *called_from) |
753 | 0 | { |
754 | 0 | if (func->type == ZEND_INTERNAL_FUNCTION) { |
755 | | #ifdef _WIN32 |
756 | | /* ASLR */ |
757 | | return 1; |
758 | | #else |
759 | 0 | return 0; |
760 | 0 | #endif |
761 | 0 | } else if (func->type == ZEND_USER_FUNCTION) { |
762 | 0 | if (func->common.fn_flags & ZEND_ACC_PRELOADED) { |
763 | 0 | return 0; |
764 | 0 | } |
765 | 0 | if (func->op_array.filename == called_from->filename |
766 | 0 | && (!func->op_array.scope |
767 | 0 | || !zend_jit_class_may_be_modified(func->op_array.scope, called_from))) { |
768 | 0 | return 0; |
769 | 0 | } |
770 | 0 | } |
771 | 0 | return 1; |
772 | 0 | } |
773 | | |
774 | | #define OP_RANGE(ssa_op, opN) \ |
775 | 0 | (((opline->opN##_type & (IS_TMP_VAR|IS_VAR|IS_CV)) && \ |
776 | 0 | ssa->var_info && \ |
777 | 0 | (ssa_op)->opN##_use >= 0 && \ |
778 | 0 | ssa->var_info[(ssa_op)->opN##_use].has_range) ? \ |
779 | 0 | &ssa->var_info[(ssa_op)->opN##_use].range : NULL) |
780 | | |
781 | 0 | #define OP1_RANGE() OP_RANGE(ssa_op, op1) |
782 | 0 | #define OP2_RANGE() OP_RANGE(ssa_op, op2) |
783 | 0 | #define OP1_DATA_RANGE() OP_RANGE(ssa_op + 1, op1) |
784 | | |
785 | | #include "jit/zend_jit_helpers.c" |
786 | | #include "Zend/zend_cpuinfo.h" |
787 | | |
788 | | #ifdef HAVE_GCC_GLOBAL_REGS |
789 | | # define GCC_GLOBAL_REGS 1 |
790 | | #else |
791 | 0 | # define GCC_GLOBAL_REGS 0 |
792 | | #endif |
793 | | |
794 | | /* By default avoid JITing inline handlers if it does not seem profitable due to lack of |
795 | | * type information. Disabling this option allows testing some JIT handlers in the |
796 | | * presence of try/catch blocks, which prevent SSA construction. */ |
797 | | #ifndef PROFITABILITY_CHECKS |
798 | | # define PROFITABILITY_CHECKS 1 |
799 | | #endif |
800 | | |
801 | 0 | #define BP_JIT_IS 6 /* Used for ISSET_ISEMPTY_DIM_OBJ. see BP_VAR_*defines in Zend/zend_compile.h */ |
802 | | |
803 | | /* The generated code may contain tautological comparisons, ignore them. */ |
804 | | #if defined(__clang__) |
805 | | # pragma clang diagnostic push |
806 | | # pragma clang diagnostic ignored "-Wtautological-compare" |
807 | | # pragma clang diagnostic ignored "-Wstring-compare" |
808 | | #endif |
809 | | |
810 | | #include "jit/zend_jit_ir.c" |
811 | | |
812 | | #if defined(__clang__) |
813 | | # pragma clang diagnostic pop |
814 | | #endif |
815 | | |
816 | | #ifdef _WIN32 |
817 | | # include <Windows.h> |
818 | | #else |
819 | | # include <sys/mman.h> |
820 | | # if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) |
821 | | # define MAP_ANONYMOUS MAP_ANON |
822 | | # endif |
823 | | #endif |
824 | | |
825 | | ZEND_EXT_API void zend_jit_status(zval *ret) |
826 | 18 | { |
827 | 18 | zval stats; |
828 | 18 | array_init(&stats); |
829 | 18 | add_assoc_bool(&stats, "enabled", JIT_G(enabled)); |
830 | 18 | add_assoc_bool(&stats, "on", JIT_G(on)); |
831 | 18 | add_assoc_long(&stats, "kind", JIT_G(trigger)); |
832 | 18 | add_assoc_long(&stats, "opt_level", JIT_G(opt_level)); |
833 | 18 | add_assoc_long(&stats, "opt_flags", JIT_G(opt_flags)); |
834 | 18 | if (dasm_buf) { |
835 | 0 | add_assoc_long(&stats, "buffer_size", (char*)dasm_end - (char*)dasm_buf); |
836 | 0 | add_assoc_long(&stats, "buffer_free", (char*)dasm_end - (char*)*dasm_ptr); |
837 | 18 | } else { |
838 | 18 | add_assoc_long(&stats, "buffer_size", 0); |
839 | 18 | add_assoc_long(&stats, "buffer_free", 0); |
840 | 18 | } |
841 | 18 | add_assoc_zval(ret, "jit", &stats); |
842 | 18 | } |
843 | | |
844 | | static bool zend_jit_inc_call_level(uint8_t opcode) |
845 | 0 | { |
846 | 0 | switch (opcode) { |
847 | 0 | case ZEND_INIT_FCALL: |
848 | 0 | case ZEND_INIT_FCALL_BY_NAME: |
849 | 0 | case ZEND_INIT_NS_FCALL_BY_NAME: |
850 | 0 | case ZEND_INIT_METHOD_CALL: |
851 | 0 | case ZEND_INIT_DYNAMIC_CALL: |
852 | 0 | case ZEND_INIT_STATIC_METHOD_CALL: |
853 | 0 | case ZEND_INIT_PARENT_PROPERTY_HOOK_CALL: |
854 | 0 | case ZEND_INIT_USER_CALL: |
855 | 0 | case ZEND_NEW: |
856 | 0 | return true; |
857 | 0 | default: |
858 | 0 | return false; |
859 | 0 | } |
860 | 0 | } |
861 | | |
862 | | static bool zend_jit_dec_call_level(uint8_t opcode) |
863 | 0 | { |
864 | 0 | switch (opcode) { |
865 | 0 | case ZEND_DO_FCALL: |
866 | 0 | case ZEND_DO_ICALL: |
867 | 0 | case ZEND_DO_UCALL: |
868 | 0 | case ZEND_DO_FCALL_BY_NAME: |
869 | 0 | case ZEND_CALLABLE_CONVERT: |
870 | 0 | return true; |
871 | 0 | default: |
872 | 0 | return false; |
873 | 0 | } |
874 | 0 | } |
875 | | |
876 | | static zend_string *zend_jit_func_name(const zend_op_array *op_array) |
877 | 0 | { |
878 | 0 | smart_str buf = {0}; |
879 | |
|
880 | 0 | if (op_array->function_name) { |
881 | 0 | smart_str_appends(&buf, JIT_PREFIX); |
882 | 0 | if (op_array->scope) { |
883 | 0 | smart_str_appendl(&buf, ZSTR_VAL(op_array->scope->name), ZSTR_LEN(op_array->scope->name)); |
884 | 0 | smart_str_appends(&buf, "::"); |
885 | 0 | } |
886 | 0 | smart_str_appendl(&buf, ZSTR_VAL(op_array->function_name), ZSTR_LEN(op_array->function_name)); |
887 | 0 | if (op_array->fn_flags & ZEND_ACC_CLOSURE) { |
888 | 0 | smart_str_appends(&buf, ":"); |
889 | 0 | smart_str_appendl(&buf, ZSTR_VAL(op_array->filename), ZSTR_LEN(op_array->filename)); |
890 | 0 | smart_str_appends(&buf, ":"); |
891 | 0 | smart_str_append_long(&buf, op_array->line_start); |
892 | 0 | } |
893 | 0 | smart_str_0(&buf); |
894 | 0 | return buf.s; |
895 | 0 | } else if (op_array->filename) { |
896 | 0 | smart_str_appends(&buf, JIT_PREFIX); |
897 | 0 | smart_str_appendl(&buf, ZSTR_VAL(op_array->filename), ZSTR_LEN(op_array->filename)); |
898 | 0 | smart_str_0(&buf); |
899 | 0 | return buf.s; |
900 | 0 | } else { |
901 | 0 | return NULL; |
902 | 0 | } |
903 | 0 | } |
904 | | |
905 | | static int zend_may_overflow(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa) |
906 | 0 | { |
907 | 0 | int res; |
908 | 0 | zend_long op1_min, op1_max, op2_min, op2_max; |
909 | |
|
910 | 0 | if (!ssa->ops || !ssa->var_info) { |
911 | 0 | return 1; |
912 | 0 | } |
913 | 0 | switch (opline->opcode) { |
914 | 0 | case ZEND_PRE_INC: |
915 | 0 | case ZEND_POST_INC: |
916 | 0 | res = ssa_op->op1_def; |
917 | 0 | if (res < 0 |
918 | 0 | || !ssa->var_info[res].has_range |
919 | 0 | || ssa->var_info[res].range.overflow) { |
920 | 0 | if (!OP1_HAS_RANGE()) { |
921 | 0 | return 1; |
922 | 0 | } |
923 | 0 | op1_max = OP1_MAX_RANGE(); |
924 | 0 | if (op1_max == ZEND_LONG_MAX) { |
925 | 0 | return 1; |
926 | 0 | } |
927 | 0 | } |
928 | 0 | return 0; |
929 | 0 | case ZEND_PRE_DEC: |
930 | 0 | case ZEND_POST_DEC: |
931 | 0 | res = ssa_op->op1_def; |
932 | 0 | if (res < 0 |
933 | 0 | || !ssa->var_info[res].has_range |
934 | 0 | || ssa->var_info[res].range.underflow) { |
935 | 0 | if (!OP1_HAS_RANGE()) { |
936 | 0 | return 1; |
937 | 0 | } |
938 | 0 | op1_min = OP1_MIN_RANGE(); |
939 | 0 | if (op1_min == ZEND_LONG_MIN) { |
940 | 0 | return 1; |
941 | 0 | } |
942 | 0 | } |
943 | 0 | return 0; |
944 | 0 | case ZEND_ADD: |
945 | 0 | res = ssa_op->result_def; |
946 | 0 | if (res < 0 |
947 | 0 | || !ssa->var_info[res].has_range |
948 | 0 | || ssa->var_info[res].range.underflow) { |
949 | 0 | if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) { |
950 | 0 | return 1; |
951 | 0 | } |
952 | 0 | op1_min = OP1_MIN_RANGE(); |
953 | 0 | op2_min = OP2_MIN_RANGE(); |
954 | 0 | if (zend_add_will_overflow(op1_min, op2_min)) { |
955 | 0 | return 1; |
956 | 0 | } |
957 | 0 | } |
958 | 0 | if (res < 0 |
959 | 0 | || !ssa->var_info[res].has_range |
960 | 0 | || ssa->var_info[res].range.overflow) { |
961 | 0 | if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) { |
962 | 0 | return 1; |
963 | 0 | } |
964 | 0 | op1_max = OP1_MAX_RANGE(); |
965 | 0 | op2_max = OP2_MAX_RANGE(); |
966 | 0 | if (zend_add_will_overflow(op1_max, op2_max)) { |
967 | 0 | return 1; |
968 | 0 | } |
969 | 0 | } |
970 | 0 | return 0; |
971 | 0 | case ZEND_SUB: |
972 | 0 | res = ssa_op->result_def; |
973 | 0 | if (res < 0 |
974 | 0 | || !ssa->var_info[res].has_range |
975 | 0 | || ssa->var_info[res].range.underflow) { |
976 | 0 | if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) { |
977 | 0 | return 1; |
978 | 0 | } |
979 | 0 | op1_min = OP1_MIN_RANGE(); |
980 | 0 | op2_max = OP2_MAX_RANGE(); |
981 | 0 | if (zend_sub_will_overflow(op1_min, op2_max)) { |
982 | 0 | return 1; |
983 | 0 | } |
984 | 0 | } |
985 | 0 | if (res < 0 |
986 | 0 | || !ssa->var_info[res].has_range |
987 | 0 | || ssa->var_info[res].range.overflow) { |
988 | 0 | if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) { |
989 | 0 | return 1; |
990 | 0 | } |
991 | 0 | op1_max = OP1_MAX_RANGE(); |
992 | 0 | op2_min = OP2_MIN_RANGE(); |
993 | 0 | if (zend_sub_will_overflow(op1_max, op2_min)) { |
994 | 0 | return 1; |
995 | 0 | } |
996 | 0 | } |
997 | 0 | return 0; |
998 | 0 | case ZEND_MUL: |
999 | 0 | res = ssa_op->result_def; |
1000 | 0 | return (res < 0 || |
1001 | 0 | !ssa->var_info[res].has_range || |
1002 | 0 | ssa->var_info[res].range.underflow || |
1003 | 0 | ssa->var_info[res].range.overflow); |
1004 | 0 | case ZEND_ASSIGN_OP: |
1005 | 0 | if (opline->extended_value == ZEND_ADD) { |
1006 | 0 | res = ssa_op->op1_def; |
1007 | 0 | if (res < 0 |
1008 | 0 | || !ssa->var_info[res].has_range |
1009 | 0 | || ssa->var_info[res].range.underflow) { |
1010 | 0 | if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) { |
1011 | 0 | return 1; |
1012 | 0 | } |
1013 | 0 | op1_min = OP1_MIN_RANGE(); |
1014 | 0 | op2_min = OP2_MIN_RANGE(); |
1015 | 0 | if (zend_add_will_overflow(op1_min, op2_min)) { |
1016 | 0 | return 1; |
1017 | 0 | } |
1018 | 0 | } |
1019 | 0 | if (res < 0 |
1020 | 0 | || !ssa->var_info[res].has_range |
1021 | 0 | || ssa->var_info[res].range.overflow) { |
1022 | 0 | if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) { |
1023 | 0 | return 1; |
1024 | 0 | } |
1025 | 0 | op1_max = OP1_MAX_RANGE(); |
1026 | 0 | op2_max = OP2_MAX_RANGE(); |
1027 | 0 | if (zend_add_will_overflow(op1_max, op2_max)) { |
1028 | 0 | return 1; |
1029 | 0 | } |
1030 | 0 | } |
1031 | 0 | return 0; |
1032 | 0 | } else if (opline->extended_value == ZEND_SUB) { |
1033 | 0 | res = ssa_op->op1_def; |
1034 | 0 | if (res < 0 |
1035 | 0 | || !ssa->var_info[res].has_range |
1036 | 0 | || ssa->var_info[res].range.underflow) { |
1037 | 0 | if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) { |
1038 | 0 | return 1; |
1039 | 0 | } |
1040 | 0 | op1_min = OP1_MIN_RANGE(); |
1041 | 0 | op2_max = OP2_MAX_RANGE(); |
1042 | 0 | if (zend_sub_will_overflow(op1_min, op2_max)) { |
1043 | 0 | return 1; |
1044 | 0 | } |
1045 | 0 | } |
1046 | 0 | if (res < 0 |
1047 | 0 | || !ssa->var_info[res].has_range |
1048 | 0 | || ssa->var_info[res].range.overflow) { |
1049 | 0 | if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) { |
1050 | 0 | return 1; |
1051 | 0 | } |
1052 | 0 | op1_max = OP1_MAX_RANGE(); |
1053 | 0 | op2_min = OP2_MIN_RANGE(); |
1054 | 0 | if (zend_sub_will_overflow(op1_max, op2_min)) { |
1055 | 0 | return 1; |
1056 | 0 | } |
1057 | 0 | } |
1058 | 0 | return 0; |
1059 | 0 | } else if (opline->extended_value == ZEND_MUL) { |
1060 | 0 | res = ssa_op->op1_def; |
1061 | 0 | return (res < 0 || |
1062 | 0 | !ssa->var_info[res].has_range || |
1063 | 0 | ssa->var_info[res].range.underflow || |
1064 | 0 | ssa->var_info[res].range.overflow); |
1065 | 0 | } |
1066 | 0 | ZEND_FALLTHROUGH; |
1067 | 0 | default: |
1068 | 0 | return 1; |
1069 | 0 | } |
1070 | 0 | } |
1071 | | |
1072 | | static int zend_jit_build_cfg(const zend_op_array *op_array, zend_cfg *cfg) |
1073 | 0 | { |
1074 | 0 | uint32_t flags; |
1075 | |
|
1076 | 0 | flags = ZEND_CFG_STACKLESS | ZEND_CFG_NO_ENTRY_PREDECESSORS | ZEND_SSA_RC_INFERENCE_FLAG | ZEND_SSA_USE_CV_RESULTS | ZEND_CFG_RECV_ENTRY; |
1077 | |
|
1078 | 0 | zend_build_cfg(&CG(arena), op_array, flags, cfg); |
1079 | | |
1080 | | /* Don't JIT huge functions. Apart from likely being detrimental due to the amount of |
1081 | | * generated code, some of our analysis is recursive and will stack overflow with many |
1082 | | * blocks. */ |
1083 | 0 | if (cfg->blocks_count > 100000) { |
1084 | 0 | return FAILURE; |
1085 | 0 | } |
1086 | | |
1087 | 0 | zend_cfg_build_predecessors(&CG(arena), cfg); |
1088 | | |
1089 | | /* Compute Dominators Tree */ |
1090 | 0 | zend_cfg_compute_dominators_tree(op_array, cfg); |
1091 | | |
1092 | | /* Identify reducible and irreducible loops */ |
1093 | 0 | zend_cfg_identify_loops(op_array, cfg); |
1094 | |
|
1095 | 0 | return SUCCESS; |
1096 | 0 | } |
1097 | | |
1098 | | static int zend_jit_op_array_analyze1(const zend_op_array *op_array, zend_script *script, zend_ssa *ssa) |
1099 | 0 | { |
1100 | 0 | if (zend_jit_build_cfg(op_array, &ssa->cfg) != SUCCESS) { |
1101 | 0 | return FAILURE; |
1102 | 0 | } |
1103 | | |
1104 | | #if 0 |
1105 | | /* TODO: debugger and profiler supports? */ |
1106 | | if ((ssa->cfg.flags & ZEND_FUNC_HAS_EXTENDED_INFO)) { |
1107 | | return FAILURE; |
1108 | | } |
1109 | | #endif |
1110 | | |
1111 | | /* TODO: move this to zend_cfg.c ? */ |
1112 | 0 | if (!op_array->function_name) { |
1113 | 0 | ssa->cfg.flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS; |
1114 | 0 | } |
1115 | |
|
1116 | 0 | if ((JIT_G(opt_level) >= ZEND_JIT_LEVEL_OPT_FUNC) |
1117 | 0 | && ssa->cfg.blocks |
1118 | 0 | && op_array->last_try_catch == 0 |
1119 | 0 | && !(op_array->fn_flags & ZEND_ACC_GENERATOR) |
1120 | 0 | && !(ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS)) { |
1121 | 0 | if (zend_build_ssa(&CG(arena), script, op_array, ZEND_SSA_RC_INFERENCE | ZEND_SSA_USE_CV_RESULTS, ssa) != SUCCESS) { |
1122 | 0 | return FAILURE; |
1123 | 0 | } |
1124 | | |
1125 | 0 | zend_ssa_compute_use_def_chains(&CG(arena), op_array, ssa); |
1126 | |
|
1127 | 0 | zend_ssa_find_false_dependencies(op_array, ssa); |
1128 | |
|
1129 | 0 | zend_ssa_find_sccs(op_array, ssa); |
1130 | 0 | } |
1131 | | |
1132 | 0 | return SUCCESS; |
1133 | 0 | } |
1134 | | |
1135 | | static int zend_jit_op_array_analyze2(const zend_op_array *op_array, zend_script *script, zend_ssa *ssa, uint32_t optimization_level) |
1136 | 0 | { |
1137 | 0 | if ((JIT_G(opt_level) >= ZEND_JIT_LEVEL_OPT_FUNC) |
1138 | 0 | && ssa->cfg.blocks |
1139 | 0 | && op_array->last_try_catch == 0 |
1140 | 0 | && !(op_array->fn_flags & ZEND_ACC_GENERATOR) |
1141 | 0 | && !(ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS)) { |
1142 | 0 | if (zend_ssa_inference(&CG(arena), op_array, script, ssa, |
1143 | 0 | optimization_level & ~ZEND_OPTIMIZER_NARROW_TO_DOUBLE) != SUCCESS) { |
1144 | 0 | return FAILURE; |
1145 | 0 | } |
1146 | 0 | } |
1147 | | |
1148 | 0 | return SUCCESS; |
1149 | 0 | } |
1150 | | |
1151 | | static void zend_jit_allocate_registers(zend_jit_ctx *ctx, const zend_op_array *op_array, zend_ssa *ssa) |
1152 | 0 | { |
1153 | 0 | void *checkpoint; |
1154 | 0 | int candidates_count, i; |
1155 | 0 | zend_jit_reg_var *ra; |
1156 | |
|
1157 | 0 | checkpoint = zend_arena_checkpoint(CG(arena)); |
1158 | 0 | ra = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_jit_reg_var)); |
1159 | 0 | candidates_count = 0; |
1160 | 0 | for (i = 0; i < ssa->vars_count; i++) { |
1161 | 0 | if (zend_jit_may_be_in_reg(op_array, ssa, i)) { |
1162 | 0 | ra[i].ref = IR_NULL; |
1163 | 0 | candidates_count++; |
1164 | 0 | } |
1165 | 0 | } |
1166 | 0 | if (!candidates_count) { |
1167 | 0 | zend_arena_release(&CG(arena), checkpoint); |
1168 | 0 | return; |
1169 | 0 | } |
1170 | | |
1171 | 0 | if (JIT_G(opt_flags) & ZEND_JIT_REG_ALLOC_GLOBAL) { |
1172 | | /* Naive SSA resolution */ |
1173 | 0 | for (i = 0; i < ssa->vars_count; i++) { |
1174 | 0 | if (ssa->vars[i].definition_phi && !ssa->vars[i].no_val) { |
1175 | 0 | zend_ssa_phi *phi = ssa->vars[i].definition_phi; |
1176 | 0 | int k, src; |
1177 | |
|
1178 | 0 | if (phi->pi >= 0) { |
1179 | 0 | src = phi->sources[0]; |
1180 | 0 | if (ra[i].ref) { |
1181 | 0 | if (!ra[src].ref) { |
1182 | 0 | ra[i].flags |= ZREG_LOAD; |
1183 | 0 | } else { |
1184 | 0 | ra[i].flags |= ZREG_PI; |
1185 | 0 | } |
1186 | 0 | } else if (ra[src].ref) { |
1187 | 0 | ra[src].flags |= ZREG_STORE; |
1188 | 0 | } |
1189 | 0 | } else { |
1190 | 0 | int need_move = 0; |
1191 | |
|
1192 | 0 | for (k = 0; k < ssa->cfg.blocks[phi->block].predecessors_count; k++) { |
1193 | 0 | src = phi->sources[k]; |
1194 | 0 | if (src >= 0) { |
1195 | 0 | if (ssa->vars[src].definition_phi |
1196 | 0 | && ssa->vars[src].definition_phi->pi >= 0 |
1197 | 0 | && phi->block == ssa->vars[src].definition_phi->block) { |
1198 | | /* Skip zero-length interval for Pi variable */ |
1199 | 0 | src = ssa->vars[src].definition_phi->sources[0]; |
1200 | 0 | } |
1201 | 0 | if (ra[i].ref) { |
1202 | 0 | if (!ra[src].ref) { |
1203 | 0 | need_move = 1; |
1204 | 0 | } |
1205 | 0 | } else if (ra[src].ref) { |
1206 | 0 | need_move = 1; |
1207 | 0 | } |
1208 | 0 | } |
1209 | 0 | } |
1210 | 0 | if (need_move) { |
1211 | 0 | if (ra[i].ref) { |
1212 | 0 | ra[i].flags |= ZREG_LOAD; |
1213 | 0 | } |
1214 | 0 | for (k = 0; k < ssa->cfg.blocks[phi->block].predecessors_count; k++) { |
1215 | 0 | src = phi->sources[k]; |
1216 | 0 | if (src >= 0) { |
1217 | 0 | if (ssa->vars[src].definition_phi |
1218 | 0 | && ssa->vars[src].definition_phi->pi >= 0 |
1219 | 0 | && phi->block == ssa->vars[src].definition_phi->block) { |
1220 | | /* Skip zero-length interval for Pi variable */ |
1221 | 0 | src = ssa->vars[src].definition_phi->sources[0]; |
1222 | 0 | } |
1223 | 0 | if (ra[src].ref) { |
1224 | 0 | ra[src].flags |= ZREG_STORE; |
1225 | 0 | } |
1226 | 0 | } |
1227 | 0 | } |
1228 | 0 | } else { |
1229 | 0 | ra[i].flags |= ZREG_PHI; |
1230 | 0 | } |
1231 | 0 | } |
1232 | 0 | } |
1233 | 0 | } |
1234 | | |
1235 | | /* Remove useless register allocation */ |
1236 | 0 | for (i = 0; i < ssa->vars_count; i++) { |
1237 | 0 | if (ra[i].ref && |
1238 | 0 | ((ra[i].flags & ZREG_LOAD) || |
1239 | 0 | ((ra[i].flags & ZREG_STORE) && ssa->vars[i].definition >= 0)) && |
1240 | 0 | ssa->vars[i].use_chain < 0) { |
1241 | 0 | bool may_remove = true; |
1242 | 0 | zend_ssa_phi *phi = ssa->vars[i].phi_use_chain; |
1243 | |
|
1244 | 0 | while (phi) { |
1245 | 0 | if (ra[phi->ssa_var].ref && |
1246 | 0 | !(ra[phi->ssa_var].flags & ZREG_LOAD)) { |
1247 | 0 | may_remove = false; |
1248 | 0 | break; |
1249 | 0 | } |
1250 | 0 | phi = zend_ssa_next_use_phi(ssa, i, phi); |
1251 | 0 | } |
1252 | 0 | if (may_remove) { |
1253 | 0 | ra[i].ref = IR_UNUSED; |
1254 | 0 | } |
1255 | 0 | } |
1256 | 0 | } |
1257 | | |
1258 | | /* Remove intervals used once */ |
1259 | 0 | for (i = 0; i < ssa->vars_count; i++) { |
1260 | 0 | if (ra[i].ref) { |
1261 | 0 | if (!(ra[i].flags & (ZREG_LOAD|ZREG_STORE))) { |
1262 | 0 | uint32_t var_num = ssa->vars[i].var; |
1263 | 0 | uint32_t op_num = ssa->vars[i].definition; |
1264 | | |
1265 | | /* Check if a tempoary variable may be freed by exception handler */ |
1266 | 0 | if (op_array->last_live_range |
1267 | 0 | && var_num >= op_array->last_var |
1268 | 0 | && ssa->vars[i].definition >= 0 |
1269 | 0 | && ssa->ops[op_num].result_def == i) { |
1270 | 0 | const zend_live_range *range = op_array->live_range; |
1271 | 0 | int j; |
1272 | |
|
1273 | 0 | op_num++; |
1274 | 0 | if (op_array->opcodes[op_num].opcode == ZEND_OP_DATA) { |
1275 | 0 | op_num++; |
1276 | 0 | } |
1277 | 0 | for (j = 0; j < op_array->last_live_range; range++, j++) { |
1278 | 0 | if (range->start > op_num) { |
1279 | | /* further blocks will not be relevant... */ |
1280 | 0 | break; |
1281 | 0 | } else if (op_num < range->end && var_num == (range->var & ~ZEND_LIVE_MASK)) { |
1282 | | /* check if opcodes in range may throw */ |
1283 | 0 | do { |
1284 | 0 | if (zend_may_throw(op_array->opcodes + op_num, ssa->ops + op_num, op_array, ssa)) { |
1285 | 0 | ra[i].flags |= ZREG_STORE; |
1286 | 0 | break; |
1287 | 0 | } |
1288 | 0 | op_num++; |
1289 | 0 | if (op_array->opcodes[op_num].opcode == ZEND_OP_DATA) { |
1290 | 0 | op_num++; |
1291 | 0 | } |
1292 | 0 | } while (op_num < range->end); |
1293 | 0 | break; |
1294 | 0 | } |
1295 | 0 | } |
1296 | 0 | } |
1297 | 0 | } |
1298 | 0 | if ((ra[i].flags & ZREG_LOAD) |
1299 | 0 | && (ra[i].flags & ZREG_STORE) |
1300 | 0 | && (ssa->vars[i].use_chain < 0 |
1301 | 0 | || zend_ssa_next_use(ssa->ops, i, ssa->vars[i].use_chain) < 0)) { |
1302 | 0 | bool may_remove = true; |
1303 | 0 | zend_ssa_phi *phi = ssa->vars[i].phi_use_chain; |
1304 | |
|
1305 | 0 | while (phi) { |
1306 | 0 | if (ra[phi->ssa_var].ref && |
1307 | 0 | !(ra[phi->ssa_var].flags & ZREG_LOAD)) { |
1308 | 0 | may_remove = false; |
1309 | 0 | break; |
1310 | 0 | } |
1311 | 0 | phi = zend_ssa_next_use_phi(ssa, i, phi); |
1312 | 0 | } |
1313 | 0 | if (may_remove) { |
1314 | 0 | ra[i].ref = IR_UNUSED; |
1315 | 0 | } |
1316 | 0 | } |
1317 | 0 | } |
1318 | 0 | } |
1319 | 0 | } |
1320 | |
|
1321 | 0 | if (JIT_G(debug) & ZEND_JIT_DEBUG_REG_ALLOC) { |
1322 | 0 | fprintf(stderr, "Live Ranges \"%s\"\n", op_array->function_name ? ZSTR_VAL(op_array->function_name) : "[main]"); |
1323 | 0 | for (i = 0; i < ssa->vars_count; i++) { |
1324 | 0 | if (ra[i].ref) { |
1325 | 0 | fprintf(stderr, "#%d.", i); |
1326 | 0 | uint32_t var_num = ssa->vars[i].var; |
1327 | 0 | zend_dump_var(op_array, (var_num < op_array->last_var ? IS_CV : 0), var_num); |
1328 | 0 | if (ra[i].flags & ZREG_LOAD) { |
1329 | 0 | fprintf(stderr, " load"); |
1330 | 0 | } |
1331 | 0 | if (ra[i].flags & ZREG_STORE) { |
1332 | 0 | fprintf(stderr, " store"); |
1333 | 0 | } |
1334 | 0 | fprintf(stderr, "\n"); |
1335 | 0 | } |
1336 | 0 | } |
1337 | 0 | fprintf(stderr, "\n"); |
1338 | 0 | } |
1339 | |
|
1340 | 0 | ctx->ra = ra; |
1341 | 0 | } |
1342 | | |
1343 | | static int zend_jit_compute_post_order(zend_cfg *cfg, int start, int *post_order) |
1344 | 0 | { |
1345 | 0 | int count = 0; |
1346 | 0 | int b, n, *p; |
1347 | 0 | zend_basic_block *bb; |
1348 | 0 | zend_worklist worklist; |
1349 | 0 | ALLOCA_FLAG(use_heap) |
1350 | |
|
1351 | 0 | ZEND_WORKLIST_ALLOCA(&worklist, cfg->blocks_count, use_heap); |
1352 | 0 | zend_worklist_push(&worklist, start); |
1353 | |
|
1354 | 0 | while (zend_worklist_len(&worklist) != 0) { |
1355 | 0 | next: |
1356 | 0 | b = zend_worklist_peek(&worklist); |
1357 | 0 | bb = &cfg->blocks[b]; |
1358 | 0 | n = bb->successors_count; |
1359 | 0 | if (n > 0) { |
1360 | 0 | p = bb->successors; |
1361 | 0 | do { |
1362 | 0 | if (cfg->blocks[*p].flags & (ZEND_BB_CATCH|ZEND_BB_FINALLY|ZEND_BB_FINALLY_END)) { |
1363 | | /* skip */ |
1364 | 0 | } else if (zend_worklist_push(&worklist, *p)) { |
1365 | 0 | goto next; |
1366 | 0 | } |
1367 | 0 | p++; |
1368 | 0 | n--; |
1369 | 0 | } while (n > 0); |
1370 | 0 | } |
1371 | 0 | zend_worklist_pop(&worklist); |
1372 | 0 | post_order[count++] = b; |
1373 | 0 | } |
1374 | 0 | ZEND_WORKLIST_FREE_ALLOCA(&worklist, use_heap); |
1375 | 0 | return count; |
1376 | 0 | } |
1377 | | |
1378 | | static bool zend_jit_next_is_send_result(const zend_op *opline) |
1379 | 0 | { |
1380 | 0 | if (opline->result_type == IS_TMP_VAR |
1381 | 0 | && (opline+1)->opcode == ZEND_SEND_VAL |
1382 | 0 | && (opline+1)->op1_type == IS_TMP_VAR |
1383 | 0 | && (opline+1)->op2_type != IS_CONST |
1384 | 0 | && (opline+1)->op1.var == opline->result.var) { |
1385 | 0 | return 1; |
1386 | 0 | } |
1387 | 0 | return 0; |
1388 | 0 | } |
1389 | | |
1390 | | static bool zend_jit_supported_binary_op(uint8_t op, uint32_t op1_info, uint32_t op2_info) |
1391 | 0 | { |
1392 | 0 | if ((op1_info & MAY_BE_UNDEF) || (op2_info & MAY_BE_UNDEF)) { |
1393 | 0 | return false; |
1394 | 0 | } |
1395 | 0 | switch (op) { |
1396 | 0 | case ZEND_POW: |
1397 | 0 | case ZEND_DIV: |
1398 | | // TODO: check for division by zero ??? |
1399 | 0 | return false; |
1400 | 0 | case ZEND_ADD: |
1401 | 0 | case ZEND_SUB: |
1402 | 0 | case ZEND_MUL: |
1403 | 0 | return (op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) |
1404 | 0 | && (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE)); |
1405 | 0 | case ZEND_BW_OR: |
1406 | 0 | case ZEND_BW_AND: |
1407 | 0 | case ZEND_BW_XOR: |
1408 | 0 | case ZEND_SL: |
1409 | 0 | case ZEND_SR: |
1410 | 0 | case ZEND_MOD: |
1411 | 0 | return (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG); |
1412 | 0 | case ZEND_CONCAT: |
1413 | 0 | return (op1_info & MAY_BE_STRING) && (op2_info & MAY_BE_STRING); |
1414 | 0 | EMPTY_SWITCH_DEFAULT_CASE() |
1415 | 0 | } |
1416 | 0 | } |
1417 | | |
1418 | | static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op *rt_opline) |
1419 | 0 | { |
1420 | 0 | int b, i, end; |
1421 | 0 | zend_op *opline; |
1422 | 0 | zend_jit_ctx ctx; |
1423 | 0 | zend_jit_ctx *jit = &ctx; |
1424 | 0 | zend_jit_reg_var *ra = NULL; |
1425 | 0 | zend_vm_opcode_handler_t handler; |
1426 | 0 | int call_level = 0; |
1427 | 0 | void *checkpoint = NULL; |
1428 | 0 | bool recv_emitted = false; /* emitted at least one RECV opcode */ |
1429 | 0 | uint8_t smart_branch_opcode; |
1430 | 0 | uint32_t target_label, target_label2; |
1431 | 0 | uint32_t op1_info, op1_def_info, op2_info, res_info, res_use_info, op1_mem_info; |
1432 | 0 | zend_jit_addr op1_addr, op1_def_addr, op2_addr, op2_def_addr, res_addr; |
1433 | 0 | zend_class_entry *ce = NULL; |
1434 | 0 | bool ce_is_instanceof; |
1435 | 0 | bool on_this; |
1436 | |
|
1437 | 0 | ZEND_ASSERT(!(op_array->fn_flags & ZEND_ACC_CLOSURE) || !(op_array->scope)); |
1438 | |
|
1439 | 0 | if (JIT_G(bisect_limit)) { |
1440 | 0 | jit_bisect_pos++; |
1441 | 0 | if (jit_bisect_pos >= JIT_G(bisect_limit)) { |
1442 | 0 | if (jit_bisect_pos == JIT_G(bisect_limit)) { |
1443 | 0 | fprintf(stderr, "Not JITing %s%s%s in %s:%d and after due to jit_bisect_limit\n", |
1444 | 0 | op_array->scope ? ZSTR_VAL(op_array->scope->name) : "", |
1445 | 0 | op_array->scope ? "::" : "", |
1446 | 0 | op_array->function_name ? ZSTR_VAL(op_array->function_name) : "{main}", |
1447 | 0 | ZSTR_VAL(op_array->filename), op_array->line_start); |
1448 | 0 | } |
1449 | 0 | return FAILURE; |
1450 | 0 | } |
1451 | 0 | } |
1452 | | |
1453 | 0 | if (ssa->cfg.flags & ZEND_FUNC_IRREDUCIBLE) { |
1454 | | /* We can't order blocks properly */ |
1455 | 0 | return FAILURE; |
1456 | 0 | } |
1457 | | |
1458 | 0 | if (rt_opline) { |
1459 | | /* Set BB_ENTRY flag to limit register usage across the OSR ENTRY point */ |
1460 | 0 | ssa->cfg.blocks[ssa->cfg.map[rt_opline - op_array->opcodes]].flags |= ZEND_BB_ENTRY; |
1461 | 0 | } |
1462 | |
|
1463 | 0 | zend_jit_start(&ctx, op_array, ssa); |
1464 | 0 | if (JIT_G(opt_flags) & (ZEND_JIT_REG_ALLOC_LOCAL|ZEND_JIT_REG_ALLOC_GLOBAL)) { |
1465 | 0 | checkpoint = zend_arena_checkpoint(CG(arena)); |
1466 | 0 | zend_jit_allocate_registers(&ctx, op_array, ssa); |
1467 | 0 | ra = ctx.ra; |
1468 | 0 | } |
1469 | | |
1470 | | /* Process blocks in Reverse Post Order */ |
1471 | 0 | int *sorted_blocks = alloca(sizeof(int) * ssa->cfg.blocks_count); |
1472 | 0 | int n = zend_jit_compute_post_order(&ssa->cfg, 0, sorted_blocks); |
1473 | |
|
1474 | 0 | while (n > 0) { |
1475 | 0 | b = sorted_blocks[--n]; |
1476 | 0 | if ((ssa->cfg.blocks[b].flags & ZEND_BB_REACHABLE) == 0) { |
1477 | 0 | continue; |
1478 | 0 | } |
1479 | | |
1480 | 0 | if (ssa->cfg.blocks[b].flags & (ZEND_BB_START|ZEND_BB_RECV_ENTRY)) { |
1481 | 0 | opline = op_array->opcodes + ssa->cfg.blocks[b].start; |
1482 | 0 | if (ssa->cfg.flags & ZEND_CFG_RECV_ENTRY) { |
1483 | 0 | if (opline->opcode == ZEND_RECV_INIT) { |
1484 | 0 | if (JIT_G(opt_level) < ZEND_JIT_LEVEL_INLINE) { |
1485 | 0 | if (opline != op_array->opcodes && (opline-1)->opcode != ZEND_RECV_INIT) { |
1486 | 0 | zend_jit_recv_entry(&ctx, b); |
1487 | 0 | } |
1488 | 0 | } else { |
1489 | 0 | if (opline != op_array->opcodes && recv_emitted) { |
1490 | 0 | zend_jit_recv_entry(&ctx, b); |
1491 | 0 | } |
1492 | 0 | } |
1493 | 0 | recv_emitted = true; |
1494 | 0 | } else if (opline->opcode == ZEND_RECV) { |
1495 | 0 | if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) { |
1496 | | /* skip */ |
1497 | 0 | zend_jit_bb_start(&ctx, b); |
1498 | 0 | zend_jit_bb_end(&ctx, b); |
1499 | 0 | continue; |
1500 | 0 | } else if (recv_emitted) { |
1501 | 0 | zend_jit_recv_entry(&ctx, b); |
1502 | 0 | } else { |
1503 | 0 | recv_emitted = true; |
1504 | 0 | } |
1505 | 0 | } else { |
1506 | 0 | if (recv_emitted) { |
1507 | 0 | zend_jit_recv_entry(&ctx, b); |
1508 | 0 | } else if (JIT_G(opt_level) < ZEND_JIT_LEVEL_INLINE && |
1509 | 0 | ssa->cfg.blocks[b].len == 1 && |
1510 | 0 | (ssa->cfg.blocks[b].flags & ZEND_BB_EXIT)) { |
1511 | | /* don't generate code for BB with single opcode */ |
1512 | 0 | zend_jit_free_ctx(&ctx); |
1513 | |
|
1514 | 0 | if (JIT_G(opt_flags) & (ZEND_JIT_REG_ALLOC_LOCAL|ZEND_JIT_REG_ALLOC_GLOBAL)) { |
1515 | 0 | zend_arena_release(&CG(arena), checkpoint); |
1516 | 0 | } |
1517 | 0 | return SUCCESS; |
1518 | 0 | } |
1519 | 0 | } |
1520 | 0 | } else if (JIT_G(opt_level) < ZEND_JIT_LEVEL_INLINE && |
1521 | 0 | ssa->cfg.blocks[b].len == 1 && |
1522 | 0 | (ssa->cfg.blocks[b].flags & ZEND_BB_EXIT)) { |
1523 | | /* don't generate code for BB with single opcode */ |
1524 | 0 | zend_jit_free_ctx(&ctx); |
1525 | |
|
1526 | 0 | if (JIT_G(opt_flags) & (ZEND_JIT_REG_ALLOC_LOCAL|ZEND_JIT_REG_ALLOC_GLOBAL)) { |
1527 | 0 | zend_arena_release(&CG(arena), checkpoint); |
1528 | 0 | } |
1529 | 0 | return SUCCESS; |
1530 | 0 | } |
1531 | 0 | } |
1532 | | |
1533 | 0 | zend_jit_bb_start(&ctx, b); |
1534 | |
|
1535 | 0 | if ((JIT_G(opt_flags) & ZEND_JIT_REG_ALLOC_GLOBAL) && ctx.ra) { |
1536 | 0 | zend_ssa_phi *phi = ssa->blocks[b].phis; |
1537 | | |
1538 | | /* First try to insert IR Phi */ |
1539 | 0 | while (phi) { |
1540 | 0 | zend_jit_reg_var *ival = &ctx.ra[phi->ssa_var]; |
1541 | |
|
1542 | 0 | if (ival->ref) { |
1543 | 0 | if (ival->flags & ZREG_PI) { |
1544 | 0 | zend_jit_gen_pi(jit, phi); |
1545 | 0 | } else if (ival->flags & ZREG_PHI) { |
1546 | 0 | zend_jit_gen_phi(jit, phi); |
1547 | 0 | } |
1548 | 0 | } |
1549 | 0 | phi = phi->next; |
1550 | 0 | } |
1551 | 0 | } |
1552 | |
|
1553 | 0 | if (rt_opline |
1554 | 0 | && (ssa->cfg.blocks[b].flags & (ZEND_BB_START|ZEND_BB_RECV_ENTRY)) == 0 |
1555 | 0 | && rt_opline == op_array->opcodes + ssa->cfg.blocks[b].start) { |
1556 | 0 | zend_jit_osr_entry(&ctx, b); /* OSR (On-Stack-Replacement) Entry-Point */ |
1557 | 0 | } |
1558 | |
|
1559 | 0 | if (JIT_G(opt_level) < ZEND_JIT_LEVEL_INLINE) { |
1560 | 0 | if ((ssa->cfg.blocks[b].flags & ZEND_BB_FOLLOW) |
1561 | 0 | && ssa->cfg.blocks[b].start != 0 |
1562 | 0 | && (op_array->opcodes[ssa->cfg.blocks[b].start - 1].opcode == ZEND_NOP |
1563 | 0 | || op_array->opcodes[ssa->cfg.blocks[b].start - 1].opcode == ZEND_SWITCH_LONG |
1564 | 0 | || op_array->opcodes[ssa->cfg.blocks[b].start - 1].opcode == ZEND_SWITCH_STRING |
1565 | 0 | || op_array->opcodes[ssa->cfg.blocks[b].start - 1].opcode == ZEND_MATCH)) { |
1566 | 0 | zend_jit_reset_last_valid_opline(&ctx); |
1567 | 0 | } else { |
1568 | 0 | zend_jit_set_last_valid_opline(&ctx, op_array->opcodes + ssa->cfg.blocks[b].start); |
1569 | 0 | } |
1570 | 0 | } else if (ssa->cfg.blocks[b].flags & ZEND_BB_TARGET) { |
1571 | 0 | zend_jit_reset_last_valid_opline(&ctx); |
1572 | 0 | } else if (ssa->cfg.blocks[b].flags & ZEND_BB_RECV_ENTRY) { |
1573 | 0 | zend_jit_reset_last_valid_opline(&ctx); |
1574 | 0 | } else if (ssa->cfg.blocks[b].flags & (ZEND_BB_START|ZEND_BB_ENTRY)) { |
1575 | 0 | zend_jit_set_last_valid_opline(&ctx, op_array->opcodes + ssa->cfg.blocks[b].start); |
1576 | 0 | } |
1577 | 0 | if (ssa->cfg.blocks[b].flags & ZEND_BB_LOOP_HEADER) { |
1578 | 0 | zend_jit_check_timeout(&ctx, op_array->opcodes + ssa->cfg.blocks[b].start, NULL); |
1579 | 0 | } |
1580 | 0 | if (!ssa->cfg.blocks[b].len) { |
1581 | 0 | zend_jit_bb_end(&ctx, b); |
1582 | 0 | continue; |
1583 | 0 | } |
1584 | 0 | if ((JIT_G(opt_flags) & ZEND_JIT_REG_ALLOC_GLOBAL) && ra) { |
1585 | 0 | zend_ssa_phi *phi = ssa->blocks[b].phis; |
1586 | |
|
1587 | 0 | while (phi) { |
1588 | 0 | zend_jit_reg_var *ival = &ra[phi->ssa_var]; |
1589 | |
|
1590 | 0 | if (ival->ref) { |
1591 | 0 | if (ival->flags & ZREG_LOAD) { |
1592 | 0 | ZEND_ASSERT(ival->ref == IR_NULL); |
1593 | |
|
1594 | 0 | if (!zend_jit_load_var(&ctx, ssa->var_info[phi->ssa_var].type, ssa->vars[phi->ssa_var].var, phi->ssa_var)) { |
1595 | 0 | goto jit_failure; |
1596 | 0 | } |
1597 | 0 | } else if (ival->flags & ZREG_STORE) { |
1598 | 0 | ZEND_ASSERT(ival->ref != IR_NULL); |
1599 | |
|
1600 | 0 | if (!zend_jit_store_var(&ctx, ssa->var_info[phi->ssa_var].type, ssa->vars[phi->ssa_var].var, phi->ssa_var, 1)) { |
1601 | 0 | goto jit_failure; |
1602 | 0 | } |
1603 | 0 | } |
1604 | 0 | } |
1605 | 0 | phi = phi->next; |
1606 | 0 | } |
1607 | 0 | } |
1608 | 0 | end = ssa->cfg.blocks[b].start + ssa->cfg.blocks[b].len - 1; |
1609 | 0 | for (i = ssa->cfg.blocks[b].start; i <= end; i++) { |
1610 | 0 | zend_ssa_op *ssa_op = ssa->ops ? &ssa->ops[i] : NULL; |
1611 | 0 | opline = op_array->opcodes + i; |
1612 | 0 | if (zend_jit_inc_call_level(opline->opcode)) { |
1613 | 0 | call_level++; |
1614 | 0 | } |
1615 | |
|
1616 | 0 | if (JIT_G(opt_level) >= ZEND_JIT_LEVEL_INLINE) { |
1617 | 0 | switch (opline->opcode) { |
1618 | 0 | case ZEND_PRE_INC: |
1619 | 0 | case ZEND_PRE_DEC: |
1620 | 0 | case ZEND_POST_INC: |
1621 | 0 | case ZEND_POST_DEC: |
1622 | 0 | if (opline->op1_type != IS_CV) { |
1623 | 0 | break; |
1624 | 0 | } |
1625 | 0 | op1_info = OP1_INFO(); |
1626 | 0 | if (!(op1_info & MAY_BE_LONG)) { |
1627 | 0 | break; |
1628 | 0 | } |
1629 | 0 | if (opline->result_type != IS_UNUSED) { |
1630 | 0 | res_use_info = -1; |
1631 | |
|
1632 | 0 | if (opline->result_type == IS_CV |
1633 | 0 | && ssa->vars |
1634 | 0 | && ssa_op->result_use >= 0 |
1635 | 0 | && !ssa->vars[ssa_op->result_use].no_val) { |
1636 | 0 | zend_jit_addr res_use_addr = RES_USE_REG_ADDR(); |
1637 | |
|
1638 | 0 | if (Z_MODE(res_use_addr) != IS_REG |
1639 | 0 | || Z_LOAD(res_use_addr) |
1640 | 0 | || Z_STORE(res_use_addr)) { |
1641 | 0 | res_use_info = RES_USE_INFO(); |
1642 | 0 | } |
1643 | 0 | } |
1644 | 0 | res_info = RES_INFO(); |
1645 | 0 | res_addr = RES_REG_ADDR(); |
1646 | 0 | } else { |
1647 | 0 | res_use_info = -1; |
1648 | 0 | res_info = -1; |
1649 | 0 | res_addr = 0; |
1650 | 0 | } |
1651 | 0 | op1_def_info = OP1_DEF_INFO(); |
1652 | 0 | if (!zend_jit_inc_dec(&ctx, opline, |
1653 | 0 | op1_info, OP1_REG_ADDR(), |
1654 | 0 | op1_def_info, OP1_DEF_REG_ADDR(), |
1655 | 0 | res_use_info, res_info, |
1656 | 0 | res_addr, |
1657 | 0 | (op1_info & MAY_BE_LONG) && (op1_def_info & MAY_BE_DOUBLE) && zend_may_overflow(opline, ssa_op, op_array, ssa), |
1658 | 0 | zend_may_throw(opline, ssa_op, op_array, ssa))) { |
1659 | 0 | goto jit_failure; |
1660 | 0 | } |
1661 | 0 | goto done; |
1662 | 0 | case ZEND_BW_OR: |
1663 | 0 | case ZEND_BW_AND: |
1664 | 0 | case ZEND_BW_XOR: |
1665 | 0 | case ZEND_SL: |
1666 | 0 | case ZEND_SR: |
1667 | 0 | case ZEND_MOD: |
1668 | 0 | if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) { |
1669 | 0 | break; |
1670 | 0 | } |
1671 | 0 | op1_info = OP1_INFO(); |
1672 | 0 | op2_info = OP2_INFO(); |
1673 | 0 | if (!(op1_info & MAY_BE_LONG) |
1674 | 0 | || !(op2_info & MAY_BE_LONG)) { |
1675 | 0 | break; |
1676 | 0 | } |
1677 | 0 | res_addr = RES_REG_ADDR(); |
1678 | 0 | if (Z_MODE(res_addr) != IS_REG |
1679 | 0 | && (i + 1) <= end |
1680 | 0 | && zend_jit_next_is_send_result(opline)) { |
1681 | 0 | i++; |
1682 | 0 | res_use_info = -1; |
1683 | 0 | res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, (opline+1)->result.var); |
1684 | 0 | if (!zend_jit_reuse_ip(&ctx)) { |
1685 | 0 | goto jit_failure; |
1686 | 0 | } |
1687 | 0 | } else { |
1688 | 0 | res_use_info = -1; |
1689 | |
|
1690 | 0 | if (opline->result_type == IS_CV |
1691 | 0 | && ssa->vars |
1692 | 0 | && ssa_op->result_use >= 0 |
1693 | 0 | && !ssa->vars[ssa_op->result_use].no_val) { |
1694 | 0 | zend_jit_addr res_use_addr = RES_USE_REG_ADDR(); |
1695 | |
|
1696 | 0 | if (Z_MODE(res_use_addr) != IS_REG |
1697 | 0 | || Z_LOAD(res_use_addr) |
1698 | 0 | || Z_STORE(res_use_addr)) { |
1699 | 0 | res_use_info = RES_USE_INFO(); |
1700 | 0 | } |
1701 | 0 | } |
1702 | 0 | } |
1703 | 0 | if (!zend_jit_long_math(&ctx, opline, |
1704 | 0 | op1_info, OP1_RANGE(), OP1_REG_ADDR(), |
1705 | 0 | op2_info, OP2_RANGE(), OP2_REG_ADDR(), |
1706 | 0 | res_use_info, RES_INFO(), res_addr, |
1707 | 0 | zend_may_throw(opline, ssa_op, op_array, ssa))) { |
1708 | 0 | goto jit_failure; |
1709 | 0 | } |
1710 | 0 | goto done; |
1711 | 0 | case ZEND_ADD: |
1712 | 0 | case ZEND_SUB: |
1713 | 0 | case ZEND_MUL: |
1714 | | // case ZEND_DIV: // TODO: check for division by zero ??? |
1715 | 0 | if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) { |
1716 | 0 | break; |
1717 | 0 | } |
1718 | 0 | op1_info = OP1_INFO(); |
1719 | 0 | op2_info = OP2_INFO(); |
1720 | 0 | if ((op1_info & MAY_BE_UNDEF) || (op2_info & MAY_BE_UNDEF)) { |
1721 | 0 | break; |
1722 | 0 | } |
1723 | 0 | if (opline->opcode == ZEND_ADD && |
1724 | 0 | (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY && |
1725 | 0 | (op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) { |
1726 | | /* pass */ |
1727 | 0 | } else if (!(op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) || |
1728 | 0 | !(op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) { |
1729 | 0 | break; |
1730 | 0 | } |
1731 | 0 | res_addr = RES_REG_ADDR(); |
1732 | 0 | if (Z_MODE(res_addr) != IS_REG |
1733 | 0 | && (i + 1) <= end |
1734 | 0 | && zend_jit_next_is_send_result(opline)) { |
1735 | 0 | i++; |
1736 | 0 | res_use_info = -1; |
1737 | 0 | res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, (opline+1)->result.var); |
1738 | 0 | if (!zend_jit_reuse_ip(&ctx)) { |
1739 | 0 | goto jit_failure; |
1740 | 0 | } |
1741 | 0 | } else { |
1742 | 0 | res_use_info = -1; |
1743 | |
|
1744 | 0 | if (opline->result_type == IS_CV |
1745 | 0 | && ssa->vars |
1746 | 0 | && ssa_op->result_use >= 0 |
1747 | 0 | && !ssa->vars[ssa_op->result_use].no_val) { |
1748 | 0 | zend_jit_addr res_use_addr = RES_USE_REG_ADDR(); |
1749 | |
|
1750 | 0 | if (Z_MODE(res_use_addr) != IS_REG |
1751 | 0 | || Z_LOAD(res_use_addr) |
1752 | 0 | || Z_STORE(res_use_addr)) { |
1753 | 0 | res_use_info = RES_USE_INFO(); |
1754 | 0 | } |
1755 | 0 | } |
1756 | 0 | } |
1757 | 0 | res_info = RES_INFO(); |
1758 | 0 | if (opline->opcode == ZEND_ADD && |
1759 | 0 | (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY && |
1760 | 0 | (op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) { |
1761 | 0 | if (!zend_jit_add_arrays(&ctx, opline, op1_info, OP1_REG_ADDR(), op2_info, OP2_REG_ADDR(), res_addr)) { |
1762 | 0 | goto jit_failure; |
1763 | 0 | } |
1764 | 0 | } else { |
1765 | 0 | if (!zend_jit_math(&ctx, opline, |
1766 | 0 | op1_info, OP1_REG_ADDR(), |
1767 | 0 | op2_info, OP2_REG_ADDR(), |
1768 | 0 | res_use_info, res_info, res_addr, |
1769 | 0 | (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (res_info & MAY_BE_DOUBLE) && zend_may_overflow(opline, ssa_op, op_array, ssa), |
1770 | 0 | zend_may_throw(opline, ssa_op, op_array, ssa))) { |
1771 | 0 | goto jit_failure; |
1772 | 0 | } |
1773 | 0 | } |
1774 | 0 | goto done; |
1775 | 0 | case ZEND_CONCAT: |
1776 | 0 | case ZEND_FAST_CONCAT: |
1777 | 0 | if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) { |
1778 | 0 | break; |
1779 | 0 | } |
1780 | 0 | op1_info = OP1_INFO(); |
1781 | 0 | op2_info = OP2_INFO(); |
1782 | 0 | if ((op1_info & MAY_BE_UNDEF) || (op2_info & MAY_BE_UNDEF)) { |
1783 | 0 | break; |
1784 | 0 | } |
1785 | 0 | if (!(op1_info & MAY_BE_STRING) || |
1786 | 0 | !(op2_info & MAY_BE_STRING)) { |
1787 | 0 | break; |
1788 | 0 | } |
1789 | 0 | res_addr = RES_REG_ADDR(); |
1790 | 0 | if ((i + 1) <= end |
1791 | 0 | && zend_jit_next_is_send_result(opline)) { |
1792 | 0 | i++; |
1793 | 0 | res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, (opline+1)->result.var); |
1794 | 0 | if (!zend_jit_reuse_ip(&ctx)) { |
1795 | 0 | goto jit_failure; |
1796 | 0 | } |
1797 | 0 | } |
1798 | 0 | if (!zend_jit_concat(&ctx, opline, |
1799 | 0 | op1_info, op2_info, res_addr, |
1800 | 0 | zend_may_throw(opline, ssa_op, op_array, ssa))) { |
1801 | 0 | goto jit_failure; |
1802 | 0 | } |
1803 | 0 | goto done; |
1804 | 0 | case ZEND_ASSIGN_OP: |
1805 | 0 | if (opline->op1_type != IS_CV || opline->result_type != IS_UNUSED) { |
1806 | 0 | break; |
1807 | 0 | } |
1808 | 0 | if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) { |
1809 | 0 | break; |
1810 | 0 | } |
1811 | 0 | op1_info = OP1_INFO(); |
1812 | 0 | op2_info = OP2_INFO(); |
1813 | 0 | if (!zend_jit_supported_binary_op( |
1814 | 0 | opline->extended_value, op1_info, op2_info)) { |
1815 | 0 | break; |
1816 | 0 | } |
1817 | 0 | op1_addr = OP1_REG_ADDR(); |
1818 | 0 | op1_mem_info = -1; |
1819 | 0 | if (Z_MODE(op1_addr) != IS_REG |
1820 | 0 | || Z_LOAD(op1_addr) |
1821 | 0 | || Z_STORE(op1_addr)) { |
1822 | 0 | op1_mem_info = op1_info; |
1823 | 0 | } |
1824 | 0 | op1_def_info = OP1_DEF_INFO(); |
1825 | 0 | if (!zend_jit_assign_op(&ctx, opline, |
1826 | 0 | op1_info, op1_addr, OP1_RANGE(), |
1827 | 0 | op1_def_info, OP1_DEF_REG_ADDR(), op1_mem_info, |
1828 | 0 | op2_info, OP2_REG_ADDR(), OP2_RANGE(), |
1829 | 0 | (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (op1_def_info & MAY_BE_DOUBLE) && zend_may_overflow(opline, ssa_op, op_array, ssa), |
1830 | 0 | zend_may_throw(opline, ssa_op, op_array, ssa))) { |
1831 | 0 | goto jit_failure; |
1832 | 0 | } |
1833 | 0 | goto done; |
1834 | 0 | case ZEND_ASSIGN_DIM_OP: |
1835 | 0 | if (opline->op1_type != IS_CV || opline->result_type != IS_UNUSED) { |
1836 | 0 | break; |
1837 | 0 | } |
1838 | 0 | if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) { |
1839 | 0 | break; |
1840 | 0 | } |
1841 | 0 | if (!zend_jit_supported_binary_op( |
1842 | 0 | opline->extended_value, MAY_BE_ANY, OP1_DATA_INFO())) { |
1843 | 0 | break; |
1844 | 0 | } |
1845 | 0 | if (!zend_jit_assign_dim_op(&ctx, opline, |
1846 | 0 | OP1_INFO(), OP1_DEF_INFO(), OP1_REG_ADDR(), 0, |
1847 | 0 | OP2_INFO(), (opline->op2_type != IS_UNUSED) ? OP2_REG_ADDR() : 0, |
1848 | 0 | (opline->op2_type != IS_UNUSED) ? OP2_RANGE() : NULL, |
1849 | 0 | OP1_DATA_INFO(), OP1_DATA_REG_ADDR(), OP1_DATA_RANGE(), IS_UNKNOWN, |
1850 | 0 | zend_may_throw(opline, ssa_op, op_array, ssa))) { |
1851 | 0 | goto jit_failure; |
1852 | 0 | } |
1853 | 0 | goto done; |
1854 | 0 | case ZEND_ASSIGN_DIM: |
1855 | 0 | if (opline->op1_type != IS_CV) { |
1856 | 0 | break; |
1857 | 0 | } |
1858 | 0 | if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) { |
1859 | 0 | break; |
1860 | 0 | } |
1861 | 0 | if (!zend_jit_assign_dim(&ctx, opline, |
1862 | 0 | OP1_INFO(), OP1_REG_ADDR(), 0, |
1863 | 0 | OP2_INFO(), (opline->op2_type != IS_UNUSED) ? OP2_REG_ADDR() : 0, |
1864 | 0 | (opline->op2_type != IS_UNUSED) ? OP2_RANGE() : NULL, |
1865 | 0 | OP1_DATA_INFO(), OP1_DATA_REG_ADDR(), |
1866 | 0 | (ctx.ra && (ssa_op+1)->op1_def >= 0) ? OP1_DATA_DEF_REG_ADDR() : 0, |
1867 | 0 | (opline->result_type != IS_UNUSED) ? RES_REG_ADDR() : 0, |
1868 | 0 | IS_UNKNOWN, |
1869 | 0 | zend_may_throw(opline, ssa_op, op_array, ssa))) { |
1870 | 0 | goto jit_failure; |
1871 | 0 | } |
1872 | 0 | goto done; |
1873 | 0 | case ZEND_PRE_INC_OBJ: |
1874 | 0 | case ZEND_PRE_DEC_OBJ: |
1875 | 0 | case ZEND_POST_INC_OBJ: |
1876 | 0 | case ZEND_POST_DEC_OBJ: |
1877 | 0 | if (opline->op2_type != IS_CONST |
1878 | 0 | || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING |
1879 | 0 | || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') { |
1880 | 0 | break; |
1881 | 0 | } |
1882 | 0 | if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) { |
1883 | 0 | break; |
1884 | 0 | } |
1885 | 0 | ce = NULL; |
1886 | 0 | ce_is_instanceof = false; |
1887 | 0 | on_this = false; |
1888 | 0 | if (opline->op1_type == IS_UNUSED) { |
1889 | 0 | op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; |
1890 | 0 | ce = op_array->scope; |
1891 | | /* scope is NULL for closures. */ |
1892 | 0 | if (ce) { |
1893 | 0 | ce_is_instanceof = !(ce->ce_flags & ZEND_ACC_FINAL); |
1894 | 0 | } |
1895 | 0 | op1_addr = 0; |
1896 | 0 | on_this = true; |
1897 | 0 | } else { |
1898 | 0 | op1_info = OP1_INFO(); |
1899 | 0 | if (!(op1_info & MAY_BE_OBJECT)) { |
1900 | 0 | break; |
1901 | 0 | } |
1902 | 0 | op1_addr = OP1_REG_ADDR(); |
1903 | 0 | if (ssa->var_info && ssa->ops) { |
1904 | 0 | zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes]; |
1905 | 0 | if (ssa_op->op1_use >= 0) { |
1906 | 0 | zend_ssa_var_info *op1_ssa = ssa->var_info + ssa_op->op1_use; |
1907 | 0 | if (op1_ssa->ce && !op1_ssa->ce->create_object) { |
1908 | 0 | ce = op1_ssa->ce; |
1909 | 0 | ce_is_instanceof = op1_ssa->is_instanceof; |
1910 | 0 | } |
1911 | 0 | } |
1912 | 0 | } |
1913 | 0 | } |
1914 | 0 | if (!zend_jit_incdec_obj(&ctx, opline, op_array, ssa, ssa_op, |
1915 | 0 | op1_info, op1_addr, |
1916 | 0 | 0, ce, ce_is_instanceof, on_this, 0, NULL, IS_UNKNOWN)) { |
1917 | 0 | goto jit_failure; |
1918 | 0 | } |
1919 | 0 | goto done; |
1920 | 0 | case ZEND_ASSIGN_OBJ_OP: |
1921 | 0 | if (opline->result_type != IS_UNUSED) { |
1922 | 0 | break; |
1923 | 0 | } |
1924 | 0 | if (opline->op2_type != IS_CONST |
1925 | 0 | || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING |
1926 | 0 | || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') { |
1927 | 0 | break; |
1928 | 0 | } |
1929 | 0 | if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) { |
1930 | 0 | break; |
1931 | 0 | } |
1932 | 0 | if (!zend_jit_supported_binary_op( |
1933 | 0 | opline->extended_value, MAY_BE_ANY, OP1_DATA_INFO())) { |
1934 | 0 | break; |
1935 | 0 | } |
1936 | 0 | ce = NULL; |
1937 | 0 | ce_is_instanceof = false; |
1938 | 0 | on_this = false; |
1939 | 0 | if (opline->op1_type == IS_UNUSED) { |
1940 | 0 | op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; |
1941 | 0 | ce = op_array->scope; |
1942 | | /* scope is NULL for closures. */ |
1943 | 0 | if (ce) { |
1944 | 0 | ce_is_instanceof = !(ce->ce_flags & ZEND_ACC_FINAL); |
1945 | 0 | } |
1946 | 0 | op1_addr = 0; |
1947 | 0 | on_this = true; |
1948 | 0 | } else { |
1949 | 0 | op1_info = OP1_INFO(); |
1950 | 0 | if (!(op1_info & MAY_BE_OBJECT)) { |
1951 | 0 | break; |
1952 | 0 | } |
1953 | 0 | op1_addr = OP1_REG_ADDR(); |
1954 | 0 | if (ssa->var_info && ssa->ops) { |
1955 | 0 | zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes]; |
1956 | 0 | if (ssa_op->op1_use >= 0) { |
1957 | 0 | zend_ssa_var_info *op1_ssa = ssa->var_info + ssa_op->op1_use; |
1958 | 0 | if (op1_ssa->ce && !op1_ssa->ce->create_object) { |
1959 | 0 | ce = op1_ssa->ce; |
1960 | 0 | ce_is_instanceof = op1_ssa->is_instanceof; |
1961 | 0 | } |
1962 | 0 | } |
1963 | 0 | } |
1964 | 0 | } |
1965 | 0 | if (!zend_jit_assign_obj_op(&ctx, opline, op_array, ssa, ssa_op, |
1966 | 0 | op1_info, op1_addr, OP1_DATA_INFO(), OP1_DATA_REG_ADDR(), OP1_DATA_RANGE(), |
1967 | 0 | 0, ce, ce_is_instanceof, on_this, 0, NULL, IS_UNKNOWN)) { |
1968 | 0 | goto jit_failure; |
1969 | 0 | } |
1970 | 0 | goto done; |
1971 | 0 | case ZEND_ASSIGN_OBJ: |
1972 | 0 | if (opline->op2_type != IS_CONST |
1973 | 0 | || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING |
1974 | 0 | || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') { |
1975 | 0 | break; |
1976 | 0 | } |
1977 | 0 | if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) { |
1978 | 0 | break; |
1979 | 0 | } |
1980 | 0 | ce = NULL; |
1981 | 0 | ce_is_instanceof = false; |
1982 | 0 | on_this = false; |
1983 | 0 | if (opline->op1_type == IS_UNUSED) { |
1984 | 0 | op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; |
1985 | 0 | ce = op_array->scope; |
1986 | | /* scope is NULL for closures. */ |
1987 | 0 | if (ce) { |
1988 | 0 | ce_is_instanceof = !(ce->ce_flags & ZEND_ACC_FINAL); |
1989 | 0 | } |
1990 | 0 | op1_addr = 0; |
1991 | 0 | on_this = true; |
1992 | 0 | } else { |
1993 | 0 | op1_info = OP1_INFO(); |
1994 | 0 | if (!(op1_info & MAY_BE_OBJECT)) { |
1995 | 0 | break; |
1996 | 0 | } |
1997 | 0 | op1_addr = OP1_REG_ADDR(); |
1998 | 0 | if (ssa->var_info && ssa->ops) { |
1999 | 0 | zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes]; |
2000 | 0 | if (ssa_op->op1_use >= 0) { |
2001 | 0 | zend_ssa_var_info *op1_ssa = ssa->var_info + ssa_op->op1_use; |
2002 | 0 | if (op1_ssa->ce && !op1_ssa->ce->create_object) { |
2003 | 0 | ce = op1_ssa->ce; |
2004 | 0 | ce_is_instanceof = op1_ssa->is_instanceof; |
2005 | 0 | } |
2006 | 0 | } |
2007 | 0 | } |
2008 | 0 | } |
2009 | 0 | if (!zend_jit_assign_obj(&ctx, opline, op_array, ssa, ssa_op, |
2010 | 0 | op1_info, op1_addr, OP1_DATA_INFO(), OP1_DATA_REG_ADDR(), OP1_DATA_DEF_REG_ADDR(), |
2011 | 0 | (opline->result_type != IS_UNUSED) ? RES_REG_ADDR() : 0, |
2012 | 0 | 0, ce, ce_is_instanceof, on_this, 0, NULL, IS_UNKNOWN, |
2013 | 0 | zend_may_throw(opline, ssa_op, op_array, ssa))) { |
2014 | 0 | goto jit_failure; |
2015 | 0 | } |
2016 | 0 | goto done; |
2017 | 0 | case ZEND_ASSIGN: |
2018 | 0 | if (opline->op1_type != IS_CV) { |
2019 | 0 | break; |
2020 | 0 | } |
2021 | 0 | if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) { |
2022 | 0 | break; |
2023 | 0 | } |
2024 | 0 | op2_addr = OP2_REG_ADDR(); |
2025 | 0 | if (ra |
2026 | 0 | && ssa->ops[opline - op_array->opcodes].op2_def >= 0 |
2027 | 0 | && !ssa->vars[ssa->ops[opline - op_array->opcodes].op2_def].no_val) { |
2028 | 0 | op2_def_addr = OP2_DEF_REG_ADDR(); |
2029 | 0 | } else { |
2030 | 0 | op2_def_addr = op2_addr; |
2031 | 0 | } |
2032 | 0 | op1_info = OP1_INFO(); |
2033 | 0 | if (ra && ssa->vars[ssa_op->op1_use].no_val) { |
2034 | 0 | op1_info |= MAY_BE_UNDEF; // requres type assignment |
2035 | 0 | } |
2036 | 0 | if (opline->result_type == IS_UNUSED) { |
2037 | 0 | res_addr = 0; |
2038 | 0 | res_info = -1; |
2039 | 0 | } else { |
2040 | 0 | res_addr = RES_REG_ADDR(); |
2041 | 0 | res_info = RES_INFO(); |
2042 | 0 | if (Z_MODE(res_addr) != IS_REG |
2043 | 0 | && (i + 1) <= end |
2044 | 0 | && zend_jit_next_is_send_result(opline) |
2045 | 0 | && (!(op1_info & MAY_HAVE_DTOR) || !(op1_info & MAY_BE_RC1))) { |
2046 | 0 | i++; |
2047 | 0 | res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, (opline+1)->result.var); |
2048 | 0 | if (!zend_jit_reuse_ip(&ctx)) { |
2049 | 0 | goto jit_failure; |
2050 | 0 | } |
2051 | 0 | } |
2052 | 0 | } |
2053 | 0 | if (!zend_jit_assign(&ctx, opline, |
2054 | 0 | op1_info, OP1_REG_ADDR(), |
2055 | 0 | OP1_DEF_INFO(), OP1_DEF_REG_ADDR(), |
2056 | 0 | OP2_INFO(), op2_addr, op2_def_addr, |
2057 | 0 | res_info, res_addr, |
2058 | 0 | 0, |
2059 | 0 | zend_may_throw(opline, ssa_op, op_array, ssa))) { |
2060 | 0 | goto jit_failure; |
2061 | 0 | } |
2062 | 0 | goto done; |
2063 | 0 | case ZEND_QM_ASSIGN: |
2064 | 0 | op1_addr = OP1_REG_ADDR(); |
2065 | 0 | if (ra |
2066 | 0 | && ssa->ops[opline - op_array->opcodes].op1_def >= 0 |
2067 | 0 | && !ssa->vars[ssa->ops[opline - op_array->opcodes].op1_def].no_val) { |
2068 | 0 | op1_def_addr = OP1_DEF_REG_ADDR(); |
2069 | 0 | } else { |
2070 | 0 | op1_def_addr = op1_addr; |
2071 | 0 | } |
2072 | 0 | if (!zend_jit_qm_assign(&ctx, opline, |
2073 | 0 | OP1_INFO(), op1_addr, op1_def_addr, |
2074 | 0 | -1, RES_INFO(), RES_REG_ADDR())) { |
2075 | 0 | goto jit_failure; |
2076 | 0 | } |
2077 | 0 | goto done; |
2078 | 0 | case ZEND_INIT_FCALL: |
2079 | 0 | case ZEND_INIT_FCALL_BY_NAME: |
2080 | 0 | case ZEND_INIT_NS_FCALL_BY_NAME: |
2081 | 0 | if (!zend_jit_init_fcall(&ctx, opline, b, op_array, ssa, ssa_op, call_level, NULL, 0)) { |
2082 | 0 | goto jit_failure; |
2083 | 0 | } |
2084 | 0 | goto done; |
2085 | 0 | case ZEND_SEND_VAL: |
2086 | 0 | case ZEND_SEND_VAL_EX: |
2087 | 0 | if (opline->op2_type == IS_CONST) { |
2088 | | /* Named parameters not supported in JIT (yet) */ |
2089 | 0 | break; |
2090 | 0 | } |
2091 | 0 | if (opline->opcode == ZEND_SEND_VAL_EX |
2092 | 0 | && opline->op2.num > MAX_ARG_FLAG_NUM) { |
2093 | 0 | break; |
2094 | 0 | } |
2095 | 0 | if (!zend_jit_send_val(&ctx, opline, |
2096 | 0 | OP1_INFO(), OP1_REG_ADDR())) { |
2097 | 0 | goto jit_failure; |
2098 | 0 | } |
2099 | 0 | goto done; |
2100 | 0 | case ZEND_SEND_REF: |
2101 | 0 | if (opline->op2_type == IS_CONST) { |
2102 | | /* Named parameters not supported in JIT (yet) */ |
2103 | 0 | break; |
2104 | 0 | } |
2105 | 0 | if (!zend_jit_send_ref(&ctx, opline, op_array, |
2106 | 0 | OP1_INFO(), 0)) { |
2107 | 0 | goto jit_failure; |
2108 | 0 | } |
2109 | 0 | goto done; |
2110 | 0 | case ZEND_SEND_VAR: |
2111 | 0 | case ZEND_SEND_VAR_EX: |
2112 | 0 | case ZEND_SEND_VAR_NO_REF: |
2113 | 0 | case ZEND_SEND_VAR_NO_REF_EX: |
2114 | 0 | case ZEND_SEND_FUNC_ARG: |
2115 | 0 | if (opline->op2_type == IS_CONST) { |
2116 | | /* Named parameters not supported in JIT (yet) */ |
2117 | 0 | break; |
2118 | 0 | } |
2119 | 0 | if ((opline->opcode == ZEND_SEND_VAR_EX |
2120 | 0 | || opline->opcode == ZEND_SEND_VAR_NO_REF_EX) |
2121 | 0 | && opline->op2.num > MAX_ARG_FLAG_NUM) { |
2122 | 0 | break; |
2123 | 0 | } |
2124 | 0 | op1_addr = OP1_REG_ADDR(); |
2125 | 0 | if (ra |
2126 | 0 | && ssa->ops[opline - op_array->opcodes].op1_def >= 0 |
2127 | 0 | && !ssa->vars[ssa->ops[opline - op_array->opcodes].op1_def].no_val) { |
2128 | 0 | op1_def_addr = OP1_DEF_REG_ADDR(); |
2129 | 0 | } else { |
2130 | 0 | op1_def_addr = op1_addr; |
2131 | 0 | } |
2132 | 0 | if (!zend_jit_send_var(&ctx, opline, op_array, |
2133 | 0 | OP1_INFO(), op1_addr, op1_def_addr)) { |
2134 | 0 | goto jit_failure; |
2135 | 0 | } |
2136 | 0 | goto done; |
2137 | 0 | case ZEND_CHECK_FUNC_ARG: |
2138 | 0 | if (opline->op2_type == IS_CONST) { |
2139 | | /* Named parameters not supported in JIT (yet) */ |
2140 | 0 | break; |
2141 | 0 | } |
2142 | 0 | if (opline->op2.num > MAX_ARG_FLAG_NUM) { |
2143 | 0 | break; |
2144 | 0 | } |
2145 | 0 | if (!zend_jit_check_func_arg(&ctx, opline)) { |
2146 | 0 | goto jit_failure; |
2147 | 0 | } |
2148 | 0 | goto done; |
2149 | 0 | case ZEND_CHECK_UNDEF_ARGS: |
2150 | 0 | if (!zend_jit_check_undef_args(&ctx, opline)) { |
2151 | 0 | goto jit_failure; |
2152 | 0 | } |
2153 | 0 | goto done; |
2154 | 0 | case ZEND_DO_UCALL: |
2155 | 0 | ZEND_FALLTHROUGH; |
2156 | 0 | case ZEND_DO_ICALL: |
2157 | 0 | case ZEND_DO_FCALL_BY_NAME: |
2158 | 0 | case ZEND_DO_FCALL: |
2159 | 0 | if (!zend_jit_do_fcall(&ctx, opline, op_array, ssa, call_level, b + 1, NULL)) { |
2160 | 0 | goto jit_failure; |
2161 | 0 | } |
2162 | 0 | goto done; |
2163 | 0 | case ZEND_IS_EQUAL: |
2164 | 0 | case ZEND_IS_NOT_EQUAL: |
2165 | 0 | case ZEND_IS_SMALLER: |
2166 | 0 | case ZEND_IS_SMALLER_OR_EQUAL: |
2167 | 0 | case ZEND_CASE: { |
2168 | 0 | res_addr = RES_REG_ADDR(); |
2169 | 0 | if ((opline->result_type & IS_TMP_VAR) |
2170 | 0 | && (i + 1) <= end |
2171 | 0 | && ((opline+1)->opcode == ZEND_JMPZ |
2172 | 0 | || (opline+1)->opcode == ZEND_JMPNZ |
2173 | 0 | || (opline+1)->opcode == ZEND_JMPZ_EX |
2174 | 0 | || (opline+1)->opcode == ZEND_JMPNZ_EX) |
2175 | 0 | && (opline+1)->op1_type == IS_TMP_VAR |
2176 | 0 | && (opline+1)->op1.var == opline->result.var) { |
2177 | 0 | i++; |
2178 | 0 | smart_branch_opcode = (opline+1)->opcode; |
2179 | 0 | target_label = ssa->cfg.blocks[b].successors[0]; |
2180 | 0 | target_label2 = ssa->cfg.blocks[b].successors[1]; |
2181 | | /* For EX variant write into the result of EX opcode. */ |
2182 | 0 | if ((opline+1)->opcode == ZEND_JMPZ_EX |
2183 | 0 | || (opline+1)->opcode == ZEND_JMPNZ_EX) { |
2184 | 0 | res_addr = OP_REG_ADDR(opline + 1, ssa_op + 1, result_type, result, result_def); |
2185 | 0 | } |
2186 | 0 | } else { |
2187 | 0 | smart_branch_opcode = 0; |
2188 | 0 | target_label = target_label2 = (uint32_t)-1; |
2189 | 0 | } |
2190 | 0 | if (!zend_jit_cmp(&ctx, opline, |
2191 | 0 | OP1_INFO(), OP1_RANGE(), OP1_REG_ADDR(), |
2192 | 0 | OP2_INFO(), OP2_RANGE(), OP2_REG_ADDR(), |
2193 | 0 | res_addr, |
2194 | 0 | zend_may_throw(opline, ssa_op, op_array, ssa), |
2195 | 0 | smart_branch_opcode, target_label, target_label2, |
2196 | 0 | NULL, 0)) { |
2197 | 0 | goto jit_failure; |
2198 | 0 | } |
2199 | 0 | goto done; |
2200 | 0 | } |
2201 | 0 | case ZEND_IS_IDENTICAL: |
2202 | 0 | case ZEND_IS_NOT_IDENTICAL: |
2203 | 0 | case ZEND_CASE_STRICT: |
2204 | 0 | res_addr = RES_REG_ADDR(); |
2205 | 0 | if ((opline->result_type & IS_TMP_VAR) |
2206 | 0 | && (i + 1) <= end |
2207 | 0 | && ((opline+1)->opcode == ZEND_JMPZ |
2208 | 0 | || (opline+1)->opcode == ZEND_JMPZ_EX |
2209 | 0 | || (opline+1)->opcode == ZEND_JMPNZ_EX |
2210 | 0 | || (opline+1)->opcode == ZEND_JMPNZ) |
2211 | 0 | && (opline+1)->op1_type == IS_TMP_VAR |
2212 | 0 | && (opline+1)->op1.var == opline->result.var) { |
2213 | 0 | i++; |
2214 | 0 | smart_branch_opcode = (opline+1)->opcode; |
2215 | 0 | target_label = ssa->cfg.blocks[b].successors[0]; |
2216 | 0 | target_label2 = ssa->cfg.blocks[b].successors[1]; |
2217 | | /* For EX variant write into the result of EX opcode. */ |
2218 | 0 | if ((opline+1)->opcode == ZEND_JMPZ_EX |
2219 | 0 | || (opline+1)->opcode == ZEND_JMPNZ_EX) { |
2220 | 0 | res_addr = OP_REG_ADDR(opline + 1, ssa_op + 1, result_type, result, result_def); |
2221 | 0 | } |
2222 | 0 | } else { |
2223 | 0 | smart_branch_opcode = 0; |
2224 | 0 | target_label = target_label2 = (uint32_t)-1; |
2225 | 0 | } |
2226 | 0 | if (!zend_jit_identical(&ctx, opline, |
2227 | 0 | OP1_INFO(), OP1_RANGE(), OP1_REG_ADDR(), |
2228 | 0 | OP2_INFO(), OP2_RANGE(), OP2_REG_ADDR(), |
2229 | 0 | res_addr, |
2230 | 0 | zend_may_throw(opline, ssa_op, op_array, ssa), |
2231 | 0 | smart_branch_opcode, target_label, target_label2, |
2232 | 0 | NULL, 0)) { |
2233 | 0 | goto jit_failure; |
2234 | 0 | } |
2235 | 0 | goto done; |
2236 | 0 | case ZEND_DEFINED: |
2237 | 0 | if ((opline->result_type & IS_TMP_VAR) |
2238 | 0 | && (i + 1) <= end |
2239 | 0 | && ((opline+1)->opcode == ZEND_JMPZ |
2240 | 0 | || (opline+1)->opcode == ZEND_JMPNZ) |
2241 | 0 | && (opline+1)->op1_type == IS_TMP_VAR |
2242 | 0 | && (opline+1)->op1.var == opline->result.var) { |
2243 | 0 | i++; |
2244 | 0 | smart_branch_opcode = (opline+1)->opcode; |
2245 | 0 | target_label = ssa->cfg.blocks[b].successors[0]; |
2246 | 0 | target_label2 = ssa->cfg.blocks[b].successors[1]; |
2247 | 0 | } else { |
2248 | 0 | smart_branch_opcode = 0; |
2249 | 0 | target_label = target_label2 = (uint32_t)-1; |
2250 | 0 | } |
2251 | 0 | if (!zend_jit_defined(&ctx, opline, smart_branch_opcode, target_label, target_label2, NULL)) { |
2252 | 0 | goto jit_failure; |
2253 | 0 | } |
2254 | 0 | goto done; |
2255 | 0 | case ZEND_TYPE_CHECK: |
2256 | 0 | if (opline->extended_value == MAY_BE_RESOURCE) { |
2257 | | // TODO: support for is_resource() ??? |
2258 | 0 | break; |
2259 | 0 | } |
2260 | 0 | if ((opline->result_type & IS_TMP_VAR) |
2261 | 0 | && (i + 1) <= end |
2262 | 0 | && ((opline+1)->opcode == ZEND_JMPZ |
2263 | 0 | || (opline+1)->opcode == ZEND_JMPNZ) |
2264 | 0 | && (opline+1)->op1_type == IS_TMP_VAR |
2265 | 0 | && (opline+1)->op1.var == opline->result.var) { |
2266 | 0 | i++; |
2267 | 0 | smart_branch_opcode = (opline+1)->opcode; |
2268 | 0 | target_label = ssa->cfg.blocks[b].successors[0]; |
2269 | 0 | target_label2 = ssa->cfg.blocks[b].successors[1]; |
2270 | 0 | } else { |
2271 | 0 | smart_branch_opcode = 0; |
2272 | 0 | target_label = target_label2 = (uint32_t)-1; |
2273 | 0 | } |
2274 | 0 | if (!zend_jit_type_check(&ctx, opline, OP1_INFO(), smart_branch_opcode, target_label, target_label2, NULL)) { |
2275 | 0 | goto jit_failure; |
2276 | 0 | } |
2277 | 0 | goto done; |
2278 | 0 | case ZEND_RETURN: |
2279 | 0 | op1_info = OP1_INFO(); |
2280 | 0 | if ((PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) |
2281 | 0 | || op_array->type == ZEND_EVAL_CODE |
2282 | | // TODO: support for top-level code |
2283 | 0 | || !op_array->function_name |
2284 | | // TODO: support for IS_UNDEF ??? |
2285 | 0 | || (op1_info & MAY_BE_UNDEF)) { |
2286 | 0 | if (!zend_jit_tail_handler(&ctx, opline)) { |
2287 | 0 | goto jit_failure; |
2288 | 0 | } |
2289 | 0 | } else { |
2290 | 0 | if (!zend_jit_return(&ctx, opline, op_array, |
2291 | 0 | op1_info, OP1_REG_ADDR())) { |
2292 | 0 | goto jit_failure; |
2293 | 0 | } |
2294 | 0 | } |
2295 | 0 | goto done; |
2296 | 0 | case ZEND_BOOL: |
2297 | 0 | case ZEND_BOOL_NOT: |
2298 | 0 | if (!zend_jit_bool_jmpznz(&ctx, opline, |
2299 | 0 | OP1_INFO(), OP1_REG_ADDR(), RES_REG_ADDR(), |
2300 | 0 | -1, -1, |
2301 | 0 | zend_may_throw(opline, ssa_op, op_array, ssa), |
2302 | 0 | opline->opcode, NULL)) { |
2303 | 0 | goto jit_failure; |
2304 | 0 | } |
2305 | 0 | goto done; |
2306 | 0 | case ZEND_JMPZ: |
2307 | 0 | case ZEND_JMPNZ: |
2308 | 0 | if (opline > op_array->opcodes + ssa->cfg.blocks[b].start && |
2309 | 0 | ((opline-1)->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) { |
2310 | | /* smart branch */ |
2311 | 0 | if (!zend_jit_cond_jmp(&ctx, opline + 1, ssa->cfg.blocks[b].successors[0])) { |
2312 | 0 | goto jit_failure; |
2313 | 0 | } |
2314 | 0 | goto done; |
2315 | 0 | } |
2316 | 0 | ZEND_FALLTHROUGH; |
2317 | 0 | case ZEND_JMPZ_EX: |
2318 | 0 | case ZEND_JMPNZ_EX: |
2319 | 0 | if (opline->result_type == IS_UNDEF) { |
2320 | 0 | res_addr = 0; |
2321 | 0 | } else { |
2322 | 0 | res_addr = RES_REG_ADDR(); |
2323 | 0 | } |
2324 | 0 | if (!zend_jit_bool_jmpznz(&ctx, opline, |
2325 | 0 | OP1_INFO(), OP1_REG_ADDR(), res_addr, |
2326 | 0 | ssa->cfg.blocks[b].successors[0], ssa->cfg.blocks[b].successors[1], |
2327 | 0 | zend_may_throw(opline, ssa_op, op_array, ssa), |
2328 | 0 | opline->opcode, NULL)) { |
2329 | 0 | goto jit_failure; |
2330 | 0 | } |
2331 | 0 | goto done; |
2332 | 0 | case ZEND_ISSET_ISEMPTY_CV: |
2333 | 0 | if ((opline->extended_value & ZEND_ISEMPTY)) { |
2334 | | // TODO: support for empty() ??? |
2335 | 0 | break; |
2336 | 0 | } |
2337 | 0 | if ((opline->result_type & IS_TMP_VAR) |
2338 | 0 | && (i + 1) <= end |
2339 | 0 | && ((opline+1)->opcode == ZEND_JMPZ |
2340 | 0 | || (opline+1)->opcode == ZEND_JMPNZ) |
2341 | 0 | && (opline+1)->op1_type == IS_TMP_VAR |
2342 | 0 | && (opline+1)->op1.var == opline->result.var) { |
2343 | 0 | i++; |
2344 | 0 | smart_branch_opcode = (opline+1)->opcode; |
2345 | 0 | target_label = ssa->cfg.blocks[b].successors[0]; |
2346 | 0 | target_label2 = ssa->cfg.blocks[b].successors[1]; |
2347 | 0 | } else { |
2348 | 0 | smart_branch_opcode = 0; |
2349 | 0 | target_label = target_label2 = (uint32_t)-1; |
2350 | 0 | } |
2351 | 0 | if (!zend_jit_isset_isempty_cv(&ctx, opline, |
2352 | 0 | OP1_INFO(), OP1_REG_ADDR(), |
2353 | 0 | smart_branch_opcode, target_label, target_label2, |
2354 | 0 | NULL)) { |
2355 | 0 | goto jit_failure; |
2356 | 0 | } |
2357 | 0 | goto done; |
2358 | 0 | case ZEND_IN_ARRAY: |
2359 | 0 | if (opline->op1_type == IS_VAR || opline->op1_type == IS_TMP_VAR) { |
2360 | 0 | break; |
2361 | 0 | } |
2362 | 0 | op1_info = OP1_INFO(); |
2363 | 0 | if ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) != MAY_BE_STRING) { |
2364 | 0 | break; |
2365 | 0 | } |
2366 | 0 | if ((opline->result_type & IS_TMP_VAR) |
2367 | 0 | && (i + 1) <= end |
2368 | 0 | && ((opline+1)->opcode == ZEND_JMPZ |
2369 | 0 | || (opline+1)->opcode == ZEND_JMPNZ) |
2370 | 0 | && (opline+1)->op1_type == IS_TMP_VAR |
2371 | 0 | && (opline+1)->op1.var == opline->result.var) { |
2372 | 0 | i++; |
2373 | 0 | smart_branch_opcode = (opline+1)->opcode; |
2374 | 0 | target_label = ssa->cfg.blocks[b].successors[0]; |
2375 | 0 | target_label2 = ssa->cfg.blocks[b].successors[1]; |
2376 | 0 | } else { |
2377 | 0 | smart_branch_opcode = 0; |
2378 | 0 | target_label = target_label2 = (uint32_t)-1; |
2379 | 0 | } |
2380 | 0 | if (!zend_jit_in_array(&ctx, opline, |
2381 | 0 | op1_info, OP1_REG_ADDR(), |
2382 | 0 | smart_branch_opcode, target_label, target_label2, |
2383 | 0 | NULL)) { |
2384 | 0 | goto jit_failure; |
2385 | 0 | } |
2386 | 0 | goto done; |
2387 | 0 | case ZEND_FETCH_DIM_R: |
2388 | 0 | case ZEND_FETCH_DIM_IS: |
2389 | 0 | case ZEND_FETCH_LIST_R: |
2390 | 0 | if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) { |
2391 | 0 | break; |
2392 | 0 | } |
2393 | 0 | if (!zend_jit_fetch_dim_read(&ctx, opline, ssa, ssa_op, |
2394 | 0 | OP1_INFO(), OP1_REG_ADDR(), 0, |
2395 | 0 | OP2_INFO(), OP2_REG_ADDR(), OP2_RANGE(), |
2396 | 0 | RES_INFO(), RES_REG_ADDR(), IS_UNKNOWN)) { |
2397 | 0 | goto jit_failure; |
2398 | 0 | } |
2399 | 0 | goto done; |
2400 | 0 | case ZEND_FETCH_DIM_W: |
2401 | 0 | case ZEND_FETCH_DIM_RW: |
2402 | | // case ZEND_FETCH_DIM_UNSET: |
2403 | 0 | case ZEND_FETCH_LIST_W: |
2404 | 0 | if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) { |
2405 | 0 | break; |
2406 | 0 | } |
2407 | 0 | if (opline->op1_type != IS_CV) { |
2408 | 0 | break; |
2409 | 0 | } |
2410 | 0 | if (!zend_jit_fetch_dim(&ctx, opline, |
2411 | 0 | OP1_INFO(), OP1_REG_ADDR(), |
2412 | 0 | OP2_INFO(), (opline->op2_type != IS_UNUSED) ? OP2_REG_ADDR() : 0, |
2413 | 0 | (opline->op2_type != IS_UNUSED) ? OP2_RANGE() : 0, |
2414 | 0 | RES_REG_ADDR(), IS_UNKNOWN)) { |
2415 | 0 | goto jit_failure; |
2416 | 0 | } |
2417 | 0 | goto done; |
2418 | 0 | case ZEND_ISSET_ISEMPTY_DIM_OBJ: |
2419 | 0 | if ((opline->extended_value & ZEND_ISEMPTY)) { |
2420 | | // TODO: support for empty() ??? |
2421 | 0 | break; |
2422 | 0 | } |
2423 | 0 | if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) { |
2424 | 0 | break; |
2425 | 0 | } |
2426 | 0 | if ((opline->result_type & IS_TMP_VAR) |
2427 | 0 | && (i + 1) <= end |
2428 | 0 | && ((opline+1)->opcode == ZEND_JMPZ |
2429 | 0 | || (opline+1)->opcode == ZEND_JMPNZ) |
2430 | 0 | && (opline+1)->op1_type == IS_TMP_VAR |
2431 | 0 | && (opline+1)->op1.var == opline->result.var) { |
2432 | 0 | i++; |
2433 | 0 | smart_branch_opcode = (opline+1)->opcode; |
2434 | 0 | target_label = ssa->cfg.blocks[b].successors[0]; |
2435 | 0 | target_label2 = ssa->cfg.blocks[b].successors[1]; |
2436 | 0 | } else { |
2437 | 0 | smart_branch_opcode = 0; |
2438 | 0 | target_label = target_label2 = (uint32_t)-1; |
2439 | 0 | } |
2440 | 0 | if (!zend_jit_isset_isempty_dim(&ctx, opline, |
2441 | 0 | OP1_INFO(), OP1_REG_ADDR(), 0, |
2442 | 0 | OP2_INFO(), OP2_REG_ADDR(), OP2_RANGE(), IS_UNKNOWN, |
2443 | 0 | zend_may_throw(opline, ssa_op, op_array, ssa), |
2444 | 0 | smart_branch_opcode, target_label, target_label2, |
2445 | 0 | NULL)) { |
2446 | 0 | goto jit_failure; |
2447 | 0 | } |
2448 | 0 | goto done; |
2449 | 0 | case ZEND_FETCH_OBJ_R: |
2450 | 0 | case ZEND_FETCH_OBJ_IS: |
2451 | 0 | case ZEND_FETCH_OBJ_W: |
2452 | 0 | ce = NULL; |
2453 | 0 | ce_is_instanceof = false; |
2454 | 0 | on_this = false; |
2455 | 0 | if (opline->op1_type == IS_UNUSED) { |
2456 | 0 | op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; |
2457 | 0 | op1_addr = 0; |
2458 | 0 | ce = op_array->scope; |
2459 | | /* scope is NULL for closures. */ |
2460 | 0 | if (ce) { |
2461 | 0 | ce_is_instanceof = !(ce->ce_flags & ZEND_ACC_FINAL); |
2462 | 0 | } |
2463 | 0 | on_this = true; |
2464 | 0 | } else { |
2465 | 0 | op1_info = OP1_INFO(); |
2466 | 0 | if (!(op1_info & MAY_BE_OBJECT)) { |
2467 | 0 | break; |
2468 | 0 | } |
2469 | 0 | op1_addr = OP1_REG_ADDR(); |
2470 | 0 | if (ssa->var_info && ssa->ops) { |
2471 | 0 | zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes]; |
2472 | 0 | if (ssa_op->op1_use >= 0) { |
2473 | 0 | zend_ssa_var_info *op1_ssa = ssa->var_info + ssa_op->op1_use; |
2474 | 0 | if (op1_ssa->ce && !op1_ssa->ce->create_object) { |
2475 | 0 | ce = op1_ssa->ce; |
2476 | 0 | ce_is_instanceof = op1_ssa->is_instanceof; |
2477 | 0 | } |
2478 | 0 | } |
2479 | 0 | } |
2480 | 0 | } |
2481 | 0 | if (opline->op2_type != IS_CONST |
2482 | 0 | || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING |
2483 | 0 | || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') { |
2484 | 0 | break; |
2485 | 0 | } |
2486 | 0 | if (!zend_jit_fetch_obj(&ctx, opline, op_array, ssa, ssa_op, |
2487 | 0 | op1_info, op1_addr, 0, ce, ce_is_instanceof, on_this, 0, 0, NULL, |
2488 | 0 | RES_REG_ADDR(), IS_UNKNOWN, |
2489 | 0 | zend_may_throw(opline, ssa_op, op_array, ssa))) { |
2490 | 0 | goto jit_failure; |
2491 | 0 | } |
2492 | 0 | goto done; |
2493 | 0 | case ZEND_FETCH_STATIC_PROP_R: |
2494 | 0 | case ZEND_FETCH_STATIC_PROP_IS: |
2495 | 0 | case ZEND_FETCH_STATIC_PROP_W: |
2496 | 0 | case ZEND_FETCH_STATIC_PROP_RW: |
2497 | 0 | case ZEND_FETCH_STATIC_PROP_UNSET: |
2498 | 0 | if (!(opline->op1_type == IS_CONST |
2499 | 0 | && (opline->op2_type == IS_CONST |
2500 | 0 | || (opline->op2_type == IS_UNUSED |
2501 | 0 | && ((opline->op2.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF |
2502 | 0 | || (opline->op2.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT))))) { |
2503 | 0 | break; |
2504 | 0 | } |
2505 | 0 | if (!zend_jit_fetch_static_prop(&ctx, opline, op_array)) { |
2506 | 0 | goto jit_failure; |
2507 | 0 | } |
2508 | 0 | goto done; |
2509 | 0 | case ZEND_BIND_GLOBAL: |
2510 | 0 | if (!ssa->ops || !ssa->var_info) { |
2511 | 0 | op1_info = MAY_BE_ANY|MAY_BE_REF; |
2512 | 0 | } else { |
2513 | 0 | op1_info = OP1_INFO(); |
2514 | 0 | } |
2515 | 0 | if (!zend_jit_bind_global(&ctx, opline, op1_info)) { |
2516 | 0 | goto jit_failure; |
2517 | 0 | } |
2518 | 0 | goto done; |
2519 | 0 | case ZEND_RECV: |
2520 | 0 | if (!zend_jit_recv(&ctx, opline, op_array)) { |
2521 | 0 | goto jit_failure; |
2522 | 0 | } |
2523 | 0 | goto done; |
2524 | 0 | case ZEND_RECV_INIT: |
2525 | 0 | if (!zend_jit_recv_init(&ctx, opline, op_array, |
2526 | 0 | (opline + 1)->opcode != ZEND_RECV_INIT, |
2527 | 0 | zend_may_throw(opline, ssa_op, op_array, ssa))) { |
2528 | 0 | goto jit_failure; |
2529 | 0 | } |
2530 | 0 | goto done; |
2531 | 0 | case ZEND_FREE: |
2532 | 0 | case ZEND_FE_FREE: |
2533 | 0 | if (!zend_jit_free(&ctx, opline, OP1_INFO(), |
2534 | 0 | zend_may_throw(opline, ssa_op, op_array, ssa))) { |
2535 | 0 | goto jit_failure; |
2536 | 0 | } |
2537 | 0 | goto done; |
2538 | 0 | case ZEND_ECHO: |
2539 | 0 | op1_info = OP1_INFO(); |
2540 | 0 | if ((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) != MAY_BE_STRING) { |
2541 | 0 | break; |
2542 | 0 | } |
2543 | 0 | if (!zend_jit_echo(&ctx, opline, op1_info)) { |
2544 | 0 | goto jit_failure; |
2545 | 0 | } |
2546 | 0 | goto done; |
2547 | 0 | case ZEND_STRLEN: |
2548 | 0 | op1_info = OP1_INFO(); |
2549 | 0 | if ((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) != MAY_BE_STRING) { |
2550 | 0 | break; |
2551 | 0 | } |
2552 | 0 | if (!zend_jit_strlen(&ctx, opline, op1_info, OP1_REG_ADDR(), RES_REG_ADDR())) { |
2553 | 0 | goto jit_failure; |
2554 | 0 | } |
2555 | 0 | goto done; |
2556 | 0 | case ZEND_COUNT: |
2557 | 0 | op1_info = OP1_INFO(); |
2558 | 0 | if ((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) != MAY_BE_ARRAY) { |
2559 | 0 | break; |
2560 | 0 | } |
2561 | 0 | if (!zend_jit_count(&ctx, opline, op1_info, OP1_REG_ADDR(), RES_REG_ADDR(), zend_may_throw(opline, ssa_op, op_array, ssa))) { |
2562 | 0 | goto jit_failure; |
2563 | 0 | } |
2564 | 0 | goto done; |
2565 | 0 | case ZEND_FETCH_THIS: |
2566 | 0 | if (!zend_jit_fetch_this(&ctx, opline, op_array, 0)) { |
2567 | 0 | goto jit_failure; |
2568 | 0 | } |
2569 | 0 | goto done; |
2570 | 0 | case ZEND_SWITCH_LONG: |
2571 | 0 | case ZEND_SWITCH_STRING: |
2572 | 0 | case ZEND_MATCH: |
2573 | 0 | if (!zend_jit_switch(&ctx, opline, op_array, ssa, NULL, NULL)) { |
2574 | 0 | goto jit_failure; |
2575 | 0 | } |
2576 | 0 | goto done; |
2577 | 0 | case ZEND_VERIFY_RETURN_TYPE: |
2578 | 0 | if (opline->op1_type == IS_UNUSED) { |
2579 | | /* Always throws */ |
2580 | 0 | break; |
2581 | 0 | } |
2582 | 0 | if (opline->op1_type == IS_CONST) { |
2583 | | /* TODO Different instruction format, has return value */ |
2584 | 0 | break; |
2585 | 0 | } |
2586 | 0 | if (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) { |
2587 | | /* Not worth bothering with */ |
2588 | 0 | break; |
2589 | 0 | } |
2590 | 0 | if (OP1_INFO() & MAY_BE_REF) { |
2591 | | /* TODO May need reference unwrapping. */ |
2592 | 0 | break; |
2593 | 0 | } |
2594 | 0 | if (!zend_jit_verify_return_type(&ctx, opline, op_array, OP1_INFO())) { |
2595 | 0 | goto jit_failure; |
2596 | 0 | } |
2597 | 0 | goto done; |
2598 | 0 | case ZEND_FE_RESET_R: |
2599 | 0 | op1_info = OP1_INFO(); |
2600 | 0 | if ((op1_info & (MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)) != MAY_BE_ARRAY) { |
2601 | 0 | break; |
2602 | 0 | } |
2603 | 0 | if (!zend_jit_fe_reset(&ctx, opline, op1_info)) { |
2604 | 0 | goto jit_failure; |
2605 | 0 | } |
2606 | 0 | goto done; |
2607 | 0 | case ZEND_FE_FETCH_R: |
2608 | 0 | op1_info = OP1_INFO(); |
2609 | 0 | if ((op1_info & MAY_BE_ANY) != MAY_BE_ARRAY) { |
2610 | 0 | break; |
2611 | 0 | } |
2612 | 0 | if (!zend_jit_fe_fetch(&ctx, opline, op1_info, OP2_INFO(), |
2613 | 0 | ssa->cfg.blocks[b].successors[0], opline->opcode, NULL)) { |
2614 | 0 | goto jit_failure; |
2615 | 0 | } |
2616 | 0 | goto done; |
2617 | 0 | case ZEND_FETCH_CONSTANT: |
2618 | 0 | if (!zend_jit_fetch_constant(&ctx, opline, op_array, ssa, ssa_op, RES_REG_ADDR())) { |
2619 | 0 | goto jit_failure; |
2620 | 0 | } |
2621 | 0 | goto done; |
2622 | 0 | case ZEND_JMP_FRAMELESS: |
2623 | 0 | if (!zend_jit_jmp_frameless(&ctx, opline, /* exit_addr */ NULL, /* guard */ 0)) { |
2624 | 0 | goto jit_failure; |
2625 | 0 | } |
2626 | 0 | goto done; |
2627 | 0 | case ZEND_INIT_METHOD_CALL: |
2628 | 0 | if (opline->op2_type != IS_CONST |
2629 | 0 | || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING) { |
2630 | 0 | break; |
2631 | 0 | } |
2632 | 0 | ce = NULL; |
2633 | 0 | ce_is_instanceof = false; |
2634 | 0 | on_this = false; |
2635 | 0 | if (opline->op1_type == IS_UNUSED) { |
2636 | 0 | op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN; |
2637 | 0 | op1_addr = 0; |
2638 | 0 | ce = op_array->scope; |
2639 | | /* scope is NULL for closures. */ |
2640 | 0 | if (ce) { |
2641 | 0 | ce_is_instanceof = !(ce->ce_flags & ZEND_ACC_FINAL); |
2642 | 0 | } |
2643 | 0 | on_this = true; |
2644 | 0 | } else { |
2645 | 0 | op1_info = OP1_INFO(); |
2646 | 0 | if (!(op1_info & MAY_BE_OBJECT)) { |
2647 | 0 | break; |
2648 | 0 | } |
2649 | 0 | op1_addr = OP1_REG_ADDR(); |
2650 | 0 | if (ssa->var_info && ssa->ops) { |
2651 | 0 | zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes]; |
2652 | 0 | if (ssa_op->op1_use >= 0) { |
2653 | 0 | zend_ssa_var_info *op1_ssa = ssa->var_info + ssa_op->op1_use; |
2654 | 0 | if (op1_ssa->ce && !op1_ssa->ce->create_object) { |
2655 | 0 | ce = op1_ssa->ce; |
2656 | 0 | ce_is_instanceof = op1_ssa->is_instanceof; |
2657 | 0 | } |
2658 | 0 | } |
2659 | 0 | } |
2660 | 0 | } |
2661 | 0 | if (!zend_jit_init_method_call(&ctx, opline, b, op_array, ssa, ssa_op, call_level, |
2662 | 0 | op1_info, op1_addr, ce, ce_is_instanceof, on_this, 0, NULL, |
2663 | 0 | NULL, 0, |
2664 | 0 | -1, -1, |
2665 | 0 | 0)) { |
2666 | 0 | goto jit_failure; |
2667 | 0 | } |
2668 | 0 | goto done; |
2669 | 0 | case ZEND_INIT_STATIC_METHOD_CALL: |
2670 | 0 | if (!(opline->op2_type == IS_CONST |
2671 | 0 | && (opline->op1_type == IS_CONST |
2672 | 0 | || (opline->op1_type == IS_UNUSED |
2673 | 0 | && ((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF |
2674 | 0 | || (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT))))) { |
2675 | 0 | break; |
2676 | 0 | } |
2677 | 0 | if (!zend_jit_init_static_method_call(&ctx, opline, b, op_array, ssa, ssa_op, call_level, |
2678 | 0 | NULL, 0)) { |
2679 | 0 | goto jit_failure; |
2680 | 0 | } |
2681 | 0 | goto done; |
2682 | 0 | case ZEND_ROPE_INIT: |
2683 | 0 | case ZEND_ROPE_ADD: |
2684 | 0 | case ZEND_ROPE_END: |
2685 | 0 | op2_info = OP2_INFO(); |
2686 | 0 | if ((op2_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) != MAY_BE_STRING) { |
2687 | 0 | break; |
2688 | 0 | } |
2689 | 0 | if (!zend_jit_rope(&ctx, opline, op2_info)) { |
2690 | 0 | goto jit_failure; |
2691 | 0 | } |
2692 | 0 | goto done; |
2693 | 0 | case ZEND_FRAMELESS_ICALL_0: |
2694 | 0 | jit_frameless_icall0(jit, opline); |
2695 | 0 | goto done; |
2696 | 0 | case ZEND_FRAMELESS_ICALL_1: |
2697 | 0 | op1_info = OP1_INFO(); |
2698 | 0 | jit_frameless_icall1(jit, opline, op1_info); |
2699 | 0 | goto done; |
2700 | 0 | case ZEND_FRAMELESS_ICALL_2: |
2701 | 0 | op1_info = OP1_INFO(); |
2702 | 0 | op2_info = OP2_INFO(); |
2703 | 0 | jit_frameless_icall2(jit, opline, op1_info, op2_info); |
2704 | 0 | goto done; |
2705 | 0 | case ZEND_FRAMELESS_ICALL_3: |
2706 | 0 | op1_info = OP1_INFO(); |
2707 | 0 | op2_info = OP2_INFO(); |
2708 | 0 | jit_frameless_icall3(jit, opline, op1_info, op2_info, OP1_DATA_INFO()); |
2709 | 0 | goto done; |
2710 | 0 | default: |
2711 | 0 | break; |
2712 | 0 | } |
2713 | 0 | } |
2714 | | |
2715 | 0 | switch (opline->opcode) { |
2716 | 0 | case ZEND_RECV_INIT: |
2717 | 0 | case ZEND_BIND_GLOBAL: |
2718 | 0 | if (opline == op_array->opcodes || |
2719 | 0 | opline->opcode != op_array->opcodes[i-1].opcode) { |
2720 | | /* repeatable opcodes */ |
2721 | 0 | if (!zend_jit_handler(&ctx, opline, |
2722 | 0 | zend_may_throw(opline, ssa_op, op_array, ssa))) { |
2723 | 0 | goto jit_failure; |
2724 | 0 | } |
2725 | 0 | } |
2726 | 0 | zend_jit_set_last_valid_opline(&ctx, opline+1); |
2727 | 0 | break; |
2728 | 0 | case ZEND_NOP: |
2729 | 0 | case ZEND_OP_DATA: |
2730 | 0 | case ZEND_SWITCH_LONG: |
2731 | 0 | case ZEND_SWITCH_STRING: |
2732 | 0 | break; |
2733 | 0 | case ZEND_MATCH: |
2734 | | /* We have to exit to the VM because the MATCH handler performs an N-way jump for |
2735 | | * which we can't generate simple (opcache.jit=1201) JIT code. */ |
2736 | 0 | if (!zend_jit_tail_handler(&ctx, opline)) { |
2737 | 0 | goto jit_failure; |
2738 | 0 | } |
2739 | 0 | break; |
2740 | 0 | case ZEND_JMP: |
2741 | 0 | if (JIT_G(opt_level) < ZEND_JIT_LEVEL_INLINE) { |
2742 | 0 | const zend_op *target = OP_JMP_ADDR(opline, opline->op1); |
2743 | |
|
2744 | 0 | if (!zend_jit_set_ip(&ctx, target)) { |
2745 | 0 | goto jit_failure; |
2746 | 0 | } |
2747 | 0 | } |
2748 | 0 | break; |
2749 | 0 | case ZEND_CATCH: |
2750 | 0 | case ZEND_FAST_CALL: |
2751 | 0 | case ZEND_FAST_RET: |
2752 | 0 | case ZEND_GENERATOR_CREATE: |
2753 | 0 | case ZEND_GENERATOR_RETURN: |
2754 | 0 | case ZEND_RETURN_BY_REF: |
2755 | 0 | case ZEND_RETURN: |
2756 | 0 | case ZEND_MATCH_ERROR: |
2757 | | /* switch through trampoline */ |
2758 | 0 | case ZEND_YIELD: |
2759 | 0 | case ZEND_YIELD_FROM: |
2760 | 0 | case ZEND_THROW: |
2761 | 0 | case ZEND_VERIFY_NEVER_TYPE: |
2762 | 0 | if (!zend_jit_tail_handler(&ctx, opline)) { |
2763 | 0 | goto jit_failure; |
2764 | 0 | } |
2765 | | /* THROW and EXIT may be used in the middle of BB */ |
2766 | | /* don't generate code for the rest of BB */ |
2767 | | |
2768 | | /* Skip current opline for call_level computation because it does not influence call_level. |
2769 | | * Don't include last opline because end of loop already checks call level of last opline. */ |
2770 | 0 | i++; |
2771 | 0 | for (; i < end; i++) { |
2772 | 0 | opline = op_array->opcodes + i; |
2773 | 0 | if (zend_jit_inc_call_level(opline->opcode)) { |
2774 | 0 | call_level++; |
2775 | 0 | } else if (zend_jit_dec_call_level(opline->opcode)) { |
2776 | 0 | call_level--; |
2777 | 0 | } |
2778 | 0 | } |
2779 | 0 | opline = op_array->opcodes + end; |
2780 | 0 | break; |
2781 | | /* stackless execution */ |
2782 | 0 | case ZEND_INCLUDE_OR_EVAL: |
2783 | 0 | case ZEND_DO_FCALL: |
2784 | 0 | case ZEND_DO_UCALL: |
2785 | 0 | case ZEND_DO_FCALL_BY_NAME: |
2786 | 0 | if (!zend_jit_call(&ctx, opline, b + 1)) { |
2787 | 0 | goto jit_failure; |
2788 | 0 | } |
2789 | 0 | break; |
2790 | 0 | case ZEND_JMPZ: |
2791 | 0 | case ZEND_JMPNZ: |
2792 | 0 | if (opline > op_array->opcodes + ssa->cfg.blocks[b].start && |
2793 | 0 | ((opline-1)->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) { |
2794 | | /* smart branch */ |
2795 | 0 | if (!zend_jit_cond_jmp(&ctx, opline + 1, ssa->cfg.blocks[b].successors[0])) { |
2796 | 0 | goto jit_failure; |
2797 | 0 | } |
2798 | 0 | goto done; |
2799 | 0 | } |
2800 | 0 | ZEND_FALLTHROUGH; |
2801 | 0 | case ZEND_JMPZ_EX: |
2802 | 0 | case ZEND_JMPNZ_EX: |
2803 | 0 | case ZEND_JMP_SET: |
2804 | 0 | case ZEND_COALESCE: |
2805 | 0 | case ZEND_JMP_NULL: |
2806 | 0 | case ZEND_FE_RESET_R: |
2807 | 0 | case ZEND_FE_RESET_RW: |
2808 | 0 | case ZEND_ASSERT_CHECK: |
2809 | 0 | case ZEND_FE_FETCH_R: |
2810 | 0 | case ZEND_FE_FETCH_RW: |
2811 | 0 | case ZEND_BIND_INIT_STATIC_OR_JMP: |
2812 | 0 | case ZEND_JMP_FRAMELESS: |
2813 | 0 | if (!zend_jit_handler(&ctx, opline, |
2814 | 0 | zend_may_throw(opline, ssa_op, op_array, ssa)) || |
2815 | 0 | !zend_jit_cond_jmp(&ctx, opline + 1, ssa->cfg.blocks[b].successors[0])) { |
2816 | 0 | goto jit_failure; |
2817 | 0 | } |
2818 | 0 | break; |
2819 | 0 | case ZEND_NEW: |
2820 | 0 | if (!zend_jit_handler(&ctx, opline, 1)) { |
2821 | 0 | return 0; |
2822 | 0 | } |
2823 | 0 | if (opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL) { |
2824 | 0 | zend_class_entry *ce = NULL; |
2825 | |
|
2826 | 0 | if (JIT_G(opt_level) >= ZEND_JIT_LEVEL_OPT_FUNC) { |
2827 | 0 | if (ssa->ops && ssa->var_info) { |
2828 | 0 | zend_ssa_var_info *res_ssa = &ssa->var_info[ssa->ops[opline - op_array->opcodes].result_def]; |
2829 | 0 | if (res_ssa->ce && !res_ssa->is_instanceof) { |
2830 | 0 | ce = res_ssa->ce; |
2831 | 0 | } |
2832 | 0 | } |
2833 | 0 | } else { |
2834 | 0 | if (opline->op1_type == IS_CONST) { |
2835 | 0 | zval *zv = RT_CONSTANT(opline, opline->op1); |
2836 | 0 | if (Z_TYPE_P(zv) == IS_STRING) { |
2837 | 0 | zval *lc = zv + 1; |
2838 | 0 | ce = (zend_class_entry*)zend_hash_find_ptr(EG(class_table), Z_STR_P(lc)); |
2839 | 0 | } |
2840 | 0 | } |
2841 | 0 | } |
2842 | |
|
2843 | 0 | i++; |
2844 | |
|
2845 | 0 | if (!ce || !(ce->ce_flags & ZEND_ACC_LINKED) || ce->constructor) { |
2846 | 0 | const zend_op *next_opline = opline + 1; |
2847 | |
|
2848 | 0 | ZEND_ASSERT(b + 1 == ssa->cfg.blocks[b].successors[0]); |
2849 | 0 | zend_jit_constructor(&ctx, next_opline, op_array, ssa, call_level, b + 1); |
2850 | 0 | } |
2851 | | |
2852 | | /* We skip over the DO_FCALL, so decrement call_level ourselves. */ |
2853 | 0 | call_level--; |
2854 | 0 | } |
2855 | 0 | break; |
2856 | 0 | case ZEND_FETCH_OBJ_R: |
2857 | 0 | if (!zend_jit_handler(&ctx, opline, |
2858 | 0 | zend_may_throw(opline, ssa_op, op_array, ssa))) { |
2859 | 0 | goto jit_failure; |
2860 | 0 | } |
2861 | | |
2862 | | /* Cache slot is only used for IS_CONST op2, so only that can result in hook fast path. */ |
2863 | 0 | if (opline->op2_type == IS_CONST) { |
2864 | 0 | if (JIT_G(opt_level) < ZEND_JIT_LEVEL_INLINE) { |
2865 | 0 | if (opline->op1_type == IS_UNUSED) { |
2866 | 0 | ce = op_array->scope; |
2867 | 0 | } else { |
2868 | 0 | ce = NULL; |
2869 | 0 | } |
2870 | 0 | } |
2871 | |
|
2872 | 0 | if (!ce || !(ce->ce_flags & ZEND_ACC_FINAL) || ce->num_hooked_props > 0) { |
2873 | | /* If a simple hook is called, exit to the VM. */ |
2874 | 0 | ir_ref if_hook_enter = ir_IF(jit_CMP_IP(jit, IR_EQ, opline + 1)); |
2875 | 0 | ir_IF_FALSE(if_hook_enter); |
2876 | 0 | if (GCC_GLOBAL_REGS) { |
2877 | 0 | ir_TAILCALL(IR_VOID, ir_LOAD_A(jit_IP(jit))); |
2878 | 0 | } else { |
2879 | 0 | zend_jit_vm_enter(jit, jit_IP(jit)); |
2880 | 0 | } |
2881 | 0 | ir_IF_TRUE(if_hook_enter); |
2882 | 0 | } |
2883 | 0 | } |
2884 | |
|
2885 | 0 | break; |
2886 | 0 | default: |
2887 | 0 | if (!zend_jit_handler(&ctx, opline, |
2888 | 0 | zend_may_throw(opline, ssa_op, op_array, ssa))) { |
2889 | 0 | goto jit_failure; |
2890 | 0 | } |
2891 | 0 | if (i == end |
2892 | 0 | && (opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) { |
2893 | | /* smart branch split across basic blocks */ |
2894 | 0 | if (!zend_jit_set_cond(&ctx, opline + 2, opline->result.var)) { |
2895 | 0 | goto jit_failure; |
2896 | 0 | } |
2897 | 0 | } |
2898 | 0 | } |
2899 | 0 | done: |
2900 | 0 | if (zend_jit_dec_call_level(opline->opcode)) { |
2901 | 0 | call_level--; |
2902 | 0 | } |
2903 | 0 | } |
2904 | 0 | zend_jit_bb_end(&ctx, b); |
2905 | 0 | } |
2906 | | |
2907 | 0 | if (jit->return_inputs) { |
2908 | 0 | zend_jit_common_return(jit); |
2909 | |
|
2910 | 0 | bool left_frame = false; |
2911 | 0 | if (op_array->last_var > 100) { |
2912 | | /* To many CVs to unroll */ |
2913 | 0 | if (!zend_jit_free_cvs(&ctx)) { |
2914 | 0 | goto jit_failure; |
2915 | 0 | } |
2916 | 0 | left_frame = true; |
2917 | 0 | } |
2918 | 0 | if (!left_frame) { |
2919 | 0 | int j; |
2920 | |
|
2921 | 0 | for (j = 0 ; j < op_array->last_var; j++) { |
2922 | 0 | uint32_t info = zend_ssa_cv_info(op_array, ssa, j); |
2923 | |
|
2924 | 0 | if (info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) { |
2925 | 0 | if (!left_frame) { |
2926 | 0 | left_frame = true; |
2927 | 0 | if (!zend_jit_leave_frame(&ctx)) { |
2928 | 0 | goto jit_failure; |
2929 | 0 | } |
2930 | 0 | } |
2931 | 0 | if (!zend_jit_free_cv(&ctx, info, j)) { |
2932 | 0 | goto jit_failure; |
2933 | 0 | } |
2934 | 0 | } |
2935 | 0 | } |
2936 | 0 | } |
2937 | 0 | if (!zend_jit_leave_func(&ctx, op_array, NULL, MAY_BE_ANY, left_frame, |
2938 | 0 | NULL, NULL, (ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) != 0, 1)) { |
2939 | 0 | goto jit_failure; |
2940 | 0 | } |
2941 | 0 | } |
2942 | | |
2943 | 0 | handler = zend_jit_finish(&ctx); |
2944 | 0 | if (!handler) { |
2945 | 0 | goto jit_failure; |
2946 | 0 | } |
2947 | 0 | zend_jit_free_ctx(&ctx); |
2948 | |
|
2949 | 0 | if (JIT_G(opt_flags) & (ZEND_JIT_REG_ALLOC_LOCAL|ZEND_JIT_REG_ALLOC_GLOBAL)) { |
2950 | 0 | zend_arena_release(&CG(arena), checkpoint); |
2951 | 0 | } |
2952 | 0 | return SUCCESS; |
2953 | | |
2954 | 0 | jit_failure: |
2955 | 0 | zend_jit_free_ctx(&ctx); |
2956 | 0 | if (JIT_G(opt_flags) & (ZEND_JIT_REG_ALLOC_LOCAL|ZEND_JIT_REG_ALLOC_GLOBAL)) { |
2957 | 0 | zend_arena_release(&CG(arena), checkpoint); |
2958 | 0 | } |
2959 | 0 | return FAILURE; |
2960 | 0 | } |
2961 | | |
2962 | | static void zend_jit_collect_calls(zend_op_array *op_array, zend_script *script) |
2963 | 0 | { |
2964 | 0 | zend_func_info *func_info; |
2965 | |
|
2966 | 0 | if (JIT_G(trigger) == ZEND_JIT_ON_FIRST_EXEC || |
2967 | 0 | JIT_G(trigger) == ZEND_JIT_ON_PROF_REQUEST || |
2968 | 0 | JIT_G(trigger) == ZEND_JIT_ON_HOT_COUNTERS) { |
2969 | 0 | func_info = ZEND_FUNC_INFO(op_array); |
2970 | 0 | } else { |
2971 | 0 | func_info = zend_arena_calloc(&CG(arena), 1, sizeof(zend_func_info)); |
2972 | 0 | ZEND_SET_FUNC_INFO(op_array, func_info); |
2973 | 0 | } |
2974 | 0 | zend_analyze_calls(&CG(arena), script, ZEND_CALL_TREE, op_array, func_info); |
2975 | 0 | } |
2976 | | |
2977 | | static void zend_jit_cleanup_func_info(zend_op_array *op_array) |
2978 | 0 | { |
2979 | 0 | zend_func_info *func_info = ZEND_FUNC_INFO(op_array); |
2980 | 0 | zend_call_info *caller_info, *callee_info; |
2981 | |
|
2982 | 0 | if (func_info) { |
2983 | 0 | caller_info = func_info->caller_info; |
2984 | 0 | callee_info = func_info->callee_info; |
2985 | |
|
2986 | 0 | if (JIT_G(trigger) == ZEND_JIT_ON_FIRST_EXEC || |
2987 | 0 | JIT_G(trigger) == ZEND_JIT_ON_PROF_REQUEST || |
2988 | 0 | JIT_G(trigger) == ZEND_JIT_ON_HOT_COUNTERS) { |
2989 | 0 | func_info->num = 0; |
2990 | 0 | func_info->flags &= ZEND_FUNC_JIT_ON_FIRST_EXEC |
2991 | 0 | | ZEND_FUNC_JIT_ON_PROF_REQUEST |
2992 | 0 | | ZEND_FUNC_JIT_ON_HOT_COUNTERS |
2993 | 0 | | ZEND_FUNC_JIT_ON_HOT_TRACE; |
2994 | 0 | memset(&func_info->ssa, 0, sizeof(zend_func_info) - offsetof(zend_func_info, ssa)); |
2995 | 0 | } else { |
2996 | 0 | ZEND_SET_FUNC_INFO(op_array, NULL); |
2997 | 0 | } |
2998 | |
|
2999 | 0 | while (caller_info) { |
3000 | 0 | if (caller_info->caller_op_array) { |
3001 | 0 | zend_jit_cleanup_func_info(caller_info->caller_op_array); |
3002 | 0 | } |
3003 | 0 | caller_info = caller_info->next_caller; |
3004 | 0 | } |
3005 | 0 | while (callee_info) { |
3006 | 0 | if (callee_info->callee_func && callee_info->callee_func->type == ZEND_USER_FUNCTION) { |
3007 | 0 | zend_jit_cleanup_func_info(&callee_info->callee_func->op_array); |
3008 | 0 | } |
3009 | 0 | callee_info = callee_info->next_callee; |
3010 | 0 | } |
3011 | 0 | } |
3012 | 0 | } |
3013 | | |
3014 | | static int zend_real_jit_func(zend_op_array *op_array, zend_script *script, const zend_op *rt_opline, uint8_t trigger) |
3015 | 0 | { |
3016 | 0 | zend_ssa ssa; |
3017 | 0 | void *checkpoint; |
3018 | 0 | zend_func_info *func_info; |
3019 | 0 | uint8_t orig_trigger; |
3020 | |
|
3021 | 0 | if (*dasm_ptr == dasm_end) { |
3022 | 0 | return FAILURE; |
3023 | 0 | } |
3024 | | |
3025 | 0 | orig_trigger = JIT_G(trigger); |
3026 | 0 | JIT_G(trigger) = trigger; |
3027 | 0 | checkpoint = zend_arena_checkpoint(CG(arena)); |
3028 | | |
3029 | | /* Build SSA */ |
3030 | 0 | memset(&ssa, 0, sizeof(zend_ssa)); |
3031 | |
|
3032 | 0 | if (op_array->fn_flags & ZEND_ACC_CLOSURE) { |
3033 | 0 | if (trigger == ZEND_JIT_ON_FIRST_EXEC) { |
3034 | 0 | zend_jit_op_array_extension *jit_extension = (zend_jit_op_array_extension*)ZEND_FUNC_INFO(op_array); |
3035 | 0 | op_array = (zend_op_array*) jit_extension->op_array; |
3036 | 0 | } else if (trigger == ZEND_JIT_ON_HOT_COUNTERS) { |
3037 | 0 | zend_jit_op_array_hot_extension *jit_extension = (zend_jit_op_array_hot_extension*)ZEND_FUNC_INFO(op_array); |
3038 | 0 | op_array = (zend_op_array*) jit_extension->op_array; |
3039 | 0 | } else { |
3040 | 0 | ZEND_ASSERT(!op_array->scope); |
3041 | 0 | } |
3042 | 0 | } |
3043 | |
|
3044 | 0 | if (zend_jit_op_array_analyze1(op_array, script, &ssa) != SUCCESS) { |
3045 | 0 | goto jit_failure; |
3046 | 0 | } |
3047 | | |
3048 | 0 | if (JIT_G(opt_level) >= ZEND_JIT_LEVEL_OPT_FUNCS) { |
3049 | 0 | zend_jit_collect_calls(op_array, script); |
3050 | 0 | func_info = ZEND_FUNC_INFO(op_array); |
3051 | 0 | func_info->call_map = zend_build_call_map(&CG(arena), func_info, op_array); |
3052 | 0 | if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { |
3053 | 0 | zend_init_func_return_info(op_array, script, &func_info->return_info); |
3054 | 0 | } |
3055 | 0 | } |
3056 | |
|
3057 | 0 | if (zend_jit_op_array_analyze2(op_array, script, &ssa, ZCG(accel_directives).optimization_level) != SUCCESS) { |
3058 | 0 | goto jit_failure; |
3059 | 0 | } |
3060 | | |
3061 | 0 | if (JIT_G(debug) & ZEND_JIT_DEBUG_SSA) { |
3062 | 0 | zend_dump_op_array(op_array, ZEND_DUMP_HIDE_UNREACHABLE|ZEND_DUMP_RC_INFERENCE|ZEND_DUMP_SSA, "JIT", &ssa); |
3063 | 0 | } |
3064 | |
|
3065 | 0 | if (zend_jit(op_array, &ssa, rt_opline) != SUCCESS) { |
3066 | 0 | goto jit_failure; |
3067 | 0 | } |
3068 | | |
3069 | 0 | zend_jit_cleanup_func_info(op_array); |
3070 | 0 | zend_arena_release(&CG(arena), checkpoint); |
3071 | 0 | JIT_G(trigger) = orig_trigger; |
3072 | 0 | return SUCCESS; |
3073 | | |
3074 | 0 | jit_failure: |
3075 | 0 | zend_jit_cleanup_func_info(op_array); |
3076 | 0 | zend_arena_release(&CG(arena), checkpoint); |
3077 | 0 | JIT_G(trigger) = orig_trigger; |
3078 | 0 | return FAILURE; |
3079 | 0 | } |
3080 | | |
3081 | | /* Run-time JIT handler */ |
3082 | | #if ZEND_VM_KIND == ZEND_VM_KIND_CALL || ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL |
3083 | | static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend_runtime_jit(ZEND_OPCODE_HANDLER_ARGS) |
3084 | | #else |
3085 | | static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_runtime_jit(ZEND_OPCODE_HANDLER_ARGS) |
3086 | | #endif |
3087 | 0 | { |
3088 | | #if GCC_GLOBAL_REGS |
3089 | | zend_execute_data *execute_data; |
3090 | | zend_op *opline; |
3091 | | #else |
3092 | 0 | const zend_op *orig_opline = opline; |
3093 | 0 | #endif |
3094 | |
|
3095 | 0 | execute_data = EG(current_execute_data); |
3096 | 0 | zend_op_array *op_array = &EX(func)->op_array; |
3097 | 0 | opline = op_array->opcodes; |
3098 | 0 | zend_jit_op_array_extension *jit_extension; |
3099 | 0 | bool do_bailout = 0; |
3100 | |
|
3101 | 0 | zend_shared_alloc_lock(); |
3102 | |
|
3103 | 0 | if (ZEND_FUNC_INFO(op_array)) { |
3104 | |
|
3105 | 0 | SHM_UNPROTECT(); |
3106 | 0 | zend_jit_unprotect(); |
3107 | |
|
3108 | 0 | zend_try { |
3109 | | /* restore original opcode handlers */ |
3110 | 0 | if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) { |
3111 | 0 | while (opline->opcode == ZEND_RECV || opline->opcode == ZEND_RECV_INIT) { |
3112 | 0 | opline++; |
3113 | 0 | } |
3114 | 0 | } |
3115 | 0 | jit_extension = (zend_jit_op_array_extension*)ZEND_FUNC_INFO(op_array); |
3116 | 0 | ((zend_op*)opline)->handler = jit_extension->orig_handler; |
3117 | | |
3118 | | /* perform real JIT for this function */ |
3119 | 0 | zend_real_jit_func(op_array, NULL, NULL, ZEND_JIT_ON_FIRST_EXEC); |
3120 | 0 | } zend_catch { |
3121 | 0 | do_bailout = true; |
3122 | 0 | } zend_end_try(); |
3123 | |
|
3124 | 0 | zend_jit_protect(); |
3125 | 0 | SHM_PROTECT(); |
3126 | 0 | } |
3127 | |
|
3128 | 0 | zend_shared_alloc_unlock(); |
3129 | |
|
3130 | 0 | if (do_bailout) { |
3131 | 0 | zend_bailout(); |
3132 | 0 | } |
3133 | | |
3134 | | /* JIT-ed code is going to be called by VM */ |
3135 | | #if GCC_GLOBAL_REGS |
3136 | | return; // ZEND_VM_CONTINUE |
3137 | | #else |
3138 | 0 | opline = orig_opline; |
3139 | 0 | ZEND_OPCODE_RETURN(); |
3140 | 0 | #endif |
3141 | 0 | } |
3142 | | |
3143 | 0 | void zend_jit_check_funcs(HashTable *function_table, bool is_method) { |
3144 | 0 | zend_op *opline; |
3145 | 0 | zend_function *func; |
3146 | 0 | zend_op_array *op_array; |
3147 | 0 | uintptr_t counter; |
3148 | 0 | zend_jit_op_array_extension *jit_extension; |
3149 | |
|
3150 | 0 | ZEND_HASH_MAP_REVERSE_FOREACH_PTR(function_table, func) { |
3151 | 0 | if (func->type == ZEND_INTERNAL_FUNCTION) { |
3152 | 0 | break; |
3153 | 0 | } |
3154 | 0 | op_array = &func->op_array; |
3155 | 0 | opline = op_array->opcodes; |
3156 | 0 | if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) { |
3157 | 0 | while (opline->opcode == ZEND_RECV || opline->opcode == ZEND_RECV_INIT) { |
3158 | 0 | opline++; |
3159 | 0 | } |
3160 | 0 | } |
3161 | 0 | if (opline->handler == zend_jit_profile_jit_handler) { |
3162 | 0 | if (!RUN_TIME_CACHE(op_array)) { |
3163 | 0 | continue; |
3164 | 0 | } |
3165 | 0 | counter = (uintptr_t)ZEND_COUNTER_INFO(op_array); |
3166 | 0 | ZEND_COUNTER_INFO(op_array) = 0; |
3167 | 0 | jit_extension = (zend_jit_op_array_extension*)ZEND_FUNC_INFO(op_array); |
3168 | 0 | opline->handler = jit_extension->orig_handler; |
3169 | 0 | if (((double)counter / (double)zend_jit_profile_counter) > JIT_G(prof_threshold)) { |
3170 | 0 | zend_real_jit_func(op_array, NULL, NULL, ZEND_JIT_ON_PROF_REQUEST); |
3171 | 0 | } |
3172 | 0 | } |
3173 | 0 | } ZEND_HASH_FOREACH_END(); |
3174 | 0 | } |
3175 | | |
3176 | | void ZEND_FASTCALL zend_jit_hot_func(zend_execute_data *execute_data, const zend_op *opline) |
3177 | 0 | { |
3178 | 0 | zend_op_array *op_array = &EX(func)->op_array; |
3179 | 0 | zend_jit_op_array_hot_extension *jit_extension; |
3180 | 0 | uint32_t i; |
3181 | 0 | bool do_bailout = 0; |
3182 | |
|
3183 | 0 | zend_shared_alloc_lock(); |
3184 | 0 | jit_extension = (zend_jit_op_array_hot_extension*)ZEND_FUNC_INFO(op_array); |
3185 | |
|
3186 | 0 | if (jit_extension) { |
3187 | 0 | SHM_UNPROTECT(); |
3188 | 0 | zend_jit_unprotect(); |
3189 | |
|
3190 | 0 | zend_try { |
3191 | 0 | for (i = 0; i < op_array->last; i++) { |
3192 | 0 | op_array->opcodes[i].handler = jit_extension->orig_handlers[i]; |
3193 | 0 | } |
3194 | |
|
3195 | 0 | EX(opline) = opline; |
3196 | | |
3197 | | /* perform real JIT for this function */ |
3198 | 0 | zend_real_jit_func(op_array, NULL, opline, ZEND_JIT_ON_HOT_COUNTERS); |
3199 | 0 | } zend_catch { |
3200 | 0 | do_bailout = 1; |
3201 | 0 | } zend_end_try(); |
3202 | |
|
3203 | 0 | zend_jit_protect(); |
3204 | 0 | SHM_PROTECT(); |
3205 | 0 | } |
3206 | |
|
3207 | 0 | zend_shared_alloc_unlock(); |
3208 | |
|
3209 | 0 | if (do_bailout) { |
3210 | 0 | zend_bailout(); |
3211 | 0 | } |
3212 | | /* JIT-ed code is going to be called by VM */ |
3213 | 0 | } |
3214 | | |
3215 | | static void zend_jit_setup_hot_counters_ex(zend_op_array *op_array, zend_cfg *cfg) |
3216 | 0 | { |
3217 | 0 | if (JIT_G(hot_func)) { |
3218 | 0 | zend_op *opline = op_array->opcodes; |
3219 | |
|
3220 | 0 | if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) { |
3221 | 0 | while (opline->opcode == ZEND_RECV || opline->opcode == ZEND_RECV_INIT) { |
3222 | 0 | opline++; |
3223 | 0 | } |
3224 | 0 | } |
3225 | |
|
3226 | 0 | opline->handler = zend_jit_func_hot_counter_handler; |
3227 | 0 | } |
3228 | |
|
3229 | 0 | if (JIT_G(hot_loop)) { |
3230 | 0 | uint32_t i; |
3231 | |
|
3232 | 0 | for (i = 0; i < cfg->blocks_count; i++) { |
3233 | 0 | if ((cfg->blocks[i].flags & ZEND_BB_REACHABLE) && |
3234 | 0 | (cfg->blocks[i].flags & ZEND_BB_LOOP_HEADER)) { |
3235 | 0 | op_array->opcodes[cfg->blocks[i].start].handler = |
3236 | 0 | zend_jit_loop_hot_counter_handler; |
3237 | 0 | } |
3238 | 0 | } |
3239 | 0 | } |
3240 | 0 | } |
3241 | | |
3242 | | static int zend_jit_restart_hot_counters(zend_op_array *op_array) |
3243 | 0 | { |
3244 | 0 | zend_jit_op_array_hot_extension *jit_extension; |
3245 | 0 | zend_cfg cfg; |
3246 | 0 | uint32_t i; |
3247 | |
|
3248 | 0 | jit_extension = (zend_jit_op_array_hot_extension*)ZEND_FUNC_INFO(op_array); |
3249 | 0 | for (i = 0; i < op_array->last; i++) { |
3250 | 0 | op_array->opcodes[i].handler = jit_extension->orig_handlers[i]; |
3251 | 0 | } |
3252 | |
|
3253 | 0 | if (zend_jit_build_cfg(op_array, &cfg) != SUCCESS) { |
3254 | 0 | return FAILURE; |
3255 | 0 | } |
3256 | | |
3257 | 0 | zend_jit_setup_hot_counters_ex(op_array, &cfg); |
3258 | |
|
3259 | 0 | return SUCCESS; |
3260 | 0 | } |
3261 | | |
3262 | | static int zend_jit_setup_hot_counters(zend_op_array *op_array) |
3263 | 0 | { |
3264 | 0 | zend_jit_op_array_hot_extension *jit_extension; |
3265 | 0 | zend_cfg cfg; |
3266 | 0 | uint32_t i; |
3267 | |
|
3268 | 0 | ZEND_ASSERT(!JIT_G(hot_func) || zend_jit_func_hot_counter_handler != NULL); |
3269 | 0 | ZEND_ASSERT(!JIT_G(hot_loop) || zend_jit_loop_hot_counter_handler != NULL); |
3270 | |
|
3271 | 0 | if (zend_jit_build_cfg(op_array, &cfg) != SUCCESS) { |
3272 | 0 | return FAILURE; |
3273 | 0 | } |
3274 | | |
3275 | 0 | jit_extension = (zend_jit_op_array_hot_extension*)zend_shared_alloc(sizeof(zend_jit_op_array_hot_extension) + (op_array->last - 1) * sizeof(void*)); |
3276 | 0 | if (!jit_extension) { |
3277 | 0 | return FAILURE; |
3278 | 0 | } |
3279 | 0 | memset(&jit_extension->func_info, 0, sizeof(zend_func_info)); |
3280 | 0 | jit_extension->func_info.flags = ZEND_FUNC_JIT_ON_HOT_COUNTERS; |
3281 | 0 | jit_extension->op_array = op_array; |
3282 | 0 | jit_extension->counter = &zend_jit_hot_counters[zend_jit_op_array_hash(op_array) & (ZEND_HOT_COUNTERS_COUNT - 1)]; |
3283 | 0 | for (i = 0; i < op_array->last; i++) { |
3284 | 0 | jit_extension->orig_handlers[i] = op_array->opcodes[i].handler; |
3285 | 0 | } |
3286 | 0 | ZEND_SET_FUNC_INFO(op_array, (void*)jit_extension); |
3287 | |
|
3288 | 0 | zend_jit_setup_hot_counters_ex(op_array, &cfg); |
3289 | |
|
3290 | 0 | zend_shared_alloc_register_xlat_entry(op_array->opcodes, jit_extension); |
3291 | |
|
3292 | 0 | return SUCCESS; |
3293 | 0 | } |
3294 | | |
3295 | | #include "jit/zend_jit_trace.c" |
3296 | | |
3297 | | int zend_jit_op_array(zend_op_array *op_array, zend_script *script) |
3298 | 0 | { |
3299 | 0 | if (dasm_ptr == NULL) { |
3300 | 0 | return FAILURE; |
3301 | 0 | } |
3302 | | |
3303 | 0 | if (JIT_G(trigger) == ZEND_JIT_ON_FIRST_EXEC) { |
3304 | 0 | zend_jit_op_array_extension *jit_extension; |
3305 | 0 | zend_op *opline = op_array->opcodes; |
3306 | |
|
3307 | 0 | if (CG(compiler_options) & ZEND_COMPILE_PRELOAD) { |
3308 | 0 | ZEND_SET_FUNC_INFO(op_array, NULL); |
3309 | 0 | zend_error(E_WARNING, "Preloading is incompatible with first-exec and profile triggered JIT"); |
3310 | 0 | return SUCCESS; |
3311 | 0 | } |
3312 | | |
3313 | | /* Set run-time JIT handler */ |
3314 | 0 | ZEND_ASSERT(zend_jit_runtime_jit_handler != NULL); |
3315 | 0 | if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) { |
3316 | 0 | while (opline->opcode == ZEND_RECV || opline->opcode == ZEND_RECV_INIT) { |
3317 | 0 | opline++; |
3318 | 0 | } |
3319 | 0 | } |
3320 | 0 | jit_extension = (zend_jit_op_array_extension*)zend_shared_alloc(sizeof(zend_jit_op_array_extension)); |
3321 | 0 | if (!jit_extension) { |
3322 | 0 | return FAILURE; |
3323 | 0 | } |
3324 | 0 | memset(&jit_extension->func_info, 0, sizeof(zend_func_info)); |
3325 | 0 | jit_extension->func_info.flags = ZEND_FUNC_JIT_ON_FIRST_EXEC; |
3326 | 0 | jit_extension->op_array = op_array; |
3327 | 0 | jit_extension->orig_handler = opline->handler; |
3328 | 0 | ZEND_SET_FUNC_INFO(op_array, (void*)jit_extension); |
3329 | 0 | opline->handler = zend_jit_runtime_jit_handler; |
3330 | 0 | zend_shared_alloc_register_xlat_entry(op_array->opcodes, jit_extension); |
3331 | |
|
3332 | 0 | return SUCCESS; |
3333 | 0 | } else if (JIT_G(trigger) == ZEND_JIT_ON_PROF_REQUEST) { |
3334 | 0 | zend_jit_op_array_extension *jit_extension; |
3335 | 0 | zend_op *opline = op_array->opcodes; |
3336 | |
|
3337 | 0 | if (CG(compiler_options) & ZEND_COMPILE_PRELOAD) { |
3338 | 0 | ZEND_SET_FUNC_INFO(op_array, NULL); |
3339 | 0 | zend_error(E_WARNING, "Preloading is incompatible with first-exec and profile triggered JIT"); |
3340 | 0 | return SUCCESS; |
3341 | 0 | } |
3342 | | |
3343 | 0 | ZEND_ASSERT(zend_jit_profile_jit_handler != NULL); |
3344 | 0 | if (op_array->function_name) { |
3345 | 0 | if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) { |
3346 | 0 | while (opline->opcode == ZEND_RECV || opline->opcode == ZEND_RECV_INIT) { |
3347 | 0 | opline++; |
3348 | 0 | } |
3349 | 0 | } |
3350 | 0 | jit_extension = (zend_jit_op_array_extension*)zend_shared_alloc(sizeof(zend_jit_op_array_extension)); |
3351 | 0 | if (!jit_extension) { |
3352 | 0 | return FAILURE; |
3353 | 0 | } |
3354 | 0 | memset(&jit_extension->func_info, 0, sizeof(zend_func_info)); |
3355 | 0 | jit_extension->func_info.flags = ZEND_FUNC_JIT_ON_PROF_REQUEST; |
3356 | 0 | jit_extension->op_array = op_array; |
3357 | 0 | jit_extension->orig_handler = opline->handler; |
3358 | 0 | ZEND_SET_FUNC_INFO(op_array, (void*)jit_extension); |
3359 | 0 | opline->handler = zend_jit_profile_jit_handler; |
3360 | 0 | zend_shared_alloc_register_xlat_entry(op_array->opcodes, jit_extension); |
3361 | 0 | } |
3362 | | |
3363 | 0 | return SUCCESS; |
3364 | 0 | } else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_COUNTERS) { |
3365 | 0 | return zend_jit_setup_hot_counters(op_array); |
3366 | 0 | } else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { |
3367 | 0 | return zend_jit_setup_hot_trace_counters(op_array); |
3368 | 0 | } else if (JIT_G(trigger) == ZEND_JIT_ON_SCRIPT_LOAD) { |
3369 | 0 | return zend_real_jit_func(op_array, script, NULL, ZEND_JIT_ON_SCRIPT_LOAD); |
3370 | 0 | } else { |
3371 | 0 | ZEND_UNREACHABLE(); |
3372 | 0 | } |
3373 | 0 | return FAILURE; |
3374 | 0 | } |
3375 | | |
3376 | | static void zend_jit_link_func_info(zend_op_array *op_array) |
3377 | 0 | { |
3378 | 0 | if (!ZEND_FUNC_INFO(op_array)) { |
3379 | 0 | void *jit_extension = zend_shared_alloc_get_xlat_entry(op_array->opcodes); |
3380 | |
|
3381 | 0 | if (jit_extension) { |
3382 | 0 | ZEND_SET_FUNC_INFO(op_array, jit_extension); |
3383 | 0 | } |
3384 | 0 | } |
3385 | 0 | } |
3386 | | |
3387 | | int zend_jit_script(zend_script *script) |
3388 | 0 | { |
3389 | 0 | void *checkpoint; |
3390 | 0 | zend_call_graph call_graph; |
3391 | 0 | zend_func_info *info; |
3392 | 0 | int i; |
3393 | |
|
3394 | 0 | if (dasm_ptr == NULL || *dasm_ptr == dasm_end) { |
3395 | 0 | return FAILURE; |
3396 | 0 | } |
3397 | | |
3398 | 0 | checkpoint = zend_arena_checkpoint(CG(arena)); |
3399 | |
|
3400 | 0 | call_graph.op_arrays_count = 0; |
3401 | 0 | zend_build_call_graph(&CG(arena), script, &call_graph); |
3402 | |
|
3403 | 0 | zend_analyze_call_graph(&CG(arena), script, &call_graph); |
3404 | |
|
3405 | 0 | if (JIT_G(trigger) == ZEND_JIT_ON_FIRST_EXEC || |
3406 | 0 | JIT_G(trigger) == ZEND_JIT_ON_PROF_REQUEST || |
3407 | 0 | JIT_G(trigger) == ZEND_JIT_ON_HOT_COUNTERS || |
3408 | 0 | JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { |
3409 | 0 | for (i = 0; i < call_graph.op_arrays_count; i++) { |
3410 | 0 | if (zend_jit_op_array(call_graph.op_arrays[i], script) != SUCCESS) { |
3411 | 0 | goto jit_failure; |
3412 | 0 | } |
3413 | 0 | } |
3414 | 0 | } else if (JIT_G(trigger) == ZEND_JIT_ON_SCRIPT_LOAD) { |
3415 | 0 | for (i = 0; i < call_graph.op_arrays_count; i++) { |
3416 | 0 | info = ZEND_FUNC_INFO(call_graph.op_arrays[i]); |
3417 | 0 | if (info) { |
3418 | 0 | if (zend_jit_op_array_analyze1(call_graph.op_arrays[i], script, &info->ssa) != SUCCESS) { |
3419 | 0 | goto jit_failure; |
3420 | 0 | } |
3421 | 0 | info->ssa.cfg.flags |= info->flags; |
3422 | 0 | info->flags = info->ssa.cfg.flags; |
3423 | 0 | } |
3424 | 0 | } |
3425 | | |
3426 | 0 | for (i = 0; i < call_graph.op_arrays_count; i++) { |
3427 | 0 | info = ZEND_FUNC_INFO(call_graph.op_arrays[i]); |
3428 | 0 | if (info) { |
3429 | 0 | info->call_map = zend_build_call_map(&CG(arena), info, call_graph.op_arrays[i]); |
3430 | 0 | if (call_graph.op_arrays[i]->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { |
3431 | 0 | zend_init_func_return_info(call_graph.op_arrays[i], script, &info->return_info); |
3432 | 0 | } |
3433 | 0 | } |
3434 | 0 | } |
3435 | |
|
3436 | 0 | for (i = 0; i < call_graph.op_arrays_count; i++) { |
3437 | 0 | info = ZEND_FUNC_INFO(call_graph.op_arrays[i]); |
3438 | 0 | if (info) { |
3439 | 0 | if (zend_jit_op_array_analyze2(call_graph.op_arrays[i], script, &info->ssa, ZCG(accel_directives).optimization_level) != SUCCESS) { |
3440 | 0 | goto jit_failure; |
3441 | 0 | } |
3442 | 0 | info->flags = info->ssa.cfg.flags; |
3443 | 0 | } |
3444 | 0 | } |
3445 | | |
3446 | 0 | for (i = 0; i < call_graph.op_arrays_count; i++) { |
3447 | 0 | info = ZEND_FUNC_INFO(call_graph.op_arrays[i]); |
3448 | 0 | if (info) { |
3449 | 0 | if (JIT_G(debug) & ZEND_JIT_DEBUG_SSA) { |
3450 | 0 | zend_dump_op_array(call_graph.op_arrays[i], ZEND_DUMP_HIDE_UNREACHABLE|ZEND_DUMP_RC_INFERENCE|ZEND_DUMP_SSA, "JIT", &info->ssa); |
3451 | 0 | } |
3452 | 0 | if (zend_jit(call_graph.op_arrays[i], &info->ssa, NULL) != SUCCESS) { |
3453 | 0 | goto jit_failure; |
3454 | 0 | } |
3455 | 0 | } |
3456 | 0 | } |
3457 | | |
3458 | 0 | for (i = 0; i < call_graph.op_arrays_count; i++) { |
3459 | 0 | ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL); |
3460 | 0 | } |
3461 | 0 | } else { |
3462 | 0 | ZEND_UNREACHABLE(); |
3463 | 0 | } |
3464 | | |
3465 | 0 | zend_arena_release(&CG(arena), checkpoint); |
3466 | |
|
3467 | 0 | if (JIT_G(trigger) == ZEND_JIT_ON_FIRST_EXEC |
3468 | 0 | || JIT_G(trigger) == ZEND_JIT_ON_PROF_REQUEST |
3469 | 0 | || JIT_G(trigger) == ZEND_JIT_ON_HOT_COUNTERS |
3470 | 0 | || JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { |
3471 | 0 | zend_class_entry *ce; |
3472 | 0 | zend_op_array *op_array; |
3473 | 0 | zval *zv; |
3474 | 0 | zend_property_info *prop; |
3475 | |
|
3476 | 0 | ZEND_HASH_MAP_FOREACH_VAL(&script->class_table, zv) { |
3477 | 0 | if (Z_TYPE_P(zv) == IS_ALIAS_PTR) { |
3478 | 0 | continue; |
3479 | 0 | } |
3480 | | |
3481 | 0 | ce = Z_PTR_P(zv); |
3482 | 0 | ZEND_ASSERT(ce->type == ZEND_USER_CLASS); |
3483 | |
|
3484 | 0 | ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) { |
3485 | 0 | zend_jit_link_func_info(op_array); |
3486 | 0 | } ZEND_HASH_FOREACH_END(); |
3487 | |
|
3488 | 0 | if (ce->num_hooked_props > 0) { |
3489 | 0 | ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, prop) { |
3490 | 0 | if (prop->hooks) { |
3491 | 0 | for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) { |
3492 | 0 | if (prop->hooks[i]) { |
3493 | 0 | op_array = &prop->hooks[i]->op_array; |
3494 | 0 | zend_jit_link_func_info(op_array); |
3495 | 0 | } |
3496 | 0 | } |
3497 | 0 | } |
3498 | 0 | } ZEND_HASH_FOREACH_END(); |
3499 | 0 | } |
3500 | 0 | } ZEND_HASH_FOREACH_END(); |
3501 | 0 | } |
3502 | | |
3503 | 0 | return SUCCESS; |
3504 | | |
3505 | 0 | jit_failure: |
3506 | 0 | if (JIT_G(trigger) == ZEND_JIT_ON_SCRIPT_LOAD) { |
3507 | 0 | for (i = 0; i < call_graph.op_arrays_count; i++) { |
3508 | 0 | ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL); |
3509 | 0 | } |
3510 | 0 | } |
3511 | 0 | zend_arena_release(&CG(arena), checkpoint); |
3512 | 0 | return FAILURE; |
3513 | 0 | } |
3514 | | |
3515 | | void zend_jit_unprotect(void) |
3516 | 0 | { |
3517 | 0 | #ifdef HAVE_MPROTECT |
3518 | 0 | if (!(JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP))) { |
3519 | 0 | int opts = PROT_READ | PROT_WRITE; |
3520 | | #ifdef ZTS |
3521 | | #ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP |
3522 | | if (zend_write_protect) { |
3523 | | pthread_jit_write_protect_np(0); |
3524 | | } |
3525 | | #endif |
3526 | | opts |= PROT_EXEC; |
3527 | | #endif |
3528 | 0 | if (mprotect(dasm_buf, dasm_size, opts) != 0) { |
3529 | 0 | fprintf(stderr, "mprotect() failed [%d] %s\n", errno, strerror(errno)); |
3530 | 0 | } |
3531 | 0 | } |
3532 | | #elif defined(_WIN32) |
3533 | | if (!(JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP))) { |
3534 | | DWORD old, new; |
3535 | | #ifdef ZTS |
3536 | | new = PAGE_EXECUTE_READWRITE; |
3537 | | #else |
3538 | | new = PAGE_READWRITE; |
3539 | | #endif |
3540 | | if (!VirtualProtect(dasm_buf, dasm_size, new, &old)) { |
3541 | | DWORD err = GetLastError(); |
3542 | | char *msg = php_win32_error_to_msg(err); |
3543 | | fprintf(stderr, "VirtualProtect() failed [%lu] %s\n", err, msg); |
3544 | | php_win32_error_msg_free(msg); |
3545 | | } |
3546 | | } |
3547 | | #endif |
3548 | 0 | } |
3549 | | |
3550 | | void zend_jit_protect(void) |
3551 | 0 | { |
3552 | 0 | #ifdef HAVE_MPROTECT |
3553 | 0 | if (!(JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP))) { |
3554 | | #ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP |
3555 | | if (zend_write_protect) { |
3556 | | pthread_jit_write_protect_np(1); |
3557 | | } |
3558 | | #endif |
3559 | 0 | if (mprotect(dasm_buf, dasm_size, PROT_READ | PROT_EXEC) != 0) { |
3560 | 0 | fprintf(stderr, "mprotect() failed [%d] %s\n", errno, strerror(errno)); |
3561 | 0 | } |
3562 | 0 | } |
3563 | | #elif defined(_WIN32) |
3564 | | if (!(JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP))) { |
3565 | | DWORD old; |
3566 | | |
3567 | | if (!VirtualProtect(dasm_buf, dasm_size, PAGE_EXECUTE_READ, &old)) { |
3568 | | DWORD err = GetLastError(); |
3569 | | char *msg = php_win32_error_to_msg(err); |
3570 | | fprintf(stderr, "VirtualProtect() failed [%lu] %s\n", err, msg); |
3571 | | php_win32_error_msg_free(msg); |
3572 | | } |
3573 | | } |
3574 | | #endif |
3575 | 0 | } |
3576 | | |
3577 | | static void zend_jit_init_handlers(void) |
3578 | 0 | { |
3579 | | #if ZEND_VM_KIND == ZEND_VM_KIND_HYBRID |
3580 | | zend_jit_runtime_jit_handler = (zend_vm_opcode_handler_t)zend_jit_stub_handlers[jit_stub_hybrid_runtime_jit]; |
3581 | | zend_jit_profile_jit_handler = (zend_vm_opcode_handler_t)zend_jit_stub_handlers[jit_stub_hybrid_profile_jit]; |
3582 | | zend_jit_func_hot_counter_handler = (zend_vm_opcode_handler_t)zend_jit_stub_handlers[jit_stub_hybrid_func_hot_counter]; |
3583 | | zend_jit_loop_hot_counter_handler = (zend_vm_opcode_handler_t)zend_jit_stub_handlers[jit_stub_hybrid_loop_hot_counter]; |
3584 | | zend_jit_func_trace_counter_handler = (zend_vm_opcode_handler_t)zend_jit_stub_handlers[jit_stub_hybrid_func_trace_counter]; |
3585 | | zend_jit_ret_trace_counter_handler = (zend_vm_opcode_handler_t)zend_jit_stub_handlers[jit_stub_hybrid_ret_trace_counter]; |
3586 | | zend_jit_loop_trace_counter_handler = (zend_vm_opcode_handler_t)zend_jit_stub_handlers[jit_stub_hybrid_loop_trace_counter]; |
3587 | | #else |
3588 | 0 | zend_jit_runtime_jit_handler = zend_runtime_jit; |
3589 | 0 | zend_jit_profile_jit_handler = zend_jit_profile_helper; |
3590 | 0 | zend_jit_func_hot_counter_handler = zend_jit_func_counter_helper; |
3591 | 0 | zend_jit_loop_hot_counter_handler = zend_jit_loop_counter_helper; |
3592 | 0 | zend_jit_func_trace_counter_handler = zend_jit_func_trace_helper; |
3593 | 0 | zend_jit_ret_trace_counter_handler = zend_jit_ret_trace_helper; |
3594 | 0 | zend_jit_loop_trace_counter_handler = zend_jit_loop_trace_helper; |
3595 | 0 | #endif |
3596 | 0 | } |
3597 | | |
3598 | | static void zend_jit_globals_ctor(zend_jit_globals *jit_globals) |
3599 | 16 | { |
3600 | 16 | memset(jit_globals, 0, sizeof(zend_jit_globals)); |
3601 | 16 | zend_jit_trace_init_caches(); |
3602 | 16 | } |
3603 | | |
3604 | | #ifdef ZTS |
3605 | | static void zend_jit_globals_dtor(zend_jit_globals *jit_globals) |
3606 | | { |
3607 | | zend_jit_trace_free_caches(jit_globals); |
3608 | | } |
3609 | | #endif |
3610 | | |
3611 | | static int zend_jit_parse_config_num(zend_long jit) |
3612 | 0 | { |
3613 | 0 | if (jit == 0) { |
3614 | 0 | JIT_G(on) = 0; |
3615 | 0 | return SUCCESS; |
3616 | 0 | } |
3617 | | |
3618 | 0 | if (jit < 0) return FAILURE; |
3619 | | |
3620 | 0 | if (jit % 10 == 0 || jit % 10 > 5) return FAILURE; |
3621 | 0 | JIT_G(opt_level) = jit % 10; |
3622 | |
|
3623 | 0 | jit /= 10; |
3624 | 0 | if (jit % 10 > 5 || jit % 10 == 4) return FAILURE; |
3625 | 0 | JIT_G(trigger) = jit % 10; |
3626 | |
|
3627 | 0 | jit /= 10; |
3628 | 0 | if (jit % 10 > 2) return FAILURE; |
3629 | 0 | JIT_G(opt_flags) = jit % 10; |
3630 | |
|
3631 | 0 | jit /= 10; |
3632 | 0 | if (jit % 10 > 1) return FAILURE; |
3633 | 0 | JIT_G(opt_flags) |= ((jit % 10) ? ZEND_JIT_CPU_AVX : 0); |
3634 | |
|
3635 | 0 | if (jit / 10 != 0) return FAILURE; |
3636 | | |
3637 | 0 | JIT_G(on) = 1; |
3638 | |
|
3639 | 0 | return SUCCESS; |
3640 | 0 | } |
3641 | | |
3642 | | int zend_jit_config(zend_string *jit, int stage) |
3643 | 147k | { |
3644 | 147k | if (stage != ZEND_INI_STAGE_STARTUP && !JIT_G(enabled)) { |
3645 | 147k | if (stage == ZEND_INI_STAGE_RUNTIME) { |
3646 | 73.9k | zend_error(E_WARNING, "Cannot change opcache.jit setting at run-time (JIT is disabled)"); |
3647 | 73.9k | } |
3648 | 147k | return FAILURE; |
3649 | 147k | } |
3650 | | |
3651 | 16 | if (zend_string_equals_literal_ci(jit, "disable")) { |
3652 | 16 | JIT_G(enabled) = 0; |
3653 | 16 | JIT_G(on) = 0; |
3654 | 16 | return SUCCESS; |
3655 | 16 | } else if (ZSTR_LEN(jit) == 0 |
3656 | 0 | || zend_string_equals_literal_ci(jit, "0") |
3657 | 0 | || zend_string_equals_literal_ci(jit, "off") |
3658 | 0 | || zend_string_equals_literal_ci(jit, "no") |
3659 | 0 | || zend_string_equals_literal_ci(jit, "false")) { |
3660 | 0 | JIT_G(enabled) = 1; |
3661 | 0 | JIT_G(on) = 0; |
3662 | 0 | return SUCCESS; |
3663 | 0 | } else if (zend_string_equals_literal_ci(jit, "1") |
3664 | 0 | || zend_string_equals_literal_ci(jit, "on") |
3665 | 0 | || zend_string_equals_literal_ci(jit, "yes") |
3666 | 0 | || zend_string_equals_literal_ci(jit, "true") |
3667 | 0 | || zend_string_equals_literal_ci(jit, "tracing")) { |
3668 | 0 | JIT_G(enabled) = 1; |
3669 | 0 | JIT_G(on) = 1; |
3670 | 0 | JIT_G(opt_level) = ZEND_JIT_LEVEL_OPT_FUNCS; |
3671 | 0 | JIT_G(trigger) = ZEND_JIT_ON_HOT_TRACE; |
3672 | 0 | JIT_G(opt_flags) = ZEND_JIT_REG_ALLOC_GLOBAL | ZEND_JIT_CPU_AVX; |
3673 | 0 | return SUCCESS; |
3674 | 0 | } else if (zend_string_equals_ci(jit, ZSTR_KNOWN(ZEND_STR_FUNCTION))) { |
3675 | 0 | JIT_G(enabled) = 1; |
3676 | 0 | JIT_G(on) = 1; |
3677 | 0 | JIT_G(opt_level) = ZEND_JIT_LEVEL_OPT_SCRIPT; |
3678 | 0 | JIT_G(trigger) = ZEND_JIT_ON_SCRIPT_LOAD; |
3679 | 0 | JIT_G(opt_flags) = ZEND_JIT_REG_ALLOC_GLOBAL | ZEND_JIT_CPU_AVX; |
3680 | 0 | return SUCCESS; |
3681 | 0 | } else { |
3682 | 0 | char *end; |
3683 | 0 | zend_long num = ZEND_STRTOL(ZSTR_VAL(jit), &end, 10); |
3684 | 0 | if (end != ZSTR_VAL(jit) + ZSTR_LEN(jit) || zend_jit_parse_config_num(num) != SUCCESS) { |
3685 | 0 | goto failure; |
3686 | 0 | } |
3687 | 0 | JIT_G(enabled) = 1; |
3688 | 0 | return SUCCESS; |
3689 | 0 | } |
3690 | | |
3691 | 0 | failure: |
3692 | 0 | zend_error(E_WARNING, "Invalid \"opcache.jit\" setting. Should be \"disable\", \"on\", \"off\", \"tracing\", \"function\" or 4-digit number"); |
3693 | 0 | JIT_G(enabled) = 0; |
3694 | 0 | JIT_G(on) = 0; |
3695 | 0 | return FAILURE; |
3696 | 16 | } |
3697 | | |
3698 | | int zend_jit_debug_config(zend_long old_val, zend_long new_val, int stage) |
3699 | 16 | { |
3700 | 16 | if (stage != ZEND_INI_STAGE_STARTUP) { |
3701 | 0 | if (((old_val ^ new_val) & ZEND_JIT_DEBUG_PERSISTENT) != 0) { |
3702 | 0 | if (stage == ZEND_INI_STAGE_RUNTIME) { |
3703 | 0 | zend_error(E_WARNING, "Some opcache.jit_debug bits cannot be changed after startup"); |
3704 | 0 | } |
3705 | 0 | return FAILURE; |
3706 | 0 | } |
3707 | 0 | } |
3708 | 16 | return SUCCESS; |
3709 | 16 | } |
3710 | | |
3711 | | void zend_jit_init(void) |
3712 | 16 | { |
3713 | | #ifdef ZTS |
3714 | | jit_globals_id = ts_allocate_fast_id(&jit_globals_id, &jit_globals_offset, sizeof(zend_jit_globals), (ts_allocate_ctor) zend_jit_globals_ctor, (ts_allocate_dtor) zend_jit_globals_dtor); |
3715 | | #else |
3716 | 16 | zend_jit_globals_ctor(&jit_globals); |
3717 | 16 | #endif |
3718 | 16 | } |
3719 | | |
3720 | | #if ZEND_VM_KIND != ZEND_VM_KIND_CALL && ZEND_VM_KIND != ZEND_VM_KIND_TAILCALL && ZEND_VM_KIND != ZEND_VM_KIND_HYBRID |
3721 | | # error JIT is compatible only with CALL and HYBRID VM |
3722 | | #endif |
3723 | | |
3724 | | int zend_jit_check_support(void) |
3725 | 0 | { |
3726 | 0 | int i; |
3727 | |
|
3728 | 0 | if (zend_execute_ex != execute_ex) { |
3729 | 0 | if (zend_dtrace_enabled) { |
3730 | 0 | zend_error(E_WARNING, "JIT is incompatible with DTrace. JIT disabled."); |
3731 | 0 | } else if (strcmp(sapi_module.name, "phpdbg") != 0) { |
3732 | 0 | zend_error(E_WARNING, "JIT is incompatible with third party extensions that override zend_execute_ex(). JIT disabled."); |
3733 | 0 | } |
3734 | 0 | JIT_G(enabled) = 0; |
3735 | 0 | JIT_G(on) = 0; |
3736 | 0 | return FAILURE; |
3737 | 0 | } |
3738 | | |
3739 | 0 | for (i = 0; i <= 256; i++) { |
3740 | 0 | switch (i) { |
3741 | | /* JIT has no effect on these opcodes */ |
3742 | 0 | case ZEND_BEGIN_SILENCE: |
3743 | 0 | case ZEND_END_SILENCE: |
3744 | 0 | break; |
3745 | 0 | default: |
3746 | 0 | if (zend_get_user_opcode_handler(i) != NULL) { |
3747 | 0 | zend_error(E_WARNING, "JIT is incompatible with third party extensions that setup user opcode handlers. JIT disabled."); |
3748 | 0 | JIT_G(enabled) = 0; |
3749 | 0 | JIT_G(on) = 0; |
3750 | 0 | return FAILURE; |
3751 | 0 | } |
3752 | 0 | } |
3753 | 0 | } |
3754 | | |
3755 | | #if defined(IR_TARGET_AARCH64) |
3756 | | if (JIT_G(buffer_size) > 128*1024*1024) { |
3757 | | zend_error(E_WARNING, "JIT on AArch64 doesn't support opcache.jit_buffer_size above 128M."); |
3758 | | JIT_G(enabled) = 0; |
3759 | | JIT_G(on) = 0; |
3760 | | return FAILURE; |
3761 | | } |
3762 | | #elif defined(IR_TARGET_X64) |
3763 | 0 | if (JIT_G(buffer_size) > 2 * Z_L(1024*1024*1024)) { |
3764 | 0 | zend_error(E_WARNING, "JIT on x86_64 doesn't support opcache.jit_buffer_size above 2G."); |
3765 | 0 | JIT_G(enabled) = 0; |
3766 | 0 | JIT_G(on) = 0; |
3767 | 0 | return FAILURE; |
3768 | 0 | } |
3769 | 0 | #endif |
3770 | | |
3771 | 0 | return SUCCESS; |
3772 | 0 | } |
3773 | | |
3774 | | void zend_jit_startup(void *buf, size_t size, bool reattached) |
3775 | 0 | { |
3776 | 0 | zend_jit_halt_op = zend_get_halt_op(); |
3777 | 0 | zend_jit_profile_counter_rid = zend_get_op_array_extension_handle(ACCELERATOR_PRODUCT_NAME); |
3778 | |
|
3779 | | #ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP |
3780 | | zend_write_protect = pthread_jit_write_protect_supported_np(); |
3781 | | #endif |
3782 | |
|
3783 | 0 | dasm_buf = buf; |
3784 | 0 | dasm_size = size; |
3785 | 0 | dasm_ptr = dasm_end = (void*)(((char*)dasm_buf) + size - sizeof(*dasm_ptr) * 2); |
3786 | |
|
3787 | 0 | #ifdef HAVE_MPROTECT |
3788 | | #ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP |
3789 | | if (zend_write_protect) { |
3790 | | pthread_jit_write_protect_np(1); |
3791 | | } |
3792 | | #endif |
3793 | 0 | if (JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP)) { |
3794 | 0 | if (mprotect(dasm_buf, dasm_size, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) { |
3795 | 0 | fprintf(stderr, "mprotect() failed [%d] %s\n", errno, strerror(errno)); |
3796 | 0 | } |
3797 | 0 | } else { |
3798 | 0 | if (mprotect(dasm_buf, dasm_size, PROT_READ | PROT_EXEC) != 0) { |
3799 | 0 | fprintf(stderr, "mprotect() failed [%d] %s\n", errno, strerror(errno)); |
3800 | 0 | } |
3801 | 0 | } |
3802 | | #elif defined(_WIN32) |
3803 | | if (JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP)) { |
3804 | | DWORD old; |
3805 | | |
3806 | | if (!VirtualProtect(dasm_buf, dasm_size, PAGE_EXECUTE_READWRITE, &old)) { |
3807 | | DWORD err = GetLastError(); |
3808 | | char *msg = php_win32_error_to_msg(err); |
3809 | | fprintf(stderr, "VirtualProtect() failed [%lu] %s\n", err, msg); |
3810 | | php_win32_error_msg_free(msg); |
3811 | | } |
3812 | | } else { |
3813 | | DWORD old; |
3814 | | |
3815 | | if (!VirtualProtect(dasm_buf, dasm_size, PAGE_EXECUTE_READ, &old)) { |
3816 | | DWORD err = GetLastError(); |
3817 | | char *msg = php_win32_error_to_msg(err); |
3818 | | fprintf(stderr, "VirtualProtect() failed [%lu] %s\n", err, msg); |
3819 | | php_win32_error_msg_free(msg); |
3820 | | } |
3821 | | } |
3822 | | #endif |
3823 | |
|
3824 | 0 | if (!reattached) { |
3825 | 0 | zend_jit_unprotect(); |
3826 | 0 | *dasm_ptr = dasm_buf; |
3827 | | #if defined(_WIN32) |
3828 | | zend_jit_stub_handlers = dasm_buf; |
3829 | | *dasm_ptr = (void**)*dasm_ptr + sizeof(zend_jit_stubs) / sizeof(zend_jit_stubs[0]); |
3830 | | #elif defined(IR_TARGET_AARCH64) |
3831 | | zend_jit_stub_handlers = dasm_buf; |
3832 | | *dasm_ptr = (void**)*dasm_ptr + (sizeof(zend_jit_stubs) / sizeof(zend_jit_stubs[0])) * 2; |
3833 | | memset(zend_jit_stub_handlers, 0, (sizeof(zend_jit_stubs) / sizeof(zend_jit_stubs[0])) * 2 * sizeof(void*)); |
3834 | | #endif |
3835 | 0 | *dasm_ptr = (void*)ZEND_MM_ALIGNED_SIZE_EX(((size_t)(*dasm_ptr)), 16); |
3836 | 0 | zend_jit_protect(); |
3837 | 0 | } else { |
3838 | | #if defined(_WIN32) || defined(IR_TARGET_AARCH64) |
3839 | | zend_jit_stub_handlers = dasm_buf; |
3840 | | zend_jit_init_handlers(); |
3841 | | #endif |
3842 | 0 | } |
3843 | |
|
3844 | 0 | zend_jit_unprotect(); |
3845 | 0 | zend_jit_setup(reattached); |
3846 | 0 | zend_jit_protect(); |
3847 | 0 | if (!reattached) { |
3848 | 0 | zend_jit_init_handlers(); |
3849 | 0 | } |
3850 | |
|
3851 | 0 | zend_jit_trace_startup(reattached); |
3852 | |
|
3853 | 0 | zend_jit_unprotect(); |
3854 | | /* save JIT buffer pos */ |
3855 | 0 | dasm_ptr[1] = dasm_ptr[0]; |
3856 | 0 | zend_jit_protect(); |
3857 | 0 | } |
3858 | | |
3859 | | void zend_jit_shutdown(void) |
3860 | 0 | { |
3861 | 0 | if (JIT_G(debug) & ZEND_JIT_DEBUG_SIZE && dasm_ptr != NULL) { |
3862 | 0 | fprintf(stderr, "\nJIT memory usage: %td\n", (ptrdiff_t)((char*)*dasm_ptr - (char*)dasm_buf)); |
3863 | 0 | } |
3864 | |
|
3865 | 0 | zend_jit_shutdown_ir(); |
3866 | |
|
3867 | | #ifdef ZTS |
3868 | | ts_free_id(jit_globals_id); |
3869 | | #else |
3870 | 0 | zend_jit_trace_free_caches(&jit_globals); |
3871 | 0 | #endif |
3872 | | |
3873 | | /* Reset global pointers to prevent use-after-free in `zend_jit_status()` |
3874 | | * after gracefully restarting Apache with mod_php, see: |
3875 | | * https://github.com/php/php-src/pull/19212 */ |
3876 | 0 | dasm_ptr = NULL; |
3877 | 0 | dasm_buf = NULL; |
3878 | 0 | dasm_end = NULL; |
3879 | 0 | dasm_size = 0; |
3880 | 0 | } |
3881 | | |
3882 | | static void zend_jit_reset_counters(void) |
3883 | 0 | { |
3884 | 0 | int i; |
3885 | |
|
3886 | 0 | for (i = 0; i < ZEND_HOT_COUNTERS_COUNT; i++) { |
3887 | 0 | zend_jit_hot_counters[i] = ZEND_JIT_COUNTER_INIT; |
3888 | 0 | } |
3889 | 0 | } |
3890 | | |
3891 | | void zend_jit_activate(void) |
3892 | 278k | { |
3893 | | #ifdef ZTS |
3894 | | if (!zend_jit_startup_ok) { |
3895 | | JIT_G(enabled) = 0; |
3896 | | JIT_G(on) = 0; |
3897 | | return; |
3898 | | } |
3899 | | #endif |
3900 | 278k | zend_jit_profile_counter = 0; |
3901 | 278k | if (JIT_G(on)) { |
3902 | 0 | if (JIT_G(trigger) == ZEND_JIT_ON_HOT_COUNTERS) { |
3903 | 0 | zend_jit_reset_counters(); |
3904 | 0 | } else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) { |
3905 | 0 | zend_jit_reset_counters(); |
3906 | 0 | zend_jit_trace_reset_caches(); |
3907 | 0 | } |
3908 | 0 | } |
3909 | 278k | } |
3910 | | |
3911 | | void zend_jit_deactivate(void) |
3912 | 278k | { |
3913 | 278k | if (zend_jit_profile_counter && !CG(unclean_shutdown)) { |
3914 | 0 | zend_class_entry *ce; |
3915 | |
|
3916 | 0 | zend_shared_alloc_lock(); |
3917 | 0 | SHM_UNPROTECT(); |
3918 | 0 | zend_jit_unprotect(); |
3919 | |
|
3920 | 0 | zend_jit_check_funcs(EG(function_table), false); |
3921 | 0 | ZEND_HASH_MAP_REVERSE_FOREACH_PTR(EG(class_table), ce) { |
3922 | 0 | if (ce->type == ZEND_INTERNAL_CLASS) { |
3923 | 0 | break; |
3924 | 0 | } |
3925 | 0 | zend_jit_check_funcs(&ce->function_table, true); |
3926 | 0 | } ZEND_HASH_FOREACH_END(); |
3927 | |
|
3928 | 0 | zend_jit_protect(); |
3929 | 0 | SHM_PROTECT(); |
3930 | 0 | zend_shared_alloc_unlock(); |
3931 | 0 | } |
3932 | | |
3933 | 278k | zend_jit_profile_counter = 0; |
3934 | 278k | } |
3935 | | |
3936 | | static void zend_jit_restart_preloaded_op_array(zend_op_array *op_array, void *context) |
3937 | 0 | { |
3938 | 0 | ZEND_IGNORE_VALUE(context); |
3939 | |
|
3940 | 0 | zend_func_info *func_info = ZEND_FUNC_INFO(op_array); |
3941 | |
|
3942 | 0 | if (!func_info) { |
3943 | 0 | return; |
3944 | 0 | } |
3945 | | |
3946 | 0 | if (func_info->flags & ZEND_FUNC_JIT_ON_HOT_TRACE) { |
3947 | 0 | zend_jit_restart_hot_trace_counters(op_array); |
3948 | 0 | } else if (func_info->flags & ZEND_FUNC_JIT_ON_HOT_COUNTERS) { |
3949 | 0 | zend_jit_restart_hot_counters(op_array); |
3950 | | #if 0 |
3951 | | // TODO: We have to restore handlers for some inner basic-blocks, but we didn't store them ??? |
3952 | | } else if (func_info->flags & (ZEND_FUNC_JIT_ON_FIRST_EXEC|ZEND_FUNC_JIT_ON_PROF_REQUEST)) { |
3953 | | zend_op *opline = op_array->opcodes; |
3954 | | zend_jit_op_array_extension *jit_extension = |
3955 | | (zend_jit_op_array_extension*)func_info; |
3956 | | |
3957 | | if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) { |
3958 | | while (opline->opcode == ZEND_RECV || opline->opcode == ZEND_RECV_INIT) { |
3959 | | opline++; |
3960 | | } |
3961 | | } |
3962 | | if (func_info->flags & ZEND_FUNC_JIT_ON_FIRST_EXEC) { |
3963 | | opline->handler = zend_jit_runtime_jit_handler; |
3964 | | } else { |
3965 | | opline->handler = zend_jit_profile_jit_handler; |
3966 | | } |
3967 | | #endif |
3968 | 0 | } |
3969 | 0 | } |
3970 | | |
3971 | | static void zend_jit_restart_preloaded_script(zend_persistent_script *script) |
3972 | 0 | { |
3973 | 0 | zend_foreach_op_array(&script->script, zend_jit_restart_preloaded_op_array, NULL); |
3974 | 0 | } |
3975 | | |
3976 | | void zend_jit_restart(void) |
3977 | 0 | { |
3978 | 0 | if (dasm_buf) { |
3979 | 0 | zend_jit_unprotect(); |
3980 | | |
3981 | | /* restore JIT buffer pos */ |
3982 | 0 | dasm_ptr[0] = dasm_ptr[1]; |
3983 | |
|
3984 | 0 | zend_jit_trace_restart(); |
3985 | |
|
3986 | 0 | if (ZCSG(preload_script)) { |
3987 | 0 | zend_jit_restart_preloaded_script(ZCSG(preload_script)); |
3988 | 0 | if (ZCSG(saved_scripts)) { |
3989 | 0 | zend_persistent_script **p = ZCSG(saved_scripts); |
3990 | |
|
3991 | 0 | while (*p) { |
3992 | 0 | zend_jit_restart_preloaded_script(*p); |
3993 | 0 | p++; |
3994 | 0 | } |
3995 | 0 | } |
3996 | 0 | } |
3997 | |
|
3998 | 0 | zend_jit_protect(); |
3999 | 0 | } |
4000 | 0 | } |
4001 | | |
4002 | | #endif /* HAVE_JIT */ |