1
0
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:
Nikita Popov
2019-02-13 11:37:32 +01:00
parent 776f4bc680
commit aef8836110
5 changed files with 24 additions and 954 deletions
+20 -2
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+1 -65
View File
@@ -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;
+1 -28
View File
@@ -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) {