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
42 #if ZEND_JIT_TARGET_X86
43 # include "jit/zend_jit_x86.h"
44 #elif ZEND_JIT_TARGET_ARM64
45 # include "jit/zend_jit_arm64.h"
46 #endif
47
48 #include "jit/zend_jit_internal.h"
49
50 #ifdef ZTS
51 int jit_globals_id;
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 # define ZEND_SSA_RC_INFERENCE_FLAG ZEND_SSA_RC_INFERENCE
61 # define RC_MAY_BE_1(info) (((info) & (MAY_BE_RC1|MAY_BE_REF)) != 0)
62 # 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 #define JIT_PREFIX "JIT$"
70 #define JIT_STUB_PREFIX "JIT$$"
71 #define TRACE_PREFIX "TRACE-"
72
73 #define DASM_M_GROW(ctx, t, p, sz, need) \
74 do { \
75 size_t _sz = (sz), _need = (need); \
76 if (_sz < _need) { \
77 if (_sz < 16) _sz = 16; \
78 while (_sz < _need) _sz += _sz; \
79 (p) = (t *)erealloc((p), _sz); \
80 (sz) = _sz; \
81 } \
82 } while(0)
83
84 #define DASM_M_FREE(ctx, p, sz) efree(p)
85
86 #if ZEND_DEBUG
87 # define DASM_CHECKS 1
88 #endif
89
90 #include "dynasm/dasm_proto.h"
91
92 typedef struct _zend_jit_stub {
93 const char *name;
94 int (*stub)(dasm_State **Dst);
95 uint32_t offset;
96 uint32_t adjustment;
97 } zend_jit_stub;
98
99 #define JIT_STUB(name, offset, adjustment) \
100 {JIT_STUB_PREFIX #name, zend_jit_ ## name ## _stub, offset, adjustment}
101
102 zend_ulong zend_jit_profile_counter = 0;
103 int zend_jit_profile_counter_rid = -1;
104
105 int16_t zend_jit_hot_counters[ZEND_HOT_COUNTERS_COUNT];
106
107 const zend_op *zend_jit_halt_op = NULL;
108 static int zend_jit_vm_kind = 0;
109
110 static void *dasm_buf = NULL;
111 static void *dasm_end = NULL;
112 static void **dasm_ptr = NULL;
113
114 static size_t dasm_size = 0;
115
116 static zend_long jit_bisect_pos = 0;
117
118 static const void *zend_jit_runtime_jit_handler = NULL;
119 static const void *zend_jit_profile_jit_handler = NULL;
120 static const void *zend_jit_func_hot_counter_handler = NULL;
121 static const void *zend_jit_loop_hot_counter_handler = NULL;
122 static const void *zend_jit_func_trace_counter_handler = NULL;
123 static const void *zend_jit_ret_trace_counter_handler = NULL;
124 static const void *zend_jit_loop_trace_counter_handler = NULL;
125
126 static int ZEND_FASTCALL zend_runtime_jit(void);
127
128 static int zend_jit_trace_op_len(const zend_op *opline);
129 static int zend_jit_trace_may_exit(const zend_op_array *op_array, const zend_op *opline);
130 static uint32_t zend_jit_trace_get_exit_point(const zend_op *to_opline, uint32_t flags);
131 static const void *zend_jit_trace_get_exit_addr(uint32_t n);
132 static void zend_jit_trace_add_code(const void *start, uint32_t size);
133 static bool zend_jit_needs_arg_dtor(const zend_function *func, uint32_t arg_num, zend_call_info *call_info);
134
135 #if ZEND_JIT_TARGET_ARM64
136 static zend_jit_trace_info *zend_jit_get_current_trace_info(void);
137 static uint32_t zend_jit_trace_find_exit_point(const void* addr);
138 #endif
139
140 static int zend_jit_assign_to_variable(dasm_State **Dst,
141 const zend_op *opline,
142 zend_jit_addr var_use_addr,
143 zend_jit_addr var_addr,
144 uint32_t var_info,
145 uint32_t var_def_info,
146 zend_uchar val_type,
147 zend_jit_addr val_addr,
148 uint32_t val_info,
149 zend_jit_addr res_addr,
150 bool check_exception);
151
dominates(const zend_basic_block * blocks,int a,int b)152 static bool dominates(const zend_basic_block *blocks, int a, int b) {
153 while (blocks[b].level > blocks[a].level) {
154 b = blocks[b].idom;
155 }
156 return a == b;
157 }
158
zend_ssa_is_last_use(const zend_op_array * op_array,const zend_ssa * ssa,int var,int use)159 static bool zend_ssa_is_last_use(const zend_op_array *op_array, const zend_ssa *ssa, int var, int use)
160 {
161 int next_use;
162
163 if (ssa->vars[var].phi_use_chain) {
164 zend_ssa_phi *phi = ssa->vars[var].phi_use_chain;
165 do {
166 if (!ssa->vars[phi->ssa_var].no_val) {
167 return 0;
168 }
169 phi = zend_ssa_next_use_phi(ssa, var, phi);
170 } while (phi);
171 }
172
173 if (ssa->cfg.blocks[ssa->cfg.map[use]].loop_header > 0
174 || (ssa->cfg.blocks[ssa->cfg.map[use]].flags & ZEND_BB_LOOP_HEADER)) {
175 int b = ssa->cfg.map[use];
176 int prev_use = ssa->vars[var].use_chain;
177
178 while (prev_use >= 0 && prev_use != use) {
179 if (b != ssa->cfg.map[prev_use]
180 && dominates(ssa->cfg.blocks, b, ssa->cfg.map[prev_use])
181 && !zend_ssa_is_no_val_use(op_array->opcodes + prev_use, ssa->ops + prev_use, var)) {
182 return 0;
183 }
184 prev_use = zend_ssa_next_use(ssa->ops, var, prev_use);
185 }
186 }
187
188 next_use = zend_ssa_next_use(ssa->ops, var, use);
189 if (next_use < 0) {
190 return 1;
191 } else if (zend_ssa_is_no_val_use(op_array->opcodes + next_use, ssa->ops + next_use, var)) {
192 return 1;
193 }
194 return 0;
195 }
196
zend_ival_is_last_use(const zend_lifetime_interval * ival,int use)197 static bool zend_ival_is_last_use(const zend_lifetime_interval *ival, int use)
198 {
199 if (ival->flags & ZREG_LAST_USE) {
200 const zend_life_range *range = &ival->range;
201
202 while (range->next) {
203 range = range->next;
204 }
205 return range->end == use;
206 }
207 return 0;
208 }
209
zend_is_commutative(zend_uchar opcode)210 static bool zend_is_commutative(zend_uchar opcode)
211 {
212 return
213 opcode == ZEND_ADD ||
214 opcode == ZEND_MUL ||
215 opcode == ZEND_BW_OR ||
216 opcode == ZEND_BW_AND ||
217 opcode == ZEND_BW_XOR;
218 }
219
zend_jit_is_constant_cmp_long_long(const zend_op * opline,zend_ssa_range * op1_range,zend_jit_addr op1_addr,zend_ssa_range * op2_range,zend_jit_addr op2_addr,bool * result)220 static int zend_jit_is_constant_cmp_long_long(const zend_op *opline,
221 zend_ssa_range *op1_range,
222 zend_jit_addr op1_addr,
223 zend_ssa_range *op2_range,
224 zend_jit_addr op2_addr,
225 bool *result)
226 {
227 zend_long op1_min;
228 zend_long op1_max;
229 zend_long op2_min;
230 zend_long op2_max;
231
232 if (op1_range) {
233 op1_min = op1_range->min;
234 op1_max = op1_range->max;
235 } else if (Z_MODE(op1_addr) == IS_CONST_ZVAL) {
236 ZEND_ASSERT(Z_TYPE_P(Z_ZV(op1_addr)) == IS_LONG);
237 op1_min = op1_max = Z_LVAL_P(Z_ZV(op1_addr));
238 } else {
239 return 0;
240 }
241
242 if (op2_range) {
243 op2_min = op2_range->min;
244 op2_max = op2_range->max;
245 } else if (Z_MODE(op2_addr) == IS_CONST_ZVAL) {
246 ZEND_ASSERT(Z_TYPE_P(Z_ZV(op2_addr)) == IS_LONG);
247 op2_min = op2_max = Z_LVAL_P(Z_ZV(op2_addr));
248 } else {
249 return 0;
250 }
251
252 switch (opline->opcode) {
253 case ZEND_IS_EQUAL:
254 case ZEND_IS_IDENTICAL:
255 case ZEND_CASE:
256 case ZEND_CASE_STRICT:
257 if (op1_min == op1_max && op2_min == op2_max && op1_min == op2_min) {
258 *result = 1;
259 return 1;
260 } else if (op1_max < op2_min || op1_min > op2_max) {
261 *result = 0;
262 return 1;
263 }
264 return 0;
265 case ZEND_IS_NOT_EQUAL:
266 case ZEND_IS_NOT_IDENTICAL:
267 if (op1_min == op1_max && op2_min == op2_max && op1_min == op2_min) {
268 *result = 0;
269 return 1;
270 } else if (op1_max < op2_min || op1_min > op2_max) {
271 *result = 1;
272 return 1;
273 }
274 return 0;
275 case ZEND_IS_SMALLER:
276 if (op1_max < op2_min) {
277 *result = 1;
278 return 1;
279 } else if (op1_min >= op2_max) {
280 *result = 0;
281 return 1;
282 }
283 return 0;
284 case ZEND_IS_SMALLER_OR_EQUAL:
285 if (op1_max <= op2_min) {
286 *result = 1;
287 return 1;
288 } else if (op1_min > op2_max) {
289 *result = 0;
290 return 1;
291 }
292 return 0;
293 default:
294 ZEND_UNREACHABLE();
295 }
296 return 0;
297 }
298
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)299 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)
300 {
301 int skip;
302
303 if (trace) {
304 zend_jit_trace_rec *p = trace;
305
306 ssa_op++;
307 while (1) {
308 if (p->op == ZEND_JIT_TRACE_VM) {
309 switch (p->opline->opcode) {
310 case ZEND_SEND_ARRAY:
311 case ZEND_SEND_USER:
312 case ZEND_SEND_UNPACK:
313 case ZEND_INIT_FCALL:
314 case ZEND_INIT_METHOD_CALL:
315 case ZEND_INIT_STATIC_METHOD_CALL:
316 case ZEND_INIT_FCALL_BY_NAME:
317 case ZEND_INIT_NS_FCALL_BY_NAME:
318 case ZEND_INIT_DYNAMIC_CALL:
319 case ZEND_NEW:
320 case ZEND_INIT_USER_CALL:
321 case ZEND_FAST_CALL:
322 case ZEND_JMP:
323 case ZEND_JMPZNZ:
324 case ZEND_JMPZ:
325 case ZEND_JMPNZ:
326 case ZEND_JMPZ_EX:
327 case ZEND_JMPNZ_EX:
328 case ZEND_FE_RESET_R:
329 case ZEND_FE_RESET_RW:
330 case ZEND_JMP_SET:
331 case ZEND_COALESCE:
332 case ZEND_JMP_NULL:
333 case ZEND_ASSERT_CHECK:
334 case ZEND_CATCH:
335 case ZEND_DECLARE_ANON_CLASS:
336 case ZEND_FE_FETCH_R:
337 case ZEND_FE_FETCH_RW:
338 return 1;
339 case ZEND_DO_ICALL:
340 case ZEND_DO_UCALL:
341 case ZEND_DO_FCALL_BY_NAME:
342 case ZEND_DO_FCALL:
343 case ZEND_CALLABLE_CONVERT:
344 return 0;
345 case ZEND_SEND_VAL:
346 case ZEND_SEND_VAR:
347 case ZEND_SEND_VAL_EX:
348 case ZEND_SEND_VAR_EX:
349 case ZEND_SEND_FUNC_ARG:
350 case ZEND_SEND_REF:
351 case ZEND_SEND_VAR_NO_REF:
352 case ZEND_SEND_VAR_NO_REF_EX:
353 /* skip */
354 break;
355 default:
356 if (zend_may_throw(opline, ssa_op, op_array, ssa)) {
357 return 1;
358 }
359 }
360 ssa_op += zend_jit_trace_op_len(opline);
361 } else if (p->op == ZEND_JIT_TRACE_ENTER ||
362 p->op == ZEND_JIT_TRACE_BACK ||
363 p->op == ZEND_JIT_TRACE_END) {
364 return 1;
365 }
366 p++;
367 }
368 }
369
370 if (!call_info) {
371 const zend_op *end = op_array->opcodes + op_array->last;
372
373 opline++;
374 ssa_op++;
375 skip = (call_level == 1);
376 while (opline != end) {
377 if (!skip) {
378 if (zend_may_throw(opline, ssa_op, op_array, ssa)) {
379 return 1;
380 }
381 }
382 switch (opline->opcode) {
383 case ZEND_SEND_VAL:
384 case ZEND_SEND_VAR:
385 case ZEND_SEND_VAL_EX:
386 case ZEND_SEND_VAR_EX:
387 case ZEND_SEND_FUNC_ARG:
388 case ZEND_SEND_REF:
389 case ZEND_SEND_VAR_NO_REF:
390 case ZEND_SEND_VAR_NO_REF_EX:
391 skip = 0;
392 break;
393 case ZEND_SEND_ARRAY:
394 case ZEND_SEND_USER:
395 case ZEND_SEND_UNPACK:
396 case ZEND_INIT_FCALL:
397 case ZEND_INIT_METHOD_CALL:
398 case ZEND_INIT_STATIC_METHOD_CALL:
399 case ZEND_INIT_FCALL_BY_NAME:
400 case ZEND_INIT_NS_FCALL_BY_NAME:
401 case ZEND_INIT_DYNAMIC_CALL:
402 case ZEND_NEW:
403 case ZEND_INIT_USER_CALL:
404 case ZEND_FAST_CALL:
405 case ZEND_JMP:
406 case ZEND_JMPZNZ:
407 case ZEND_JMPZ:
408 case ZEND_JMPNZ:
409 case ZEND_JMPZ_EX:
410 case ZEND_JMPNZ_EX:
411 case ZEND_FE_RESET_R:
412 case ZEND_FE_RESET_RW:
413 case ZEND_JMP_SET:
414 case ZEND_COALESCE:
415 case ZEND_JMP_NULL:
416 case ZEND_ASSERT_CHECK:
417 case ZEND_CATCH:
418 case ZEND_DECLARE_ANON_CLASS:
419 case ZEND_FE_FETCH_R:
420 case ZEND_FE_FETCH_RW:
421 return 1;
422 case ZEND_DO_ICALL:
423 case ZEND_DO_UCALL:
424 case ZEND_DO_FCALL_BY_NAME:
425 case ZEND_DO_FCALL:
426 case ZEND_CALLABLE_CONVERT:
427 end = opline;
428 if (end - op_array->opcodes >= ssa->cfg.blocks[b].start + ssa->cfg.blocks[b].len) {
429 /* INIT_FCALL and DO_FCALL in different BasicBlocks */
430 return 1;
431 }
432 return 0;
433 }
434 opline++;
435 ssa_op++;
436 }
437
438 return 1;
439 } else {
440 const zend_op *end = call_info->caller_call_opline;
441
442 /* end may be null if an opcode like EXIT is part of the argument list. */
443 if (!end || end - op_array->opcodes >= ssa->cfg.blocks[b].start + ssa->cfg.blocks[b].len) {
444 /* INIT_FCALL and DO_FCALL in different BasicBlocks */
445 return 1;
446 }
447
448 opline++;
449 ssa_op++;
450 skip = (call_level == 1);
451 while (opline != end) {
452 if (skip) {
453 switch (opline->opcode) {
454 case ZEND_SEND_VAL:
455 case ZEND_SEND_VAR:
456 case ZEND_SEND_VAL_EX:
457 case ZEND_SEND_VAR_EX:
458 case ZEND_SEND_FUNC_ARG:
459 case ZEND_SEND_REF:
460 case ZEND_SEND_VAR_NO_REF:
461 case ZEND_SEND_VAR_NO_REF_EX:
462 skip = 0;
463 break;
464 case ZEND_SEND_ARRAY:
465 case ZEND_SEND_USER:
466 case ZEND_SEND_UNPACK:
467 return 1;
468 }
469 } else {
470 if (zend_may_throw(opline, ssa_op, op_array, ssa)) {
471 return 1;
472 }
473 }
474 opline++;
475 ssa_op++;
476 }
477
478 return 0;
479 }
480 }
481
skip_valid_arguments(const zend_op_array * op_array,zend_ssa * ssa,const zend_call_info * call_info)482 static uint32_t skip_valid_arguments(const zend_op_array *op_array, zend_ssa *ssa, const zend_call_info *call_info)
483 {
484 uint32_t num_args = 0;
485 zend_function *func = call_info->callee_func;
486
487 /* It's okay to handle prototypes here, because they can only increase the accepted arguments.
488 * Anything legal for the parent method is also legal for the parent method. */
489 while (num_args < call_info->num_args) {
490 zend_arg_info *arg_info = func->op_array.arg_info + num_args;
491
492 if (ZEND_TYPE_IS_SET(arg_info->type)) {
493 if (ZEND_TYPE_IS_ONLY_MASK(arg_info->type)) {
494 zend_op *opline = call_info->arg_info[num_args].opline;
495 zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
496 uint32_t type_mask = ZEND_TYPE_PURE_MASK(arg_info->type);
497 if ((OP1_INFO() & (MAY_BE_ANY|MAY_BE_UNDEF)) & ~type_mask) {
498 break;
499 }
500 } else {
501 break;
502 }
503 }
504 num_args++;
505 }
506 return num_args;
507 }
508
zend_ssa_cv_info(const zend_op_array * op_array,zend_ssa * ssa,uint32_t var)509 static uint32_t zend_ssa_cv_info(const zend_op_array *op_array, zend_ssa *ssa, uint32_t var)
510 {
511 uint32_t j, info;
512
513 if (ssa->vars && ssa->var_info) {
514 info = ssa->var_info[var].type;
515 for (j = op_array->last_var; j < ssa->vars_count; j++) {
516 if (ssa->vars[j].var == var) {
517 info |= ssa->var_info[j].type;
518 }
519 }
520 } else {
521 info = MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_ANY | MAY_BE_UNDEF |
522 MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
523 }
524
525 #ifdef ZEND_JIT_USE_RC_INFERENCE
526 /* Refcount may be increased by RETURN opcode */
527 if ((info & MAY_BE_RC1) && !(info & MAY_BE_RCN)) {
528 for (j = 0; j < ssa->cfg.blocks_count; j++) {
529 if ((ssa->cfg.blocks[j].flags & ZEND_BB_REACHABLE) &&
530 ssa->cfg.blocks[j].len > 0) {
531 const zend_op *opline = op_array->opcodes + ssa->cfg.blocks[j].start + ssa->cfg.blocks[j].len - 1;
532
533 if (opline->opcode == ZEND_RETURN) {
534 if (opline->op1_type == IS_CV && opline->op1.var == EX_NUM_TO_VAR(var)) {
535 info |= MAY_BE_RCN;
536 break;
537 }
538 }
539 }
540 }
541 }
542 #endif
543
544 return info;
545 }
546
zend_jit_may_avoid_refcounting(const zend_op * opline,uint32_t op1_info)547 static bool zend_jit_may_avoid_refcounting(const zend_op *opline, uint32_t op1_info)
548 {
549 switch (opline->opcode) {
550 case ZEND_FETCH_OBJ_FUNC_ARG:
551 if (!JIT_G(current_frame) ||
552 !JIT_G(current_frame)->call->func ||
553 !TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) {
554 return 0;
555 }
556 /* break missing intentionally */
557 case ZEND_FETCH_OBJ_R:
558 case ZEND_FETCH_OBJ_IS:
559 if ((op1_info & MAY_BE_OBJECT)
560 && opline->op2_type == IS_CONST
561 && Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) == IS_STRING
562 && Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] != '\0') {
563 return 1;
564 }
565 break;
566 case ZEND_FETCH_DIM_FUNC_ARG:
567 if (!JIT_G(current_frame) ||
568 !JIT_G(current_frame)->call->func ||
569 !TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) {
570 return 0;
571 }
572 /* break missing intentionally */
573 case ZEND_FETCH_DIM_R:
574 case ZEND_FETCH_DIM_IS:
575 return 1;
576 case ZEND_ISSET_ISEMPTY_DIM_OBJ:
577 if (!(opline->extended_value & ZEND_ISEMPTY)) {
578 return 1;
579 }
580 break;
581 }
582 return 0;
583 }
584
zend_jit_is_persistent_constant(zval * key,uint32_t flags)585 static bool zend_jit_is_persistent_constant(zval *key, uint32_t flags)
586 {
587 zval *zv;
588 zend_constant *c = NULL;
589
590 /* null/true/false are resolved during compilation, so don't check for them here. */
591 zv = zend_hash_find_known_hash(EG(zend_constants), Z_STR_P(key));
592 if (zv) {
593 c = (zend_constant*)Z_PTR_P(zv);
594 } else if (flags & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) {
595 key++;
596 zv = zend_hash_find_known_hash(EG(zend_constants), Z_STR_P(key));
597 if (zv) {
598 c = (zend_constant*)Z_PTR_P(zv);
599 }
600 }
601 return c && (ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT);
602 }
603
zend_get_known_property_info(const zend_op_array * op_array,zend_class_entry * ce,zend_string * member,bool on_this,zend_string * filename)604 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)
605 {
606 zend_property_info *info = NULL;
607
608 if ((on_this && (op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) ||
609 !ce ||
610 !(ce->ce_flags & ZEND_ACC_LINKED) ||
611 (ce->ce_flags & ZEND_ACC_TRAIT) ||
612 ce->create_object) {
613 return NULL;
614 }
615
616 if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
617 if (ce->info.user.filename != filename) {
618 /* class declaration might be changed independently */
619 return NULL;
620 }
621
622 if (ce->parent) {
623 zend_class_entry *parent = ce->parent;
624
625 do {
626 if (parent->type == ZEND_INTERNAL_CLASS) {
627 break;
628 } else if (parent->info.user.filename != filename) {
629 /* some of parents class declarations might be changed independently */
630 /* TODO: this check may be not enough, because even
631 * in the same it's possible to conditionally define
632 * few classes with the same name, and "parent" may
633 * change from request to request.
634 */
635 return NULL;
636 }
637 parent = parent->parent;
638 } while (parent);
639 }
640 }
641
642 info = (zend_property_info*)zend_hash_find_ptr(&ce->properties_info, member);
643 if (info == NULL ||
644 !IS_VALID_PROPERTY_OFFSET(info->offset) ||
645 (info->flags & ZEND_ACC_STATIC)) {
646 return NULL;
647 }
648
649 if (info->flags & ZEND_ACC_PUBLIC) {
650 return info;
651 } else if (on_this) {
652 if (ce == info->ce) {
653 if (ce == op_array->scope) {
654 return info;
655 } else {
656 return NULL;
657 }
658 } else if ((info->flags & ZEND_ACC_PROTECTED)
659 && instanceof_function_slow(ce, info->ce)) {
660 return info;
661 }
662 }
663
664 return NULL;
665 }
666
zend_may_be_dynamic_property(zend_class_entry * ce,zend_string * member,bool on_this,zend_string * filename)667 static bool zend_may_be_dynamic_property(zend_class_entry *ce, zend_string *member, bool on_this, zend_string *filename)
668 {
669 zend_property_info *info;
670
671 if (!ce || (ce->ce_flags & ZEND_ACC_TRAIT)) {
672 return 1;
673 }
674
675 if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
676 if (ce->info.user.filename != filename) {
677 /* class declaration might be changed independently */
678 return 1;
679 }
680 }
681
682 info = (zend_property_info*)zend_hash_find_ptr(&ce->properties_info, member);
683 if (info == NULL ||
684 !IS_VALID_PROPERTY_OFFSET(info->offset) ||
685 (info->flags & ZEND_ACC_STATIC)) {
686 return 1;
687 }
688
689 if (!(info->flags & ZEND_ACC_PUBLIC) &&
690 (!on_this || info->ce != ce)) {
691 return 1;
692 }
693
694 return 0;
695 }
696
697 #define OP_RANGE(ssa_op, opN) \
698 (((opline->opN##_type & (IS_TMP_VAR|IS_VAR|IS_CV)) && \
699 ssa->var_info && \
700 (ssa_op)->opN##_use >= 0 && \
701 ssa->var_info[(ssa_op)->opN##_use].has_range) ? \
702 &ssa->var_info[(ssa_op)->opN##_use].range : NULL)
703
704 #define OP1_RANGE() OP_RANGE(ssa_op, op1)
705 #define OP2_RANGE() OP_RANGE(ssa_op, op2)
706 #define OP1_DATA_RANGE() OP_RANGE(ssa_op + 1, op1)
707
708 #if ZEND_JIT_TARGET_X86
709 # include "dynasm/dasm_x86.h"
710 #elif ZEND_JIT_TARGET_ARM64
711 static int zend_jit_add_veneer(dasm_State *Dst, void *buffer, uint32_t ins, int *b, uint32_t *cp, ptrdiff_t offset);
712 # define DASM_ADD_VENEER zend_jit_add_veneer
713 # include "dynasm/dasm_arm64.h"
714 #endif
715
716 #include "jit/zend_jit_helpers.c"
717 #include "jit/zend_jit_disasm.c"
718 #ifndef _WIN32
719 # include "jit/zend_jit_gdb.h"
720 # include "jit/zend_jit_perf_dump.c"
721 #endif
722 #ifdef HAVE_OPROFILE
723 # include "jit/zend_jit_oprofile.c"
724 #endif
725
726 #include "Zend/zend_cpuinfo.h"
727
728 #ifdef HAVE_VALGRIND
729 # include <valgrind/valgrind.h>
730 #endif
731
732 #ifdef HAVE_GCC_GLOBAL_REGS
733 # define GCC_GLOBAL_REGS 1
734 #else
735 # define GCC_GLOBAL_REGS 0
736 #endif
737
738 /* By default avoid JITing inline handlers if it does not seem profitable due to lack of
739 * type information. Disabling this option allows testing some JIT handlers in the
740 * presence of try/catch blocks, which prevent SSA construction. */
741 #ifndef PROFITABILITY_CHECKS
742 # define PROFITABILITY_CHECKS 1
743 #endif
744
745 #define BP_JIT_IS 6 /* Used for ISSET_ISEMPTY_DIM_OBJ. see BP_VAR_*defines in Zend/zend_compile.h */
746
747 typedef enum _sp_adj_kind {
748 SP_ADJ_NONE,
749 SP_ADJ_RET,
750 SP_ADJ_VM,
751 SP_ADJ_JIT,
752 SP_ADJ_ASSIGN,
753 SP_ADJ_LAST
754 } sp_adj_kind;
755
756 static int sp_adj[SP_ADJ_LAST];
757
758 /* The generated code may contain tautological comparisons, ignore them. */
759 #if defined(__clang__)
760 # pragma clang diagnostic push
761 # pragma clang diagnostic ignored "-Wtautological-compare"
762 # pragma clang diagnostic ignored "-Wstring-compare"
763 #endif
764
765 #if ZEND_JIT_TARGET_X86
766 # include "jit/zend_jit_vtune.c"
767 # include "jit/zend_jit_x86.c"
768 #elif ZEND_JIT_TARGET_ARM64
769 # include "jit/zend_jit_arm64.c"
770 #endif
771
772 #if defined(__clang__)
773 # pragma clang diagnostic pop
774 #endif
775
776 #if _WIN32
777 # include <Windows.h>
778 #else
779 # include <sys/mman.h>
780 # if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
781 # define MAP_ANONYMOUS MAP_ANON
782 # endif
783 #endif
784
zend_jit_status(zval * ret)785 ZEND_EXT_API void zend_jit_status(zval *ret)
786 {
787 zval stats;
788 array_init(&stats);
789 add_assoc_bool(&stats, "enabled", JIT_G(enabled));
790 add_assoc_bool(&stats, "on", JIT_G(on));
791 add_assoc_long(&stats, "kind", JIT_G(trigger));
792 add_assoc_long(&stats, "opt_level", JIT_G(opt_level));
793 add_assoc_long(&stats, "opt_flags", JIT_G(opt_flags));
794 if (dasm_buf) {
795 add_assoc_long(&stats, "buffer_size", (char*)dasm_end - (char*)dasm_buf);
796 add_assoc_long(&stats, "buffer_free", (char*)dasm_end - (char*)*dasm_ptr);
797 } else {
798 add_assoc_long(&stats, "buffer_size", 0);
799 add_assoc_long(&stats, "buffer_free", 0);
800 }
801 add_assoc_zval(ret, "jit", &stats);
802 }
803
zend_jit_func_name(const zend_op_array * op_array)804 static zend_string *zend_jit_func_name(const zend_op_array *op_array)
805 {
806 smart_str buf = {0};
807
808 if (op_array->function_name) {
809 if (op_array->scope) {
810 smart_str_appends(&buf, JIT_PREFIX);
811 smart_str_appendl(&buf, ZSTR_VAL(op_array->scope->name), ZSTR_LEN(op_array->scope->name));
812 smart_str_appends(&buf, "::");
813 smart_str_appendl(&buf, ZSTR_VAL(op_array->function_name), ZSTR_LEN(op_array->function_name));
814 smart_str_0(&buf);
815 return buf.s;
816 } else {
817 smart_str_appends(&buf, JIT_PREFIX);
818 smart_str_appendl(&buf, ZSTR_VAL(op_array->function_name), ZSTR_LEN(op_array->function_name));
819 smart_str_0(&buf);
820 return buf.s;
821 }
822 } else if (op_array->filename) {
823 smart_str_appends(&buf, JIT_PREFIX);
824 smart_str_appendl(&buf, ZSTR_VAL(op_array->filename), ZSTR_LEN(op_array->filename));
825 smart_str_0(&buf);
826 return buf.s;
827 } else {
828 return NULL;
829 }
830 }
831
832 #if ZEND_DEBUG
handle_dasm_error(int ret)833 static void handle_dasm_error(int ret) {
834 switch (ret & 0xff000000u) {
835 case DASM_S_NOMEM:
836 fprintf(stderr, "DASM_S_NOMEM\n");
837 break;
838 case DASM_S_PHASE:
839 fprintf(stderr, "DASM_S_PHASE\n");
840 break;
841 case DASM_S_MATCH_SEC:
842 fprintf(stderr, "DASM_S_MATCH_SEC\n");
843 break;
844 case DASM_S_RANGE_I:
845 fprintf(stderr, "DASM_S_RANGE_I\n");
846 break;
847 case DASM_S_RANGE_SEC:
848 fprintf(stderr, "DASM_S_RANGE_SEC\n");
849 break;
850 case DASM_S_RANGE_LG:
851 fprintf(stderr, "DASM_S_RANGE_LG\n");
852 break;
853 case DASM_S_RANGE_PC:
854 fprintf(stderr, "DASM_S_RANGE_PC %d\n", ret & 0xffffffu);
855 break;
856 #ifdef DASM_S_RANGE_VREG
857 case DASM_S_RANGE_VREG:
858 fprintf(stderr, "DASM_S_RANGE_VREG\n");
859 break;
860 #endif
861 #ifdef DASM_S_UNDEF_L
862 case DASM_S_UNDEF_L:
863 fprintf(stderr, "DASM_S_UNDEF_L\n");
864 break;
865 #endif
866 #ifdef DASM_S_UNDEF_LG
867 case DASM_S_UNDEF_LG:
868 fprintf(stderr, "DASM_S_UNDEF_LG\n");
869 break;
870 #endif
871 #ifdef DASM_S_RANGE_REL
872 case DASM_S_RANGE_REL:
873 fprintf(stderr, "DASM_S_RANGE_REL\n");
874 break;
875 #endif
876 case DASM_S_UNDEF_PC:
877 fprintf(stderr, "DASM_S_UNDEF_PC\n");
878 break;
879 default:
880 fprintf(stderr, "DASM_S_%0x\n", ret & 0xff000000u);
881 break;
882 }
883 ZEND_UNREACHABLE();
884 }
885 #endif
886
dasm_link_and_encode(dasm_State ** dasm_state,const zend_op_array * op_array,zend_ssa * ssa,const zend_op * rt_opline,zend_lifetime_interval ** ra,const char * name,uint32_t trace_num,uint32_t sp_offset,uint32_t sp_adjustment)887 static void *dasm_link_and_encode(dasm_State **dasm_state,
888 const zend_op_array *op_array,
889 zend_ssa *ssa,
890 const zend_op *rt_opline,
891 zend_lifetime_interval **ra,
892 const char *name,
893 uint32_t trace_num,
894 uint32_t sp_offset,
895 uint32_t sp_adjustment)
896 {
897 size_t size;
898 int ret;
899 void *entry;
900 #if defined(HAVE_DISASM) || defined(HAVE_GDB) || defined(HAVE_OPROFILE) || defined(HAVE_PERFTOOLS) || defined(HAVE_VTUNE)
901 zend_string *str = NULL;
902 #endif
903
904 if (rt_opline && ssa && ssa->cfg.map) {
905 /* Create additional entry point, to switch from interpreter to JIT-ed
906 * code at run-time.
907 */
908 int b = ssa->cfg.map[rt_opline - op_array->opcodes];
909
910 //#ifdef CONTEXT_THREADED_JIT
911 // if (!(ssa->cfg.blocks[b].flags & (ZEND_BB_START|ZEND_BB_RECV_ENTRY))) {
912 //#else
913 if (!(ssa->cfg.blocks[b].flags & (ZEND_BB_START|ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY))) {
914 //#endif
915 zend_jit_label(dasm_state, ssa->cfg.blocks_count + b);
916 zend_jit_prologue(dasm_state);
917 if (ra) {
918 int i;
919 zend_lifetime_interval *ival;
920 zend_life_range *range;
921 uint32_t pos = rt_opline - op_array->opcodes;
922
923 for (i = 0; i < ssa->vars_count; i++) {
924 ival = ra[i];
925
926 if (ival && ival->reg != ZREG_NONE) {
927 range = &ival->range;
928
929 if (pos >= range->start && pos <= range->end) {
930 if (!zend_jit_load_var(dasm_state, ssa->var_info[i].type, ssa->vars[i].var, ival->reg)) {
931 return NULL;
932 }
933 break;
934 }
935 range = range->next;
936 }
937 }
938 }
939 zend_jit_jmp(dasm_state, b);
940 }
941 }
942
943 ret = dasm_link(dasm_state, &size);
944 if (ret != DASM_S_OK) {
945 #if ZEND_DEBUG
946 handle_dasm_error(ret);
947 #endif
948 return NULL;
949 }
950
951 if ((void*)((char*)*dasm_ptr + size) > dasm_end) {
952 *dasm_ptr = dasm_end; //prevent further try
953 // TODO: jit_buffer_size overflow ???
954 return NULL;
955 }
956
957 #if ZEND_JIT_TARGET_ARM64
958 dasm_venners_size = 0;
959 #endif
960
961 ret = dasm_encode(dasm_state, *dasm_ptr);
962 if (ret != DASM_S_OK) {
963 #if ZEND_DEBUG
964 handle_dasm_error(ret);
965 #endif
966 return NULL;
967 }
968
969 #if ZEND_JIT_TARGET_ARM64
970 size += dasm_venners_size;
971 #endif
972
973 entry = *dasm_ptr;
974 *dasm_ptr = (void*)((char*)*dasm_ptr + ZEND_MM_ALIGNED_SIZE_EX(size, DASM_ALIGNMENT));
975
976 /* flush the hardware I-cache */
977 JIT_CACHE_FLUSH(entry, entry + size);
978
979 if (trace_num) {
980 zend_jit_trace_add_code(entry, dasm_getpclabel(dasm_state, 1));
981 }
982
983 if (op_array && ssa) {
984 int b;
985
986 for (b = 0; b < ssa->cfg.blocks_count; b++) {
987 //#ifdef CONTEXT_THREADED_JIT
988 // if (ssa->cfg.blocks[b].flags & (ZEND_BB_START|ZEND_BB_RECV_ENTRY)) {
989 //#else
990 if (ssa->cfg.blocks[b].flags & (ZEND_BB_START|ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY)) {
991 //#endif
992 zend_op *opline = op_array->opcodes + ssa->cfg.blocks[b].start;
993 int offset = dasm_getpclabel(dasm_state, ssa->cfg.blocks_count + b);
994
995 if (offset >= 0) {
996 opline->handler = (void*)(((char*)entry) + offset);
997 }
998 }
999 }
1000 if (rt_opline && ssa && ssa->cfg.map) {
1001 int b = ssa->cfg.map[rt_opline - op_array->opcodes];
1002 zend_op *opline = (zend_op*)rt_opline;
1003 int offset = dasm_getpclabel(dasm_state, ssa->cfg.blocks_count + b);
1004
1005 if (offset >= 0) {
1006 opline->handler = (void*)(((char*)entry) + offset);
1007 }
1008 }
1009 }
1010
1011 #if defined(HAVE_DISASM) || defined(HAVE_GDB) || defined(HAVE_OPROFILE) || defined(HAVE_PERFTOOLS) || defined(HAVE_VTUNE)
1012 if (!name) {
1013 if (JIT_G(debug) & (ZEND_JIT_DEBUG_ASM|ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_OPROFILE|ZEND_JIT_DEBUG_PERF|ZEND_JIT_DEBUG_VTUNE|ZEND_JIT_DEBUG_PERF_DUMP)) {
1014 str = zend_jit_func_name(op_array);
1015 if (str) {
1016 name = ZSTR_VAL(str);
1017 }
1018 }
1019 #ifdef HAVE_DISASM
1020 if (JIT_G(debug) & ZEND_JIT_DEBUG_ASM) {
1021 zend_jit_disasm_add_symbol(name, (uintptr_t)entry, size);
1022 zend_jit_disasm(
1023 name,
1024 (op_array && op_array->filename) ? ZSTR_VAL(op_array->filename) : NULL,
1025 op_array,
1026 &ssa->cfg,
1027 entry,
1028 size);
1029 }
1030 } else {
1031 if (JIT_G(debug) & (ZEND_JIT_DEBUG_ASM_STUBS|ZEND_JIT_DEBUG_ASM)) {
1032 zend_jit_disasm_add_symbol(name, (uintptr_t)entry, size);
1033 if ((JIT_G(debug) & (trace_num ? ZEND_JIT_DEBUG_ASM : ZEND_JIT_DEBUG_ASM_STUBS)) != 0) {
1034 zend_jit_disasm(
1035 name,
1036 (op_array && op_array->filename) ? ZSTR_VAL(op_array->filename) : NULL,
1037 op_array,
1038 ssa ? &ssa->cfg : NULL,
1039 entry,
1040 size);
1041 }
1042 }
1043 # endif
1044 }
1045 #endif
1046
1047 #ifdef HAVE_GDB
1048 if (JIT_G(debug) & ZEND_JIT_DEBUG_GDB) {
1049 if (name) {
1050 zend_jit_gdb_register(
1051 name,
1052 op_array,
1053 entry,
1054 size,
1055 sp_adj[sp_offset],
1056 sp_adj[sp_adjustment]);
1057 }
1058 }
1059 #endif
1060
1061 #ifdef HAVE_OPROFILE
1062 if (JIT_G(debug) & ZEND_JIT_DEBUG_OPROFILE) {
1063 zend_jit_oprofile_register(
1064 name,
1065 entry,
1066 size);
1067 }
1068 #endif
1069
1070 #ifdef HAVE_PERFTOOLS
1071 if (JIT_G(debug) & (ZEND_JIT_DEBUG_PERF|ZEND_JIT_DEBUG_PERF_DUMP)) {
1072 if (name) {
1073 zend_jit_perf_map_register(
1074 name,
1075 entry,
1076 size);
1077 if (JIT_G(debug) & ZEND_JIT_DEBUG_PERF_DUMP) {
1078 zend_jit_perf_jitdump_register(
1079 name,
1080 entry,
1081 size);
1082 }
1083 }
1084 }
1085 #endif
1086
1087 #ifdef HAVE_VTUNE
1088 if (JIT_G(debug) & ZEND_JIT_DEBUG_VTUNE) {
1089 if (name) {
1090 zend_jit_vtune_register(
1091 name,
1092 entry,
1093 size);
1094 }
1095 }
1096 #endif
1097
1098 #if defined(HAVE_DISASM) || defined(HAVE_GDB) || defined(HAVE_OPROFILE) || defined(HAVE_PERFTOOLS) || defined(HAVE_VTUNE)
1099 if (str) {
1100 zend_string_release(str);
1101 }
1102 #endif
1103
1104 return entry;
1105 }
1106
zend_may_overflow(const zend_op * opline,const zend_ssa_op * ssa_op,const zend_op_array * op_array,zend_ssa * ssa)1107 static int zend_may_overflow(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa)
1108 {
1109 int res;
1110 zend_long op1_min, op1_max, op2_min, op2_max;
1111
1112 if (!ssa->ops || !ssa->var_info) {
1113 return 1;
1114 }
1115 switch (opline->opcode) {
1116 case ZEND_PRE_INC:
1117 case ZEND_POST_INC:
1118 res = ssa_op->op1_def;
1119 if (res < 0
1120 || !ssa->var_info[res].has_range
1121 || ssa->var_info[res].range.overflow) {
1122 if (!OP1_HAS_RANGE()) {
1123 return 1;
1124 }
1125 op1_max = OP1_MAX_RANGE();
1126 if (op1_max == ZEND_LONG_MAX) {
1127 return 1;
1128 }
1129 }
1130 return 0;
1131 case ZEND_PRE_DEC:
1132 case ZEND_POST_DEC:
1133 res = ssa_op->op1_def;
1134 if (res < 0
1135 || !ssa->var_info[res].has_range
1136 || ssa->var_info[res].range.underflow) {
1137 if (!OP1_HAS_RANGE()) {
1138 return 1;
1139 }
1140 op1_min = OP1_MIN_RANGE();
1141 if (op1_min == ZEND_LONG_MIN) {
1142 return 1;
1143 }
1144 }
1145 return 0;
1146 case ZEND_ADD:
1147 res = ssa_op->result_def;
1148 if (res < 0
1149 || !ssa->var_info[res].has_range
1150 || ssa->var_info[res].range.underflow) {
1151 if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
1152 return 1;
1153 }
1154 op1_min = OP1_MIN_RANGE();
1155 op2_min = OP2_MIN_RANGE();
1156 if (zend_add_will_overflow(op1_min, op2_min)) {
1157 return 1;
1158 }
1159 }
1160 if (res < 0
1161 || !ssa->var_info[res].has_range
1162 || ssa->var_info[res].range.overflow) {
1163 if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
1164 return 1;
1165 }
1166 op1_max = OP1_MAX_RANGE();
1167 op2_max = OP2_MAX_RANGE();
1168 if (zend_add_will_overflow(op1_max, op2_max)) {
1169 return 1;
1170 }
1171 }
1172 return 0;
1173 case ZEND_SUB:
1174 res = ssa_op->result_def;
1175 if (res < 0
1176 || !ssa->var_info[res].has_range
1177 || ssa->var_info[res].range.underflow) {
1178 if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
1179 return 1;
1180 }
1181 op1_min = OP1_MIN_RANGE();
1182 op2_max = OP2_MAX_RANGE();
1183 if (zend_sub_will_overflow(op1_min, op2_max)) {
1184 return 1;
1185 }
1186 }
1187 if (res < 0
1188 || !ssa->var_info[res].has_range
1189 || ssa->var_info[res].range.overflow) {
1190 if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
1191 return 1;
1192 }
1193 op1_max = OP1_MAX_RANGE();
1194 op2_min = OP2_MIN_RANGE();
1195 if (zend_sub_will_overflow(op1_max, op2_min)) {
1196 return 1;
1197 }
1198 }
1199 return 0;
1200 case ZEND_MUL:
1201 res = ssa_op->result_def;
1202 return (res < 0 ||
1203 !ssa->var_info[res].has_range ||
1204 ssa->var_info[res].range.underflow ||
1205 ssa->var_info[res].range.overflow);
1206 case ZEND_ASSIGN_OP:
1207 if (opline->extended_value == ZEND_ADD) {
1208 res = ssa_op->op1_def;
1209 if (res < 0
1210 || !ssa->var_info[res].has_range
1211 || ssa->var_info[res].range.underflow) {
1212 if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
1213 return 1;
1214 }
1215 op1_min = OP1_MIN_RANGE();
1216 op2_min = OP2_MIN_RANGE();
1217 if (zend_add_will_overflow(op1_min, op2_min)) {
1218 return 1;
1219 }
1220 }
1221 if (res < 0
1222 || !ssa->var_info[res].has_range
1223 || ssa->var_info[res].range.overflow) {
1224 if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
1225 return 1;
1226 }
1227 op1_max = OP1_MAX_RANGE();
1228 op2_max = OP2_MAX_RANGE();
1229 if (zend_add_will_overflow(op1_max, op2_max)) {
1230 return 1;
1231 }
1232 }
1233 return 0;
1234 } else if (opline->extended_value == ZEND_SUB) {
1235 res = ssa_op->op1_def;
1236 if (res < 0
1237 || !ssa->var_info[res].has_range
1238 || ssa->var_info[res].range.underflow) {
1239 if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
1240 return 1;
1241 }
1242 op1_min = OP1_MIN_RANGE();
1243 op2_max = OP2_MAX_RANGE();
1244 if (zend_sub_will_overflow(op1_min, op2_max)) {
1245 return 1;
1246 }
1247 }
1248 if (res < 0
1249 || !ssa->var_info[res].has_range
1250 || ssa->var_info[res].range.overflow) {
1251 if (!OP1_HAS_RANGE() || !OP2_HAS_RANGE()) {
1252 return 1;
1253 }
1254 op1_max = OP1_MAX_RANGE();
1255 op2_min = OP2_MIN_RANGE();
1256 if (zend_sub_will_overflow(op1_max, op2_min)) {
1257 return 1;
1258 }
1259 }
1260 return 0;
1261 } else if (opline->extended_value == ZEND_MUL) {
1262 res = ssa_op->op1_def;
1263 return (res < 0 ||
1264 !ssa->var_info[res].has_range ||
1265 ssa->var_info[res].range.underflow ||
1266 ssa->var_info[res].range.overflow);
1267 }
1268 ZEND_FALLTHROUGH;
1269 default:
1270 return 1;
1271 }
1272 }
1273
zend_jit_build_cfg(const zend_op_array * op_array,zend_cfg * cfg)1274 static int zend_jit_build_cfg(const zend_op_array *op_array, zend_cfg *cfg)
1275 {
1276 uint32_t flags;
1277
1278 flags = ZEND_CFG_STACKLESS | ZEND_CFG_NO_ENTRY_PREDECESSORS | ZEND_SSA_RC_INFERENCE_FLAG | ZEND_SSA_USE_CV_RESULTS | ZEND_CFG_RECV_ENTRY;
1279
1280 if (zend_build_cfg(&CG(arena), op_array, flags, cfg) != SUCCESS) {
1281 return FAILURE;
1282 }
1283
1284 /* Don't JIT huge functions. Apart from likely being detrimental due to the amount of
1285 * generated code, some of our analysis is recursive and will stack overflow with many
1286 * blocks. */
1287 if (cfg->blocks_count > 100000) {
1288 return FAILURE;
1289 }
1290
1291 if (zend_cfg_build_predecessors(&CG(arena), cfg) != SUCCESS) {
1292 return FAILURE;
1293 }
1294
1295 /* Compute Dominators Tree */
1296 if (zend_cfg_compute_dominators_tree(op_array, cfg) != SUCCESS) {
1297 return FAILURE;
1298 }
1299
1300 /* Identify reducible and irreducible loops */
1301 if (zend_cfg_identify_loops(op_array, cfg) != SUCCESS) {
1302 return FAILURE;
1303 }
1304
1305 return SUCCESS;
1306 }
1307
zend_jit_op_array_analyze1(const zend_op_array * op_array,zend_script * script,zend_ssa * ssa)1308 static int zend_jit_op_array_analyze1(const zend_op_array *op_array, zend_script *script, zend_ssa *ssa)
1309 {
1310 if (zend_jit_build_cfg(op_array, &ssa->cfg) != SUCCESS) {
1311 return FAILURE;
1312 }
1313
1314 #if 0
1315 /* TODO: debugger and profiler supports? */
1316 if ((ssa->cfg.flags & ZEND_FUNC_HAS_EXTENDED_INFO)) {
1317 return FAILURE;
1318 }
1319 #endif
1320
1321 /* TODO: move this to zend_cfg.c ? */
1322 if (!op_array->function_name) {
1323 ssa->cfg.flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
1324 }
1325
1326 if ((JIT_G(opt_level) >= ZEND_JIT_LEVEL_OPT_FUNC)
1327 && ssa->cfg.blocks
1328 && op_array->last_try_catch == 0
1329 && !(op_array->fn_flags & ZEND_ACC_GENERATOR)
1330 && !(ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS)) {
1331 if (zend_build_ssa(&CG(arena), script, op_array, ZEND_SSA_RC_INFERENCE | ZEND_SSA_USE_CV_RESULTS, ssa) != SUCCESS) {
1332 return FAILURE;
1333 }
1334
1335 if (zend_ssa_compute_use_def_chains(&CG(arena), op_array, ssa) != SUCCESS) {
1336 return FAILURE;
1337 }
1338
1339 if (zend_ssa_find_false_dependencies(op_array, ssa) != SUCCESS) {
1340 return FAILURE;
1341 }
1342
1343 if (zend_ssa_find_sccs(op_array, ssa) != SUCCESS){
1344 return FAILURE;
1345 }
1346 }
1347
1348 return SUCCESS;
1349 }
1350
zend_jit_op_array_analyze2(const zend_op_array * op_array,zend_script * script,zend_ssa * ssa,uint32_t optimization_level)1351 static int zend_jit_op_array_analyze2(const zend_op_array *op_array, zend_script *script, zend_ssa *ssa, uint32_t optimization_level)
1352 {
1353 if ((JIT_G(opt_level) >= ZEND_JIT_LEVEL_OPT_FUNC)
1354 && ssa->cfg.blocks
1355 && op_array->last_try_catch == 0
1356 && !(op_array->fn_flags & ZEND_ACC_GENERATOR)
1357 && !(ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS)) {
1358 if (zend_ssa_inference(&CG(arena), op_array, script, ssa,
1359 optimization_level & ~ZEND_OPTIMIZER_NARROW_TO_DOUBLE) != SUCCESS) {
1360 return FAILURE;
1361 }
1362 }
1363
1364 return SUCCESS;
1365 }
1366
zend_jit_add_range(zend_lifetime_interval ** intervals,int var,uint32_t from,uint32_t to)1367 static int zend_jit_add_range(zend_lifetime_interval **intervals, int var, uint32_t from, uint32_t to)
1368 {
1369 zend_lifetime_interval *ival = intervals[var];
1370
1371 if (!ival) {
1372 ival = zend_arena_alloc(&CG(arena), sizeof(zend_lifetime_interval));
1373 if (!ival) {
1374 return FAILURE;
1375 }
1376 ival->ssa_var = var;
1377 ival->reg = ZREG_NONE;
1378 ival->flags = 0;
1379 ival->range.start = from;
1380 ival->range.end = to;
1381 ival->range.next = NULL;
1382 ival->hint = NULL;
1383 ival->used_as_hint = NULL;
1384 intervals[var] = ival;
1385 } else if (ival->range.start > to + 1) {
1386 zend_life_range *range = zend_arena_alloc(&CG(arena), sizeof(zend_life_range));
1387
1388 if (!range) {
1389 return FAILURE;
1390 }
1391 range->start = ival->range.start;
1392 range->end = ival->range.end;
1393 range->next = ival->range.next;
1394 ival->range.start = from;
1395 ival->range.end = to;
1396 ival->range.next = range;
1397 } else if (ival->range.start == to + 1) {
1398 ival->range.start = from;
1399 } else {
1400 zend_life_range *range = &ival->range;
1401 zend_life_range *last = NULL;
1402
1403 do {
1404 if (range->start > to + 1) {
1405 break;
1406 } else if (range->end + 1 >= from) {
1407 if (range->start > from) {
1408 range->start = from;
1409 }
1410 last = range;
1411 range = range->next;
1412 while (range) {
1413 if (range->start > to + 1) {
1414 break;
1415 }
1416 last->end = range->end;
1417 range = range->next;
1418 last->next = range;
1419 }
1420 if (to > last->end) {
1421 last->end = to;
1422 }
1423 return SUCCESS;
1424 }
1425 last = range;
1426 range = range->next;
1427 } while (range);
1428
1429 range = zend_arena_alloc(&CG(arena), sizeof(zend_life_range));
1430 if (!range) {
1431 return FAILURE;
1432 }
1433 range->start = from;
1434 range->end = to;
1435 range->next = last->next;
1436 last->next = range;
1437 }
1438
1439 return SUCCESS;
1440 }
1441
zend_jit_begin_range(zend_lifetime_interval ** intervals,int var,uint32_t block_start,uint32_t from)1442 static int zend_jit_begin_range(zend_lifetime_interval **intervals, int var, uint32_t block_start, uint32_t from)
1443 {
1444 if (block_start != from && intervals[var]) {
1445 zend_life_range *range = &intervals[var]->range;
1446
1447 do {
1448 if (from >= range->start && from <= range->end) {
1449 if (range->start == block_start) {
1450 range->start = from;
1451 } else {
1452 zend_life_range *r = zend_arena_alloc(&CG(arena), sizeof(zend_life_range));
1453 if (!r) {
1454 return FAILURE;
1455 }
1456 r->start = from;
1457 r->end = range->end;
1458 r->next = range->next;
1459 range->end = block_start - 1;
1460 range->next = r;
1461 }
1462 return SUCCESS;
1463 }
1464 range = range->next;
1465 } while (range);
1466 }
1467
1468 // dead store
1469 return zend_jit_add_range(intervals, var, from, from);
1470 }
1471
zend_jit_insert_interval(zend_lifetime_interval ** list,zend_lifetime_interval * ival)1472 static void zend_jit_insert_interval(zend_lifetime_interval **list, zend_lifetime_interval *ival)
1473 {
1474 while (1) {
1475 if (*list == NULL) {
1476 *list = ival;
1477 ival->list_next = NULL;
1478 return;
1479 } else if (ival->range.start < (*list)->range.start) {
1480 ival->list_next = *list;
1481 *list = ival;
1482 return;
1483 }
1484 list = &(*list)->list_next;
1485 }
1486 }
1487
zend_jit_split_interval(zend_lifetime_interval * current,uint32_t pos,zend_lifetime_interval ** list,zend_lifetime_interval ** free)1488 static int zend_jit_split_interval(zend_lifetime_interval *current, uint32_t pos, zend_lifetime_interval **list, zend_lifetime_interval **free)
1489 {
1490 zend_lifetime_interval *ival;
1491 zend_life_range *range = ¤t->range;
1492 zend_life_range *prev = NULL;
1493
1494 if (*free) {
1495 ival = *free;
1496 *free = ival->list_next;
1497 } else {
1498 ival = zend_arena_alloc(&CG(arena), sizeof(zend_lifetime_interval));
1499
1500 if (!ival) {
1501 return FAILURE;
1502 }
1503 }
1504
1505 current->flags |= ZREG_STORE;
1506
1507 ival->ssa_var = current->ssa_var;
1508 ival->reg = ZREG_NONE;
1509 ival->flags |= ZREG_SPLIT | ZREG_LOAD;
1510 ival->flags &= ~ZREG_STORE;
1511 ival->hint = NULL;
1512
1513 do {
1514 if (pos >= range->start && pos <= range->end) {
1515 break;
1516 }
1517 prev = range;
1518 range = range->next;
1519 } while(range);
1520
1521 ZEND_ASSERT(range != NULL);
1522
1523 ival->range.start = pos;
1524 ival->range.end = range->end;
1525 ival->range.next = range->next;
1526
1527 if (pos == range->start) {
1528 ZEND_ASSERT(prev != NULL);
1529 prev->next = NULL;
1530 } else {
1531 range->end = pos - 1;
1532 }
1533
1534 zend_jit_insert_interval(list, ival);
1535
1536 return SUCCESS;
1537 }
1538
zend_jit_sort_intervals(zend_lifetime_interval ** intervals,int count)1539 static zend_lifetime_interval *zend_jit_sort_intervals(zend_lifetime_interval **intervals, int count)
1540 {
1541 zend_lifetime_interval *list, *last;
1542 int i;
1543
1544 list = NULL;
1545 i = 0;
1546 while (i < count) {
1547 list = intervals[i];
1548 i++;
1549 if (list) {
1550 last = list;
1551 last->list_next = NULL;
1552 break;
1553 }
1554 }
1555
1556 while (i < count) {
1557 zend_lifetime_interval *ival = intervals[i];
1558
1559 i++;
1560 if (ival) {
1561 if ((ival->range.start > last->range.start) ||
1562 (ival->range.start == last->range.start &&
1563 ((!ival->hint && last->hint && last->hint != ival) ||
1564 ival->range.end > last->range.end))) {
1565 last->list_next = ival;
1566 last = ival;
1567 ival->list_next = NULL;
1568 } else {
1569 zend_lifetime_interval **p = &list;
1570
1571 while (1) {
1572 if (*p == NULL) {
1573 *p = last = ival;
1574 ival->list_next = NULL;
1575 break;
1576 } else if ((ival->range.start < (*p)->range.start) ||
1577 (ival->range.start == (*p)->range.start &&
1578 ((ival->hint && !(*p)->hint && ival->hint != *p) ||
1579 ival->range.end < (*p)->range.end))) {
1580 ival->list_next = *p;
1581 *p = ival;
1582 break;
1583 }
1584 p = &(*p)->list_next;
1585 }
1586 }
1587 }
1588 }
1589
1590 return list;
1591 }
1592
zend_jit_print_regset(zend_regset regset)1593 static ZEND_ATTRIBUTE_UNUSED void zend_jit_print_regset(zend_regset regset)
1594 {
1595 zend_reg reg;
1596 int first = 1;
1597
1598 ZEND_REGSET_FOREACH(regset, reg) {
1599 if (first) {
1600 first = 0;
1601 fprintf(stderr, "%s", zend_reg_name[reg]);
1602 } else {
1603 fprintf(stderr, ", %s", zend_reg_name[reg]);
1604 }
1605 } ZEND_REGSET_FOREACH_END();
1606 }
1607
zend_jit_compute_block_order_int(zend_ssa * ssa,int n,int * block_order)1608 static int *zend_jit_compute_block_order_int(zend_ssa *ssa, int n, int *block_order)
1609 {
1610 zend_basic_block *b = ssa->cfg.blocks + n;
1611
1612 tail_call:
1613 *block_order = n;
1614 block_order++;
1615
1616 n = b->children;
1617 while (n >= 0) {
1618 b = ssa->cfg.blocks + n;
1619 if (b->next_child < 0) {
1620 goto tail_call;
1621 }
1622 block_order = zend_jit_compute_block_order_int(ssa, n, block_order);
1623 n = b->next_child;
1624 }
1625
1626 return block_order;
1627 }
1628
zend_jit_compute_block_order(zend_ssa * ssa,int * block_order)1629 static int zend_jit_compute_block_order(zend_ssa *ssa, int *block_order)
1630 {
1631 int *end = zend_jit_compute_block_order_int(ssa, 0, block_order);
1632
1633 return end - block_order;
1634 }
1635
zend_jit_in_loop(zend_ssa * ssa,int header,zend_basic_block * b)1636 static bool zend_jit_in_loop(zend_ssa *ssa, int header, zend_basic_block *b)
1637 {
1638 while (b->loop_header >= 0) {
1639 if (b->loop_header == header) {
1640 return 1;
1641 }
1642 b = ssa->cfg.blocks + b->loop_header;
1643 }
1644 return 0;
1645 }
1646
zend_jit_compute_loop_body(zend_ssa * ssa,int header,int n,zend_bitset loop_body)1647 static void zend_jit_compute_loop_body(zend_ssa *ssa, int header, int n, zend_bitset loop_body)
1648 {
1649 zend_basic_block *b = ssa->cfg.blocks + n;
1650 uint32_t i;
1651
1652 tail_call:
1653 if (b->len) {
1654 for (i = b->start; i < b->start + b->len; i++) {
1655 zend_bitset_incl(loop_body, i);
1656 }
1657 }
1658
1659 n = b->children;
1660 while (n >= 0) {
1661 b = ssa->cfg.blocks + n;
1662 if (zend_jit_in_loop(ssa, header, b)) {
1663 if (b->next_child < 0) {
1664 goto tail_call;
1665 }
1666 zend_jit_compute_loop_body(ssa, header, n, loop_body);
1667 }
1668 n = b->next_child;
1669 }
1670 }
1671
zend_jit_add_hint(zend_lifetime_interval ** intervals,int dst,int src)1672 static void zend_jit_add_hint(zend_lifetime_interval **intervals, int dst, int src)
1673 {
1674 if (intervals[dst]->range.start < intervals[src]->range.start) {
1675 int tmp = src;
1676 src = dst;
1677 dst = tmp;
1678 }
1679 while (dst != src && intervals[dst]->hint) {
1680 if (intervals[dst]->hint->range.start < intervals[src]->range.start) {
1681 int tmp = src;
1682 src = intervals[dst]->hint->ssa_var;
1683 dst = tmp;
1684 } else {
1685 dst = intervals[dst]->hint->ssa_var;
1686 }
1687 }
1688 if (dst != src) {
1689 intervals[dst]->hint = intervals[src];
1690 }
1691 }
1692
1693 /* See "Linear Scan Register Allocation on SSA Form", Christian Wimmer and
1694 Michael Franz, CGO'10 (2010), Figure 4. */
zend_jit_compute_liveness(const zend_op_array * op_array,zend_ssa * ssa,zend_bitset candidates,zend_lifetime_interval ** list)1695 static int zend_jit_compute_liveness(const zend_op_array *op_array, zend_ssa *ssa, zend_bitset candidates, zend_lifetime_interval **list)
1696 {
1697 int set_size, i, j, k, l;
1698 uint32_t n;
1699 zend_bitset live, live_in, pi_vars, loop_body;
1700 int *block_order;
1701 zend_ssa_phi *phi;
1702 zend_lifetime_interval **intervals;
1703 size_t mem_size;
1704 ALLOCA_FLAG(use_heap);
1705
1706 set_size = zend_bitset_len(ssa->vars_count);
1707 mem_size =
1708 ZEND_MM_ALIGNED_SIZE(ssa->vars_count * sizeof(zend_lifetime_interval*)) +
1709 ZEND_MM_ALIGNED_SIZE((set_size * ssa->cfg.blocks_count) * ZEND_BITSET_ELM_SIZE) +
1710 ZEND_MM_ALIGNED_SIZE(set_size * ZEND_BITSET_ELM_SIZE) +
1711 ZEND_MM_ALIGNED_SIZE(set_size * ZEND_BITSET_ELM_SIZE) +
1712 ZEND_MM_ALIGNED_SIZE(zend_bitset_len(op_array->last) * ZEND_BITSET_ELM_SIZE) +
1713 ZEND_MM_ALIGNED_SIZE(ssa->cfg.blocks_count * sizeof(int));
1714 intervals = do_alloca(mem_size, use_heap);
1715 if (!intervals) {
1716 *list = NULL;
1717 return FAILURE;
1718 }
1719
1720 live_in = (zend_bitset)((char*)intervals + ZEND_MM_ALIGNED_SIZE(ssa->vars_count * sizeof(zend_lifetime_interval*)));
1721 live = (zend_bitset)((char*)live_in + ZEND_MM_ALIGNED_SIZE((set_size * ssa->cfg.blocks_count) * ZEND_BITSET_ELM_SIZE));
1722 pi_vars = (zend_bitset)((char*)live + ZEND_MM_ALIGNED_SIZE(set_size * ZEND_BITSET_ELM_SIZE));
1723 loop_body = (zend_bitset)((char*)pi_vars + ZEND_MM_ALIGNED_SIZE(set_size * ZEND_BITSET_ELM_SIZE));
1724 block_order = (int*)((char*)loop_body + ZEND_MM_ALIGNED_SIZE(zend_bitset_len(op_array->last) * ZEND_BITSET_ELM_SIZE));
1725
1726 memset(intervals, 0, ssa->vars_count * sizeof(zend_lifetime_interval*));
1727 zend_bitset_clear(live_in, set_size * ssa->cfg.blocks_count);
1728
1729 /* TODO: Provide a linear block order where all dominators of a block
1730 * are before this block, and where all blocks belonging to the same loop
1731 * are contiguous ???
1732 */
1733 for (l = zend_jit_compute_block_order(ssa, block_order) - 1; l >= 0; l--) {
1734 zend_basic_block *b;
1735
1736 i = block_order[l];
1737 b = ssa->cfg.blocks + i;
1738
1739 /* live = UNION of successor.liveIn for each successor of b */
1740 /* live.add(phi.inputOf(b)) for each phi of successors of b */
1741 zend_bitset_clear(live, set_size);
1742 for (j = 0; j < b->successors_count; j++) {
1743 int succ = b->successors[j];
1744
1745 zend_bitset_union(live, live_in + set_size * succ, set_size);
1746 zend_bitset_clear(pi_vars, set_size);
1747 for (phi = ssa->blocks[succ].phis; phi; phi = phi->next) {
1748 if (ssa->vars[phi->ssa_var].no_val) {
1749 /* skip */
1750 } else if (phi->pi >= 0) {
1751 if (phi->pi == i && phi->sources[0] >= 0) {
1752 if (zend_bitset_in(candidates, phi->sources[0])) {
1753 zend_bitset_incl(live, phi->sources[0]);
1754 }
1755 zend_bitset_incl(pi_vars, phi->var);
1756 }
1757 } else if (!zend_bitset_in(pi_vars, phi->var)) {
1758 for (k = 0; k < ssa->cfg.blocks[succ].predecessors_count; k++) {
1759 if (ssa->cfg.predecessors[ssa->cfg.blocks[succ].predecessor_offset + k] == i) {
1760 if (phi->sources[k] >= 0 && zend_bitset_in(candidates, phi->sources[k])) {
1761 zend_bitset_incl(live, phi->sources[k]);
1762 }
1763 break;
1764 }
1765 }
1766 }
1767 }
1768 }
1769
1770 /* addRange(var, b.from, b.to) for each var in live */
1771 ZEND_BITSET_FOREACH(live, set_size, j) {
1772 if (zend_bitset_in(candidates, j)) {
1773 if (zend_jit_add_range(intervals, j, b->start, b->start + b->len - 1) != SUCCESS) {
1774 goto failure;
1775 }
1776 }
1777 } ZEND_BITSET_FOREACH_END();
1778
1779 /* for each operation op of b in reverse order */
1780 for (n = b->start + b->len; n > b->start;) {
1781 zend_ssa_op *op;
1782 const zend_op *opline;
1783 uint32_t num;
1784
1785 n--;
1786 op = ssa->ops + n;
1787 opline = op_array->opcodes + n;
1788
1789 if (UNEXPECTED(opline->opcode == ZEND_OP_DATA)) {
1790 num = n - 1;
1791 } else {
1792 num = n;
1793 }
1794
1795 /* for each output operand opd of op do */
1796 /* setFrom(opd, op) */
1797 /* live.remove(opd) */
1798 if (op->op1_def >= 0 && zend_bitset_in(candidates, op->op1_def)) {
1799 if (zend_jit_begin_range(intervals, op->op1_def, b->start, num) != SUCCESS) {
1800 goto failure;
1801 }
1802 zend_bitset_excl(live, op->op1_def);
1803 }
1804 if (op->op2_def >= 0 && zend_bitset_in(candidates, op->op2_def)) {
1805 if (zend_jit_begin_range(intervals, op->op2_def, b->start, num) != SUCCESS) {
1806 goto failure;
1807 }
1808 zend_bitset_excl(live, op->op2_def);
1809 }
1810 if (op->result_def >= 0 && zend_bitset_in(candidates, op->result_def)) {
1811 if (zend_jit_begin_range(intervals, op->result_def, b->start, num) != SUCCESS) {
1812 goto failure;
1813 }
1814 zend_bitset_excl(live, op->result_def);
1815 }
1816
1817 /* for each input operand opd of op do */
1818 /* live.add(opd) */
1819 /* addRange(opd, b.from, op) */
1820 if (op->op1_use >= 0
1821 && zend_bitset_in(candidates, op->op1_use)
1822 && !zend_ssa_is_no_val_use(opline, op, op->op1_use)) {
1823 zend_bitset_incl(live, op->op1_use);
1824 if (zend_jit_add_range(intervals, op->op1_use, b->start, num) != SUCCESS) {
1825 goto failure;
1826 }
1827 }
1828 if (op->op2_use >= 0
1829 && zend_bitset_in(candidates, op->op2_use)
1830 && !zend_ssa_is_no_val_use(opline, op, op->op2_use)) {
1831 zend_bitset_incl(live, op->op2_use);
1832 if (zend_jit_add_range(intervals, op->op2_use, b->start, num) != SUCCESS) {
1833 goto failure;
1834 }
1835 }
1836 if (op->result_use >= 0
1837 && zend_bitset_in(candidates, op->result_use)
1838 && !zend_ssa_is_no_val_use(opline, op, op->result_use)) {
1839 zend_bitset_incl(live, op->result_use);
1840 if (zend_jit_add_range(intervals, op->result_use, b->start, num) != SUCCESS) {
1841 goto failure;
1842 }
1843 }
1844 }
1845
1846 /* live.remove(phi.output) for each phi of b */
1847 for (phi = ssa->blocks[i].phis; phi; phi = phi->next) {
1848 zend_bitset_excl(live, phi->ssa_var);
1849 }
1850
1851 /* b.liveIn = live */
1852 zend_bitset_copy(live_in + set_size * i, live, set_size);
1853 }
1854
1855 for (i = ssa->cfg.blocks_count - 1; i >= 0; i--) {
1856 zend_basic_block *b = ssa->cfg.blocks + i;
1857
1858 /* if b is loop header */
1859 if ((b->flags & ZEND_BB_LOOP_HEADER)) {
1860 live = live_in + set_size * i;
1861
1862 if (!zend_bitset_empty(live, set_size)) {
1863 uint32_t set_size2 = zend_bitset_len(op_array->last);
1864
1865 zend_bitset_clear(loop_body, set_size2);
1866 zend_jit_compute_loop_body(ssa, i, i, loop_body);
1867 while (!zend_bitset_empty(loop_body, set_size2)) {
1868 uint32_t from = zend_bitset_first(loop_body, set_size2);
1869 uint32_t to = from;
1870
1871 do {
1872 zend_bitset_excl(loop_body, to);
1873 to++;
1874 } while (zend_bitset_in(loop_body, to));
1875 to--;
1876
1877 ZEND_BITSET_FOREACH(live, set_size, j) {
1878 if (zend_jit_add_range(intervals, j, from, to) != SUCCESS) {
1879 goto failure;
1880 }
1881 } ZEND_BITSET_FOREACH_END();
1882 }
1883 }
1884 }
1885
1886 }
1887
1888 if (JIT_G(opt_flags) & ZEND_JIT_REG_ALLOC_GLOBAL) {
1889 /* Register hinting (a cheap way for register coalescing) */
1890 for (i = 0; i < ssa->vars_count; i++) {
1891 if (intervals[i]) {
1892 int src;
1893
1894 if (ssa->vars[i].definition_phi) {
1895 zend_ssa_phi *phi = ssa->vars[i].definition_phi;
1896
1897 if (phi->pi >= 0) {
1898 src = phi->sources[0];
1899 if (intervals[src]) {
1900 zend_jit_add_hint(intervals, i, src);
1901 }
1902 } else {
1903 for (k = 0; k < ssa->cfg.blocks[phi->block].predecessors_count; k++) {
1904 src = phi->sources[k];
1905 if (src >= 0) {
1906 if (ssa->vars[src].definition_phi
1907 && ssa->vars[src].definition_phi->pi >= 0
1908 && phi->block == ssa->vars[src].definition_phi->block) {
1909 /* Skip zero-length interval for Pi variable */
1910 src = ssa->vars[src].definition_phi->sources[0];
1911 }
1912 if (intervals[src]) {
1913 zend_jit_add_hint(intervals, i, src);
1914 }
1915 }
1916 }
1917 }
1918 }
1919 }
1920 }
1921 for (i = 0; i < ssa->vars_count; i++) {
1922 if (intervals[i] && !intervals[i]->hint) {
1923
1924 if (ssa->vars[i].definition >= 0) {
1925 uint32_t line = ssa->vars[i].definition;
1926 const zend_op *opline = op_array->opcodes + line;
1927
1928 switch (opline->opcode) {
1929 case ZEND_QM_ASSIGN:
1930 case ZEND_POST_INC:
1931 case ZEND_POST_DEC:
1932 if (ssa->ops[line].op1_use >= 0 &&
1933 intervals[ssa->ops[line].op1_use] &&
1934 (i == ssa->ops[line].op1_def ||
1935 (i == ssa->ops[line].result_def &&
1936 (ssa->ops[line].op1_def < 0 ||
1937 !intervals[ssa->ops[line].op1_def])))) {
1938 zend_jit_add_hint(intervals, i, ssa->ops[line].op1_use);
1939 }
1940 break;
1941 case ZEND_SEND_VAR:
1942 case ZEND_PRE_INC:
1943 case ZEND_PRE_DEC:
1944 if (i == ssa->ops[line].op1_def &&
1945 ssa->ops[line].op1_use >= 0 &&
1946 intervals[ssa->ops[line].op1_use]) {
1947 zend_jit_add_hint(intervals, i, ssa->ops[line].op1_use);
1948 }
1949 break;
1950 case ZEND_ASSIGN:
1951 if (ssa->ops[line].op2_use >= 0 &&
1952 intervals[ssa->ops[line].op2_use] &&
1953 (i == ssa->ops[line].op2_def ||
1954 (i == ssa->ops[line].op1_def &&
1955 (ssa->ops[line].op2_def < 0 ||
1956 !intervals[ssa->ops[line].op2_def])) ||
1957 (i == ssa->ops[line].result_def &&
1958 (ssa->ops[line].op2_def < 0 ||
1959 !intervals[ssa->ops[line].op2_def]) &&
1960 (ssa->ops[line].op1_def < 0 ||
1961 !intervals[ssa->ops[line].op1_def])))) {
1962 zend_jit_add_hint(intervals, i, ssa->ops[line].op2_use);
1963 }
1964 break;
1965 case ZEND_SUB:
1966 case ZEND_ADD:
1967 case ZEND_MUL:
1968 case ZEND_BW_OR:
1969 case ZEND_BW_AND:
1970 case ZEND_BW_XOR:
1971 if (i == ssa->ops[line].result_def) {
1972 if (ssa->ops[line].op1_use >= 0 &&
1973 intervals[ssa->ops[line].op1_use] &&
1974 ssa->ops[line].op1_use_chain < 0 &&
1975 !ssa->vars[ssa->ops[line].op1_use].phi_use_chain &&
1976 (ssa->var_info[i].type & MAY_BE_ANY) ==
1977 (ssa->var_info[ssa->ops[line].op1_use].type & MAY_BE_ANY)) {
1978 zend_jit_add_hint(intervals, i, ssa->ops[line].op1_use);
1979 } else if (opline->opcode != ZEND_SUB &&
1980 ssa->ops[line].op2_use >= 0 &&
1981 intervals[ssa->ops[line].op2_use] &&
1982 ssa->ops[line].op2_use_chain < 0 &&
1983 !ssa->vars[ssa->ops[line].op2_use].phi_use_chain &&
1984 (ssa->var_info[i].type & MAY_BE_ANY) ==
1985 (ssa->var_info[ssa->ops[line].op2_use].type & MAY_BE_ANY)) {
1986 zend_jit_add_hint(intervals, i, ssa->ops[line].op2_use);
1987 }
1988 }
1989 break;
1990 }
1991 }
1992 }
1993 }
1994 }
1995
1996 *list = zend_jit_sort_intervals(intervals, ssa->vars_count);
1997
1998 if (*list) {
1999 zend_lifetime_interval *ival = *list;
2000 while (ival) {
2001 if (ival->hint) {
2002 ival->hint->used_as_hint = ival;
2003 }
2004 ival = ival->list_next;
2005 }
2006 }
2007
2008 free_alloca(intervals, use_heap);
2009 return SUCCESS;
2010
2011 failure:
2012 *list = NULL;
2013 free_alloca(intervals, use_heap);
2014 return FAILURE;
2015 }
2016
zend_interval_end(zend_lifetime_interval * ival)2017 static uint32_t zend_interval_end(zend_lifetime_interval *ival)
2018 {
2019 zend_life_range *range = &ival->range;
2020
2021 while (range->next) {
2022 range = range->next;
2023 }
2024 return range->end;
2025 }
2026
zend_interval_covers(zend_lifetime_interval * ival,uint32_t position)2027 static bool zend_interval_covers(zend_lifetime_interval *ival, uint32_t position)
2028 {
2029 zend_life_range *range = &ival->range;
2030
2031 do {
2032 if (position >= range->start && position <= range->end) {
2033 return 1;
2034 }
2035 range = range->next;
2036 } while (range);
2037
2038 return 0;
2039 }
2040
zend_interval_intersection(zend_lifetime_interval * ival1,zend_lifetime_interval * ival2)2041 static uint32_t zend_interval_intersection(zend_lifetime_interval *ival1, zend_lifetime_interval *ival2)
2042 {
2043 zend_life_range *r1 = &ival1->range;
2044 zend_life_range *r2 = &ival2->range;
2045
2046 do {
2047 if (r1->start <= r2->end) {
2048 if (r2->start <= r1->end) {
2049 return MAX(r1->start, r2->start);
2050 } else {
2051 r2 = r2->next;
2052 }
2053 } else {
2054 r1 = r1->next;
2055 }
2056 } while (r1 && r2);
2057
2058 return 0xffffffff;
2059 }
2060
2061 /* See "Optimized Interval Splitting in a Linear Scan Register Allocator",
2062 Christian Wimmer VEE'05 (2005), Figure 4. Allocation without spilling */
zend_jit_try_allocate_free_reg(const zend_op_array * op_array,const zend_op ** ssa_opcodes,zend_ssa * ssa,zend_lifetime_interval * current,zend_regset available,zend_regset * hints,zend_lifetime_interval * active,zend_lifetime_interval * inactive,zend_lifetime_interval ** list,zend_lifetime_interval ** free)2063 static int zend_jit_try_allocate_free_reg(const zend_op_array *op_array, const zend_op **ssa_opcodes, zend_ssa *ssa, zend_lifetime_interval *current, zend_regset available, zend_regset *hints, zend_lifetime_interval *active, zend_lifetime_interval *inactive, zend_lifetime_interval **list, zend_lifetime_interval **free)
2064 {
2065 zend_lifetime_interval *it;
2066 uint32_t freeUntilPos[ZREG_NUM];
2067 uint32_t pos, pos2;
2068 zend_reg i, reg, reg2;
2069 zend_reg hint = ZREG_NONE;
2070 zend_regset low_priority_regs;
2071 zend_life_range *range;
2072
2073 if ((ssa->var_info[current->ssa_var].type & MAY_BE_ANY) == MAY_BE_DOUBLE) {
2074 available = ZEND_REGSET_INTERSECTION(available, ZEND_REGSET_FP);
2075 } else {
2076 available = ZEND_REGSET_INTERSECTION(available, ZEND_REGSET_GP);
2077 }
2078
2079 /* TODO: Allow usage of preserved registers ???
2080 * Their values have to be stored in prologue and restored in epilogue
2081 */
2082 available = ZEND_REGSET_DIFFERENCE(available, ZEND_REGSET_PRESERVED);
2083
2084 /* Set freeUntilPos of all physical registers to maxInt */
2085 for (i = 0; i < ZREG_NUM; i++) {
2086 freeUntilPos[i] = 0xffffffff;
2087 }
2088
2089 /* for each interval it in active do */
2090 /* freeUntilPos[it.reg] = 0 */
2091 it = active;
2092 if (ssa->vars[current->ssa_var].definition == current->range.start) {
2093 while (it) {
2094 if (current->range.start != zend_interval_end(it)) {
2095 freeUntilPos[it->reg] = 0;
2096 } else if (zend_jit_may_reuse_reg(
2097 ssa_opcodes ? ssa_opcodes[current->range.start] : op_array->opcodes + current->range.start,
2098 ssa->ops + current->range.start, ssa, current->ssa_var, it->ssa_var)) {
2099 if (!ZEND_REGSET_IN(*hints, it->reg) &&
2100 /* TODO: Avoid most often scratch registers. Find a better way ??? */
2101 (!current->used_as_hint ||
2102 !ZEND_REGSET_IN(ZEND_REGSET_LOW_PRIORITY, it->reg))) {
2103 hint = it->reg;
2104 }
2105 } else {
2106 freeUntilPos[it->reg] = 0;
2107 }
2108 it = it->list_next;
2109 }
2110 } else {
2111 while (it) {
2112 freeUntilPos[it->reg] = 0;
2113 it = it->list_next;
2114 }
2115 }
2116 if (current->hint) {
2117 hint = current->hint->reg;
2118 if (hint != ZREG_NONE && current->hint->used_as_hint == current) {
2119 ZEND_REGSET_EXCL(*hints, hint);
2120 }
2121 }
2122
2123 if (hint == ZREG_NONE && ZEND_REGSET_IS_EMPTY(available)) {
2124 return 0;
2125 }
2126
2127 /* See "Linear Scan Register Allocation on SSA Form", Christian Wimmer and
2128 Michael Franz, CGO'10 (2010), Figure 6. */
2129 if (current->flags & ZREG_SPLIT) {
2130 /* for each interval it in inactive intersecting with current do */
2131 /* freeUntilPos[it.reg] = next intersection of it with current */
2132 it = inactive;
2133 while (it) {
2134 uint32_t next = zend_interval_intersection(current, it);
2135
2136 //ZEND_ASSERT(next != 0xffffffff && !current->split);
2137 if (next < freeUntilPos[it->reg]) {
2138 freeUntilPos[it->reg] = next;
2139 }
2140 it = it->list_next;
2141 }
2142 }
2143
2144 /* Handle Scratch Registers */
2145 /* TODO: Optimize ??? */
2146 range = ¤t->range;
2147 do {
2148 uint32_t line = range->start;
2149 uint32_t last_use_line = (uint32_t)-1;
2150 zend_regset regset;
2151 zend_reg reg;
2152
2153 if ((current->flags & ZREG_LAST_USE) && !range->next) {
2154 last_use_line = range->end;
2155 }
2156 if (ssa->ops[line].op1_def == current->ssa_var ||
2157 ssa->ops[line].op2_def == current->ssa_var ||
2158 ssa->ops[line].result_def == current->ssa_var) {
2159 regset = zend_jit_get_def_scratch_regset(
2160 ssa_opcodes ? ssa_opcodes[line] : op_array->opcodes + line,
2161 ssa->ops + line,
2162 op_array, ssa, current->ssa_var, line == last_use_line);
2163 ZEND_REGSET_FOREACH(regset, reg) {
2164 if (line < freeUntilPos[reg]) {
2165 freeUntilPos[reg] = line;
2166 }
2167 } ZEND_REGSET_FOREACH_END();
2168 line++;
2169 }
2170 while (line <= range->end) {
2171 regset = zend_jit_get_scratch_regset(
2172 ssa_opcodes ? ssa_opcodes[line] : op_array->opcodes + line,
2173 ssa->ops + line,
2174 op_array, ssa, current->ssa_var, line == last_use_line);
2175 ZEND_REGSET_FOREACH(regset, reg) {
2176 if (line < freeUntilPos[reg]) {
2177 freeUntilPos[reg] = line;
2178 }
2179 } ZEND_REGSET_FOREACH_END();
2180 line++;
2181 }
2182 range = range->next;
2183 } while (range);
2184
2185 #if 0
2186 /* Coalescing */
2187 if (ssa->vars[current->ssa_var].definition == current->start) {
2188 zend_op *opline = op_array->opcodes + current->start;
2189 int hint = -1;
2190
2191 switch (opline->opcode) {
2192 case ZEND_ASSIGN:
2193 hint = ssa->ops[current->start].op2_use;
2194 case ZEND_QM_ASSIGN:
2195 hint = ssa->ops[current->start].op1_use;
2196 break;
2197 case ZEND_ADD:
2198 case ZEND_SUB:
2199 case ZEND_MUL:
2200 hint = ssa->ops[current->start].op1_use;
2201 break;
2202 case ZEND_ASSIGN_OP:
2203 if (opline->extended_value == ZEND_ADD
2204 || opline->extended_value == ZEND_SUB
2205 || opline->extended_value == ZEND_MUL) {
2206 hint = ssa->ops[current->start].op1_use;
2207 }
2208 break;
2209 }
2210 if (hint >= 0) {
2211 }
2212 }
2213 #endif
2214
2215 if (hint != ZREG_NONE && freeUntilPos[hint] > zend_interval_end(current)) {
2216 current->reg = hint;
2217 if (current->used_as_hint) {
2218 ZEND_REGSET_INCL(*hints, hint);
2219 }
2220 return 1;
2221 }
2222
2223 if (ZEND_REGSET_IS_EMPTY(available)) {
2224 return 0;
2225 }
2226
2227 pos = 0; reg = ZREG_NONE;
2228 pos2 = 0; reg2 = ZREG_NONE;
2229 low_priority_regs = *hints;
2230 if (current->used_as_hint) {
2231 /* TODO: Avoid most often scratch registers. Find a better way ??? */
2232 low_priority_regs = ZEND_REGSET_UNION(low_priority_regs, ZEND_REGSET_LOW_PRIORITY);
2233 }
2234
2235 ZEND_REGSET_FOREACH(available, i) {
2236 if (ZEND_REGSET_IN(low_priority_regs, i)) {
2237 if (freeUntilPos[i] > pos2) {
2238 reg2 = i;
2239 pos2 = freeUntilPos[i];
2240 }
2241 } else if (freeUntilPos[i] > pos) {
2242 reg = i;
2243 pos = freeUntilPos[i];
2244 }
2245 } ZEND_REGSET_FOREACH_END();
2246
2247 if (reg == ZREG_NONE) {
2248 if (reg2 != ZREG_NONE) {
2249 reg = reg2;
2250 pos = pos2;
2251 reg2 = ZREG_NONE;
2252 }
2253 }
2254
2255 if (reg == ZREG_NONE) {
2256 /* no register available without spilling */
2257 return 0;
2258 } else if (zend_interval_end(current) < pos) {
2259 /* register available for the whole interval */
2260 current->reg = reg;
2261 if (current->used_as_hint) {
2262 ZEND_REGSET_INCL(*hints, reg);
2263 }
2264 return 1;
2265 #if 0
2266 // TODO: allow low priority register usage
2267 } else if (reg2 != ZREG_NONE && zend_interval_end(current) < pos2) {
2268 /* register available for the whole interval */
2269 current->reg = reg2;
2270 if (current->used_as_hint) {
2271 ZEND_REGSET_INCL(*hints, reg2);
2272 }
2273 return 1;
2274 #endif
2275 } else {
2276 /* TODO: enable interval splitting ??? */
2277 /* register available for the first part of the interval */
2278 if (1 || zend_jit_split_interval(current, pos, list, free) != SUCCESS) {
2279 return 0;
2280 }
2281 current->reg = reg;
2282 if (current->used_as_hint) {
2283 ZEND_REGSET_INCL(*hints, reg);
2284 }
2285 return 1;
2286 }
2287 }
2288
2289 /* See "Optimized Interval Splitting in a Linear Scan Register Allocator",
2290 Christian Wimmer VEE'05 (2005), Figure 5. Allocation with spilling.
2291 and "Linear Scan Register Allocation on SSA Form", Christian Wimmer and
2292 Michael Franz, CGO'10 (2010), Figure 6. */
zend_jit_allocate_blocked_reg(void)2293 static int zend_jit_allocate_blocked_reg(void)
2294 {
2295 /* TODO: ??? */
2296 return 0;
2297 }
2298
2299 /* See "Optimized Interval Splitting in a Linear Scan Register Allocator",
2300 Christian Wimmer VEE'10 (2005), Figure 2. */
zend_jit_linear_scan(const zend_op_array * op_array,const zend_op ** ssa_opcodes,zend_ssa * ssa,zend_lifetime_interval * list)2301 static zend_lifetime_interval* zend_jit_linear_scan(const zend_op_array *op_array, const zend_op **ssa_opcodes, zend_ssa *ssa, zend_lifetime_interval *list)
2302 {
2303 zend_lifetime_interval *unhandled, *active, *inactive, *handled, *free;
2304 zend_lifetime_interval *current, **p, *q;
2305 uint32_t position;
2306 zend_regset available = ZEND_REGSET_UNION(ZEND_REGSET_GP, ZEND_REGSET_FP);
2307 zend_regset hints = ZEND_REGSET_EMPTY;
2308
2309 unhandled = list;
2310 /* active = inactive = handled = free = {} */
2311 active = inactive = handled = free = NULL;
2312 while (unhandled != NULL) {
2313 current = unhandled;
2314 unhandled = unhandled->list_next;
2315 position = current->range.start;
2316
2317 p = &active;
2318 while (*p) {
2319 uint32_t end = zend_interval_end(*p);
2320
2321 q = *p;
2322 if (end < position) {
2323 /* move ival from active to handled */
2324 ZEND_REGSET_INCL(available, q->reg);
2325 *p = q->list_next;
2326 q->list_next = handled;
2327 handled = q;
2328 } else if (!zend_interval_covers(q, position)) {
2329 /* move ival from active to inactive */
2330 ZEND_REGSET_INCL(available, q->reg);
2331 *p = q->list_next;
2332 q->list_next = inactive;
2333 inactive = q;
2334 } else {
2335 p = &q->list_next;
2336 }
2337 }
2338
2339 p = &inactive;
2340 while (*p) {
2341 uint32_t end = zend_interval_end(*p);
2342
2343 q = *p;
2344 if (end < position) {
2345 /* move ival from inactive to handled */
2346 *p = q->list_next;
2347 q->list_next = handled;
2348 handled = q;
2349 } else if (zend_interval_covers(q, position)) {
2350 /* move ival from inactive to active */
2351 ZEND_REGSET_EXCL(available, q->reg);
2352 *p = q->list_next;
2353 q->list_next = active;
2354 active = q;
2355 } else {
2356 p = &q->list_next;
2357 }
2358 }
2359
2360 if (zend_jit_try_allocate_free_reg(op_array, ssa_opcodes, ssa, current, available, &hints, active, inactive, &unhandled, &free) ||
2361 zend_jit_allocate_blocked_reg()) {
2362 ZEND_REGSET_EXCL(available, current->reg);
2363 current->list_next = active;
2364 active = current;
2365 } else {
2366 current->list_next = free;
2367 free = current;
2368 }
2369 }
2370
2371 /* move active to handled */
2372 while (active) {
2373 current = active;
2374 active = active->list_next;
2375 current->list_next = handled;
2376 handled = current;
2377 }
2378
2379 /* move inactive to handled */
2380 while (inactive) {
2381 current = inactive;
2382 inactive = inactive->list_next;
2383 current->list_next = handled;
2384 handled = current;
2385 }
2386
2387 return handled;
2388 }
2389
zend_jit_dump_lifetime_interval(const zend_op_array * op_array,const zend_ssa * ssa,const zend_lifetime_interval * ival)2390 static void zend_jit_dump_lifetime_interval(const zend_op_array *op_array, const zend_ssa *ssa, const zend_lifetime_interval *ival)
2391 {
2392 zend_life_range *range;
2393 int var_num = ssa->vars[ival->ssa_var].var;
2394
2395 fprintf(stderr, "#%d.", ival->ssa_var);
2396 zend_dump_var(op_array, (var_num < op_array->last_var ? IS_CV : 0), var_num);
2397 fprintf(stderr, ": %u-%u", ival->range.start, ival->range.end);
2398 range = ival->range.next;
2399 while (range) {
2400 fprintf(stderr, ", %u-%u", range->start, range->end);
2401 range = range->next;
2402 }
2403 if (ival->reg != ZREG_NONE) {
2404 fprintf(stderr, " (%s)", zend_reg_name[ival->reg]);
2405 }
2406 if (ival->flags & ZREG_LAST_USE) {
2407 fprintf(stderr, " last_use");
2408 }
2409 if (ival->flags & ZREG_LOAD) {
2410 fprintf(stderr, " load");
2411 }
2412 if (ival->flags & ZREG_STORE) {
2413 fprintf(stderr, " store");
2414 }
2415 if (ival->hint) {
2416 fprintf(stderr, " hint");
2417 if (ival->hint->ssa_var >= 0) {
2418 var_num = ssa->vars[ival->hint->ssa_var].var;
2419 fprintf(stderr, "=#%d.", ival->hint->ssa_var);
2420 zend_dump_var(op_array, (var_num < op_array->last_var ? IS_CV : 0), var_num);
2421 }
2422 if (ival->hint->reg != ZREG_NONE) {
2423 fprintf(stderr, " (%s)", zend_reg_name[ival->hint->reg]);
2424 }
2425 }
2426 fprintf(stderr, "\n");
2427 }
2428
zend_jit_allocate_registers(const zend_op_array * op_array,zend_ssa * ssa)2429 static zend_lifetime_interval** zend_jit_allocate_registers(const zend_op_array *op_array, zend_ssa *ssa)
2430 {
2431 void *checkpoint;
2432 int set_size, candidates_count, i;
2433 zend_bitset candidates = NULL;
2434 zend_lifetime_interval *list, *ival;
2435 zend_lifetime_interval **intervals;
2436 ALLOCA_FLAG(use_heap);
2437
2438 if (!ssa->var_info) {
2439 return NULL;
2440 }
2441
2442 /* Identify SSA variables suitable for register allocation */
2443 set_size = zend_bitset_len(ssa->vars_count);
2444 candidates = ZEND_BITSET_ALLOCA(set_size, use_heap);
2445 if (!candidates) {
2446 return NULL;
2447 }
2448 candidates_count = 0;
2449 zend_bitset_clear(candidates, set_size);
2450 for (i = 0; i < ssa->vars_count; i++) {
2451 if (zend_jit_may_be_in_reg(op_array, ssa, i)) {
2452 zend_bitset_incl(candidates, i);
2453 candidates_count++;
2454 }
2455 }
2456 if (!candidates_count) {
2457 free_alloca(candidates, use_heap);
2458 return NULL;
2459 }
2460
2461 checkpoint = zend_arena_checkpoint(CG(arena));
2462
2463 /* Find life-time intervals */
2464 if (zend_jit_compute_liveness(op_array, ssa, candidates, &list) != SUCCESS) {
2465 goto failure;
2466 }
2467
2468 if (list) {
2469 /* Set ZREG_LAST_USE flags */
2470 ival = list;
2471 while (ival) {
2472 zend_life_range *range = &ival->range;
2473
2474 while (range->next) {
2475 range = range->next;
2476 }
2477 if (zend_ssa_is_last_use(op_array, ssa, ival->ssa_var, range->end)) {
2478 ival->flags |= ZREG_LAST_USE;
2479 }
2480 ival = ival->list_next;
2481 }
2482 }
2483
2484 if (list) {
2485 if (JIT_G(debug) & ZEND_JIT_DEBUG_REG_ALLOC) {
2486 fprintf(stderr, "Live Ranges \"%s\"\n", op_array->function_name ? ZSTR_VAL(op_array->function_name) : "[main]");
2487 ival = list;
2488 while (ival) {
2489 zend_jit_dump_lifetime_interval(op_array, ssa, ival);
2490 ival = ival->list_next;
2491 }
2492 fprintf(stderr, "\n");
2493 }
2494
2495 /* Linear Scan Register Allocation */
2496 list = zend_jit_linear_scan(op_array, NULL, ssa, list);
2497
2498 if (list) {
2499 intervals = zend_arena_calloc(&CG(arena), ssa->vars_count, sizeof(zend_lifetime_interval*));
2500 if (!intervals) {
2501 goto failure;
2502 }
2503
2504 ival = list;
2505 while (ival != NULL) {
2506 zend_lifetime_interval *next = ival->list_next;
2507
2508 ival->list_next = intervals[ival->ssa_var];
2509 intervals[ival->ssa_var] = ival;
2510 ival = next;
2511 }
2512
2513 if (JIT_G(opt_flags) & ZEND_JIT_REG_ALLOC_GLOBAL) {
2514 /* Naive SSA resolution */
2515 for (i = 0; i < ssa->vars_count; i++) {
2516 if (ssa->vars[i].definition_phi && !ssa->vars[i].no_val) {
2517 zend_ssa_phi *phi = ssa->vars[i].definition_phi;
2518 int k, src;
2519
2520 if (phi->pi >= 0) {
2521 if (!ssa->vars[i].phi_use_chain
2522 || ssa->vars[i].phi_use_chain->block != phi->block) {
2523 src = phi->sources[0];
2524 if (intervals[i]) {
2525 if (!intervals[src]) {
2526 intervals[i]->flags |= ZREG_LOAD;
2527 } else if (intervals[i]->reg != intervals[src]->reg) {
2528 intervals[i]->flags |= ZREG_LOAD;
2529 intervals[src]->flags |= ZREG_STORE;
2530 }
2531 } else if (intervals[src]) {
2532 intervals[src]->flags |= ZREG_STORE;
2533 }
2534 }
2535 } else {
2536 int need_move = 0;
2537
2538 for (k = 0; k < ssa->cfg.blocks[phi->block].predecessors_count; k++) {
2539 src = phi->sources[k];
2540 if (src >= 0) {
2541 if (ssa->vars[src].definition_phi
2542 && ssa->vars[src].definition_phi->pi >= 0
2543 && phi->block == ssa->vars[src].definition_phi->block) {
2544 /* Skip zero-length interval for Pi variable */
2545 src = ssa->vars[src].definition_phi->sources[0];
2546 }
2547 if (intervals[i]) {
2548 if (!intervals[src]) {
2549 need_move = 1;
2550 } else if (intervals[i]->reg != intervals[src]->reg) {
2551 need_move = 1;
2552 }
2553 } else if (intervals[src]) {
2554 need_move = 1;
2555 }
2556 }
2557 }
2558 if (need_move) {
2559 if (intervals[i]) {
2560 intervals[i]->flags |= ZREG_LOAD;
2561 }
2562 for (k = 0; k < ssa->cfg.blocks[phi->block].predecessors_count; k++) {
2563 src = phi->sources[k];
2564 if (src >= 0) {
2565 if (ssa->vars[src].definition_phi
2566 && ssa->vars[src].definition_phi->pi >= 0
2567 && phi->block == ssa->vars[src].definition_phi->block) {
2568 /* Skip zero-length interval for Pi variable */
2569 src = ssa->vars[src].definition_phi->sources[0];
2570 }
2571 if (intervals[src]) {
2572 intervals[src]->flags |= ZREG_STORE;
2573 }
2574 }
2575 }
2576 }
2577 }
2578 }
2579 }
2580 /* Remove useless register allocation */
2581 for (i = 0; i < ssa->vars_count; i++) {
2582 if (intervals[i] &&
2583 ((intervals[i]->flags & ZREG_LOAD) ||
2584 ((intervals[i]->flags & ZREG_STORE) && ssa->vars[i].definition >= 0)) &&
2585 ssa->vars[i].use_chain < 0) {
2586 bool may_remove = 1;
2587 zend_ssa_phi *phi = ssa->vars[i].phi_use_chain;
2588
2589 while (phi) {
2590 if (intervals[phi->ssa_var] &&
2591 !(intervals[phi->ssa_var]->flags & ZREG_LOAD)) {
2592 may_remove = 0;
2593 break;
2594 }
2595 phi = zend_ssa_next_use_phi(ssa, i, phi);
2596 }
2597 if (may_remove) {
2598 intervals[i] = NULL;
2599 }
2600 }
2601 }
2602 /* Remove intervals used once */
2603 for (i = 0; i < ssa->vars_count; i++) {
2604 if (intervals[i] &&
2605 (intervals[i]->flags & ZREG_LOAD) &&
2606 (intervals[i]->flags & ZREG_STORE) &&
2607 (ssa->vars[i].use_chain < 0 ||
2608 zend_ssa_next_use(ssa->ops, i, ssa->vars[i].use_chain) < 0)) {
2609 bool may_remove = 1;
2610 zend_ssa_phi *phi = ssa->vars[i].phi_use_chain;
2611
2612 while (phi) {
2613 if (intervals[phi->ssa_var] &&
2614 !(intervals[phi->ssa_var]->flags & ZREG_LOAD)) {
2615 may_remove = 0;
2616 break;
2617 }
2618 phi = zend_ssa_next_use_phi(ssa, i, phi);
2619 }
2620 if (may_remove) {
2621 intervals[i] = NULL;
2622 }
2623 }
2624 }
2625 }
2626
2627 if (JIT_G(debug) & ZEND_JIT_DEBUG_REG_ALLOC) {
2628 fprintf(stderr, "Allocated Live Ranges \"%s\"\n", op_array->function_name ? ZSTR_VAL(op_array->function_name) : "[main]");
2629 for (i = 0; i < ssa->vars_count; i++) {
2630 ival = intervals[i];
2631 while (ival) {
2632 zend_jit_dump_lifetime_interval(op_array, ssa, ival);
2633 ival = ival->list_next;
2634 }
2635 }
2636 fprintf(stderr, "\n");
2637 }
2638
2639 free_alloca(candidates, use_heap);
2640 return intervals;
2641 }
2642 }
2643
2644 failure:
2645 zend_arena_release(&CG(arena), checkpoint);
2646 free_alloca(candidates, use_heap);
2647 return NULL;
2648 }
2649
zend_jit_next_is_send_result(const zend_op * opline)2650 static bool zend_jit_next_is_send_result(const zend_op *opline)
2651 {
2652 if (opline->result_type == IS_TMP_VAR
2653 && (opline+1)->opcode == ZEND_SEND_VAL
2654 && (opline+1)->op1_type == IS_TMP_VAR
2655 && (opline+1)->op2_type != IS_CONST
2656 && (opline+1)->op1.var == opline->result.var) {
2657 return 1;
2658 }
2659 return 0;
2660 }
2661
zend_jit_supported_binary_op(zend_uchar op,uint32_t op1_info,uint32_t op2_info)2662 static bool zend_jit_supported_binary_op(zend_uchar op, uint32_t op1_info, uint32_t op2_info)
2663 {
2664 if ((op1_info & MAY_BE_UNDEF) || (op2_info & MAY_BE_UNDEF)) {
2665 return false;
2666 }
2667 switch (op) {
2668 case ZEND_POW:
2669 case ZEND_DIV:
2670 // TODO: check for division by zero ???
2671 return false;
2672 case ZEND_ADD:
2673 case ZEND_SUB:
2674 case ZEND_MUL:
2675 return (op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE))
2676 && (op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE));
2677 case ZEND_BW_OR:
2678 case ZEND_BW_AND:
2679 case ZEND_BW_XOR:
2680 case ZEND_SL:
2681 case ZEND_SR:
2682 case ZEND_MOD:
2683 return (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG);
2684 case ZEND_CONCAT:
2685 return (op1_info & MAY_BE_STRING) && (op2_info & MAY_BE_STRING);
2686 EMPTY_SWITCH_DEFAULT_CASE()
2687 }
2688 }
2689
zend_jit(const zend_op_array * op_array,zend_ssa * ssa,const zend_op * rt_opline)2690 static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op *rt_opline)
2691 {
2692 int b, i, end;
2693 zend_op *opline;
2694 dasm_State* dasm_state = NULL;
2695 void *handler;
2696 int call_level = 0;
2697 void *checkpoint = NULL;
2698 zend_lifetime_interval **ra = NULL;
2699 bool is_terminated = 1; /* previous basic block is terminated by jump */
2700 bool recv_emitted = 0; /* emitted at least one RECV opcode */
2701 zend_uchar smart_branch_opcode;
2702 uint32_t target_label, target_label2;
2703 uint32_t op1_info, op1_def_info, op2_info, res_info, res_use_info;
2704 zend_jit_addr op1_addr, op1_def_addr, op2_addr, op2_def_addr, res_addr;
2705 zend_class_entry *ce;
2706 bool ce_is_instanceof;
2707 bool on_this;
2708
2709 if (JIT_G(bisect_limit)) {
2710 jit_bisect_pos++;
2711 if (jit_bisect_pos >= JIT_G(bisect_limit)) {
2712 if (jit_bisect_pos == JIT_G(bisect_limit)) {
2713 fprintf(stderr, "Not JITing %s%s%s in %s:%d and after due to jit_bisect_limit\n",
2714 op_array->scope ? ZSTR_VAL(op_array->scope->name) : "",
2715 op_array->scope ? "::" : "",
2716 op_array->function_name ? ZSTR_VAL(op_array->function_name) : "{main}",
2717 ZSTR_VAL(op_array->filename), op_array->line_start);
2718 }
2719 return FAILURE;
2720 }
2721 }
2722
2723 if (JIT_G(opt_flags) & (ZEND_JIT_REG_ALLOC_LOCAL|ZEND_JIT_REG_ALLOC_GLOBAL)) {
2724 checkpoint = zend_arena_checkpoint(CG(arena));
2725 ra = zend_jit_allocate_registers(op_array, ssa);
2726 }
2727
2728 /* mark hidden branch targets */
2729 for (b = 0; b < ssa->cfg.blocks_count; b++) {
2730 if (ssa->cfg.blocks[b].flags & ZEND_BB_REACHABLE &&
2731 ssa->cfg.blocks[b].len > 1) {
2732
2733 opline = op_array->opcodes + ssa->cfg.blocks[b].start + ssa->cfg.blocks[b].len - 1;
2734 if (opline->opcode == ZEND_DO_FCALL &&
2735 (opline-1)->opcode == ZEND_NEW) {
2736 ssa->cfg.blocks[ssa->cfg.blocks[b].successors[0]].flags |= ZEND_BB_TARGET;
2737 }
2738 }
2739 }
2740
2741 dasm_init(&dasm_state, DASM_MAXSECTION);
2742 dasm_setupglobal(&dasm_state, dasm_labels, zend_lb_MAX);
2743 dasm_setup(&dasm_state, dasm_actions);
2744
2745 dasm_growpc(&dasm_state, ssa->cfg.blocks_count * 2 + 1);
2746
2747 zend_jit_align_func(&dasm_state);
2748 for (b = 0; b < ssa->cfg.blocks_count; b++) {
2749 if ((ssa->cfg.blocks[b].flags & ZEND_BB_REACHABLE) == 0) {
2750 continue;
2751 }
2752 //#ifndef CONTEXT_THREADED_JIT
2753 if (ssa->cfg.blocks[b].flags & ZEND_BB_ENTRY) {
2754 if (ssa->cfg.blocks[b].flags & ZEND_BB_TARGET) {
2755 /* pass */
2756 } else if (JIT_G(opt_level) < ZEND_JIT_LEVEL_INLINE &&
2757 ssa->cfg.blocks[b].len == 1 &&
2758 (ssa->cfg.blocks[b].flags & ZEND_BB_EXIT) &&
2759 op_array->opcodes[ssa->cfg.blocks[b].start].opcode != ZEND_JMP) {
2760 /* don't generate code for BB with single opcode */
2761 continue;
2762 }
2763 if (ssa->cfg.blocks[b].flags & ZEND_BB_FOLLOW) {
2764 if (!is_terminated) {
2765 zend_jit_jmp(&dasm_state, b);
2766 }
2767 }
2768 zend_jit_label(&dasm_state, ssa->cfg.blocks_count + b);
2769 zend_jit_prologue(&dasm_state);
2770 } else
2771 //#endif
2772 if (ssa->cfg.blocks[b].flags & (ZEND_BB_START|ZEND_BB_RECV_ENTRY)) {
2773 opline = op_array->opcodes + ssa->cfg.blocks[b].start;
2774 if (ssa->cfg.flags & ZEND_CFG_RECV_ENTRY) {
2775 if (opline->opcode == ZEND_RECV_INIT) {
2776 if (opline == op_array->opcodes ||
2777 (opline-1)->opcode != ZEND_RECV_INIT) {
2778 if (recv_emitted) {
2779 zend_jit_jmp(&dasm_state, b);
2780 }
2781 zend_jit_label(&dasm_state, ssa->cfg.blocks_count + b);
2782 for (i = 1; (opline+i)->opcode == ZEND_RECV_INIT; i++) {
2783 zend_jit_label(&dasm_state, ssa->cfg.blocks_count + b + i);
2784 }
2785 zend_jit_prologue(&dasm_state);
2786 }
2787 recv_emitted = 1;
2788 } else if (opline->opcode == ZEND_RECV) {
2789 if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
2790 /* skip */
2791 continue;
2792 } else if (recv_emitted) {
2793 zend_jit_jmp(&dasm_state, b);
2794 zend_jit_label(&dasm_state, ssa->cfg.blocks_count + b);
2795 zend_jit_prologue(&dasm_state);
2796 } else {
2797 zend_arg_info *arg_info;
2798
2799 if (opline->op1.num <= op_array->num_args) {
2800 arg_info = &op_array->arg_info[opline->op1.num - 1];
2801 } else if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
2802 arg_info = &op_array->arg_info[op_array->num_args];
2803 } else {
2804 /* skip */
2805 continue;
2806 }
2807 if (!ZEND_TYPE_IS_SET(arg_info->type)) {
2808 /* skip */
2809 continue;
2810 }
2811 zend_jit_label(&dasm_state, ssa->cfg.blocks_count + b);
2812 zend_jit_prologue(&dasm_state);
2813 recv_emitted = 1;
2814 }
2815 } else {
2816 if (recv_emitted) {
2817 zend_jit_jmp(&dasm_state, b);
2818 } else if (JIT_G(opt_level) < ZEND_JIT_LEVEL_INLINE &&
2819 ssa->cfg.blocks[b].len == 1 &&
2820 (ssa->cfg.blocks[b].flags & ZEND_BB_EXIT)) {
2821 /* don't generate code for BB with single opcode */
2822 dasm_free(&dasm_state);
2823
2824 if (JIT_G(opt_flags) & (ZEND_JIT_REG_ALLOC_LOCAL|ZEND_JIT_REG_ALLOC_GLOBAL)) {
2825 zend_arena_release(&CG(arena), checkpoint);
2826 }
2827 return SUCCESS;
2828 }
2829 zend_jit_label(&dasm_state, ssa->cfg.blocks_count + b);
2830 zend_jit_prologue(&dasm_state);
2831 recv_emitted = 1;
2832 }
2833 } else if (JIT_G(opt_level) < ZEND_JIT_LEVEL_INLINE &&
2834 ssa->cfg.blocks[b].len == 1 &&
2835 (ssa->cfg.blocks[b].flags & ZEND_BB_EXIT)) {
2836 /* don't generate code for BB with single opcode */
2837 dasm_free(&dasm_state);
2838
2839 if (JIT_G(opt_flags) & (ZEND_JIT_REG_ALLOC_LOCAL|ZEND_JIT_REG_ALLOC_GLOBAL)) {
2840 zend_arena_release(&CG(arena), checkpoint);
2841 }
2842 return SUCCESS;
2843 } else {
2844 zend_jit_label(&dasm_state, ssa->cfg.blocks_count + b);
2845 zend_jit_prologue(&dasm_state);
2846 }
2847 }
2848
2849 is_terminated = 0;
2850
2851 zend_jit_label(&dasm_state, b);
2852 if (JIT_G(opt_level) < ZEND_JIT_LEVEL_INLINE) {
2853 if ((ssa->cfg.blocks[b].flags & ZEND_BB_FOLLOW)
2854 && ssa->cfg.blocks[b].start != 0
2855 && (op_array->opcodes[ssa->cfg.blocks[b].start - 1].opcode == ZEND_NOP
2856 || op_array->opcodes[ssa->cfg.blocks[b].start - 1].opcode == ZEND_SWITCH_LONG
2857 || op_array->opcodes[ssa->cfg.blocks[b].start - 1].opcode == ZEND_SWITCH_STRING
2858 || op_array->opcodes[ssa->cfg.blocks[b].start - 1].opcode == ZEND_MATCH)) {
2859 zend_jit_reset_last_valid_opline();
2860 if (!zend_jit_set_ip(&dasm_state, op_array->opcodes + ssa->cfg.blocks[b].start)) {
2861 goto jit_failure;
2862 }
2863 } else {
2864 zend_jit_set_last_valid_opline(op_array->opcodes + ssa->cfg.blocks[b].start);
2865 }
2866 } else if (ssa->cfg.blocks[b].flags & ZEND_BB_TARGET) {
2867 zend_jit_reset_last_valid_opline();
2868 } else if (ssa->cfg.blocks[b].flags & (ZEND_BB_START|ZEND_BB_RECV_ENTRY|ZEND_BB_ENTRY)) {
2869 zend_jit_set_last_valid_opline(op_array->opcodes + ssa->cfg.blocks[b].start);
2870 }
2871 if (ssa->cfg.blocks[b].flags & ZEND_BB_LOOP_HEADER) {
2872 if (!zend_jit_check_timeout(&dasm_state, op_array->opcodes + ssa->cfg.blocks[b].start, NULL)) {
2873 goto jit_failure;
2874 }
2875 }
2876 if (!ssa->cfg.blocks[b].len) {
2877 continue;
2878 }
2879 if ((JIT_G(opt_flags) & ZEND_JIT_REG_ALLOC_GLOBAL) && ra) {
2880 zend_ssa_phi *phi = ssa->blocks[b].phis;
2881
2882 while (phi) {
2883 zend_lifetime_interval *ival = ra[phi->ssa_var];
2884
2885 if (ival) {
2886 if (ival->flags & ZREG_LOAD) {
2887 ZEND_ASSERT(ival->reg != ZREG_NONE);
2888
2889 if (!zend_jit_load_var(&dasm_state, ssa->var_info[phi->ssa_var].type, ssa->vars[phi->ssa_var].var, ival->reg)) {
2890 goto jit_failure;
2891 }
2892 } else if (ival->flags & ZREG_STORE) {
2893 ZEND_ASSERT(ival->reg != ZREG_NONE);
2894
2895 if (!zend_jit_store_var(&dasm_state, ssa->var_info[phi->ssa_var].type, ssa->vars[phi->ssa_var].var, ival->reg, 1)) {
2896 goto jit_failure;
2897 }
2898 }
2899 }
2900 phi = phi->next;
2901 }
2902 }
2903 end = ssa->cfg.blocks[b].start + ssa->cfg.blocks[b].len - 1;
2904 for (i = ssa->cfg.blocks[b].start; i <= end; i++) {
2905 zend_ssa_op *ssa_op = ssa->ops ? &ssa->ops[i] : NULL;
2906 opline = op_array->opcodes + i;
2907 switch (opline->opcode) {
2908 case ZEND_INIT_FCALL:
2909 case ZEND_INIT_FCALL_BY_NAME:
2910 case ZEND_INIT_NS_FCALL_BY_NAME:
2911 case ZEND_INIT_METHOD_CALL:
2912 case ZEND_INIT_DYNAMIC_CALL:
2913 case ZEND_INIT_STATIC_METHOD_CALL:
2914 case ZEND_INIT_USER_CALL:
2915 case ZEND_NEW:
2916 call_level++;
2917 }
2918
2919 if (JIT_G(opt_level) >= ZEND_JIT_LEVEL_INLINE) {
2920 switch (opline->opcode) {
2921 case ZEND_PRE_INC:
2922 case ZEND_PRE_DEC:
2923 case ZEND_POST_INC:
2924 case ZEND_POST_DEC:
2925 if (opline->op1_type != IS_CV) {
2926 break;
2927 }
2928 op1_info = OP1_INFO();
2929 if (!(op1_info & MAY_BE_LONG)) {
2930 break;
2931 }
2932 if (opline->result_type != IS_UNUSED) {
2933 res_use_info = -1;
2934
2935 if (opline->result_type == IS_CV
2936 && ssa->vars
2937 && ssa_op->result_use >= 0
2938 && !ssa->vars[ssa_op->result_use].no_val) {
2939 zend_jit_addr res_use_addr = RES_USE_REG_ADDR();
2940
2941 if (Z_MODE(res_use_addr) != IS_REG
2942 || Z_LOAD(res_use_addr)
2943 || Z_STORE(res_use_addr)) {
2944 res_use_info = RES_USE_INFO();
2945 }
2946 }
2947 res_info = RES_INFO();
2948 res_addr = RES_REG_ADDR();
2949 } else {
2950 res_use_info = -1;
2951 res_info = -1;
2952 res_addr = 0;
2953 }
2954 op1_def_info = OP1_DEF_INFO();
2955 if (!zend_jit_inc_dec(&dasm_state, opline,
2956 op1_info, OP1_REG_ADDR(),
2957 op1_def_info, OP1_DEF_REG_ADDR(),
2958 res_use_info, res_info,
2959 res_addr,
2960 (op1_info & MAY_BE_LONG) && (op1_def_info & MAY_BE_DOUBLE) && zend_may_overflow(opline, ssa_op, op_array, ssa),
2961 zend_may_throw(opline, ssa_op, op_array, ssa))) {
2962 goto jit_failure;
2963 }
2964 goto done;
2965 case ZEND_BW_OR:
2966 case ZEND_BW_AND:
2967 case ZEND_BW_XOR:
2968 case ZEND_SL:
2969 case ZEND_SR:
2970 case ZEND_MOD:
2971 if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
2972 break;
2973 }
2974 op1_info = OP1_INFO();
2975 op2_info = OP2_INFO();
2976 if ((op1_info & MAY_BE_UNDEF) || (op2_info & MAY_BE_UNDEF)) {
2977 break;
2978 }
2979 if (!(op1_info & MAY_BE_LONG)
2980 || !(op2_info & MAY_BE_LONG)) {
2981 break;
2982 }
2983 res_addr = RES_REG_ADDR();
2984 if (Z_MODE(res_addr) != IS_REG
2985 && (i + 1) <= end
2986 && zend_jit_next_is_send_result(opline)) {
2987 i++;
2988 res_use_info = -1;
2989 res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, (opline+1)->result.var);
2990 if (!zend_jit_reuse_ip(&dasm_state)) {
2991 goto jit_failure;
2992 }
2993 } else {
2994 res_use_info = -1;
2995
2996 if (opline->result_type == IS_CV
2997 && ssa->vars
2998 && ssa_op->result_use >= 0
2999 && !ssa->vars[ssa_op->result_use].no_val) {
3000 zend_jit_addr res_use_addr = RES_USE_REG_ADDR();
3001
3002 if (Z_MODE(res_use_addr) != IS_REG
3003 || Z_LOAD(res_use_addr)
3004 || Z_STORE(res_use_addr)) {
3005 res_use_info = RES_USE_INFO();
3006 }
3007 }
3008 }
3009 if (!zend_jit_long_math(&dasm_state, opline,
3010 op1_info, OP1_RANGE(), OP1_REG_ADDR(),
3011 op2_info, OP2_RANGE(), OP2_REG_ADDR(),
3012 res_use_info, RES_INFO(), res_addr,
3013 zend_may_throw(opline, ssa_op, op_array, ssa))) {
3014 goto jit_failure;
3015 }
3016 goto done;
3017 case ZEND_ADD:
3018 case ZEND_SUB:
3019 case ZEND_MUL:
3020 // case ZEND_DIV: // TODO: check for division by zero ???
3021 if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
3022 break;
3023 }
3024 op1_info = OP1_INFO();
3025 op2_info = OP2_INFO();
3026 if ((op1_info & MAY_BE_UNDEF) || (op2_info & MAY_BE_UNDEF)) {
3027 break;
3028 }
3029 if (opline->opcode == ZEND_ADD &&
3030 (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY &&
3031 (op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) {
3032 /* pass */
3033 } else if (!(op1_info & (MAY_BE_LONG|MAY_BE_DOUBLE)) ||
3034 !(op2_info & (MAY_BE_LONG|MAY_BE_DOUBLE))) {
3035 break;
3036 }
3037 res_addr = RES_REG_ADDR();
3038 if (Z_MODE(res_addr) != IS_REG
3039 && (i + 1) <= end
3040 && zend_jit_next_is_send_result(opline)) {
3041 i++;
3042 res_use_info = -1;
3043 res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, (opline+1)->result.var);
3044 if (!zend_jit_reuse_ip(&dasm_state)) {
3045 goto jit_failure;
3046 }
3047 } else {
3048 res_use_info = -1;
3049
3050 if (opline->result_type == IS_CV
3051 && ssa->vars
3052 && ssa_op->result_use >= 0
3053 && !ssa->vars[ssa_op->result_use].no_val) {
3054 zend_jit_addr res_use_addr = RES_USE_REG_ADDR();
3055
3056 if (Z_MODE(res_use_addr) != IS_REG
3057 || Z_LOAD(res_use_addr)
3058 || Z_STORE(res_use_addr)) {
3059 res_use_info = RES_USE_INFO();
3060 }
3061 }
3062 }
3063 res_info = RES_INFO();
3064 if (opline->opcode == ZEND_ADD &&
3065 (op1_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY &&
3066 (op2_info & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY) {
3067 if (!zend_jit_add_arrays(&dasm_state, opline, op1_info, OP1_REG_ADDR(), op2_info, OP2_REG_ADDR(), res_addr)) {
3068 goto jit_failure;
3069 }
3070 } else {
3071 if (!zend_jit_math(&dasm_state, opline,
3072 op1_info, OP1_REG_ADDR(),
3073 op2_info, OP2_REG_ADDR(),
3074 res_use_info, res_info, res_addr,
3075 (op1_info & MAY_BE_LONG) && (op2_info & MAY_BE_LONG) && (res_info & MAY_BE_DOUBLE) && zend_may_overflow(opline, ssa_op, op_array, ssa),
3076 zend_may_throw(opline, ssa_op, op_array, ssa))) {
3077 goto jit_failure;
3078 }
3079 }
3080 goto done;
3081 case ZEND_CONCAT:
3082 case ZEND_FAST_CONCAT:
3083 if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
3084 break;
3085 }
3086 op1_info = OP1_INFO();
3087 op2_info = OP2_INFO();
3088 if ((op1_info & MAY_BE_UNDEF) || (op2_info & MAY_BE_UNDEF)) {
3089 break;
3090 }
3091 if (!(op1_info & MAY_BE_STRING) ||
3092 !(op2_info & MAY_BE_STRING)) {
3093 break;
3094 }
3095 res_addr = RES_REG_ADDR();
3096 if ((i + 1) <= end
3097 && zend_jit_next_is_send_result(opline)) {
3098 i++;
3099 res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, (opline+1)->result.var);
3100 if (!zend_jit_reuse_ip(&dasm_state)) {
3101 goto jit_failure;
3102 }
3103 }
3104 if (!zend_jit_concat(&dasm_state, opline,
3105 op1_info, op2_info, res_addr,
3106 zend_may_throw(opline, ssa_op, op_array, ssa))) {
3107 goto jit_failure;
3108 }
3109 goto done;
3110 case ZEND_ASSIGN_OP:
3111 if (opline->op1_type != IS_CV || opline->result_type != IS_UNUSED) {
3112 break;
3113 }
3114 if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
3115 break;
3116 }
3117 op1_info = OP1_INFO();
3118 op2_info = OP2_INFO();
3119 if (!zend_jit_supported_binary_op(
3120 opline->extended_value, op1_info, op2_info)) {
3121 break;
3122 }
3123 op1_def_info = OP1_DEF_INFO();
3124 if (!zend_jit_assign_op(&dasm_state, opline,
3125 op1_info, op1_def_info, OP1_RANGE(),
3126 op2_info, OP2_RANGE(),
3127 (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),
3128 zend_may_throw(opline, ssa_op, op_array, ssa))) {
3129 goto jit_failure;
3130 }
3131 goto done;
3132 case ZEND_ASSIGN_DIM_OP:
3133 if (opline->op1_type != IS_CV || opline->result_type != IS_UNUSED) {
3134 break;
3135 }
3136 if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
3137 break;
3138 }
3139 if (!zend_jit_supported_binary_op(
3140 opline->extended_value, MAY_BE_ANY, OP1_DATA_INFO())) {
3141 break;
3142 }
3143 if (!zend_jit_assign_dim_op(&dasm_state, opline,
3144 OP1_INFO(), OP1_DEF_INFO(), OP1_REG_ADDR(), OP2_INFO(),
3145 OP1_DATA_INFO(), OP1_DATA_RANGE(), IS_UNKNOWN,
3146 zend_may_throw(opline, ssa_op, op_array, ssa))) {
3147 goto jit_failure;
3148 }
3149 goto done;
3150 case ZEND_ASSIGN_DIM:
3151 if (opline->op1_type != IS_CV) {
3152 break;
3153 }
3154 if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
3155 break;
3156 }
3157 if (!zend_jit_assign_dim(&dasm_state, opline,
3158 OP1_INFO(), OP1_REG_ADDR(), OP2_INFO(), OP1_DATA_INFO(), IS_UNKNOWN,
3159 zend_may_throw(opline, ssa_op, op_array, ssa))) {
3160 goto jit_failure;
3161 }
3162 goto done;
3163 case ZEND_PRE_INC_OBJ:
3164 case ZEND_PRE_DEC_OBJ:
3165 case ZEND_POST_INC_OBJ:
3166 case ZEND_POST_DEC_OBJ:
3167 if (opline->op2_type != IS_CONST
3168 || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING
3169 || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') {
3170 break;
3171 }
3172 if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
3173 break;
3174 }
3175 ce = NULL;
3176 ce_is_instanceof = 0;
3177 on_this = 0;
3178 if (opline->op1_type == IS_UNUSED) {
3179 op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN;
3180 ce = op_array->scope;
3181 ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0;
3182 op1_addr = 0;
3183 on_this = 1;
3184 } else {
3185 op1_info = OP1_INFO();
3186 if (!(op1_info & MAY_BE_OBJECT)) {
3187 break;
3188 }
3189 op1_addr = OP1_REG_ADDR();
3190 if (ssa->var_info && ssa->ops) {
3191 zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
3192 if (ssa_op->op1_use >= 0) {
3193 zend_ssa_var_info *op1_ssa = ssa->var_info + ssa_op->op1_use;
3194 if (op1_ssa->ce && !op1_ssa->ce->create_object) {
3195 ce = op1_ssa->ce;
3196 ce_is_instanceof = op1_ssa->is_instanceof;
3197 }
3198 }
3199 }
3200 }
3201 if (!zend_jit_incdec_obj(&dasm_state, opline, op_array, ssa, ssa_op,
3202 op1_info, op1_addr,
3203 0, ce, ce_is_instanceof, on_this, 0, NULL, IS_UNKNOWN)) {
3204 goto jit_failure;
3205 }
3206 goto done;
3207 case ZEND_ASSIGN_OBJ_OP:
3208 if (opline->result_type != IS_UNUSED) {
3209 break;
3210 }
3211 if (opline->op2_type != IS_CONST
3212 || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING
3213 || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') {
3214 break;
3215 }
3216 if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
3217 break;
3218 }
3219 if (!zend_jit_supported_binary_op(
3220 opline->extended_value, MAY_BE_ANY, OP1_DATA_INFO())) {
3221 break;
3222 }
3223 ce = NULL;
3224 ce_is_instanceof = 0;
3225 on_this = 0;
3226 if (opline->op1_type == IS_UNUSED) {
3227 op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN;
3228 ce = op_array->scope;
3229 ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0;
3230 op1_addr = 0;
3231 on_this = 1;
3232 } else {
3233 op1_info = OP1_INFO();
3234 if (!(op1_info & MAY_BE_OBJECT)) {
3235 break;
3236 }
3237 op1_addr = OP1_REG_ADDR();
3238 if (ssa->var_info && ssa->ops) {
3239 zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
3240 if (ssa_op->op1_use >= 0) {
3241 zend_ssa_var_info *op1_ssa = ssa->var_info + ssa_op->op1_use;
3242 if (op1_ssa->ce && !op1_ssa->ce->create_object) {
3243 ce = op1_ssa->ce;
3244 ce_is_instanceof = op1_ssa->is_instanceof;
3245 }
3246 }
3247 }
3248 }
3249 if (!zend_jit_assign_obj_op(&dasm_state, opline, op_array, ssa, ssa_op,
3250 op1_info, op1_addr, OP1_DATA_INFO(), OP1_DATA_RANGE(),
3251 0, ce, ce_is_instanceof, on_this, 0, NULL, IS_UNKNOWN)) {
3252 goto jit_failure;
3253 }
3254 goto done;
3255 case ZEND_ASSIGN_OBJ:
3256 if (opline->op2_type != IS_CONST
3257 || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING
3258 || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') {
3259 break;
3260 }
3261 if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
3262 break;
3263 }
3264 ce = NULL;
3265 ce_is_instanceof = 0;
3266 on_this = 0;
3267 if (opline->op1_type == IS_UNUSED) {
3268 op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN;
3269 ce = op_array->scope;
3270 ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0;
3271 op1_addr = 0;
3272 on_this = 1;
3273 } else {
3274 op1_info = OP1_INFO();
3275 if (!(op1_info & MAY_BE_OBJECT)) {
3276 break;
3277 }
3278 op1_addr = OP1_REG_ADDR();
3279 if (ssa->var_info && ssa->ops) {
3280 zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
3281 if (ssa_op->op1_use >= 0) {
3282 zend_ssa_var_info *op1_ssa = ssa->var_info + ssa_op->op1_use;
3283 if (op1_ssa->ce && !op1_ssa->ce->create_object) {
3284 ce = op1_ssa->ce;
3285 ce_is_instanceof = op1_ssa->is_instanceof;
3286 }
3287 }
3288 }
3289 }
3290 if (!zend_jit_assign_obj(&dasm_state, opline, op_array, ssa, ssa_op,
3291 op1_info, op1_addr, OP1_DATA_INFO(),
3292 0, ce, ce_is_instanceof, on_this, 0, NULL, IS_UNKNOWN,
3293 zend_may_throw(opline, ssa_op, op_array, ssa))) {
3294 goto jit_failure;
3295 }
3296 goto done;
3297 case ZEND_ASSIGN:
3298 if (opline->op1_type != IS_CV) {
3299 break;
3300 }
3301 if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
3302 break;
3303 }
3304 op2_addr = OP2_REG_ADDR();
3305 if (ra
3306 && ssa->ops[opline - op_array->opcodes].op2_def >= 0
3307 && !ssa->vars[ssa->ops[opline - op_array->opcodes].op2_def].no_val) {
3308 op2_def_addr = OP2_DEF_REG_ADDR();
3309 } else {
3310 op2_def_addr = op2_addr;
3311 }
3312 op1_info = OP1_INFO();
3313 if (ra && ssa->vars[ssa_op->op1_use].no_val) {
3314 op1_info |= MAY_BE_UNDEF; // requres type assignment
3315 }
3316 if (opline->result_type == IS_UNUSED) {
3317 res_addr = 0;
3318 res_info = -1;
3319 } else {
3320 res_addr = RES_REG_ADDR();
3321 res_info = RES_INFO();
3322 if (Z_MODE(res_addr) != IS_REG
3323 && (i + 1) <= end
3324 && zend_jit_next_is_send_result(opline)
3325 && (!(op1_info & MAY_HAVE_DTOR) || !(op1_info & MAY_BE_RC1))) {
3326 i++;
3327 res_addr = ZEND_ADDR_MEM_ZVAL(ZREG_RX, (opline+1)->result.var);
3328 if (!zend_jit_reuse_ip(&dasm_state)) {
3329 goto jit_failure;
3330 }
3331 }
3332 }
3333 if (!zend_jit_assign(&dasm_state, opline,
3334 op1_info, OP1_REG_ADDR(),
3335 OP1_DEF_INFO(), OP1_DEF_REG_ADDR(),
3336 OP2_INFO(), op2_addr, op2_def_addr,
3337 res_info, res_addr,
3338 zend_may_throw(opline, ssa_op, op_array, ssa))) {
3339 goto jit_failure;
3340 }
3341 goto done;
3342 case ZEND_QM_ASSIGN:
3343 op1_addr = OP1_REG_ADDR();
3344 if (ra
3345 && ssa->ops[opline - op_array->opcodes].op1_def >= 0
3346 && !ssa->vars[ssa->ops[opline - op_array->opcodes].op1_def].no_val) {
3347 op1_def_addr = OP1_DEF_REG_ADDR();
3348 } else {
3349 op1_def_addr = op1_addr;
3350 }
3351 if (!zend_jit_qm_assign(&dasm_state, opline,
3352 OP1_INFO(), op1_addr, op1_def_addr,
3353 -1, RES_INFO(), RES_REG_ADDR())) {
3354 goto jit_failure;
3355 }
3356 goto done;
3357 case ZEND_INIT_FCALL:
3358 case ZEND_INIT_FCALL_BY_NAME:
3359 case ZEND_INIT_NS_FCALL_BY_NAME:
3360 if (!zend_jit_init_fcall(&dasm_state, opline, b, op_array, ssa, ssa_op, call_level, NULL, 0)) {
3361 goto jit_failure;
3362 }
3363 goto done;
3364 case ZEND_SEND_VAL:
3365 case ZEND_SEND_VAL_EX:
3366 if (opline->op2_type == IS_CONST) {
3367 /* Named parameters not supported in JIT (yet) */
3368 break;
3369 }
3370 if (opline->opcode == ZEND_SEND_VAL_EX
3371 && opline->op2.num > MAX_ARG_FLAG_NUM) {
3372 break;
3373 }
3374 if (!zend_jit_send_val(&dasm_state, opline,
3375 OP1_INFO(), OP1_REG_ADDR())) {
3376 goto jit_failure;
3377 }
3378 goto done;
3379 case ZEND_SEND_REF:
3380 if (opline->op2_type == IS_CONST) {
3381 /* Named parameters not supported in JIT (yet) */
3382 break;
3383 }
3384 if (!zend_jit_send_ref(&dasm_state, opline, op_array,
3385 OP1_INFO(), 0)) {
3386 goto jit_failure;
3387 }
3388 goto done;
3389 case ZEND_SEND_VAR:
3390 case ZEND_SEND_VAR_EX:
3391 case ZEND_SEND_VAR_NO_REF:
3392 case ZEND_SEND_VAR_NO_REF_EX:
3393 case ZEND_SEND_FUNC_ARG:
3394 if (opline->op2_type == IS_CONST) {
3395 /* Named parameters not supported in JIT (yet) */
3396 break;
3397 }
3398 if ((opline->opcode == ZEND_SEND_VAR_EX
3399 || opline->opcode == ZEND_SEND_VAR_NO_REF_EX)
3400 && opline->op2.num > MAX_ARG_FLAG_NUM) {
3401 break;
3402 }
3403 op1_addr = OP1_REG_ADDR();
3404 if (ra
3405 && ssa->ops[opline - op_array->opcodes].op1_def >= 0
3406 && !ssa->vars[ssa->ops[opline - op_array->opcodes].op1_def].no_val) {
3407 op1_def_addr = OP1_DEF_REG_ADDR();
3408 } else {
3409 op1_def_addr = op1_addr;
3410 }
3411 if (!zend_jit_send_var(&dasm_state, opline, op_array,
3412 OP1_INFO(), op1_addr, op1_def_addr)) {
3413 goto jit_failure;
3414 }
3415 goto done;
3416 case ZEND_CHECK_FUNC_ARG:
3417 if (opline->op2_type == IS_CONST) {
3418 /* Named parameters not supported in JIT (yet) */
3419 break;
3420 }
3421 if (opline->op2.num > MAX_ARG_FLAG_NUM) {
3422 break;
3423 }
3424 if (!zend_jit_check_func_arg(&dasm_state, opline)) {
3425 goto jit_failure;
3426 }
3427 goto done;
3428 case ZEND_CHECK_UNDEF_ARGS:
3429 if (!zend_jit_check_undef_args(&dasm_state, opline)) {
3430 goto jit_failure;
3431 }
3432 goto done;
3433 case ZEND_DO_UCALL:
3434 is_terminated = 1;
3435 ZEND_FALLTHROUGH;
3436 case ZEND_DO_ICALL:
3437 case ZEND_DO_FCALL_BY_NAME:
3438 case ZEND_DO_FCALL:
3439 if (!zend_jit_do_fcall(&dasm_state, opline, op_array, ssa, call_level, b + 1, NULL)) {
3440 goto jit_failure;
3441 }
3442 goto done;
3443 case ZEND_IS_EQUAL:
3444 case ZEND_IS_NOT_EQUAL:
3445 case ZEND_IS_SMALLER:
3446 case ZEND_IS_SMALLER_OR_EQUAL:
3447 case ZEND_CASE: {
3448 res_addr = RES_REG_ADDR();
3449 if ((opline->result_type & IS_TMP_VAR)
3450 && (i + 1) <= end
3451 && ((opline+1)->opcode == ZEND_JMPZ
3452 || (opline+1)->opcode == ZEND_JMPNZ
3453 || (opline+1)->opcode == ZEND_JMPZ_EX
3454 || (opline+1)->opcode == ZEND_JMPNZ_EX
3455 || (opline+1)->opcode == ZEND_JMPZNZ)
3456 && (opline+1)->op1_type == IS_TMP_VAR
3457 && (opline+1)->op1.var == opline->result.var) {
3458 i++;
3459 smart_branch_opcode = (opline+1)->opcode;
3460 target_label = ssa->cfg.blocks[b].successors[0];
3461 target_label2 = ssa->cfg.blocks[b].successors[1];
3462 /* For EX variant write into the result of EX opcode. */
3463 if ((opline+1)->opcode == ZEND_JMPZ_EX
3464 || (opline+1)->opcode == ZEND_JMPNZ_EX) {
3465 res_addr = OP_REG_ADDR(opline + 1, result_type, result, result_def);
3466 }
3467 } else {
3468 smart_branch_opcode = 0;
3469 target_label = target_label2 = (uint32_t)-1;
3470 }
3471 if (!zend_jit_cmp(&dasm_state, opline,
3472 OP1_INFO(), OP1_RANGE(), OP1_REG_ADDR(),
3473 OP2_INFO(), OP2_RANGE(), OP2_REG_ADDR(),
3474 res_addr,
3475 zend_may_throw(opline, ssa_op, op_array, ssa),
3476 smart_branch_opcode, target_label, target_label2,
3477 NULL, 0)) {
3478 goto jit_failure;
3479 }
3480 goto done;
3481 }
3482 case ZEND_IS_IDENTICAL:
3483 case ZEND_IS_NOT_IDENTICAL:
3484 case ZEND_CASE_STRICT:
3485 if ((opline->result_type & IS_TMP_VAR)
3486 && (i + 1) <= end
3487 && ((opline+1)->opcode == ZEND_JMPZ
3488 || (opline+1)->opcode == ZEND_JMPNZ
3489 || (opline+1)->opcode == ZEND_JMPZNZ)
3490 && (opline+1)->op1_type == IS_TMP_VAR
3491 && (opline+1)->op1.var == opline->result.var) {
3492 i++;
3493 smart_branch_opcode = (opline+1)->opcode;
3494 target_label = ssa->cfg.blocks[b].successors[0];
3495 target_label2 = ssa->cfg.blocks[b].successors[1];
3496 } else {
3497 smart_branch_opcode = 0;
3498 target_label = target_label2 = (uint32_t)-1;
3499 }
3500 if (!zend_jit_identical(&dasm_state, opline,
3501 OP1_INFO(), OP1_RANGE(), OP1_REG_ADDR(),
3502 OP2_INFO(), OP2_RANGE(), OP2_REG_ADDR(),
3503 RES_REG_ADDR(),
3504 zend_may_throw(opline, ssa_op, op_array, ssa),
3505 smart_branch_opcode, target_label, target_label2,
3506 NULL, 0)) {
3507 goto jit_failure;
3508 }
3509 goto done;
3510 case ZEND_DEFINED:
3511 if ((opline->result_type & IS_TMP_VAR)
3512 && (i + 1) <= end
3513 && ((opline+1)->opcode == ZEND_JMPZ
3514 || (opline+1)->opcode == ZEND_JMPNZ
3515 || (opline+1)->opcode == ZEND_JMPZNZ)
3516 && (opline+1)->op1_type == IS_TMP_VAR
3517 && (opline+1)->op1.var == opline->result.var) {
3518 i++;
3519 smart_branch_opcode = (opline+1)->opcode;
3520 target_label = ssa->cfg.blocks[b].successors[0];
3521 target_label2 = ssa->cfg.blocks[b].successors[1];
3522 } else {
3523 smart_branch_opcode = 0;
3524 target_label = target_label2 = (uint32_t)-1;
3525 }
3526 if (!zend_jit_defined(&dasm_state, opline, smart_branch_opcode, target_label, target_label2, NULL)) {
3527 goto jit_failure;
3528 }
3529 goto done;
3530 case ZEND_TYPE_CHECK:
3531 if (opline->extended_value == MAY_BE_RESOURCE) {
3532 // TODO: support for is_resource() ???
3533 break;
3534 }
3535 if ((opline->result_type & IS_TMP_VAR)
3536 && (i + 1) <= end
3537 && ((opline+1)->opcode == ZEND_JMPZ
3538 || (opline+1)->opcode == ZEND_JMPNZ
3539 || (opline+1)->opcode == ZEND_JMPZNZ)
3540 && (opline+1)->op1_type == IS_TMP_VAR
3541 && (opline+1)->op1.var == opline->result.var) {
3542 i++;
3543 smart_branch_opcode = (opline+1)->opcode;
3544 target_label = ssa->cfg.blocks[b].successors[0];
3545 target_label2 = ssa->cfg.blocks[b].successors[1];
3546 } else {
3547 smart_branch_opcode = 0;
3548 target_label = target_label2 = (uint32_t)-1;
3549 }
3550 if (!zend_jit_type_check(&dasm_state, opline, OP1_INFO(), smart_branch_opcode, target_label, target_label2, NULL)) {
3551 goto jit_failure;
3552 }
3553 goto done;
3554 case ZEND_RETURN:
3555 op1_info = OP1_INFO();
3556 if ((PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info))
3557 || op_array->type == ZEND_EVAL_CODE
3558 // TODO: support for top-level code
3559 || !op_array->function_name
3560 // TODO: support for IS_UNDEF ???
3561 || (op1_info & MAY_BE_UNDEF)) {
3562 if (!zend_jit_tail_handler(&dasm_state, opline)) {
3563 goto jit_failure;
3564 }
3565 } else {
3566 int j;
3567 bool left_frame = 0;
3568
3569 if (!zend_jit_return(&dasm_state, opline, op_array,
3570 op1_info, OP1_REG_ADDR())) {
3571 goto jit_failure;
3572 }
3573 if (jit_return_label >= 0) {
3574 if (!zend_jit_jmp(&dasm_state, jit_return_label)) {
3575 goto jit_failure;
3576 }
3577 goto done;
3578 }
3579 jit_return_label = ssa->cfg.blocks_count * 2;
3580 if (!zend_jit_label(&dasm_state, jit_return_label)) {
3581 goto jit_failure;
3582 }
3583 if (op_array->last_var > 100) {
3584 /* To many CVs to unroll */
3585 if (!zend_jit_free_cvs(&dasm_state)) {
3586 goto jit_failure;
3587 }
3588 left_frame = 1;
3589 }
3590 if (!left_frame) {
3591 for (j = 0 ; j < op_array->last_var; j++) {
3592 uint32_t info = zend_ssa_cv_info(op_array, ssa, j);
3593
3594 if (info & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) {
3595 if (!left_frame) {
3596 left_frame = 1;
3597 if (!zend_jit_leave_frame(&dasm_state)) {
3598 goto jit_failure;
3599 }
3600 }
3601 if (!zend_jit_free_cv(&dasm_state, info, j)) {
3602 goto jit_failure;
3603 }
3604 }
3605 }
3606 }
3607 if (!zend_jit_leave_func(&dasm_state, op_array, opline, op1_info, left_frame,
3608 NULL, NULL, (ssa->cfg.flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) != 0, 1)) {
3609 goto jit_failure;
3610 }
3611 }
3612 goto done;
3613 case ZEND_BOOL:
3614 case ZEND_BOOL_NOT:
3615 if (!zend_jit_bool_jmpznz(&dasm_state, opline,
3616 OP1_INFO(), OP1_REG_ADDR(), RES_REG_ADDR(),
3617 -1, -1,
3618 zend_may_throw(opline, ssa_op, op_array, ssa),
3619 opline->opcode, NULL)) {
3620 goto jit_failure;
3621 }
3622 goto done;
3623 case ZEND_JMPZ:
3624 case ZEND_JMPNZ:
3625 if (opline > op_array->opcodes + ssa->cfg.blocks[b].start &&
3626 ((opline-1)->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
3627 /* smart branch */
3628 if (!zend_jit_cond_jmp(&dasm_state, opline + 1, ssa->cfg.blocks[b].successors[0])) {
3629 goto jit_failure;
3630 }
3631 goto done;
3632 }
3633 ZEND_FALLTHROUGH;
3634 case ZEND_JMPZNZ:
3635 case ZEND_JMPZ_EX:
3636 case ZEND_JMPNZ_EX:
3637 if (opline->result_type == IS_UNDEF) {
3638 res_addr = 0;
3639 } else {
3640 res_addr = RES_REG_ADDR();
3641 }
3642 if (!zend_jit_bool_jmpznz(&dasm_state, opline,
3643 OP1_INFO(), OP1_REG_ADDR(), res_addr,
3644 ssa->cfg.blocks[b].successors[0], ssa->cfg.blocks[b].successors[1],
3645 zend_may_throw(opline, ssa_op, op_array, ssa),
3646 opline->opcode, NULL)) {
3647 goto jit_failure;
3648 }
3649 goto done;
3650 case ZEND_ISSET_ISEMPTY_CV:
3651 if ((opline->extended_value & ZEND_ISEMPTY)) {
3652 // TODO: support for empty() ???
3653 break;
3654 }
3655 if ((opline->result_type & IS_TMP_VAR)
3656 && (i + 1) <= end
3657 && ((opline+1)->opcode == ZEND_JMPZ
3658 || (opline+1)->opcode == ZEND_JMPNZ
3659 || (opline+1)->opcode == ZEND_JMPZNZ)
3660 && (opline+1)->op1_type == IS_TMP_VAR
3661 && (opline+1)->op1.var == opline->result.var) {
3662 i++;
3663 smart_branch_opcode = (opline+1)->opcode;
3664 target_label = ssa->cfg.blocks[b].successors[0];
3665 target_label2 = ssa->cfg.blocks[b].successors[1];
3666 } else {
3667 smart_branch_opcode = 0;
3668 target_label = target_label2 = (uint32_t)-1;
3669 }
3670 if (!zend_jit_isset_isempty_cv(&dasm_state, opline,
3671 OP1_INFO(), OP1_REG_ADDR(),
3672 smart_branch_opcode, target_label, target_label2,
3673 NULL)) {
3674 goto jit_failure;
3675 }
3676 goto done;
3677 case ZEND_IN_ARRAY:
3678 if (opline->op1_type == IS_VAR || opline->op1_type == IS_TMP_VAR) {
3679 break;
3680 }
3681 op1_info = OP1_INFO();
3682 if ((op1_info & (MAY_BE_ANY|MAY_BE_UNDEF|MAY_BE_REF)) != MAY_BE_STRING) {
3683 break;
3684 }
3685 if ((opline->result_type & IS_TMP_VAR)
3686 && (i + 1) <= end
3687 && ((opline+1)->opcode == ZEND_JMPZ
3688 || (opline+1)->opcode == ZEND_JMPNZ
3689 || (opline+1)->opcode == ZEND_JMPZNZ)
3690 && (opline+1)->op1_type == IS_TMP_VAR
3691 && (opline+1)->op1.var == opline->result.var) {
3692 i++;
3693 smart_branch_opcode = (opline+1)->opcode;
3694 target_label = ssa->cfg.blocks[b].successors[0];
3695 target_label2 = ssa->cfg.blocks[b].successors[1];
3696 } else {
3697 smart_branch_opcode = 0;
3698 target_label = target_label2 = (uint32_t)-1;
3699 }
3700 if (!zend_jit_in_array(&dasm_state, opline,
3701 op1_info, OP1_REG_ADDR(),
3702 smart_branch_opcode, target_label, target_label2,
3703 NULL)) {
3704 goto jit_failure;
3705 }
3706 goto done;
3707 case ZEND_FETCH_DIM_R:
3708 case ZEND_FETCH_DIM_IS:
3709 case ZEND_FETCH_LIST_R:
3710 if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
3711 break;
3712 }
3713 if (!zend_jit_fetch_dim_read(&dasm_state, opline, ssa, ssa_op,
3714 OP1_INFO(), OP1_REG_ADDR(), 0,
3715 OP2_INFO(), RES_INFO(), RES_REG_ADDR(), IS_UNKNOWN)) {
3716 goto jit_failure;
3717 }
3718 goto done;
3719 case ZEND_FETCH_DIM_W:
3720 case ZEND_FETCH_DIM_RW:
3721 // case ZEND_FETCH_DIM_UNSET:
3722 case ZEND_FETCH_LIST_W:
3723 if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
3724 break;
3725 }
3726 if (opline->op1_type != IS_CV) {
3727 break;
3728 }
3729 if (!zend_jit_fetch_dim(&dasm_state, opline,
3730 OP1_INFO(), OP1_REG_ADDR(), OP2_INFO(), RES_REG_ADDR(), IS_UNKNOWN)) {
3731 goto jit_failure;
3732 }
3733 goto done;
3734 case ZEND_ISSET_ISEMPTY_DIM_OBJ:
3735 if ((opline->extended_value & ZEND_ISEMPTY)) {
3736 // TODO: support for empty() ???
3737 break;
3738 }
3739 if (PROFITABILITY_CHECKS && (!ssa->ops || !ssa->var_info)) {
3740 break;
3741 }
3742 if ((opline->result_type & IS_TMP_VAR)
3743 && (i + 1) <= end
3744 && ((opline+1)->opcode == ZEND_JMPZ
3745 || (opline+1)->opcode == ZEND_JMPNZ
3746 || (opline+1)->opcode == ZEND_JMPZNZ)
3747 && (opline+1)->op1_type == IS_TMP_VAR
3748 && (opline+1)->op1.var == opline->result.var) {
3749 i++;
3750 smart_branch_opcode = (opline+1)->opcode;
3751 target_label = ssa->cfg.blocks[b].successors[0];
3752 target_label2 = ssa->cfg.blocks[b].successors[1];
3753 } else {
3754 smart_branch_opcode = 0;
3755 target_label = target_label2 = (uint32_t)-1;
3756 }
3757 if (!zend_jit_isset_isempty_dim(&dasm_state, opline,
3758 OP1_INFO(), OP1_REG_ADDR(), 0,
3759 OP2_INFO(), IS_UNKNOWN,
3760 zend_may_throw(opline, ssa_op, op_array, ssa),
3761 smart_branch_opcode, target_label, target_label2,
3762 NULL)) {
3763 goto jit_failure;
3764 }
3765 goto done;
3766 case ZEND_FETCH_OBJ_R:
3767 case ZEND_FETCH_OBJ_IS:
3768 case ZEND_FETCH_OBJ_W:
3769 if (opline->op2_type != IS_CONST
3770 || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING
3771 || Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))[0] == '\0') {
3772 break;
3773 }
3774 ce = NULL;
3775 ce_is_instanceof = 0;
3776 on_this = 0;
3777 if (opline->op1_type == IS_UNUSED) {
3778 op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN;
3779 op1_addr = 0;
3780 ce = op_array->scope;
3781 ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0;
3782 on_this = 1;
3783 } else {
3784 op1_info = OP1_INFO();
3785 if (!(op1_info & MAY_BE_OBJECT)) {
3786 break;
3787 }
3788 op1_addr = OP1_REG_ADDR();
3789 if (ssa->var_info && ssa->ops) {
3790 zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
3791 if (ssa_op->op1_use >= 0) {
3792 zend_ssa_var_info *op1_ssa = ssa->var_info + ssa_op->op1_use;
3793 if (op1_ssa->ce && !op1_ssa->ce->create_object) {
3794 ce = op1_ssa->ce;
3795 ce_is_instanceof = op1_ssa->is_instanceof;
3796 }
3797 }
3798 }
3799 }
3800 if (!zend_jit_fetch_obj(&dasm_state, opline, op_array, ssa, ssa_op,
3801 op1_info, op1_addr, 0, ce, ce_is_instanceof, on_this, 0, 0, NULL,
3802 IS_UNKNOWN,
3803 zend_may_throw(opline, ssa_op, op_array, ssa))) {
3804 goto jit_failure;
3805 }
3806 goto done;
3807 case ZEND_BIND_GLOBAL:
3808 if (!ssa->ops || !ssa->var_info) {
3809 op1_info = MAY_BE_ANY|MAY_BE_REF;
3810 } else {
3811 op1_info = OP1_INFO();
3812 }
3813 if (!zend_jit_bind_global(&dasm_state, opline, op1_info)) {
3814 goto jit_failure;
3815 }
3816 goto done;
3817 case ZEND_RECV:
3818 if (!zend_jit_recv(&dasm_state, opline, op_array)) {
3819 goto jit_failure;
3820 }
3821 goto done;
3822 case ZEND_RECV_INIT:
3823 if (!zend_jit_recv_init(&dasm_state, opline, op_array,
3824 (opline + 1)->opcode != ZEND_RECV_INIT,
3825 zend_may_throw(opline, ssa_op, op_array, ssa))) {
3826 goto jit_failure;
3827 }
3828 goto done;
3829 case ZEND_FREE:
3830 case ZEND_FE_FREE:
3831 if (!zend_jit_free(&dasm_state, opline, OP1_INFO(),
3832 zend_may_throw(opline, ssa_op, op_array, ssa))) {
3833 goto jit_failure;
3834 }
3835 goto done;
3836 case ZEND_ECHO:
3837 op1_info = OP1_INFO();
3838 if ((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) != MAY_BE_STRING) {
3839 break;
3840 }
3841 if (!zend_jit_echo(&dasm_state, opline, op1_info)) {
3842 goto jit_failure;
3843 }
3844 goto done;
3845 case ZEND_STRLEN:
3846 op1_info = OP1_INFO();
3847 if ((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) != MAY_BE_STRING) {
3848 break;
3849 }
3850 if (!zend_jit_strlen(&dasm_state, opline, op1_info, OP1_REG_ADDR(), RES_REG_ADDR())) {
3851 goto jit_failure;
3852 }
3853 goto done;
3854 case ZEND_COUNT:
3855 op1_info = OP1_INFO();
3856 if ((op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) != MAY_BE_ARRAY) {
3857 break;
3858 }
3859 if (!zend_jit_count(&dasm_state, opline, op1_info, OP1_REG_ADDR(), RES_REG_ADDR(), zend_may_throw(opline, ssa_op, op_array, ssa))) {
3860 goto jit_failure;
3861 }
3862 goto done;
3863 case ZEND_FETCH_THIS:
3864 if (!zend_jit_fetch_this(&dasm_state, opline, op_array, 0)) {
3865 goto jit_failure;
3866 }
3867 goto done;
3868 case ZEND_SWITCH_LONG:
3869 case ZEND_SWITCH_STRING:
3870 case ZEND_MATCH:
3871 if (!zend_jit_switch(&dasm_state, opline, op_array, ssa, NULL, NULL)) {
3872 goto jit_failure;
3873 }
3874 goto done;
3875 case ZEND_VERIFY_RETURN_TYPE:
3876 if (opline->op1_type == IS_UNUSED) {
3877 /* Always throws */
3878 break;
3879 }
3880 if (opline->op1_type == IS_CONST) {
3881 /* TODO Different instruction format, has return value */
3882 break;
3883 }
3884 if (op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE) {
3885 /* Not worth bothering with */
3886 break;
3887 }
3888 if (OP1_INFO() & MAY_BE_REF) {
3889 /* TODO May need reference unwrapping. */
3890 break;
3891 }
3892 if (!zend_jit_verify_return_type(&dasm_state, opline, op_array, OP1_INFO())) {
3893 goto jit_failure;
3894 }
3895 goto done;
3896 case ZEND_FE_RESET_R:
3897 op1_info = OP1_INFO();
3898 if ((op1_info & (MAY_BE_ANY|MAY_BE_REF|MAY_BE_UNDEF)) != MAY_BE_ARRAY) {
3899 break;
3900 }
3901 if (!zend_jit_fe_reset(&dasm_state, opline, op1_info)) {
3902 goto jit_failure;
3903 }
3904 goto done;
3905 case ZEND_FE_FETCH_R:
3906 op1_info = OP1_INFO();
3907 if ((op1_info & MAY_BE_ANY) != MAY_BE_ARRAY) {
3908 break;
3909 }
3910 if (!zend_jit_fe_fetch(&dasm_state, opline, op1_info, OP2_INFO(),
3911 ssa->cfg.blocks[b].successors[0], opline->opcode, NULL)) {
3912 goto jit_failure;
3913 }
3914 goto done;
3915 case ZEND_FETCH_CONSTANT:
3916 if (!zend_jit_fetch_constant(&dasm_state, opline, op_array, ssa, ssa_op, RES_REG_ADDR())) {
3917 goto jit_failure;
3918 }
3919 goto done;
3920 case ZEND_INIT_METHOD_CALL:
3921 if (opline->op2_type != IS_CONST
3922 || Z_TYPE_P(RT_CONSTANT(opline, opline->op2)) != IS_STRING) {
3923 break;
3924 }
3925 ce = NULL;
3926 ce_is_instanceof = 0;
3927 on_this = 0;
3928 if (opline->op1_type == IS_UNUSED) {
3929 op1_info = MAY_BE_OBJECT|MAY_BE_RC1|MAY_BE_RCN;
3930 op1_addr = 0;
3931 ce = op_array->scope;
3932 ce_is_instanceof = (ce->ce_flags & ZEND_ACC_FINAL) != 0;
3933 on_this = 1;
3934 } else {
3935 op1_info = OP1_INFO();
3936 if (!(op1_info & MAY_BE_OBJECT)) {
3937 break;
3938 }
3939 op1_addr = OP1_REG_ADDR();
3940 if (ssa->var_info && ssa->ops) {
3941 zend_ssa_op *ssa_op = &ssa->ops[opline - op_array->opcodes];
3942 if (ssa_op->op1_use >= 0) {
3943 zend_ssa_var_info *op1_ssa = ssa->var_info + ssa_op->op1_use;
3944 if (op1_ssa->ce && !op1_ssa->ce->create_object) {
3945 ce = op1_ssa->ce;
3946 ce_is_instanceof = op1_ssa->is_instanceof;
3947 }
3948 }
3949 }
3950 }
3951 if (!zend_jit_init_method_call(&dasm_state, opline, b, op_array, ssa, ssa_op, call_level,
3952 op1_info, op1_addr, ce, ce_is_instanceof, on_this, 0, NULL,
3953 NULL, 0, 0)) {
3954 goto jit_failure;
3955 }
3956 goto done;
3957 case ZEND_ROPE_INIT:
3958 case ZEND_ROPE_ADD:
3959 case ZEND_ROPE_END:
3960 op2_info = OP2_INFO();
3961 if ((op2_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) != MAY_BE_STRING) {
3962 break;
3963 }
3964 if (!zend_jit_rope(&dasm_state, opline, op2_info)) {
3965 goto jit_failure;
3966 }
3967 goto done;
3968 default:
3969 break;
3970 }
3971 }
3972
3973 switch (opline->opcode) {
3974 case ZEND_RECV_INIT:
3975 case ZEND_BIND_GLOBAL:
3976 if (opline == op_array->opcodes ||
3977 opline->opcode != op_array->opcodes[i-1].opcode) {
3978 /* repeatable opcodes */
3979 if (!zend_jit_handler(&dasm_state, opline,
3980 zend_may_throw(opline, ssa_op, op_array, ssa))) {
3981 goto jit_failure;
3982 }
3983 }
3984 zend_jit_set_last_valid_opline(opline+1);
3985 break;
3986 case ZEND_NOP:
3987 case ZEND_OP_DATA:
3988 case ZEND_SWITCH_LONG:
3989 case ZEND_SWITCH_STRING:
3990 case ZEND_MATCH:
3991 break;
3992 case ZEND_JMP:
3993 if (JIT_G(opt_level) < ZEND_JIT_LEVEL_INLINE) {
3994 const zend_op *target = OP_JMP_ADDR(opline, opline->op1);
3995
3996 if (!zend_jit_set_ip(&dasm_state, target)) {
3997 goto jit_failure;
3998 }
3999 }
4000 if (!zend_jit_jmp(&dasm_state, ssa->cfg.blocks[b].successors[0])) {
4001 goto jit_failure;
4002 }
4003 is_terminated = 1;
4004 break;
4005 case ZEND_CATCH:
4006 case ZEND_FAST_CALL:
4007 case ZEND_FAST_RET:
4008 case ZEND_GENERATOR_CREATE:
4009 case ZEND_GENERATOR_RETURN:
4010 case ZEND_RETURN_BY_REF:
4011 case ZEND_RETURN:
4012 case ZEND_EXIT:
4013 case ZEND_MATCH_ERROR:
4014 /* switch through trampoline */
4015 case ZEND_YIELD:
4016 case ZEND_YIELD_FROM:
4017 if (!zend_jit_tail_handler(&dasm_state, opline)) {
4018 goto jit_failure;
4019 }
4020 is_terminated = 1;
4021 break;
4022 /* stackless execution */
4023 case ZEND_INCLUDE_OR_EVAL:
4024 case ZEND_DO_FCALL:
4025 case ZEND_DO_UCALL:
4026 case ZEND_DO_FCALL_BY_NAME:
4027 if (!zend_jit_call(&dasm_state, opline, b + 1)) {
4028 goto jit_failure;
4029 }
4030 is_terminated = 1;
4031 break;
4032 case ZEND_JMPZNZ:
4033 if (!zend_jit_handler(&dasm_state, opline,
4034 zend_may_throw(opline, ssa_op, op_array, ssa)) ||
4035 !zend_jit_cond_jmp(&dasm_state, OP_JMP_ADDR(opline, opline->op2), ssa->cfg.blocks[b].successors[1]) ||
4036 !zend_jit_jmp(&dasm_state, ssa->cfg.blocks[b].successors[0])) {
4037 goto jit_failure;
4038 }
4039 is_terminated = 1;
4040 break;
4041 case ZEND_JMPZ:
4042 case ZEND_JMPNZ:
4043 if (opline > op_array->opcodes + ssa->cfg.blocks[b].start &&
4044 ((opline-1)->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
4045 /* smart branch */
4046 if (!zend_jit_cond_jmp(&dasm_state, opline + 1, ssa->cfg.blocks[b].successors[0])) {
4047 goto jit_failure;
4048 }
4049 goto done;
4050 }
4051 ZEND_FALLTHROUGH;
4052 case ZEND_JMPZ_EX:
4053 case ZEND_JMPNZ_EX:
4054 case ZEND_JMP_SET:
4055 case ZEND_COALESCE:
4056 case ZEND_JMP_NULL:
4057 case ZEND_FE_RESET_R:
4058 case ZEND_FE_RESET_RW:
4059 case ZEND_ASSERT_CHECK:
4060 case ZEND_FE_FETCH_R:
4061 case ZEND_FE_FETCH_RW:
4062 if (!zend_jit_handler(&dasm_state, opline,
4063 zend_may_throw(opline, ssa_op, op_array, ssa)) ||
4064 !zend_jit_cond_jmp(&dasm_state, opline + 1, ssa->cfg.blocks[b].successors[0])) {
4065 goto jit_failure;
4066 }
4067 break;
4068 case ZEND_NEW:
4069 if (!zend_jit_handler(&dasm_state, opline, 1)) {
4070 return 0;
4071 }
4072 if (opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL) {
4073 zend_class_entry *ce = NULL;
4074
4075 if (JIT_G(opt_level) >= ZEND_JIT_LEVEL_OPT_FUNC) {
4076 if (ssa->ops && ssa->var_info) {
4077 zend_ssa_var_info *res_ssa = &ssa->var_info[ssa->ops[opline - op_array->opcodes].result_def];
4078 if (res_ssa->ce && !res_ssa->is_instanceof) {
4079 ce = res_ssa->ce;
4080 }
4081 }
4082 } else {
4083 if (opline->op1_type == IS_CONST) {
4084 zval *zv = RT_CONSTANT(opline, opline->op1);
4085 if (Z_TYPE_P(zv) == IS_STRING) {
4086 zval *lc = zv + 1;
4087 ce = (zend_class_entry*)zend_hash_find_ptr(EG(class_table), Z_STR_P(lc));
4088 }
4089 }
4090 }
4091
4092 i++;
4093
4094 if (!ce || !(ce->ce_flags & ZEND_ACC_LINKED) || ce->constructor) {
4095 const zend_op *next_opline = opline + 1;
4096
4097 zend_jit_cond_jmp(&dasm_state, next_opline, ssa->cfg.blocks[b].successors[0]);
4098 if (JIT_G(opt_level) < ZEND_JIT_LEVEL_INLINE) {
4099 zend_jit_call(&dasm_state, next_opline, b + 1);
4100 is_terminated = 1;
4101 } else {
4102 zend_jit_do_fcall(&dasm_state, next_opline, op_array, ssa, call_level, b + 1, NULL);
4103 }
4104 }
4105
4106 /* We skip over the DO_FCALL, so decrement call_level ourselves. */
4107 call_level--;
4108 }
4109 break;
4110 default:
4111 if (!zend_jit_handler(&dasm_state, opline,
4112 zend_may_throw(opline, ssa_op, op_array, ssa))) {
4113 goto jit_failure;
4114 }
4115 if (i == end
4116 && (opline->result_type & (IS_SMART_BRANCH_JMPZ|IS_SMART_BRANCH_JMPNZ)) != 0) {
4117 /* smart branch split across basic blocks */
4118 if (!zend_jit_cond_jmp(&dasm_state, opline + 2, ssa->cfg.blocks[b+1].successors[0])) {
4119 goto jit_failure;
4120 }
4121 if (!zend_jit_jmp(&dasm_state, ssa->cfg.blocks[b+1].successors[1])) {
4122 goto jit_failure;
4123 }
4124 is_terminated = 1;
4125 }
4126 }
4127 done:
4128 switch (opline->opcode) {
4129 case ZEND_DO_FCALL:
4130 case ZEND_DO_ICALL:
4131 case ZEND_DO_UCALL:
4132 case ZEND_DO_FCALL_BY_NAME:
4133 case ZEND_CALLABLE_CONVERT:
4134 call_level--;
4135 }
4136 }
4137 }
4138
4139 handler = dasm_link_and_encode(&dasm_state, op_array, ssa, rt_opline, ra, NULL, 0,
4140 (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) ? SP_ADJ_VM : SP_ADJ_RET, SP_ADJ_JIT);
4141 if (!handler) {
4142 goto jit_failure;
4143 }
4144 dasm_free(&dasm_state);
4145
4146 if (JIT_G(opt_flags) & (ZEND_JIT_REG_ALLOC_LOCAL|ZEND_JIT_REG_ALLOC_GLOBAL)) {
4147 zend_arena_release(&CG(arena), checkpoint);
4148 }
4149 return SUCCESS;
4150
4151 jit_failure:
4152 if (dasm_state) {
4153 dasm_free(&dasm_state);
4154 }
4155 if (JIT_G(opt_flags) & (ZEND_JIT_REG_ALLOC_LOCAL|ZEND_JIT_REG_ALLOC_GLOBAL)) {
4156 zend_arena_release(&CG(arena), checkpoint);
4157 }
4158 return FAILURE;
4159 }
4160
zend_jit_collect_calls(zend_op_array * op_array,zend_script * script)4161 static int zend_jit_collect_calls(zend_op_array *op_array, zend_script *script)
4162 {
4163 zend_func_info *func_info;
4164
4165 if (JIT_G(trigger) == ZEND_JIT_ON_FIRST_EXEC ||
4166 JIT_G(trigger) == ZEND_JIT_ON_PROF_REQUEST ||
4167 JIT_G(trigger) == ZEND_JIT_ON_HOT_COUNTERS) {
4168 func_info = ZEND_FUNC_INFO(op_array);
4169 } else {
4170 func_info = zend_arena_calloc(&CG(arena), 1, sizeof(zend_func_info));
4171 ZEND_SET_FUNC_INFO(op_array, func_info);
4172 }
4173 return zend_analyze_calls(&CG(arena), script, ZEND_CALL_TREE, op_array, func_info);
4174 }
4175
zend_jit_cleanup_func_info(zend_op_array * op_array)4176 static void zend_jit_cleanup_func_info(zend_op_array *op_array)
4177 {
4178 zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
4179 zend_call_info *caller_info, *callee_info;
4180
4181 if (func_info) {
4182 caller_info = func_info->caller_info;
4183 callee_info = func_info->callee_info;
4184
4185 if (JIT_G(trigger) == ZEND_JIT_ON_FIRST_EXEC ||
4186 JIT_G(trigger) == ZEND_JIT_ON_PROF_REQUEST ||
4187 JIT_G(trigger) == ZEND_JIT_ON_HOT_COUNTERS) {
4188 func_info->num = 0;
4189 func_info->flags &= ZEND_FUNC_JIT_ON_FIRST_EXEC
4190 | ZEND_FUNC_JIT_ON_PROF_REQUEST
4191 | ZEND_FUNC_JIT_ON_HOT_COUNTERS
4192 | ZEND_FUNC_JIT_ON_HOT_TRACE;
4193 memset(&func_info->ssa, 0, sizeof(zend_func_info) - offsetof(zend_func_info, ssa));
4194 } else {
4195 ZEND_SET_FUNC_INFO(op_array, NULL);
4196 }
4197
4198 while (caller_info) {
4199 if (caller_info->caller_op_array) {
4200 zend_jit_cleanup_func_info(caller_info->caller_op_array);
4201 }
4202 caller_info = caller_info->next_caller;
4203 }
4204 while (callee_info) {
4205 if (callee_info->callee_func && callee_info->callee_func->type == ZEND_USER_FUNCTION) {
4206 zend_jit_cleanup_func_info(&callee_info->callee_func->op_array);
4207 }
4208 callee_info = callee_info->next_callee;
4209 }
4210 }
4211 }
4212
zend_real_jit_func(zend_op_array * op_array,zend_script * script,const zend_op * rt_opline)4213 static int zend_real_jit_func(zend_op_array *op_array, zend_script *script, const zend_op *rt_opline)
4214 {
4215 zend_ssa ssa;
4216 void *checkpoint;
4217 zend_func_info *func_info;
4218
4219 if (*dasm_ptr == dasm_end) {
4220 return FAILURE;
4221 }
4222
4223 checkpoint = zend_arena_checkpoint(CG(arena));
4224
4225 /* Build SSA */
4226 memset(&ssa, 0, sizeof(zend_ssa));
4227
4228 if (zend_jit_op_array_analyze1(op_array, script, &ssa) != SUCCESS) {
4229 goto jit_failure;
4230 }
4231
4232 if (JIT_G(opt_level) >= ZEND_JIT_LEVEL_OPT_FUNCS) {
4233 if (zend_jit_collect_calls(op_array, script) != SUCCESS) {
4234 goto jit_failure;
4235 }
4236 func_info = ZEND_FUNC_INFO(op_array);
4237 func_info->call_map = zend_build_call_map(&CG(arena), func_info, op_array);
4238 if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
4239 zend_init_func_return_info(op_array, script, &func_info->return_info);
4240 }
4241 }
4242
4243 if (zend_jit_op_array_analyze2(op_array, script, &ssa, ZCG(accel_directives).optimization_level) != SUCCESS) {
4244 goto jit_failure;
4245 }
4246
4247 if (JIT_G(debug) & ZEND_JIT_DEBUG_SSA) {
4248 zend_dump_op_array(op_array, ZEND_DUMP_HIDE_UNREACHABLE|ZEND_DUMP_RC_INFERENCE|ZEND_DUMP_SSA, "JIT", &ssa);
4249 }
4250
4251 if (zend_jit(op_array, &ssa, rt_opline) != SUCCESS) {
4252 goto jit_failure;
4253 }
4254
4255 zend_jit_cleanup_func_info(op_array);
4256 zend_arena_release(&CG(arena), checkpoint);
4257 return SUCCESS;
4258
4259 jit_failure:
4260 zend_jit_cleanup_func_info(op_array);
4261 zend_arena_release(&CG(arena), checkpoint);
4262 return FAILURE;
4263 }
4264
4265 /* Run-time JIT handler */
zend_runtime_jit(void)4266 static int ZEND_FASTCALL zend_runtime_jit(void)
4267 {
4268 zend_execute_data *execute_data = EG(current_execute_data);
4269 zend_op_array *op_array = &EX(func)->op_array;
4270 zend_op *opline = op_array->opcodes;
4271 zend_jit_op_array_extension *jit_extension;
4272 bool do_bailout = 0;
4273
4274 zend_shared_alloc_lock();
4275
4276 if (ZEND_FUNC_INFO(op_array)) {
4277
4278 SHM_UNPROTECT();
4279 zend_jit_unprotect();
4280
4281 zend_try {
4282 /* restore original opcode handlers */
4283 if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
4284 while (opline->opcode == ZEND_RECV || opline->opcode == ZEND_RECV_INIT) {
4285 opline++;
4286 }
4287 }
4288 jit_extension = (zend_jit_op_array_extension*)ZEND_FUNC_INFO(op_array);
4289 opline->handler = jit_extension->orig_handler;
4290
4291 /* perform real JIT for this function */
4292 zend_real_jit_func(op_array, NULL, NULL);
4293 } zend_catch {
4294 do_bailout = true;
4295 } zend_end_try();
4296
4297 zend_jit_protect();
4298 SHM_PROTECT();
4299 }
4300
4301 zend_shared_alloc_unlock();
4302
4303 if (do_bailout) {
4304 zend_bailout();
4305 }
4306
4307 /* JIT-ed code is going to be called by VM */
4308 return 0;
4309 }
4310
zend_jit_check_funcs(HashTable * function_table,bool is_method)4311 void zend_jit_check_funcs(HashTable *function_table, bool is_method) {
4312 zend_op *opline;
4313 zend_function *func;
4314 zend_op_array *op_array;
4315 uintptr_t counter;
4316 zend_jit_op_array_extension *jit_extension;
4317
4318 ZEND_HASH_REVERSE_FOREACH_PTR(function_table, func) {
4319 if (func->type == ZEND_INTERNAL_FUNCTION) {
4320 break;
4321 }
4322 op_array = &func->op_array;
4323 opline = op_array->opcodes;
4324 if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
4325 while (opline->opcode == ZEND_RECV || opline->opcode == ZEND_RECV_INIT) {
4326 opline++;
4327 }
4328 }
4329 if (opline->handler == zend_jit_profile_jit_handler) {
4330 if (!RUN_TIME_CACHE(op_array)) {
4331 continue;
4332 }
4333 counter = (uintptr_t)ZEND_COUNTER_INFO(op_array);
4334 ZEND_COUNTER_INFO(op_array) = 0;
4335 jit_extension = (zend_jit_op_array_extension*)ZEND_FUNC_INFO(op_array);
4336 opline->handler = jit_extension->orig_handler;
4337 if (((double)counter / (double)zend_jit_profile_counter) > JIT_G(prof_threshold)) {
4338 zend_real_jit_func(op_array, NULL, NULL);
4339 }
4340 }
4341 } ZEND_HASH_FOREACH_END();
4342 }
4343
zend_jit_hot_func(zend_execute_data * execute_data,const zend_op * opline)4344 void ZEND_FASTCALL zend_jit_hot_func(zend_execute_data *execute_data, const zend_op *opline)
4345 {
4346 zend_op_array *op_array = &EX(func)->op_array;
4347 zend_jit_op_array_hot_extension *jit_extension;
4348 uint32_t i;
4349 bool do_bailout = 0;
4350
4351 zend_shared_alloc_lock();
4352 jit_extension = (zend_jit_op_array_hot_extension*)ZEND_FUNC_INFO(op_array);
4353
4354 if (jit_extension) {
4355 SHM_UNPROTECT();
4356 zend_jit_unprotect();
4357
4358 zend_try {
4359 for (i = 0; i < op_array->last; i++) {
4360 op_array->opcodes[i].handler = jit_extension->orig_handlers[i];
4361 }
4362
4363 /* perform real JIT for this function */
4364 zend_real_jit_func(op_array, NULL, opline);
4365 } zend_catch {
4366 do_bailout = 1;
4367 } zend_end_try();
4368
4369 zend_jit_protect();
4370 SHM_PROTECT();
4371 }
4372
4373 zend_shared_alloc_unlock();
4374
4375 if (do_bailout) {
4376 zend_bailout();
4377 }
4378 /* JIT-ed code is going to be called by VM */
4379 }
4380
zend_jit_setup_hot_counters_ex(zend_op_array * op_array,zend_cfg * cfg)4381 static void zend_jit_setup_hot_counters_ex(zend_op_array *op_array, zend_cfg *cfg)
4382 {
4383 if (JIT_G(hot_func)) {
4384 zend_op *opline = op_array->opcodes;
4385
4386 if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
4387 while (opline->opcode == ZEND_RECV || opline->opcode == ZEND_RECV_INIT) {
4388 opline++;
4389 }
4390 }
4391
4392 opline->handler = (const void*)zend_jit_func_hot_counter_handler;
4393 }
4394
4395 if (JIT_G(hot_loop)) {
4396 uint32_t i;
4397
4398 for (i = 0; i < cfg->blocks_count; i++) {
4399 if ((cfg->blocks[i].flags & ZEND_BB_REACHABLE) &&
4400 (cfg->blocks[i].flags & ZEND_BB_LOOP_HEADER)) {
4401 op_array->opcodes[cfg->blocks[i].start].handler =
4402 (const void*)zend_jit_loop_hot_counter_handler;
4403 }
4404 }
4405 }
4406 }
4407
zend_jit_restart_hot_counters(zend_op_array * op_array)4408 static int zend_jit_restart_hot_counters(zend_op_array *op_array)
4409 {
4410 zend_jit_op_array_hot_extension *jit_extension;
4411 zend_cfg cfg;
4412 uint32_t i;
4413
4414 jit_extension = (zend_jit_op_array_hot_extension*)ZEND_FUNC_INFO(op_array);
4415 for (i = 0; i < op_array->last; i++) {
4416 op_array->opcodes[i].handler = jit_extension->orig_handlers[i];
4417 }
4418
4419 if (zend_jit_build_cfg(op_array, &cfg) != SUCCESS) {
4420 return FAILURE;
4421 }
4422
4423 zend_jit_setup_hot_counters_ex(op_array, &cfg);
4424
4425 return SUCCESS;
4426 }
4427
zend_jit_setup_hot_counters(zend_op_array * op_array)4428 static int zend_jit_setup_hot_counters(zend_op_array *op_array)
4429 {
4430 zend_jit_op_array_hot_extension *jit_extension;
4431 zend_cfg cfg;
4432 uint32_t i;
4433
4434 ZEND_ASSERT(zend_jit_func_hot_counter_handler != NULL);
4435 ZEND_ASSERT(zend_jit_loop_hot_counter_handler != NULL);
4436
4437 if (zend_jit_build_cfg(op_array, &cfg) != SUCCESS) {
4438 return FAILURE;
4439 }
4440
4441 jit_extension = (zend_jit_op_array_hot_extension*)zend_shared_alloc(sizeof(zend_jit_op_array_hot_extension) + (op_array->last - 1) * sizeof(void*));
4442 if (!jit_extension) {
4443 return FAILURE;
4444 }
4445 memset(&jit_extension->func_info, 0, sizeof(zend_func_info));
4446 jit_extension->func_info.flags = ZEND_FUNC_JIT_ON_HOT_COUNTERS;
4447 jit_extension->counter = &zend_jit_hot_counters[zend_jit_op_array_hash(op_array) & (ZEND_HOT_COUNTERS_COUNT - 1)];
4448 for (i = 0; i < op_array->last; i++) {
4449 jit_extension->orig_handlers[i] = op_array->opcodes[i].handler;
4450 }
4451 ZEND_SET_FUNC_INFO(op_array, (void*)jit_extension);
4452
4453 zend_jit_setup_hot_counters_ex(op_array, &cfg);
4454
4455 zend_shared_alloc_register_xlat_entry(op_array->opcodes, jit_extension);
4456
4457 return SUCCESS;
4458 }
4459
4460 #include "jit/zend_jit_trace.c"
4461
zend_jit_op_array(zend_op_array * op_array,zend_script * script)4462 ZEND_EXT_API int zend_jit_op_array(zend_op_array *op_array, zend_script *script)
4463 {
4464 if (dasm_ptr == NULL) {
4465 return FAILURE;
4466 }
4467
4468 if (JIT_G(trigger) == ZEND_JIT_ON_FIRST_EXEC) {
4469 zend_jit_op_array_extension *jit_extension;
4470 zend_op *opline = op_array->opcodes;
4471
4472 if (CG(compiler_options) & ZEND_COMPILE_PRELOAD) {
4473 ZEND_SET_FUNC_INFO(op_array, NULL);
4474 zend_error(E_WARNING, "Preloading is incompatible with first-exec and profile triggered JIT");
4475 return SUCCESS;
4476 }
4477
4478 /* Set run-time JIT handler */
4479 ZEND_ASSERT(zend_jit_runtime_jit_handler != NULL);
4480 if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
4481 while (opline->opcode == ZEND_RECV || opline->opcode == ZEND_RECV_INIT) {
4482 opline++;
4483 }
4484 }
4485 jit_extension = (zend_jit_op_array_extension*)zend_shared_alloc(sizeof(zend_jit_op_array_extension));
4486 if (!jit_extension) {
4487 return FAILURE;
4488 }
4489 memset(&jit_extension->func_info, 0, sizeof(zend_func_info));
4490 jit_extension->func_info.flags = ZEND_FUNC_JIT_ON_FIRST_EXEC;
4491 jit_extension->orig_handler = (void*)opline->handler;
4492 ZEND_SET_FUNC_INFO(op_array, (void*)jit_extension);
4493 opline->handler = (const void*)zend_jit_runtime_jit_handler;
4494 zend_shared_alloc_register_xlat_entry(op_array->opcodes, jit_extension);
4495
4496 return SUCCESS;
4497 } else if (JIT_G(trigger) == ZEND_JIT_ON_PROF_REQUEST) {
4498 zend_jit_op_array_extension *jit_extension;
4499 zend_op *opline = op_array->opcodes;
4500
4501 if (CG(compiler_options) & ZEND_COMPILE_PRELOAD) {
4502 ZEND_SET_FUNC_INFO(op_array, NULL);
4503 zend_error(E_WARNING, "Preloading is incompatible with first-exec and profile triggered JIT");
4504 return SUCCESS;
4505 }
4506
4507 ZEND_ASSERT(zend_jit_profile_jit_handler != NULL);
4508 if (op_array->function_name) {
4509 if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
4510 while (opline->opcode == ZEND_RECV || opline->opcode == ZEND_RECV_INIT) {
4511 opline++;
4512 }
4513 }
4514 jit_extension = (zend_jit_op_array_extension*)zend_shared_alloc(sizeof(zend_jit_op_array_extension));
4515 if (!jit_extension) {
4516 return FAILURE;
4517 }
4518 memset(&jit_extension->func_info, 0, sizeof(zend_func_info));
4519 jit_extension->func_info.flags = ZEND_FUNC_JIT_ON_PROF_REQUEST;
4520 jit_extension->orig_handler = (void*)opline->handler;
4521 ZEND_SET_FUNC_INFO(op_array, (void*)jit_extension);
4522 opline->handler = (const void*)zend_jit_profile_jit_handler;
4523 zend_shared_alloc_register_xlat_entry(op_array->opcodes, jit_extension);
4524 }
4525
4526 return SUCCESS;
4527 } else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_COUNTERS) {
4528 return zend_jit_setup_hot_counters(op_array);
4529 } else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
4530 return zend_jit_setup_hot_trace_counters(op_array);
4531 } else if (JIT_G(trigger) == ZEND_JIT_ON_SCRIPT_LOAD) {
4532 return zend_real_jit_func(op_array, script, NULL);
4533 } else {
4534 ZEND_UNREACHABLE();
4535 }
4536 }
4537
zend_jit_script(zend_script * script)4538 ZEND_EXT_API int zend_jit_script(zend_script *script)
4539 {
4540 void *checkpoint;
4541 zend_call_graph call_graph;
4542 zend_func_info *info;
4543 int i;
4544
4545 if (dasm_ptr == NULL || *dasm_ptr == dasm_end) {
4546 return FAILURE;
4547 }
4548
4549 checkpoint = zend_arena_checkpoint(CG(arena));
4550
4551 call_graph.op_arrays_count = 0;
4552 if (zend_build_call_graph(&CG(arena), script, &call_graph) != SUCCESS) {
4553 goto jit_failure;
4554 }
4555
4556 zend_analyze_call_graph(&CG(arena), script, &call_graph);
4557
4558 if (JIT_G(trigger) == ZEND_JIT_ON_FIRST_EXEC ||
4559 JIT_G(trigger) == ZEND_JIT_ON_PROF_REQUEST ||
4560 JIT_G(trigger) == ZEND_JIT_ON_HOT_COUNTERS ||
4561 JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
4562 for (i = 0; i < call_graph.op_arrays_count; i++) {
4563 if (zend_jit_op_array(call_graph.op_arrays[i], script) != SUCCESS) {
4564 goto jit_failure;
4565 }
4566 }
4567 } else if (JIT_G(trigger) == ZEND_JIT_ON_SCRIPT_LOAD) {
4568 for (i = 0; i < call_graph.op_arrays_count; i++) {
4569 info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
4570 if (info) {
4571 if (zend_jit_op_array_analyze1(call_graph.op_arrays[i], script, &info->ssa) != SUCCESS) {
4572 goto jit_failure;
4573 }
4574 info->flags = info->ssa.cfg.flags;
4575 }
4576 }
4577
4578 for (i = 0; i < call_graph.op_arrays_count; i++) {
4579 info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
4580 if (info) {
4581 info->call_map = zend_build_call_map(&CG(arena), info, call_graph.op_arrays[i]);
4582 if (call_graph.op_arrays[i]->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
4583 zend_init_func_return_info(call_graph.op_arrays[i], script, &info->return_info);
4584 }
4585 }
4586 }
4587
4588 for (i = 0; i < call_graph.op_arrays_count; i++) {
4589 info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
4590 if (info) {
4591 if (zend_jit_op_array_analyze2(call_graph.op_arrays[i], script, &info->ssa, ZCG(accel_directives).optimization_level) != SUCCESS) {
4592 goto jit_failure;
4593 }
4594 info->flags = info->ssa.cfg.flags;
4595 }
4596 }
4597
4598 if (JIT_G(debug) & ZEND_JIT_DEBUG_SSA) {
4599 for (i = 0; i < call_graph.op_arrays_count; i++) {
4600 info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
4601 if (info) {
4602 zend_dump_op_array(call_graph.op_arrays[i], ZEND_DUMP_HIDE_UNREACHABLE|ZEND_DUMP_RC_INFERENCE|ZEND_DUMP_SSA, "JIT", &info->ssa);
4603 }
4604 }
4605 }
4606
4607 for (i = 0; i < call_graph.op_arrays_count; i++) {
4608 info = ZEND_FUNC_INFO(call_graph.op_arrays[i]);
4609 if (info) {
4610 if (zend_jit(call_graph.op_arrays[i], &info->ssa, NULL) != SUCCESS) {
4611 goto jit_failure;
4612 }
4613 }
4614 }
4615
4616 for (i = 0; i < call_graph.op_arrays_count; i++) {
4617 ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL);
4618 }
4619 } else {
4620 ZEND_UNREACHABLE();
4621 }
4622
4623 zend_arena_release(&CG(arena), checkpoint);
4624
4625 if (JIT_G(trigger) == ZEND_JIT_ON_FIRST_EXEC
4626 || JIT_G(trigger) == ZEND_JIT_ON_PROF_REQUEST
4627 || JIT_G(trigger) == ZEND_JIT_ON_HOT_COUNTERS
4628 || JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
4629 zend_class_entry *ce;
4630 zend_op_array *op_array;
4631
4632 ZEND_HASH_FOREACH_PTR(&script->class_table, ce) {
4633 ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) {
4634 if (!ZEND_FUNC_INFO(op_array)) {
4635 void *jit_extension = zend_shared_alloc_get_xlat_entry(op_array->opcodes);
4636
4637 if (jit_extension) {
4638 ZEND_SET_FUNC_INFO(op_array, jit_extension);
4639 }
4640 }
4641 } ZEND_HASH_FOREACH_END();
4642 } ZEND_HASH_FOREACH_END();
4643 }
4644
4645 return SUCCESS;
4646
4647 jit_failure:
4648 if (JIT_G(trigger) == ZEND_JIT_ON_SCRIPT_LOAD) {
4649 for (i = 0; i < call_graph.op_arrays_count; i++) {
4650 ZEND_SET_FUNC_INFO(call_graph.op_arrays[i], NULL);
4651 }
4652 }
4653 zend_arena_release(&CG(arena), checkpoint);
4654 return FAILURE;
4655 }
4656
zend_jit_unprotect(void)4657 ZEND_EXT_API void zend_jit_unprotect(void)
4658 {
4659 #ifdef HAVE_MPROTECT
4660 if (!(JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP))) {
4661 int opts = PROT_READ | PROT_WRITE;
4662 #ifdef ZTS
4663 /* TODO: EXEC+WRITE is not supported in macOS. Removing EXEC is still buggy as
4664 * other threads, which are executing the JITed code, would crash anyway. */
4665 # ifndef __APPLE__
4666 /* Another thread may be executing JITed code. */
4667 opts |= PROT_EXEC;
4668 # endif
4669 #endif
4670 if (mprotect(dasm_buf, dasm_size, opts) != 0) {
4671 fprintf(stderr, "mprotect() failed [%d] %s\n", errno, strerror(errno));
4672 }
4673 }
4674 #elif _WIN32
4675 if (!(JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP))) {
4676 DWORD old, new;
4677 #ifdef ZTS
4678 new = PAGE_EXECUTE_READWRITE;
4679 #else
4680 new = PAGE_READWRITE;
4681 #endif
4682 if (!VirtualProtect(dasm_buf, dasm_size, new, &old)) {
4683 DWORD err = GetLastError();
4684 char *msg = php_win32_error_to_msg(err);
4685 fprintf(stderr, "VirtualProtect() failed [%u] %s\n", err, msg);
4686 php_win32_error_msg_free(msg);
4687 }
4688 }
4689 #endif
4690 }
4691
zend_jit_protect(void)4692 ZEND_EXT_API void zend_jit_protect(void)
4693 {
4694 #ifdef HAVE_MPROTECT
4695 if (!(JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP))) {
4696 if (mprotect(dasm_buf, dasm_size, PROT_READ | PROT_EXEC) != 0) {
4697 fprintf(stderr, "mprotect() failed [%d] %s\n", errno, strerror(errno));
4698 }
4699 }
4700 #elif _WIN32
4701 if (!(JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP))) {
4702 DWORD old;
4703
4704 if (!VirtualProtect(dasm_buf, dasm_size, PAGE_EXECUTE_READ, &old)) {
4705 DWORD err = GetLastError();
4706 char *msg = php_win32_error_to_msg(err);
4707 fprintf(stderr, "VirtualProtect() failed [%u] %s\n", err, msg);
4708 php_win32_error_msg_free(msg);
4709 }
4710 }
4711 #endif
4712 }
4713
zend_jit_init_handlers(void)4714 static void zend_jit_init_handlers(void)
4715 {
4716 if (zend_jit_vm_kind == ZEND_VM_KIND_HYBRID) {
4717 zend_jit_runtime_jit_handler = dasm_labels[zend_lbhybrid_runtime_jit];
4718 zend_jit_profile_jit_handler = dasm_labels[zend_lbhybrid_profile_jit];
4719 zend_jit_func_hot_counter_handler = dasm_labels[zend_lbhybrid_func_hot_counter];
4720 zend_jit_loop_hot_counter_handler = dasm_labels[zend_lbhybrid_loop_hot_counter];
4721 zend_jit_func_trace_counter_handler = dasm_labels[zend_lbhybrid_func_trace_counter];
4722 zend_jit_ret_trace_counter_handler = dasm_labels[zend_lbhybrid_ret_trace_counter];
4723 zend_jit_loop_trace_counter_handler = dasm_labels[zend_lbhybrid_loop_trace_counter];
4724 } else {
4725 zend_jit_runtime_jit_handler = (const void*)zend_runtime_jit;
4726 zend_jit_profile_jit_handler = (const void*)zend_jit_profile_helper;
4727 zend_jit_func_hot_counter_handler = (const void*)zend_jit_func_counter_helper;
4728 zend_jit_loop_hot_counter_handler = (const void*)zend_jit_loop_counter_helper;
4729 zend_jit_func_trace_counter_handler = (const void*)zend_jit_func_trace_helper;
4730 zend_jit_ret_trace_counter_handler = (const void*)zend_jit_ret_trace_helper;
4731 zend_jit_loop_trace_counter_handler = (const void*)zend_jit_loop_trace_helper;
4732 }
4733 }
4734
zend_jit_make_stubs(void)4735 static int zend_jit_make_stubs(void)
4736 {
4737 dasm_State* dasm_state = NULL;
4738 uint32_t i;
4739
4740 dasm_init(&dasm_state, DASM_MAXSECTION);
4741 dasm_setupglobal(&dasm_state, dasm_labels, zend_lb_MAX);
4742
4743 for (i = 0; i < sizeof(zend_jit_stubs)/sizeof(zend_jit_stubs[0]); i++) {
4744 dasm_setup(&dasm_state, dasm_actions);
4745 if (!zend_jit_stubs[i].stub(&dasm_state)) {
4746 return 0;
4747 }
4748 if (!dasm_link_and_encode(&dasm_state, NULL, NULL, NULL, NULL, zend_jit_stubs[i].name, 0,
4749 zend_jit_stubs[i].offset, zend_jit_stubs[i].adjustment)) {
4750 return 0;
4751 }
4752 }
4753
4754 zend_jit_init_handlers();
4755
4756 dasm_free(&dasm_state);
4757 return 1;
4758 }
4759
zend_jit_globals_ctor(zend_jit_globals * jit_globals)4760 static void zend_jit_globals_ctor(zend_jit_globals *jit_globals)
4761 {
4762 memset(jit_globals, 0, sizeof(zend_jit_globals));
4763 zend_jit_trace_init_caches();
4764 }
4765
4766 #ifdef ZTS
zend_jit_globals_dtor(zend_jit_globals * jit_globals)4767 static void zend_jit_globals_dtor(zend_jit_globals *jit_globals)
4768 {
4769 zend_jit_trace_free_caches(jit_globals);
4770 }
4771 #endif
4772
zend_jit_parse_config_num(zend_long jit)4773 static int zend_jit_parse_config_num(zend_long jit)
4774 {
4775 if (jit == 0) {
4776 JIT_G(on) = 0;
4777 return SUCCESS;
4778 }
4779
4780 if (jit < 0) return FAILURE;
4781
4782 if (jit % 10 == 0 || jit % 10 > 5) return FAILURE;
4783 JIT_G(opt_level) = jit % 10;
4784
4785 jit /= 10;
4786 if (jit % 10 > 5) return FAILURE;
4787 JIT_G(trigger) = jit % 10;
4788
4789 jit /= 10;
4790 if (jit % 10 > 2) return FAILURE;
4791 JIT_G(opt_flags) = jit % 10;
4792
4793 jit /= 10;
4794 if (jit % 10 > 1) return FAILURE;
4795 JIT_G(opt_flags) |= ((jit % 10) ? ZEND_JIT_CPU_AVX : 0);
4796
4797 if (jit / 10 != 0) return FAILURE;
4798
4799 JIT_G(on) = 1;
4800
4801 return SUCCESS;
4802 }
4803
zend_jit_config(zend_string * jit,int stage)4804 ZEND_EXT_API int zend_jit_config(zend_string *jit, int stage)
4805 {
4806 if (stage != ZEND_INI_STAGE_STARTUP && !JIT_G(enabled)) {
4807 if (stage == ZEND_INI_STAGE_RUNTIME) {
4808 zend_error(E_WARNING, "Cannot change opcache.jit setting at run-time (JIT is disabled)");
4809 }
4810 return FAILURE;
4811 }
4812
4813 if (ZSTR_LEN(jit) == 0
4814 || zend_string_equals_literal_ci(jit, "disable")) {
4815 JIT_G(enabled) = 0;
4816 JIT_G(on) = 0;
4817 return SUCCESS;
4818 } else if (zend_string_equals_literal_ci(jit, "0")
4819 || zend_string_equals_literal_ci(jit, "off")
4820 || zend_string_equals_literal_ci(jit, "no")
4821 || zend_string_equals_literal_ci(jit, "false")) {
4822 JIT_G(enabled) = 1;
4823 JIT_G(on) = 0;
4824 return SUCCESS;
4825 } else if (zend_string_equals_literal_ci(jit, "1")
4826 || zend_string_equals_literal_ci(jit, "on")
4827 || zend_string_equals_literal_ci(jit, "yes")
4828 || zend_string_equals_literal_ci(jit, "true")
4829 || zend_string_equals_literal_ci(jit, "tracing")) {
4830 JIT_G(enabled) = 1;
4831 JIT_G(on) = 1;
4832 JIT_G(opt_level) = ZEND_JIT_LEVEL_OPT_FUNCS;
4833 JIT_G(trigger) = ZEND_JIT_ON_HOT_TRACE;
4834 JIT_G(opt_flags) = ZEND_JIT_REG_ALLOC_GLOBAL | ZEND_JIT_CPU_AVX;
4835 return SUCCESS;
4836 } else if (zend_string_equals_literal_ci(jit, "function")) {
4837 JIT_G(enabled) = 1;
4838 JIT_G(on) = 1;
4839 JIT_G(opt_level) = ZEND_JIT_LEVEL_OPT_SCRIPT;
4840 JIT_G(trigger) = ZEND_JIT_ON_SCRIPT_LOAD;
4841 JIT_G(opt_flags) = ZEND_JIT_REG_ALLOC_GLOBAL | ZEND_JIT_CPU_AVX;
4842 return SUCCESS;
4843 } else {
4844 char *end;
4845 zend_long num = ZEND_STRTOL(ZSTR_VAL(jit), &end, 10);
4846 if (end != ZSTR_VAL(jit) + ZSTR_LEN(jit) || zend_jit_parse_config_num(num) != SUCCESS) {
4847 goto failure;
4848 }
4849 JIT_G(enabled) = 1;
4850 return SUCCESS;
4851 }
4852
4853 failure:
4854 zend_error(E_WARNING, "Invalid \"opcache.jit\" setting. Should be \"disable\", \"on\", \"off\", \"tracing\", \"function\" or 4-digit number");
4855 JIT_G(enabled) = 0;
4856 JIT_G(on) = 0;
4857 return FAILURE;
4858 }
4859
zend_jit_debug_config(zend_long old_val,zend_long new_val,int stage)4860 ZEND_EXT_API int zend_jit_debug_config(zend_long old_val, zend_long new_val, int stage)
4861 {
4862 if (stage != ZEND_INI_STAGE_STARTUP) {
4863 if (((old_val ^ new_val) & ZEND_JIT_DEBUG_PERSISTENT) != 0) {
4864 if (stage == ZEND_INI_STAGE_RUNTIME) {
4865 zend_error(E_WARNING, "Some opcache.jit_debug bits cannot be changed after startup");
4866 }
4867 return FAILURE;
4868 }
4869 #ifdef HAVE_DISASM
4870 if (new_val & (ZEND_JIT_DEBUG_ASM|ZEND_JIT_DEBUG_ASM_STUBS)) {
4871 if (JIT_G(enabled) && !JIT_G(symbols) && !zend_jit_disasm_init()) {
4872 // TODO: error reporting and cleanup ???
4873 return FAILURE;
4874 }
4875 // TODO: symbols for JIT-ed code compiled before are missing ???
4876 }
4877 #endif
4878 }
4879 return SUCCESS;
4880 }
4881
zend_jit_init(void)4882 ZEND_EXT_API void zend_jit_init(void)
4883 {
4884 #ifdef ZTS
4885 jit_globals_id = ts_allocate_id(&jit_globals_id, sizeof(zend_jit_globals), (ts_allocate_ctor) zend_jit_globals_ctor, (ts_allocate_dtor) zend_jit_globals_dtor);
4886 #else
4887 zend_jit_globals_ctor(&jit_globals);
4888 #endif
4889 }
4890
zend_jit_check_support(void)4891 ZEND_EXT_API int zend_jit_check_support(void)
4892 {
4893 int i;
4894
4895 zend_jit_vm_kind = zend_vm_kind();
4896 if (zend_jit_vm_kind != ZEND_VM_KIND_CALL &&
4897 zend_jit_vm_kind != ZEND_VM_KIND_HYBRID) {
4898 zend_error(E_WARNING, "JIT is compatible only with CALL and HYBRID VM. JIT disabled.");
4899 JIT_G(enabled) = 0;
4900 JIT_G(on) = 0;
4901 return FAILURE;
4902 }
4903
4904 if (zend_execute_ex != execute_ex) {
4905 if (strcmp(sapi_module.name, "phpdbg") != 0) {
4906 zend_error(E_WARNING, "JIT is incompatible with third party extensions that override zend_execute_ex(). JIT disabled.");
4907 }
4908 JIT_G(enabled) = 0;
4909 JIT_G(on) = 0;
4910 return FAILURE;
4911 }
4912
4913 for (i = 0; i <= 256; i++) {
4914 switch (i) {
4915 /* JIT has no effect on these opcodes */
4916 case ZEND_BEGIN_SILENCE:
4917 case ZEND_END_SILENCE:
4918 case ZEND_EXIT:
4919 break;
4920 default:
4921 if (zend_get_user_opcode_handler(i) != NULL) {
4922 zend_error(E_WARNING, "JIT is incompatible with third party extensions that setup user opcode handlers. JIT disabled.");
4923 JIT_G(enabled) = 0;
4924 JIT_G(on) = 0;
4925 return FAILURE;
4926 }
4927 }
4928 }
4929
4930 return SUCCESS;
4931 }
4932
zend_jit_startup(void * buf,size_t size,bool reattached)4933 ZEND_EXT_API int zend_jit_startup(void *buf, size_t size, bool reattached)
4934 {
4935 int ret;
4936
4937 zend_jit_halt_op = zend_get_halt_op();
4938
4939 if (zend_jit_setup() != SUCCESS) {
4940 // TODO: error reporting and cleanup ???
4941 return FAILURE;
4942 }
4943
4944 zend_jit_profile_counter_rid = zend_get_op_array_extension_handle(ACCELERATOR_PRODUCT_NAME);
4945
4946 #ifdef HAVE_GDB
4947 zend_jit_gdb_init();
4948 #endif
4949
4950 #ifdef HAVE_OPROFILE
4951 if (JIT_G(debug) & ZEND_JIT_DEBUG_OPROFILE) {
4952 if (!zend_jit_oprofile_startup()) {
4953 // TODO: error reporting and cleanup ???
4954 return FAILURE;
4955 }
4956 }
4957 #endif
4958
4959 dasm_buf = buf;
4960 dasm_size = size;
4961
4962 #ifdef HAVE_MPROTECT
4963 if (JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP)) {
4964 if (mprotect(dasm_buf, dasm_size, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) {
4965 fprintf(stderr, "mprotect() failed [%d] %s\n", errno, strerror(errno));
4966 }
4967 } else {
4968 if (mprotect(dasm_buf, dasm_size, PROT_READ | PROT_EXEC) != 0) {
4969 fprintf(stderr, "mprotect() failed [%d] %s\n", errno, strerror(errno));
4970 }
4971 }
4972 #elif _WIN32
4973 if (JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP)) {
4974 DWORD old;
4975
4976 if (!VirtualProtect(dasm_buf, dasm_size, PAGE_EXECUTE_READWRITE, &old)) {
4977 DWORD err = GetLastError();
4978 char *msg = php_win32_error_to_msg(err);
4979 fprintf(stderr, "VirtualProtect() failed [%u] %s\n", err, msg);
4980 php_win32_error_msg_free(msg);
4981 }
4982 } else {
4983 DWORD old;
4984
4985 if (!VirtualProtect(dasm_buf, dasm_size, PAGE_EXECUTE_READ, &old)) {
4986 DWORD err = GetLastError();
4987 char *msg = php_win32_error_to_msg(err);
4988 fprintf(stderr, "VirtualProtect() failed [%u] %s\n", err, msg);
4989 php_win32_error_msg_free(msg);
4990 }
4991 }
4992 #endif
4993
4994 dasm_ptr = dasm_end = (void*)(((char*)dasm_buf) + size - sizeof(*dasm_ptr) * 2);
4995 if (!reattached) {
4996 zend_jit_unprotect();
4997 *dasm_ptr = dasm_buf;
4998 #if _WIN32
4999 /* reserve space for global labels */
5000 *dasm_ptr = (void**)*dasm_ptr + zend_lb_MAX;
5001 #endif
5002 zend_jit_protect();
5003 }
5004
5005 #ifdef HAVE_DISASM
5006 if (JIT_G(debug) & (ZEND_JIT_DEBUG_ASM|ZEND_JIT_DEBUG_ASM_STUBS)) {
5007 if (!zend_jit_disasm_init()) {
5008 // TODO: error reporting and cleanup ???
5009 return FAILURE;
5010 }
5011 }
5012 #endif
5013
5014 #ifdef HAVE_PERFTOOLS
5015 if (JIT_G(debug) & ZEND_JIT_DEBUG_PERF_DUMP) {
5016 zend_jit_perf_jitdump_open();
5017 }
5018 #endif
5019
5020 if (!reattached) {
5021 zend_jit_unprotect();
5022 ret = zend_jit_make_stubs();
5023 #if _WIN32
5024 /* save global labels */
5025 memcpy(dasm_buf, dasm_labels, sizeof(void*) * zend_lb_MAX);
5026 #endif
5027 zend_jit_protect();
5028 if (!ret) {
5029 // TODO: error reporting and cleanup ???
5030 return FAILURE;
5031 }
5032 } else {
5033 #if _WIN32
5034 /* restore global labels */
5035 memcpy(dasm_labels, dasm_buf, sizeof(void*) * zend_lb_MAX);
5036 zend_jit_init_handlers();
5037 #endif
5038 }
5039
5040 if (zend_jit_trace_startup(reattached) != SUCCESS) {
5041 return FAILURE;
5042 }
5043
5044 zend_jit_unprotect();
5045 #if ZEND_JIT_TARGET_ARM64
5046 /* reserve space for global labels veneers */
5047 dasm_labels_veneers = *dasm_ptr;
5048 *dasm_ptr = (void**)*dasm_ptr + ZEND_MM_ALIGNED_SIZE_EX(zend_lb_MAX, DASM_ALIGNMENT);
5049 memset(dasm_labels_veneers, 0, sizeof(void*) * ZEND_MM_ALIGNED_SIZE_EX(zend_lb_MAX, DASM_ALIGNMENT));
5050 #endif
5051 /* save JIT buffer pos */
5052 dasm_ptr[1] = dasm_ptr[0];
5053 zend_jit_protect();
5054
5055 return SUCCESS;
5056 }
5057
zend_jit_shutdown(void)5058 ZEND_EXT_API void zend_jit_shutdown(void)
5059 {
5060 if (JIT_G(debug) & ZEND_JIT_DEBUG_SIZE) {
5061 fprintf(stderr, "\nJIT memory usage: %td\n", (ptrdiff_t)((char*)*dasm_ptr - (char*)dasm_buf));
5062 }
5063
5064 #ifdef HAVE_OPROFILE
5065 if (JIT_G(debug) & ZEND_JIT_DEBUG_OPROFILE) {
5066 zend_jit_oprofile_shutdown();
5067 }
5068 #endif
5069
5070 #ifdef HAVE_GDB
5071 if (JIT_G(debug) & ZEND_JIT_DEBUG_GDB) {
5072 zend_jit_gdb_unregister();
5073 }
5074 #endif
5075
5076 #ifdef HAVE_DISASM
5077 zend_jit_disasm_shutdown();
5078 #endif
5079
5080 #ifdef HAVE_PERFTOOLS
5081 if (JIT_G(debug) & ZEND_JIT_DEBUG_PERF_DUMP) {
5082 zend_jit_perf_jitdump_close();
5083 }
5084 #endif
5085 #ifdef ZTS
5086 ts_free_id(jit_globals_id);
5087 #else
5088 zend_jit_trace_free_caches(&jit_globals);
5089 #endif
5090 }
5091
zend_jit_reset_counters(void)5092 static void zend_jit_reset_counters(void)
5093 {
5094 int i;
5095
5096 for (i = 0; i < ZEND_HOT_COUNTERS_COUNT; i++) {
5097 zend_jit_hot_counters[i] = ZEND_JIT_COUNTER_INIT;
5098 }
5099 }
5100
zend_jit_activate(void)5101 ZEND_EXT_API void zend_jit_activate(void)
5102 {
5103 zend_jit_profile_counter = 0;
5104 if (JIT_G(on)) {
5105 if (JIT_G(trigger) == ZEND_JIT_ON_HOT_COUNTERS) {
5106 zend_jit_reset_counters();
5107 } else if (JIT_G(trigger) == ZEND_JIT_ON_HOT_TRACE) {
5108 zend_jit_reset_counters();
5109 zend_jit_trace_reset_caches();
5110 }
5111 }
5112 }
5113
zend_jit_deactivate(void)5114 ZEND_EXT_API void zend_jit_deactivate(void)
5115 {
5116 if (zend_jit_profile_counter && !CG(unclean_shutdown)) {
5117 zend_class_entry *ce;
5118
5119 zend_shared_alloc_lock();
5120 SHM_UNPROTECT();
5121 zend_jit_unprotect();
5122
5123 zend_jit_check_funcs(EG(function_table), 0);
5124 ZEND_HASH_REVERSE_FOREACH_PTR(EG(class_table), ce) {
5125 if (ce->type == ZEND_INTERNAL_CLASS) {
5126 break;
5127 }
5128 zend_jit_check_funcs(&ce->function_table, 1);
5129 } ZEND_HASH_FOREACH_END();
5130
5131 zend_jit_protect();
5132 SHM_PROTECT();
5133 zend_shared_alloc_unlock();
5134 }
5135
5136 zend_jit_profile_counter = 0;
5137 }
5138
zend_jit_restart_preloaded_op_array(zend_op_array * op_array)5139 static void zend_jit_restart_preloaded_op_array(zend_op_array *op_array)
5140 {
5141 zend_func_info *func_info = ZEND_FUNC_INFO(op_array);
5142
5143 if (!func_info) {
5144 return;
5145 }
5146
5147 if (func_info->flags & ZEND_FUNC_JIT_ON_HOT_TRACE) {
5148 zend_jit_restart_hot_trace_counters(op_array);
5149 } else if (func_info->flags & ZEND_FUNC_JIT_ON_HOT_COUNTERS) {
5150 zend_jit_restart_hot_counters(op_array);
5151 #if 0
5152 // TODO: We have to restore handlers for some inner basic-blocks, but we didn't store them ???
5153 } else if (func_info->flags & (ZEND_FUNC_JIT_ON_FIRST_EXEC|ZEND_FUNC_JIT_ON_PROF_REQUEST)) {
5154 zend_op *opline = op_array->opcodes;
5155 zend_jit_op_array_extension *jit_extension =
5156 (zend_jit_op_array_extension*)func_info;
5157
5158 if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
5159 while (opline->opcode == ZEND_RECV || opline->opcode == ZEND_RECV_INIT) {
5160 opline++;
5161 }
5162 }
5163 if (func_info->flags & ZEND_FUNC_JIT_ON_FIRST_EXEC) {
5164 opline->handler = (const void*)zend_jit_runtime_jit_handler;
5165 } else {
5166 opline->handler = (const void*)zend_jit_profile_jit_handler;
5167 }
5168 #endif
5169 }
5170 if (op_array->num_dynamic_func_defs) {
5171 for (uint32_t i = 0; i < op_array->num_dynamic_func_defs; i++) {
5172 zend_jit_restart_preloaded_op_array(op_array->dynamic_func_defs[i]);
5173 }
5174 }
5175 }
5176
zend_jit_restart_preloaded_script(zend_persistent_script * script)5177 static void zend_jit_restart_preloaded_script(zend_persistent_script *script)
5178 {
5179 zend_class_entry *ce;
5180 zend_op_array *op_array;
5181
5182 zend_jit_restart_preloaded_op_array(&script->script.main_op_array);
5183
5184 ZEND_HASH_FOREACH_PTR(&script->script.function_table, op_array) {
5185 zend_jit_restart_preloaded_op_array(op_array);
5186 } ZEND_HASH_FOREACH_END();
5187
5188 ZEND_HASH_FOREACH_PTR(&script->script.class_table, ce) {
5189 ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) {
5190 if (op_array->type == ZEND_USER_FUNCTION) {
5191 zend_jit_restart_preloaded_op_array(op_array);
5192 }
5193 } ZEND_HASH_FOREACH_END();
5194 } ZEND_HASH_FOREACH_END();
5195 }
5196
zend_jit_restart(void)5197 ZEND_EXT_API void zend_jit_restart(void)
5198 {
5199 if (dasm_buf) {
5200 zend_jit_unprotect();
5201
5202 #if ZEND_JIT_TARGET_ARM64
5203 memset(dasm_labels_veneers, 0, sizeof(void*) * ZEND_MM_ALIGNED_SIZE_EX(zend_lb_MAX, DASM_ALIGNMENT));
5204 #endif
5205
5206 /* restore JIT buffer pos */
5207 dasm_ptr[0] = dasm_ptr[1];
5208
5209 zend_jit_trace_restart();
5210
5211 if (ZCSG(preload_script)) {
5212 zend_jit_restart_preloaded_script(ZCSG(preload_script));
5213 if (ZCSG(saved_scripts)) {
5214 zend_persistent_script **p = ZCSG(saved_scripts);
5215
5216 while (*p) {
5217 zend_jit_restart_preloaded_script(*p);
5218 p++;
5219 }
5220 }
5221 }
5222
5223 zend_jit_protect();
5224
5225 #ifdef HAVE_DISASM
5226 if (JIT_G(debug) & (ZEND_JIT_DEBUG_ASM|ZEND_JIT_DEBUG_ASM_STUBS)) {
5227 zend_jit_disasm_shutdown();
5228 zend_jit_disasm_init();
5229 }
5230 #endif
5231 }
5232 }
5233
5234 #endif /* HAVE_JIT */
5235