Coverage Report

Created: 2026-02-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/Zend/Optimizer/compact_literals.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Zend OPcache                                                         |
4
   +----------------------------------------------------------------------+
5
   | Copyright (c) The PHP Group                                          |
6
   +----------------------------------------------------------------------+
7
   | This source file is subject to version 3.01 of the PHP license,      |
8
   | that is bundled with this package in the file LICENSE, and is        |
9
   | available through the world-wide-web at the following url:           |
10
   | https://www.php.net/license/3_01.txt                                 |
11
   | If you did not receive a copy of the PHP license and are unable to   |
12
   | obtain it through the world-wide-web, please send a note to          |
13
   | license@php.net so we can mail you a copy immediately.               |
14
   +----------------------------------------------------------------------+
15
   | Authors: Dmitry Stogov <dmitry@php.net>                              |
16
   |          Xinchen Hui <laruence@php.net>                              |
17
   +----------------------------------------------------------------------+
18
*/
19
20
/* pass 11
21
 * - compact literals table
22
 */
23
24
#include "Optimizer/zend_optimizer.h"
25
#include "Optimizer/zend_optimizer_internal.h"
26
#include "zend_API.h"
27
#include "zend_constants.h"
28
#include "zend_execute.h"
29
#include "zend_vm.h"
30
#include "zend_extensions.h"
31
32
#define DEBUG_COMPACT_LITERALS 0
33
34
2.03k
#define LITERAL_CLASS_CONST 1
35
4.25k
#define LITERAL_STATIC_METHOD 2
36
8.72k
#define LITERAL_STATIC_PROPERTY 3
37
38
typedef struct _literal_info {
39
  uint8_t num_related;
40
} literal_info;
41
42
888k
#define LITERAL_INFO(n, related) do { \
43
888k
    info[n].num_related = (related); \
44
888k
  } while (0)
45
46
static uint32_t add_static_slot(HashTable     *hash,
47
                                zend_op_array *op_array,
48
                                uint32_t       op1,
49
                                uint32_t       op2,
50
                                uint32_t       kind,
51
                                uint32_t       *cache_size)
52
8.81k
{
53
8.81k
  uint32_t ret;
54
8.81k
  zval *class_name = &op_array->literals[op1];
55
8.81k
  zval *prop_name = &op_array->literals[op2];
56
8.81k
  zval *pos, tmp;
57
58
8.81k
  zend_string *key = zend_create_member_string(Z_STR_P(class_name), Z_STR_P(prop_name));
59
8.81k
  ZSTR_H(key) = zend_string_hash_func(key);
60
8.81k
  ZSTR_H(key) += kind;
61
62
8.81k
  pos = zend_hash_find(hash, key);
63
8.81k
  if (pos) {
64
2.62k
    ret = Z_LVAL_P(pos);
65
6.19k
  } else {
66
6.19k
    ret = *cache_size;
67
6.19k
    *cache_size += (kind == LITERAL_STATIC_PROPERTY ? 3 : 2) * sizeof(void *);
68
6.19k
    ZVAL_LONG(&tmp, ret);
69
6.19k
    zend_hash_add(hash, key, &tmp);
70
6.19k
  }
71
8.81k
  zend_string_release_ex(key, 0);
72
8.81k
  return ret;
73
8.81k
}
74
75
static inline void bias_key(zend_string *key, uint32_t bias)
76
162k
{
77
  /* Add a bias to the hash so we can distinguish string keys
78
   * that would otherwise be the same. */
79
162k
  ZSTR_H(key) = zend_string_hash_val(key) + bias;
80
162k
}
81
82
static zend_string *create_str_cache_key(zval *literal, uint8_t num_related)
83
695k
{
84
695k
  ZEND_ASSERT(Z_TYPE_P(literal) == IS_STRING);
85
695k
  if (num_related == 1) {
86
539k
    return zend_string_copy(Z_STR_P(literal));
87
539k
  }
88
89
  /* Concatenate all the related literals for the cache key. */
90
155k
  zend_string *key;
91
155k
  if (num_related == 2) {
92
153k
    ZEND_ASSERT(Z_TYPE_P(literal + 1) == IS_STRING);
93
153k
    key = zend_string_concat2(
94
153k
      Z_STRVAL_P(literal), Z_STRLEN_P(literal),
95
153k
      Z_STRVAL_P(literal + 1), Z_STRLEN_P(literal + 1));
96
153k
  } else if (num_related == 3) {
97
2.21k
    ZEND_ASSERT(Z_TYPE_P(literal + 1) == IS_STRING && Z_TYPE_P(literal + 2) == IS_STRING);
98
2.21k
    key = zend_string_concat3(
99
2.21k
      Z_STRVAL_P(literal), Z_STRLEN_P(literal),
100
2.21k
      Z_STRVAL_P(literal + 1), Z_STRLEN_P(literal + 1),
101
2.21k
      Z_STRVAL_P(literal + 2), Z_STRLEN_P(literal + 2));
102
2.21k
  } else {
103
0
    ZEND_ASSERT(0 && "Currently not needed");
104
0
  }
105
106
155k
  bias_key(key, num_related - 1);
107
155k
  return key;
108
155k
}
109
110
void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx)
111
83.6k
{
112
83.6k
  zend_op *opline, *end;
113
83.6k
  int n, *map;
114
83.6k
  uint32_t cache_size;
115
83.6k
  zval zv, *pos;
116
83.6k
  literal_info *info;
117
83.6k
  int l_null = -1;
118
83.6k
  int l_false = -1;
119
83.6k
  int l_true = -1;
120
83.6k
  int l_empty_arr = -1;
121
83.6k
  HashTable hash;
122
83.6k
  zend_string *key = NULL;
123
83.6k
  void *checkpoint = zend_arena_checkpoint(ctx->arena);
124
83.6k
  int *const_slot, *class_slot, *func_slot, *bind_var_slot, *property_slot, *method_slot, *jmp_slot;
125
126
83.6k
  if (op_array->last_literal) {
127
83.6k
    uint32_t j;
128
83.6k
    info = (literal_info*)zend_arena_calloc(&ctx->arena, op_array->last_literal, sizeof(literal_info));
129
130
      /* Mark literals of specific types */
131
83.6k
    opline = op_array->opcodes;
132
83.6k
    end = opline + op_array->last;
133
2.14M
    while (opline < end) {
134
2.05M
      switch (opline->opcode) {
135
0
        case ZEND_JMP_FRAMELESS:
136
0
          LITERAL_INFO(opline->op1.constant, 1);
137
0
          break;
138
3.73k
        case ZEND_INIT_FCALL_BY_NAME:
139
3.73k
          LITERAL_INFO(opline->op2.constant, 2);
140
3.73k
          break;
141
2.02k
        case ZEND_INIT_NS_FCALL_BY_NAME:
142
2.02k
          LITERAL_INFO(opline->op2.constant, 3);
143
2.02k
          break;
144
34.9k
        case ZEND_INIT_METHOD_CALL:
145
34.9k
          if (opline->op1_type == IS_CONST) {
146
120
            LITERAL_INFO(opline->op1.constant, 1);
147
120
          }
148
34.9k
          if (opline->op2_type == IS_CONST) {
149
34.7k
            LITERAL_INFO(opline->op2.constant, 2);
150
34.7k
          }
151
34.9k
          break;
152
5.53k
        case ZEND_INIT_STATIC_METHOD_CALL:
153
5.53k
          if (opline->op1_type == IS_CONST) {
154
4.34k
            LITERAL_INFO(opline->op1.constant, 2);
155
4.34k
          }
156
5.53k
          if (opline->op2_type == IS_CONST) {
157
5.16k
            LITERAL_INFO(opline->op2.constant, 2);
158
5.16k
          }
159
5.53k
          break;
160
206
        case ZEND_INIT_PARENT_PROPERTY_HOOK_CALL:
161
206
          LITERAL_INFO(opline->op1.constant, 1);
162
206
          break;
163
29.3k
        case ZEND_CATCH:
164
29.3k
          LITERAL_INFO(opline->op1.constant, 2);
165
29.3k
          break;
166
18.4k
        case ZEND_FETCH_CONSTANT:
167
18.4k
          if (opline->op1.num & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) {
168
194
            LITERAL_INFO(opline->op2.constant, 3);
169
18.2k
          } else {
170
18.2k
            LITERAL_INFO(opline->op2.constant, 2);
171
18.2k
          }
172
18.4k
          break;
173
2.51k
        case ZEND_FETCH_CLASS_CONSTANT:
174
2.51k
          if (opline->op1_type == IS_CONST) {
175
2.06k
            LITERAL_INFO(opline->op1.constant, 2);
176
2.06k
          }
177
2.51k
          if (opline->op2_type == IS_CONST) {
178
2.44k
            LITERAL_INFO(opline->op2.constant, 1);
179
2.44k
          }
180
2.51k
          break;
181
927
        case ZEND_ASSIGN_STATIC_PROP:
182
1.09k
        case ZEND_ASSIGN_STATIC_PROP_REF:
183
2.91k
        case ZEND_FETCH_STATIC_PROP_R:
184
3.21k
        case ZEND_FETCH_STATIC_PROP_W:
185
3.21k
        case ZEND_FETCH_STATIC_PROP_RW:
186
3.44k
        case ZEND_FETCH_STATIC_PROP_IS:
187
3.51k
        case ZEND_FETCH_STATIC_PROP_UNSET:
188
3.75k
        case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
189
3.77k
        case ZEND_UNSET_STATIC_PROP:
190
3.88k
        case ZEND_ISSET_ISEMPTY_STATIC_PROP:
191
3.92k
        case ZEND_PRE_INC_STATIC_PROP:
192
3.92k
        case ZEND_PRE_DEC_STATIC_PROP:
193
3.98k
        case ZEND_POST_INC_STATIC_PROP:
194
4.00k
        case ZEND_POST_DEC_STATIC_PROP:
195
4.08k
        case ZEND_ASSIGN_STATIC_PROP_OP:
196
4.08k
          if (opline->op2_type == IS_CONST) {
197
2.87k
            LITERAL_INFO(opline->op2.constant, 2);
198
2.87k
          }
199
4.08k
          if (opline->op1_type == IS_CONST) {
200
3.70k
            LITERAL_INFO(opline->op1.constant, 1);
201
3.70k
          }
202
4.08k
          break;
203
871
        case ZEND_FETCH_CLASS:
204
1.56k
        case ZEND_INSTANCEOF:
205
1.56k
          if (opline->op2_type == IS_CONST) {
206
644
            LITERAL_INFO(opline->op2.constant, 2);
207
644
          }
208
1.56k
          break;
209
47.8k
        case ZEND_NEW:
210
47.8k
          if (opline->op1_type == IS_CONST) {
211
46.7k
            LITERAL_INFO(opline->op1.constant, 2);
212
46.7k
          }
213
47.8k
          break;
214
4.25k
        case ZEND_DECLARE_CLASS:
215
5.52k
        case ZEND_DECLARE_CLASS_DELAYED:
216
5.52k
          LITERAL_INFO(opline->op1.constant, 2);
217
5.52k
          if (opline->op2_type == IS_CONST) {
218
1.91k
            LITERAL_INFO(opline->op2.constant, 1);
219
1.91k
          }
220
5.52k
          break;
221
1.72k
        case ZEND_ISSET_ISEMPTY_DIM_OBJ:
222
9.27k
        case ZEND_ASSIGN_DIM:
223
10.4k
        case ZEND_UNSET_DIM:
224
17.9k
        case ZEND_FETCH_DIM_R:
225
20.2k
        case ZEND_FETCH_DIM_W:
226
20.9k
        case ZEND_FETCH_DIM_RW:
227
21.9k
        case ZEND_FETCH_DIM_IS:
228
22.2k
        case ZEND_FETCH_DIM_FUNC_ARG:
229
22.4k
        case ZEND_FETCH_DIM_UNSET:
230
23.6k
        case ZEND_FETCH_LIST_R:
231
23.8k
        case ZEND_FETCH_LIST_W:
232
24.9k
        case ZEND_ASSIGN_DIM_OP:
233
24.9k
          if (opline->op1_type == IS_CONST) {
234
306
            LITERAL_INFO(opline->op1.constant, 1);
235
306
          }
236
24.9k
          if (opline->op2_type == IS_CONST) {
237
14.3k
            if (Z_EXTRA(op_array->literals[opline->op2.constant]) == ZEND_EXTRA_VALUE) {
238
486
              LITERAL_INFO(opline->op2.constant, 2);
239
13.8k
            } else {
240
13.8k
              LITERAL_INFO(opline->op2.constant, 1);
241
13.8k
            }
242
14.3k
          }
243
24.9k
          break;
244
1.87M
        default:
245
1.87M
          if (opline->op1_type == IS_CONST) {
246
273k
            LITERAL_INFO(opline->op1.constant, 1);
247
273k
          }
248
1.87M
          if (opline->op2_type == IS_CONST) {
249
436k
            LITERAL_INFO(opline->op2.constant, 1);
250
436k
          }
251
1.87M
          break;
252
2.05M
      }
253
2.05M
      opline++;
254
2.05M
    }
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
83.6k
    j = 0;
273
83.6k
    zend_hash_init(&hash, op_array->last_literal, NULL, NULL, 0);
274
83.6k
    map = (int*)zend_arena_alloc(&ctx->arena, op_array->last_literal * sizeof(int));
275
83.6k
    memset(map, 0, op_array->last_literal * sizeof(int));
276
1.02M
    for (uint32_t i = 0; i < op_array->last_literal; i++) {
277
937k
      if (!info[i].num_related) {
278
        /* unset literal */
279
49.0k
        zval_ptr_dtor_nogc(&op_array->literals[i]);
280
49.0k
        continue;
281
49.0k
      }
282
888k
      switch (Z_TYPE(op_array->literals[i])) {
283
32.8k
        case IS_NULL:
284
32.8k
          ZEND_ASSERT(info[i].num_related == 1);
285
32.8k
          if (l_null < 0) {
286
27.9k
            l_null = j;
287
27.9k
            if (i != j) {
288
7.76k
              op_array->literals[j] = op_array->literals[i];
289
7.76k
              info[j] = info[i];
290
7.76k
            }
291
27.9k
            j++;
292
27.9k
          }
293
32.8k
          map[i] = l_null;
294
32.8k
          break;
295
3.10k
        case IS_FALSE:
296
3.10k
          ZEND_ASSERT(info[i].num_related == 1);
297
3.10k
          if (l_false < 0) {
298
1.76k
            l_false = j;
299
1.76k
            if (i != j) {
300
745
              op_array->literals[j] = op_array->literals[i];
301
745
              info[j] = info[i];
302
745
            }
303
1.76k
            j++;
304
1.76k
          }
305
3.10k
          map[i] = l_false;
306
3.10k
          break;
307
3.32k
        case IS_TRUE:
308
3.32k
          ZEND_ASSERT(info[i].num_related == 1);
309
3.32k
          if (l_true < 0) {
310
2.04k
            l_true = j;
311
2.04k
            if (i != j) {
312
821
              op_array->literals[j] = op_array->literals[i];
313
821
              info[j] = info[i];
314
821
            }
315
2.04k
            j++;
316
2.04k
          }
317
3.32k
          map[i] = l_true;
318
3.32k
          break;
319
128k
        case IS_LONG:
320
128k
          if (info[i].num_related == 1) {
321
127k
            if ((pos = zend_hash_index_find(&hash, Z_LVAL(op_array->literals[i]))) != NULL) {
322
35.7k
              map[i] = Z_LVAL_P(pos);
323
91.8k
            } else {
324
91.8k
              map[i] = j;
325
91.8k
              ZVAL_LONG(&zv, j);
326
91.8k
              zend_hash_index_add_new(&hash, Z_LVAL(op_array->literals[i]), &zv);
327
91.8k
              if (i != j) {
328
35.1k
                op_array->literals[j] = op_array->literals[i];
329
35.1k
                info[j] = info[i];
330
35.1k
              }
331
91.8k
              j++;
332
91.8k
            }
333
127k
          } else {
334
486
            ZEND_ASSERT(info[i].num_related == 2);
335
486
            key = zend_string_init(Z_STRVAL(op_array->literals[i+1]), Z_STRLEN(op_array->literals[i+1]), 0);
336
486
            bias_key(key, 100 + info[i].num_related - 1);
337
486
            if ((pos = zend_hash_find(&hash, key)) != NULL) {
338
212
              ZEND_ASSERT(info[Z_LVAL_P(pos)].num_related == 2);
339
212
              map[i] = Z_LVAL_P(pos);
340
212
              zval_ptr_dtor_nogc(&op_array->literals[i+1]);
341
274
            } else {
342
274
              map[i] = j;
343
274
              ZVAL_LONG(&zv, j);
344
274
              zend_hash_add_new(&hash, key, &zv);
345
274
              if (i != j) {
346
212
                op_array->literals[j] = op_array->literals[i];
347
212
                info[j] = info[i];
348
212
                op_array->literals[j+1] = op_array->literals[i+1];
349
212
                info[j+1] = info[i+1];
350
212
              }
351
274
              j += 2;
352
274
            }
353
486
            zend_string_release_ex(key, 0);
354
486
            i++;
355
486
          }
356
128k
          break;
357
128k
        case IS_DOUBLE:
358
6.75k
          ZEND_ASSERT(info[i].num_related == 1);
359
6.75k
          key = zend_string_init((char*)&Z_DVAL(op_array->literals[i]), sizeof(double), 0);
360
6.75k
          bias_key(key, 200);
361
6.75k
          if ((pos = zend_hash_find(&hash, key))) {
362
1.34k
            map[i] = Z_LVAL_P(pos);
363
5.41k
          } else {
364
5.41k
            map[i] = j;
365
5.41k
            ZVAL_LONG(&zv, j);
366
5.41k
            zend_hash_add_new(&hash, key, &zv);
367
5.41k
            if (i != j) {
368
2.84k
              op_array->literals[j] = op_array->literals[i];
369
2.84k
              info[j] = info[i];
370
2.84k
            }
371
5.41k
            j++;
372
5.41k
          }
373
6.75k
          zend_string_release_ex(key, 0);
374
6.75k
          break;
375
695k
        case IS_STRING: {
376
695k
          key = create_str_cache_key(&op_array->literals[i], info[i].num_related);
377
695k
          if ((pos = zend_hash_find(&hash, key)) != NULL) {
378
259k
            ZEND_ASSERT(Z_TYPE(op_array->literals[Z_LVAL_P(pos)]) == IS_STRING &&
379
259k
              info[i].num_related == info[Z_LVAL_P(pos)].num_related);
380
259k
            zend_string_release_ex(key, 0);
381
259k
            map[i] = Z_LVAL_P(pos);
382
259k
            zval_ptr_dtor_nogc(&op_array->literals[i]);
383
259k
            n = info[i].num_related;
384
309k
            while (n > 1) {
385
49.6k
              i++;
386
49.6k
              zval_ptr_dtor_nogc(&op_array->literals[i]);
387
49.6k
              n--;
388
49.6k
            }
389
435k
          } else {
390
435k
            map[i] = j;
391
435k
            ZVAL_LONG(&zv, j);
392
435k
            zend_hash_add_new(&hash, key, &zv);
393
435k
            zend_string_release_ex(key, 0);
394
435k
            if (i != j) {
395
128k
              op_array->literals[j] = op_array->literals[i];
396
128k
              info[j] = info[i];
397
128k
            }
398
435k
            j++;
399
435k
            n = info[i].num_related;
400
543k
            while (n > 1) {
401
108k
              i++;
402
108k
              if (i != j) op_array->literals[j] = op_array->literals[i];
403
108k
              j++;
404
108k
              n--;
405
108k
            }
406
435k
          }
407
695k
          break;
408
695k
        }
409
695k
        case IS_ARRAY:
410
18.3k
          ZEND_ASSERT(info[i].num_related == 1);
411
18.3k
          if (zend_hash_num_elements(Z_ARRVAL(op_array->literals[i])) == 0) {
412
2.86k
            if (l_empty_arr < 0) {
413
2.25k
              l_empty_arr = j;
414
2.25k
              if (i != j) {
415
1.00k
                op_array->literals[j] = op_array->literals[i];
416
1.00k
                info[j] = info[i];
417
1.00k
              }
418
2.25k
              j++;
419
2.25k
            } else {
420
610
              zval_ptr_dtor_nogc(&op_array->literals[i]);
421
610
            }
422
2.86k
            map[i] = l_empty_arr;
423
2.86k
            break;
424
2.86k
          }
425
15.4k
          ZEND_FALLTHROUGH;
426
16.7k
        default:
427
          /* don't merge other types */
428
16.7k
          ZEND_ASSERT(info[i].num_related == 1);
429
16.7k
          map[i] = j;
430
16.7k
          if (i != j) {
431
2.54k
            op_array->literals[j] = op_array->literals[i];
432
2.54k
            info[j] = info[i];
433
2.54k
          }
434
16.7k
          j++;
435
16.7k
          break;
436
888k
      }
437
888k
    }
438
439
    /* Only clean "hash", as it will be reused in the loop below. */
440
83.6k
    zend_hash_clean(&hash);
441
83.6k
    op_array->last_literal = j;
442
443
83.6k
    const_slot = zend_arena_alloc(&ctx->arena, j * 7 * sizeof(int));
444
83.6k
    memset(const_slot, -1, j * 7 * sizeof(int));
445
83.6k
    class_slot = const_slot + j;
446
83.6k
    func_slot = class_slot + j;
447
83.6k
    bind_var_slot = func_slot + j;
448
83.6k
    property_slot = bind_var_slot + j;
449
83.6k
    method_slot = property_slot + j;
450
83.6k
    jmp_slot = method_slot + j;
451
452
    /* Update opcodes to use new literals table */
453
83.6k
    cache_size = zend_op_array_extension_handles * sizeof(void*);
454
83.6k
    opline = op_array->opcodes;
455
83.6k
    end = opline + op_array->last;
456
2.14M
    while (opline < end) {
457
2.05M
      if (opline->op1_type == IS_CONST) {
458
366k
        opline->op1.constant = map[opline->op1.constant];
459
366k
      }
460
2.05M
      if (opline->op2_type == IS_CONST) {
461
522k
        opline->op2.constant = map[opline->op2.constant];
462
522k
      }
463
2.05M
      switch (opline->opcode) {
464
74
        case ZEND_ASSIGN_STATIC_PROP_OP:
465
74
          if (opline->op1_type == IS_CONST) {
466
            // op1 static property
467
74
            if (opline->op2_type == IS_CONST) {
468
74
              (opline+1)->extended_value = add_static_slot(&hash, op_array,
469
74
                opline->op2.constant,
470
74
                opline->op1.constant,
471
74
                LITERAL_STATIC_PROPERTY,
472
74
                &cache_size);
473
74
            } else {
474
0
              (opline+1)->extended_value = cache_size;
475
0
              cache_size += 3 * sizeof(void *);
476
0
            }
477
74
          } else if (opline->op2_type == IS_CONST) {
478
            // op2 class
479
0
            if (class_slot[opline->op2.constant] >= 0) {
480
0
              (opline+1)->extended_value = class_slot[opline->op2.constant];
481
0
            } else {
482
0
              (opline+1)->extended_value = cache_size;
483
0
              class_slot[opline->op2.constant] = cache_size;
484
0
              cache_size += sizeof(void *);
485
0
            }
486
0
          }
487
74
          break;
488
705
        case ZEND_ASSIGN_OBJ_OP:
489
705
          if (opline->op2_type == IS_CONST) {
490
            // op2 property
491
633
            if (opline->op1_type == IS_UNUSED &&
492
157
                property_slot[opline->op2.constant] >= 0) {
493
58
              (opline+1)->extended_value = property_slot[opline->op2.constant];
494
575
            } else {
495
575
              (opline+1)->extended_value = cache_size;
496
575
              cache_size += 3 * sizeof(void *);
497
575
              if (opline->op1_type == IS_UNUSED) {
498
99
                property_slot[opline->op2.constant] = (opline+1)->extended_value;
499
99
              }
500
575
            }
501
633
          }
502
705
          break;
503
9.15k
        case ZEND_ASSIGN_OBJ:
504
9.69k
        case ZEND_ASSIGN_OBJ_REF:
505
59.4k
        case ZEND_FETCH_OBJ_R:
506
61.9k
        case ZEND_FETCH_OBJ_W:
507
62.0k
        case ZEND_FETCH_OBJ_RW:
508
62.9k
        case ZEND_FETCH_OBJ_IS:
509
63.1k
        case ZEND_FETCH_OBJ_UNSET:
510
63.3k
        case ZEND_FETCH_OBJ_FUNC_ARG:
511
64.1k
        case ZEND_UNSET_OBJ:
512
64.6k
        case ZEND_PRE_INC_OBJ:
513
64.9k
        case ZEND_PRE_DEC_OBJ:
514
65.1k
        case ZEND_POST_INC_OBJ:
515
65.1k
        case ZEND_POST_DEC_OBJ:
516
65.1k
          if (opline->op2_type == IS_CONST) {
517
            // op2 property
518
63.6k
            if (opline->op1_type == IS_UNUSED &&
519
8.13k
                property_slot[opline->op2.constant] >= 0) {
520
1.40k
              opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
521
62.1k
            } else {
522
62.1k
              opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
523
62.1k
              cache_size += 3 * sizeof(void *);
524
62.1k
              if (opline->op1_type == IS_UNUSED) {
525
6.72k
                property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS;
526
6.72k
              }
527
62.1k
            }
528
63.6k
          }
529
65.1k
          break;
530
726
        case ZEND_ISSET_ISEMPTY_PROP_OBJ:
531
726
          if (opline->op2_type == IS_CONST) {
532
            // op2 property
533
598
            if (opline->op1_type == IS_UNUSED &&
534
72
                property_slot[opline->op2.constant] >= 0) {
535
12
              opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_ISEMPTY);
536
586
            } else {
537
586
              opline->extended_value = cache_size | (opline->extended_value & ZEND_ISEMPTY);
538
586
              cache_size += 3 * sizeof(void *);
539
586
              if (opline->op1_type == IS_UNUSED) {
540
60
                property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_ISEMPTY;
541
60
              }
542
586
            }
543
598
          }
544
726
          break;
545
93.8k
        case ZEND_INIT_FCALL:
546
97.5k
        case ZEND_INIT_FCALL_BY_NAME:
547
99.6k
        case ZEND_INIT_NS_FCALL_BY_NAME:
548
          // op2 func
549
99.6k
          if (func_slot[opline->op2.constant] >= 0) {
550
43.6k
            opline->result.num = func_slot[opline->op2.constant];
551
55.9k
          } else {
552
55.9k
            opline->result.num = cache_size;
553
55.9k
            cache_size += sizeof(void *);
554
55.9k
            func_slot[opline->op2.constant] = opline->result.num;
555
55.9k
          }
556
99.6k
          break;
557
34.9k
        case ZEND_INIT_METHOD_CALL:
558
34.9k
          if (opline->op2_type == IS_CONST) {
559
            // op2 method
560
34.7k
            if (opline->op1_type == IS_UNUSED &&
561
584
                method_slot[opline->op2.constant] >= 0) {
562
26
              opline->result.num = method_slot[opline->op2.constant];
563
34.6k
            } else {
564
34.6k
              opline->result.num = cache_size;
565
34.6k
              cache_size += 2 * sizeof(void *);
566
34.6k
              if (opline->op1_type == IS_UNUSED) {
567
558
                method_slot[opline->op2.constant] = opline->result.num;
568
558
              }
569
34.6k
            }
570
34.7k
          }
571
34.9k
          break;
572
5.53k
        case ZEND_INIT_STATIC_METHOD_CALL:
573
5.53k
          if (opline->op2_type == IS_CONST) {
574
            // op2 static method
575
5.16k
            if (opline->op1_type == IS_CONST) {
576
4.25k
              opline->result.num = add_static_slot(&hash, op_array,
577
4.25k
                opline->op1.constant,
578
4.25k
                opline->op2.constant,
579
4.25k
                LITERAL_STATIC_METHOD,
580
4.25k
                &cache_size);
581
4.25k
            } else {
582
913
              opline->result.num = cache_size;
583
913
              cache_size += 2 * sizeof(void *);
584
913
            }
585
5.16k
          } else if (opline->op1_type == IS_CONST) {
586
            // op1 class
587
84
            if (class_slot[opline->op1.constant] >= 0) {
588
38
              opline->result.num = class_slot[opline->op1.constant];
589
46
            } else {
590
46
              opline->result.num = cache_size;
591
46
              cache_size += sizeof(void *);
592
46
              class_slot[opline->op1.constant] = opline->result.num;
593
46
            }
594
84
          }
595
5.53k
          break;
596
101
        case ZEND_DEFINED:
597
          // op1 const
598
101
          if (const_slot[opline->op1.constant] >= 0) {
599
46
            opline->extended_value = const_slot[opline->op1.constant];
600
55
          } else {
601
55
            opline->extended_value = cache_size;
602
55
            cache_size += sizeof(void *);
603
55
            const_slot[opline->op1.constant] = opline->extended_value;
604
55
          }
605
101
          break;
606
18.4k
        case ZEND_FETCH_CONSTANT:
607
          // op2 const
608
18.4k
          if (const_slot[opline->op2.constant] >= 0) {
609
10.9k
            opline->extended_value = const_slot[opline->op2.constant];
610
10.9k
          } else {
611
7.57k
            opline->extended_value = cache_size;
612
7.57k
            cache_size += sizeof(void *);
613
7.57k
            const_slot[opline->op2.constant] = opline->extended_value;
614
7.57k
          }
615
18.4k
          break;
616
2.51k
        case ZEND_FETCH_CLASS_CONSTANT:
617
2.51k
          if (opline->op1_type == IS_CONST
618
2.06k
            && opline->op2_type == IS_CONST
619
2.03k
            && Z_TYPE(op_array->literals[opline->op2.constant]) == IS_STRING) {
620
            // op1/op2 class_const
621
2.03k
            opline->extended_value = add_static_slot(&hash, op_array,
622
2.03k
              opline->op1.constant,
623
2.03k
              opline->op2.constant,
624
2.03k
              LITERAL_CLASS_CONST,
625
2.03k
              &cache_size);
626
2.03k
          } else {
627
487
            opline->extended_value = cache_size;
628
487
            cache_size += 2 * sizeof(void *);
629
487
          }
630
2.51k
          break;
631
927
        case ZEND_ASSIGN_STATIC_PROP:
632
1.09k
        case ZEND_ASSIGN_STATIC_PROP_REF:
633
2.91k
        case ZEND_FETCH_STATIC_PROP_R:
634
3.21k
        case ZEND_FETCH_STATIC_PROP_W:
635
3.21k
        case ZEND_FETCH_STATIC_PROP_RW:
636
3.44k
        case ZEND_FETCH_STATIC_PROP_IS:
637
3.51k
        case ZEND_FETCH_STATIC_PROP_UNSET:
638
3.75k
        case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
639
3.77k
        case ZEND_UNSET_STATIC_PROP:
640
3.88k
        case ZEND_ISSET_ISEMPTY_STATIC_PROP:
641
3.92k
        case ZEND_PRE_INC_STATIC_PROP:
642
3.92k
        case ZEND_PRE_DEC_STATIC_PROP:
643
3.98k
        case ZEND_POST_INC_STATIC_PROP:
644
4.00k
        case ZEND_POST_DEC_STATIC_PROP:
645
4.00k
          if (opline->op1_type == IS_CONST) {
646
            // op1 static property
647
3.63k
            if (opline->op2_type == IS_CONST) {
648
2.45k
              opline->extended_value = add_static_slot(&hash, op_array,
649
2.45k
                opline->op2.constant,
650
2.45k
                opline->op1.constant,
651
2.45k
                LITERAL_STATIC_PROPERTY,
652
2.45k
                &cache_size) | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
653
2.45k
            } else {
654
1.17k
              opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
655
1.17k
              cache_size += 3 * sizeof(void *);
656
1.17k
            }
657
3.63k
          } else if (opline->op2_type == IS_CONST) {
658
            // op2 class
659
344
            if (class_slot[opline->op2.constant] >= 0) {
660
298
              opline->extended_value = class_slot[opline->op2.constant] | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
661
298
            } else {
662
46
              opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
663
46
              class_slot[opline->op2.constant] = cache_size;
664
46
              cache_size += sizeof(void *);
665
46
            }
666
344
          }
667
4.00k
          break;
668
871
        case ZEND_FETCH_CLASS:
669
1.56k
        case ZEND_INSTANCEOF:
670
1.56k
          if (opline->op2_type == IS_CONST) {
671
            // op2 class
672
644
            if (class_slot[opline->op2.constant] >= 0) {
673
472
              opline->extended_value = class_slot[opline->op2.constant];
674
472
            } else {
675
172
              opline->extended_value = cache_size;
676
172
              cache_size += sizeof(void *);
677
172
              class_slot[opline->op2.constant] = opline->extended_value;
678
172
            }
679
644
          }
680
1.56k
          break;
681
47.8k
        case ZEND_NEW:
682
47.8k
          if (opline->op1_type == IS_CONST) {
683
            // op1 class
684
46.7k
            if (class_slot[opline->op1.constant] >= 0) {
685
9.02k
              opline->op2.num = class_slot[opline->op1.constant];
686
37.7k
            } else {
687
37.7k
              opline->op2.num = cache_size;
688
37.7k
              cache_size += sizeof(void *);
689
37.7k
              class_slot[opline->op1.constant] = opline->op2.num;
690
37.7k
            }
691
46.7k
          }
692
47.8k
          break;
693
29.3k
        case ZEND_CATCH:
694
29.3k
          if (opline->op1_type == IS_CONST) {
695
            // op1 class
696
29.3k
            if (class_slot[opline->op1.constant] >= 0) {
697
6.07k
              opline->extended_value = class_slot[opline->op1.constant] | (opline->extended_value & ZEND_LAST_CATCH);
698
23.3k
            } else {
699
23.3k
              opline->extended_value = cache_size | (opline->extended_value & ZEND_LAST_CATCH);
700
23.3k
              cache_size += sizeof(void *);
701
23.3k
              class_slot[opline->op1.constant] = opline->extended_value & ~ZEND_LAST_CATCH;
702
23.3k
            }
703
29.3k
          }
704
29.3k
          break;
705
728
        case ZEND_BIND_GLOBAL:
706
          // op2 bind var
707
728
          if (bind_var_slot[opline->op2.constant] >= 0) {
708
14
            opline->extended_value = bind_var_slot[opline->op2.constant];
709
714
          } else {
710
714
            opline->extended_value = cache_size;
711
714
            cache_size += sizeof(void *);
712
714
            bind_var_slot[opline->op2.constant] = opline->extended_value;
713
714
          }
714
728
          break;
715
579
        case ZEND_DECLARE_ANON_CLASS:
716
1.85k
        case ZEND_DECLARE_CLASS_DELAYED:
717
1.85k
          opline->extended_value = cache_size;
718
1.85k
          cache_size += sizeof(void *);
719
1.85k
          break;
720
0
        case ZEND_JMP_FRAMELESS:
721
          // op1 func
722
0
          if (jmp_slot[opline->op1.constant] >= 0) {
723
0
            opline->extended_value = jmp_slot[opline->op1.constant];
724
0
          } else {
725
0
            opline->extended_value = cache_size;
726
0
            cache_size += sizeof(void *);
727
0
            jmp_slot[opline->op1.constant] = opline->extended_value;
728
0
          }
729
0
          break;
730
94.0k
        case ZEND_SEND_VAL:
731
106k
        case ZEND_SEND_VAL_EX:
732
164k
        case ZEND_SEND_VAR:
733
173k
        case ZEND_SEND_VAR_EX:
734
174k
        case ZEND_SEND_VAR_NO_REF:
735
175k
        case ZEND_SEND_VAR_NO_REF_EX:
736
177k
        case ZEND_SEND_REF:
737
177k
        case ZEND_SEND_FUNC_ARG:
738
178k
        case ZEND_CHECK_FUNC_ARG:
739
178k
          if (opline->op2_type == IS_CONST) {
740
1.70k
            opline->result.num = cache_size;
741
1.70k
            cache_size += 2 * sizeof(void *);
742
1.70k
          }
743
178k
          break;
744
474
        case ZEND_CALLABLE_CONVERT:
745
474
          if (opline->extended_value != (uint32_t)-1) {
746
176
            opline->extended_value = cache_size;
747
176
            cache_size += sizeof(void *);
748
176
          }
749
474
          break;
750
2.05M
      }
751
2.05M
      opline++;
752
2.05M
    }
753
83.6k
    op_array->cache_size = cache_size;
754
83.6k
    zend_hash_destroy(&hash);
755
83.6k
    zend_arena_release(&ctx->arena, checkpoint);
756
757
83.6k
    if (1) {
758
83.6k
      opline = op_array->opcodes;
759
105k
      while (1) {
760
105k
        if (opline->opcode == ZEND_RECV_INIT) {
761
2.40k
          zval *val = &op_array->literals[opline->op2.constant];
762
763
2.40k
          if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
764
            /* Ensure zval is aligned to 8 bytes */
765
678
            op_array->cache_size = ZEND_MM_ALIGNED_SIZE_EX(op_array->cache_size, 8);
766
678
            Z_CACHE_SLOT_P(val) = op_array->cache_size;
767
678
            op_array->cache_size += sizeof(zval);
768
678
          }
769
103k
        } else if (opline->opcode != ZEND_RECV) {
770
83.6k
          break;
771
83.6k
        }
772
22.1k
        opline++;
773
22.1k
      }
774
83.6k
    }
775
776
#if DEBUG_COMPACT_LITERALS
777
    {
778
      fprintf(stderr, "Optimized literals table size %d\n", op_array->last_literal);
779
780
      for (uint32_t i = 0; i < op_array->last_literal; i++) {
781
        zend_string *str = zval_get_string(op_array->literals + i);
782
        fprintf(stderr, "Literal %" PRIu32 ", val (%zu):%s\n", i, ZSTR_LEN(str), ZSTR_VAL(str));
783
        zend_string_release(str);
784
      }
785
      fflush(stderr);
786
    }
787
#endif
788
83.6k
  }
789
83.6k
}