Skip to content

Commit 753287d

Browse files
committed
VAR|TMP overhaul
The aim of this PR is twofold: - Reduce the number of highly similar TMP|VAR handlers - Avoid ZVAL_DEREF in most of these cases This is achieved by guaranteeing that all zend_compile_expr() calls, as well as all other compile calls with BP_VAR_R, will result in a TMP variable. This implies that the result will not contain an IS_INDIRECT or IS_REFERENCE value, which was mostly already the case, with two exceptions: - Calls to return-by-reference functions. Because return-by-reference functions are quite rare, this is solved by delegating the DEREF to the RETURN_BY_REF handler, which will examine the stack to check whether the caller expects a VAR or TMP to understand whether the DEREF is needed. - By-reference assignments, including both $a = &$b, as well as $a = [&$b]. When the result of these expressions is used in a BP_VAR_R context, it will be passed to a new ZEND_DEREF opcode beforehand. This is exceptionally rare. Preliminary testing shows a 1.1% wall time improvement in Symfony Demo and roughly 0.5% in Wordpress.
1 parent 0dd1bdc commit 753287d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+16537
-23050
lines changed

Zend/Optimizer/block_pass.c

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -289,29 +289,34 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
289289
MAKE_NOP(opline);
290290
++(*opt_count);
291291
break;
292-
case ZEND_ASSIGN:
293-
case ZEND_ASSIGN_DIM:
294-
case ZEND_ASSIGN_OBJ:
295-
case ZEND_ASSIGN_STATIC_PROP:
296-
case ZEND_ASSIGN_OP:
297-
case ZEND_ASSIGN_DIM_OP:
298-
case ZEND_ASSIGN_OBJ_OP:
299-
case ZEND_ASSIGN_STATIC_PROP_OP:
300-
case ZEND_PRE_INC:
301-
case ZEND_PRE_DEC:
302-
case ZEND_PRE_INC_OBJ:
303-
case ZEND_PRE_DEC_OBJ:
304-
case ZEND_PRE_INC_STATIC_PROP:
305-
case ZEND_PRE_DEC_STATIC_PROP:
292+
case ZEND_QM_ASSIGN:
306293
if (src < op_array->opcodes + block->start) {
307294
break;
308295
}
309296
src->result_type = IS_UNUSED;
310297
VAR_SOURCE(opline->op1) = NULL;
311298
MAKE_NOP(opline);
312299
++(*opt_count);
300+
if (src->op1_type & (IS_VAR|IS_TMP_VAR)) {
301+
src->opcode = ZEND_FREE;
302+
} else if (src->op1_type == IS_CONST) {
303+
MAKE_NOP(src);
304+
} else if (src->op1_type == IS_CV) {
305+
src->opcode = ZEND_CHECK_VAR;
306+
SET_UNUSED(src->result);
307+
}
313308
break;
314309
default:
310+
if (!zend_op_may_elide_result(src->opcode)) {
311+
break;
312+
}
313+
if (src < op_array->opcodes + block->start) {
314+
break;
315+
}
316+
src->result_type = IS_UNUSED;
317+
VAR_SOURCE(opline->op1) = NULL;
318+
MAKE_NOP(opline);
319+
++(*opt_count);
315320
break;
316321
}
317322
}
@@ -985,6 +990,8 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
985990
src = VAR_SOURCE(opline->op1);
986991
if (src &&
987992
src->opcode != ZEND_COPY_TMP &&
993+
/* See gh20628.phpt, breaks live-range calculation. */
994+
src->opcode != ZEND_NEW &&
988995
src->opcode != ZEND_ADD_ARRAY_ELEMENT &&
989996
src->opcode != ZEND_ADD_ARRAY_UNPACK &&
990997
(src->opcode != ZEND_DECLARE_LAMBDA_FUNCTION ||

Zend/Optimizer/dce.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,16 @@ static inline bool may_have_side_effects(
162162
case ZEND_EXT_FCALL_END:
163163
case ZEND_TICKS:
164164
case ZEND_YIELD:
165-
case ZEND_YIELD_FROM:
166165
case ZEND_VERIFY_NEVER_TYPE:
167166
/* Intrinsic side effects */
168167
return true;
168+
case ZEND_YIELD_FROM: {
169+
uint32_t t1 = OP1_INFO();
170+
if ((t1 & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY && MAY_BE_EMPTY_ONLY(t1)) {
171+
return false;
172+
}
173+
return true;
174+
}
169175
case ZEND_DO_FCALL:
170176
case ZEND_DO_FCALL_BY_NAME:
171177
case ZEND_DO_ICALL:

Zend/Optimizer/zend_inference.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5336,6 +5336,13 @@ ZEND_API bool zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op
53365336
return 1;
53375337
}
53385338
return 0;
5339+
case ZEND_YIELD_FROM: {
5340+
uint32_t t1 = OP1_INFO();
5341+
if ((t1 & (MAY_BE_ANY|MAY_BE_UNDEF)) == MAY_BE_ARRAY && MAY_BE_EMPTY_ONLY(t1)) {
5342+
return false;
5343+
}
5344+
return true;
5345+
}
53395346
default:
53405347
return 1;
53415348
}

Zend/tests/gh20628.inc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<?php
2+
3+
$this->bar();

Zend/tests/gh20628.phpt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
Invalid opcode for method call with FETCH_THIS
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public function bar() {
8+
var_dump($this, __METHOD__);
9+
}
10+
11+
public function test() {
12+
require __DIR__ . '/gh20628.inc';
13+
}
14+
}
15+
16+
(new Foo)->test();
17+
18+
?>
19+
--EXPECTF--
20+
object(Foo)#%d (0) {
21+
}
22+
string(8) "Foo::bar"

Zend/tests/gh20628_001.phpt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
Nullsafe operator does not support BP_VAR_W
3+
--FILE--
4+
<?php
5+
6+
function &test($foo) {
7+
return $foo?->bar();
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Cannot take reference of a nullsafe chain in %s on line %d

Zend/tests/gh20628_002.phpt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
Pipes support return-by-reference
3+
--FILE--
4+
<?php
5+
6+
function &foo($val) {
7+
global $x;
8+
$x = $val;
9+
return $x;
10+
}
11+
12+
function &bar() {
13+
return 42 |> foo(...);
14+
}
15+
16+
$xRef = &bar();
17+
$xRef++;
18+
var_dump($x);
19+
20+
?>
21+
--EXPECT--
22+
int(43)

Zend/tests/gh20628_003.phpt

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
--TEST--
2+
Missing separation for ASSIGN_DIM with ref OP_DATA
3+
--FILE--
4+
<?php
5+
6+
function a() {
7+
$a[] = $b[] = &$a;
8+
var_dump($a, $b);
9+
}
10+
11+
function b() {
12+
$a = [];
13+
$b = [];
14+
$a[] = $b[] = &$a;
15+
var_dump($a, $b);
16+
}
17+
18+
function c() {
19+
$a[] = $b = &$a;
20+
var_dump($a, $b);
21+
}
22+
23+
function d() {
24+
$a = $b[] = &$a;
25+
var_dump($a, $b);
26+
}
27+
28+
a();
29+
b();
30+
c();
31+
d();
32+
33+
?>
34+
--EXPECT--
35+
array(1) {
36+
[0]=>
37+
NULL
38+
}
39+
array(1) {
40+
[0]=>
41+
&array(1) {
42+
[0]=>
43+
NULL
44+
}
45+
}
46+
array(1) {
47+
[0]=>
48+
array(0) {
49+
}
50+
}
51+
array(1) {
52+
[0]=>
53+
&array(1) {
54+
[0]=>
55+
array(0) {
56+
}
57+
}
58+
}
59+
array(1) {
60+
[0]=>
61+
NULL
62+
}
63+
array(1) {
64+
[0]=>
65+
NULL
66+
}
67+
NULL
68+
array(1) {
69+
[0]=>
70+
&NULL
71+
}

Zend/tests/list/bug73663.phpt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
--TEST--
22
Bug #73663 ("Invalid opcode 65/16/8" occurs with a variable created with list())
3+
--XFAIL--
4+
list() assign resulting in a ref only when there's a single by-ref fetch is kinda weird.
35
--FILE--
46
<?php
57
function change(&$ref) {

Zend/tests/operator_unsupported_types.phpt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ Unsupported operand types: stdClass * stdClass
287287
Unsupported operand types: stdClass * resource
288288
Unsupported operand types: stdClass * string
289289
Unsupported operand types: resource * array
290-
Unsupported operand types: stdClass * resource
290+
Unsupported operand types: resource * stdClass
291291
Unsupported operand types: resource * resource
292292
Unsupported operand types: resource * string
293293
Unsupported operand types: string * array
@@ -753,7 +753,7 @@ Unsupported operand types: stdClass & stdClass
753753
Unsupported operand types: stdClass & resource
754754
Unsupported operand types: stdClass & string
755755
Unsupported operand types: resource & array
756-
Unsupported operand types: stdClass & resource
756+
Unsupported operand types: resource & stdClass
757757
Unsupported operand types: resource & resource
758758
Unsupported operand types: resource & string
759759
Unsupported operand types: string & array
@@ -828,7 +828,7 @@ Unsupported operand types: stdClass | stdClass
828828
Unsupported operand types: stdClass | resource
829829
Unsupported operand types: stdClass | string
830830
Unsupported operand types: resource | array
831-
Unsupported operand types: stdClass | resource
831+
Unsupported operand types: resource | stdClass
832832
Unsupported operand types: resource | resource
833833
Unsupported operand types: resource | string
834834
Unsupported operand types: string | array
@@ -903,7 +903,7 @@ Unsupported operand types: stdClass ^ stdClass
903903
Unsupported operand types: stdClass ^ resource
904904
Unsupported operand types: stdClass ^ string
905905
Unsupported operand types: resource ^ array
906-
Unsupported operand types: stdClass ^ resource
906+
Unsupported operand types: resource ^ stdClass
907907
Unsupported operand types: resource ^ resource
908908
Unsupported operand types: resource ^ string
909909
Unsupported operand types: string ^ array

0 commit comments

Comments
 (0)