Skip to content

Commit 03651fa

Browse files
committed
Optimize special functions with help of JMP_FRAMELESS
1 parent 06738fc commit 03651fa

4 files changed

Lines changed: 118 additions & 27 deletions

File tree

Zend/tests/bug74164.phpt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ call_user_func(function (array &$ref) {var_dump("xxx");}, 'not_an_array_variable
1414
--EXPECTF--
1515
Fatal error: Uncaught Exception: {closure:%s:%d}(): Argument #1 ($ref) must be passed by reference, value given in %s:%d
1616
Stack trace:
17-
#0 [internal function]: {closure:%s:%d}(2, '%s', '%s', 9)
18-
#1 %sbug74164.php(%d): call_user_func(%s)
19-
#2 {main}
17+
#0 %s(%d): {closure:%s:%d}(2, '%s', '%s', 9)
18+
#1 {main}
2019
thrown in %sbug74164.php on line %d

Zend/tests/call_user_functions/call_user_func_strict_arginfo_check.phpt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,9 @@ namespace Foo;
1010
var_dump(call_user_func('strlen', false));
1111

1212
?>
13-
--EXPECT--
14-
int(0)
13+
--EXPECTF--
14+
Fatal error: Uncaught TypeError: strlen(): Argument #1 ($string) must be of type string, false given in %s:%d
15+
Stack trace:
16+
#0 %s(%d): strlen(false)
17+
#1 {main}
18+
thrown in %s on line %d

Zend/zend_compile.c

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ static zend_op *zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t
101101
static void zend_compile_expr(znode *result, zend_ast *ast);
102102
static void zend_compile_stmt(zend_ast *ast);
103103
static void zend_compile_assign(znode *result, zend_ast *ast);
104+
static zend_result zend_try_compile_special_func(znode *result, zend_string *lcname, zend_ast_list *args, zend_function *fbc, uint32_t type);
105+
static bool zend_is_special_func(const zend_string *name);
104106

105107
#ifdef ZEND_CHECK_STACK_LIMIT
106108
zend_never_inline static void zend_stack_limit_error(void)
@@ -4709,6 +4711,7 @@ static uint32_t zend_compile_frameless_icall(znode *result, zend_ast_list *args,
47094711
static void zend_compile_ns_call(znode *result, znode *name_node, zend_ast *args_ast, uint32_t lineno, uint32_t type) /* {{{ */
47104712
{
47114713
int name_constants = zend_add_ns_func_name_literal(Z_STR(name_node->u.constant));
4714+
bool special_func_handler = false;
47124715

47134716
/* Find frameless function with same name. */
47144717
zend_function *frameless_function = NULL;
@@ -4725,7 +4728,13 @@ static void zend_compile_ns_call(znode *result, znode *name_node, zend_ast *args
47254728
const zend_frameless_function_info *frameless_function_info = NULL;
47264729
if (frameless_function) {
47274730
frameless_function_info = find_frameless_function_info(zend_ast_get_list(args_ast), frameless_function, type);
4728-
if (frameless_function_info) {
4731+
4732+
if (!frameless_function_info) {
4733+
/* Check for dedicated special function. */
4734+
special_func_handler = zend_is_special_func(frameless_function->common.function_name);
4735+
}
4736+
4737+
if (frameless_function_info || special_func_handler) {
47294738
CG(context).in_jmp_frameless_branch = true;
47304739
znode op1;
47314740
op1.op_type = IS_CONST;
@@ -4759,6 +4768,44 @@ static void zend_compile_ns_call(znode *result, znode *name_node, zend_ast *args
47594768
SET_NODE(flf_icall->result, result);
47604769
zend_update_jump_target_to_next(jmp_end_opnum);
47614770

4771+
CG(context).in_jmp_frameless_branch = false;
4772+
} else if (special_func_handler) {
4773+
CG(zend_lineno) = lineno;
4774+
4775+
uint32_t jmp_end_opnum = zend_emit_jump(0);
4776+
uint32_t jmp_fl_target = get_next_op_number();
4777+
4778+
znode tmp_result;
4779+
if (zend_try_compile_special_func(&tmp_result, frameless_function->common.function_name, zend_ast_get_list(args_ast), frameless_function, type) != SUCCESS) {
4780+
zend_op *ops = CG(active_op_array)->opcodes;
4781+
4782+
/* Undo work */
4783+
zend_op *jmp_fl = &ops[jmp_fl_opnum];
4784+
jmp_fl->opcode = ZEND_NOP;
4785+
SET_UNUSED(jmp_fl->op1);
4786+
zend_op *jmp_end = &ops[jmp_end_opnum];
4787+
jmp_end->opcode = ZEND_NOP;
4788+
SET_UNUSED(jmp_end->op1);
4789+
} else {
4790+
uint32_t result_target = get_next_op_number() - 1;
4791+
4792+
zend_op *jmp_fl = &CG(active_op_array)->opcodes[jmp_fl_opnum];
4793+
jmp_fl->op2.opline_num = jmp_fl_target;
4794+
jmp_fl->extended_value = zend_alloc_cache_slot();
4795+
4796+
if (tmp_result.op_type == IS_CONST || 1 /* TODO ? */) {
4797+
zend_op *qm_assign = zend_emit_op(NULL, ZEND_QM_ASSIGN, &tmp_result, NULL);
4798+
SET_NODE(qm_assign->result, result);
4799+
} else {
4800+
// TODO: we're left with a useless tmp?
4801+
zend_op *res_op = &CG(active_op_array)->opcodes[result_target];
4802+
res_op->result_type = result->op_type;
4803+
res_op->result = result->u.op;
4804+
}
4805+
4806+
zend_update_jump_target_to_next(jmp_end_opnum);
4807+
}
4808+
47624809
CG(context).in_jmp_frameless_branch = false;
47634810
}
47644811
}
@@ -4936,6 +4983,62 @@ static zend_result zend_compile_func_sprintf(znode *result, zend_ast_list *args)
49364983
return SUCCESS;
49374984
}
49384985

4986+
typedef struct zend_special_func {
4987+
const char *name;
4988+
size_t name_len;
4989+
// zend_result (*handler)(znode *, zend_ast_list *);
4990+
} zend_special_func;
4991+
4992+
static const zend_special_func zend_special_func_compile_handlers[] = {
4993+
{ZEND_STRL("strlen")},
4994+
{ZEND_STRL("is_null")},
4995+
{ZEND_STRL("is_bool")},
4996+
{ZEND_STRL("is_long")},
4997+
{ZEND_STRL("is_int")},
4998+
{ZEND_STRL("is_integer")},
4999+
{ZEND_STRL("is_float")},
5000+
{ZEND_STRL("is_double")},
5001+
{ZEND_STRL("is_string")},
5002+
{ZEND_STRL("is_array")},
5003+
{ZEND_STRL("is_object")},
5004+
{ZEND_STRL("is_resource")},
5005+
{ZEND_STRL("is_scalar")},
5006+
{ZEND_STRL("boolval")},
5007+
{ZEND_STRL("intval")},
5008+
{ZEND_STRL("floatval")},
5009+
{ZEND_STRL("doubleval")},
5010+
{ZEND_STRL("strval")},
5011+
{ZEND_STRL("defined")},
5012+
{ZEND_STRL("chr")},
5013+
{ZEND_STRL("ord")},
5014+
{ZEND_STRL("call_user_func_array")},
5015+
{ZEND_STRL("call_user_func")},
5016+
{ZEND_STRL("in_array")},
5017+
{ZEND_STRL("count")},
5018+
{ZEND_STRL("sizeof")},
5019+
{ZEND_STRL("get_class")},
5020+
{ZEND_STRL("get_called_class")},
5021+
{ZEND_STRL("gettype")},
5022+
{ZEND_STRL("func_num_args")},
5023+
{ZEND_STRL("func_get_args")},
5024+
{ZEND_STRL("array_slice")},
5025+
{ZEND_STRL("array_key_exists")},
5026+
{ZEND_STRL("sprintf")},
5027+
{NULL, 0},
5028+
};
5029+
5030+
static bool zend_is_special_func(const zend_string *name)
5031+
{
5032+
const zend_special_func *entry = &zend_special_func_compile_handlers[0];
5033+
for (; entry->name; entry++) {
5034+
if (zend_string_equals_cstr(name, entry->name, entry->name_len)) {
5035+
return true;
5036+
}
5037+
}
5038+
return false;
5039+
}
5040+
5041+
// TODO: maybe this can migrate to use 'zend_special_func_compile_handlers'
49395042
static zend_result zend_try_compile_special_func_ex(znode *result, zend_string *lcname, zend_ast_list *args, zend_function *fbc, uint32_t type) /* {{{ */
49405043
{
49415044
if (zend_string_equals_literal(lcname, "strlen")) {

ext/reflection/tests/ReflectionFiber_notrace_2.phpt

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,13 @@ var_dump($reflection->getTrace());
1818
--EXPECTF--
1919
string(%d) "%sReflectionFiber_notrace_2.php"
2020
int(5)
21-
array(3) {
21+
array(2) {
2222
[0]=>
23-
array(4) {
23+
array(6) {
24+
["file"]=>
25+
string(%d) "%s"
26+
["line"]=>
27+
int(5)
2428
["function"]=>
2529
string(7) "suspend"
2630
["class"]=>
@@ -32,25 +36,6 @@ array(3) {
3236
}
3337
}
3438
[1]=>
35-
array(4) {
36-
["file"]=>
37-
string(%d) "%sReflectionFiber_notrace_2.php"
38-
["line"]=>
39-
int(5)
40-
["function"]=>
41-
string(14) "call_user_func"
42-
["args"]=>
43-
array(1) {
44-
[0]=>
45-
array(2) {
46-
[0]=>
47-
string(5) "Fiber"
48-
[1]=>
49-
string(7) "suspend"
50-
}
51-
}
52-
}
53-
[2]=>
5439
array(2) {
5540
["function"]=>
5641
string(%d) "{closure:%s:%d}"

0 commit comments

Comments
 (0)