mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Optimize match(true) (#18423)
* Optimizer: Optimize `IS_IDENTICAL` with true/false/null to `TYPE_CHECK` This optimization is already happening in the compiler for explicit `===` expressions, but not for `match()`, which also compiles to `IS_IDENTICAL`. * Optimizer: Optimize `T = BOOL(X) + TYPE_CHECK(T, true)` to just `BOOL` Resolves php/php-src#18411
This commit is contained in:
@@ -479,6 +479,10 @@ PHP 8.5 UPGRADE NOTES
|
||||
14. Performance Improvements
|
||||
========================================
|
||||
|
||||
- Core:
|
||||
. Remove OPcodes for identity comparisons against booleans, particularly
|
||||
for the match(true) pattern.
|
||||
|
||||
- ReflectionProperty:
|
||||
. Improved performance of the following methods: getValue(), getRawValue(),
|
||||
isInitialized(), setValue(), setRawValue().
|
||||
|
||||
@@ -470,7 +470,67 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
|
||||
goto optimize_bool;
|
||||
}
|
||||
break;
|
||||
case ZEND_IS_IDENTICAL:
|
||||
if (opline->op1_type == IS_CONST &&
|
||||
opline->op2_type == IS_CONST) {
|
||||
goto optimize_constant_binary_op;
|
||||
}
|
||||
|
||||
if (opline->op1_type == IS_CONST &&
|
||||
(Z_TYPE(ZEND_OP1_LITERAL(opline)) <= IS_TRUE && Z_TYPE(ZEND_OP1_LITERAL(opline)) >= IS_NULL)) {
|
||||
/* IS_IDENTICAL(TRUE, T) => TYPE_CHECK(T, TRUE)
|
||||
* IS_IDENTICAL(FALSE, T) => TYPE_CHECK(T, FALSE)
|
||||
* IS_IDENTICAL(NULL, T) => TYPE_CHECK(T, NULL)
|
||||
*/
|
||||
opline->opcode = ZEND_TYPE_CHECK;
|
||||
opline->extended_value = (1 << Z_TYPE(ZEND_OP1_LITERAL(opline)));
|
||||
COPY_NODE(opline->op1, opline->op2);
|
||||
SET_UNUSED(opline->op2);
|
||||
++(*opt_count);
|
||||
goto optimize_type_check;
|
||||
} else if (opline->op2_type == IS_CONST &&
|
||||
(Z_TYPE(ZEND_OP2_LITERAL(opline)) <= IS_TRUE && Z_TYPE(ZEND_OP2_LITERAL(opline)) >= IS_NULL)) {
|
||||
/* IS_IDENTICAL(T, TRUE) => TYPE_CHECK(T, TRUE)
|
||||
* IS_IDENTICAL(T, FALSE) => TYPE_CHECK(T, FALSE)
|
||||
* IS_IDENTICAL(T, NULL) => TYPE_CHECK(T, NULL)
|
||||
*/
|
||||
opline->opcode = ZEND_TYPE_CHECK;
|
||||
opline->extended_value = (1 << Z_TYPE(ZEND_OP2_LITERAL(opline)));
|
||||
SET_UNUSED(opline->op2);
|
||||
++(*opt_count);
|
||||
goto optimize_type_check;
|
||||
}
|
||||
break;
|
||||
case ZEND_TYPE_CHECK:
|
||||
optimize_type_check:
|
||||
if (opline->extended_value == (1 << IS_TRUE) || opline->extended_value == (1 << IS_FALSE)) {
|
||||
if (opline->op1_type == IS_TMP_VAR &&
|
||||
!zend_bitset_in(used_ext, VAR_NUM(opline->op1.var))) {
|
||||
src = VAR_SOURCE(opline->op1);
|
||||
|
||||
if (src) {
|
||||
switch (src->opcode) {
|
||||
case ZEND_BOOL:
|
||||
case ZEND_BOOL_NOT:
|
||||
/* T = BOOL(X) + TYPE_CHECK(T, TRUE) -> BOOL(X), NOP
|
||||
* T = BOOL(X) + TYPE_CHECK(T, FALSE) -> BOOL_NOT(X), NOP
|
||||
* T = BOOL_NOT(X) + TYPE_CHECK(T, TRUE) -> BOOL_NOT(X), NOP
|
||||
* T = BOOL_NOT(X) + TYPE_CHECK(T, FALSE) -> BOOL(X), NOP
|
||||
*/
|
||||
src->opcode =
|
||||
((src->opcode == ZEND_BOOL) == (opline->extended_value == (1 << IS_TRUE))) ?
|
||||
ZEND_BOOL : ZEND_BOOL_NOT;
|
||||
COPY_NODE(src->result, opline->result);
|
||||
SET_VAR_SOURCE(src);
|
||||
MAKE_NOP(opline);
|
||||
++(*opt_count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ZEND_BOOL:
|
||||
case ZEND_BOOL_NOT:
|
||||
optimize_bool:
|
||||
@@ -803,7 +863,6 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
|
||||
case ZEND_SR:
|
||||
case ZEND_IS_SMALLER:
|
||||
case ZEND_IS_SMALLER_OR_EQUAL:
|
||||
case ZEND_IS_IDENTICAL:
|
||||
case ZEND_IS_NOT_IDENTICAL:
|
||||
case ZEND_BOOL_XOR:
|
||||
case ZEND_BW_OR:
|
||||
|
||||
49
ext/opcache/tests/match/005.phpt
Normal file
49
ext/opcache/tests/match/005.phpt
Normal file
@@ -0,0 +1,49 @@
|
||||
--TEST--
|
||||
Match expression true
|
||||
--INI--
|
||||
opcache.enable=1
|
||||
opcache.enable_cli=1
|
||||
opcache.opt_debug_level=0x20000
|
||||
zend_test.observer.enabled=0
|
||||
--EXTENSIONS--
|
||||
opcache
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$text = 'Bienvenue chez nous';
|
||||
|
||||
$result = match (true) {
|
||||
!!preg_match('/Welcome/', $text), !!preg_match('/Hello/', $text) => 'en',
|
||||
!!preg_match('/Bienvenue/', $text), !!preg_match('/Bonjour/', $text) => 'fr',
|
||||
default => 'other',
|
||||
};
|
||||
|
||||
var_dump($result);
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
$_main:
|
||||
; (lines=20, args=0, vars=2, tmps=1)
|
||||
; (after optimizer)
|
||||
; %s
|
||||
0000 ASSIGN CV0($text) string("Bienvenue chez nous")
|
||||
0001 T2 = FRAMELESS_ICALL_2(preg_match) string("/Welcome/") CV0($text)
|
||||
0002 JMPNZ T2 0010
|
||||
0003 T2 = FRAMELESS_ICALL_2(preg_match) string("/Hello/") CV0($text)
|
||||
0004 JMPNZ T2 0010
|
||||
0005 T2 = FRAMELESS_ICALL_2(preg_match) string("/Bienvenue/") CV0($text)
|
||||
0006 JMPNZ T2 0012
|
||||
0007 T2 = FRAMELESS_ICALL_2(preg_match) string("/Bonjour/") CV0($text)
|
||||
0008 JMPNZ T2 0012
|
||||
0009 JMP 0014
|
||||
0010 T2 = QM_ASSIGN string("en")
|
||||
0011 JMP 0015
|
||||
0012 T2 = QM_ASSIGN string("fr")
|
||||
0013 JMP 0015
|
||||
0014 T2 = QM_ASSIGN string("other")
|
||||
0015 ASSIGN CV1($result) T2
|
||||
0016 INIT_FCALL 1 %d string("var_dump")
|
||||
0017 SEND_VAR CV1($result) 1
|
||||
0018 DO_ICALL
|
||||
0019 RETURN int(1)
|
||||
string(2) "fr"
|
||||
49
ext/opcache/tests/opt/block_pass_007.phpt
Normal file
49
ext/opcache/tests/opt/block_pass_007.phpt
Normal file
@@ -0,0 +1,49 @@
|
||||
--TEST--
|
||||
Block Pass 007: BOOL + TYPE_CHECK
|
||||
--INI--
|
||||
opcache.enable=1
|
||||
opcache.enable_cli=1
|
||||
opcache.optimization_level=-1
|
||||
opcache.opt_debug_level=0x20000
|
||||
--EXTENSIONS--
|
||||
opcache
|
||||
--FILE--
|
||||
<?php
|
||||
$f = random_int(1, 2);
|
||||
|
||||
var_dump(!$f === true);
|
||||
var_dump(!$f === false);
|
||||
var_dump(!!$f === true);
|
||||
var_dump(!!$f === false);
|
||||
?>
|
||||
--EXPECTF--
|
||||
$_main:
|
||||
; (lines=22, args=0, vars=1, tmps=1)
|
||||
; (after optimizer)
|
||||
; %s
|
||||
0000 INIT_FCALL 2 %d string("random_int")
|
||||
0001 SEND_VAL int(1) 1
|
||||
0002 SEND_VAL int(2) 2
|
||||
0003 V1 = DO_ICALL
|
||||
0004 ASSIGN CV0($f) V1
|
||||
0005 INIT_FCALL 1 %d string("var_dump")
|
||||
0006 T1 = BOOL_NOT CV0($f)
|
||||
0007 SEND_VAL T1 1
|
||||
0008 DO_ICALL
|
||||
0009 INIT_FCALL 1 %d string("var_dump")
|
||||
0010 T1 = BOOL CV0($f)
|
||||
0011 SEND_VAL T1 1
|
||||
0012 DO_ICALL
|
||||
0013 INIT_FCALL 1 %d string("var_dump")
|
||||
0014 T1 = BOOL CV0($f)
|
||||
0015 SEND_VAL T1 1
|
||||
0016 DO_ICALL
|
||||
0017 INIT_FCALL 1 %d string("var_dump")
|
||||
0018 T1 = BOOL_NOT CV0($f)
|
||||
0019 SEND_VAL T1 1
|
||||
0020 DO_ICALL
|
||||
0021 RETURN int(1)
|
||||
bool(false)
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(false)
|
||||
Reference in New Issue
Block a user