Coverage Report

Created: 2026-07-25 06:39

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 © The PHP Group and Contributors.                          |
6
   +----------------------------------------------------------------------+
7
   | This source file is subject to the Modified BSD License that is      |
8
   | bundled with this package in the file LICENSE, and is available      |
9
   | through the World Wide Web at <https://www.php.net/license/>.        |
10
   |                                                                      |
11
   | SPDX-License-Identifier: BSD-3-Clause                                |
12
   +----------------------------------------------------------------------+
13
   | Authors: Dmitry Stogov <dmitry@php.net>                              |
14
   |          Xinchen Hui <laruence@php.net>                              |
15
   +----------------------------------------------------------------------+
16
*/
17
18
/* pass 11
19
 * - compact literals table
20
 */
21
22
#include "Optimizer/zend_optimizer.h"
23
#include "Optimizer/zend_optimizer_internal.h"
24
#include "zend_API.h"
25
#include "zend_constants.h"
26
#include "zend_execute.h"
27
#include "zend_vm.h"
28
#include "zend_extensions.h"
29
30
#define DEBUG_COMPACT_LITERALS 0
31
32
2.85k
#define LITERAL_CLASS_CONST 1
33
5.68k
#define LITERAL_STATIC_METHOD 2
34
12.5k
#define LITERAL_STATIC_PROPERTY 3
35
36
typedef struct _literal_info {
37
  uint8_t num_related;
38
} literal_info;
39
40
1.23M
#define LITERAL_INFO(n, related) do { \
41
1.23M
    info[n].num_related = (related); \
42
1.23M
  } while (0)
43
44
static uint32_t add_static_slot(HashTable     *hash,
45
                                zend_op_array *op_array,
46
                                uint32_t       op1,
47
                                uint32_t       op2,
48
                                uint32_t       kind,
49
                                uint32_t       *cache_size)
50
12.8k
{
51
12.8k
  uint32_t ret;
52
12.8k
  zval *class_name = &op_array->literals[op1];
53
12.8k
  zval *prop_name = &op_array->literals[op2];
54
12.8k
  zval *pos, tmp;
55
56
12.8k
  zend_string *key = zend_create_member_string(Z_STR_P(class_name), Z_STR_P(prop_name));
57
12.8k
  ZSTR_H(key) = zend_string_hash_func(key);
58
12.8k
  ZSTR_H(key) += kind;
59
60
12.8k
  pos = zend_hash_find(hash, key);
61
12.8k
  if (pos) {
62
4.72k
    ret = Z_LVAL_P(pos);
63
8.17k
  } else {
64
8.17k
    ret = *cache_size;
65
8.17k
    *cache_size += (kind == LITERAL_STATIC_PROPERTY ? 3 : 2) * sizeof(void *);
66
8.17k
    ZVAL_LONG(&tmp, ret);
67
8.17k
    zend_hash_add(hash, key, &tmp);
68
8.17k
  }
69
12.8k
  zend_string_release_ex(key, 0);
70
12.8k
  return ret;
71
12.8k
}
72
73
static inline void bias_key(zend_string *key, uint32_t bias)
74
212k
{
75
  /* Add a bias to the hash so we can distinguish string keys
76
   * that would otherwise be the same. */
77
212k
  ZSTR_H(key) = zend_string_hash_val(key) + bias;
78
212k
}
79
80
static zend_string *create_str_cache_key(zval *literal, uint8_t num_related)
81
948k
{
82
948k
  ZEND_ASSERT(Z_TYPE_P(literal) == IS_STRING);
83
948k
  if (num_related == 1) {
84
748k
    return zend_string_copy(Z_STR_P(literal));
85
748k
  }
86
87
  /* Concatenate all the related literals for the cache key. */
88
199k
  zend_string *key;
89
199k
  if (num_related == 2) {
90
196k
    ZEND_ASSERT(Z_TYPE_P(literal + 1) == IS_STRING);
91
196k
    key = zend_string_concat2(
92
196k
      Z_STRVAL_P(literal), Z_STRLEN_P(literal),
93
196k
      Z_STRVAL_P(literal + 1), Z_STRLEN_P(literal + 1));
94
196k
  } else if (num_related == 3) {
95
3.04k
    ZEND_ASSERT(Z_TYPE_P(literal + 1) == IS_STRING && Z_TYPE_P(literal + 2) == IS_STRING);
96
3.04k
    key = zend_string_concat3(
97
3.04k
      Z_STRVAL_P(literal), Z_STRLEN_P(literal),
98
3.04k
      Z_STRVAL_P(literal + 1), Z_STRLEN_P(literal + 1),
99
3.04k
      Z_STRVAL_P(literal + 2), Z_STRLEN_P(literal + 2));
100
3.04k
  } else {
101
0
    ZEND_ASSERT(0 && "Currently not needed");
102
0
  }
103
104
199k
  bias_key(key, num_related - 1);
105
199k
  return key;
106
199k
}
107
108
void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx)
109
116k
{
110
116k
  zend_op *opline, *end;
111
116k
  int n, *map;
112
116k
  uint32_t cache_size;
113
116k
  zval zv, *pos;
114
116k
  literal_info *info;
115
116k
  int l_null = -1;
116
116k
  int l_false = -1;
117
116k
  int l_true = -1;
118
116k
  int l_empty_arr = -1;
119
116k
  HashTable hash;
120
116k
  zend_string *key = NULL;
121
116k
  void *checkpoint = zend_arena_checkpoint(ctx->arena);
122
116k
  int *const_slot, *class_slot, *func_slot, *bind_var_slot, *property_slot, *method_slot, *jmp_slot;
123
124
116k
  if (op_array->last_literal) {
125
116k
    uint32_t j;
126
116k
    info = (literal_info*)zend_arena_calloc(&ctx->arena, op_array->last_literal, sizeof(literal_info));
127
128
      /* Mark literals of specific types */
129
116k
    opline = op_array->opcodes;
130
116k
    end = opline + op_array->last;
131
2.92M
    while (opline < end) {
132
2.80M
      switch (opline->opcode) {
133
0
        case ZEND_JMP_FRAMELESS:
134
0
          LITERAL_INFO(opline->op1.constant, 1);
135
0
          break;
136
7.06k
        case ZEND_INIT_FCALL_BY_NAME:
137
7.06k
          LITERAL_INFO(opline->op2.constant, 2);
138
7.06k
          break;
139
2.66k
        case ZEND_INIT_NS_FCALL_BY_NAME:
140
2.66k
          LITERAL_INFO(opline->op2.constant, 3);
141
2.66k
          break;
142
43.7k
        case ZEND_INIT_METHOD_CALL:
143
43.7k
          if (opline->op1_type == IS_CONST) {
144
174
            LITERAL_INFO(opline->op1.constant, 1);
145
174
          }
146
43.7k
          if (opline->op2_type == IS_CONST) {
147
43.3k
            LITERAL_INFO(opline->op2.constant, 2);
148
43.3k
          }
149
43.7k
          break;
150
7.38k
        case ZEND_INIT_STATIC_METHOD_CALL:
151
7.38k
          if (opline->op1_type == IS_CONST) {
152
5.81k
            LITERAL_INFO(opline->op1.constant, 2);
153
5.81k
          }
154
7.38k
          if (opline->op2_type == IS_CONST) {
155
6.73k
            LITERAL_INFO(opline->op2.constant, 2);
156
6.73k
          }
157
7.38k
          break;
158
229
        case ZEND_INIT_PARENT_PROPERTY_HOOK_CALL:
159
229
          LITERAL_INFO(opline->op1.constant, 1);
160
229
          break;
161
33.8k
        case ZEND_CATCH:
162
33.8k
          LITERAL_INFO(opline->op1.constant, 2);
163
33.8k
          break;
164
26.1k
        case ZEND_FETCH_CONSTANT:
165
26.1k
          if (opline->op1.num & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) {
166
371
            LITERAL_INFO(opline->op2.constant, 3);
167
25.7k
          } else {
168
25.7k
            LITERAL_INFO(opline->op2.constant, 2);
169
25.7k
          }
170
26.1k
          break;
171
3.58k
        case ZEND_FETCH_CLASS_CONSTANT:
172
3.58k
          if (opline->op1_type == IS_CONST) {
173
2.92k
            LITERAL_INFO(opline->op1.constant, 2);
174
2.92k
          }
175
3.58k
          if (opline->op2_type == IS_CONST) {
176
3.44k
            LITERAL_INFO(opline->op2.constant, 1);
177
3.44k
          }
178
3.58k
          break;
179
1.66k
        case ZEND_ASSIGN_STATIC_PROP:
180
1.97k
        case ZEND_ASSIGN_STATIC_PROP_REF:
181
4.78k
        case ZEND_FETCH_STATIC_PROP_R:
182
5.33k
        case ZEND_FETCH_STATIC_PROP_W:
183
5.33k
        case ZEND_FETCH_STATIC_PROP_RW:
184
5.89k
        case ZEND_FETCH_STATIC_PROP_IS:
185
6.00k
        case ZEND_FETCH_STATIC_PROP_UNSET:
186
6.29k
        case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
187
6.31k
        case ZEND_UNSET_STATIC_PROP:
188
6.46k
        case ZEND_ISSET_ISEMPTY_STATIC_PROP:
189
6.52k
        case ZEND_PRE_INC_STATIC_PROP:
190
6.52k
        case ZEND_PRE_DEC_STATIC_PROP:
191
6.62k
        case ZEND_POST_INC_STATIC_PROP:
192
6.65k
        case ZEND_POST_DEC_STATIC_PROP:
193
6.82k
        case ZEND_ASSIGN_STATIC_PROP_OP:
194
6.82k
          if (opline->op2_type == IS_CONST) {
195
5.11k
            LITERAL_INFO(opline->op2.constant, 2);
196
5.11k
          }
197
6.82k
          if (opline->op1_type == IS_CONST) {
198
6.01k
            LITERAL_INFO(opline->op1.constant, 1);
199
6.01k
          }
200
6.82k
          break;
201
1.29k
        case ZEND_FETCH_CLASS:
202
1.76k
        case ZEND_INSTANCEOF:
203
1.76k
          if (opline->op2_type == IS_CONST) {
204
404
            LITERAL_INFO(opline->op2.constant, 2);
205
404
          }
206
1.76k
          break;
207
60.2k
        case ZEND_NEW:
208
60.2k
          if (opline->op1_type == IS_CONST) {
209
58.7k
            LITERAL_INFO(opline->op1.constant, 2);
210
58.7k
          }
211
60.2k
          break;
212
5.24k
        case ZEND_DECLARE_CLASS:
213
6.75k
        case ZEND_DECLARE_CLASS_DELAYED:
214
6.75k
          LITERAL_INFO(opline->op1.constant, 2);
215
6.75k
          if (opline->op2_type == IS_CONST) {
216
2.25k
            LITERAL_INFO(opline->op2.constant, 1);
217
2.25k
          }
218
6.75k
          break;
219
2.57k
        case ZEND_ISSET_ISEMPTY_DIM_OBJ:
220
18.7k
        case ZEND_ASSIGN_DIM:
221
20.5k
        case ZEND_UNSET_DIM:
222
34.6k
        case ZEND_FETCH_DIM_R:
223
40.9k
        case ZEND_FETCH_DIM_W:
224
42.0k
        case ZEND_FETCH_DIM_RW:
225
44.0k
        case ZEND_FETCH_DIM_IS:
226
44.7k
        case ZEND_FETCH_DIM_FUNC_ARG:
227
45.0k
        case ZEND_FETCH_DIM_UNSET:
228
46.8k
        case ZEND_FETCH_LIST_R:
229
47.2k
        case ZEND_FETCH_LIST_W:
230
50.4k
        case ZEND_ASSIGN_DIM_OP:
231
50.4k
          if (opline->op1_type == IS_CONST) {
232
631
            LITERAL_INFO(opline->op1.constant, 1);
233
631
          }
234
50.4k
          if (opline->op2_type == IS_CONST) {
235
28.5k
            if (Z_EXTRA(op_array->literals[opline->op2.constant]) == ZEND_EXTRA_VALUE) {
236
737
              LITERAL_INFO(opline->op2.constant, 2);
237
27.8k
            } else {
238
27.8k
              LITERAL_INFO(opline->op2.constant, 1);
239
27.8k
            }
240
28.5k
          }
241
50.4k
          break;
242
2.55M
        default:
243
2.55M
          if (opline->op1_type == IS_CONST) {
244
378k
            LITERAL_INFO(opline->op1.constant, 1);
245
378k
          }
246
2.55M
          if (opline->op2_type == IS_CONST) {
247
615k
            LITERAL_INFO(opline->op2.constant, 1);
248
615k
          }
249
2.55M
          break;
250
2.80M
      }
251
2.80M
      opline++;
252
2.80M
    }
253
254
#if DEBUG_COMPACT_LITERALS
255
    {
256
      fprintf(stderr, "File %s func %s\n", op_array->filename->val,
257
          op_array->function_name ? op_array->function_name->val : "main");
258
      fprintf(stderr, "Literals table size %d\n", op_array->last_literal);
259
260
      for (uint32_t i = 0; i < op_array->last_literal; i++) {
261
        zend_string *str = zval_get_string(op_array->literals + i);
262
        fprintf(stderr, "Literal %" PRIu32 ", val (%zu):%s\n", i, ZSTR_LEN(str), ZSTR_VAL(str));
263
        zend_string_release(str);
264
      }
265
      fflush(stderr);
266
    }
267
#endif
268
269
    /* Merge equal constants */
270
116k
    j = 0;
271
116k
    zend_hash_init(&hash, op_array->last_literal, NULL, NULL, 0);
272
116k
    map = (int*)zend_arena_alloc(&ctx->arena, op_array->last_literal * sizeof(int));
273
116k
    memset(map, 0, op_array->last_literal * sizeof(int));
274
1.42M
    for (uint32_t i = 0; i < op_array->last_literal; i++) {
275
1.30M
      if (!info[i].num_related) {
276
        /* unset literal */
277
72.9k
        zval_ptr_dtor_nogc(&op_array->literals[i]);
278
72.9k
        continue;
279
72.9k
      }
280
1.23M
      switch (Z_TYPE(op_array->literals[i])) {
281
46.6k
        case IS_NULL:
282
46.6k
          ZEND_ASSERT(info[i].num_related == 1);
283
46.6k
          if (l_null < 0) {
284
37.9k
            l_null = j;
285
37.9k
            if (i != j) {
286
11.2k
              op_array->literals[j] = op_array->literals[i];
287
11.2k
              info[j] = info[i];
288
11.2k
            }
289
37.9k
            j++;
290
37.9k
          }
291
46.6k
          map[i] = l_null;
292
46.6k
          break;
293
5.05k
        case IS_FALSE:
294
5.05k
          ZEND_ASSERT(info[i].num_related == 1);
295
5.05k
          if (l_false < 0) {
296
3.01k
            l_false = j;
297
3.01k
            if (i != j) {
298
1.24k
              op_array->literals[j] = op_array->literals[i];
299
1.24k
              info[j] = info[i];
300
1.24k
            }
301
3.01k
            j++;
302
3.01k
          }
303
5.05k
          map[i] = l_false;
304
5.05k
          break;
305
5.21k
        case IS_TRUE:
306
5.21k
          ZEND_ASSERT(info[i].num_related == 1);
307
5.21k
          if (l_true < 0) {
308
3.29k
            l_true = j;
309
3.29k
            if (i != j) {
310
1.25k
              op_array->literals[j] = op_array->literals[i];
311
1.25k
              info[j] = info[i];
312
1.25k
            }
313
3.29k
            j++;
314
3.29k
          }
315
5.21k
          map[i] = l_true;
316
5.21k
          break;
317
191k
        case IS_LONG:
318
191k
          if (info[i].num_related == 1) {
319
190k
            if ((pos = zend_hash_index_find(&hash, Z_LVAL(op_array->literals[i]))) != NULL) {
320
60.1k
              map[i] = Z_LVAL_P(pos);
321
130k
            } else {
322
130k
              map[i] = j;
323
130k
              ZVAL_LONG(&zv, j);
324
130k
              zend_hash_index_add_new(&hash, Z_LVAL(op_array->literals[i]), &zv);
325
130k
              if (i != j) {
326
51.5k
                op_array->literals[j] = op_array->literals[i];
327
51.5k
                info[j] = info[i];
328
51.5k
              }
329
130k
              j++;
330
130k
            }
331
190k
          } else {
332
737
            ZEND_ASSERT(info[i].num_related == 2);
333
737
            key = zend_string_init(Z_STRVAL(op_array->literals[i+1]), Z_STRLEN(op_array->literals[i+1]), 0);
334
737
            bias_key(key, 100 + info[i].num_related - 1);
335
737
            if ((pos = zend_hash_find(&hash, key)) != NULL) {
336
368
              ZEND_ASSERT(info[Z_LVAL_P(pos)].num_related == 2);
337
368
              map[i] = Z_LVAL_P(pos);
338
368
              zval_ptr_dtor_nogc(&op_array->literals[i+1]);
339
369
            } else {
340
369
              map[i] = j;
341
369
              ZVAL_LONG(&zv, j);
342
369
              zend_hash_add_new(&hash, key, &zv);
343
369
              if (i != j) {
344
275
                op_array->literals[j] = op_array->literals[i];
345
275
                info[j] = info[i];
346
275
                op_array->literals[j+1] = op_array->literals[i+1];
347
275
                info[j+1] = info[i+1];
348
275
              }
349
369
              j += 2;
350
369
            }
351
737
            zend_string_release_ex(key, 0);
352
737
            i++;
353
737
          }
354
191k
          break;
355
191k
        case IS_DOUBLE:
356
11.6k
          ZEND_ASSERT(info[i].num_related == 1);
357
11.6k
          key = zend_string_init((char*)&Z_DVAL(op_array->literals[i]), sizeof(double), 0);
358
11.6k
          bias_key(key, 200);
359
11.6k
          if ((pos = zend_hash_find(&hash, key))) {
360
2.88k
            map[i] = Z_LVAL_P(pos);
361
8.79k
          } else {
362
8.79k
            map[i] = j;
363
8.79k
            ZVAL_LONG(&zv, j);
364
8.79k
            zend_hash_add_new(&hash, key, &zv);
365
8.79k
            if (i != j) {
366
4.65k
              op_array->literals[j] = op_array->literals[i];
367
4.65k
              info[j] = info[i];
368
4.65k
            }
369
8.79k
            j++;
370
8.79k
          }
371
11.6k
          zend_string_release_ex(key, 0);
372
11.6k
          break;
373
948k
        case IS_STRING: {
374
948k
          key = create_str_cache_key(&op_array->literals[i], info[i].num_related);
375
948k
          if ((pos = zend_hash_find(&hash, key)) != NULL) {
376
369k
            ZEND_ASSERT(Z_TYPE(op_array->literals[Z_LVAL_P(pos)]) == IS_STRING &&
377
369k
              info[i].num_related == info[Z_LVAL_P(pos)].num_related);
378
369k
            zend_string_release_ex(key, 0);
379
369k
            map[i] = Z_LVAL_P(pos);
380
369k
            zval_ptr_dtor_nogc(&op_array->literals[i]);
381
369k
            n = info[i].num_related;
382
434k
            while (n > 1) {
383
65.2k
              i++;
384
65.2k
              zval_ptr_dtor_nogc(&op_array->literals[i]);
385
65.2k
              n--;
386
65.2k
            }
387
579k
          } else {
388
579k
            map[i] = j;
389
579k
            ZVAL_LONG(&zv, j);
390
579k
            zend_hash_add_new(&hash, key, &zv);
391
579k
            zend_string_release_ex(key, 0);
392
579k
            if (i != j) {
393
176k
              op_array->literals[j] = op_array->literals[i];
394
176k
              info[j] = info[i];
395
176k
            }
396
579k
            j++;
397
579k
            n = info[i].num_related;
398
716k
            while (n > 1) {
399
137k
              i++;
400
137k
              if (i != j) op_array->literals[j] = op_array->literals[i];
401
137k
              j++;
402
137k
              n--;
403
137k
            }
404
579k
          }
405
948k
          break;
406
948k
        }
407
948k
        case IS_ARRAY:
408
24.4k
          ZEND_ASSERT(info[i].num_related == 1);
409
24.4k
          if (zend_hash_num_elements(Z_ARRVAL(op_array->literals[i])) == 0) {
410
4.83k
            if (l_empty_arr < 0) {
411
3.74k
              l_empty_arr = j;
412
3.74k
              if (i != j) {
413
1.43k
                op_array->literals[j] = op_array->literals[i];
414
1.43k
                info[j] = info[i];
415
1.43k
              }
416
3.74k
              j++;
417
3.74k
            } else {
418
1.08k
              zval_ptr_dtor_nogc(&op_array->literals[i]);
419
1.08k
            }
420
4.83k
            map[i] = l_empty_arr;
421
4.83k
            break;
422
4.83k
          }
423
19.5k
          ZEND_FALLTHROUGH;
424
21.5k
        default:
425
          /* don't merge other types */
426
21.5k
          ZEND_ASSERT(info[i].num_related == 1);
427
21.5k
          map[i] = j;
428
21.5k
          if (i != j) {
429
4.31k
            op_array->literals[j] = op_array->literals[i];
430
4.31k
            info[j] = info[i];
431
4.31k
          }
432
21.5k
          j++;
433
21.5k
          break;
434
1.23M
      }
435
1.23M
    }
436
437
    /* Only clean "hash", as it will be reused in the loop below. */
438
116k
    zend_hash_clean(&hash);
439
116k
    op_array->last_literal = j;
440
441
116k
    const_slot = zend_arena_alloc(&ctx->arena, j * 7 * sizeof(int));
442
116k
    memset(const_slot, -1, j * 7 * sizeof(int));
443
116k
    class_slot = const_slot + j;
444
116k
    func_slot = class_slot + j;
445
116k
    bind_var_slot = func_slot + j;
446
116k
    property_slot = bind_var_slot + j;
447
116k
    method_slot = property_slot + j;
448
116k
    jmp_slot = method_slot + j;
449
450
    /* Update opcodes to use new literals table */
451
116k
    cache_size = zend_op_array_extension_handles * sizeof(void*);
452
116k
    opline = op_array->opcodes;
453
116k
    end = opline + op_array->last;
454
2.92M
    while (opline < end) {
455
2.80M
      if (opline->op1_type == IS_CONST) {
456
494k
        opline->op1.constant = map[opline->op1.constant];
457
494k
      }
458
2.80M
      if (opline->op2_type == IS_CONST) {
459
740k
        opline->op2.constant = map[opline->op2.constant];
460
740k
      }
461
2.80M
      switch (opline->opcode) {
462
176
        case ZEND_ASSIGN_STATIC_PROP_OP:
463
176
          if (opline->op1_type == IS_CONST) {
464
            // op1 static property
465
176
            if (opline->op2_type == IS_CONST) {
466
174
              (opline+1)->extended_value = add_static_slot(&hash, op_array,
467
174
                opline->op2.constant,
468
174
                opline->op1.constant,
469
174
                LITERAL_STATIC_PROPERTY,
470
174
                &cache_size);
471
174
            } else {
472
2
              (opline+1)->extended_value = cache_size;
473
2
              cache_size += 3 * sizeof(void *);
474
2
            }
475
176
          } else if (opline->op2_type == IS_CONST) {
476
            // op2 class
477
0
            if (class_slot[opline->op2.constant] >= 0) {
478
0
              (opline+1)->extended_value = class_slot[opline->op2.constant];
479
0
            } else {
480
0
              (opline+1)->extended_value = cache_size;
481
0
              class_slot[opline->op2.constant] = cache_size;
482
0
              cache_size += sizeof(void *);
483
0
            }
484
0
          }
485
176
          break;
486
1.19k
        case ZEND_ASSIGN_OBJ_OP:
487
1.19k
          if (opline->op2_type == IS_CONST) {
488
            // op2 property
489
938
            if (opline->op1_type == IS_UNUSED &&
490
228
                property_slot[opline->op2.constant] >= 0) {
491
95
              (opline+1)->extended_value = property_slot[opline->op2.constant];
492
843
            } else {
493
843
              (opline+1)->extended_value = cache_size;
494
843
              cache_size += 3 * sizeof(void *);
495
843
              if (opline->op1_type == IS_UNUSED) {
496
133
                property_slot[opline->op2.constant] = (opline+1)->extended_value;
497
133
              }
498
843
            }
499
938
          }
500
1.19k
          break;
501
14.1k
        case ZEND_ASSIGN_OBJ:
502
15.0k
        case ZEND_ASSIGN_OBJ_REF:
503
82.1k
        case ZEND_FETCH_OBJ_R:
504
87.3k
        case ZEND_FETCH_OBJ_W:
505
87.5k
        case ZEND_FETCH_OBJ_RW:
506
89.6k
        case ZEND_FETCH_OBJ_IS:
507
89.9k
        case ZEND_FETCH_OBJ_UNSET:
508
90.4k
        case ZEND_FETCH_OBJ_FUNC_ARG:
509
91.6k
        case ZEND_UNSET_OBJ:
510
92.4k
        case ZEND_PRE_INC_OBJ:
511
92.7k
        case ZEND_PRE_DEC_OBJ:
512
93.0k
        case ZEND_POST_INC_OBJ:
513
93.1k
        case ZEND_POST_DEC_OBJ:
514
93.1k
          if (opline->op2_type == IS_CONST) {
515
            // op2 property
516
89.2k
            if (opline->op1_type == IS_UNUSED &&
517
11.6k
                property_slot[opline->op2.constant] >= 0) {
518
2.16k
              opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
519
87.0k
            } else {
520
87.0k
              opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
521
87.0k
              cache_size += 3 * sizeof(void *);
522
87.0k
              if (opline->op1_type == IS_UNUSED) {
523
9.46k
                property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS;
524
9.46k
              }
525
87.0k
            }
526
89.2k
          }
527
93.1k
          break;
528
1.27k
        case ZEND_ISSET_ISEMPTY_PROP_OBJ:
529
1.27k
          if (opline->op2_type == IS_CONST) {
530
            // op2 property
531
1.02k
            if (opline->op1_type == IS_UNUSED &&
532
122
                property_slot[opline->op2.constant] >= 0) {
533
12
              opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_ISEMPTY);
534
1.01k
            } else {
535
1.01k
              opline->extended_value = cache_size | (opline->extended_value & ZEND_ISEMPTY);
536
1.01k
              cache_size += 3 * sizeof(void *);
537
1.01k
              if (opline->op1_type == IS_UNUSED) {
538
110
                property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_ISEMPTY;
539
110
              }
540
1.01k
            }
541
1.02k
          }
542
1.27k
          break;
543
135k
        case ZEND_INIT_FCALL:
544
142k
        case ZEND_INIT_FCALL_BY_NAME:
545
145k
        case ZEND_INIT_NS_FCALL_BY_NAME:
546
          // op2 func
547
145k
          if (func_slot[opline->op2.constant] >= 0) {
548
63.8k
            opline->result.num = func_slot[opline->op2.constant];
549
81.7k
          } else {
550
81.7k
            opline->result.num = cache_size;
551
81.7k
            cache_size += sizeof(void *);
552
81.7k
            func_slot[opline->op2.constant] = opline->result.num;
553
81.7k
          }
554
145k
          break;
555
43.7k
        case ZEND_INIT_METHOD_CALL:
556
43.7k
          if (opline->op2_type == IS_CONST) {
557
            // op2 method
558
43.3k
            if (opline->op1_type == IS_UNUSED &&
559
873
                method_slot[opline->op2.constant] >= 0) {
560
88
              opline->result.num = method_slot[opline->op2.constant];
561
43.2k
            } else {
562
43.2k
              opline->result.num = cache_size;
563
43.2k
              cache_size += 2 * sizeof(void *);
564
43.2k
              if (opline->op1_type == IS_UNUSED) {
565
785
                method_slot[opline->op2.constant] = opline->result.num;
566
785
              }
567
43.2k
            }
568
43.3k
          }
569
43.7k
          break;
570
7.38k
        case ZEND_INIT_STATIC_METHOD_CALL:
571
7.38k
          if (opline->op2_type == IS_CONST) {
572
            // op2 static method
573
6.73k
            if (opline->op1_type == IS_CONST) {
574
5.68k
              opline->result.num = add_static_slot(&hash, op_array,
575
5.68k
                opline->op1.constant,
576
5.68k
                opline->op2.constant,
577
5.68k
                LITERAL_STATIC_METHOD,
578
5.68k
                &cache_size);
579
5.68k
            } else {
580
1.05k
              opline->result.num = cache_size;
581
1.05k
              cache_size += 2 * sizeof(void *);
582
1.05k
            }
583
6.73k
          } else if (opline->op1_type == IS_CONST) {
584
            // op1 class
585
134
            if (class_slot[opline->op1.constant] >= 0) {
586
55
              opline->result.num = class_slot[opline->op1.constant];
587
79
            } else {
588
79
              opline->result.num = cache_size;
589
79
              cache_size += sizeof(void *);
590
79
              class_slot[opline->op1.constant] = opline->result.num;
591
79
            }
592
134
          }
593
7.38k
          break;
594
138
        case ZEND_DEFINED:
595
          // op1 const
596
138
          if (const_slot[opline->op1.constant] >= 0) {
597
51
            opline->extended_value = const_slot[opline->op1.constant];
598
87
          } else {
599
87
            opline->extended_value = cache_size;
600
87
            cache_size += sizeof(void *);
601
87
            const_slot[opline->op1.constant] = opline->extended_value;
602
87
          }
603
138
          break;
604
26.1k
        case ZEND_FETCH_CONSTANT:
605
          // op2 const
606
26.1k
          if (const_slot[opline->op2.constant] >= 0) {
607
14.3k
            opline->extended_value = const_slot[opline->op2.constant];
608
14.3k
          } else {
609
11.8k
            opline->extended_value = cache_size;
610
11.8k
            cache_size += sizeof(void *);
611
11.8k
            const_slot[opline->op2.constant] = opline->extended_value;
612
11.8k
          }
613
26.1k
          break;
614
3.58k
        case ZEND_FETCH_CLASS_CONSTANT:
615
3.58k
          if (opline->op1_type == IS_CONST
616
2.92k
            && opline->op2_type == IS_CONST
617
2.85k
            && Z_TYPE(op_array->literals[opline->op2.constant]) == IS_STRING) {
618
            // op1/op2 class_const
619
2.85k
            opline->extended_value = add_static_slot(&hash, op_array,
620
2.85k
              opline->op1.constant,
621
2.85k
              opline->op2.constant,
622
2.85k
              LITERAL_CLASS_CONST,
623
2.85k
              &cache_size);
624
2.85k
          } else {
625
731
            opline->extended_value = cache_size;
626
731
            cache_size += 2 * sizeof(void *);
627
731
          }
628
3.58k
          break;
629
1.66k
        case ZEND_ASSIGN_STATIC_PROP:
630
1.97k
        case ZEND_ASSIGN_STATIC_PROP_REF:
631
4.78k
        case ZEND_FETCH_STATIC_PROP_R:
632
5.33k
        case ZEND_FETCH_STATIC_PROP_W:
633
5.33k
        case ZEND_FETCH_STATIC_PROP_RW:
634
5.89k
        case ZEND_FETCH_STATIC_PROP_IS:
635
6.00k
        case ZEND_FETCH_STATIC_PROP_UNSET:
636
6.29k
        case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
637
6.31k
        case ZEND_UNSET_STATIC_PROP:
638
6.46k
        case ZEND_ISSET_ISEMPTY_STATIC_PROP:
639
6.52k
        case ZEND_PRE_INC_STATIC_PROP:
640
6.52k
        case ZEND_PRE_DEC_STATIC_PROP:
641
6.62k
        case ZEND_POST_INC_STATIC_PROP:
642
6.65k
        case ZEND_POST_DEC_STATIC_PROP:
643
6.65k
          if (opline->op1_type == IS_CONST) {
644
            // op1 static property
645
5.84k
            if (opline->op2_type == IS_CONST) {
646
4.17k
              opline->extended_value = add_static_slot(&hash, op_array,
647
4.17k
                opline->op2.constant,
648
4.17k
                opline->op1.constant,
649
4.17k
                LITERAL_STATIC_PROPERTY,
650
4.17k
                &cache_size) | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
651
4.17k
            } else {
652
1.66k
              opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
653
1.66k
              cache_size += 3 * sizeof(void *);
654
1.66k
            }
655
5.84k
          } else if (opline->op2_type == IS_CONST) {
656
            // op2 class
657
765
            if (class_slot[opline->op2.constant] >= 0) {
658
648
              opline->extended_value = class_slot[opline->op2.constant] | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
659
648
            } else {
660
117
              opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
661
117
              class_slot[opline->op2.constant] = cache_size;
662
117
              cache_size += sizeof(void *);
663
117
            }
664
765
          }
665
6.65k
          break;
666
1.29k
        case ZEND_FETCH_CLASS:
667
1.76k
        case ZEND_INSTANCEOF:
668
1.76k
          if (opline->op2_type == IS_CONST) {
669
            // op2 class
670
404
            if (class_slot[opline->op2.constant] >= 0) {
671
186
              opline->extended_value = class_slot[opline->op2.constant];
672
218
            } else {
673
218
              opline->extended_value = cache_size;
674
218
              cache_size += sizeof(void *);
675
218
              class_slot[opline->op2.constant] = opline->extended_value;
676
218
            }
677
404
          }
678
1.76k
          break;
679
60.2k
        case ZEND_NEW:
680
60.2k
          if (opline->op1_type == IS_CONST) {
681
            // op1 class
682
58.7k
            if (class_slot[opline->op1.constant] >= 0) {
683
12.4k
              opline->op2.num = class_slot[opline->op1.constant];
684
46.3k
            } else {
685
46.3k
              opline->op2.num = cache_size;
686
46.3k
              cache_size += sizeof(void *);
687
46.3k
              class_slot[opline->op1.constant] = opline->op2.num;
688
46.3k
            }
689
58.7k
          }
690
60.2k
          break;
691
33.8k
        case ZEND_CATCH:
692
33.8k
          if (opline->op1_type == IS_CONST) {
693
            // op1 class
694
33.8k
            if (class_slot[opline->op1.constant] >= 0) {
695
7.19k
              opline->extended_value = class_slot[opline->op1.constant] | (opline->extended_value & ZEND_LAST_CATCH);
696
26.6k
            } else {
697
26.6k
              opline->extended_value = cache_size | (opline->extended_value & ZEND_LAST_CATCH);
698
26.6k
              cache_size += sizeof(void *);
699
26.6k
              class_slot[opline->op1.constant] = opline->extended_value & ~ZEND_LAST_CATCH;
700
26.6k
            }
701
33.8k
          }
702
33.8k
          break;
703
1.18k
        case ZEND_BIND_GLOBAL:
704
          // op2 bind var
705
1.18k
          if (bind_var_slot[opline->op2.constant] >= 0) {
706
36
            opline->extended_value = bind_var_slot[opline->op2.constant];
707
1.15k
          } else {
708
1.15k
            opline->extended_value = cache_size;
709
1.15k
            cache_size += sizeof(void *);
710
1.15k
            bind_var_slot[opline->op2.constant] = opline->extended_value;
711
1.15k
          }
712
1.18k
          break;
713
854
        case ZEND_DECLARE_ANON_CLASS:
714
2.36k
        case ZEND_DECLARE_CLASS_DELAYED:
715
2.36k
          opline->extended_value = cache_size;
716
2.36k
          cache_size += sizeof(void *);
717
2.36k
          break;
718
0
        case ZEND_JMP_FRAMELESS:
719
          // op1 func
720
0
          if (jmp_slot[opline->op1.constant] >= 0) {
721
0
            opline->extended_value = jmp_slot[opline->op1.constant];
722
0
          } else {
723
0
            opline->extended_value = cache_size;
724
0
            cache_size += sizeof(void *);
725
0
            jmp_slot[opline->op1.constant] = opline->extended_value;
726
0
          }
727
0
          break;
728
136k
        case ZEND_SEND_VAL:
729
156k
        case ZEND_SEND_VAL_EX:
730
230k
        case ZEND_SEND_VAR:
731
243k
        case ZEND_SEND_VAR_EX:
732
244k
        case ZEND_SEND_VAR_NO_REF:
733
246k
        case ZEND_SEND_VAR_NO_REF_EX:
734
249k
        case ZEND_SEND_REF:
735
251k
        case ZEND_SEND_FUNC_ARG:
736
252k
        case ZEND_SEND_PLACEHOLDER:
737
253k
        case ZEND_CHECK_FUNC_ARG:
738
253k
          if (opline->op2_type == IS_CONST) {
739
4.49k
            opline->result.num = cache_size;
740
4.49k
            cache_size += 2 * sizeof(void *);
741
4.49k
          }
742
253k
          break;
743
1.18k
        case ZEND_CALLABLE_CONVERT:
744
1.18k
          if (opline->extended_value != (uint32_t)-1) {
745
637
            opline->extended_value = cache_size;
746
637
            cache_size += sizeof(void *);
747
637
          }
748
1.18k
          break;
749
904
        case ZEND_CALLABLE_CONVERT_PARTIAL:
750
904
          opline->op1.num = cache_size;
751
904
          cache_size += 2 * sizeof(void *);
752
904
          break;
753
2.80M
      }
754
2.80M
      opline++;
755
2.80M
    }
756
116k
    op_array->cache_size = cache_size;
757
116k
    zend_hash_destroy(&hash);
758
116k
    zend_arena_release(&ctx->arena, checkpoint);
759
760
116k
    if (1) {
761
116k
      opline = op_array->opcodes;
762
150k
      while (1) {
763
150k
        if (opline->opcode == ZEND_RECV_INIT) {
764
4.38k
          zval *val = &op_array->literals[opline->op2.constant];
765
766
4.38k
          if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
767
            /* Ensure zval is aligned to 8 bytes */
768
988
            op_array->cache_size = ZEND_MM_ALIGNED_SIZE_EX(op_array->cache_size, 8);
769
988
            Z_CACHE_SLOT_P(val) = op_array->cache_size;
770
988
            op_array->cache_size += sizeof(zval);
771
988
          }
772
145k
        } else if (opline->opcode != ZEND_RECV) {
773
116k
          break;
774
116k
        }
775
34.0k
        opline++;
776
34.0k
      }
777
116k
    }
778
779
#if DEBUG_COMPACT_LITERALS
780
    {
781
      fprintf(stderr, "Optimized literals table size %d\n", op_array->last_literal);
782
783
      for (uint32_t i = 0; i < op_array->last_literal; i++) {
784
        zend_string *str = zval_get_string(op_array->literals + i);
785
        fprintf(stderr, "Literal %" PRIu32 ", val (%zu):%s\n", i, ZSTR_LEN(str), ZSTR_VAL(str));
786
        zend_string_release(str);
787
      }
788
      fflush(stderr);
789
    }
790
#endif
791
116k
  }
792
116k
}