1 /*
2 +----------------------------------------------------------------------+
3 | Zend OPcache |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1998-2018 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 | http://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@zend.com> |
16 | Xinchen Hui <laruence@php.net> |
17 +----------------------------------------------------------------------+
18 */
19
20 /* pass 11
21 * - compact literals table
22 */
23
24 #include "php.h"
25 #include "Optimizer/zend_optimizer.h"
26 #include "Optimizer/zend_optimizer_internal.h"
27 #include "zend_API.h"
28 #include "zend_constants.h"
29 #include "zend_execute.h"
30 #include "zend_vm.h"
31
32 #define DEBUG_COMPACT_LITERALS 0
33
34 #define LITERAL_VALUE 0x0100
35 #define LITERAL_FUNC 0x0200
36 #define LITERAL_CLASS 0x0300
37 #define LITERAL_CONST 0x0400
38 #define LITERAL_CLASS_CONST 0x0500
39 #define LITERAL_STATIC_METHOD 0x0600
40 #define LITERAL_STATIC_PROPERTY 0x0700
41 #define LITERAL_METHOD 0x0800
42 #define LITERAL_PROPERTY 0x0900
43 #define LITERAL_GLOBAL 0x0A00
44
45 #define LITERAL_EX_CLASS 0x4000
46 #define LITERAL_EX_OBJ 0x2000
47 #define LITERAL_MAY_MERGE 0x1000
48 #define LITERAL_KIND_MASK 0x0f00
49 #define LITERAL_NUM_RELATED_MASK 0x000f
50 #define LITERAL_NUM_SLOTS_MASK 0x00f0
51 #define LITERAL_NUM_SLOTS_SHIFT 4
52
53 #define LITERAL_NUM_RELATED(info) (info & LITERAL_NUM_RELATED_MASK)
54 #define LITERAL_NUM_SLOTS(info) ((info & LITERAL_NUM_SLOTS_MASK) >> LITERAL_NUM_SLOTS_SHIFT)
55
56 typedef struct _literal_info {
57 uint32_t flags; /* bitmask (see defines above) */
58 union {
59 int num; /* variable number or class name literal number */
60 } u;
61 } literal_info;
62
63 #define LITERAL_FLAGS(kind, slots, related) \
64 ((kind) | ((slots) << LITERAL_NUM_SLOTS_SHIFT) | (related))
65
66 #define LITERAL_INFO(n, kind, merge, slots, related) do { \
67 info[n].flags = (((merge) ? LITERAL_MAY_MERGE : 0) | LITERAL_FLAGS(kind, slots, related)); \
68 } while (0)
69
70 #define LITERAL_INFO_CLASS(n, kind, merge, slots, related, _num) do { \
71 info[n].flags = (LITERAL_EX_CLASS | ((merge) ? LITERAL_MAY_MERGE : 0) | LITERAL_FLAGS(kind, slots, related)); \
72 info[n].u.num = (_num); \
73 } while (0)
74
75 #define LITERAL_INFO_OBJ(n, kind, merge, slots, related) do { \
76 info[n].flags = (LITERAL_EX_OBJ | ((merge) ? LITERAL_MAY_MERGE : 0) | LITERAL_FLAGS(kind, slots, related)); \
77 info[n].u.num = (uint32_t)-1; \
78 } while (0)
79
optimizer_literal_obj_info(literal_info * info,zend_uchar op_type,znode_op op,int constant,uint32_t kind,uint32_t slots,uint32_t related,zend_op_array * op_array)80 static void optimizer_literal_obj_info(literal_info *info,
81 zend_uchar op_type,
82 znode_op op,
83 int constant,
84 uint32_t kind,
85 uint32_t slots,
86 uint32_t related,
87 zend_op_array *op_array)
88 {
89 /* For now we merge only $this object properties and methods.
90 * In general it's also possible to do it for any CV variable as well,
91 * but it would require complex dataflow and/or type analysis.
92 */
93 if (Z_TYPE(op_array->literals[constant]) == IS_STRING &&
94 op_type == IS_UNUSED) {
95 LITERAL_INFO_OBJ(constant, kind, 1, slots, related);
96 } else {
97 LITERAL_INFO(constant, kind, 0, slots, related);
98 }
99 }
100
optimizer_literal_class_info(literal_info * info,zend_uchar op_type,znode_op op,int constant,uint32_t kind,uint32_t slots,uint32_t related,zend_op_array * op_array)101 static void optimizer_literal_class_info(literal_info *info,
102 zend_uchar op_type,
103 znode_op op,
104 int constant,
105 uint32_t kind,
106 uint32_t slots,
107 uint32_t related,
108 zend_op_array *op_array)
109 {
110 if (op_type == IS_CONST) {
111 LITERAL_INFO_CLASS(constant, kind, 1, slots, related, op.constant);
112 } else {
113 LITERAL_INFO(constant, kind, 0, slots, related);
114 }
115 }
116
zend_optimizer_compact_literals(zend_op_array * op_array,zend_optimizer_ctx * ctx)117 void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx)
118 {
119 zend_op *opline, *end;
120 int i, j, n, *map, cache_size;
121 zval zv, *pos;
122 literal_info *info;
123 int l_null = -1;
124 int l_false = -1;
125 int l_true = -1;
126 int l_empty_arr = -1;
127 HashTable hash;
128 zend_string *key = NULL;
129 void *checkpoint = zend_arena_checkpoint(ctx->arena);
130
131 if (op_array->last_literal) {
132 cache_size = 0;
133 info = (literal_info*)zend_arena_calloc(&ctx->arena, op_array->last_literal, sizeof(literal_info));
134
135 /* Mark literals of specific types */
136 opline = op_array->opcodes;
137 end = opline + op_array->last;
138 while (opline < end) {
139 switch (opline->opcode) {
140 case ZEND_INIT_FCALL:
141 LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 1, 1, 1);
142 break;
143 case ZEND_INIT_FCALL_BY_NAME:
144 LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 1, 1, 2);
145 break;
146 case ZEND_INIT_NS_FCALL_BY_NAME:
147 LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 1, 1, 3);
148 break;
149 case ZEND_INIT_METHOD_CALL:
150 if (opline->op1_type == IS_CONST) {
151 LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1, 0, 1);
152 }
153 if (opline->op2_type == IS_CONST) {
154 optimizer_literal_obj_info(
155 info,
156 opline->op1_type,
157 opline->op1,
158 opline->op2.constant,
159 LITERAL_METHOD, 2, 2,
160 op_array);
161 }
162 break;
163 case ZEND_INIT_STATIC_METHOD_CALL:
164 if (opline->op1_type == IS_CONST) {
165 LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 1, 1, 2);
166 }
167 if (opline->op2_type == IS_CONST) {
168 optimizer_literal_class_info(
169 info,
170 opline->op1_type,
171 opline->op1,
172 opline->op2.constant,
173 LITERAL_STATIC_METHOD, (opline->op1_type == IS_CONST) ? 1 : 2, 2,
174 op_array);
175 }
176 break;
177 case ZEND_CATCH:
178 LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 1, 1, 2);
179 break;
180 case ZEND_DEFINED:
181 LITERAL_INFO(opline->op1.constant, LITERAL_CONST, 1, 1, 2);
182 break;
183 case ZEND_FETCH_CONSTANT:
184 if ((opline->extended_value & (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) == (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) {
185 LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 1, 1, 5);
186 } else {
187 LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 1, 1, 3);
188 }
189 break;
190 case ZEND_FETCH_CLASS_CONSTANT:
191 if (opline->op1_type == IS_CONST) {
192 LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 1, 1, 2);
193 }
194 optimizer_literal_class_info(
195 info,
196 opline->op1_type,
197 opline->op1,
198 opline->op2.constant,
199 LITERAL_CLASS_CONST, (opline->op1_type == IS_CONST) ? 1 : 2, 1,
200 op_array);
201 break;
202 case ZEND_FETCH_STATIC_PROP_R:
203 case ZEND_FETCH_STATIC_PROP_W:
204 case ZEND_FETCH_STATIC_PROP_RW:
205 case ZEND_FETCH_STATIC_PROP_IS:
206 case ZEND_FETCH_STATIC_PROP_UNSET:
207 case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
208 case ZEND_UNSET_STATIC_PROP:
209 case ZEND_ISSET_ISEMPTY_STATIC_PROP:
210 if (opline->op2_type == IS_CONST) {
211 LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 1, 1, 2);
212 }
213 if (opline->op1_type == IS_CONST) {
214 optimizer_literal_class_info(
215 info,
216 opline->op2_type,
217 opline->op2,
218 opline->op1.constant,
219 LITERAL_STATIC_PROPERTY, 2, 1,
220 op_array);
221 }
222 break;
223 case ZEND_FETCH_CLASS:
224 case ZEND_ADD_INTERFACE:
225 case ZEND_ADD_TRAIT:
226 case ZEND_INSTANCEOF:
227 if (opline->op2_type == IS_CONST) {
228 LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 1, 1, 2);
229 }
230 break;
231 case ZEND_NEW:
232 if (opline->op1_type == IS_CONST) {
233 LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 1, 1, 2);
234 }
235 break;
236 case ZEND_ASSIGN_OBJ:
237 case ZEND_FETCH_OBJ_R:
238 case ZEND_FETCH_OBJ_W:
239 case ZEND_FETCH_OBJ_RW:
240 case ZEND_FETCH_OBJ_IS:
241 case ZEND_FETCH_OBJ_UNSET:
242 case ZEND_FETCH_OBJ_FUNC_ARG:
243 case ZEND_UNSET_OBJ:
244 case ZEND_PRE_INC_OBJ:
245 case ZEND_PRE_DEC_OBJ:
246 case ZEND_POST_INC_OBJ:
247 case ZEND_POST_DEC_OBJ:
248 case ZEND_ISSET_ISEMPTY_PROP_OBJ:
249 if (opline->op2_type == IS_CONST) {
250 optimizer_literal_obj_info(
251 info,
252 opline->op1_type,
253 opline->op1,
254 opline->op2.constant,
255 LITERAL_PROPERTY, 2, 1,
256 op_array);
257 }
258 break;
259 case ZEND_ASSIGN_ADD:
260 case ZEND_ASSIGN_SUB:
261 case ZEND_ASSIGN_MUL:
262 case ZEND_ASSIGN_DIV:
263 case ZEND_ASSIGN_POW:
264 case ZEND_ASSIGN_MOD:
265 case ZEND_ASSIGN_SL:
266 case ZEND_ASSIGN_SR:
267 case ZEND_ASSIGN_CONCAT:
268 case ZEND_ASSIGN_BW_OR:
269 case ZEND_ASSIGN_BW_AND:
270 case ZEND_ASSIGN_BW_XOR:
271 if (opline->op2_type == IS_CONST) {
272 if (opline->extended_value == ZEND_ASSIGN_OBJ) {
273 optimizer_literal_obj_info(
274 info,
275 opline->op1_type,
276 opline->op1,
277 opline->op2.constant,
278 LITERAL_PROPERTY, 2, 1,
279 op_array);
280 } else {
281 LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1, 0, 1);
282 }
283 }
284 break;
285 case ZEND_BIND_GLOBAL:
286 LITERAL_INFO(opline->op2.constant, LITERAL_GLOBAL, 0, 1, 1);
287 break;
288 case ZEND_RECV_INIT:
289 LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 0, 0, 1);
290 if (Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) != (uint32_t)-1) {
291 Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) = cache_size;
292 cache_size += sizeof(void *);
293 }
294 break;
295 case ZEND_DECLARE_FUNCTION:
296 case ZEND_DECLARE_CLASS:
297 case ZEND_DECLARE_INHERITED_CLASS:
298 case ZEND_DECLARE_INHERITED_CLASS_DELAYED:
299 LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 0, 0, 2);
300 break;
301 case ZEND_RECV:
302 case ZEND_RECV_VARIADIC:
303 case ZEND_VERIFY_RETURN_TYPE:
304 if (opline->op2.num != (uint32_t)-1) {
305 opline->op2.num = cache_size;
306 cache_size += sizeof(void *);
307 }
308 default:
309 if (opline->op1_type == IS_CONST) {
310 LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1, 0, 1);
311 }
312 if (opline->op2_type == IS_CONST) {
313 LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1, 0, 1);
314 }
315 break;
316 }
317 opline++;
318 }
319
320 #if DEBUG_COMPACT_LITERALS
321 {
322 int i, use_copy;
323 fprintf(stderr, "File %s func %s\n", op_array->filename->val,
324 op_array->function_name ? op_array->function_name->val : "main");
325 fprintf(stderr, "Literlas table size %d\n", op_array->last_literal);
326
327 for (i = 0; i < op_array->last_literal; i++) {
328 zval zv;
329 ZVAL_COPY_VALUE(&zv, op_array->literals + i);
330 use_copy = zend_make_printable_zval(op_array->literals + i, &zv);
331 fprintf(stderr, "Literal %d, val (%d):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv));
332 if (use_copy) {
333 zval_ptr_dtor_nogc(&zv);
334 }
335 }
336 fflush(stderr);
337 }
338 #endif
339
340 /* Merge equal constants */
341 j = 0;
342 zend_hash_init(&hash, op_array->last_literal, NULL, NULL, 0);
343 map = (int*)zend_arena_alloc(&ctx->arena, op_array->last_literal * sizeof(int));
344 memset(map, 0, op_array->last_literal * sizeof(int));
345 for (i = 0; i < op_array->last_literal; i++) {
346 if (!info[i].flags) {
347 /* unsed literal */
348 zval_ptr_dtor_nogc(&op_array->literals[i]);
349 continue;
350 }
351 switch (Z_TYPE(op_array->literals[i])) {
352 case IS_NULL:
353 /* Only checking MAY_MERGE for IS_NULL here
354 * is because only IS_NULL can be default value for class type hinting(RECV_INIT). */
355 if ((info[i].flags & LITERAL_MAY_MERGE)) {
356 if (l_null < 0) {
357 l_null = j;
358 if (i != j) {
359 op_array->literals[j] = op_array->literals[i];
360 info[j] = info[i];
361 }
362 j++;
363 }
364 map[i] = l_null;
365 } else {
366 map[i] = j;
367 if (i != j) {
368 op_array->literals[j] = op_array->literals[i];
369 info[j] = info[i];
370 }
371 j++;
372 }
373 break;
374 case IS_FALSE:
375 if (l_false < 0) {
376 l_false = j;
377 if (i != j) {
378 op_array->literals[j] = op_array->literals[i];
379 info[j] = info[i];
380 }
381 j++;
382 }
383 map[i] = l_false;
384 break;
385 case IS_TRUE:
386 if (l_true < 0) {
387 l_true = j;
388 if (i != j) {
389 op_array->literals[j] = op_array->literals[i];
390 info[j] = info[i];
391 }
392 j++;
393 }
394 map[i] = l_true;
395 break;
396 case IS_LONG:
397 if ((pos = zend_hash_index_find(&hash, Z_LVAL(op_array->literals[i]))) != NULL) {
398 map[i] = Z_LVAL_P(pos);
399 } else {
400 map[i] = j;
401 ZVAL_LONG(&zv, j);
402 zend_hash_index_add_new(&hash, Z_LVAL(op_array->literals[i]), &zv);
403 if (i != j) {
404 op_array->literals[j] = op_array->literals[i];
405 info[j] = info[i];
406 }
407 j++;
408 }
409 break;
410 case IS_DOUBLE:
411 if ((pos = zend_hash_str_find(&hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double))) != NULL) {
412 map[i] = Z_LVAL_P(pos);
413 } else {
414 map[i] = j;
415 ZVAL_LONG(&zv, j);
416 zend_hash_str_add(&hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double), &zv);
417 if (i != j) {
418 op_array->literals[j] = op_array->literals[i];
419 info[j] = info[i];
420 }
421 j++;
422 }
423 break;
424 case IS_STRING:
425 case IS_CONSTANT:
426 if (info[i].flags & LITERAL_MAY_MERGE) {
427 if (info[i].flags & LITERAL_EX_OBJ) {
428 int key_len = sizeof("$this->") - 1 + Z_STRLEN(op_array->literals[i]);
429 key = zend_string_alloc(key_len, 0);
430 memcpy(ZSTR_VAL(key), "$this->", sizeof("$this->") - 1);
431 memcpy(ZSTR_VAL(key) + sizeof("$this->") - 1, Z_STRVAL(op_array->literals[i]), Z_STRLEN(op_array->literals[i]) + 1);
432 ZSTR_LEN(key) = key_len;
433 } else if (info[i].flags & LITERAL_EX_CLASS) {
434 int key_len;
435 zval *class_name = &op_array->literals[(info[i].u.num < i) ? map[info[i].u.num] : info[i].u.num];
436 key_len = Z_STRLEN_P(class_name) + sizeof("::") - 1 + Z_STRLEN(op_array->literals[i]);
437 key = zend_string_alloc(key_len, 0);
438 memcpy(ZSTR_VAL(key), Z_STRVAL_P(class_name), Z_STRLEN_P(class_name));
439 memcpy(ZSTR_VAL(key) + Z_STRLEN_P(class_name), "::", sizeof("::") - 1);
440 memcpy(ZSTR_VAL(key) + Z_STRLEN_P(class_name) + sizeof("::") - 1,
441 Z_STRVAL(op_array->literals[i]),
442 Z_STRLEN(op_array->literals[i]) + 1);
443 } else {
444 key = zend_string_init(Z_STRVAL(op_array->literals[i]), Z_STRLEN(op_array->literals[i]), 0);
445 }
446 ZSTR_H(key) = zend_hash_func(ZSTR_VAL(key), ZSTR_LEN(key));
447 ZSTR_H(key) += info[i].flags;
448 }
449 if ((info[i].flags & LITERAL_MAY_MERGE) &&
450 (pos = zend_hash_find(&hash, key)) != NULL &&
451 Z_TYPE(op_array->literals[i]) == Z_TYPE(op_array->literals[Z_LVAL_P(pos)]) &&
452 info[i].flags == info[Z_LVAL_P(pos)].flags) {
453
454 zend_string_release(key);
455 map[i] = Z_LVAL_P(pos);
456 zval_ptr_dtor_nogc(&op_array->literals[i]);
457 n = LITERAL_NUM_RELATED(info[i].flags);
458 while (n > 1) {
459 i++;
460 zval_ptr_dtor_nogc(&op_array->literals[i]);
461 n--;
462 }
463 } else {
464 map[i] = j;
465 if (info[i].flags & LITERAL_MAY_MERGE) {
466 ZVAL_LONG(&zv, j);
467 zend_hash_add_new(&hash, key, &zv);
468 zend_string_release(key);
469 }
470 if (i != j) {
471 op_array->literals[j] = op_array->literals[i];
472 info[j] = info[i];
473 }
474 if (LITERAL_NUM_SLOTS(info[i].flags)) {
475 Z_CACHE_SLOT(op_array->literals[j]) = cache_size;
476 cache_size += LITERAL_NUM_SLOTS(info[i].flags) * sizeof(void*);
477 }
478 j++;
479 n = LITERAL_NUM_RELATED(info[i].flags);
480 while (n > 1) {
481 i++;
482 if (i != j) op_array->literals[j] = op_array->literals[i];
483 j++;
484 n--;
485 }
486 }
487 break;
488 case IS_ARRAY:
489 if (zend_hash_num_elements(Z_ARRVAL(op_array->literals[i])) == 0) {
490 if (l_empty_arr < 0) {
491 l_empty_arr = j;
492 if (i != j) {
493 op_array->literals[j] = op_array->literals[i];
494 info[j] = info[i];
495 }
496 j++;
497 } else {
498 zval_ptr_dtor_nogc(&op_array->literals[i]);
499 }
500 map[i] = l_empty_arr;
501 break;
502 }
503 /* break missing intentionally */
504 default:
505 /* don't merge other types */
506 map[i] = j;
507 if (i != j) {
508 op_array->literals[j] = op_array->literals[i];
509 info[j] = info[i];
510 }
511 j++;
512 break;
513 }
514 }
515 zend_hash_destroy(&hash);
516 op_array->last_literal = j;
517 op_array->cache_size = cache_size;
518
519 /* Update opcodes to use new literals table */
520 opline = op_array->opcodes;
521 end = opline + op_array->last;
522 while (opline < end) {
523 if (opline->op1_type == IS_CONST) {
524 opline->op1.constant = map[opline->op1.constant];
525 }
526 if (opline->op2_type == IS_CONST) {
527 opline->op2.constant = map[opline->op2.constant];
528 }
529 opline++;
530 }
531 zend_arena_release(&ctx->arena, checkpoint);
532
533 #if DEBUG_COMPACT_LITERALS
534 {
535 int i, use_copy;
536 fprintf(stderr, "Optimized literlas table size %d\n", op_array->last_literal);
537
538 for (i = 0; i < op_array->last_literal; i++) {
539 zval zv;
540 ZVAL_COPY_VALUE(&zv, op_array->literals + i);
541 use_copy = zend_make_printable_zval(op_array->literals + i, &zv);
542 fprintf(stderr, "Literal %d, val (%d):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv));
543 if (use_copy) {
544 zval_ptr_dtor_nogc(&zv);
545 }
546 }
547 fflush(stderr);
548 }
549 #endif
550 }
551 }
552