php-src/ext/opcache/Optimizer/compact_literals.c

824 lines
26 KiB
C

/*
+----------------------------------------------------------------------+
| Zend OPcache |
+----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Dmitry Stogov <dmitry@php.net> |
| Xinchen Hui <laruence@php.net> |
+----------------------------------------------------------------------+
*/
/* pass 11
* - compact literals table
*/
#include "php.h"
#include "Optimizer/zend_optimizer.h"
#include "Optimizer/zend_optimizer_internal.h"
#include "zend_API.h"
#include "zend_constants.h"
#include "zend_execute.h"
#include "zend_vm.h"
#include "zend_extensions.h"
#define DEBUG_COMPACT_LITERALS 0
#define LITERAL_VALUE 0x0100
#define LITERAL_FUNC 0x0200
#define LITERAL_CLASS 0x0300
#define LITERAL_CONST 0x0400
#define LITERAL_CLASS_CONST 0x0500
#define LITERAL_STATIC_METHOD 0x0600
#define LITERAL_STATIC_PROPERTY 0x0700
#define LITERAL_METHOD 0x0800
#define LITERAL_PROPERTY 0x0900
#define LITERAL_GLOBAL 0x0A00
#define LITERAL_KIND_MASK 0x0f00
#define LITERAL_NUM_RELATED_MASK 0x000f
#define LITERAL_NUM_RELATED(info) (info & LITERAL_NUM_RELATED_MASK)
typedef struct _literal_info {
uint32_t flags; /* bitmask (see defines above) */
} literal_info;
#define LITERAL_INFO(n, kind, related) do { \
info[n].flags = ((kind) | (related)); \
} while (0)
static zend_bool class_name_type_hint(const zend_op_array *op_array, uint32_t arg_num)
{
zend_arg_info *arg_info;
if (arg_num > 0) {
if (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) {
if (EXPECTED(arg_num <= op_array->num_args)) {
arg_info = &op_array->arg_info[arg_num-1];
} else if (UNEXPECTED(op_array->fn_flags & ZEND_ACC_VARIADIC)) {
arg_info = &op_array->arg_info[op_array->num_args];
} else {
return 0;
}
return ZEND_TYPE_IS_CLASS(arg_info->type);
}
} else {
arg_info = op_array->arg_info - 1;
return ZEND_TYPE_IS_CLASS(arg_info->type);
}
return 0;
}
static uint32_t add_static_slot(HashTable *hash,
zend_op_array *op_array,
uint32_t op1,
uint32_t op2,
uint32_t kind,
int *cache_size)
{
uint32_t ret;
zend_string *key;
size_t key_len;
zval *class_name = &op_array->literals[op1];
zval *prop_name = &op_array->literals[op2];
zval *pos, tmp;
key_len = Z_STRLEN_P(class_name) + sizeof("::") - 1 + Z_STRLEN_P(prop_name);
key = zend_string_alloc(key_len, 0);
memcpy(ZSTR_VAL(key), Z_STRVAL_P(class_name), Z_STRLEN_P(class_name));
memcpy(ZSTR_VAL(key) + Z_STRLEN_P(class_name), "::", sizeof("::") - 1);
memcpy(ZSTR_VAL(key) + Z_STRLEN_P(class_name) + sizeof("::") - 1,
Z_STRVAL_P(prop_name),
Z_STRLEN_P(prop_name) + 1);
ZSTR_H(key) = zend_string_hash_func(key);
ZSTR_H(key) += kind;
pos = zend_hash_find(hash, key);
if (pos) {
ret = Z_LVAL_P(pos);
} else {
ret = *cache_size;
*cache_size += (kind == LITERAL_STATIC_PROPERTY ? 3 : 2) * sizeof(void *);
ZVAL_LONG(&tmp, ret);
zend_hash_add(hash, key, &tmp);
}
zend_string_release_ex(key, 0);
return ret;
}
void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx)
{
zend_op *opline, *end;
int i, j, n, *map, cache_size;
zval zv, *pos;
literal_info *info;
int l_null = -1;
int l_false = -1;
int l_true = -1;
int l_empty_arr = -1;
HashTable hash;
zend_string *key = NULL;
void *checkpoint = zend_arena_checkpoint(ctx->arena);
int *const_slot, *class_slot, *func_slot, *bind_var_slot, *property_slot, *method_slot;
if (op_array->last_literal) {
info = (literal_info*)zend_arena_calloc(&ctx->arena, op_array->last_literal, sizeof(literal_info));
/* Mark literals of specific types */
opline = op_array->opcodes;
end = opline + op_array->last;
while (opline < end) {
switch (opline->opcode) {
case ZEND_INIT_FCALL:
LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 1);
break;
case ZEND_INIT_FCALL_BY_NAME:
LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 2);
break;
case ZEND_INIT_NS_FCALL_BY_NAME:
LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 3);
break;
case ZEND_INIT_METHOD_CALL:
if (opline->op1_type == IS_CONST) {
LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
}
if (opline->op2_type == IS_CONST) {
LITERAL_INFO(opline->op2.constant, LITERAL_METHOD, 2);
}
break;
case ZEND_INIT_STATIC_METHOD_CALL:
if (opline->op1_type == IS_CONST) {
LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
}
if (opline->op2_type == IS_CONST) {
LITERAL_INFO(opline->op2.constant, LITERAL_STATIC_METHOD, 2);
}
break;
case ZEND_CATCH:
LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
break;
case ZEND_DEFINED:
LITERAL_INFO(opline->op1.constant, LITERAL_CONST, 2);
break;
case ZEND_FETCH_CONSTANT:
if ((opline->op1.num & (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) == (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) {
LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 5);
} else {
LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 3);
}
break;
case ZEND_FETCH_CLASS_CONSTANT:
if (opline->op1_type == IS_CONST) {
LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
}
LITERAL_INFO(opline->op2.constant, LITERAL_CLASS_CONST, 1);
break;
case ZEND_ASSIGN_STATIC_PROP:
case ZEND_ASSIGN_STATIC_PROP_REF:
case ZEND_FETCH_STATIC_PROP_R:
case ZEND_FETCH_STATIC_PROP_W:
case ZEND_FETCH_STATIC_PROP_RW:
case ZEND_FETCH_STATIC_PROP_IS:
case ZEND_FETCH_STATIC_PROP_UNSET:
case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
case ZEND_UNSET_STATIC_PROP:
case ZEND_ISSET_ISEMPTY_STATIC_PROP:
case ZEND_PRE_INC_STATIC_PROP:
case ZEND_PRE_DEC_STATIC_PROP:
case ZEND_POST_INC_STATIC_PROP:
case ZEND_POST_DEC_STATIC_PROP:
case ZEND_ASSIGN_STATIC_PROP_OP:
if (opline->op2_type == IS_CONST) {
LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 2);
}
if (opline->op1_type == IS_CONST) {
LITERAL_INFO(opline->op1.constant, LITERAL_STATIC_PROPERTY, 1);
}
break;
case ZEND_FETCH_CLASS:
case ZEND_INSTANCEOF:
if (opline->op2_type == IS_CONST) {
LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 2);
}
break;
case ZEND_NEW:
if (opline->op1_type == IS_CONST) {
LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
}
break;
case ZEND_ASSIGN_OBJ:
case ZEND_ASSIGN_OBJ_REF:
case ZEND_FETCH_OBJ_R:
case ZEND_FETCH_OBJ_W:
case ZEND_FETCH_OBJ_RW:
case ZEND_FETCH_OBJ_IS:
case ZEND_FETCH_OBJ_UNSET:
case ZEND_FETCH_OBJ_FUNC_ARG:
case ZEND_UNSET_OBJ:
case ZEND_PRE_INC_OBJ:
case ZEND_PRE_DEC_OBJ:
case ZEND_POST_INC_OBJ:
case ZEND_POST_DEC_OBJ:
case ZEND_ISSET_ISEMPTY_PROP_OBJ:
case ZEND_ASSIGN_OBJ_OP:
if (opline->op2_type == IS_CONST) {
LITERAL_INFO(opline->op2.constant, LITERAL_PROPERTY, 1);
}
break;
case ZEND_BIND_GLOBAL:
LITERAL_INFO(opline->op2.constant, LITERAL_GLOBAL, 1);
break;
case ZEND_RECV_INIT:
LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
break;
case ZEND_DECLARE_FUNCTION:
LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 2);
break;
case ZEND_DECLARE_CLASS:
case ZEND_DECLARE_CLASS_DELAYED:
LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 2);
if (opline->op2_type == IS_CONST) {
LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
}
break;
case ZEND_ISSET_ISEMPTY_DIM_OBJ:
case ZEND_ASSIGN_DIM:
case ZEND_UNSET_DIM:
case ZEND_FETCH_DIM_R:
case ZEND_FETCH_DIM_W:
case ZEND_FETCH_DIM_RW:
case ZEND_FETCH_DIM_IS:
case ZEND_FETCH_DIM_FUNC_ARG:
case ZEND_FETCH_DIM_UNSET:
case ZEND_FETCH_LIST_R:
case ZEND_FETCH_LIST_W:
case ZEND_ASSIGN_DIM_OP:
if (opline->op1_type == IS_CONST) {
LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
}
if (opline->op2_type == IS_CONST) {
if (Z_EXTRA(op_array->literals[opline->op2.constant]) == ZEND_EXTRA_VALUE) {
LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 2);
} else {
LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
}
}
break;
default:
if (opline->op1_type == IS_CONST) {
LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
}
if (opline->op2_type == IS_CONST) {
LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
}
break;
}
opline++;
}
#if DEBUG_COMPACT_LITERALS
{
int i, use_copy;
fprintf(stderr, "File %s func %s\n", op_array->filename->val,
op_array->function_name ? op_array->function_name->val : "main");
fprintf(stderr, "Literals table size %d\n", op_array->last_literal);
for (i = 0; i < op_array->last_literal; i++) {
zval zv;
ZVAL_COPY_VALUE(&zv, op_array->literals + i);
use_copy = zend_make_printable_zval(op_array->literals + i, &zv);
fprintf(stderr, "Literal %d, val (%zu):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv));
if (use_copy) {
zval_ptr_dtor_nogc(&zv);
}
}
fflush(stderr);
}
#endif
/* Merge equal constants */
j = 0;
zend_hash_init(&hash, op_array->last_literal, NULL, NULL, 0);
map = (int*)zend_arena_alloc(&ctx->arena, op_array->last_literal * sizeof(int));
memset(map, 0, op_array->last_literal * sizeof(int));
for (i = 0; i < op_array->last_literal; i++) {
if (!info[i].flags) {
/* unset literal */
zval_ptr_dtor_nogc(&op_array->literals[i]);
continue;
}
switch (Z_TYPE(op_array->literals[i])) {
case IS_NULL:
if (l_null < 0) {
l_null = j;
if (i != j) {
op_array->literals[j] = op_array->literals[i];
info[j] = info[i];
}
j++;
}
map[i] = l_null;
break;
case IS_FALSE:
if (l_false < 0) {
l_false = j;
if (i != j) {
op_array->literals[j] = op_array->literals[i];
info[j] = info[i];
}
j++;
}
map[i] = l_false;
break;
case IS_TRUE:
if (l_true < 0) {
l_true = j;
if (i != j) {
op_array->literals[j] = op_array->literals[i];
info[j] = info[i];
}
j++;
}
map[i] = l_true;
break;
case IS_LONG:
if (LITERAL_NUM_RELATED(info[i].flags) == 1) {
if ((pos = zend_hash_index_find(&hash, Z_LVAL(op_array->literals[i]))) != NULL) {
map[i] = Z_LVAL_P(pos);
} else {
map[i] = j;
ZVAL_LONG(&zv, j);
zend_hash_index_add_new(&hash, Z_LVAL(op_array->literals[i]), &zv);
if (i != j) {
op_array->literals[j] = op_array->literals[i];
info[j] = info[i];
}
j++;
}
} else {
ZEND_ASSERT(LITERAL_NUM_RELATED(info[i].flags) == 2);
key = zend_string_init(Z_STRVAL(op_array->literals[i+1]), Z_STRLEN(op_array->literals[i+1]), 0);
ZSTR_H(key) = ZSTR_HASH(Z_STR(op_array->literals[i+1])) + 100 +
LITERAL_NUM_RELATED(info[i].flags) - 1;
if ((pos = zend_hash_find(&hash, key)) != NULL
&& LITERAL_NUM_RELATED(info[Z_LVAL_P(pos)].flags) == 2) {
map[i] = Z_LVAL_P(pos);
zval_ptr_dtor_nogc(&op_array->literals[i+1]);
} else {
map[i] = j;
ZVAL_LONG(&zv, j);
zend_hash_add_new(&hash, key, &zv);
if (i != j) {
op_array->literals[j] = op_array->literals[i];
info[j] = info[i];
op_array->literals[j+1] = op_array->literals[i+1];
info[j+1] = info[i+1];
}
j += 2;
}
zend_string_release_ex(key, 0);
i++;
}
break;
case IS_DOUBLE:
if ((pos = zend_hash_str_find(&hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double))) != NULL) {
map[i] = Z_LVAL_P(pos);
} else {
map[i] = j;
ZVAL_LONG(&zv, j);
zend_hash_str_add(&hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double), &zv);
if (i != j) {
op_array->literals[j] = op_array->literals[i];
info[j] = info[i];
}
j++;
}
break;
case IS_STRING: {
if (LITERAL_NUM_RELATED(info[i].flags) == 1) {
key = zend_string_copy(Z_STR(op_array->literals[i]));
} else if ((info[i].flags & LITERAL_KIND_MASK) != LITERAL_VALUE) {
key = zend_string_init(Z_STRVAL(op_array->literals[i]), Z_STRLEN(op_array->literals[i]), 0);
ZSTR_H(key) = ZSTR_HASH(Z_STR(op_array->literals[i])) +
LITERAL_NUM_RELATED(info[i].flags) - 1;
} else {
/* Don't merge LITERAL_VALUE that has related literals */
key = NULL;
}
if (key && (pos = zend_hash_find(&hash, key)) != NULL &&
Z_TYPE(op_array->literals[Z_LVAL_P(pos)]) == IS_STRING &&
LITERAL_NUM_RELATED(info[i].flags) == LITERAL_NUM_RELATED(info[Z_LVAL_P(pos)].flags) &&
(LITERAL_NUM_RELATED(info[i].flags) != 2 ||
((info[i].flags & LITERAL_KIND_MASK) != LITERAL_VALUE &&
(info[Z_LVAL_P(pos)].flags & LITERAL_KIND_MASK) != LITERAL_VALUE))) {
zend_string_release_ex(key, 0);
map[i] = Z_LVAL_P(pos);
zval_ptr_dtor_nogc(&op_array->literals[i]);
n = LITERAL_NUM_RELATED(info[i].flags);
while (n > 1) {
i++;
zval_ptr_dtor_nogc(&op_array->literals[i]);
n--;
}
} else {
map[i] = j;
ZVAL_LONG(&zv, j);
if (key) {
zend_hash_add_new(&hash, key, &zv);
zend_string_release_ex(key, 0);
}
if (i != j) {
op_array->literals[j] = op_array->literals[i];
info[j] = info[i];
}
j++;
n = LITERAL_NUM_RELATED(info[i].flags);
while (n > 1) {
i++;
if (i != j) op_array->literals[j] = op_array->literals[i];
j++;
n--;
}
}
break;
}
case IS_ARRAY:
if (zend_hash_num_elements(Z_ARRVAL(op_array->literals[i])) == 0) {
if (l_empty_arr < 0) {
l_empty_arr = j;
if (i != j) {
op_array->literals[j] = op_array->literals[i];
info[j] = info[i];
}
j++;
} else {
zval_ptr_dtor_nogc(&op_array->literals[i]);
}
map[i] = l_empty_arr;
break;
}
/* break missing intentionally */
default:
/* don't merge other types */
map[i] = j;
if (i != j) {
op_array->literals[j] = op_array->literals[i];
info[j] = info[i];
}
j++;
break;
}
}
zend_hash_clean(&hash);
op_array->last_literal = j;
const_slot = zend_arena_alloc(&ctx->arena, j * 6 * sizeof(int));
memset(const_slot, -1, j * 6 * sizeof(int));
class_slot = const_slot + j;
func_slot = class_slot + j;
bind_var_slot = func_slot + j;
property_slot = bind_var_slot + j;
method_slot = property_slot + j;
/* Update opcodes to use new literals table */
cache_size = zend_op_array_extension_handles * sizeof(void*);
opline = op_array->opcodes;
end = opline + op_array->last;
while (opline < end) {
if (opline->op1_type == IS_CONST) {
opline->op1.constant = map[opline->op1.constant];
}
if (opline->op2_type == IS_CONST) {
opline->op2.constant = map[opline->op2.constant];
}
switch (opline->opcode) {
case ZEND_RECV_INIT:
if (class_name_type_hint(op_array, opline->op1.num)) {
opline->extended_value = cache_size;
cache_size += sizeof(void *);
}
break;
case ZEND_RECV:
case ZEND_RECV_VARIADIC:
if (class_name_type_hint(op_array, opline->op1.num)) {
opline->op2.num = cache_size;
cache_size += sizeof(void *);
}
break;
case ZEND_VERIFY_RETURN_TYPE:
if (class_name_type_hint(op_array, 0)) {
opline->op2.num = cache_size;
cache_size += sizeof(void *);
}
break;
case ZEND_ASSIGN_STATIC_PROP_OP:
if (opline->op1_type == IS_CONST) {
// op1 static property
if (opline->op2_type == IS_CONST) {
(opline+1)->extended_value = add_static_slot(&hash, op_array,
opline->op2.constant,
opline->op1.constant,
LITERAL_STATIC_PROPERTY,
&cache_size);
} else {
(opline+1)->extended_value = cache_size;
cache_size += 3 * sizeof(void *);
}
} else if (opline->op2_type == IS_CONST) {
// op2 class
if (class_slot[opline->op2.constant] >= 0) {
(opline+1)->extended_value = class_slot[opline->op2.constant];
} else {
(opline+1)->extended_value = cache_size;
class_slot[opline->op2.constant] = cache_size;
cache_size += sizeof(void *);
}
}
break;
case ZEND_ASSIGN_OBJ_OP:
if (opline->op2_type == IS_CONST) {
// op2 property
if (opline->op1_type == IS_UNUSED &&
property_slot[opline->op2.constant] >= 0) {
(opline+1)->extended_value = property_slot[opline->op2.constant];
} else {
(opline+1)->extended_value = cache_size;
cache_size += 3 * sizeof(void *);
if (opline->op1_type == IS_UNUSED) {
property_slot[opline->op2.constant] = (opline+1)->extended_value;
}
}
}
break;
case ZEND_ASSIGN_OBJ:
case ZEND_ASSIGN_OBJ_REF:
case ZEND_FETCH_OBJ_R:
case ZEND_FETCH_OBJ_W:
case ZEND_FETCH_OBJ_RW:
case ZEND_FETCH_OBJ_IS:
case ZEND_FETCH_OBJ_UNSET:
case ZEND_FETCH_OBJ_FUNC_ARG:
case ZEND_UNSET_OBJ:
case ZEND_PRE_INC_OBJ:
case ZEND_PRE_DEC_OBJ:
case ZEND_POST_INC_OBJ:
case ZEND_POST_DEC_OBJ:
if (opline->op2_type == IS_CONST) {
// op2 property
if (opline->op1_type == IS_UNUSED &&
property_slot[opline->op2.constant] >= 0) {
opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
} else {
opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
cache_size += 3 * sizeof(void *);
if (opline->op1_type == IS_UNUSED) {
property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS;
}
}
}
break;
case ZEND_ISSET_ISEMPTY_PROP_OBJ:
if (opline->op2_type == IS_CONST) {
// op2 property
if (opline->op1_type == IS_UNUSED &&
property_slot[opline->op2.constant] >= 0) {
opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_ISEMPTY);
} else {
opline->extended_value = cache_size | (opline->extended_value & ZEND_ISEMPTY);
cache_size += 3 * sizeof(void *);
if (opline->op1_type == IS_UNUSED) {
property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_ISEMPTY;
}
}
}
break;
case ZEND_INIT_FCALL:
case ZEND_INIT_FCALL_BY_NAME:
case ZEND_INIT_NS_FCALL_BY_NAME:
// op2 func
if (func_slot[opline->op2.constant] >= 0) {
opline->result.num = func_slot[opline->op2.constant];
} else {
opline->result.num = cache_size;
cache_size += sizeof(void *);
func_slot[opline->op2.constant] = opline->result.num;
}
break;
case ZEND_INIT_METHOD_CALL:
if (opline->op2_type == IS_CONST) {
// op2 method
if (opline->op1_type == IS_UNUSED &&
method_slot[opline->op2.constant] >= 0) {
opline->result.num = method_slot[opline->op2.constant];
} else {
opline->result.num = cache_size;
cache_size += 2 * sizeof(void *);
if (opline->op1_type == IS_UNUSED) {
method_slot[opline->op2.constant] = opline->result.num;
}
}
}
break;
case ZEND_INIT_STATIC_METHOD_CALL:
if (opline->op2_type == IS_CONST) {
// op2 static method
if (opline->op1_type == IS_CONST) {
opline->result.num = add_static_slot(&hash, op_array,
opline->op1.constant,
opline->op2.constant,
LITERAL_STATIC_METHOD,
&cache_size);
} else {
opline->result.num = cache_size;
cache_size += 2 * sizeof(void *);
}
} else if (opline->op1_type == IS_CONST) {
// op1 class
if (class_slot[opline->op1.constant] >= 0) {
opline->result.num = class_slot[opline->op1.constant];
} else {
opline->result.num = cache_size;
cache_size += sizeof(void *);
class_slot[opline->op1.constant] = opline->result.num;
}
}
break;
case ZEND_DEFINED:
// op1 const
if (const_slot[opline->op1.constant] >= 0) {
opline->extended_value = const_slot[opline->op1.constant];
} else {
opline->extended_value = cache_size;
cache_size += sizeof(void *);
const_slot[opline->op1.constant] = opline->extended_value;
}
break;
case ZEND_FETCH_CONSTANT:
// op2 const
if (const_slot[opline->op2.constant] >= 0) {
opline->extended_value = const_slot[opline->op2.constant];
} else {
opline->extended_value = cache_size;
cache_size += sizeof(void *);
const_slot[opline->op2.constant] = opline->extended_value;
}
break;
case ZEND_FETCH_CLASS_CONSTANT:
if (opline->op1_type == IS_CONST) {
// op1/op2 class_const
opline->extended_value = add_static_slot(&hash, op_array,
opline->op1.constant,
opline->op2.constant,
LITERAL_CLASS_CONST,
&cache_size);
} else {
opline->extended_value = cache_size;
cache_size += 2 * sizeof(void *);
}
break;
case ZEND_ASSIGN_STATIC_PROP:
case ZEND_ASSIGN_STATIC_PROP_REF:
case ZEND_FETCH_STATIC_PROP_R:
case ZEND_FETCH_STATIC_PROP_W:
case ZEND_FETCH_STATIC_PROP_RW:
case ZEND_FETCH_STATIC_PROP_IS:
case ZEND_FETCH_STATIC_PROP_UNSET:
case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
case ZEND_UNSET_STATIC_PROP:
case ZEND_ISSET_ISEMPTY_STATIC_PROP:
case ZEND_PRE_INC_STATIC_PROP:
case ZEND_PRE_DEC_STATIC_PROP:
case ZEND_POST_INC_STATIC_PROP:
case ZEND_POST_DEC_STATIC_PROP:
if (opline->op1_type == IS_CONST) {
// op1 static property
if (opline->op2_type == IS_CONST) {
opline->extended_value = add_static_slot(&hash, op_array,
opline->op2.constant,
opline->op1.constant,
LITERAL_STATIC_PROPERTY,
&cache_size) | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
} else {
opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
cache_size += 3 * sizeof(void *);
}
} else if (opline->op2_type == IS_CONST) {
// op2 class
if (class_slot[opline->op2.constant] >= 0) {
opline->extended_value = class_slot[opline->op2.constant] | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
} else {
opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
class_slot[opline->op2.constant] = cache_size;
cache_size += sizeof(void *);
}
}
break;
case ZEND_FETCH_CLASS:
case ZEND_INSTANCEOF:
if (opline->op2_type == IS_CONST) {
// op2 class
if (class_slot[opline->op2.constant] >= 0) {
opline->extended_value = class_slot[opline->op2.constant];
} else {
opline->extended_value = cache_size;
cache_size += sizeof(void *);
class_slot[opline->op2.constant] = opline->extended_value;
}
}
break;
case ZEND_NEW:
if (opline->op1_type == IS_CONST) {
// op1 class
if (class_slot[opline->op1.constant] >= 0) {
opline->op2.num = class_slot[opline->op1.constant];
} else {
opline->op2.num = cache_size;
cache_size += sizeof(void *);
class_slot[opline->op1.constant] = opline->op2.num;
}
}
break;
case ZEND_CATCH:
if (opline->op1_type == IS_CONST) {
// op1 class
if (class_slot[opline->op1.constant] >= 0) {
opline->extended_value = class_slot[opline->op1.constant] | (opline->extended_value & ZEND_LAST_CATCH);
} else {
opline->extended_value = cache_size | (opline->extended_value & ZEND_LAST_CATCH);
cache_size += sizeof(void *);
class_slot[opline->op1.constant] = opline->extended_value & ~ZEND_LAST_CATCH;
}
}
break;
case ZEND_BIND_GLOBAL:
// op2 bind var
if (bind_var_slot[opline->op2.constant] >= 0) {
opline->extended_value = bind_var_slot[opline->op2.constant];
} else {
opline->extended_value = cache_size;
cache_size += sizeof(void *);
bind_var_slot[opline->op2.constant] = opline->extended_value;
}
break;
case ZEND_DECLARE_LAMBDA_FUNCTION:
case ZEND_DECLARE_ANON_CLASS:
case ZEND_DECLARE_CLASS_DELAYED:
opline->extended_value = cache_size;
cache_size += sizeof(void *);
break;
}
opline++;
}
op_array->cache_size = cache_size;
zend_hash_destroy(&hash);
zend_arena_release(&ctx->arena, checkpoint);
if (1) {
opline = op_array->opcodes;
while (1) {
if (opline->opcode == ZEND_RECV_INIT) {
zval *val = &op_array->literals[opline->op2.constant];
if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
/* Ensure zval is aligned to 8 bytes */
op_array->cache_size = ZEND_MM_ALIGNED_SIZE_EX(op_array->cache_size, 8);
Z_CACHE_SLOT_P(val) = op_array->cache_size;
op_array->cache_size += sizeof(zval);
}
} else if (opline->opcode != ZEND_RECV && opline->opcode != ZEND_EXT_NOP) {
break;
}
opline++;
}
}
#if DEBUG_COMPACT_LITERALS
{
int i, use_copy;
fprintf(stderr, "Optimized literals table size %d\n", op_array->last_literal);
for (i = 0; i < op_array->last_literal; i++) {
zval zv;
ZVAL_COPY_VALUE(&zv, op_array->literals + i);
use_copy = zend_make_printable_zval(op_array->literals + i, &zv);
fprintf(stderr, "Literal %d, val (%zu):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv));
if (use_copy) {
zval_ptr_dtor_nogc(&zv);
}
}
fflush(stderr);
}
#endif
}
}