1 /* pass 11
2 * - compact literals table
3 */
4 #if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
5
6 #define DEBUG_COMPACT_LITERALS 0
7
8 #define LITERAL_VALUE 0x0100
9 #define LITERAL_FUNC 0x0200
10 #define LITERAL_CLASS 0x0300
11 #define LITERAL_CONST 0x0400
12 #define LITERAL_CLASS_CONST 0x0500
13 #define LITERAL_STATIC_METHOD 0x0600
14 #define LITERAL_STATIC_PROPERTY 0x0700
15 #define LITERAL_METHOD 0x0800
16 #define LITERAL_PROPERTY 0x0900
17
18 #define LITERAL_EX_CLASS 0x4000
19 #define LITERAL_EX_OBJ 0x2000
20 #define LITERAL_MAY_MERGE 0x1000
21 #define LITERAL_KIND_MASK 0x0f00
22 #define LITERAL_NUM_RELATED_MASK 0x000f
23 #define LITERAL_NUM_SLOTS_MASK 0x00f0
24 #define LITERAL_NUM_SLOTS_SHIFT 4
25
26 #define LITERAL_NUM_RELATED(info) (info & LITERAL_NUM_RELATED_MASK)
27 #define LITERAL_NUM_SLOTS(info) ((info & LITERAL_NUM_SLOTS_MASK) >> LITERAL_NUM_SLOTS_SHIFT)
28
29 typedef struct _literal_info {
30 zend_uint flags; /* bitmask (see defines above) */
31 union {
32 int num; /* variable number or class name literal number */
33 } u;
34 } literal_info;
35
36 #define LITERAL_FLAGS(kind, slots, related) \
37 ((kind) | ((slots) << LITERAL_NUM_SLOTS_SHIFT) | (related))
38
39 #define LITERAL_INFO(n, kind, merge, slots, related) do { \
40 info[n].flags = (((merge) ? LITERAL_MAY_MERGE : 0) | LITERAL_FLAGS(kind, slots, related)); \
41 } while (0)
42
43 #define LITERAL_INFO_CLASS(n, kind, merge, slots, related, _num) do { \
44 info[n].flags = (LITERAL_EX_CLASS | ((merge) ? LITERAL_MAY_MERGE : 0) | LITERAL_FLAGS(kind, slots, related)); \
45 info[n].u.num = (_num); \
46 } while (0)
47
48 #define LITERAL_INFO_OBJ(n, kind, merge, slots, related, _num) do { \
49 info[n].flags = (LITERAL_EX_OBJ | ((merge) ? LITERAL_MAY_MERGE : 0) | LITERAL_FLAGS(kind, slots, related)); \
50 info[n].u.num = (_num); \
51 } while (0)
52
optimizer_literal_obj_info(literal_info * info,zend_uchar op_type,znode_op op,int constant,zend_uint kind,zend_uint slots,zend_uint related,zend_op_array * op_array)53 static void optimizer_literal_obj_info(literal_info *info,
54 zend_uchar op_type,
55 znode_op op,
56 int constant,
57 zend_uint kind,
58 zend_uint slots,
59 zend_uint related,
60 zend_op_array *op_array)
61 {
62 /* For now we merge only $this object properties and methods.
63 * In general it's also possible to do it for any CV variable as well,
64 * but it would require complex dataflow and/or type analysis.
65 */
66 if (Z_TYPE(op_array->literals[constant].constant) == IS_STRING &&
67 op_type == IS_UNUSED) {
68 LITERAL_INFO_OBJ(constant, kind, 1, slots, related, op_array->this_var);
69 } else {
70 LITERAL_INFO(constant, kind, 0, slots, related);
71 }
72 }
73
optimizer_literal_class_info(literal_info * info,zend_uchar op_type,znode_op op,int constant,zend_uint kind,zend_uint slots,zend_uint related,zend_op_array * op_array)74 static void optimizer_literal_class_info(literal_info *info,
75 zend_uchar op_type,
76 znode_op op,
77 int constant,
78 zend_uint kind,
79 zend_uint slots,
80 zend_uint related,
81 zend_op_array *op_array)
82 {
83 if (op_type == IS_CONST) {
84 LITERAL_INFO_CLASS(constant, kind, 1, slots, related, op.constant);
85 } else {
86 LITERAL_INFO(constant, kind, 0, slots, related);
87 }
88 }
89
optimizer_compact_literals(zend_op_array * op_array TSRMLS_DC)90 static void optimizer_compact_literals(zend_op_array *op_array TSRMLS_DC)
91 {
92 zend_op *opline, *end;
93 int i, j, n, *pos, *map, cache_slots;
94 ulong h;
95 literal_info *info;
96 int l_null = -1;
97 int l_false = -1;
98 int l_true = -1;
99 HashTable hash;
100 char *key;
101 int key_len;
102
103 if (op_array->last_literal) {
104 info = (literal_info*)ecalloc(op_array->last_literal, sizeof(literal_info));
105
106 /* Mark literals of specific types */
107 opline = op_array->opcodes;
108 end = opline + op_array->last;
109 while (opline < end) {
110 switch (opline->opcode) {
111 case ZEND_DO_FCALL:
112 LITERAL_INFO(opline->op1.constant, LITERAL_FUNC, 1, 1, 1);
113 break;
114 case ZEND_INIT_FCALL_BY_NAME:
115 if (ZEND_OP2_TYPE(opline) == IS_CONST) {
116 LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 1, 1, 2);
117 }
118 break;
119 case ZEND_INIT_NS_FCALL_BY_NAME:
120 LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 1, 1, 3);
121 break;
122 case ZEND_INIT_METHOD_CALL:
123 if (ZEND_OP2_TYPE(opline) == IS_CONST) {
124 optimizer_literal_obj_info(
125 info,
126 opline->op1_type,
127 opline->op1,
128 opline->op2.constant,
129 LITERAL_METHOD, 2, 2,
130 op_array);
131 }
132 break;
133 case ZEND_INIT_STATIC_METHOD_CALL:
134 if (ZEND_OP1_TYPE(opline) == IS_CONST) {
135 LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 1, 1, 2);
136 }
137 if (ZEND_OP2_TYPE(opline) == IS_CONST) {
138 optimizer_literal_class_info(
139 info,
140 opline->op1_type,
141 opline->op1,
142 opline->op2.constant,
143 LITERAL_STATIC_METHOD, (ZEND_OP1_TYPE(opline) == IS_CONST) ? 1 : 2, 2,
144 op_array);
145 }
146 break;
147 case ZEND_CATCH:
148 LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 1, 1, 2);
149 break;
150 case ZEND_FETCH_CONSTANT:
151 if (ZEND_OP1_TYPE(opline) == IS_UNUSED) {
152 if ((opline->extended_value & (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) == (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) {
153 LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 1, 1, 5);
154 } else {
155 LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 1, 1, 3);
156 }
157 } else {
158 if (ZEND_OP1_TYPE(opline) == IS_CONST) {
159 LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 1, 1, 2);
160 }
161 optimizer_literal_class_info(
162 info,
163 opline->op1_type,
164 opline->op1,
165 opline->op2.constant,
166 LITERAL_CLASS_CONST, (ZEND_OP1_TYPE(opline) == IS_CONST) ? 1 : 2, 1,
167 op_array);
168 }
169 break;
170 case ZEND_FETCH_R:
171 case ZEND_FETCH_W:
172 case ZEND_FETCH_RW:
173 case ZEND_FETCH_IS:
174 case ZEND_FETCH_UNSET:
175 case ZEND_FETCH_FUNC_ARG:
176 case ZEND_UNSET_VAR:
177 case ZEND_ISSET_ISEMPTY_VAR:
178 if (ZEND_OP2_TYPE(opline) == IS_UNUSED) {
179 if (ZEND_OP1_TYPE(opline) == IS_CONST) {
180 LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1, 0, 1);
181 }
182 } else {
183 if (ZEND_OP2_TYPE(opline) == IS_CONST) {
184 LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 1, 1, 2);
185 }
186 if (ZEND_OP1_TYPE(opline) == IS_CONST) {
187 optimizer_literal_class_info(
188 info,
189 opline->op2_type,
190 opline->op2,
191 opline->op1.constant,
192 LITERAL_STATIC_PROPERTY, 2, 1,
193 op_array);
194 }
195 }
196 break;
197 case ZEND_FETCH_CLASS:
198 case ZEND_ADD_INTERFACE:
199 case ZEND_ADD_TRAIT:
200 if (ZEND_OP2_TYPE(opline) == IS_CONST) {
201 LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 1, 1, 2);
202 }
203 break;
204 case ZEND_ASSIGN_OBJ:
205 case ZEND_FETCH_OBJ_R:
206 case ZEND_FETCH_OBJ_W:
207 case ZEND_FETCH_OBJ_RW:
208 case ZEND_FETCH_OBJ_IS:
209 case ZEND_FETCH_OBJ_UNSET:
210 case ZEND_FETCH_OBJ_FUNC_ARG:
211 case ZEND_UNSET_OBJ:
212 case ZEND_PRE_INC_OBJ:
213 case ZEND_PRE_DEC_OBJ:
214 case ZEND_POST_INC_OBJ:
215 case ZEND_POST_DEC_OBJ:
216 case ZEND_ISSET_ISEMPTY_PROP_OBJ:
217 if (ZEND_OP2_TYPE(opline) == IS_CONST) {
218 optimizer_literal_obj_info(
219 info,
220 opline->op1_type,
221 opline->op1,
222 opline->op2.constant,
223 LITERAL_PROPERTY, 2, 1,
224 op_array);
225 }
226 break;
227 case ZEND_ASSIGN_ADD:
228 case ZEND_ASSIGN_SUB:
229 case ZEND_ASSIGN_MUL:
230 case ZEND_ASSIGN_DIV:
231 case ZEND_ASSIGN_MOD:
232 case ZEND_ASSIGN_SL:
233 case ZEND_ASSIGN_SR:
234 case ZEND_ASSIGN_CONCAT:
235 case ZEND_ASSIGN_BW_OR:
236 case ZEND_ASSIGN_BW_AND:
237 case ZEND_ASSIGN_BW_XOR:
238 if (ZEND_OP2_TYPE(opline) == IS_CONST) {
239 if (opline->extended_value == ZEND_ASSIGN_OBJ) {
240 optimizer_literal_obj_info(
241 info,
242 opline->op1_type,
243 opline->op1,
244 opline->op2.constant,
245 LITERAL_PROPERTY, 2, 1,
246 op_array);
247 } else {
248 LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1, 0, 1);
249 }
250 }
251 break;
252 default:
253 if (ZEND_OP1_TYPE(opline) == IS_CONST) {
254 LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1, 0, 1);
255 }
256 if (ZEND_OP2_TYPE(opline) == IS_CONST) {
257 LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1, 0, 1);
258 }
259 break;
260 }
261 opline++;
262 }
263
264 #if DEBUG_COMPACT_LITERALS
265 {
266 int i, use_copy;
267 fprintf(stderr, "File %s func %s\n", op_array->filename,
268 op_array->function_name? op_array->function_name : "main");
269 fprintf(stderr, "Literlas table size %d\n", op_array->last_literal);
270
271 for (i = 0; i < op_array->last_literal; i++) {
272 zval zv = op_array->literals[i].constant;
273 zend_make_printable_zval(&op_array->literals[i].constant, &zv, &use_copy);
274 fprintf(stderr, "Literal %d, val (%d):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv));
275 if (use_copy) {
276 zval_dtor(&zv);
277 }
278 }
279 fflush(stderr);
280 }
281 #endif
282
283 /* Merge equal constants */
284 j = 0; cache_slots = 0;
285 zend_hash_init(&hash, 16, NULL, NULL, 0);
286 map = (int*)ecalloc(op_array->last_literal, sizeof(int));
287 for (i = 0; i < op_array->last_literal; i++) {
288 if (!info[i].flags) {
289 /* unsed literal */
290 zval_dtor(&op_array->literals[i].constant);
291 continue;
292 }
293 switch (Z_TYPE(op_array->literals[i].constant)) {
294 case IS_NULL:
295 if (l_null < 0) {
296 l_null = j;
297 if (i != j) {
298 op_array->literals[j] = op_array->literals[i];
299 info[j] = info[i];
300 }
301 j++;
302 }
303 map[i] = l_null;
304 break;
305 case IS_BOOL:
306 if (Z_LVAL(op_array->literals[i].constant)) {
307 if (l_true < 0) {
308 l_true = j;
309 if (i != j) {
310 op_array->literals[j] = op_array->literals[i];
311 info[j] = info[i];
312 }
313 j++;
314 }
315 map[i] = l_true;
316 } else {
317 if (l_false < 0) {
318 l_false = j;
319 if (i != j) {
320 op_array->literals[j] = op_array->literals[i];
321 info[j] = info[i];
322 }
323 j++;
324 }
325 map[i] = l_false;
326 }
327 break;
328 case IS_LONG:
329 if (zend_hash_index_find(&hash, Z_LVAL(op_array->literals[i].constant), (void**)&pos) == SUCCESS) {
330 map[i] = *pos;
331 } else {
332 map[i] = j;
333 zend_hash_index_update(&hash, Z_LVAL(op_array->literals[i].constant), (void**)&j, sizeof(int), NULL);
334 if (i != j) {
335 op_array->literals[j] = op_array->literals[i];
336 info[j] = info[i];
337 }
338 j++;
339 }
340 break;
341 case IS_DOUBLE:
342 if (zend_hash_find(&hash, (char*)&Z_DVAL(op_array->literals[i].constant), sizeof(double), (void**)&pos) == SUCCESS) {
343 map[i] = *pos;
344 } else {
345 map[i] = j;
346 zend_hash_add(&hash, (char*)&Z_DVAL(op_array->literals[i].constant), sizeof(double), (void**)&j, sizeof(int), NULL);
347 if (i != j) {
348 op_array->literals[j] = op_array->literals[i];
349 info[j] = info[i];
350 }
351 j++;
352 }
353 break;
354 case IS_STRING:
355 case IS_CONSTANT:
356 if (info[i].flags & LITERAL_MAY_MERGE) {
357 if (info[i].flags & LITERAL_EX_OBJ) {
358 key_len = MAX_LENGTH_OF_LONG + sizeof("->") + Z_STRLEN(op_array->literals[i].constant);
359 key = emalloc(key_len);
360 key_len = snprintf(key, key_len-1, "%d->%s", info[i].u.num, Z_STRVAL(op_array->literals[i].constant));
361 } else if (info[i].flags & LITERAL_EX_CLASS) {
362 zval *class_name = &op_array->literals[(info[i].u.num < i) ? map[info[i].u.num] : info[i].u.num].constant;
363 key_len = Z_STRLEN_P(class_name) + sizeof("::") + Z_STRLEN(op_array->literals[i].constant);
364 key = emalloc(key_len);
365 memcpy(key, Z_STRVAL_P(class_name), Z_STRLEN_P(class_name));
366 memcpy(key + Z_STRLEN_P(class_name), "::", sizeof("::") - 1);
367 memcpy(key + Z_STRLEN_P(class_name) + sizeof("::") - 1,
368 Z_STRVAL(op_array->literals[i].constant),
369 Z_STRLEN(op_array->literals[i].constant) + 1);
370 } else {
371 key = Z_STRVAL(op_array->literals[i].constant);
372 key_len = Z_STRLEN(op_array->literals[i].constant)+1;
373 }
374 h = zend_hash_func(key, key_len);
375 h += info[i].flags;
376 }
377 if ((info[i].flags & LITERAL_MAY_MERGE) &&
378 zend_hash_quick_find(&hash, key, key_len, h, (void**)&pos) == SUCCESS &&
379 Z_TYPE(op_array->literals[i].constant) == Z_TYPE(op_array->literals[*pos].constant) &&
380 info[i].flags == info[*pos].flags) {
381
382 if (info[i].flags & (LITERAL_EX_OBJ|LITERAL_EX_CLASS)) {
383 efree(key);
384 }
385 map[i] = *pos;
386 zval_dtor(&op_array->literals[i].constant);
387 n = LITERAL_NUM_RELATED(info[i].flags);
388 while (n > 1) {
389 i++;
390 zval_dtor(&op_array->literals[i].constant);
391 n--;
392 }
393 } else {
394 map[i] = j;
395 if (info[i].flags & LITERAL_MAY_MERGE) {
396 zend_hash_quick_add(&hash, key, key_len, h, (void**)&j, sizeof(int), NULL);
397 if (info[i].flags & (LITERAL_EX_OBJ|LITERAL_EX_CLASS)) {
398 efree(key);
399 }
400 }
401 if (i != j) {
402 op_array->literals[j] = op_array->literals[i];
403 info[j] = info[i];
404 }
405 if (!op_array->literals[j].hash_value) {
406 if (IS_INTERNED(Z_STRVAL(op_array->literals[j].constant))) {
407 op_array->literals[j].hash_value = INTERNED_HASH(Z_STRVAL(op_array->literals[j].constant));
408 } else {
409 op_array->literals[j].hash_value = zend_hash_func(Z_STRVAL(op_array->literals[j].constant), Z_STRLEN(op_array->literals[j].constant)+1);
410 }
411 }
412 if (LITERAL_NUM_SLOTS(info[i].flags)) {
413 op_array->literals[j].cache_slot = cache_slots;
414 cache_slots += LITERAL_NUM_SLOTS(info[i].flags);
415 }
416 j++;
417 n = LITERAL_NUM_RELATED(info[i].flags);
418 while (n > 1) {
419 i++;
420 if (i != j) op_array->literals[j] = op_array->literals[i];
421 if (!op_array->literals[j].hash_value) {
422 if (IS_INTERNED(Z_STRVAL(op_array->literals[j].constant))) {
423 op_array->literals[j].hash_value = INTERNED_HASH(Z_STRVAL(op_array->literals[j].constant));
424 } else {
425 op_array->literals[j].hash_value = zend_hash_func(Z_STRVAL(op_array->literals[j].constant), Z_STRLEN(op_array->literals[j].constant)+1);
426 }
427 }
428 j++;
429 n--;
430 }
431 }
432 break;
433 default:
434 /* don't merge other types */
435 map[i] = j;
436 if (i != j) {
437 op_array->literals[j] = op_array->literals[i];
438 info[j] = info[i];
439 }
440 j++;
441 break;
442 }
443 }
444 zend_hash_destroy(&hash);
445 op_array->last_literal = j;
446 op_array->last_cache_slot = cache_slots;
447
448 /* Update opcodes to use new literals table */
449 opline = op_array->opcodes;
450 end = opline + op_array->last;
451 while (opline < end) {
452 if (ZEND_OP1_TYPE(opline) == IS_CONST) {
453 opline->op1.constant = map[opline->op1.constant];
454 }
455 if (ZEND_OP2_TYPE(opline) == IS_CONST) {
456 opline->op2.constant = map[opline->op2.constant];
457 }
458 opline++;
459 }
460 efree(map);
461 efree(info);
462
463 #if DEBUG_COMPACT_LITERALS
464 {
465 int i, use_copy;
466 fprintf(stderr, "Optimized literlas table size %d\n", op_array->last_literal);
467
468 for (i = 0; i < op_array->last_literal; i++) {
469 zval zv = op_array->literals[i].constant;
470 zend_make_printable_zval(&op_array->literals[i].constant, &zv, &use_copy);
471 fprintf(stderr, "Literal %d, val (%d):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv));
472 if (use_copy) {
473 zval_dtor(&zv);
474 }
475 }
476 fflush(stderr);
477 }
478 #endif
479 }
480 }
481 #endif
482