mirror of
https://github.com/php/php-src.git
synced 2026-04-24 00:18:23 +02:00
Don't check $this existence in object opcodes
We are now guaranteed that $this always exists inside methods, as well as insides closures (if they use $this at all). This removes checks for $this existence from the individual object opcodes. Instead ZEND_FETCH_THIS is used in the cases where $this is not guaranteed to exist, which is mainly the pseudo-main scope. Closes GH-3822.
This commit is contained in:
+20
-2
@@ -2429,6 +2429,16 @@ static zend_bool is_this_fetch(zend_ast *ast) /* {{{ */
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static zend_bool this_guaranteed_exists() /* {{{ */
|
||||
{
|
||||
zend_op_array *op_array = CG(active_op_array);
|
||||
/* Instance methods always have a $this.
|
||||
* This also includes closures that have a scope and use $this. */
|
||||
return op_array->scope != NULL
|
||||
&& (op_array->fn_flags & ZEND_ACC_STATIC) == 0;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static zend_op *zend_compile_simple_var(znode *result, zend_ast *ast, uint32_t type, int delayed) /* {{{ */
|
||||
{
|
||||
if (is_this_fetch(ast)) {
|
||||
@@ -2531,7 +2541,11 @@ static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t
|
||||
zend_op *opline;
|
||||
|
||||
if (is_this_fetch(obj_ast)) {
|
||||
obj_node.op_type = IS_UNUSED;
|
||||
if (this_guaranteed_exists()) {
|
||||
obj_node.op_type = IS_UNUSED;
|
||||
} else {
|
||||
zend_emit_op(&obj_node, ZEND_FETCH_THIS, NULL, NULL);
|
||||
}
|
||||
CG(active_op_array)->fn_flags |= ZEND_ACC_USES_THIS;
|
||||
} else {
|
||||
opline = zend_delayed_compile_var(&obj_node, obj_ast, type, 0);
|
||||
@@ -3929,7 +3943,11 @@ void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type) /* {{
|
||||
zend_function *fbc = NULL;
|
||||
|
||||
if (is_this_fetch(obj_ast)) {
|
||||
obj_node.op_type = IS_UNUSED;
|
||||
if (this_guaranteed_exists()) {
|
||||
obj_node.op_type = IS_UNUSED;
|
||||
} else {
|
||||
zend_emit_op(&obj_node, ZEND_FETCH_THIS, NULL, NULL);
|
||||
}
|
||||
CG(active_op_array)->fn_flags |= ZEND_ACC_USES_THIS;
|
||||
} else {
|
||||
zend_compile_expr(&obj_node, obj_ast);
|
||||
|
||||
+1
-65
@@ -994,11 +994,6 @@ ZEND_VM_HANDLER(28, ZEND_ASSIGN_OBJ_OP, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, OP)
|
||||
|
||||
SAVE_OPLINE();
|
||||
object = GET_OP1_OBJ_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);
|
||||
|
||||
if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
|
||||
ZEND_VM_DISPATCH_TO_HELPER(zend_this_not_in_object_context_helper);
|
||||
}
|
||||
|
||||
property = GET_OP2_ZVAL_PTR(BP_VAR_R);
|
||||
|
||||
do {
|
||||
@@ -1257,11 +1252,6 @@ ZEND_VM_HANDLER(132, ZEND_PRE_INC_OBJ, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, CACH
|
||||
|
||||
SAVE_OPLINE();
|
||||
object = GET_OP1_OBJ_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);
|
||||
|
||||
if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
|
||||
ZEND_VM_DISPATCH_TO_HELPER(zend_this_not_in_object_context_helper);
|
||||
}
|
||||
|
||||
property = GET_OP2_ZVAL_PTR(BP_VAR_R);
|
||||
|
||||
do {
|
||||
@@ -1335,11 +1325,6 @@ ZEND_VM_HANDLER(134, ZEND_POST_INC_OBJ, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, CAC
|
||||
|
||||
SAVE_OPLINE();
|
||||
object = GET_OP1_OBJ_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);
|
||||
|
||||
if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
|
||||
ZEND_VM_DISPATCH_TO_HELPER(zend_this_not_in_object_context_helper);
|
||||
}
|
||||
|
||||
property = GET_OP2_ZVAL_PTR(BP_VAR_R);
|
||||
|
||||
do {
|
||||
@@ -2007,11 +1992,6 @@ ZEND_VM_HOT_OBJ_HANDLER(82, ZEND_FETCH_OBJ_R, CONST|TMPVAR|UNUSED|THIS|CV, CONST
|
||||
|
||||
SAVE_OPLINE();
|
||||
container = GET_OP1_OBJ_ZVAL_PTR_UNDEF(BP_VAR_R);
|
||||
|
||||
if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
|
||||
ZEND_VM_DISPATCH_TO_HELPER(zend_this_not_in_object_context_helper);
|
||||
}
|
||||
|
||||
offset = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
|
||||
|
||||
if (OP1_TYPE == IS_CONST ||
|
||||
@@ -2132,10 +2112,6 @@ ZEND_VM_HANDLER(85, ZEND_FETCH_OBJ_W, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, FETCH
|
||||
SAVE_OPLINE();
|
||||
|
||||
container = GET_OP1_OBJ_ZVAL_PTR_PTR_UNDEF(BP_VAR_W);
|
||||
if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
|
||||
ZEND_VM_DISPATCH_TO_HELPER(zend_this_not_in_object_context_helper);
|
||||
}
|
||||
|
||||
property = GET_OP2_ZVAL_PTR(BP_VAR_R);
|
||||
result = EX_VAR(opline->result.var);
|
||||
zend_fetch_property_address(
|
||||
@@ -2156,10 +2132,6 @@ ZEND_VM_HANDLER(88, ZEND_FETCH_OBJ_RW, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, CACH
|
||||
|
||||
SAVE_OPLINE();
|
||||
container = GET_OP1_OBJ_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW);
|
||||
|
||||
if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
|
||||
ZEND_VM_DISPATCH_TO_HELPER(zend_this_not_in_object_context_helper);
|
||||
}
|
||||
property = GET_OP2_ZVAL_PTR(BP_VAR_R);
|
||||
result = EX_VAR(opline->result.var);
|
||||
zend_fetch_property_address(result, container, OP1_TYPE, property, OP2_TYPE, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_RW, 0, 1 OPLINE_CC EXECUTE_DATA_CC);
|
||||
@@ -2179,11 +2151,6 @@ ZEND_VM_COLD_CONST_HANDLER(91, ZEND_FETCH_OBJ_IS, CONST|TMPVAR|UNUSED|THIS|CV, C
|
||||
|
||||
SAVE_OPLINE();
|
||||
container = GET_OP1_OBJ_ZVAL_PTR(BP_VAR_IS);
|
||||
|
||||
if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
|
||||
ZEND_VM_DISPATCH_TO_HELPER(zend_this_not_in_object_context_helper);
|
||||
}
|
||||
|
||||
offset = GET_OP2_ZVAL_PTR(BP_VAR_R);
|
||||
|
||||
if (OP1_TYPE == IS_CONST ||
|
||||
@@ -2310,11 +2277,6 @@ ZEND_VM_HANDLER(97, ZEND_FETCH_OBJ_UNSET, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, C
|
||||
|
||||
SAVE_OPLINE();
|
||||
container = GET_OP1_OBJ_ZVAL_PTR_PTR_UNDEF(BP_VAR_UNSET);
|
||||
|
||||
if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
|
||||
ZEND_VM_DISPATCH_TO_HELPER(zend_this_not_in_object_context_helper);
|
||||
}
|
||||
|
||||
property = GET_OP2_ZVAL_PTR(BP_VAR_R);
|
||||
result = EX_VAR(opline->result.var);
|
||||
zend_fetch_property_address(result, container, OP1_TYPE, property, OP2_TYPE, ((OP2_TYPE == IS_CONST) ? CACHE_ADDR(opline->extended_value) : NULL), BP_VAR_UNSET, 0, 1 OPLINE_CC EXECUTE_DATA_CC);
|
||||
@@ -2369,11 +2331,6 @@ ZEND_VM_HANDLER(24, ZEND_ASSIGN_OBJ, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, CACHE_
|
||||
|
||||
SAVE_OPLINE();
|
||||
object = GET_OP1_OBJ_ZVAL_PTR_PTR_UNDEF(BP_VAR_W);
|
||||
|
||||
if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
|
||||
ZEND_VM_DISPATCH_TO_HELPER(zend_this_not_in_object_context_helper);
|
||||
}
|
||||
|
||||
property = GET_OP2_ZVAL_PTR(BP_VAR_R);
|
||||
value = GET_OP_DATA_ZVAL_PTR(BP_VAR_R);
|
||||
|
||||
@@ -2712,11 +2669,6 @@ ZEND_VM_HANDLER(32, ZEND_ASSIGN_OBJ_REF, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, CA
|
||||
SAVE_OPLINE();
|
||||
|
||||
container = GET_OP1_OBJ_ZVAL_PTR_PTR_UNDEF(BP_VAR_W);
|
||||
|
||||
if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
|
||||
ZEND_VM_DISPATCH_TO_HELPER(zend_this_not_in_object_context_helper);
|
||||
}
|
||||
|
||||
property = GET_OP2_ZVAL_PTR(BP_VAR_R);
|
||||
|
||||
value_ptr = GET_OP_DATA_ZVAL_PTR_PTR(BP_VAR_W);
|
||||
@@ -3403,10 +3355,6 @@ ZEND_VM_HOT_OBJ_HANDLER(112, ZEND_INIT_METHOD_CALL, CONST|TMPVAR|UNUSED|THIS|CV,
|
||||
|
||||
object = GET_OP1_OBJ_ZVAL_PTR_UNDEF(BP_VAR_R);
|
||||
|
||||
if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) {
|
||||
ZEND_VM_DISPATCH_TO_HELPER(zend_this_not_in_object_context_helper);
|
||||
}
|
||||
|
||||
if (OP2_TYPE != IS_CONST) {
|
||||
function_name = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
|
||||
}
|
||||
@@ -5342,10 +5290,6 @@ ZEND_VM_COLD_CONST_HANDLER(110, ZEND_CLONE, CONST|TMPVAR|UNUSED|THIS|CV, ANY)
|
||||
SAVE_OPLINE();
|
||||
obj = GET_OP1_OBJ_ZVAL_PTR_UNDEF(BP_VAR_R);
|
||||
|
||||
if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(obj) == IS_UNDEF)) {
|
||||
ZEND_VM_DISPATCH_TO_HELPER(zend_this_not_in_object_context_helper);
|
||||
}
|
||||
|
||||
do {
|
||||
if (OP1_TYPE == IS_CONST ||
|
||||
(OP1_TYPE != IS_UNUSED && UNEXPECTED(Z_TYPE_P(obj) != IS_OBJECT))) {
|
||||
@@ -6080,9 +6024,6 @@ ZEND_VM_HANDLER(76, ZEND_UNSET_OBJ, VAR|UNUSED|THIS|CV, CONST|TMPVAR|CV, CACHE_S
|
||||
|
||||
SAVE_OPLINE();
|
||||
container = GET_OP1_OBJ_ZVAL_PTR_PTR_UNDEF(BP_VAR_UNSET);
|
||||
if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
|
||||
ZEND_VM_DISPATCH_TO_HELPER(zend_this_not_in_object_context_helper);
|
||||
}
|
||||
offset = GET_OP2_ZVAL_PTR(BP_VAR_R);
|
||||
|
||||
do {
|
||||
@@ -6784,11 +6725,6 @@ ZEND_VM_COLD_CONST_HANDLER(148, ZEND_ISSET_ISEMPTY_PROP_OBJ, CONST|TMPVAR|UNUSED
|
||||
|
||||
SAVE_OPLINE();
|
||||
container = GET_OP1_OBJ_ZVAL_PTR(BP_VAR_IS);
|
||||
|
||||
if (OP1_TYPE == IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) {
|
||||
ZEND_VM_DISPATCH_TO_HELPER(zend_this_not_in_object_context_helper);
|
||||
}
|
||||
|
||||
offset = GET_OP2_ZVAL_PTR(BP_VAR_R);
|
||||
|
||||
if (OP1_TYPE == IS_CONST ||
|
||||
@@ -8229,7 +8165,7 @@ ZEND_VM_HANDLER(183, ZEND_BIND_STATIC, CV, UNUSED, REF)
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
}
|
||||
|
||||
ZEND_VM_HANDLER(184, ZEND_FETCH_THIS, UNUSED, UNUSED)
|
||||
ZEND_VM_HOT_HANDLER(184, ZEND_FETCH_THIS, UNUSED, UNUSED)
|
||||
{
|
||||
USE_OPLINE
|
||||
|
||||
|
||||
+1
-794
File diff suppressed because it is too large
Load Diff
@@ -1899,66 +1899,6 @@ failure:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void zend_calc_checked_this_r(zend_bitset checked_this, const zend_op_array *op_array, zend_cfg *cfg, int b, int checked)
|
||||
{
|
||||
zend_op *opline = &op_array->opcodes[cfg->blocks[b].start];
|
||||
zend_op *end = opline + cfg->blocks[b].len;
|
||||
int old_checked = checked;
|
||||
int i;
|
||||
|
||||
for (; opline < end; opline++) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_ASSIGN_OBJ_OP:
|
||||
case ZEND_PRE_INC_OBJ:
|
||||
case ZEND_PRE_DEC_OBJ:
|
||||
case ZEND_POST_INC_OBJ:
|
||||
case ZEND_POST_DEC_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_FUNC_ARG:
|
||||
case ZEND_FETCH_OBJ_UNSET:
|
||||
case ZEND_ASSIGN_OBJ:
|
||||
case ZEND_ASSIGN_OBJ_REF:
|
||||
case ZEND_INIT_METHOD_CALL:
|
||||
case ZEND_CLONE:
|
||||
case ZEND_UNSET_OBJ:
|
||||
case ZEND_ISSET_ISEMPTY_PROP_OBJ:
|
||||
if (opline->op1_type != IS_UNUSED) {
|
||||
break;
|
||||
}
|
||||
case ZEND_FETCH_THIS:
|
||||
if (checked) {
|
||||
zend_bitset_incl(checked_this, (opline - op_array->opcodes));
|
||||
} else {
|
||||
checked = 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cfg->blocks[b].flags & ZEND_BB_TRY) {
|
||||
checked = old_checked;
|
||||
}
|
||||
|
||||
for (i = cfg->blocks[b].children; i >= 0; i = cfg->blocks[i].next_child) {
|
||||
zend_calc_checked_this_r(checked_this, op_array, cfg, i, checked);
|
||||
}
|
||||
}
|
||||
|
||||
static zend_bitset zend_calc_checked_this(zend_arena **arena, const zend_op_array *op_array, zend_cfg *cfg)
|
||||
{
|
||||
uint32_t bitset_len = zend_bitset_len(op_array->last);
|
||||
zend_bitset checked_this = zend_arena_calloc(arena, bitset_len, ZEND_BITSET_ELM_SIZE);
|
||||
|
||||
zend_calc_checked_this_r(checked_this, op_array, cfg, 0, 0);
|
||||
|
||||
return checked_this;
|
||||
}
|
||||
|
||||
static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op *rt_opline)
|
||||
{
|
||||
int b, i, end;
|
||||
@@ -1968,7 +1908,6 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
|
||||
int call_level = 0;
|
||||
void *checkpoint = NULL;
|
||||
zend_lifetime_interval **ra = NULL;
|
||||
zend_bitset checked_this = NULL;
|
||||
zend_bool is_terminated = 1; /* previous basic block is terminated by jump */
|
||||
zend_bool recv_emitted = 0; /* emitted at least one RECV opcode */
|
||||
|
||||
@@ -2348,10 +2287,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
|
||||
goto done;
|
||||
case ZEND_FETCH_OBJ_R:
|
||||
case ZEND_FETCH_OBJ_IS:
|
||||
if (opline->op1_type == IS_UNUSED && !checked_this) {
|
||||
checked_this = zend_calc_checked_this(&CG(arena), op_array, &ssa->cfg);
|
||||
}
|
||||
if (!zend_jit_fetch_obj_read(&dasm_state, opline, op_array, ssa, checked_this)) {
|
||||
if (!zend_jit_fetch_obj_read(&dasm_state, opline, op_array, ssa)) {
|
||||
goto jit_failure;
|
||||
}
|
||||
goto done;
|
||||
|
||||
@@ -2003,24 +2003,6 @@ static int zend_jit_cannot_add_element_stub(dasm_State **Dst)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int zend_jit_not_obj_stub(dasm_State **Dst)
|
||||
{
|
||||
|->not_obj:
|
||||
|.if X64
|
||||
| xor CARG1, CARG1
|
||||
| LOAD_ADDR CARG2, "Using $this when not in object context"
|
||||
| EXT_CALL zend_throw_error, r0
|
||||
|.else
|
||||
| sub r4, 8
|
||||
| push "Using $this when not in object context"
|
||||
| push 0
|
||||
| EXT_CALL zend_throw_error, r0
|
||||
| add r4, 16
|
||||
|.endif
|
||||
| jmp ->exception_handler
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int zend_jit_undefined_function_stub(dasm_State **Dst)
|
||||
{
|
||||
|->undefined_function:
|
||||
@@ -2283,7 +2265,6 @@ static const zend_jit_stub zend_jit_stubs[] = {
|
||||
JIT_STUB(undefined_offset_ex),
|
||||
JIT_STUB(undefined_index_ex),
|
||||
JIT_STUB(cannot_add_element_ex),
|
||||
JIT_STUB(not_obj),
|
||||
JIT_STUB(undefined_function),
|
||||
JIT_STUB(negative_shift),
|
||||
JIT_STUB(mod_by_zero),
|
||||
@@ -9389,7 +9370,7 @@ static zend_bool zend_may_be_dynamic_property(zend_class_entry *ce, zend_string
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zend_jit_fetch_obj_read(dasm_State **Dst, zend_op *opline, const zend_op_array *op_array, zend_ssa *ssa, zend_bitset checked_this)
|
||||
static int zend_jit_fetch_obj_read(dasm_State **Dst, zend_op *opline, const zend_op_array *op_array, zend_ssa *ssa)
|
||||
{
|
||||
uint32_t op1_info;
|
||||
zend_class_entry *ce = NULL;
|
||||
@@ -9433,14 +9414,6 @@ static int zend_jit_fetch_obj_read(dasm_State **Dst, zend_op *opline, const zend
|
||||
offset = zend_get_known_property_offset(ce, Z_STR_P(member), opline->op1_type == IS_UNUSED, op_array->filename);
|
||||
|
||||
if (opline->op1_type == IS_UNUSED) {
|
||||
if (!checked_this || !zend_bitset_in(checked_this, (opline - op_array->opcodes))) {
|
||||
| IF_ZVAL_TYPE this_addr, IS_UNDEF, >1
|
||||
|.cold_code
|
||||
|1:
|
||||
| SAVE_VALID_OPLINE opline
|
||||
| jmp ->not_obj
|
||||
|.code
|
||||
}
|
||||
| GET_ZVAL_PTR FCARG1a, this_addr
|
||||
} else {
|
||||
if (op1_info & MAY_BE_REF) {
|
||||
|
||||
Reference in New Issue
Block a user