void zend_optimizer_update_op1_const(zend_op_array *op_array, zend_op *opline, zval *val) { if (opline->opcode == ZEND_FREE) { MAKE_NOP(opline); zval_dtor(val); } else { ZEND_OP1_TYPE(opline) = IS_CONST; if (Z_TYPE_P(val) == IS_STRING) { switch (opline->opcode) { case ZEND_INIT_STATIC_METHOD_CALL: case ZEND_CATCH: case ZEND_FETCH_CONSTANT: case ZEND_DEFINED: case ZEND_NEW: opline->op1.constant = zend_optimizer_add_literal(op_array, val); zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline))); Z_CACHE_SLOT(op_array->literals[opline->op1.constant]) = op_array->last_cache_slot++; zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val)); zend_optimizer_add_literal(op_array, val); zend_string_hash_val(Z_STR(op_array->literals[opline->op1.constant+1])); break; default: opline->op1.constant = zend_optimizer_add_literal(op_array, val); zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline))); break; } } else { opline->op1.constant = zend_optimizer_add_literal(op_array, val); } } }
int zend_optimizer_add_literal(zend_op_array *op_array, zval *zv) { int i = op_array->last_literal; op_array->last_literal++; op_array->literals = (zval*)erealloc(op_array->literals, op_array->last_literal * sizeof(zval)); ZVAL_COPY_VALUE(&op_array->literals[i], zv); Z_CACHE_SLOT(op_array->literals[i]) = -1; return i; }
int zend_optimizer_add_literal(zend_op_array *op_array, zval *zv) { int i = op_array->last_literal; op_array->last_literal++; op_array->literals = (zval*)erealloc(op_array->literals, op_array->last_literal * sizeof(zval)); ZVAL_COPY_VALUE(&op_array->literals[i], zv); Z_CACHE_SLOT(op_array->literals[i]) = -1; //??? Z_SET_REFCOUNT(op_array->literals[i].constant, 2); //??? Z_SET_ISREF(op_array->literals[i].constant); return i; }
int zend_optimizer_update_op1_const(zend_op_array *op_array, zend_op *opline, zval *val) { switch (opline->opcode) { case ZEND_FREE: MAKE_NOP(opline); zval_dtor(val); break; case ZEND_INIT_STATIC_METHOD_CALL: case ZEND_CATCH: case ZEND_FETCH_CONSTANT: case ZEND_FETCH_CLASS_CONSTANT: case ZEND_DEFINED: case ZEND_NEW: REQUIRES_STRING(val); ZEND_OP1_TYPE(opline) = IS_CONST; drop_leading_backslash(val); opline->op1.constant = zend_optimizer_add_literal(op_array, val); zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline))); Z_CACHE_SLOT(op_array->literals[opline->op1.constant]) = op_array->cache_size; op_array->cache_size += sizeof(void*); zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); break; case ZEND_CONCAT: case ZEND_FAST_CONCAT: TO_STRING_NOWARN(val); /* break missing intentionally */ default: ZEND_OP1_TYPE(opline) = IS_CONST; opline->op1.constant = zend_optimizer_add_literal(op_array, val); if (Z_TYPE_P(val) == IS_STRING) { zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline))); } break; } return 1; }
void zend_optimizer_update_op2_const(zend_op_array *op_array, zend_op *opline, zval *val) { ZEND_OP2_TYPE(opline) = IS_CONST; if (opline->opcode == ZEND_INIT_FCALL) { zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val)); opline->op2.constant = zend_optimizer_add_literal(op_array, val); zend_string_hash_val(Z_STR(ZEND_OP2_LITERAL(opline))); Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) = op_array->cache_size; op_array->cache_size += sizeof(void*); return; } else if (opline->opcode == ZEND_ROPE_INIT || opline->opcode == ZEND_ROPE_ADD || opline->opcode == ZEND_ROPE_END || opline->opcode == ZEND_CONCAT || opline->opcode == ZEND_FAST_CONCAT) { convert_to_string(val); } opline->op2.constant = zend_optimizer_add_literal(op_array, val); if (Z_TYPE_P(val) == IS_STRING) { zend_string_hash_val(Z_STR(ZEND_OP2_LITERAL(opline))); switch (opline->opcode) { case ZEND_FETCH_CLASS: case ZEND_INIT_FCALL_BY_NAME: /*case ZEND_INIT_NS_FCALL_BY_NAME:*/ case ZEND_ADD_INTERFACE: case ZEND_ADD_TRAIT: case ZEND_INSTANCEOF: 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: Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) = op_array->cache_size; op_array->cache_size += sizeof(void*); zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val)); zend_optimizer_add_literal(op_array, val); zend_string_hash_val(Z_STR(op_array->literals[opline->op2.constant+1])); break; case ZEND_INIT_DYNAMIC_CALL: opline->opcode = ZEND_INIT_FCALL_BY_NAME; Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) = op_array->cache_size; op_array->cache_size += sizeof(void*); zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val)); zend_optimizer_add_literal(op_array, val); zend_string_hash_val(Z_STR(op_array->literals[opline->op2.constant+1])); break; case ZEND_INIT_METHOD_CALL: case ZEND_INIT_STATIC_METHOD_CALL: zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val)); zend_optimizer_add_literal(op_array, val); zend_string_hash_val(Z_STR(op_array->literals[opline->op2.constant+1])); /* break missing intentionally */ /*case ZEND_FETCH_CLASS_CONSTANT:*/ case ZEND_ASSIGN_OBJ: 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: Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) = op_array->cache_size; op_array->cache_size += 2 * sizeof(void*); break; case ZEND_ASSIGN_ADD: case ZEND_ASSIGN_SUB: case ZEND_ASSIGN_MUL: case ZEND_ASSIGN_DIV: case ZEND_ASSIGN_POW: case ZEND_ASSIGN_MOD: case ZEND_ASSIGN_SL: case ZEND_ASSIGN_SR: case ZEND_ASSIGN_CONCAT: case ZEND_ASSIGN_BW_OR: case ZEND_ASSIGN_BW_AND: case ZEND_ASSIGN_BW_XOR: if (opline->extended_value == ZEND_ASSIGN_OBJ) { Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) = op_array->cache_size; op_array->cache_size += 2 * sizeof(void*); } break; case ZEND_OP_DATA: if ((opline-1)->opcode == ZEND_ASSIGN_DIM || ((opline-1)->extended_value == ZEND_ASSIGN_DIM && ((opline-1)->opcode == ZEND_ASSIGN_ADD || (opline-1)->opcode == ZEND_ASSIGN_SUB || (opline-1)->opcode == ZEND_ASSIGN_MUL || (opline-1)->opcode == ZEND_ASSIGN_DIV || (opline-1)->opcode == ZEND_ASSIGN_POW || (opline-1)->opcode == ZEND_ASSIGN_MOD || (opline-1)->opcode == ZEND_ASSIGN_SL || (opline-1)->opcode == ZEND_ASSIGN_SR || (opline-1)->opcode == ZEND_ASSIGN_CONCAT || (opline-1)->opcode == ZEND_ASSIGN_BW_OR || (opline-1)->opcode == ZEND_ASSIGN_BW_AND || (opline-1)->opcode == ZEND_ASSIGN_BW_XOR))) { goto check_numeric; } break; case ZEND_ISSET_ISEMPTY_DIM_OBJ: case ZEND_ADD_ARRAY_ELEMENT: case ZEND_INIT_ARRAY: 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: check_numeric: { zend_ulong index; if (ZEND_HANDLE_NUMERIC(Z_STR_P(val), index)) { zval_dtor(val); ZVAL_LONG(val, index); op_array->literals[opline->op2.constant] = *val; } } break; default: break; } } }
void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx) { zend_op *opline = op_array->opcodes; zend_op *end = opline + op_array->last; int call = 0; void *checkpoint; optimizer_call_info *call_stack; if (op_array->last < 2) { return; } checkpoint = zend_arena_checkpoint(ctx->arena); call_stack = zend_arena_calloc(&ctx->arena, op_array->last / 2, sizeof(optimizer_call_info)); while (opline < end) { switch (opline->opcode) { case ZEND_INIT_FCALL_BY_NAME: case ZEND_INIT_NS_FCALL_BY_NAME: case ZEND_INIT_STATIC_METHOD_CALL: case ZEND_INIT_METHOD_CALL: case ZEND_INIT_FCALL: case ZEND_NEW: call_stack[call].func = zend_optimizer_get_called_func( ctx->script, op_array, opline, 0); call_stack[call].try_inline = opline->opcode != ZEND_NEW; /* break missing intentionally */ case ZEND_INIT_DYNAMIC_CALL: case ZEND_INIT_USER_CALL: call_stack[call].opline = opline; call++; break; case ZEND_DO_FCALL: case ZEND_DO_ICALL: case ZEND_DO_UCALL: case ZEND_DO_FCALL_BY_NAME: call--; if (call_stack[call].func && call_stack[call].opline) { zend_op *fcall = call_stack[call].opline; if (fcall->opcode == ZEND_INIT_FCALL) { /* nothing to do */ } else if (fcall->opcode == ZEND_INIT_FCALL_BY_NAME) { fcall->opcode = ZEND_INIT_FCALL; fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func); Z_CACHE_SLOT(op_array->literals[fcall->op2.constant + 1]) = Z_CACHE_SLOT(op_array->literals[fcall->op2.constant]); literal_dtor(&ZEND_OP2_LITERAL(fcall)); fcall->op2.constant = fcall->op2.constant + 1; opline->opcode = zend_get_call_op(fcall, call_stack[call].func); } else if (fcall->opcode == ZEND_INIT_NS_FCALL_BY_NAME) { fcall->opcode = ZEND_INIT_FCALL; fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func); Z_CACHE_SLOT(op_array->literals[fcall->op2.constant + 1]) = Z_CACHE_SLOT(op_array->literals[fcall->op2.constant]); literal_dtor(&op_array->literals[fcall->op2.constant]); literal_dtor(&op_array->literals[fcall->op2.constant + 2]); fcall->op2.constant = fcall->op2.constant + 1; opline->opcode = zend_get_call_op(fcall, call_stack[call].func); } else if (fcall->opcode == ZEND_INIT_STATIC_METHOD_CALL || fcall->opcode == ZEND_INIT_METHOD_CALL || fcall->opcode == ZEND_NEW) { /* We don't have specialized opcodes for this, do nothing */ } else { ZEND_ASSERT(0); } if ((ZEND_OPTIMIZER_PASS_16 & ctx->optimization_level) && call_stack[call].try_inline) { zend_try_inline_call(op_array, fcall, opline, call_stack[call].func); } } call_stack[call].func = NULL; call_stack[call].opline = NULL; call_stack[call].try_inline = 0; break; case ZEND_FETCH_FUNC_ARG: case ZEND_FETCH_STATIC_PROP_FUNC_ARG: case ZEND_FETCH_OBJ_FUNC_ARG: case ZEND_FETCH_DIM_FUNC_ARG: if (call_stack[call - 1].func) { if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, (opline->extended_value & ZEND_FETCH_ARG_MASK))) { opline->extended_value &= ZEND_FETCH_TYPE_MASK; if (opline->opcode != ZEND_FETCH_STATIC_PROP_FUNC_ARG) { opline->opcode -= 9; } else { opline->opcode = ZEND_FETCH_STATIC_PROP_W; } } else { if (opline->opcode == ZEND_FETCH_DIM_FUNC_ARG && opline->op2_type == IS_UNUSED) { /* FETCH_DIM_FUNC_ARG supports UNUSED op2, while FETCH_DIM_R does not. * Performing the replacement would create an invalid opcode. */ call_stack[call - 1].try_inline = 0; break; } opline->extended_value &= ZEND_FETCH_TYPE_MASK; if (opline->opcode != ZEND_FETCH_STATIC_PROP_FUNC_ARG) { opline->opcode -= 12; } else { opline->opcode = ZEND_FETCH_STATIC_PROP_R; } } } break; case ZEND_SEND_VAL_EX: if (call_stack[call - 1].func) { if (ARG_MUST_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) { /* We won't convert it into_DO_FCALL to emit error at run-time */ call_stack[call - 1].opline = NULL; } else { opline->opcode = ZEND_SEND_VAL; } } break; case ZEND_SEND_VAR_EX: if (call_stack[call - 1].func) { if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) { opline->opcode = ZEND_SEND_REF; } else { opline->opcode = ZEND_SEND_VAR; } } break; case ZEND_SEND_VAR_NO_REF_EX: if (call_stack[call - 1].func) { if (ARG_MUST_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) { opline->opcode = ZEND_SEND_VAR_NO_REF; } else if (ARG_MAY_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) { opline->opcode = ZEND_SEND_VAL; } else { opline->opcode = ZEND_SEND_VAR; } } break; case ZEND_SEND_UNPACK: case ZEND_SEND_USER: case ZEND_SEND_ARRAY: call_stack[call - 1].try_inline = 0; break; default: break; } opline++; } zend_arena_release(&ctx->arena, checkpoint); }
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); if (op_array->last_literal) { cache_size = 0; 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, 1, 1); break; case ZEND_INIT_FCALL_BY_NAME: LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 1, 1, 2); break; case ZEND_INIT_NS_FCALL_BY_NAME: LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 1, 1, 3); break; case ZEND_INIT_METHOD_CALL: if (ZEND_OP2_TYPE(opline) == IS_CONST) { optimizer_literal_obj_info( info, opline->op1_type, opline->op1, opline->op2.constant, LITERAL_METHOD, 2, 2, op_array); } break; case ZEND_INIT_STATIC_METHOD_CALL: if (ZEND_OP1_TYPE(opline) == IS_CONST) { LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 1, 1, 2); } if (ZEND_OP2_TYPE(opline) == IS_CONST) { optimizer_literal_class_info( info, opline->op1_type, opline->op1, opline->op2.constant, LITERAL_STATIC_METHOD, (ZEND_OP1_TYPE(opline) == IS_CONST) ? 1 : 2, 2, op_array); } break; case ZEND_CATCH: LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 1, 1, 2); break; case ZEND_DEFINED: LITERAL_INFO(opline->op1.constant, LITERAL_CONST, 1, 1, 2); break; case ZEND_FETCH_CONSTANT: if ((opline->extended_value & (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) == (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) { LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 1, 1, 5); } else { LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 1, 1, 3); } break; case ZEND_FETCH_CLASS_CONSTANT: if (ZEND_OP1_TYPE(opline) == IS_CONST) { LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 1, 1, 2); } optimizer_literal_class_info( info, opline->op1_type, opline->op1, opline->op2.constant, LITERAL_CLASS_CONST, (ZEND_OP1_TYPE(opline) == IS_CONST) ? 1 : 2, 1, op_array); break; 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: if (ZEND_OP2_TYPE(opline) == IS_CONST) { LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 1, 1, 2); } if (ZEND_OP1_TYPE(opline) == IS_CONST) { optimizer_literal_class_info( info, opline->op2_type, opline->op2, opline->op1.constant, LITERAL_STATIC_PROPERTY, 2, 1, op_array); } break; case ZEND_FETCH_CLASS: case ZEND_ADD_INTERFACE: case ZEND_ADD_TRAIT: case ZEND_INSTANCEOF: if (ZEND_OP2_TYPE(opline) == IS_CONST) { LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 1, 1, 2); } break; case ZEND_NEW: if (ZEND_OP1_TYPE(opline) == IS_CONST) { LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 1, 1, 2); } break; case ZEND_ASSIGN_OBJ: 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: if (ZEND_OP2_TYPE(opline) == IS_CONST) { optimizer_literal_obj_info( info, opline->op1_type, opline->op1, opline->op2.constant, LITERAL_PROPERTY, 2, 1, op_array); } break; case ZEND_ASSIGN_ADD: case ZEND_ASSIGN_SUB: case ZEND_ASSIGN_MUL: case ZEND_ASSIGN_DIV: case ZEND_ASSIGN_POW: case ZEND_ASSIGN_MOD: case ZEND_ASSIGN_SL: case ZEND_ASSIGN_SR: case ZEND_ASSIGN_CONCAT: case ZEND_ASSIGN_BW_OR: case ZEND_ASSIGN_BW_AND: case ZEND_ASSIGN_BW_XOR: if (ZEND_OP2_TYPE(opline) == IS_CONST) { if (opline->extended_value == ZEND_ASSIGN_OBJ) { optimizer_literal_obj_info( info, opline->op1_type, opline->op1, opline->op2.constant, LITERAL_PROPERTY, 2, 1, op_array); } else { LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1, 0, 1); } } break; case ZEND_BIND_GLOBAL: LITERAL_INFO(opline->op2.constant, LITERAL_GLOBAL, 0, 1, 1); break; case ZEND_RECV_INIT: LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 0, 0, 1); if (Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) != (uint32_t)-1) { Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) = cache_size; cache_size += sizeof(void *); } break; case ZEND_DECLARE_FUNCTION: case ZEND_DECLARE_CLASS: case ZEND_DECLARE_INHERITED_CLASS: case ZEND_DECLARE_INHERITED_CLASS_DELAYED: LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 0, 0, 2); break; case ZEND_RECV: case ZEND_RECV_VARIADIC: case ZEND_VERIFY_RETURN_TYPE: if (opline->op2.num != (uint32_t)-1) { opline->op2.num = cache_size; cache_size += sizeof(void *); } default: if (ZEND_OP1_TYPE(opline) == IS_CONST) { LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1, 0, 1); } if (ZEND_OP2_TYPE(opline) == IS_CONST) { LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1, 0, 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, "Literlas 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 (%d):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv)); if (use_copy) { zval_dtor(&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) { /* unsed literal */ zval_dtor(&op_array->literals[i]); continue; } switch (Z_TYPE(op_array->literals[i])) { case IS_NULL: /* Only checking MAY_MERGE for IS_NULL here * is because only IS_NULL can be default value for class type hinting(RECV_INIT). */ if ((info[i].flags & LITERAL_MAY_MERGE)) { 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; } else { map[i] = j; if (i != j) { op_array->literals[j] = op_array->literals[i]; info[j] = info[i]; } j++; } 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 ((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++; } 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: case IS_CONSTANT: if (info[i].flags & LITERAL_MAY_MERGE) { if (info[i].flags & LITERAL_EX_OBJ) { int key_len = sizeof("$this->") - 1 + Z_STRLEN(op_array->literals[i]); key = zend_string_alloc(key_len, 0); memcpy(ZSTR_VAL(key), "$this->", sizeof("$this->") - 1); memcpy(ZSTR_VAL(key) + sizeof("$this->") - 1, Z_STRVAL(op_array->literals[i]), Z_STRLEN(op_array->literals[i]) + 1); ZSTR_LEN(key) = key_len; } else if (info[i].flags & LITERAL_EX_CLASS) { int key_len; zval *class_name = &op_array->literals[(info[i].u.num < i) ? map[info[i].u.num] : info[i].u.num]; key_len = Z_STRLEN_P(class_name) + sizeof("::") - 1 + Z_STRLEN(op_array->literals[i]); 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(op_array->literals[i]), Z_STRLEN(op_array->literals[i]) + 1); } else { key = zend_string_init(Z_STRVAL(op_array->literals[i]), Z_STRLEN(op_array->literals[i]), 0); } ZSTR_H(key) = zend_hash_func(ZSTR_VAL(key), ZSTR_LEN(key)); ZSTR_H(key) += info[i].flags; } if ((info[i].flags & LITERAL_MAY_MERGE) && (pos = zend_hash_find(&hash, key)) != NULL && Z_TYPE(op_array->literals[i]) == Z_TYPE(op_array->literals[Z_LVAL_P(pos)]) && info[i].flags == info[Z_LVAL_P(pos)].flags) { zend_string_release(key); map[i] = Z_LVAL_P(pos); zval_dtor(&op_array->literals[i]); n = LITERAL_NUM_RELATED(info[i].flags); while (n > 1) { i++; zval_dtor(&op_array->literals[i]); n--; } } else { map[i] = j; if (info[i].flags & LITERAL_MAY_MERGE) { ZVAL_LONG(&zv, j); zend_hash_add_new(&hash, key, &zv); zend_string_release(key); } if (i != j) { op_array->literals[j] = op_array->literals[i]; info[j] = info[i]; } if (LITERAL_NUM_SLOTS(info[i].flags)) { Z_CACHE_SLOT(op_array->literals[j]) = cache_size; cache_size += LITERAL_NUM_SLOTS(info[i].flags) * sizeof(void*); } 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_dtor(&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_destroy(&hash); op_array->last_literal = j; op_array->cache_size = cache_size; /* Update opcodes to use new literals table */ opline = op_array->opcodes; end = opline + op_array->last; while (opline < end) { if (ZEND_OP1_TYPE(opline) == IS_CONST) { opline->op1.constant = map[opline->op1.constant]; } if (ZEND_OP2_TYPE(opline) == IS_CONST) { opline->op2.constant = map[opline->op2.constant]; } opline++; } zend_arena_release(&ctx->arena, checkpoint); #if DEBUG_COMPACT_LITERALS { int i, use_copy; fprintf(stderr, "Optimized literlas 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 (%d):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv)); if (use_copy) { zval_dtor(&zv); } } fflush(stderr); } #endif } }
void optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx) { zend_op *opline = op_array->opcodes; zend_op *end = opline + op_array->last; int call = 0; void *checkpoint; optimizer_call_info *call_stack; if (op_array->last < 2) { return; } checkpoint = zend_arena_checkpoint(ctx->arena); call_stack = zend_arena_calloc(&ctx->arena, op_array->last / 2, sizeof(optimizer_call_info)); while (opline < end) { switch (opline->opcode) { case ZEND_INIT_FCALL_BY_NAME: case ZEND_INIT_NS_FCALL_BY_NAME: if (ZEND_OP2_IS_CONST_STRING(opline)) { zend_function *func; zval *function_name = &op_array->literals[opline->op2.constant + 1]; if ((func = zend_hash_find_ptr(&ctx->script->function_table, Z_STR_P(function_name))) != NULL) { call_stack[call].func = func; } } /* break missing intentionally */ case ZEND_NEW: case ZEND_INIT_METHOD_CALL: case ZEND_INIT_STATIC_METHOD_CALL: case ZEND_INIT_FCALL: case ZEND_INIT_USER_CALL: call_stack[call].opline = opline; call++; break; case ZEND_DO_FCALL: call--; if (call_stack[call].func && call_stack[call].opline) { zend_op *fcall = call_stack[call].opline; if (fcall->opcode == ZEND_INIT_FCALL_BY_NAME) { fcall->opcode = ZEND_INIT_FCALL; fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func); Z_CACHE_SLOT(op_array->literals[fcall->op2.constant + 1]) = Z_CACHE_SLOT(op_array->literals[fcall->op2.constant]); literal_dtor(&ZEND_OP2_LITERAL(fcall)); fcall->op2.constant = fcall->op2.constant + 1; } else if (fcall->opcode == ZEND_INIT_NS_FCALL_BY_NAME) { fcall->opcode = ZEND_INIT_FCALL; fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func); Z_CACHE_SLOT(op_array->literals[fcall->op2.constant + 1]) = Z_CACHE_SLOT(op_array->literals[fcall->op2.constant]); literal_dtor(&op_array->literals[fcall->op2.constant]); literal_dtor(&op_array->literals[fcall->op2.constant + 2]); fcall->op2.constant = fcall->op2.constant + 1; } else { ZEND_ASSERT(0); } } call_stack[call].func = NULL; call_stack[call].opline = NULL; break; case ZEND_FETCH_FUNC_ARG: case ZEND_FETCH_OBJ_FUNC_ARG: case ZEND_FETCH_DIM_FUNC_ARG: if (call_stack[call - 1].func) { if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, (opline->extended_value & ZEND_FETCH_ARG_MASK))) { opline->extended_value = 0; opline->opcode -= 9; } else { opline->extended_value = 0; opline->opcode -= 12; } } break; case ZEND_SEND_VAL_EX: if (call_stack[call - 1].func) { if (ARG_MUST_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) { /* We won't convert it into_DO_FCALL to emit error at run-time */ call_stack[call - 1].opline = NULL; } else { opline->opcode = ZEND_SEND_VAL; } } break; case ZEND_SEND_VAR_EX: if (call_stack[call - 1].func) { if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) { opline->opcode = ZEND_SEND_REF; } else { opline->opcode = ZEND_SEND_VAR; } } break; case ZEND_SEND_VAR_NO_REF: if (!(opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) && call_stack[call - 1].func) { if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) { opline->extended_value |= ZEND_ARG_COMPILE_TIME_BOUND | ZEND_ARG_SEND_BY_REF; } else { opline->opcode = ZEND_SEND_VAR; opline->extended_value = 0; } } break; #if 0 case ZEND_SEND_REF: if (opline->extended_value != ZEND_ARG_COMPILE_TIME_BOUND && call_stack[call - 1].func) { /* We won't handle run-time pass by reference */ call_stack[call - 1].opline = NULL; } break; #endif case ZEND_SEND_UNPACK: call_stack[call - 1].func = NULL; call_stack[call - 1].opline = NULL; break; default: break; } opline++; } zend_arena_release(&ctx->arena, checkpoint); }
static inline void alloc_cache_slots_op2(zend_op_array *op_array, zend_op *opline, uint32_t num) { Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) = op_array->cache_size; op_array->cache_size += num * sizeof(void *); }