Coverage Report

Created: 2025-09-27 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/Zend/Optimizer/compact_literals.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Zend OPcache                                                         |
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
   |          Xinchen Hui <laruence@php.net>                              |
17
   +----------------------------------------------------------------------+
18
*/
19
20
/* pass 11
21
 * - compact literals table
22
 */
23
24
#include "Optimizer/zend_optimizer.h"
25
#include "Optimizer/zend_optimizer_internal.h"
26
#include "zend_API.h"
27
#include "zend_constants.h"
28
#include "zend_execute.h"
29
#include "zend_vm.h"
30
#include "zend_extensions.h"
31
32
#define DEBUG_COMPACT_LITERALS 0
33
34
2.62k
#define LITERAL_CLASS_CONST 1
35
5.09k
#define LITERAL_STATIC_METHOD 2
36
11.1k
#define LITERAL_STATIC_PROPERTY 3
37
38
typedef struct _literal_info {
39
  uint8_t num_related;
40
} literal_info;
41
42
1.05M
#define LITERAL_INFO(n, related) do { \
43
1.05M
    info[n].num_related = (related); \
44
1.05M
  } while (0)
45
46
static uint32_t add_static_slot(HashTable     *hash,
47
                                zend_op_array *op_array,
48
                                uint32_t       op1,
49
                                uint32_t       op2,
50
                                uint32_t       kind,
51
                                uint32_t       *cache_size)
52
11.2k
{
53
11.2k
  uint32_t ret;
54
11.2k
  zval *class_name = &op_array->literals[op1];
55
11.2k
  zval *prop_name = &op_array->literals[op2];
56
11.2k
  zval *pos, tmp;
57
58
11.2k
  zend_string *key = zend_create_member_string(Z_STR_P(class_name), Z_STR_P(prop_name));
59
11.2k
  ZSTR_H(key) = zend_string_hash_func(key);
60
11.2k
  ZSTR_H(key) += kind;
61
62
11.2k
  pos = zend_hash_find(hash, key);
63
11.2k
  if (pos) {
64
3.64k
    ret = Z_LVAL_P(pos);
65
7.60k
  } else {
66
7.60k
    ret = *cache_size;
67
7.60k
    *cache_size += (kind == LITERAL_STATIC_PROPERTY ? 3 : 2) * sizeof(void *);
68
7.60k
    ZVAL_LONG(&tmp, ret);
69
7.60k
    zend_hash_add(hash, key, &tmp);
70
7.60k
  }
71
11.2k
  zend_string_release_ex(key, 0);
72
11.2k
  return ret;
73
11.2k
}
74
75
static inline void bias_key(zend_string *key, uint32_t bias)
76
235k
{
77
  /* Add a bias to the hash so we can distinguish string keys
78
   * that would otherwise be the same. */
79
235k
  ZSTR_H(key) = zend_string_hash_val(key) + bias;
80
235k
}
81
82
static zend_string *create_str_cache_key(zval *literal, uint8_t num_related)
83
824k
{
84
824k
  ZEND_ASSERT(Z_TYPE_P(literal) == IS_STRING);
85
824k
  if (num_related == 1) {
86
598k
    return zend_string_copy(Z_STR_P(literal));
87
598k
  }
88
89
  /* Concatenate all the related literals for the cache key. */
90
226k
  zend_string *key;
91
226k
  if (num_related == 2) {
92
223k
    ZEND_ASSERT(Z_TYPE_P(literal + 1) == IS_STRING);
93
223k
    key = zend_string_concat2(
94
223k
      Z_STRVAL_P(literal), Z_STRLEN_P(literal),
95
223k
      Z_STRVAL_P(literal + 1), Z_STRLEN_P(literal + 1));
96
223k
  } else if (num_related == 3) {
97
3.02k
    ZEND_ASSERT(Z_TYPE_P(literal + 1) == IS_STRING && Z_TYPE_P(literal + 2) == IS_STRING);
98
3.02k
    key = zend_string_concat3(
99
3.02k
      Z_STRVAL_P(literal), Z_STRLEN_P(literal),
100
3.02k
      Z_STRVAL_P(literal + 1), Z_STRLEN_P(literal + 1),
101
3.02k
      Z_STRVAL_P(literal + 2), Z_STRLEN_P(literal + 2));
102
3.02k
  } else {
103
0
    ZEND_ASSERT(0 && "Currently not needed");
104
0
  }
105
106
226k
  bias_key(key, num_related - 1);
107
226k
  return key;
108
226k
}
109
110
void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx)
111
102k
{
112
102k
  zend_op *opline, *end;
113
102k
  int i, j, n, *map;
114
102k
  uint32_t cache_size;
115
102k
  zval zv, *pos;
116
102k
  literal_info *info;
117
102k
  int l_null = -1;
118
102k
  int l_false = -1;
119
102k
  int l_true = -1;
120
102k
  int l_empty_arr = -1;
121
102k
  HashTable hash;
122
102k
  zend_string *key = NULL;
123
102k
  void *checkpoint = zend_arena_checkpoint(ctx->arena);
124
102k
  int *const_slot, *class_slot, *func_slot, *bind_var_slot, *property_slot, *method_slot, *jmp_slot;
125
126
102k
  if (op_array->last_literal) {
127
102k
    info = (literal_info*)zend_arena_calloc(&ctx->arena, op_array->last_literal, sizeof(literal_info));
128
129
      /* Mark literals of specific types */
130
102k
    opline = op_array->opcodes;
131
102k
    end = opline + op_array->last;
132
2.59M
    while (opline < end) {
133
2.49M
      switch (opline->opcode) {
134
0
        case ZEND_JMP_FRAMELESS:
135
0
          LITERAL_INFO(opline->op1.constant, 1);
136
0
          break;
137
7.06k
        case ZEND_INIT_FCALL_BY_NAME:
138
7.06k
          LITERAL_INFO(opline->op2.constant, 2);
139
7.06k
          break;
140
2.58k
        case ZEND_INIT_NS_FCALL_BY_NAME:
141
2.58k
          LITERAL_INFO(opline->op2.constant, 3);
142
2.58k
          break;
143
42.8k
        case ZEND_INIT_METHOD_CALL:
144
42.8k
          if (opline->op1_type == IS_CONST) {
145
202
            LITERAL_INFO(opline->op1.constant, 1);
146
202
          }
147
42.8k
          if (opline->op2_type == IS_CONST) {
148
42.6k
            LITERAL_INFO(opline->op2.constant, 2);
149
42.6k
          }
150
42.8k
          break;
151
6.25k
        case ZEND_INIT_STATIC_METHOD_CALL:
152
6.25k
          if (opline->op1_type == IS_CONST) {
153
5.21k
            LITERAL_INFO(opline->op1.constant, 2);
154
5.21k
          }
155
6.25k
          if (opline->op2_type == IS_CONST) {
156
5.77k
            LITERAL_INFO(opline->op2.constant, 2);
157
5.77k
          }
158
6.25k
          break;
159
190
        case ZEND_INIT_PARENT_PROPERTY_HOOK_CALL:
160
190
          LITERAL_INFO(opline->op1.constant, 1);
161
190
          break;
162
36.0k
        case ZEND_CATCH:
163
36.0k
          LITERAL_INFO(opline->op1.constant, 2);
164
36.0k
          break;
165
54.4k
        case ZEND_FETCH_CONSTANT:
166
54.4k
          if (opline->op1.num & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) {
167
436
            LITERAL_INFO(opline->op2.constant, 3);
168
54.0k
          } else {
169
54.0k
            LITERAL_INFO(opline->op2.constant, 2);
170
54.0k
          }
171
54.4k
          break;
172
3.14k
        case ZEND_FETCH_CLASS_CONSTANT:
173
3.14k
          if (opline->op1_type == IS_CONST) {
174
2.66k
            LITERAL_INFO(opline->op1.constant, 2);
175
2.66k
          }
176
3.14k
          if (opline->op2_type == IS_CONST) {
177
3.07k
            LITERAL_INFO(opline->op2.constant, 1);
178
3.07k
          }
179
3.14k
          break;
180
1.18k
        case ZEND_ASSIGN_STATIC_PROP:
181
1.42k
        case ZEND_ASSIGN_STATIC_PROP_REF:
182
3.68k
        case ZEND_FETCH_STATIC_PROP_R:
183
4.05k
        case ZEND_FETCH_STATIC_PROP_W:
184
4.05k
        case ZEND_FETCH_STATIC_PROP_RW:
185
4.21k
        case ZEND_FETCH_STATIC_PROP_IS:
186
4.26k
        case ZEND_FETCH_STATIC_PROP_UNSET:
187
4.62k
        case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
188
4.64k
        case ZEND_UNSET_STATIC_PROP:
189
4.74k
        case ZEND_ISSET_ISEMPTY_STATIC_PROP:
190
4.79k
        case ZEND_PRE_INC_STATIC_PROP:
191
4.79k
        case ZEND_PRE_DEC_STATIC_PROP:
192
4.86k
        case ZEND_POST_INC_STATIC_PROP:
193
4.89k
        case ZEND_POST_DEC_STATIC_PROP:
194
4.97k
        case ZEND_ASSIGN_STATIC_PROP_OP:
195
4.97k
          if (opline->op2_type == IS_CONST) {
196
3.74k
            LITERAL_INFO(opline->op2.constant, 2);
197
3.74k
          }
198
4.97k
          if (opline->op1_type == IS_CONST) {
199
4.72k
            LITERAL_INFO(opline->op1.constant, 1);
200
4.72k
          }
201
4.97k
          break;
202
1.30k
        case ZEND_FETCH_CLASS:
203
1.65k
        case ZEND_INSTANCEOF:
204
1.65k
          if (opline->op2_type == IS_CONST) {
205
297
            LITERAL_INFO(opline->op2.constant, 2);
206
297
          }
207
1.65k
          break;
208
61.3k
        case ZEND_NEW:
209
61.3k
          if (opline->op1_type == IS_CONST) {
210
60.0k
            LITERAL_INFO(opline->op1.constant, 2);
211
60.0k
          }
212
61.3k
          break;
213
4.83k
        case ZEND_DECLARE_CLASS:
214
6.20k
        case ZEND_DECLARE_CLASS_DELAYED:
215
6.20k
          LITERAL_INFO(opline->op1.constant, 2);
216
6.20k
          if (opline->op2_type == IS_CONST) {
217
2.05k
            LITERAL_INFO(opline->op2.constant, 1);
218
2.05k
          }
219
6.20k
          break;
220
2.39k
        case ZEND_ISSET_ISEMPTY_DIM_OBJ:
221
11.4k
        case ZEND_ASSIGN_DIM:
222
12.5k
        case ZEND_UNSET_DIM:
223
21.4k
        case ZEND_FETCH_DIM_R:
224
24.1k
        case ZEND_FETCH_DIM_W:
225
24.9k
        case ZEND_FETCH_DIM_RW:
226
26.0k
        case ZEND_FETCH_DIM_IS:
227
26.7k
        case ZEND_FETCH_DIM_FUNC_ARG:
228
26.9k
        case ZEND_FETCH_DIM_UNSET:
229
28.1k
        case ZEND_FETCH_LIST_R:
230
28.4k
        case ZEND_FETCH_LIST_W:
231
29.8k
        case ZEND_ASSIGN_DIM_OP:
232
29.8k
          if (opline->op1_type == IS_CONST) {
233
369
            LITERAL_INFO(opline->op1.constant, 1);
234
369
          }
235
29.8k
          if (opline->op2_type == IS_CONST) {
236
17.9k
            if (Z_EXTRA(op_array->literals[opline->op2.constant]) == ZEND_EXTRA_VALUE) {
237
599
              LITERAL_INFO(opline->op2.constant, 2);
238
17.3k
            } else {
239
17.3k
              LITERAL_INFO(opline->op2.constant, 1);
240
17.3k
            }
241
17.9k
          }
242
29.8k
          break;
243
2.23M
        default:
244
2.23M
          if (opline->op1_type == IS_CONST) {
245
328k
            LITERAL_INFO(opline->op1.constant, 1);
246
328k
          }
247
2.23M
          if (opline->op2_type == IS_CONST) {
248
476k
            LITERAL_INFO(opline->op2.constant, 1);
249
476k
          }
250
2.23M
          break;
251
2.49M
      }
252
2.49M
      opline++;
253
2.49M
    }
254
255
#if DEBUG_COMPACT_LITERALS
256
    {
257
      fprintf(stderr, "File %s func %s\n", op_array->filename->val,
258
          op_array->function_name ? op_array->function_name->val : "main");
259
      fprintf(stderr, "Literals table size %d\n", op_array->last_literal);
260
261
      for (int i = 0; i < op_array->last_literal; i++) {
262
        zend_string *str = zval_get_string(op_array->literals + i);
263
        fprintf(stderr, "Literal %d, val (%zu):%s\n", i, ZSTR_LEN(str), ZSTR_VAL(str));
264
        zend_string_release(str);
265
      }
266
      fflush(stderr);
267
    }
268
#endif
269
270
    /* Merge equal constants */
271
102k
    j = 0;
272
102k
    zend_hash_init(&hash, op_array->last_literal, NULL, NULL, 0);
273
102k
    map = (int*)zend_arena_alloc(&ctx->arena, op_array->last_literal * sizeof(int));
274
102k
    memset(map, 0, op_array->last_literal * sizeof(int));
275
1.22M
    for (i = 0; i < op_array->last_literal; i++) {
276
1.11M
      if (!info[i].num_related) {
277
        /* unset literal */
278
60.3k
        zval_ptr_dtor_nogc(&op_array->literals[i]);
279
60.3k
        continue;
280
60.3k
      }
281
1.05M
      switch (Z_TYPE(op_array->literals[i])) {
282
36.7k
        case IS_NULL:
283
36.7k
          ZEND_ASSERT(info[i].num_related == 1);
284
36.7k
          if (l_null < 0) {
285
31.5k
            l_null = j;
286
31.5k
            if (i != j) {
287
8.46k
              op_array->literals[j] = op_array->literals[i];
288
8.46k
              info[j] = info[i];
289
8.46k
            }
290
31.5k
            j++;
291
31.5k
          }
292
36.7k
          map[i] = l_null;
293
36.7k
          break;
294
3.57k
        case IS_FALSE:
295
3.57k
          ZEND_ASSERT(info[i].num_related == 1);
296
3.57k
          if (l_false < 0) {
297
2.09k
            l_false = j;
298
2.09k
            if (i != j) {
299
794
              op_array->literals[j] = op_array->literals[i];
300
794
              info[j] = info[i];
301
794
            }
302
2.09k
            j++;
303
2.09k
          }
304
3.57k
          map[i] = l_false;
305
3.57k
          break;
306
3.97k
        case IS_TRUE:
307
3.97k
          ZEND_ASSERT(info[i].num_related == 1);
308
3.97k
          if (l_true < 0) {
309
2.37k
            l_true = j;
310
2.37k
            if (i != j) {
311
991
              op_array->literals[j] = op_array->literals[i];
312
991
              info[j] = info[i];
313
991
            }
314
2.37k
            j++;
315
2.37k
          }
316
3.97k
          map[i] = l_true;
317
3.97k
          break;
318
161k
        case IS_LONG:
319
161k
          if (info[i].num_related == 1) {
320
160k
            if ((pos = zend_hash_index_find(&hash, Z_LVAL(op_array->literals[i]))) != NULL) {
321
48.2k
              map[i] = Z_LVAL_P(pos);
322
112k
            } else {
323
112k
              map[i] = j;
324
112k
              ZVAL_LONG(&zv, j);
325
112k
              zend_hash_index_add_new(&hash, Z_LVAL(op_array->literals[i]), &zv);
326
112k
              if (i != j) {
327
39.4k
                op_array->literals[j] = op_array->literals[i];
328
39.4k
                info[j] = info[i];
329
39.4k
              }
330
112k
              j++;
331
112k
            }
332
160k
          } else {
333
599
            ZEND_ASSERT(info[i].num_related == 2);
334
599
            key = zend_string_init(Z_STRVAL(op_array->literals[i+1]), Z_STRLEN(op_array->literals[i+1]), 0);
335
599
            bias_key(key, 100 + info[i].num_related - 1);
336
599
            if ((pos = zend_hash_find(&hash, key)) != NULL) {
337
256
              ZEND_ASSERT(info[Z_LVAL_P(pos)].num_related == 2);
338
256
              map[i] = Z_LVAL_P(pos);
339
256
              zval_ptr_dtor_nogc(&op_array->literals[i+1]);
340
343
            } else {
341
343
              map[i] = j;
342
343
              ZVAL_LONG(&zv, j);
343
343
              zend_hash_add_new(&hash, key, &zv);
344
343
              if (i != j) {
345
273
                op_array->literals[j] = op_array->literals[i];
346
273
                info[j] = info[i];
347
273
                op_array->literals[j+1] = op_array->literals[i+1];
348
273
                info[j+1] = info[i+1];
349
273
              }
350
343
              j += 2;
351
343
            }
352
599
            zend_string_release_ex(key, 0);
353
599
            i++;
354
599
          }
355
161k
          break;
356
161k
        case IS_DOUBLE:
357
8.27k
          ZEND_ASSERT(info[i].num_related == 1);
358
8.27k
          key = zend_string_init((char*)&Z_DVAL(op_array->literals[i]), sizeof(double), 0);
359
8.27k
          bias_key(key, 200);
360
8.27k
          if ((pos = zend_hash_find(&hash, key))) {
361
1.83k
            map[i] = Z_LVAL_P(pos);
362
6.43k
          } else {
363
6.43k
            map[i] = j;
364
6.43k
            ZVAL_LONG(&zv, j);
365
6.43k
            zend_hash_add_new(&hash, key, &zv);
366
6.43k
            if (i != j) {
367
3.28k
              op_array->literals[j] = op_array->literals[i];
368
3.28k
              info[j] = info[i];
369
3.28k
            }
370
6.43k
            j++;
371
6.43k
          }
372
8.27k
          zend_string_release_ex(key, 0);
373
8.27k
          break;
374
824k
        case IS_STRING: {
375
824k
          key = create_str_cache_key(&op_array->literals[i], info[i].num_related);
376
824k
          if ((pos = zend_hash_find(&hash, key)) != NULL) {
377
321k
            ZEND_ASSERT(Z_TYPE(op_array->literals[Z_LVAL_P(pos)]) == IS_STRING &&
378
321k
              info[i].num_related == info[Z_LVAL_P(pos)].num_related);
379
321k
            zend_string_release_ex(key, 0);
380
321k
            map[i] = Z_LVAL_P(pos);
381
321k
            zval_ptr_dtor_nogc(&op_array->literals[i]);
382
321k
            n = info[i].num_related;
383
407k
            while (n > 1) {
384
86.3k
              i++;
385
86.3k
              zval_ptr_dtor_nogc(&op_array->literals[i]);
386
86.3k
              n--;
387
86.3k
            }
388
503k
          } else {
389
503k
            map[i] = j;
390
503k
            ZVAL_LONG(&zv, j);
391
503k
            zend_hash_add_new(&hash, key, &zv);
392
503k
            zend_string_release_ex(key, 0);
393
503k
            if (i != j) {
394
143k
              op_array->literals[j] = op_array->literals[i];
395
143k
              info[j] = info[i];
396
143k
            }
397
503k
            j++;
398
503k
            n = info[i].num_related;
399
646k
            while (n > 1) {
400
143k
              i++;
401
143k
              if (i != j) op_array->literals[j] = op_array->literals[i];
402
143k
              j++;
403
143k
              n--;
404
143k
            }
405
503k
          }
406
824k
          break;
407
824k
        }
408
824k
        case IS_ARRAY:
409
19.0k
          ZEND_ASSERT(info[i].num_related == 1);
410
19.0k
          if (zend_hash_num_elements(Z_ARRVAL(op_array->literals[i])) == 0) {
411
3.04k
            if (l_empty_arr < 0) {
412
2.43k
              l_empty_arr = j;
413
2.43k
              if (i != j) {
414
1.07k
                op_array->literals[j] = op_array->literals[i];
415
1.07k
                info[j] = info[i];
416
1.07k
              }
417
2.43k
              j++;
418
2.43k
            } else {
419
610
              zval_ptr_dtor_nogc(&op_array->literals[i]);
420
610
            }
421
3.04k
            map[i] = l_empty_arr;
422
3.04k
            break;
423
3.04k
          }
424
16.0k
          ZEND_FALLTHROUGH;
425
17.8k
        default:
426
          /* don't merge other types */
427
17.8k
          ZEND_ASSERT(info[i].num_related == 1);
428
17.8k
          map[i] = j;
429
17.8k
          if (i != j) {
430
3.23k
            op_array->literals[j] = op_array->literals[i];
431
3.23k
            info[j] = info[i];
432
3.23k
          }
433
17.8k
          j++;
434
17.8k
          break;
435
1.05M
      }
436
1.05M
    }
437
438
    /* Only clean "hash", as it will be reused in the loop below. */
439
102k
    zend_hash_clean(&hash);
440
102k
    op_array->last_literal = j;
441
442
102k
    const_slot = zend_arena_alloc(&ctx->arena, j * 7 * sizeof(int));
443
102k
    memset(const_slot, -1, j * 7 * sizeof(int));
444
102k
    class_slot = const_slot + j;
445
102k
    func_slot = class_slot + j;
446
102k
    bind_var_slot = func_slot + j;
447
102k
    property_slot = bind_var_slot + j;
448
102k
    method_slot = property_slot + j;
449
102k
    jmp_slot = method_slot + j;
450
451
    /* Update opcodes to use new literals table */
452
102k
    cache_size = zend_op_array_extension_handles * sizeof(void*);
453
102k
    opline = op_array->opcodes;
454
102k
    end = opline + op_array->last;
455
2.59M
    while (opline < end) {
456
2.49M
      if (opline->op1_type == IS_CONST) {
457
443k
        opline->op1.constant = map[opline->op1.constant];
458
443k
      }
459
2.49M
      if (opline->op2_type == IS_CONST) {
460
615k
        opline->op2.constant = map[opline->op2.constant];
461
615k
      }
462
2.49M
      switch (opline->opcode) {
463
88
        case ZEND_ASSIGN_STATIC_PROP_OP:
464
88
          if (opline->op1_type == IS_CONST) {
465
            // op1 static property
466
88
            if (opline->op2_type == IS_CONST) {
467
86
              (opline+1)->extended_value = add_static_slot(&hash, op_array,
468
86
                opline->op2.constant,
469
86
                opline->op1.constant,
470
86
                LITERAL_STATIC_PROPERTY,
471
86
                &cache_size);
472
86
            } else {
473
2
              (opline+1)->extended_value = cache_size;
474
2
              cache_size += 3 * sizeof(void *);
475
2
            }
476
88
          } else if (opline->op2_type == IS_CONST) {
477
            // op2 class
478
0
            if (class_slot[opline->op2.constant] >= 0) {
479
0
              (opline+1)->extended_value = class_slot[opline->op2.constant];
480
0
            } else {
481
0
              (opline+1)->extended_value = cache_size;
482
0
              class_slot[opline->op2.constant] = cache_size;
483
0
              cache_size += sizeof(void *);
484
0
            }
485
0
          }
486
88
          break;
487
761
        case ZEND_ASSIGN_OBJ_OP:
488
761
          if (opline->op2_type == IS_CONST) {
489
            // op2 property
490
667
            if (opline->op1_type == IS_UNUSED &&
491
147
                property_slot[opline->op2.constant] >= 0) {
492
58
              (opline+1)->extended_value = property_slot[opline->op2.constant];
493
609
            } else {
494
609
              (opline+1)->extended_value = cache_size;
495
609
              cache_size += 3 * sizeof(void *);
496
609
              if (opline->op1_type == IS_UNUSED) {
497
89
                property_slot[opline->op2.constant] = (opline+1)->extended_value;
498
89
              }
499
609
            }
500
667
          }
501
761
          break;
502
9.54k
        case ZEND_ASSIGN_OBJ:
503
10.0k
        case ZEND_ASSIGN_OBJ_REF:
504
64.9k
        case ZEND_FETCH_OBJ_R:
505
67.7k
        case ZEND_FETCH_OBJ_W:
506
67.8k
        case ZEND_FETCH_OBJ_RW:
507
68.6k
        case ZEND_FETCH_OBJ_IS:
508
68.8k
        case ZEND_FETCH_OBJ_UNSET:
509
69.3k
        case ZEND_FETCH_OBJ_FUNC_ARG:
510
70.1k
        case ZEND_UNSET_OBJ:
511
70.7k
        case ZEND_PRE_INC_OBJ:
512
71.0k
        case ZEND_PRE_DEC_OBJ:
513
71.3k
        case ZEND_POST_INC_OBJ:
514
71.4k
        case ZEND_POST_DEC_OBJ:
515
71.4k
          if (opline->op2_type == IS_CONST) {
516
            // op2 property
517
70.0k
            if (opline->op1_type == IS_UNUSED &&
518
7.88k
                property_slot[opline->op2.constant] >= 0) {
519
1.21k
              opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
520
68.8k
            } else {
521
68.8k
              opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
522
68.8k
              cache_size += 3 * sizeof(void *);
523
68.8k
              if (opline->op1_type == IS_UNUSED) {
524
6.66k
                property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS;
525
6.66k
              }
526
68.8k
            }
527
70.0k
          }
528
71.4k
          break;
529
673
        case ZEND_ISSET_ISEMPTY_PROP_OBJ:
530
673
          if (opline->op2_type == IS_CONST) {
531
            // op2 property
532
575
            if (opline->op1_type == IS_UNUSED &&
533
58
                property_slot[opline->op2.constant] >= 0) {
534
12
              opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_ISEMPTY);
535
563
            } else {
536
563
              opline->extended_value = cache_size | (opline->extended_value & ZEND_ISEMPTY);
537
563
              cache_size += 3 * sizeof(void *);
538
563
              if (opline->op1_type == IS_UNUSED) {
539
46
                property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_ISEMPTY;
540
46
              }
541
563
            }
542
575
          }
543
673
          break;
544
105k
        case ZEND_INIT_FCALL:
545
112k
        case ZEND_INIT_FCALL_BY_NAME:
546
115k
        case ZEND_INIT_NS_FCALL_BY_NAME:
547
          // op2 func
548
115k
          if (func_slot[opline->op2.constant] >= 0) {
549
50.8k
            opline->result.num = func_slot[opline->op2.constant];
550
64.6k
          } else {
551
64.6k
            opline->result.num = cache_size;
552
64.6k
            cache_size += sizeof(void *);
553
64.6k
            func_slot[opline->op2.constant] = opline->result.num;
554
64.6k
          }
555
115k
          break;
556
42.8k
        case ZEND_INIT_METHOD_CALL:
557
42.8k
          if (opline->op2_type == IS_CONST) {
558
            // op2 method
559
42.6k
            if (opline->op1_type == IS_UNUSED &&
560
575
                method_slot[opline->op2.constant] >= 0) {
561
10
              opline->result.num = method_slot[opline->op2.constant];
562
42.6k
            } else {
563
42.6k
              opline->result.num = cache_size;
564
42.6k
              cache_size += 2 * sizeof(void *);
565
42.6k
              if (opline->op1_type == IS_UNUSED) {
566
565
                method_slot[opline->op2.constant] = opline->result.num;
567
565
              }
568
42.6k
            }
569
42.6k
          }
570
42.8k
          break;
571
6.25k
        case ZEND_INIT_STATIC_METHOD_CALL:
572
6.25k
          if (opline->op2_type == IS_CONST) {
573
            // op2 static method
574
5.77k
            if (opline->op1_type == IS_CONST) {
575
5.09k
              opline->result.num = add_static_slot(&hash, op_array,
576
5.09k
                opline->op1.constant,
577
5.09k
                opline->op2.constant,
578
5.09k
                LITERAL_STATIC_METHOD,
579
5.09k
                &cache_size);
580
5.09k
            } else {
581
674
              opline->result.num = cache_size;
582
674
              cache_size += 2 * sizeof(void *);
583
674
            }
584
5.77k
          } else if (opline->op1_type == IS_CONST) {
585
            // op1 class
586
113
            if (class_slot[opline->op1.constant] >= 0) {
587
50
              opline->result.num = class_slot[opline->op1.constant];
588
63
            } else {
589
63
              opline->result.num = cache_size;
590
63
              cache_size += sizeof(void *);
591
63
              class_slot[opline->op1.constant] = opline->result.num;
592
63
            }
593
113
          }
594
6.25k
          break;
595
75
        case ZEND_DEFINED:
596
          // op1 const
597
75
          if (const_slot[opline->op1.constant] >= 0) {
598
29
            opline->extended_value = const_slot[opline->op1.constant];
599
46
          } else {
600
46
            opline->extended_value = cache_size;
601
46
            cache_size += sizeof(void *);
602
46
            const_slot[opline->op1.constant] = opline->extended_value;
603
46
          }
604
75
          break;
605
54.4k
        case ZEND_FETCH_CONSTANT:
606
          // op2 const
607
54.4k
          if (const_slot[opline->op2.constant] >= 0) {
608
37.0k
            opline->extended_value = const_slot[opline->op2.constant];
609
37.0k
          } else {
610
17.3k
            opline->extended_value = cache_size;
611
17.3k
            cache_size += sizeof(void *);
612
17.3k
            const_slot[opline->op2.constant] = opline->extended_value;
613
17.3k
          }
614
54.4k
          break;
615
3.14k
        case ZEND_FETCH_CLASS_CONSTANT:
616
3.14k
          if (opline->op1_type == IS_CONST
617
2.66k
            && opline->op2_type == IS_CONST
618
2.62k
            && Z_TYPE(op_array->literals[opline->op2.constant]) == IS_STRING) {
619
            // op1/op2 class_const
620
2.62k
            opline->extended_value = add_static_slot(&hash, op_array,
621
2.62k
              opline->op1.constant,
622
2.62k
              opline->op2.constant,
623
2.62k
              LITERAL_CLASS_CONST,
624
2.62k
              &cache_size);
625
2.62k
          } else {
626
521
            opline->extended_value = cache_size;
627
521
            cache_size += 2 * sizeof(void *);
628
521
          }
629
3.14k
          break;
630
1.18k
        case ZEND_ASSIGN_STATIC_PROP:
631
1.42k
        case ZEND_ASSIGN_STATIC_PROP_REF:
632
3.68k
        case ZEND_FETCH_STATIC_PROP_R:
633
4.05k
        case ZEND_FETCH_STATIC_PROP_W:
634
4.05k
        case ZEND_FETCH_STATIC_PROP_RW:
635
4.21k
        case ZEND_FETCH_STATIC_PROP_IS:
636
4.26k
        case ZEND_FETCH_STATIC_PROP_UNSET:
637
4.62k
        case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
638
4.64k
        case ZEND_UNSET_STATIC_PROP:
639
4.74k
        case ZEND_ISSET_ISEMPTY_STATIC_PROP:
640
4.79k
        case ZEND_PRE_INC_STATIC_PROP:
641
4.79k
        case ZEND_PRE_DEC_STATIC_PROP:
642
4.86k
        case ZEND_POST_INC_STATIC_PROP:
643
4.89k
        case ZEND_POST_DEC_STATIC_PROP:
644
4.89k
          if (opline->op1_type == IS_CONST) {
645
            // op1 static property
646
4.63k
            if (opline->op2_type == IS_CONST) {
647
3.44k
              opline->extended_value = add_static_slot(&hash, op_array,
648
3.44k
                opline->op2.constant,
649
3.44k
                opline->op1.constant,
650
3.44k
                LITERAL_STATIC_PROPERTY,
651
3.44k
                &cache_size) | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
652
3.44k
            } else {
653
1.19k
              opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
654
1.19k
              cache_size += 3 * sizeof(void *);
655
1.19k
            }
656
4.63k
          } else if (opline->op2_type == IS_CONST) {
657
            // op2 class
658
216
            if (class_slot[opline->op2.constant] >= 0) {
659
186
              opline->extended_value = class_slot[opline->op2.constant] | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
660
186
            } else {
661
30
              opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
662
30
              class_slot[opline->op2.constant] = cache_size;
663
30
              cache_size += sizeof(void *);
664
30
            }
665
216
          }
666
4.89k
          break;
667
1.30k
        case ZEND_FETCH_CLASS:
668
1.65k
        case ZEND_INSTANCEOF:
669
1.65k
          if (opline->op2_type == IS_CONST) {
670
            // op2 class
671
297
            if (class_slot[opline->op2.constant] >= 0) {
672
136
              opline->extended_value = class_slot[opline->op2.constant];
673
161
            } else {
674
161
              opline->extended_value = cache_size;
675
161
              cache_size += sizeof(void *);
676
161
              class_slot[opline->op2.constant] = opline->extended_value;
677
161
            }
678
297
          }
679
1.65k
          break;
680
61.3k
        case ZEND_NEW:
681
61.3k
          if (opline->op1_type == IS_CONST) {
682
            // op1 class
683
60.0k
            if (class_slot[opline->op1.constant] >= 0) {
684
10.0k
              opline->op2.num = class_slot[opline->op1.constant];
685
49.9k
            } else {
686
49.9k
              opline->op2.num = cache_size;
687
49.9k
              cache_size += sizeof(void *);
688
49.9k
              class_slot[opline->op1.constant] = opline->op2.num;
689
49.9k
            }
690
60.0k
          }
691
61.3k
          break;
692
36.0k
        case ZEND_CATCH:
693
36.0k
          if (opline->op1_type == IS_CONST) {
694
            // op1 class
695
36.0k
            if (class_slot[opline->op1.constant] >= 0) {
696
6.54k
              opline->extended_value = class_slot[opline->op1.constant] | (opline->extended_value & ZEND_LAST_CATCH);
697
29.5k
            } else {
698
29.5k
              opline->extended_value = cache_size | (opline->extended_value & ZEND_LAST_CATCH);
699
29.5k
              cache_size += sizeof(void *);
700
29.5k
              class_slot[opline->op1.constant] = opline->extended_value & ~ZEND_LAST_CATCH;
701
29.5k
            }
702
36.0k
          }
703
36.0k
          break;
704
666
        case ZEND_BIND_GLOBAL:
705
          // op2 bind var
706
666
          if (bind_var_slot[opline->op2.constant] >= 0) {
707
16
            opline->extended_value = bind_var_slot[opline->op2.constant];
708
650
          } else {
709
650
            opline->extended_value = cache_size;
710
650
            cache_size += sizeof(void *);
711
650
            bind_var_slot[opline->op2.constant] = opline->extended_value;
712
650
          }
713
666
          break;
714
734
        case ZEND_DECLARE_ANON_CLASS:
715
2.11k
        case ZEND_DECLARE_CLASS_DELAYED:
716
2.11k
          opline->extended_value = cache_size;
717
2.11k
          cache_size += sizeof(void *);
718
2.11k
          break;
719
0
        case ZEND_JMP_FRAMELESS:
720
          // op1 func
721
0
          if (jmp_slot[opline->op1.constant] >= 0) {
722
0
            opline->extended_value = jmp_slot[opline->op1.constant];
723
0
          } else {
724
0
            opline->extended_value = cache_size;
725
0
            cache_size += sizeof(void *);
726
0
            jmp_slot[opline->op1.constant] = opline->extended_value;
727
0
          }
728
0
          break;
729
69.9k
        case ZEND_SEND_VAL:
730
99.5k
        case ZEND_SEND_VAL_EX:
731
148k
        case ZEND_SEND_VAR:
732
208k
        case ZEND_SEND_VAR_EX:
733
209k
        case ZEND_SEND_VAR_NO_REF:
734
213k
        case ZEND_SEND_VAR_NO_REF_EX:
735
215k
        case ZEND_SEND_REF:
736
216k
        case ZEND_SEND_FUNC_ARG:
737
217k
        case ZEND_CHECK_FUNC_ARG:
738
217k
          if (opline->op2_type == IS_CONST) {
739
2.74k
            opline->result.num = cache_size;
740
2.74k
            cache_size += 2 * sizeof(void *);
741
2.74k
          }
742
217k
          break;
743
2.49M
      }
744
2.49M
      opline++;
745
2.49M
    }
746
102k
    op_array->cache_size = cache_size;
747
102k
    zend_hash_destroy(&hash);
748
102k
    zend_arena_release(&ctx->arena, checkpoint);
749
750
102k
    if (1) {
751
102k
      opline = op_array->opcodes;
752
126k
      while (1) {
753
126k
        if (opline->opcode == ZEND_RECV_INIT) {
754
2.71k
          zval *val = &op_array->literals[opline->op2.constant];
755
756
2.71k
          if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
757
            /* Ensure zval is aligned to 8 bytes */
758
615
            op_array->cache_size = ZEND_MM_ALIGNED_SIZE_EX(op_array->cache_size, 8);
759
615
            Z_CACHE_SLOT_P(val) = op_array->cache_size;
760
615
            op_array->cache_size += sizeof(zval);
761
615
          }
762
124k
        } else if (opline->opcode != ZEND_RECV) {
763
102k
          break;
764
102k
        }
765
24.1k
        opline++;
766
24.1k
      }
767
102k
    }
768
769
#if DEBUG_COMPACT_LITERALS
770
    {
771
      fprintf(stderr, "Optimized literals table size %d\n", op_array->last_literal);
772
773
      for (int i = 0; i < op_array->last_literal; i++) {
774
        zend_string *str = zval_get_string(op_array->literals + i);
775
        fprintf(stderr, "Literal %d, val (%zu):%s\n", i, ZSTR_LEN(str), ZSTR_VAL(str));
776
        zend_string_release(str);
777
      }
778
      fflush(stderr);
779
    }
780
#endif
781
102k
  }
782
102k
}