Skip to content

Commit 973c312

Browse files
committed
Improve the optimizer's check if a function is a prototype or not
Currently, a function is considered a prototype if the function is not marked as final. However, a class marked as final or an anonymous class also make it impossible for a function to be overridden. Therefore, we know in these 2 cases too that the function is not a prototype. This allows the type inference algorithm to determine some types more precisely, and can allow for more optimizations of the instructions. Additionally, place some computation of the flags in their respective blocks as a micro-optimization.
1 parent 9f02a11 commit 973c312

File tree

2 files changed

+80
-3
lines changed

2 files changed

+80
-3
lines changed

Zend/Optimizer/zend_optimizer.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -891,16 +891,23 @@ zend_function *zend_optimizer_get_called_func(
891891
&op_array->scope->function_table, method_name);
892892
if (fbc) {
893893
bool is_private = (fbc->common.fn_flags & ZEND_ACC_PRIVATE) != 0;
894-
bool is_final = (fbc->common.fn_flags & ZEND_ACC_FINAL) != 0;
895-
bool same_scope = fbc->common.scope == op_array->scope;
896894
if (is_private) {
897895
/* Only use private method if in the same scope. We can't even use it
898896
* as a prototype, as it may be overridden with changed signature. */
897+
bool same_scope = fbc->common.scope == op_array->scope;
899898
return same_scope ? fbc : NULL;
900899
}
901900
/* If the method is non-final, it may be overridden,
902901
* but only with a compatible method signature. */
903-
*is_prototype = !is_final;
902+
/* We know it isn't a prototype if it can be overridden.
903+
* A method cannot be overridden if the class or method is final, or
904+
* if it's an anonymous class. */
905+
bool can_be_overridden = (fbc->common.fn_flags & ZEND_ACC_FINAL) == 0 &&
906+
(fbc->common.scope->ce_flags & ZEND_ACC_FINAL) == 0 &&
907+
(fbc->common.scope->ce_flags & ZEND_ACC_ANON_CLASS) == 0;
908+
if (can_be_overridden) {
909+
*is_prototype = true;
910+
}
904911
return fbc;
905912
}
906913
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
--TEST--
2+
Type inference test with final or anonymous class
3+
--INI--
4+
opcache.enable=1
5+
opcache.enable_cli=1
6+
opcache.optimization_level=-1
7+
opcache.opt_debug_level=0x20000
8+
opcache.preload=
9+
--EXTENSIONS--
10+
opcache
11+
--FILE--
12+
<?php
13+
14+
final class Test {
15+
public function getInt(): int {
16+
return 42;
17+
}
18+
public function getInt2(): int {
19+
return $this->getInt();
20+
}
21+
}
22+
23+
$anon = new class {
24+
public function getInt(): int {
25+
return 42;
26+
}
27+
public function getInt2(): int {
28+
return $this->getInt();
29+
}
30+
};
31+
32+
?>
33+
--EXPECTF--
34+
$_main:
35+
; (lines=5, args=0, vars=1, tmps=2)
36+
; (after optimizer)
37+
; %s
38+
0000 V2 = DECLARE_ANON_CLASS string("class@anonymous")
39+
0001 V1 = NEW 0 V2
40+
0002 DO_FCALL
41+
0003 ASSIGN CV0($anon) V1
42+
0004 RETURN int(1)
43+
LIVE RANGES:
44+
1: 0002 - 0003 (new)
45+
46+
Test::getInt:
47+
; (lines=1, args=0, vars=0, tmps=0)
48+
; (after optimizer)
49+
; %s
50+
0000 RETURN int(42)
51+
52+
Test::getInt2:
53+
; (lines=2, args=0, vars=0, tmps=1)
54+
; (after optimizer)
55+
; %s
56+
0000 V0 = QM_ASSIGN int(42)
57+
0001 RETURN V0
58+
59+
class@anonymous::getInt:
60+
; (lines=1, args=0, vars=0, tmps=0)
61+
; (after optimizer)
62+
; %s
63+
0000 RETURN int(42)
64+
65+
class@anonymous::getInt2:
66+
; (lines=2, args=0, vars=0, tmps=1)
67+
; (after optimizer)
68+
; %s
69+
0000 V0 = QM_ASSIGN int(42)
70+
0001 RETURN V0

0 commit comments

Comments
 (0)