diff options
Diffstat (limited to 'Zend/Optimizer/compact_literals.c')
-rw-r--r-- | Zend/Optimizer/compact_literals.c | 843 |
1 files changed, 843 insertions, 0 deletions
diff --git a/Zend/Optimizer/compact_literals.c b/Zend/Optimizer/compact_literals.c new file mode 100644 index 0000000000..0e1529d2bd --- /dev/null +++ b/Zend/Optimizer/compact_literals.c @@ -0,0 +1,843 @@ +/* + +----------------------------------------------------------------------+ + | 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 size_t type_num_classes(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)) { + return 0; + } + 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; + } + } else { + arg_info = op_array->arg_info - 1; + } + + if (ZEND_TYPE_HAS_CLASS(arg_info->type)) { + if (ZEND_TYPE_HAS_LIST(arg_info->type)) { + return ZEND_TYPE_LIST(arg_info->type)->num_types; + } + return 1; + } + + 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; + zval *class_name = &op_array->literals[op1]; + zval *prop_name = &op_array->literals[op2]; + zval *pos, tmp; + + zend_string *key = zend_create_member_string(Z_STR_P(class_name), Z_STR_P(prop_name)); + 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, double_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, 1); + break; + case ZEND_FETCH_CONSTANT: + if (opline->op1.num & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) { + LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 3); + } else { + LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 2); + } + 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->op1_type == IS_CONST) { + LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1); + } + 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); + /* Use separate hashtable for doubles stored as string keys, to avoid collisions. */ + zend_hash_init(&double_hash, 0, 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(&double_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_new(&double_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; + } + } + + /* Only clean "hash", as it will be reused in the loop below. */ + zend_hash_clean(&hash); + zend_hash_destroy(&double_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: + case ZEND_RECV: + case ZEND_RECV_VARIADIC: + { + size_t num_classes = type_num_classes(op_array, opline->op1.num); + if (num_classes) { + opline->extended_value = cache_size; + cache_size += num_classes * sizeof(void *); + } + break; + } + case ZEND_VERIFY_RETURN_TYPE: + { + size_t num_classes = type_num_classes(op_array, 0); + if (num_classes) { + opline->op2.num = cache_size; + cache_size += num_classes * 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; + case ZEND_SEND_VAL: + case ZEND_SEND_VAL_EX: + case ZEND_SEND_VAR: + case ZEND_SEND_VAR_EX: + case ZEND_SEND_VAR_NO_REF: + case ZEND_SEND_VAR_NO_REF_EX: + case ZEND_SEND_REF: + case ZEND_SEND_FUNC_ARG: + case ZEND_CHECK_FUNC_ARG: + if (opline->op2_type == IS_CONST) { + opline->result.num = cache_size; + cache_size += 2 * 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) { + 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 + } +} |