Skip to content

Optimize match(true) #18423

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 60 additions & 1 deletion Zend/Optimizer/block_pass.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
49 changes: 49 additions & 0 deletions ext/opcache/tests/match/005.phpt
Original file line number Diff line number Diff line change
@@ -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 changes: 49 additions & 0 deletions ext/opcache/tests/opt/block_pass_007.phpt
Original file line number Diff line number Diff line change
@@ -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)