Skip to content

Commit a3a4549

Browse files
committed
Support PFAs in array_map() optimization
1 parent 2ee213a commit a3a4549

File tree

3 files changed

+202
-30
lines changed

3 files changed

+202
-30
lines changed

Zend/zend_compile.c

Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ static void zend_compile_expr(znode *result, zend_ast *ast);
105105
static void zend_compile_stmt(zend_ast *ast);
106106
static void zend_compile_assign(znode *result, zend_ast *ast);
107107

108+
static zend_ast *zend_partial_apply(zend_ast *callable_ast, zend_ast *pipe_arg);
109+
108110
#ifdef ZEND_CHECK_STACK_LIMIT
109111
zend_never_inline static void zend_stack_limit_error(void)
110112
{
@@ -5165,43 +5167,20 @@ static zend_result zend_compile_func_array_map(znode *result, zend_ast_list *arg
51655167
}
51665168

51675169
zend_ast *callback = args->child[0];
5168-
5169-
/* Bail out if the callback is not a FCC/PFA. */
5170-
zend_ast *args_ast;
5171-
switch (callback->kind) {
5172-
case ZEND_AST_CALL:
5173-
case ZEND_AST_STATIC_CALL:
5174-
args_ast = zend_ast_call_get_args(callback);
5175-
if (args_ast->kind != ZEND_AST_CALLABLE_CONVERT) {
5176-
return FAILURE;
5177-
}
5178-
5179-
break;
5180-
default:
5181-
return FAILURE;
5182-
}
5183-
5184-
/* Bail out if the callback is assert() due to the AST stringification logic
5185-
* breaking for the generated call.
5186-
*/
5187-
if (callback->kind == ZEND_AST_CALL && zend_string_equals_literal_ci(zend_ast_get_str(callback->child[0]), "assert")) {
5170+
if (callback->kind != ZEND_AST_CALL && callback->kind != ZEND_AST_STATIC_CALL) {
51885171
return FAILURE;
51895172
}
51905173

51915174
znode value;
51925175
value.op_type = IS_TMP_VAR;
51935176
value.u.op.var = get_temporary_variable();
51945177

5195-
zend_ast_list *callback_args = zend_ast_get_list(((zend_ast_fcc*)args_ast)->args);
5196-
zend_ast *call_args = zend_ast_create_list(0, ZEND_AST_ARG_LIST);
5197-
for (uint32_t i = 0; i < callback_args->children; i++) {
5198-
zend_ast *child = callback_args->child[i];
5199-
if (child->kind == ZEND_AST_PLACEHOLDER_ARG) {
5200-
call_args = zend_ast_list_add(call_args, zend_ast_create_znode(&value));
5201-
} else {
5202-
ZEND_ASSERT(0 && "not implemented");
5203-
call_args = zend_ast_list_add(call_args, child);
5204-
}
5178+
zend_ast *call_args = zend_partial_apply(callback,
5179+
zend_ast_create_znode(&value));
5180+
if (!call_args) {
5181+
CG(active_op_array)->T--;
5182+
/* The callback is not a FCC/PFA, or is not optimizable */
5183+
return FAILURE;
52055184
}
52065185

52075186
zend_op *opline;
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
--TEST--
2+
array_map(): foreach optimization - PFA
3+
--EXTENSIONS--
4+
opcache
5+
--INI--
6+
opcache.enable=1
7+
opcache.enable_cli=1
8+
opcache.opt_debug_level=0x20000
9+
--FILE--
10+
<?php
11+
12+
function plusn($x, $n) {
13+
return $x + $n;
14+
}
15+
16+
$array = range(1, 10);
17+
18+
$foo = array_map(plusn(?, 2), $array);
19+
20+
var_dump($foo);
21+
22+
?>
23+
--EXPECTF--
24+
$_main:
25+
; (lines=%d, args=0, vars=%d, tmps=%d)
26+
; (after optimizer)
27+
; %s
28+
0000 INIT_FCALL 2 %d string("range")
29+
0001 SEND_VAL int(1) 1
30+
0002 SEND_VAL int(10) 2
31+
0003 V2 = DO_ICALL
32+
0004 ASSIGN CV0($array) V2
33+
0005 TYPE_ASSERT 131079 string("array_map") CV0($array)
34+
0006 T2 = INIT_ARRAY 0 (packed) NEXT
35+
0007 V3 = FE_RESET_R CV0($array) 0015
36+
0008 T5 = FE_FETCH_R V3 T4 0015
37+
0009 INIT_FCALL 2 128 string("plusn")
38+
0010 SEND_VAL T4 1
39+
0011 SEND_VAL int(2) 2
40+
0012 V4 = DO_UCALL
41+
0013 T2 = ADD_ARRAY_ELEMENT V4 T5
42+
0014 JMP 0008
43+
0015 FE_FREE V3
44+
0016 ASSIGN CV1($foo) T2
45+
0017 INIT_FCALL 1 96 string("var_dump")
46+
0018 SEND_VAR CV1($foo) 1
47+
0019 DO_ICALL
48+
0020 RETURN int(1)
49+
LIVE RANGES:
50+
2: 0007 - 0016 (tmp/var)
51+
3: 0008 - 0015 (loop)
52+
4: 0009 - 0010 (tmp/var)
53+
5: 0009 - 0013 (tmp/var)
54+
55+
plusn:
56+
; (lines=4, args=2, vars=2, tmps=1)
57+
; (after optimizer)
58+
; %s
59+
0000 CV0($x) = RECV 1
60+
0001 CV1($n) = RECV 2
61+
0002 T2 = ADD CV0($x) CV1($n)
62+
0003 RETURN T2
63+
array(10) {
64+
[0]=>
65+
int(3)
66+
[1]=>
67+
int(4)
68+
[2]=>
69+
int(5)
70+
[3]=>
71+
int(6)
72+
[4]=>
73+
int(7)
74+
[5]=>
75+
int(8)
76+
[6]=>
77+
int(9)
78+
[7]=>
79+
int(10)
80+
[8]=>
81+
int(11)
82+
[9]=>
83+
int(12)
84+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
--TEST--
2+
array_map(): foreach optimization - unoptimizable PFA
3+
--EXTENSIONS--
4+
opcache
5+
--INI--
6+
opcache.enable=1
7+
opcache.enable_cli=1
8+
opcache.opt_debug_level=0x20000
9+
--FILE--
10+
<?php
11+
12+
function plusn($x, $n) {
13+
return $x + $n;
14+
}
15+
16+
$array = range(1, 10);
17+
18+
$foo = array_map(plusn(n: 2, ...), $array);
19+
20+
var_dump($foo);
21+
22+
?>
23+
--EXPECTF--
24+
$_main:
25+
; (lines=%d, args=0, vars=%d, tmps=%d)
26+
; (after optimizer)
27+
; %s
28+
0000 INIT_FCALL 2 %d string("range")
29+
0001 SEND_VAL int(1) 1
30+
0002 SEND_VAL int(10) 2
31+
0003 V2 = DO_ICALL
32+
0004 ASSIGN CV0($array) V2
33+
0005 INIT_FCALL 2 112 string("array_map")
34+
0006 INIT_FCALL 0 128 string("plusn")
35+
0007 SEND_VAL int(2) string("n")
36+
0008 T2 = CALLABLE_CONVERT_PARTIAL 2
37+
0009 SEND_VAL T2 1
38+
0010 SEND_VAR CV0($array) 2
39+
0011 V2 = DO_ICALL
40+
0012 ASSIGN CV1($foo) V2
41+
0013 INIT_FCALL 1 96 string("var_dump")
42+
0014 SEND_VAR CV1($foo) 1
43+
0015 DO_ICALL
44+
0016 RETURN int(1)
45+
46+
plusn:
47+
; (lines=4, args=2, vars=2, tmps=1)
48+
; (after optimizer)
49+
; %s
50+
0000 CV0($x) = RECV 1
51+
0001 CV1($n) = RECV 2
52+
0002 T2 = ADD CV0($x) CV1($n)
53+
0003 RETURN T2
54+
55+
$_main:
56+
; (lines=4, args=0, vars=1, tmps=1)
57+
; (after optimizer)
58+
; %s:1-9
59+
0000 T1 = DECLARE_LAMBDA_FUNCTION 0
60+
0001 BIND_LEXICAL T1 CV0($n)
61+
0002 FREE T1
62+
0003 RETURN int(1)
63+
LIVE RANGES:
64+
1: 0001 - 0002 (tmp/var)
65+
66+
{closure:pfa:%s:9}:
67+
; (lines=18, args=1, vars=2, tmps=2)
68+
; (after optimizer)
69+
; %s:9-9
70+
0000 CV0($x) = RECV 1
71+
0001 BIND_STATIC CV1($n)
72+
0002 T3 = FUNC_NUM_ARGS
73+
0003 T2 = IS_SMALLER_OR_EQUAL T3 int(1)
74+
0004 JMPZ T2 0010
75+
0005 INIT_FCALL 2 128 string("plusn")
76+
0006 SEND_VAR CV0($x) 1
77+
0007 SEND_VAR CV1($n) 2
78+
0008 V2 = DO_UCALL
79+
0009 RETURN V2
80+
0010 INIT_FCALL 2 128 string("plusn")
81+
0011 SEND_VAR CV0($x) 1
82+
0012 SEND_VAR CV1($n) 2
83+
0013 T2 = FUNC_GET_ARGS int(1)
84+
0014 SEND_UNPACK T2
85+
0015 CHECK_UNDEF_ARGS
86+
0016 V2 = DO_UCALL
87+
0017 RETURN V2
88+
array(10) {
89+
[0]=>
90+
int(3)
91+
[1]=>
92+
int(4)
93+
[2]=>
94+
int(5)
95+
[3]=>
96+
int(6)
97+
[4]=>
98+
int(7)
99+
[5]=>
100+
int(8)
101+
[6]=>
102+
int(9)
103+
[7]=>
104+
int(10)
105+
[8]=>
106+
int(11)
107+
[9]=>
108+
int(12)
109+
}

0 commit comments

Comments
 (0)