Coverage Report

Created: 2026-06-02 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
1.23k
#define LITERAL_CLASS_CONST 1
33
2.32k
#define LITERAL_STATIC_METHOD 2
34
5.13k
#define LITERAL_STATIC_PROPERTY 3
35
36
typedef struct _literal_info {
37
  uint8_t num_related;
38
} literal_info;
39
40
476k
#define LITERAL_INFO(n, related) do { \
41
476k
    info[n].num_related = (related); \
42
476k
  } 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
5.19k
{
51
5.19k
  uint32_t ret;
52
5.19k
  zval *class_name = &op_array->literals[op1];
53
5.19k
  zval *prop_name = &op_array->literals[op2];
54
5.19k
  zval *pos, tmp;
55
56
5.19k
  zend_string *key = zend_create_member_string(Z_STR_P(class_name), Z_STR_P(prop_name));
57
5.19k
  ZSTR_H(key) = zend_string_hash_func(key);
58
5.19k
  ZSTR_H(key) += kind;
59
60
5.19k
  pos = zend_hash_find(hash, key);
61
5.19k
  if (pos) {
62
1.69k
    ret = Z_LVAL_P(pos);
63
3.49k
  } else {
64
3.49k
    ret = *cache_size;
65
3.49k
    *cache_size += (kind == LITERAL_STATIC_PROPERTY ? 3 : 2) * sizeof(void *);
66
3.49k
    ZVAL_LONG(&tmp, ret);
67
3.49k
    zend_hash_add(hash, key, &tmp);
68
3.49k
  }
69
5.19k
  zend_string_release_ex(key, 0);
70
5.19k
  return ret;
71
5.19k
}
72
73
static inline void bias_key(zend_string *key, uint32_t bias)
74
88.6k
{
75
  /* Add a bias to the hash so we can distinguish string keys
76
   * that would otherwise be the same. */
77
88.6k
  ZSTR_H(key) = zend_string_hash_val(key) + bias;
78
88.6k
}
79
80
static zend_string *create_str_cache_key(zval *literal, uint8_t num_related)
81
369k
{
82
369k
  ZEND_ASSERT(Z_TYPE_P(literal) == IS_STRING);
83
369k
  if (num_related == 1) {
84
284k
    return zend_string_copy(Z_STR_P(literal));
85
284k
  }
86
87
  /* Concatenate all the related literals for the cache key. */
88
84.9k
  zend_string *key;
89
84.9k
  if (num_related == 2) {
90
83.4k
    ZEND_ASSERT(Z_TYPE_P(literal + 1) == IS_STRING);
91
83.4k
    key = zend_string_concat2(
92
83.4k
      Z_STRVAL_P(literal), Z_STRLEN_P(literal),
93
83.4k
      Z_STRVAL_P(literal + 1), Z_STRLEN_P(literal + 1));
94
83.4k
  } else if (num_related == 3) {
95
1.45k
    ZEND_ASSERT(Z_TYPE_P(literal + 1) == IS_STRING && Z_TYPE_P(literal + 2) == IS_STRING);
96
1.45k
    key = zend_string_concat3(
97
1.45k
      Z_STRVAL_P(literal), Z_STRLEN_P(literal),
98
1.45k
      Z_STRVAL_P(literal + 1), Z_STRLEN_P(literal + 1),
99
1.45k
      Z_STRVAL_P(literal + 2), Z_STRLEN_P(literal + 2));
100
1.45k
  } else {
101
0
    ZEND_ASSERT(0 && "Currently not needed");
102
0
  }
103
104
84.9k
  bias_key(key, num_related - 1);
105
84.9k
  return key;
106
84.9k
}
107
108
void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx)
109
47.7k
{
110
47.7k
  zend_op *opline, *end;
111
47.7k
  int n, *map;
112
47.7k
  uint32_t cache_size;
113
47.7k
  zval zv, *pos;
114
47.7k
  literal_info *info;
115
47.7k
  int l_null = -1;
116
47.7k
  int l_false = -1;
117
47.7k
  int l_true = -1;
118
47.7k
  int l_empty_arr = -1;
119
47.7k
  HashTable hash;
120
47.7k
  zend_string *key = NULL;
121
47.7k
  void *checkpoint = zend_arena_checkpoint(ctx->arena);
122
47.7k
  int *const_slot, *class_slot, *func_slot, *bind_var_slot, *property_slot, *method_slot, *jmp_slot;
123
124
47.7k
  if (op_array->last_literal) {
125
47.7k
    uint32_t j;
126
47.7k
    info = (literal_info*)zend_arena_calloc(&ctx->arena, op_array->last_literal, sizeof(literal_info));
127
128
      /* Mark literals of specific types */
129
47.7k
    opline = op_array->opcodes;
130
47.7k
    end = opline + op_array->last;
131
1.13M
    while (opline < end) {
132
1.08M
      switch (opline->opcode) {
133
0
        case ZEND_JMP_FRAMELESS:
134
0
          LITERAL_INFO(opline->op1.constant, 1);
135
0
          break;
136
2.44k
        case ZEND_INIT_FCALL_BY_NAME:
137
2.44k
          LITERAL_INFO(opline->op2.constant, 2);
138
2.44k
          break;
139
1.28k
        case ZEND_INIT_NS_FCALL_BY_NAME:
140
1.28k
          LITERAL_INFO(opline->op2.constant, 3);
141
1.28k
          break;
142
18.2k
        case ZEND_INIT_METHOD_CALL:
143
18.2k
          if (opline->op1_type == IS_CONST) {
144
68
            LITERAL_INFO(opline->op1.constant, 1);
145
68
          }
146
18.2k
          if (opline->op2_type == IS_CONST) {
147
18.1k
            LITERAL_INFO(opline->op2.constant, 2);
148
18.1k
          }
149
18.2k
          break;
150
2.83k
        case ZEND_INIT_STATIC_METHOD_CALL:
151
2.83k
          if (opline->op1_type == IS_CONST) {
152
2.35k
            LITERAL_INFO(opline->op1.constant, 2);
153
2.35k
          }
154
2.83k
          if (opline->op2_type == IS_CONST) {
155
2.66k
            LITERAL_INFO(opline->op2.constant, 2);
156
2.66k
          }
157
2.83k
          break;
158
119
        case ZEND_INIT_PARENT_PROPERTY_HOOK_CALL:
159
119
          LITERAL_INFO(opline->op1.constant, 1);
160
119
          break;
161
13.7k
        case ZEND_CATCH:
162
13.7k
          LITERAL_INFO(opline->op1.constant, 2);
163
13.7k
          break;
164
14.2k
        case ZEND_FETCH_CONSTANT:
165
14.2k
          if (opline->op1.num & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) {
166
178
            LITERAL_INFO(opline->op2.constant, 3);
167
14.1k
          } else {
168
14.1k
            LITERAL_INFO(opline->op2.constant, 2);
169
14.1k
          }
170
14.2k
          break;
171
1.52k
        case ZEND_FETCH_CLASS_CONSTANT:
172
1.52k
          if (opline->op1_type == IS_CONST) {
173
1.25k
            LITERAL_INFO(opline->op1.constant, 2);
174
1.25k
          }
175
1.52k
          if (opline->op2_type == IS_CONST) {
176
1.48k
            LITERAL_INFO(opline->op2.constant, 1);
177
1.48k
          }
178
1.52k
          break;
179
591
        case ZEND_ASSIGN_STATIC_PROP:
180
727
        case ZEND_ASSIGN_STATIC_PROP_REF:
181
1.74k
        case ZEND_FETCH_STATIC_PROP_R:
182
1.92k
        case ZEND_FETCH_STATIC_PROP_W:
183
1.92k
        case ZEND_FETCH_STATIC_PROP_RW:
184
2.10k
        case ZEND_FETCH_STATIC_PROP_IS:
185
2.14k
        case ZEND_FETCH_STATIC_PROP_UNSET:
186
2.31k
        case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
187
2.32k
        case ZEND_UNSET_STATIC_PROP:
188
2.38k
        case ZEND_ISSET_ISEMPTY_STATIC_PROP:
189
2.40k
        case ZEND_PRE_INC_STATIC_PROP:
190
2.40k
        case ZEND_PRE_DEC_STATIC_PROP:
191
2.43k
        case ZEND_POST_INC_STATIC_PROP:
192
2.45k
        case ZEND_POST_DEC_STATIC_PROP:
193
2.51k
        case ZEND_ASSIGN_STATIC_PROP_OP:
194
2.51k
          if (opline->op2_type == IS_CONST) {
195
1.90k
            LITERAL_INFO(opline->op2.constant, 2);
196
1.90k
          }
197
2.51k
          if (opline->op1_type == IS_CONST) {
198
2.22k
            LITERAL_INFO(opline->op1.constant, 1);
199
2.22k
          }
200
2.51k
          break;
201
473
        case ZEND_FETCH_CLASS:
202
663
        case ZEND_INSTANCEOF:
203
663
          if (opline->op2_type == IS_CONST) {
204
165
            LITERAL_INFO(opline->op2.constant, 2);
205
165
          }
206
663
          break;
207
23.6k
        case ZEND_NEW:
208
23.6k
          if (opline->op1_type == IS_CONST) {
209
23.0k
            LITERAL_INFO(opline->op1.constant, 2);
210
23.0k
          }
211
23.6k
          break;
212
2.57k
        case ZEND_DECLARE_CLASS:
213
3.62k
        case ZEND_DECLARE_CLASS_DELAYED:
214
3.62k
          LITERAL_INFO(opline->op1.constant, 2);
215
3.62k
          if (opline->op2_type == IS_CONST) {
216
1.39k
            LITERAL_INFO(opline->op2.constant, 1);
217
1.39k
          }
218
3.62k
          break;
219
1.00k
        case ZEND_ISSET_ISEMPTY_DIM_OBJ:
220
6.09k
        case ZEND_ASSIGN_DIM:
221
6.79k
        case ZEND_UNSET_DIM:
222
12.0k
        case ZEND_FETCH_DIM_R:
223
13.9k
        case ZEND_FETCH_DIM_W:
224
14.6k
        case ZEND_FETCH_DIM_RW:
225
15.2k
        case ZEND_FETCH_DIM_IS:
226
15.5k
        case ZEND_FETCH_DIM_FUNC_ARG:
227
15.7k
        case ZEND_FETCH_DIM_UNSET:
228
16.3k
        case ZEND_FETCH_LIST_R:
229
16.5k
        case ZEND_FETCH_LIST_W:
230
17.6k
        case ZEND_ASSIGN_DIM_OP:
231
17.6k
          if (opline->op1_type == IS_CONST) {
232
274
            LITERAL_INFO(opline->op1.constant, 1);
233
274
          }
234
17.6k
          if (opline->op2_type == IS_CONST) {
235
10.4k
            if (Z_EXTRA(op_array->literals[opline->op2.constant]) == ZEND_EXTRA_VALUE) {
236
306
              LITERAL_INFO(opline->op2.constant, 2);
237
10.1k
            } else {
238
10.1k
              LITERAL_INFO(opline->op2.constant, 1);
239
10.1k
            }
240
10.4k
          }
241
17.6k
          break;
242
985k
        default:
243
985k
          if (opline->op1_type == IS_CONST) {
244
146k
            LITERAL_INFO(opline->op1.constant, 1);
245
146k
          }
246
985k
          if (opline->op2_type == IS_CONST) {
247
229k
            LITERAL_INFO(opline->op2.constant, 1);
248
229k
          }
249
985k
          break;
250
1.08M
      }
251
1.08M
      opline++;
252
1.08M
    }
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
47.7k
    j = 0;
271
47.7k
    zend_hash_init(&hash, op_array->last_literal, NULL, NULL, 0);
272
47.7k
    map = (int*)zend_arena_alloc(&ctx->arena, op_array->last_literal * sizeof(int));
273
47.7k
    memset(map, 0, op_array->last_literal * sizeof(int));
274
553k
    for (uint32_t i = 0; i < op_array->last_literal; i++) {
275
505k
      if (!info[i].num_related) {
276
        /* unset literal */
277
29.1k
        zval_ptr_dtor_nogc(&op_array->literals[i]);
278
29.1k
        continue;
279
29.1k
      }
280
476k
      switch (Z_TYPE(op_array->literals[i])) {
281
19.4k
        case IS_NULL:
282
19.4k
          ZEND_ASSERT(info[i].num_related == 1);
283
19.4k
          if (l_null < 0) {
284
16.0k
            l_null = j;
285
16.0k
            if (i != j) {
286
4.38k
              op_array->literals[j] = op_array->literals[i];
287
4.38k
              info[j] = info[i];
288
4.38k
            }
289
16.0k
            j++;
290
16.0k
          }
291
19.4k
          map[i] = l_null;
292
19.4k
          break;
293
2.05k
        case IS_FALSE:
294
2.05k
          ZEND_ASSERT(info[i].num_related == 1);
295
2.05k
          if (l_false < 0) {
296
1.16k
            l_false = j;
297
1.16k
            if (i != j) {
298
472
              op_array->literals[j] = op_array->literals[i];
299
472
              info[j] = info[i];
300
472
            }
301
1.16k
            j++;
302
1.16k
          }
303
2.05k
          map[i] = l_false;
304
2.05k
          break;
305
1.83k
        case IS_TRUE:
306
1.83k
          ZEND_ASSERT(info[i].num_related == 1);
307
1.83k
          if (l_true < 0) {
308
1.17k
            l_true = j;
309
1.17k
            if (i != j) {
310
473
              op_array->literals[j] = op_array->literals[i];
311
473
              info[j] = info[i];
312
473
            }
313
1.17k
            j++;
314
1.17k
          }
315
1.83k
          map[i] = l_true;
316
1.83k
          break;
317
69.9k
        case IS_LONG:
318
69.9k
          if (info[i].num_related == 1) {
319
69.6k
            if ((pos = zend_hash_index_find(&hash, Z_LVAL(op_array->literals[i]))) != NULL) {
320
19.0k
              map[i] = Z_LVAL_P(pos);
321
50.5k
            } else {
322
50.5k
              map[i] = j;
323
50.5k
              ZVAL_LONG(&zv, j);
324
50.5k
              zend_hash_index_add_new(&hash, Z_LVAL(op_array->literals[i]), &zv);
325
50.5k
              if (i != j) {
326
18.8k
                op_array->literals[j] = op_array->literals[i];
327
18.8k
                info[j] = info[i];
328
18.8k
              }
329
50.5k
              j++;
330
50.5k
            }
331
69.6k
          } else {
332
306
            ZEND_ASSERT(info[i].num_related == 2);
333
306
            key = zend_string_init(Z_STRVAL(op_array->literals[i+1]), Z_STRLEN(op_array->literals[i+1]), 0);
334
306
            bias_key(key, 100 + info[i].num_related - 1);
335
306
            if ((pos = zend_hash_find(&hash, key)) != NULL) {
336
142
              ZEND_ASSERT(info[Z_LVAL_P(pos)].num_related == 2);
337
142
              map[i] = Z_LVAL_P(pos);
338
142
              zval_ptr_dtor_nogc(&op_array->literals[i+1]);
339
164
            } else {
340
164
              map[i] = j;
341
164
              ZVAL_LONG(&zv, j);
342
164
              zend_hash_add_new(&hash, key, &zv);
343
164
              if (i != j) {
344
128
                op_array->literals[j] = op_array->literals[i];
345
128
                info[j] = info[i];
346
128
                op_array->literals[j+1] = op_array->literals[i+1];
347
128
                info[j+1] = info[i+1];
348
128
              }
349
164
              j += 2;
350
164
            }
351
306
            zend_string_release_ex(key, 0);
352
306
            i++;
353
306
          }
354
69.9k
          break;
355
69.9k
        case IS_DOUBLE:
356
3.38k
          ZEND_ASSERT(info[i].num_related == 1);
357
3.38k
          key = zend_string_init((char*)&Z_DVAL(op_array->literals[i]), sizeof(double), 0);
358
3.38k
          bias_key(key, 200);
359
3.38k
          if ((pos = zend_hash_find(&hash, key))) {
360
767
            map[i] = Z_LVAL_P(pos);
361
2.61k
          } else {
362
2.61k
            map[i] = j;
363
2.61k
            ZVAL_LONG(&zv, j);
364
2.61k
            zend_hash_add_new(&hash, key, &zv);
365
2.61k
            if (i != j) {
366
1.31k
              op_array->literals[j] = op_array->literals[i];
367
1.31k
              info[j] = info[i];
368
1.31k
            }
369
2.61k
            j++;
370
2.61k
          }
371
3.38k
          zend_string_release_ex(key, 0);
372
3.38k
          break;
373
369k
        case IS_STRING: {
374
369k
          key = create_str_cache_key(&op_array->literals[i], info[i].num_related);
375
369k
          if ((pos = zend_hash_find(&hash, key)) != NULL) {
376
146k
            ZEND_ASSERT(Z_TYPE(op_array->literals[Z_LVAL_P(pos)]) == IS_STRING &&
377
146k
              info[i].num_related == info[Z_LVAL_P(pos)].num_related);
378
146k
            zend_string_release_ex(key, 0);
379
146k
            map[i] = Z_LVAL_P(pos);
380
146k
            zval_ptr_dtor_nogc(&op_array->literals[i]);
381
146k
            n = info[i].num_related;
382
176k
            while (n > 1) {
383
30.1k
              i++;
384
30.1k
              zval_ptr_dtor_nogc(&op_array->literals[i]);
385
30.1k
              n--;
386
30.1k
            }
387
223k
          } else {
388
223k
            map[i] = j;
389
223k
            ZVAL_LONG(&zv, j);
390
223k
            zend_hash_add_new(&hash, key, &zv);
391
223k
            zend_string_release_ex(key, 0);
392
223k
            if (i != j) {
393
67.5k
              op_array->literals[j] = op_array->literals[i];
394
67.5k
              info[j] = info[i];
395
67.5k
            }
396
223k
            j++;
397
223k
            n = info[i].num_related;
398
279k
            while (n > 1) {
399
56.2k
              i++;
400
56.2k
              if (i != j) op_array->literals[j] = op_array->literals[i];
401
56.2k
              j++;
402
56.2k
              n--;
403
56.2k
            }
404
223k
          }
405
369k
          break;
406
369k
        }
407
369k
        case IS_ARRAY:
408
9.63k
          ZEND_ASSERT(info[i].num_related == 1);
409
9.63k
          if (zend_hash_num_elements(Z_ARRVAL(op_array->literals[i])) == 0) {
410
1.64k
            if (l_empty_arr < 0) {
411
1.31k
              l_empty_arr = j;
412
1.31k
              if (i != j) {
413
483
                op_array->literals[j] = op_array->literals[i];
414
483
                info[j] = info[i];
415
483
              }
416
1.31k
              j++;
417
1.31k
            } else {
418
329
              zval_ptr_dtor_nogc(&op_array->literals[i]);
419
329
            }
420
1.64k
            map[i] = l_empty_arr;
421
1.64k
            break;
422
1.64k
          }
423
7.99k
          ZEND_FALLTHROUGH;
424
8.75k
        default:
425
          /* don't merge other types */
426
8.75k
          ZEND_ASSERT(info[i].num_related == 1);
427
8.75k
          map[i] = j;
428
8.75k
          if (i != j) {
429
1.60k
            op_array->literals[j] = op_array->literals[i];
430
1.60k
            info[j] = info[i];
431
1.60k
          }
432
8.75k
          j++;
433
8.75k
          break;
434
476k
      }
435
476k
    }
436
437
    /* Only clean "hash", as it will be reused in the loop below. */
438
47.7k
    zend_hash_clean(&hash);
439
47.7k
    op_array->last_literal = j;
440
441
47.7k
    const_slot = zend_arena_alloc(&ctx->arena, j * 7 * sizeof(int));
442
47.7k
    memset(const_slot, -1, j * 7 * sizeof(int));
443
47.7k
    class_slot = const_slot + j;
444
47.7k
    func_slot = class_slot + j;
445
47.7k
    bind_var_slot = func_slot + j;
446
47.7k
    property_slot = bind_var_slot + j;
447
47.7k
    method_slot = property_slot + j;
448
47.7k
    jmp_slot = method_slot + j;
449
450
    /* Update opcodes to use new literals table */
451
47.7k
    cache_size = zend_op_array_extension_handles * sizeof(void*);
452
47.7k
    opline = op_array->opcodes;
453
47.7k
    end = opline + op_array->last;
454
1.13M
    while (opline < end) {
455
1.08M
      if (opline->op1_type == IS_CONST) {
456
193k
        opline->op1.constant = map[opline->op1.constant];
457
193k
      }
458
1.08M
      if (opline->op2_type == IS_CONST) {
459
283k
        opline->op2.constant = map[opline->op2.constant];
460
283k
      }
461
1.08M
      switch (opline->opcode) {
462
66
        case ZEND_ASSIGN_STATIC_PROP_OP:
463
66
          if (opline->op1_type == IS_CONST) {
464
            // op1 static property
465
66
            if (opline->op2_type == IS_CONST) {
466
66
              (opline+1)->extended_value = add_static_slot(&hash, op_array,
467
66
                opline->op2.constant,
468
66
                opline->op1.constant,
469
66
                LITERAL_STATIC_PROPERTY,
470
66
                &cache_size);
471
66
            } else {
472
0
              (opline+1)->extended_value = cache_size;
473
0
              cache_size += 3 * sizeof(void *);
474
0
            }
475
66
          } 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
66
          break;
486
467
        case ZEND_ASSIGN_OBJ_OP:
487
467
          if (opline->op2_type == IS_CONST) {
488
            // op2 property
489
391
            if (opline->op1_type == IS_UNUSED &&
490
91
                property_slot[opline->op2.constant] >= 0) {
491
39
              (opline+1)->extended_value = property_slot[opline->op2.constant];
492
352
            } else {
493
352
              (opline+1)->extended_value = cache_size;
494
352
              cache_size += 3 * sizeof(void *);
495
352
              if (opline->op1_type == IS_UNUSED) {
496
52
                property_slot[opline->op2.constant] = (opline+1)->extended_value;
497
52
              }
498
352
            }
499
391
          }
500
467
          break;
501
5.50k
        case ZEND_ASSIGN_OBJ:
502
5.83k
        case ZEND_ASSIGN_OBJ_REF:
503
32.0k
        case ZEND_FETCH_OBJ_R:
504
33.7k
        case ZEND_FETCH_OBJ_W:
505
33.8k
        case ZEND_FETCH_OBJ_RW:
506
34.4k
        case ZEND_FETCH_OBJ_IS:
507
34.6k
        case ZEND_FETCH_OBJ_UNSET:
508
34.7k
        case ZEND_FETCH_OBJ_FUNC_ARG:
509
35.2k
        case ZEND_UNSET_OBJ:
510
35.5k
        case ZEND_PRE_INC_OBJ:
511
35.6k
        case ZEND_PRE_DEC_OBJ:
512
35.8k
        case ZEND_POST_INC_OBJ:
513
35.8k
        case ZEND_POST_DEC_OBJ:
514
35.8k
          if (opline->op2_type == IS_CONST) {
515
            // op2 property
516
34.8k
            if (opline->op1_type == IS_UNUSED &&
517
4.64k
                property_slot[opline->op2.constant] >= 0) {
518
847
              opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
519
33.9k
            } else {
520
33.9k
              opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
521
33.9k
              cache_size += 3 * sizeof(void *);
522
33.9k
              if (opline->op1_type == IS_UNUSED) {
523
3.79k
                property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS;
524
3.79k
              }
525
33.9k
            }
526
34.8k
          }
527
35.8k
          break;
528
472
        case ZEND_ISSET_ISEMPTY_PROP_OBJ:
529
472
          if (opline->op2_type == IS_CONST) {
530
            // op2 property
531
374
            if (opline->op1_type == IS_UNUSED &&
532
56
                property_slot[opline->op2.constant] >= 0) {
533
6
              opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_ISEMPTY);
534
368
            } else {
535
368
              opline->extended_value = cache_size | (opline->extended_value & ZEND_ISEMPTY);
536
368
              cache_size += 3 * sizeof(void *);
537
368
              if (opline->op1_type == IS_UNUSED) {
538
50
                property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_ISEMPTY;
539
50
              }
540
368
            }
541
374
          }
542
472
          break;
543
51.5k
        case ZEND_INIT_FCALL:
544
54.0k
        case ZEND_INIT_FCALL_BY_NAME:
545
55.3k
        case ZEND_INIT_NS_FCALL_BY_NAME:
546
          // op2 func
547
55.3k
          if (func_slot[opline->op2.constant] >= 0) {
548
23.5k
            opline->result.num = func_slot[opline->op2.constant];
549
31.7k
          } else {
550
31.7k
            opline->result.num = cache_size;
551
31.7k
            cache_size += sizeof(void *);
552
31.7k
            func_slot[opline->op2.constant] = opline->result.num;
553
31.7k
          }
554
55.3k
          break;
555
18.2k
        case ZEND_INIT_METHOD_CALL:
556
18.2k
          if (opline->op2_type == IS_CONST) {
557
            // op2 method
558
18.1k
            if (opline->op1_type == IS_UNUSED &&
559
313
                method_slot[opline->op2.constant] >= 0) {
560
23
              opline->result.num = method_slot[opline->op2.constant];
561
18.1k
            } else {
562
18.1k
              opline->result.num = cache_size;
563
18.1k
              cache_size += 2 * sizeof(void *);
564
18.1k
              if (opline->op1_type == IS_UNUSED) {
565
290
                method_slot[opline->op2.constant] = opline->result.num;
566
290
              }
567
18.1k
            }
568
18.1k
          }
569
18.2k
          break;
570
2.83k
        case ZEND_INIT_STATIC_METHOD_CALL:
571
2.83k
          if (opline->op2_type == IS_CONST) {
572
            // op2 static method
573
2.66k
            if (opline->op1_type == IS_CONST) {
574
2.32k
              opline->result.num = add_static_slot(&hash, op_array,
575
2.32k
                opline->op1.constant,
576
2.32k
                opline->op2.constant,
577
2.32k
                LITERAL_STATIC_METHOD,
578
2.32k
                &cache_size);
579
2.32k
            } else {
580
340
              opline->result.num = cache_size;
581
340
              cache_size += 2 * sizeof(void *);
582
340
            }
583
2.66k
          } else if (opline->op1_type == IS_CONST) {
584
            // op1 class
585
37
            if (class_slot[opline->op1.constant] >= 0) {
586
10
              opline->result.num = class_slot[opline->op1.constant];
587
27
            } else {
588
27
              opline->result.num = cache_size;
589
27
              cache_size += sizeof(void *);
590
27
              class_slot[opline->op1.constant] = opline->result.num;
591
27
            }
592
37
          }
593
2.83k
          break;
594
46
        case ZEND_DEFINED:
595
          // op1 const
596
46
          if (const_slot[opline->op1.constant] >= 0) {
597
13
            opline->extended_value = const_slot[opline->op1.constant];
598
33
          } else {
599
33
            opline->extended_value = cache_size;
600
33
            cache_size += sizeof(void *);
601
33
            const_slot[opline->op1.constant] = opline->extended_value;
602
33
          }
603
46
          break;
604
14.2k
        case ZEND_FETCH_CONSTANT:
605
          // op2 const
606
14.2k
          if (const_slot[opline->op2.constant] >= 0) {
607
9.29k
            opline->extended_value = const_slot[opline->op2.constant];
608
9.29k
          } else {
609
5.00k
            opline->extended_value = cache_size;
610
5.00k
            cache_size += sizeof(void *);
611
5.00k
            const_slot[opline->op2.constant] = opline->extended_value;
612
5.00k
          }
613
14.2k
          break;
614
1.52k
        case ZEND_FETCH_CLASS_CONSTANT:
615
1.52k
          if (opline->op1_type == IS_CONST
616
1.25k
            && opline->op2_type == IS_CONST
617
1.23k
            && Z_TYPE(op_array->literals[opline->op2.constant]) == IS_STRING) {
618
            // op1/op2 class_const
619
1.23k
            opline->extended_value = add_static_slot(&hash, op_array,
620
1.23k
              opline->op1.constant,
621
1.23k
              opline->op2.constant,
622
1.23k
              LITERAL_CLASS_CONST,
623
1.23k
              &cache_size);
624
1.23k
          } else {
625
293
            opline->extended_value = cache_size;
626
293
            cache_size += 2 * sizeof(void *);
627
293
          }
628
1.52k
          break;
629
591
        case ZEND_ASSIGN_STATIC_PROP:
630
727
        case ZEND_ASSIGN_STATIC_PROP_REF:
631
1.74k
        case ZEND_FETCH_STATIC_PROP_R:
632
1.92k
        case ZEND_FETCH_STATIC_PROP_W:
633
1.92k
        case ZEND_FETCH_STATIC_PROP_RW:
634
2.10k
        case ZEND_FETCH_STATIC_PROP_IS:
635
2.14k
        case ZEND_FETCH_STATIC_PROP_UNSET:
636
2.31k
        case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
637
2.32k
        case ZEND_UNSET_STATIC_PROP:
638
2.38k
        case ZEND_ISSET_ISEMPTY_STATIC_PROP:
639
2.40k
        case ZEND_PRE_INC_STATIC_PROP:
640
2.40k
        case ZEND_PRE_DEC_STATIC_PROP:
641
2.43k
        case ZEND_POST_INC_STATIC_PROP:
642
2.45k
        case ZEND_POST_DEC_STATIC_PROP:
643
2.45k
          if (opline->op1_type == IS_CONST) {
644
            // op1 static property
645
2.16k
            if (opline->op2_type == IS_CONST) {
646
1.57k
              opline->extended_value = add_static_slot(&hash, op_array,
647
1.57k
                opline->op2.constant,
648
1.57k
                opline->op1.constant,
649
1.57k
                LITERAL_STATIC_PROPERTY,
650
1.57k
                &cache_size) | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
651
1.57k
            } else {
652
589
              opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
653
589
              cache_size += 3 * sizeof(void *);
654
589
            }
655
2.16k
          } else if (opline->op2_type == IS_CONST) {
656
            // op2 class
657
266
            if (class_slot[opline->op2.constant] >= 0) {
658
230
              opline->extended_value = class_slot[opline->op2.constant] | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
659
230
            } else {
660
36
              opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
661
36
              class_slot[opline->op2.constant] = cache_size;
662
36
              cache_size += sizeof(void *);
663
36
            }
664
266
          }
665
2.45k
          break;
666
473
        case ZEND_FETCH_CLASS:
667
663
        case ZEND_INSTANCEOF:
668
663
          if (opline->op2_type == IS_CONST) {
669
            // op2 class
670
165
            if (class_slot[opline->op2.constant] >= 0) {
671
72
              opline->extended_value = class_slot[opline->op2.constant];
672
93
            } else {
673
93
              opline->extended_value = cache_size;
674
93
              cache_size += sizeof(void *);
675
93
              class_slot[opline->op2.constant] = opline->extended_value;
676
93
            }
677
165
          }
678
663
          break;
679
23.6k
        case ZEND_NEW:
680
23.6k
          if (opline->op1_type == IS_CONST) {
681
            // op1 class
682
23.0k
            if (class_slot[opline->op1.constant] >= 0) {
683
4.31k
              opline->op2.num = class_slot[opline->op1.constant];
684
18.7k
            } else {
685
18.7k
              opline->op2.num = cache_size;
686
18.7k
              cache_size += sizeof(void *);
687
18.7k
              class_slot[opline->op1.constant] = opline->op2.num;
688
18.7k
            }
689
23.0k
          }
690
23.6k
          break;
691
13.7k
        case ZEND_CATCH:
692
13.7k
          if (opline->op1_type == IS_CONST) {
693
            // op1 class
694
13.7k
            if (class_slot[opline->op1.constant] >= 0) {
695
3.21k
              opline->extended_value = class_slot[opline->op1.constant] | (opline->extended_value & ZEND_LAST_CATCH);
696
10.5k
            } else {
697
10.5k
              opline->extended_value = cache_size | (opline->extended_value & ZEND_LAST_CATCH);
698
10.5k
              cache_size += sizeof(void *);
699
10.5k
              class_slot[opline->op1.constant] = opline->extended_value & ~ZEND_LAST_CATCH;
700
10.5k
            }
701
13.7k
          }
702
13.7k
          break;
703
448
        case ZEND_BIND_GLOBAL:
704
          // op2 bind var
705
448
          if (bind_var_slot[opline->op2.constant] >= 0) {
706
14
            opline->extended_value = bind_var_slot[opline->op2.constant];
707
434
          } else {
708
434
            opline->extended_value = cache_size;
709
434
            cache_size += sizeof(void *);
710
434
            bind_var_slot[opline->op2.constant] = opline->extended_value;
711
434
          }
712
448
          break;
713
335
        case ZEND_DECLARE_ANON_CLASS:
714
1.38k
        case ZEND_DECLARE_CLASS_DELAYED:
715
1.38k
          opline->extended_value = cache_size;
716
1.38k
          cache_size += sizeof(void *);
717
1.38k
          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
52.1k
        case ZEND_SEND_VAL:
729
59.2k
        case ZEND_SEND_VAL_EX:
730
87.2k
        case ZEND_SEND_VAR:
731
92.5k
        case ZEND_SEND_VAR_EX:
732
92.6k
        case ZEND_SEND_VAR_NO_REF:
733
93.4k
        case ZEND_SEND_VAR_NO_REF_EX:
734
94.5k
        case ZEND_SEND_REF:
735
95.1k
        case ZEND_SEND_FUNC_ARG:
736
95.7k
        case ZEND_CHECK_FUNC_ARG:
737
95.7k
          if (opline->op2_type == IS_CONST) {
738
1.18k
            opline->result.num = cache_size;
739
1.18k
            cache_size += 2 * sizeof(void *);
740
1.18k
          }
741
95.7k
          break;
742
301
        case ZEND_CALLABLE_CONVERT:
743
301
          if (opline->extended_value != (uint32_t)-1) {
744
137
            opline->extended_value = cache_size;
745
137
            cache_size += sizeof(void *);
746
137
          }
747
301
          break;
748
1.08M
      }
749
1.08M
      opline++;
750
1.08M
    }
751
47.7k
    op_array->cache_size = cache_size;
752
47.7k
    zend_hash_destroy(&hash);
753
47.7k
    zend_arena_release(&ctx->arena, checkpoint);
754
755
47.7k
    if (1) {
756
47.7k
      opline = op_array->opcodes;
757
60.6k
      while (1) {
758
60.6k
        if (opline->opcode == ZEND_RECV_INIT) {
759
1.56k
          zval *val = &op_array->literals[opline->op2.constant];
760
761
1.56k
          if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
762
            /* Ensure zval is aligned to 8 bytes */
763
340
            op_array->cache_size = ZEND_MM_ALIGNED_SIZE_EX(op_array->cache_size, 8);
764
340
            Z_CACHE_SLOT_P(val) = op_array->cache_size;
765
340
            op_array->cache_size += sizeof(zval);
766
340
          }
767
59.0k
        } else if (opline->opcode != ZEND_RECV) {
768
47.7k
          break;
769
47.7k
        }
770
12.9k
        opline++;
771
12.9k
      }
772
47.7k
    }
773
774
#if DEBUG_COMPACT_LITERALS
775
    {
776
      fprintf(stderr, "Optimized literals table size %d\n", op_array->last_literal);
777
778
      for (uint32_t i = 0; i < op_array->last_literal; i++) {
779
        zend_string *str = zval_get_string(op_array->literals + i);
780
        fprintf(stderr, "Literal %" PRIu32 ", val (%zu):%s\n", i, ZSTR_LEN(str), ZSTR_VAL(str));
781
        zend_string_release(str);
782
      }
783
      fflush(stderr);
784
    }
785
#endif
786
47.7k
  }
787
47.7k
}