Coverage Report

Created: 2026-10-28 06:20

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