Implement match expression

RFC: https://wiki.php.net/rfc/match_expression_v2

Closes GH-5371.
This commit is contained in:
Ilija Tovilo 2020-04-09 22:36:37 +02:00
parent c60d0dc2f4
commit 9fa1d13301
No known key found for this signature in database
GPG Key ID: 3F123D0ADD448198
58 changed files with 2445 additions and 622 deletions

View File

@ -571,6 +571,8 @@ PHP 8.0 UPGRADE NOTES
. Added support for constructor property promotion (declaring properties in
the constructor signature).
RFC: https://wiki.php.net/rfc/constructor_promotion
. Added support for `match` expression.
RFC: https://wiki.php.net/rfc/match_expression_v2
- Date:
. Added DateTime::createFromInterface() and

36
Zend/tests/match/001.phpt Normal file
View File

@ -0,0 +1,36 @@
--TEST--
Basic match expression functionality test
--FILE--
<?php
function wordify($x) {
return match ($x) {
0 => 'Zero',
1 => 'One',
2 => 'Two',
3 => 'Three',
4 => 'Four',
5 => 'Five',
6 => 'Six',
7 => 'Seven',
8 => 'Eight',
9 => 'Nine',
};
}
for ($i = 0; $i <= 9; $i++) {
print wordify($i) . "\n";
}
?>
--EXPECT--
Zero
One
Two
Three
Four
Five
Six
Seven
Eight
Nine

19
Zend/tests/match/002.phpt Normal file
View File

@ -0,0 +1,19 @@
--TEST--
Match expression omit trailing comma
--FILE--
<?php
function print_bool($bool) {
echo match ($bool) {
true => "true\n",
false => "false\n"
};
}
print_bool(true);
print_bool(false);
?>
--EXPECT--
true
false

24
Zend/tests/match/003.phpt Normal file
View File

@ -0,0 +1,24 @@
--TEST--
Match expression default case
--FILE--
<?php
function get_value($i) {
return match ($i) {
1 => 1,
2 => 2,
default => 'default',
};
}
echo get_value(0) . "\n";
echo get_value(1) . "\n";
echo get_value(2) . "\n";
echo get_value(3) . "\n";
?>
--EXPECT--
default
1
2
default

31
Zend/tests/match/004.phpt Normal file
View File

@ -0,0 +1,31 @@
--TEST--
Match expression with true as expression
--FILE--
<?php
function get_range($i) {
return match (true) {
$i >= 50 => '50+',
$i >= 40 => '40-50',
$i >= 30 => '30-40',
$i >= 20 => '20-30',
$i >= 10 => '10-20',
default => '0-10',
};
}
echo get_range(22) . "\n";
echo get_range(0) . "\n";
echo get_range(59) . "\n";
echo get_range(13) . "\n";
echo get_range(39) . "\n";
echo get_range(40) . "\n";
?>
--EXPECT--
20-30
0-10
50+
10-20
30-40
40-50

12
Zend/tests/match/005.phpt Normal file
View File

@ -0,0 +1,12 @@
--TEST--
Match expression discarding result
--FILE--
<?php
match (1) {
1 => print "Executed\n",
};
?>
--EXPECT--
Executed

13
Zend/tests/match/006.phpt Normal file
View File

@ -0,0 +1,13 @@
--TEST--
Match expression with no cases
--FILE--
<?php
$x = match (true) {};
?>
--EXPECTF--
Fatal error: Uncaught UnhandledMatchError: Unhandled match value of type bool in %s
Stack trace:
#0 {main}
thrown in %s on line %d

26
Zend/tests/match/007.phpt Normal file
View File

@ -0,0 +1,26 @@
--TEST--
Match expression exception on unhandled case
--FILE--
<?php
function get_value($i) {
return match ($i) {
1 => 1,
2 => 2,
};
}
echo get_value(1) . "\n";
echo get_value(2) . "\n";
echo get_value(3) . "\n";
?>
--EXPECTF--
1
2
Fatal error: Uncaught UnhandledMatchError: Unhandled match value of type int in %s
Stack trace:
#0 %s: get_value(3)
#1 {main}
thrown in %s on line %d

25
Zend/tests/match/008.phpt Normal file
View File

@ -0,0 +1,25 @@
--TEST--
Match expression multiple conditions per case
--FILE--
<?php
function is_working_day($day) {
return match ($day) {
1, 7 => false,
2, 3, 4, 5, 6 => true,
};
}
for ($i = 1; $i <= 7; $i++) {
var_dump(is_working_day($i));
}
?>
--EXPECT--
bool(false)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(false)

22
Zend/tests/match/009.phpt Normal file
View File

@ -0,0 +1,22 @@
--TEST--
Pretty printing for match expression
--FILE--
<?php
assert((function () {
match ('foo') {
'foo', 'bar' => false,
'baz' => 'a',
default => 'b',
};
})());
?>
--EXPECTF--
Warning: assert(): assert(function () {
match ('foo') {
'foo', 'bar' => false,
'baz' => 'a',
default => 'b',
};
}()) failed in %s on line %d

19
Zend/tests/match/011.phpt Normal file
View File

@ -0,0 +1,19 @@
--TEST--
Implicit break in match expression
--FILE--
<?php
function dump_and_return($string) {
var_dump($string);
return $string;
}
var_dump(match ('foo') {
'foo' => dump_and_return('foo'),
'bar' => dump_and_return('bar'),
});
?>
--EXPECT--
string(3) "foo"
string(3) "foo"

36
Zend/tests/match/012.phpt Normal file
View File

@ -0,0 +1,36 @@
--TEST--
Strict comparison in match expression
--FILE--
<?php
function wrong() {
throw new Exception();
}
var_dump(match (0) {
null => wrong(),
false => wrong(),
0.0 => wrong(),
[] => wrong(),
'' => wrong(),
0 => 'int',
});
function get_value() {
return 0;
}
var_dump(match (get_value()) {
null => wrong(),
false => wrong(),
0.0 => wrong(),
[] => wrong(),
'' => wrong(),
0 => 'int',
default => 'default',
});
?>
--EXPECT--
string(3) "int"
string(3) "int"

92
Zend/tests/match/017.phpt Normal file
View File

@ -0,0 +1,92 @@
--TEST--
Test strict comparison with match expression jump table
--FILE--
<?php
function wrong() {
throw new Exception();
}
function test_int($char) {
return match ($char) {
0 => wrong(),
1 => wrong(),
2 => wrong(),
3 => wrong(),
4 => wrong(),
5 => wrong(),
6 => wrong(),
7 => wrong(),
8 => wrong(),
9 => wrong(),
default => 'Not matched',
};
}
foreach (range(0, 9) as $int) {
var_dump((string) $int);
var_dump(test_int((string) $int));
}
function test_string($int) {
return match ($int) {
'0' => wrong(),
'1' => wrong(),
'2' => wrong(),
'3' => wrong(),
'4' => wrong(),
'5' => wrong(),
'6' => wrong(),
'7' => wrong(),
'8' => wrong(),
'9' => wrong(),
default => 'Not matched',
};
}
foreach (range(0, 9) as $int) {
var_dump($int);
var_dump(test_string($int));
}
--EXPECT--
string(1) "0"
string(11) "Not matched"
string(1) "1"
string(11) "Not matched"
string(1) "2"
string(11) "Not matched"
string(1) "3"
string(11) "Not matched"
string(1) "4"
string(11) "Not matched"
string(1) "5"
string(11) "Not matched"
string(1) "6"
string(11) "Not matched"
string(1) "7"
string(11) "Not matched"
string(1) "8"
string(11) "Not matched"
string(1) "9"
string(11) "Not matched"
int(0)
string(11) "Not matched"
int(1)
string(11) "Not matched"
int(2)
string(11) "Not matched"
int(3)
string(11) "Not matched"
int(4)
string(11) "Not matched"
int(5)
string(11) "Not matched"
int(6)
string(11) "Not matched"
int(7)
string(11) "Not matched"
int(8)
string(11) "Not matched"
int(9)
string(11) "Not matched"

20
Zend/tests/match/023.phpt Normal file
View File

@ -0,0 +1,20 @@
--TEST--
Test match strict comparison with true expression
--FILE--
<?php
function wrong() {
throw new Exception();
}
echo match (true) {
'truthy' => wrong(),
['truthy'] => wrong(),
new stdClass() => wrong(),
1 => wrong(),
1.0 => wrong(),
true => "true\n",
};
--EXPECT--
true

19
Zend/tests/match/024.phpt Normal file
View File

@ -0,0 +1,19 @@
--TEST--
Test match strict comparison with false expression
--FILE--
<?php
function wrong() {
throw new Exception();
}
echo match (false) {
'' => wrong(),
[] => wrong(),
0 => wrong(),
0.0 => wrong(),
false => "false\n",
};
--EXPECT--
false

36
Zend/tests/match/027.phpt Normal file
View File

@ -0,0 +1,36 @@
--TEST--
Test result of match cannot be modified by reference
--FILE--
<?php
// opcache can't be certain Test::usesRef is actually this method
if (!class_exists('Test')) {
class Test {
public static function usesRef(&$x) {
$x = 'modified';
}
public static function usesValue($x) {
echo "usesValue $x\n";
}
}
}
function main() {
$i = 0;
Test::usesValue(match(true) { true => $i });
echo "i is $i\n";
$j = 1;
Test::usesRef(match(true) { true => $j });
echo "j is $j\n";
}
main();
--EXPECTF--
usesValue 0
i is 0
Fatal error: Uncaught Error: Cannot pass parameter 1 by reference in %s027.php:20
Stack trace:
#0 %s027.php(23): main()
#1 {main}
thrown in %s027.php on line 20

36
Zend/tests/match/028.phpt Normal file
View File

@ -0,0 +1,36 @@
--TEST--
Test result of match cannot be modified by reference
--FILE--
<?php
// opcache can't be certain Test::usesRef is actually this method
if (!class_exists('Test')) {
class Test {
public static function usesRef(&$x) {
$x = 'modified';
}
public static function usesValue($x) {
echo "usesValue $x\n";
}
}
}
function main(int $i): int {
Test::usesValue(match(true) { true => $i });
Test::usesValue(match($i) { 42 => $i });
var_dump($i);
Test::usesRef(match(true) { true => $i });
var_dump($i);
}
try {
main(42);
} catch (Error $e) {
printf("Caught %s\n", $e->getMessage());
}
--EXPECT--
usesValue 42
usesValue 42
int(42)
Caught Cannot pass parameter 1 by reference

22
Zend/tests/match/029.phpt Normal file
View File

@ -0,0 +1,22 @@
--TEST--
Test long match with undefined variable
--FILE--
<?php
set_error_handler(function ($errno, $message) {
throw new Exception("Custom error handler: $message");
});
echo match ($undefVar) {
default => "This should not get printed with or without opcache\n",
1, 2, 3, 4, 5 => "Also should not be printed\n",
};
echo "unreachable\n";
--EXPECTF--
Fatal error: Uncaught Exception: Custom error handler: Undefined variable $undefVar in %s029.php:4
Stack trace:
#0 %s029.php(7): {closure}(%d, 'Undefined varia...', '%s', %d)
#1 {main}
thrown in %s029.php on line 4

22
Zend/tests/match/030.phpt Normal file
View File

@ -0,0 +1,22 @@
--TEST--
Test string match with undefined variable
--FILE--
<?php
set_error_handler(function ($errno, $message) {
throw new Exception("Custom error handler: $message");
});
echo match ($undefVar) {
default => "This should not get printed with or without opcache\n",
'1', '2', '3', '4', '5' => "Also should not be printed\n",
};
echo "unreachable\n";
--EXPECTF--
Fatal error: Uncaught Exception: Custom error handler: Undefined variable $undefVar in %s030.php:4
Stack trace:
#0 %s030.php(7): {closure}(%d, 'Undefined varia...', '%s', %d)
#1 {main}
thrown in %s030.php on line 4

68
Zend/tests/match/037.phpt Normal file
View File

@ -0,0 +1,68 @@
--TEST--
Test match jumptable with only one arm
--FILE--
<?php
try {
var_dump(match(true) {
1, 2, 3, 4, 5 => 'foo',
});
} catch (Error $e) {
var_dump((string) $e);
}
try {
var_dump(match(6) {
1, 2, 3, 4, 5 => 'foo',
});
} catch (Error $e) {
var_dump((string) $e);
}
try {
var_dump(match('3') {
1, 2, 3, 4, 5 => 'foo',
});
} catch (Error $e) {
var_dump((string) $e);
}
var_dump(match(3) {
1, 2, 3, 4, 5 => 'foo',
});
var_dump(match(true) {
1, 2, 3, 4, 5 => 'foo',
default => 'bar',
});
var_dump(match(6) {
1, 2, 3, 4, 5 => 'foo',
default => 'bar',
});
var_dump(match('3') {
1, 2, 3, 4, 5 => 'foo',
default => 'bar',
});
var_dump(match(3) {
1, 2, 3, 4, 5 => 'foo',
default => 'bar',
});
--EXPECTF--
string(%d) "UnhandledMatchError: Unhandled match value of type bool in %s037.php:5
Stack trace:
#0 {main}"
string(%d) "UnhandledMatchError: Unhandled match value of type int in %s037.php:13
Stack trace:
#0 {main}"
string(%d) "UnhandledMatchError: Unhandled match value of type string in %s037.php:21
Stack trace:
#0 {main}"
string(3) "foo"
string(3) "bar"
string(3) "bar"
string(3) "bar"
string(3) "foo"

15
Zend/tests/match/038.phpt Normal file
View File

@ -0,0 +1,15 @@
--TEST--
Test multiple default arms in match in different arms
--FILE--
<?php
match (1) {
default => 'foo',
1 => 'bar',
2 => 'baz',
default => 'qux',
};
?>
--EXPECTF--
Fatal error: Match expressions may only contain one default arm in %s on line 7

61
Zend/tests/match/039.phpt Normal file
View File

@ -0,0 +1,61 @@
--TEST--
Test match with duplicate conditions
--FILE--
<?php
$value = 1;
echo match ($value) {
1 => 1,
2 => 2,
3 => 3,
4 => 4,
5 => 5,
1 => 6,
};
echo "\n";
echo match ($value) {
2, 1 => '2, 1',
1 => 1,
3 => 3,
4 => 4,
5 => 5,
};
echo "\n";
echo match ($value) {
1, 1 => '1, 1',
2, 2 => '2, 2',
3, 3 => '3, 3',
4, 4 => '4, 4',
5, 5 => '5, 5',
};
echo "\n";
echo match ($value) {
1 => 1,
1 => 2,
};
echo "\n";
echo match ($value) {
2, 1 => '2, 1',
1 => 1,
};
echo "\n";
echo match ($value) {
1, 1 => '1, 1',
1 => 1,
};
echo "\n";
?>
--EXPECT--
1
2, 1
1, 1
1
2, 1
1, 1

44
Zend/tests/match/040.phpt Normal file
View File

@ -0,0 +1,44 @@
--TEST--
Test match with mixed int/string jumptable
--FILE--
<?php
function test($value) {
echo match ($value) {
1 => '1 int',
'1' => '1 string',
2 => '2 int',
'2' => '2 string',
3 => '3 int',
'3' => '3 string',
4 => '4 int',
'4' => '4 string',
5 => '5 int',
'5' => '5 string',
};
echo "\n";
}
test(1);
test('1');
test(2);
test('2');
test(3);
test('3');
test(4);
test('4');
test(5);
test('5');
?>
--EXPECT--
1 int
1 string
2 int
2 string
3 int
3 string
4 int
4 string
5 int
5 string

33
Zend/tests/match/041.phpt Normal file
View File

@ -0,0 +1,33 @@
--TEST--
Match expression with trailing comma in condition list
--FILE--
<?php
function print_bool($bool) {
echo match ($bool) {
false,
0,
=> "false\n",
true,
1,
=> "true\n",
default,
=> "not bool\n",
};
}
print_bool(false);
print_bool(0);
print_bool(true);
print_bool(1);
print_bool(2);
print_bool('foo');
?>
--EXPECT--
false
false
true
true
not bool
not bool

22
Zend/tests/match/042.phpt Normal file
View File

@ -0,0 +1,22 @@
--TEST--
Match expression with undefined variable as expression
--FILE--
<?php
var_dump(match ($undefinedVariable) {
null => 'null',
default => 'default',
});
var_dump(match ($undefinedVariable) {
1, 2, 3, 4, 5 => 'foo',
default => 'bar',
});
?>
--EXPECTF--
Warning: Undefined variable $undefinedVariable in %s.php on line 3
string(4) "null"
Warning: Undefined variable $undefinedVariable in %s.php on line 8
string(3) "bar"

View File

@ -1590,6 +1590,7 @@ simple_list:
break;
case ZEND_AST_SWITCH_LIST:
case ZEND_AST_CATCH_LIST:
case ZEND_AST_MATCH_ARM_LIST:
zend_ast_export_list(str, (zend_ast_list*)ast, 0, 0, indent);
break;
case ZEND_AST_CLOSURE_USES:
@ -1967,6 +1968,25 @@ simple_list:
}
zend_ast_export_stmt(str, ast->child[1], indent + 1);
break;
case ZEND_AST_MATCH:
smart_str_appends(str, "match (");
zend_ast_export_ex(str, ast->child[0], 0, indent);
smart_str_appends(str, ") {\n");
zend_ast_export_ex(str, ast->child[1], 0, indent + 1);
zend_ast_export_indent(str, indent);
smart_str_appendc(str, '}');
break;
case ZEND_AST_MATCH_ARM:
zend_ast_export_indent(str, indent);
if (ast->child[0]) {
zend_ast_export_list(str, (zend_ast_list*)ast->child[0], 1, 0, indent);
smart_str_appends(str, " => ");
} else {
smart_str_appends(str, "default => ");
}
zend_ast_export_ex(str, ast->child[1], 0, 0);
smart_str_appends(str, ",\n");
break;
case ZEND_AST_DECLARE:
smart_str_appends(str, "declare(");
ZEND_ASSERT(ast->child[0]->kind == ZEND_AST_CONST_DECL);

View File

@ -63,6 +63,7 @@ enum _zend_ast_kind {
ZEND_AST_USE,
ZEND_AST_TYPE_UNION,
ZEND_AST_ATTRIBUTE_LIST,
ZEND_AST_MATCH_ARM_LIST,
/* 0 child nodes */
ZEND_AST_MAGIC_CONST = 0 << ZEND_AST_NUM_CHILDREN_SHIFT,
@ -141,6 +142,8 @@ enum _zend_ast_kind {
ZEND_AST_GROUP_USE,
ZEND_AST_CLASS_CONST_GROUP,
ZEND_AST_ATTRIBUTE,
ZEND_AST_MATCH,
ZEND_AST_MATCH_ARM,
/* 3 child nodes */
ZEND_AST_METHOD_CALL = 3 << ZEND_AST_NUM_CHILDREN_SHIFT,

View File

@ -2113,6 +2113,7 @@ ZEND_API int zend_is_smart_branch(const zend_op *opline) /* {{{ */
case ZEND_IS_SMALLER:
case ZEND_IS_SMALLER_OR_EQUAL:
case ZEND_CASE:
case ZEND_CASE_STRICT:
case ZEND_ISSET_ISEMPTY_CV:
case ZEND_ISSET_ISEMPTY_VAR:
case ZEND_ISSET_ISEMPTY_DIM_OBJ:
@ -5166,6 +5167,227 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */
}
/* }}} */
static uint32_t count_match_conds(zend_ast_list *arms)
{
uint32_t num_conds = 0;
for (uint32_t i = 0; i < arms->children; i++) {
zend_ast *arm_ast = arms->child[i];
if (arm_ast->child[0] == NULL) {
continue;
}
zend_ast_list *conds = zend_ast_get_list(arm_ast->child[0]);
num_conds += conds->children;
}
return num_conds;
}
static zend_bool can_match_use_jumptable(zend_ast_list *arms) {
for (uint32_t i = 0; i < arms->children; i++) {
zend_ast *arm_ast = arms->child[i];
if (!arm_ast->child[0]) {
/* Skip default arm */
continue;
}
zend_ast_list *conds = zend_ast_get_list(arm_ast->child[0]);
for (uint32_t j = 0; j < conds->children; j++) {
zend_ast **cond_ast = &conds->child[j];
zend_eval_const_expr(cond_ast);
if ((*cond_ast)->kind != ZEND_AST_ZVAL) {
return 0;
}
zval *cond_zv = zend_ast_get_zval(*cond_ast);
if (Z_TYPE_P(cond_zv) != IS_LONG && Z_TYPE_P(cond_zv) != IS_STRING) {
return 0;
}
}
}
return 1;
}
void zend_compile_match(znode *result, zend_ast *ast)
{
zend_ast *expr_ast = ast->child[0];
zend_ast_list *arms = zend_ast_get_list(ast->child[1]);
zend_bool has_default_arm = 0;
uint32_t opnum_match = (uint32_t)-1;
znode expr_node;
zend_compile_expr(&expr_node, expr_ast);
znode case_node;
case_node.op_type = IS_TMP_VAR;
case_node.u.op.var = get_temporary_variable();
uint32_t num_conds = count_match_conds(arms);
zend_uchar can_use_jumptable = can_match_use_jumptable(arms);
zend_bool uses_jumptable = can_use_jumptable && num_conds >= 2;
HashTable *jumptable = NULL;
uint32_t *jmpnz_opnums = NULL;
for (uint32_t i = 0; i < arms->children; ++i) {
zend_ast *arm_ast = arms->child[i];
if (!arm_ast->child[0]) {
if (has_default_arm) {
CG(zend_lineno) = arm_ast->lineno;
zend_error_noreturn(E_COMPILE_ERROR,
"Match expressions may only contain one default arm");
}
has_default_arm = 1;
}
}
if (uses_jumptable) {
znode jumptable_op;
ALLOC_HASHTABLE(jumptable);
zend_hash_init(jumptable, num_conds, NULL, NULL, 0);
jumptable_op.op_type = IS_CONST;
ZVAL_ARR(&jumptable_op.u.constant, jumptable);
zend_op *opline = zend_emit_op(NULL, ZEND_MATCH, &expr_node, &jumptable_op);
if (opline->op1_type == IS_CONST) {
Z_TRY_ADDREF_P(CT_CONSTANT(opline->op1));
}
opnum_match = opline - CG(active_op_array)->opcodes;
} else {
jmpnz_opnums = safe_emalloc(sizeof(uint32_t), num_conds, 0);
uint32_t cond_count = 0;
for (uint32_t i = 0; i < arms->children; ++i) {
zend_ast *arm_ast = arms->child[i];
if (!arm_ast->child[0]) {
continue;
}
zend_ast_list *conds = zend_ast_get_list(arm_ast->child[0]);
for (uint32_t j = 0; j < conds->children; j++) {
zend_ast *cond_ast = conds->child[j];
znode cond_node;
zend_compile_expr(&cond_node, cond_ast);
uint32_t opcode = (expr_node.op_type & (IS_VAR|IS_TMP_VAR)) ? ZEND_CASE_STRICT : ZEND_IS_IDENTICAL;
zend_op *opline = zend_emit_op(NULL, opcode, &expr_node, &cond_node);
SET_NODE(opline->result, &case_node);
if (opline->op1_type == IS_CONST) {
Z_TRY_ADDREF_P(CT_CONSTANT(opline->op1));
}
jmpnz_opnums[cond_count] = zend_emit_cond_jump(ZEND_JMPNZ, &case_node, 0);
cond_count++;
}
}
}
uint32_t opnum_default_jmp = 0;
if (!uses_jumptable) {
opnum_default_jmp = zend_emit_jump(0);
}
zend_bool is_first_case = 1;
uint32_t cond_count = 0;
uint32_t *jmp_end_opnums = safe_emalloc(sizeof(uint32_t), arms->children, 0);
for (uint32_t i = 0; i < arms->children; ++i) {
zend_ast *arm_ast = arms->child[i];
zend_ast *body_ast = arm_ast->child[1];
if (arm_ast->child[0] != NULL) {
zend_ast_list *conds = zend_ast_get_list(arm_ast->child[0]);
for (uint32_t j = 0; j < conds->children; j++) {
zend_ast *cond_ast = conds->child[j];
if (jmpnz_opnums != NULL) {
zend_update_jump_target_to_next(jmpnz_opnums[cond_count]);
}
if (jumptable) {
zval *cond_zv = zend_ast_get_zval(cond_ast);
zval jmp_target;
ZVAL_LONG(&jmp_target, get_next_op_number());
if (Z_TYPE_P(cond_zv) == IS_LONG) {
zend_hash_index_add(jumptable, Z_LVAL_P(cond_zv), &jmp_target);
} else {
ZEND_ASSERT(Z_TYPE_P(cond_zv) == IS_STRING);
zend_hash_add(jumptable, Z_STR_P(cond_zv), &jmp_target);
}
}
cond_count++;
}
} else {
if (!uses_jumptable) {
zend_update_jump_target_to_next(opnum_default_jmp);
}
if (jumptable) {
ZEND_ASSERT(opnum_match != (uint32_t)-1);
zend_op *opline = &CG(active_op_array)->opcodes[opnum_match];
opline->extended_value = get_next_op_number();
}
}
znode body_node;
zend_compile_expr(&body_node, body_ast);
if (is_first_case) {
zend_emit_op_tmp(result, ZEND_QM_ASSIGN, &body_node, NULL);
is_first_case = 0;
} else {
zend_op *opline_qm_assign = zend_emit_op(NULL, ZEND_QM_ASSIGN, &body_node, NULL);
SET_NODE(opline_qm_assign->result, result);
}
jmp_end_opnums[i] = zend_emit_jump(0);
}
// Initialize result in case there is no arm
if (arms->children == 0) {
result->op_type = IS_CONST;
ZVAL_NULL(&result->u.constant);
}
if (!has_default_arm) {
if (!uses_jumptable) {
zend_update_jump_target_to_next(opnum_default_jmp);
}
if (jumptable) {
zend_op *opline = &CG(active_op_array)->opcodes[opnum_match];
opline->extended_value = get_next_op_number();
}
zend_emit_op(NULL, ZEND_MATCH_ERROR, &expr_node, NULL);
}
for (uint32_t i = 0; i < arms->children; ++i) {
zend_update_jump_target_to_next(jmp_end_opnums[i]);
}
if (expr_node.op_type & (IS_VAR|IS_TMP_VAR)) {
zend_op *opline = zend_emit_op(NULL, ZEND_FREE, &expr_node, NULL);
opline->extended_value = ZEND_FREE_SWITCH;
} else if (expr_node.op_type == IS_CONST) {
zval_ptr_dtor_nogc(&expr_node.u.constant);
}
if (jmpnz_opnums != NULL) {
efree(jmpnz_opnums);
}
efree(jmp_end_opnums);
}
void zend_compile_try(zend_ast *ast) /* {{{ */
{
zend_ast *try_ast = ast->child[0];
@ -9176,6 +9398,9 @@ void zend_compile_expr(znode *result, zend_ast *ast) /* {{{ */
case ZEND_AST_THROW:
zend_compile_throw(result, ast);
return;
case ZEND_AST_MATCH:
zend_compile_match(result, ast);
return;
default:
ZEND_ASSERT(0 /* not supported */);
}

View File

@ -40,6 +40,7 @@ ZEND_API zend_class_entry *zend_ce_argument_count_error;
ZEND_API zend_class_entry *zend_ce_value_error;
ZEND_API zend_class_entry *zend_ce_arithmetic_error;
ZEND_API zend_class_entry *zend_ce_division_by_zero_error;
ZEND_API zend_class_entry *zend_ce_unhandled_match_error;
/* Internal pseudo-exception that is not exposed to userland. */
static zend_class_entry zend_ce_unwind_exit;
@ -800,6 +801,10 @@ void zend_register_default_exception(void) /* {{{ */
zend_ce_division_by_zero_error->create_object = zend_default_exception_new;
INIT_CLASS_ENTRY(zend_ce_unwind_exit, "UnwindExit", NULL);
INIT_CLASS_ENTRY(ce, "UnhandledMatchError", NULL);
zend_ce_unhandled_match_error = zend_register_internal_class_ex(&ce, zend_ce_error);
zend_ce_unhandled_match_error->create_object = zend_default_exception_new;
}
/* }}} */

View File

@ -35,6 +35,7 @@ extern ZEND_API zend_class_entry *zend_ce_argument_count_error;
extern ZEND_API zend_class_entry *zend_ce_value_error;
extern ZEND_API zend_class_entry *zend_ce_arithmetic_error;
extern ZEND_API zend_class_entry *zend_ce_division_by_zero_error;
extern ZEND_API zend_class_entry *zend_ce_unhandled_match_error;
ZEND_API void zend_exception_set_previous(zend_object *exception, zend_object *add_previous);
ZEND_API void zend_exception_save(void);

View File

@ -130,6 +130,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%token <ident> T_ENDSWITCH "endswitch (T_ENDSWITCH)"
%token <ident> T_CASE "case (T_CASE)"
%token <ident> T_DEFAULT "default (T_DEFAULT)"
%token <ident> T_MATCH "match (T_MATCH)"
%token <ident> T_BREAK "break (T_BREAK)"
%token <ident> T_CONTINUE "continue (T_CONTINUE)"
%token <ident> T_GOTO "goto (T_GOTO)"
@ -261,6 +262,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%type <ast> inline_function union_type
%type <ast> attributed_statement attributed_class_statement attributed_parameter
%type <ast> attribute_decl attribute attributes
%type <ast> match match_arm_list non_empty_match_arm_list match_arm match_arm_cond_list
%type <num> returns_ref function fn is_reference is_variadic variable_modifiers
%type <num> method_modifiers non_empty_member_modifiers member_modifier optional_visibility_modifier
@ -284,7 +286,7 @@ reserved_non_modifiers:
| T_THROW | T_USE | T_INSTEADOF | T_GLOBAL | T_VAR | T_UNSET | T_ISSET | T_EMPTY | T_CONTINUE | T_GOTO
| T_FUNCTION | T_CONST | T_RETURN | T_PRINT | T_YIELD | T_LIST | T_SWITCH | T_ENDSWITCH | T_CASE | T_DEFAULT | T_BREAK
| T_ARRAY | T_CALLABLE | T_EXTENDS | T_IMPLEMENTS | T_NAMESPACE | T_TRAIT | T_INTERFACE | T_CLASS
| T_CLASS_C | T_TRAIT_C | T_FUNC_C | T_METHOD_C | T_LINE | T_FILE | T_DIR | T_NS_C | T_FN
| T_CLASS_C | T_TRAIT_C | T_FUNC_C | T_METHOD_C | T_LINE | T_FILE | T_DIR | T_NS_C | T_FN | T_MATCH
;
semi_reserved:
@ -624,6 +626,34 @@ case_separator:
;
match:
T_MATCH '(' expr ')' '{' match_arm_list '}'
{ $$ = zend_ast_create(ZEND_AST_MATCH, $3, $6); };
;
match_arm_list:
%empty { $$ = zend_ast_create_list(0, ZEND_AST_MATCH_ARM_LIST); }
| non_empty_match_arm_list possible_comma { $$ = $1; }
;
non_empty_match_arm_list:
match_arm { $$ = zend_ast_create_list(1, ZEND_AST_MATCH_ARM_LIST, $1); }
| non_empty_match_arm_list ',' match_arm { $$ = zend_ast_list_add($1, $3); }
;
match_arm:
match_arm_cond_list possible_comma T_DOUBLE_ARROW expr
{ $$ = zend_ast_create(ZEND_AST_MATCH_ARM, $1, $4); }
| T_DEFAULT possible_comma T_DOUBLE_ARROW expr
{ $$ = zend_ast_create(ZEND_AST_MATCH_ARM, NULL, $4); }
;
match_arm_cond_list:
expr { $$ = zend_ast_create_list(1, ZEND_AST_EXPR_LIST, $1); }
| match_arm_cond_list ',' expr { $$ = zend_ast_list_add($1, $3); }
;
while_statement:
statement { $$ = $1; }
| ':' inner_statement_list T_ENDWHILE ';' { $$ = $2; }
@ -1079,6 +1109,7 @@ expr:
| T_STATIC inline_function { $$ = $2; ((zend_ast_decl *) $$)->flags |= ZEND_ACC_STATIC; }
| attributes T_STATIC inline_function
{ $$ = zend_ast_with_attributes($3, $1); ((zend_ast_decl *) $$)->flags |= ZEND_ACC_STATIC; }
| match { $$ = $1; }
;

View File

@ -1512,6 +1512,10 @@ NEWLINE ("\r"|"\n"|"\r\n")
RETURN_TOKEN_WITH_IDENT(T_SWITCH);
}
<ST_IN_SCRIPTING>"match" {
RETURN_TOKEN_WITH_IDENT(T_MATCH);
}
<ST_IN_SCRIPTING>"endswitch" {
RETURN_TOKEN_WITH_IDENT(T_ENDSWITCH);
}

View File

@ -763,7 +763,9 @@ static zend_bool keeps_op1_alive(zend_op *opline) {
/* These opcodes don't consume their OP1 operand,
* it is later freed by something else. */
if (opline->opcode == ZEND_CASE
|| opline->opcode == ZEND_CASE_STRICT
|| opline->opcode == ZEND_SWITCH_LONG
|| opline->opcode == ZEND_MATCH
|| opline->opcode == ZEND_FETCH_LIST_R
|| opline->opcode == ZEND_COPY_TMP) {
return 1;
@ -1039,6 +1041,7 @@ ZEND_API int pass_two(zend_op_array *op_array)
break;
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
case ZEND_MATCH:
{
/* absolute indexes to relative offsets */
HashTable *jumptable = Z_ARRVAL_P(CT_CONSTANT(opline->op2));
@ -1108,6 +1111,7 @@ ZEND_API binary_op_type get_binary_op(int opcode)
case ZEND_CONCAT:
return (binary_op_type) concat_function;
case ZEND_IS_IDENTICAL:
case ZEND_CASE_STRICT:
return (binary_op_type) is_identical_function;
case ZEND_IS_NOT_IDENTICAL:
return (binary_op_type) is_not_identical_function;

View File

@ -457,6 +457,20 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(16, ZEND_IS_IDENTICAL, CONST|TMP|VAR|CV, CONST|T
ZEND_VM_SMART_BRANCH(result, 1);
}
ZEND_VM_HANDLER(196, ZEND_CASE_STRICT, TMP|VAR, CONST|TMP|VAR|CV)
{
USE_OPLINE
zval *op1, *op2;
zend_bool result;
SAVE_OPLINE();
op1 = GET_OP1_ZVAL_PTR_DEREF(BP_VAR_R);
op2 = GET_OP2_ZVAL_PTR_DEREF(BP_VAR_R);
result = fast_is_identical_function(op1, op2);
FREE_OP2();
ZEND_VM_SMART_BRANCH(result, 1);
}
ZEND_VM_COLD_CONSTCONST_HANDLER(17, ZEND_IS_NOT_IDENTICAL, CONST|TMP|VAR|CV, CONST|TMP|VAR|CV, SPEC(COMMUTATIVE))
{
USE_OPLINE
@ -8394,6 +8408,58 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(188, ZEND_SWITCH_STRING, CONST|TMPVARCV, CONST,
}
}
ZEND_VM_COLD_CONSTCONST_HANDLER(195, ZEND_MATCH, CONST|TMPVARCV, CONST, JMP_ADDR)
{
USE_OPLINE
zval *op, *jump_zv;
HashTable *jumptable;
op = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
jumptable = Z_ARRVAL_P(GET_OP2_ZVAL_PTR(BP_VAR_R));
ZEND_VM_C_LABEL(match_try_again):
if (Z_TYPE_P(op) == IS_LONG) {
jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(op));
} else if (Z_TYPE_P(op) == IS_STRING) {
jump_zv = zend_hash_find_ex(jumptable, Z_STR_P(op), OP1_TYPE == IS_CONST);
} else if (Z_TYPE_P(op) == IS_REFERENCE) {
op = Z_REFVAL_P(op);
ZEND_VM_C_GOTO(match_try_again);
} else {
if (UNEXPECTED((OP1_TYPE & IS_CV) && Z_TYPE_P(op) == IS_UNDEF)) {
SAVE_OPLINE();
op = ZVAL_UNDEFINED_OP1();
if (UNEXPECTED(EG(exception))) {
HANDLE_EXCEPTION();
}
ZEND_VM_C_GOTO(match_try_again);
}
ZEND_VM_C_GOTO(default_branch);
}
if (jump_zv != NULL) {
ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv));
ZEND_VM_CONTINUE();
} else {
ZEND_VM_C_LABEL(default_branch):
/* default */
ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
ZEND_VM_CONTINUE();
}
}
ZEND_VM_COLD_CONST_HANDLER(197, ZEND_MATCH_ERROR, CONST|TMPVARCV, UNUSED)
{
USE_OPLINE
zval *op;
SAVE_OPLINE();
op = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
zend_throw_exception_ex(zend_ce_unhandled_match_error, 0, "Unhandled match value of type %s", zend_zval_type_name(op));
HANDLE_EXCEPTION();
}
ZEND_VM_COLD_CONSTCONST_HANDLER(189, ZEND_IN_ARRAY, CONST|TMP|VAR|CV, CONST, NUM)
{
USE_OPLINE

View File

@ -6496,6 +6496,47 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SWITCH_STRING_SPE
}
}
static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_MATCH_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *op, *jump_zv;
HashTable *jumptable;
op = RT_CONSTANT(opline, opline->op1);
jumptable = Z_ARRVAL_P(RT_CONSTANT(opline, opline->op2));
match_try_again:
if (Z_TYPE_P(op) == IS_LONG) {
jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(op));
} else if (Z_TYPE_P(op) == IS_STRING) {
jump_zv = zend_hash_find_ex(jumptable, Z_STR_P(op), IS_CONST == IS_CONST);
} else if (Z_TYPE_P(op) == IS_REFERENCE) {
op = Z_REFVAL_P(op);
goto match_try_again;
} else {
if (UNEXPECTED((IS_CONST & IS_CV) && Z_TYPE_P(op) == IS_UNDEF)) {
SAVE_OPLINE();
op = ZVAL_UNDEFINED_OP1();
if (UNEXPECTED(EG(exception))) {
HANDLE_EXCEPTION();
}
goto match_try_again;
}
goto default_branch;
}
if (jump_zv != NULL) {
ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv));
ZEND_VM_CONTINUE();
} else {
default_branch:
/* default */
ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
ZEND_VM_CONTINUE();
}
}
static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IN_ARRAY_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -9237,6 +9278,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_UNUSED_HANDLE
ZEND_VM_RETURN();
}
static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_MATCH_ERROR_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *op;
SAVE_OPLINE();
op = RT_CONSTANT(opline, opline->op1);
zend_throw_exception_ex(zend_ce_unhandled_match_error, 0, "Unhandled match value of type %s", zend_zval_type_name(op));
HANDLE_EXCEPTION();
}
static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_COUNT_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -11350,6 +11402,47 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SWITCH_STRING_SPEC_TMPVARCV_CO
}
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_MATCH_SPEC_TMPVARCV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *op, *jump_zv;
HashTable *jumptable;
op = EX_VAR(opline->op1.var);
jumptable = Z_ARRVAL_P(RT_CONSTANT(opline, opline->op2));
match_try_again:
if (Z_TYPE_P(op) == IS_LONG) {
jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(op));
} else if (Z_TYPE_P(op) == IS_STRING) {
jump_zv = zend_hash_find_ex(jumptable, Z_STR_P(op), (IS_TMP_VAR|IS_VAR|IS_CV) == IS_CONST);
} else if (Z_TYPE_P(op) == IS_REFERENCE) {
op = Z_REFVAL_P(op);
goto match_try_again;
} else {
if (UNEXPECTED(((IS_TMP_VAR|IS_VAR|IS_CV) & IS_CV) && Z_TYPE_P(op) == IS_UNDEF)) {
SAVE_OPLINE();
op = ZVAL_UNDEFINED_OP1();
if (UNEXPECTED(EG(exception))) {
HANDLE_EXCEPTION();
}
goto match_try_again;
}
goto default_branch;
}
if (jump_zv != NULL) {
ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv));
ZEND_VM_CONTINUE();
} else {
default_branch:
/* default */
ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
ZEND_VM_CONTINUE();
}
}
static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -12666,6 +12759,17 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_LIST_R_SPEC_TMPVARCV_TMP
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_MATCH_ERROR_SPEC_TMPVARCV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *op;
SAVE_OPLINE();
op = EX_VAR(opline->op1.var);
zend_throw_exception_ex(zend_ce_unhandled_match_error, 0, "Unhandled match value of type %s", zend_zval_type_name(op));
HANDLE_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_LIST_R_SPEC_TMPVARCV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -17922,6 +18026,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_TMP_CONST_HA
ZEND_VM_SMART_BRANCH(result, 1);
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_STRICT_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *op1, *op2;
zend_bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC);
op2 = RT_CONSTANT(opline, opline->op2);
result = fast_is_identical_function(op1, op2);
ZEND_VM_SMART_BRANCH(result, 1);
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -18723,6 +18841,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_TMP_TMP_HAND
ZEND_VM_SMART_BRANCH(result, 1);
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_STRICT_SPEC_TMP_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *op1, *op2;
zend_bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC);
op2 = _get_zval_ptr_tmp(opline->op2.var EXECUTE_DATA_CC);
result = fast_is_identical_function(op1, op2);
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
ZEND_VM_SMART_BRANCH(result, 1);
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_TMP_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -18738,6 +18870,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_TMP_TMP_
ZEND_VM_SMART_BRANCH(result, 1);
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_STRICT_SPEC_TMP_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *op1, *op2;
zend_bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC);
op2 = _get_zval_ptr_var_deref(opline->op2.var EXECUTE_DATA_CC);
result = fast_is_identical_function(op1, op2);
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
ZEND_VM_SMART_BRANCH(result, 1);
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0
@ -19073,6 +19219,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_GET_TYPE_SPEC_TMP_UNUSED_HANDL
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_STRICT_SPEC_TMP_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *op1, *op2;
zend_bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_tmp(opline->op1.var EXECUTE_DATA_CC);
op2 = _get_zval_ptr_cv_deref_BP_VAR_R(opline->op2.var EXECUTE_DATA_CC);
result = fast_is_identical_function(op1, op2);
ZEND_VM_SMART_BRANCH(result, 1);
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
#if 0
@ -20943,6 +21103,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_VAR_CONST_HA
ZEND_VM_SMART_BRANCH(result, 1);
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_STRICT_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *op1, *op2;
zend_bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC);
op2 = RT_CONSTANT(opline, opline->op2);
result = fast_is_identical_function(op1, op2);
ZEND_VM_SMART_BRANCH(result, 1);
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -25349,6 +25523,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_VAR_TMP_HAND
ZEND_VM_SMART_BRANCH(result, 1);
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_STRICT_SPEC_VAR_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *op1, *op2;
zend_bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC);
op2 = _get_zval_ptr_tmp(opline->op2.var EXECUTE_DATA_CC);
result = fast_is_identical_function(op1, op2);
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
ZEND_VM_SMART_BRANCH(result, 1);
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_VAR_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -25419,6 +25607,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_IDENTICAL_SPEC_VAR_VAR_HAND
ZEND_VM_SMART_BRANCH(result, 1);
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_STRICT_SPEC_VAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *op1, *op2;
zend_bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC);
op2 = _get_zval_ptr_var_deref(opline->op2.var EXECUTE_DATA_CC);
result = fast_is_identical_function(op1, op2);
zval_ptr_dtor_nogc(EX_VAR(opline->op2.var));
ZEND_VM_SMART_BRANCH(result, 1);
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_IDENTICAL_SPEC_VAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -26657,6 +26859,20 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_GET_TYPE_SPEC_VAR_UNUSED_HANDL
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_STRICT_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *op1, *op2;
zend_bool result;
SAVE_OPLINE();
op1 = _get_zval_ptr_var_deref(opline->op1.var EXECUTE_DATA_CC);
op2 = _get_zval_ptr_cv_deref_BP_VAR_R(opline->op2.var EXECUTE_DATA_CC);
result = fast_is_identical_function(op1, op2);
ZEND_VM_SMART_BRANCH(result, 1);
}
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_OBJ_OP_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
@ -50838,6 +51054,41 @@ ZEND_API void execute_ex(zend_execute_data *ex)
(void*)&&ZEND_ARRAY_KEY_EXISTS_SPEC_CV_TMPVAR_LABEL,
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_ARRAY_KEY_EXISTS_SPEC_CV_CV_LABEL,
(void*)&&ZEND_MATCH_SPEC_CONST_CONST_LABEL,
(void*)&&ZEND_MATCH_SPEC_TMPVARCV_CONST_LABEL,
(void*)&&ZEND_MATCH_SPEC_TMPVARCV_CONST_LABEL,
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_MATCH_SPEC_TMPVARCV_CONST_LABEL,
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_CASE_STRICT_SPEC_TMP_CONST_LABEL,
(void*)&&ZEND_CASE_STRICT_SPEC_TMP_TMP_LABEL,
(void*)&&ZEND_CASE_STRICT_SPEC_TMP_VAR_LABEL,
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_CASE_STRICT_SPEC_TMP_CV_LABEL,
(void*)&&ZEND_CASE_STRICT_SPEC_VAR_CONST_LABEL,
(void*)&&ZEND_CASE_STRICT_SPEC_VAR_TMP_LABEL,
(void*)&&ZEND_CASE_STRICT_SPEC_VAR_VAR_LABEL,
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_CASE_STRICT_SPEC_VAR_CV_LABEL,
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_MATCH_ERROR_SPEC_CONST_UNUSED_LABEL,
(void*)&&ZEND_MATCH_ERROR_SPEC_TMPVARCV_UNUSED_LABEL,
(void*)&&ZEND_MATCH_ERROR_SPEC_TMPVARCV_UNUSED_LABEL,
(void*)&&ZEND_NULL_LABEL,
(void*)&&ZEND_MATCH_ERROR_SPEC_TMPVARCV_UNUSED_LABEL,
(void*)&&ZEND_RECV_NOTYPE_SPEC_LABEL,
(void*)&&ZEND_JMP_FORWARD_SPEC_LABEL,
(void*)&&ZEND_NULL_LABEL,
@ -52490,6 +52741,10 @@ zend_leave_helper_SPEC_LABEL:
VM_TRACE(ZEND_SWITCH_STRING_SPEC_CONST_CONST)
ZEND_SWITCH_STRING_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_MATCH_SPEC_CONST_CONST):
VM_TRACE(ZEND_MATCH_SPEC_CONST_CONST)
ZEND_MATCH_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_IN_ARRAY_SPEC_CONST_CONST):
VM_TRACE(ZEND_IN_ARRAY_SPEC_CONST_CONST)
ZEND_IN_ARRAY_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@ -52750,6 +53005,10 @@ zend_leave_helper_SPEC_LABEL:
VM_TRACE(ZEND_YIELD_SPEC_CONST_UNUSED)
ZEND_YIELD_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_MATCH_ERROR_SPEC_CONST_UNUSED):
VM_TRACE(ZEND_MATCH_ERROR_SPEC_CONST_UNUSED)
ZEND_MATCH_ERROR_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_COUNT_SPEC_CONST_UNUSED):
VM_TRACE(ZEND_COUNT_SPEC_CONST_UNUSED)
ZEND_COUNT_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@ -52938,6 +53197,10 @@ zend_leave_helper_SPEC_LABEL:
VM_TRACE(ZEND_SWITCH_STRING_SPEC_TMPVARCV_CONST)
ZEND_SWITCH_STRING_SPEC_TMPVARCV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_MATCH_SPEC_TMPVARCV_CONST):
VM_TRACE(ZEND_MATCH_SPEC_TMPVARCV_CONST)
ZEND_MATCH_SPEC_TMPVARCV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST):
VM_TRACE(ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST)
ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@ -53266,6 +53529,10 @@ zend_leave_helper_SPEC_LABEL:
VM_TRACE(ZEND_FETCH_LIST_R_SPEC_TMPVARCV_TMPVAR)
ZEND_FETCH_LIST_R_SPEC_TMPVARCV_TMPVAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_MATCH_ERROR_SPEC_TMPVARCV_UNUSED):
VM_TRACE(ZEND_MATCH_ERROR_SPEC_TMPVARCV_UNUSED)
ZEND_MATCH_ERROR_SPEC_TMPVARCV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_FETCH_LIST_R_SPEC_TMPVARCV_CV):
VM_TRACE(ZEND_FETCH_LIST_R_SPEC_TMPVARCV_CV)
ZEND_FETCH_LIST_R_SPEC_TMPVARCV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@ -53755,6 +54022,10 @@ zend_leave_helper_SPEC_LABEL:
VM_TRACE(ZEND_IS_IDENTICAL_SPEC_TMP_CONST)
ZEND_IS_IDENTICAL_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_CASE_STRICT_SPEC_TMP_CONST):
VM_TRACE(ZEND_CASE_STRICT_SPEC_TMP_CONST)
ZEND_CASE_STRICT_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_IS_NOT_IDENTICAL_SPEC_TMP_CONST):
VM_TRACE(ZEND_IS_NOT_IDENTICAL_SPEC_TMP_CONST)
ZEND_IS_NOT_IDENTICAL_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@ -53823,10 +54094,18 @@ zend_leave_helper_SPEC_LABEL:
VM_TRACE(ZEND_IS_IDENTICAL_SPEC_TMP_TMP)
ZEND_IS_IDENTICAL_SPEC_TMP_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_CASE_STRICT_SPEC_TMP_TMP):
VM_TRACE(ZEND_CASE_STRICT_SPEC_TMP_TMP)
ZEND_CASE_STRICT_SPEC_TMP_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_IS_NOT_IDENTICAL_SPEC_TMP_TMP):
VM_TRACE(ZEND_IS_NOT_IDENTICAL_SPEC_TMP_TMP)
ZEND_IS_NOT_IDENTICAL_SPEC_TMP_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_CASE_STRICT_SPEC_TMP_VAR):
VM_TRACE(ZEND_CASE_STRICT_SPEC_TMP_VAR)
ZEND_CASE_STRICT_SPEC_TMP_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_UNUSED):
VM_TRACE(ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_UNUSED)
ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@ -53851,6 +54130,10 @@ zend_leave_helper_SPEC_LABEL:
VM_TRACE(ZEND_GET_TYPE_SPEC_TMP_UNUSED)
ZEND_GET_TYPE_SPEC_TMP_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_CASE_STRICT_SPEC_TMP_CV):
VM_TRACE(ZEND_CASE_STRICT_SPEC_TMP_CV)
ZEND_CASE_STRICT_SPEC_TMP_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_CV):
VM_TRACE(ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_CV)
ZEND_FETCH_DIM_FUNC_ARG_SPEC_TMP_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@ -54068,6 +54351,10 @@ zend_leave_helper_SPEC_LABEL:
VM_TRACE(ZEND_IS_IDENTICAL_SPEC_VAR_CONST)
ZEND_IS_IDENTICAL_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_CASE_STRICT_SPEC_VAR_CONST):
VM_TRACE(ZEND_CASE_STRICT_SPEC_VAR_CONST)
ZEND_CASE_STRICT_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_IS_NOT_IDENTICAL_SPEC_VAR_CONST):
VM_TRACE(ZEND_IS_NOT_IDENTICAL_SPEC_VAR_CONST)
ZEND_IS_NOT_IDENTICAL_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@ -54332,6 +54619,10 @@ zend_leave_helper_SPEC_LABEL:
VM_TRACE(ZEND_IS_IDENTICAL_SPEC_VAR_TMP)
ZEND_IS_IDENTICAL_SPEC_VAR_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_CASE_STRICT_SPEC_VAR_TMP):
VM_TRACE(ZEND_CASE_STRICT_SPEC_VAR_TMP)
ZEND_CASE_STRICT_SPEC_VAR_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_IS_NOT_IDENTICAL_SPEC_VAR_TMP):
VM_TRACE(ZEND_IS_NOT_IDENTICAL_SPEC_VAR_TMP)
ZEND_IS_NOT_IDENTICAL_SPEC_VAR_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@ -54348,6 +54639,10 @@ zend_leave_helper_SPEC_LABEL:
VM_TRACE(ZEND_IS_IDENTICAL_SPEC_VAR_VAR)
ZEND_IS_IDENTICAL_SPEC_VAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_CASE_STRICT_SPEC_VAR_VAR):
VM_TRACE(ZEND_CASE_STRICT_SPEC_VAR_VAR)
ZEND_CASE_STRICT_SPEC_VAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_IS_NOT_IDENTICAL_SPEC_VAR_VAR):
VM_TRACE(ZEND_IS_NOT_IDENTICAL_SPEC_VAR_VAR)
ZEND_IS_NOT_IDENTICAL_SPEC_VAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@ -54432,6 +54727,10 @@ zend_leave_helper_SPEC_LABEL:
VM_TRACE(ZEND_GET_TYPE_SPEC_VAR_UNUSED)
ZEND_GET_TYPE_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_CASE_STRICT_SPEC_VAR_CV):
VM_TRACE(ZEND_CASE_STRICT_SPEC_VAR_CV)
ZEND_CASE_STRICT_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
HYBRID_BREAK();
HYBRID_CASE(ZEND_ASSIGN_OBJ_OP_SPEC_VAR_CV):
VM_TRACE(ZEND_ASSIGN_OBJ_OP_SPEC_VAR_CV)
ZEND_ASSIGN_OBJ_OP_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
@ -58382,6 +58681,41 @@ void zend_vm_init(void)
ZEND_ARRAY_KEY_EXISTS_SPEC_CV_TMPVAR_HANDLER,
ZEND_NULL_HANDLER,
ZEND_ARRAY_KEY_EXISTS_SPEC_CV_CV_HANDLER,
ZEND_MATCH_SPEC_CONST_CONST_HANDLER,
ZEND_MATCH_SPEC_TMPVARCV_CONST_HANDLER,
ZEND_MATCH_SPEC_TMPVARCV_CONST_HANDLER,
ZEND_NULL_HANDLER,
ZEND_MATCH_SPEC_TMPVARCV_CONST_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_CASE_STRICT_SPEC_TMP_CONST_HANDLER,
ZEND_CASE_STRICT_SPEC_TMP_TMP_HANDLER,
ZEND_CASE_STRICT_SPEC_TMP_VAR_HANDLER,
ZEND_NULL_HANDLER,
ZEND_CASE_STRICT_SPEC_TMP_CV_HANDLER,
ZEND_CASE_STRICT_SPEC_VAR_CONST_HANDLER,
ZEND_CASE_STRICT_SPEC_VAR_TMP_HANDLER,
ZEND_CASE_STRICT_SPEC_VAR_VAR_HANDLER,
ZEND_NULL_HANDLER,
ZEND_CASE_STRICT_SPEC_VAR_CV_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_MATCH_ERROR_SPEC_CONST_UNUSED_HANDLER,
ZEND_MATCH_ERROR_SPEC_TMPVARCV_UNUSED_HANDLER,
ZEND_MATCH_ERROR_SPEC_TMPVARCV_UNUSED_HANDLER,
ZEND_NULL_HANDLER,
ZEND_MATCH_ERROR_SPEC_TMPVARCV_UNUSED_HANDLER,
ZEND_RECV_NOTYPE_SPEC_HANDLER,
ZEND_JMP_FORWARD_SPEC_HANDLER,
ZEND_NULL_HANDLER,
@ -59483,7 +59817,10 @@ void zend_vm_init(void)
2284,
2285 | SPEC_RULE_OP1,
2290 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
3218
2315 | SPEC_RULE_OP1,
2320 | SPEC_RULE_OP1 | SPEC_RULE_OP2,
2345 | SPEC_RULE_OP1,
3253
};
#if (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)
zend_opcode_handler_funcs = labels;
@ -59648,7 +59985,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2317 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 2352 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
if (op->op1_type < op->op2_type) {
zend_swap_operands(op);
}
@ -59656,7 +59993,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2342 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 2377 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
if (op->op1_type < op->op2_type) {
zend_swap_operands(op);
}
@ -59664,7 +60001,7 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2367 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 2402 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
if (op->op1_type < op->op2_type) {
zend_swap_operands(op);
}
@ -59675,17 +60012,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2392 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
spec = 2427 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
} else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2417 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
spec = 2452 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2442 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
spec = 2477 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
}
break;
case ZEND_MUL:
@ -59696,17 +60033,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2467 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 2502 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2492 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 2527 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2517 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 2552 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_IDENTICAL:
@ -59717,14 +60054,14 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2542 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 2577 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2617 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 2652 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op->op1_type == IS_CV && (op->op2_type & (IS_CONST|IS_CV)) && !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info & (MAY_BE_UNDEF|MAY_BE_REF))) {
spec = 2842 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 2877 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_NOT_IDENTICAL:
@ -59735,14 +60072,14 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2692 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 2727 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2767 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 2802 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op->op1_type == IS_CV && (op->op2_type & (IS_CONST|IS_CV)) && !(op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) && !(op2_info & (MAY_BE_UNDEF|MAY_BE_REF))) {
spec = 2847 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
spec = 2882 | SPEC_RULE_OP2 | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_EQUAL:
@ -59753,12 +60090,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2542 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 2577 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2617 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 2652 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_NOT_EQUAL:
@ -59769,12 +60106,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2692 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 2727 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2767 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
spec = 2802 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH | SPEC_RULE_COMMUTATIVE;
}
break;
case ZEND_IS_SMALLER:
@ -59782,12 +60119,12 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2852 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
spec = 2887 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 2927 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
spec = 2962 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
}
break;
case ZEND_IS_SMALLER_OR_EQUAL:
@ -59795,74 +60132,74 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3002 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
spec = 3037 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
} else if (op1_info == MAY_BE_DOUBLE && op2_info == MAY_BE_DOUBLE) {
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3077 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
spec = 3112 | SPEC_RULE_OP1 | SPEC_RULE_OP2 | SPEC_RULE_SMART_BRANCH;
}
break;
case ZEND_QM_ASSIGN:
if (op1_info == MAY_BE_LONG) {
spec = 3164 | SPEC_RULE_OP1;
spec = 3199 | SPEC_RULE_OP1;
} else if (op1_info == MAY_BE_DOUBLE) {
spec = 3169 | SPEC_RULE_OP1;
spec = 3204 | SPEC_RULE_OP1;
} else if ((op->op1_type == IS_CONST) ? !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1)) : (!(op1_info & ((MAY_BE_ANY|MAY_BE_UNDEF)-(MAY_BE_NULL|MAY_BE_FALSE|MAY_BE_TRUE|MAY_BE_LONG|MAY_BE_DOUBLE))))) {
spec = 3174 | SPEC_RULE_OP1;
spec = 3209 | SPEC_RULE_OP1;
}
break;
case ZEND_PRE_INC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
spec = 3152 | SPEC_RULE_RETVAL;
spec = 3187 | SPEC_RULE_RETVAL;
} else if (op1_info == MAY_BE_LONG) {
spec = 3154 | SPEC_RULE_RETVAL;
spec = 3189 | SPEC_RULE_RETVAL;
}
break;
case ZEND_PRE_DEC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
spec = 3156 | SPEC_RULE_RETVAL;
spec = 3191 | SPEC_RULE_RETVAL;
} else if (op1_info == MAY_BE_LONG) {
spec = 3158 | SPEC_RULE_RETVAL;
spec = 3193 | SPEC_RULE_RETVAL;
}
break;
case ZEND_POST_INC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
spec = 3160;
spec = 3195;
} else if (op1_info == MAY_BE_LONG) {
spec = 3161;
spec = 3196;
}
break;
case ZEND_POST_DEC:
if (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG) {
spec = 3162;
spec = 3197;
} else if (op1_info == MAY_BE_LONG) {
spec = 3163;
spec = 3198;
}
break;
case ZEND_JMP:
if (OP_JMP_ADDR(op, op->op1) > op) {
spec = 2316;
spec = 2351;
}
break;
case ZEND_RECV:
if (op->op2.num == MAY_BE_ANY) {
spec = 2315;
spec = 2350;
}
break;
case ZEND_SEND_VAL:
if (op->op1_type == IS_CONST && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) {
spec = 3214;
spec = 3249;
}
break;
case ZEND_SEND_VAR_EX:
if (op->op2.num <= MAX_ARG_FLAG_NUM && (op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) {
spec = 3209 | SPEC_RULE_OP1;
spec = 3244 | SPEC_RULE_OP1;
}
break;
case ZEND_FE_FETCH_R:
if (op->op2_type == IS_CV && (op1_info & (MAY_BE_UNDEF|MAY_BE_ANY|MAY_BE_REF)) == MAY_BE_ARRAY) {
spec = 3216 | SPEC_RULE_RETVAL;
spec = 3251 | SPEC_RULE_RETVAL;
}
break;
case ZEND_FETCH_DIM_R:
@ -59870,17 +60207,17 @@ ZEND_API void ZEND_FASTCALL zend_vm_set_opcode_handler_ex(zend_op* op, uint32_t
if (op->op1_type == IS_CONST && op->op2_type == IS_CONST) {
break;
}
spec = 3179 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
spec = 3214 | SPEC_RULE_OP1 | SPEC_RULE_OP2;
}
break;
case ZEND_SEND_VAL_EX:
if (op->op2.num <= MAX_ARG_FLAG_NUM && op->op1_type == IS_CONST && !Z_REFCOUNTED_P(RT_CONSTANT(op, op->op1))) {
spec = 3215;
spec = 3250;
}
break;
case ZEND_SEND_VAR:
if ((op1_info & (MAY_BE_UNDEF|MAY_BE_REF)) == 0) {
spec = 3204 | SPEC_RULE_OP1;
spec = 3239 | SPEC_RULE_OP1;
}
break;
case ZEND_BW_OR:

File diff suppressed because it is too large Load Diff

View File

@ -22,7 +22,7 @@
#include <zend.h>
#include <zend_vm_opcodes.h>
static const char *zend_vm_opcodes_names[195] = {
static const char *zend_vm_opcodes_names[198] = {
"ZEND_NOP",
"ZEND_ADD",
"ZEND_SUB",
@ -218,9 +218,12 @@ static const char *zend_vm_opcodes_names[195] = {
"ZEND_GET_CALLED_CLASS",
"ZEND_GET_TYPE",
"ZEND_ARRAY_KEY_EXISTS",
"ZEND_MATCH",
"ZEND_CASE_STRICT",
"ZEND_MATCH_ERROR",
};
static uint32_t zend_vm_opcodes_flags[195] = {
static uint32_t zend_vm_opcodes_flags[198] = {
0x00000000,
0x00000b0b,
0x00000b0b,
@ -416,6 +419,9 @@ static uint32_t zend_vm_opcodes_flags[195] = {
0x00000101,
0x00000103,
0x00000707,
0x0300030b,
0x00000301,
0x0000010b,
};
ZEND_API const char* ZEND_FASTCALL zend_get_opcode_name(zend_uchar opcode) {

View File

@ -271,7 +271,10 @@ END_EXTERN_C()
#define ZEND_GET_CALLED_CLASS 192
#define ZEND_GET_TYPE 193
#define ZEND_ARRAY_KEY_EXISTS 194
#define ZEND_MATCH 195
#define ZEND_CASE_STRICT 196
#define ZEND_MATCH_ERROR 197
#define ZEND_VM_LAST_OPCODE 194
#define ZEND_VM_LAST_OPCODE 197
#endif

View File

@ -115,6 +115,10 @@ static int get_const_switch_target(zend_cfg *cfg, zend_op_array *op_array, zend_
/* fallback to next block */
return block->successors[block->successors_count - 1];
}
if (opline->opcode == ZEND_MATCH && Z_TYPE_P(val) != IS_LONG && Z_TYPE_P(val) != IS_STRING) {
/* always jump to the default arm */
return block->successors[block->successors_count - 1];
}
if (Z_TYPE_P(val) == IS_LONG) {
zv = zend_hash_index_find(jumptable, Z_LVAL_P(val));
} else {
@ -123,7 +127,7 @@ static int get_const_switch_target(zend_cfg *cfg, zend_op_array *op_array, zend_
}
if (!zv) {
/* default */
return block->successors[block->successors_count - 2];
return block->successors[block->successors_count - (opline->opcode == ZEND_MATCH ? 1 : 2)];
}
return cfg->map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))];
}
@ -369,6 +373,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
case ZEND_MATCH:
if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
/* SWITCH variable will be deleted later by FREE, so we can't optimize it */
Tsource[VAR_NUM(opline->op1.var)] = NULL;
@ -387,6 +392,7 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
break;
case ZEND_CASE:
case ZEND_CASE_STRICT:
case ZEND_COPY_TMP:
if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
/* Variable will be deleted later by FREE, so we can't optimize it */
@ -1046,11 +1052,12 @@ static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array, zend_op
break;
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
case ZEND_MATCH:
{
HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
zval *zv;
uint32_t s = 0;
ZEND_ASSERT(b->successors_count == 2 + zend_hash_num_elements(jumptable));
ZEND_ASSERT(b->successors_count == (opline->opcode == ZEND_MATCH ? 1 : 2) + zend_hash_num_elements(jumptable));
ZEND_HASH_FOREACH_VAL(jumptable, zv) {
Z_LVAL_P(zv) = ZEND_OPLINE_TO_OFFSET(opline, new_opcodes + blocks[b->successors[s++]].start);

View File

@ -106,6 +106,7 @@ static inline zend_bool may_have_side_effects(
case ZEND_IS_SMALLER:
case ZEND_IS_SMALLER_OR_EQUAL:
case ZEND_CASE:
case ZEND_CASE_STRICT:
case ZEND_CAST:
case ZEND_ROPE_INIT:
case ZEND_ROPE_ADD:
@ -146,6 +147,7 @@ static inline zend_bool may_have_side_effects(
case ZEND_ECHO:
case ZEND_INCLUDE_OR_EVAL:
case ZEND_THROW:
case ZEND_MATCH_ERROR:
case ZEND_EXT_STMT:
case ZEND_EXT_FCALL_BEGIN:
case ZEND_EXT_FCALL_END:
@ -391,7 +393,8 @@ static zend_bool dce_instr(context *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))&& !is_var_dead(ctx, ssa_op->op1_use)) {
if (!try_remove_var_def(ctx, ssa_op->op1_use, ssa_op->op1_use_chain, opline)) {
if (ssa->var_info[ssa_op->op1_use].type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)
&& opline->opcode != ZEND_CASE) {
&& opline->opcode != ZEND_CASE
&& opline->opcode != ZEND_CASE_STRICT) {
free_var = ssa_op->op1_use;
free_var_type = opline->op1_type;
}

View File

@ -641,6 +641,7 @@ static void zend_ssa_replace_control_link(zend_op_array *op_array, zend_ssa *ssa
break;
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
case ZEND_MATCH:
{
HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
zval *zv;
@ -895,48 +896,29 @@ optimize_jmpnz:
break;
}
case ZEND_SWITCH_LONG:
if (opline->op1_type == IS_CONST) {
zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
if (Z_TYPE_P(zv) != IS_LONG) {
removed_ops++;
MAKE_NOP(opline);
opline->extended_value = 0;
take_successor_ex(ssa, block_num, block, block->successors[0]);
goto optimize_nop;
} else {
HashTable *jmptable = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant));
zval *jmp_zv = zend_hash_index_find(jmptable, Z_LVAL_P(zv));
uint32_t target;
if (jmp_zv) {
target = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(jmp_zv));
} else {
target = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value);
}
opline->opcode = ZEND_JMP;
opline->extended_value = 0;
SET_UNUSED(opline->op1);
ZEND_SET_OP_JMP_ADDR(opline, opline->op1, op_array->opcodes + target);
SET_UNUSED(opline->op2);
take_successor_ex(ssa, block_num, block, ssa->cfg.map[target]);
goto optimize_jmp;
}
}
break;
case ZEND_SWITCH_STRING:
case ZEND_MATCH:
if (opline->op1_type == IS_CONST) {
zval *zv = CT_CONSTANT_EX(op_array, opline->op1.constant);
if (Z_TYPE_P(zv) != IS_STRING) {
zend_uchar type = Z_TYPE_P(zv);
zend_bool correct_type =
(opline->opcode == ZEND_SWITCH_LONG && type == IS_LONG)
|| (opline->opcode == ZEND_SWITCH_STRING && type == IS_STRING)
|| (opline->opcode == ZEND_MATCH && (type == IS_LONG || type == IS_STRING));
if (!correct_type) {
removed_ops++;
MAKE_NOP(opline);
opline->extended_value = 0;
take_successor_ex(ssa, block_num, block, block->successors[0]);
take_successor_ex(ssa, block_num, block, block->successors[block->successors_count - 1]);
goto optimize_nop;
} else {
HashTable *jmptable = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant));
zval *jmp_zv = zend_hash_find(jmptable, Z_STR_P(zv));
uint32_t target;
zval *jmp_zv = type == IS_LONG
? zend_hash_index_find(jmptable, Z_LVAL_P(zv))
: zend_hash_find(jmptable, Z_STR_P(zv));
uint32_t target;
if (jmp_zv) {
target = ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(jmp_zv));
} else {

View File

@ -82,6 +82,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
case ZEND_BOOL_XOR:
case ZEND_SPACESHIP:
case ZEND_CASE:
case ZEND_CASE_STRICT:
if (opline->op1_type == IS_CONST &&
opline->op2_type == IS_CONST) {
/* binary operation with constant operands */
@ -663,6 +664,7 @@ constant_binary_op:
case ZEND_GENERATOR_RETURN:
case ZEND_EXIT:
case ZEND_THROW:
case ZEND_MATCH_ERROR:
case ZEND_CATCH:
case ZEND_FAST_CALL:
case ZEND_FAST_RET:

View File

@ -300,10 +300,15 @@ static zend_bool try_replace_op1(
switch (opline->opcode) {
case ZEND_CASE:
opline->opcode = ZEND_IS_EQUAL;
/* break missing intentionally */
goto replace_op1_simple;
case ZEND_CASE_STRICT:
opline->opcode = ZEND_IS_IDENTICAL;
goto replace_op1_simple;
case ZEND_FETCH_LIST_R:
case ZEND_SWITCH_STRING:
case ZEND_SWITCH_LONG:
case ZEND_MATCH:
replace_op1_simple:
if (Z_TYPE(zv) == IS_STRING) {
zend_string_hash_val(Z_STR(zv));
}
@ -1460,6 +1465,7 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o
case ZEND_BW_XOR:
case ZEND_BOOL_XOR:
case ZEND_CASE:
case ZEND_CASE_STRICT:
SKIP_IF_TOP(op1);
SKIP_IF_TOP(op2);
@ -1986,29 +1992,23 @@ static void sccp_mark_feasible_successors(
s = zend_hash_num_elements(Z_ARR_P(op1)) != 0;
break;
case ZEND_SWITCH_LONG:
if (Z_TYPE_P(op1) == IS_LONG) {
zend_op_array *op_array = scdf->op_array;
zend_ssa *ssa = scdf->ssa;
HashTable *jmptable = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant));
zval *jmp_zv = zend_hash_index_find(jmptable, Z_LVAL_P(op1));
int target;
if (jmp_zv) {
target = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(jmp_zv))];
} else {
target = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
}
scdf_mark_edge_feasible(scdf, block_num, target);
return;
}
s = 0;
break;
case ZEND_SWITCH_STRING:
if (Z_TYPE_P(op1) == IS_STRING) {
case ZEND_MATCH:
{
zend_bool strict_comparison = opline->opcode == ZEND_MATCH;
zend_uchar type = Z_TYPE_P(op1);
zend_bool correct_type =
(opline->opcode == ZEND_SWITCH_LONG && type == IS_LONG)
|| (opline->opcode == ZEND_SWITCH_STRING && type == IS_STRING)
|| (opline->opcode == ZEND_MATCH && (type == IS_LONG || type == IS_STRING));
if (correct_type) {
zend_op_array *op_array = scdf->op_array;
zend_ssa *ssa = scdf->ssa;
HashTable *jmptable = Z_ARRVAL_P(CT_CONSTANT_EX(op_array, opline->op2.constant));
zval *jmp_zv = zend_hash_find(jmptable, Z_STR_P(op1));
zval *jmp_zv = type == IS_LONG
? zend_hash_index_find(jmptable, Z_LVAL_P(op1))
: zend_hash_find(jmptable, Z_STR_P(op1));
int target;
if (jmp_zv) {
@ -2018,9 +2018,16 @@ static void sccp_mark_feasible_successors(
}
scdf_mark_edge_feasible(scdf, block_num, target);
return;
} else if (strict_comparison) {
zend_op_array *op_array = scdf->op_array;
zend_ssa *ssa = scdf->ssa;
int target = ssa->cfg.map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
scdf_mark_edge_feasible(scdf, block_num, target);
return;
}
s = 0;
break;
}
default:
for (s = 0; s < block->successors_count; s++) {
scdf_mark_edge_feasible(scdf, block_num, block->successors[s]);

View File

@ -73,7 +73,11 @@ static void zend_mark_reachable(zend_op *opcodes, zend_cfg *cfg, zend_basic_bloc
succ->flags |= ZEND_BB_FOLLOW;
}
} else {
ZEND_ASSERT(opcode == ZEND_SWITCH_LONG || opcode == ZEND_SWITCH_STRING);
ZEND_ASSERT(
opcode == ZEND_SWITCH_LONG
|| opcode == ZEND_SWITCH_STRING
|| opcode == ZEND_MATCH
);
if (i == b->successors_count - 1) {
succ->flags |= ZEND_BB_FOLLOW | ZEND_BB_TARGET;
} else {
@ -296,6 +300,7 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b
case ZEND_RETURN_BY_REF:
case ZEND_GENERATOR_RETURN:
case ZEND_EXIT:
case ZEND_MATCH_ERROR:
if (i + 1 < op_array->last) {
BB_START(i + 1);
}
@ -391,6 +396,7 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b
break;
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
case ZEND_MATCH:
{
HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2));
zval *zv;
@ -507,6 +513,7 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b
case ZEND_GENERATOR_RETURN:
case ZEND_EXIT:
case ZEND_THROW:
case ZEND_MATCH_ERROR:
break;
case ZEND_JMP:
block->successors_count = 1;
@ -557,12 +564,13 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b
break;
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
case ZEND_MATCH:
{
HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2));
zval *zv;
uint32_t s = 0;
block->successors_count = 2 + zend_hash_num_elements(jumptable);
block->successors_count = (opline->opcode == ZEND_MATCH ? 1 : 2) + zend_hash_num_elements(jumptable);
block->successors = zend_arena_calloc(arena, block->successors_count, sizeof(int));
ZEND_HASH_FOREACH_VAL(jumptable, zv) {
@ -570,7 +578,9 @@ int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t b
} ZEND_HASH_FOREACH_END();
block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
block->successors[s++] = j + 1;
if (opline->opcode != ZEND_MATCH) {
block->successors[s++] = j + 1;
}
break;
}
default:

View File

@ -615,7 +615,11 @@ void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *b, cons
if (opline->op2_type == IS_CONST) {
zval *op = CRT_CONSTANT(opline->op2);
if (opline->opcode == ZEND_SWITCH_LONG || opline->opcode == ZEND_SWITCH_STRING) {
if (
opline->opcode == ZEND_SWITCH_LONG
|| opline->opcode == ZEND_SWITCH_STRING
|| opline->opcode == ZEND_MATCH
) {
HashTable *jumptable = Z_ARRVAL_P(op);
zend_string *key;
zend_ulong num_key;

View File

@ -2413,6 +2413,7 @@ static zend_always_inline int _zend_update_type_info(
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_CASE:
case ZEND_CASE_STRICT:
case ZEND_BOOL:
case ZEND_ISSET_ISEMPTY_CV:
case ZEND_ISSET_ISEMPTY_VAR:
@ -4312,6 +4313,7 @@ int zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op, const ze
if (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY)) {
switch (opline->opcode) {
case ZEND_CASE:
case ZEND_CASE_STRICT:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
case ZEND_FETCH_LIST_R:
@ -4380,12 +4382,14 @@ int zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op, const ze
case ZEND_COALESCE:
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
case ZEND_MATCH:
case ZEND_ISSET_ISEMPTY_VAR:
case ZEND_ISSET_ISEMPTY_CV:
case ZEND_FUNC_NUM_ARGS:
case ZEND_FUNC_GET_ARGS:
case ZEND_COPY_TMP:
case ZEND_CHECK_FUNC_ARG:
case ZEND_CASE_STRICT:
return 0;
case ZEND_INIT_FCALL:
/* can't throw, because call is resolved at compile time */

View File

@ -293,6 +293,7 @@ int zend_optimizer_update_op1_const(zend_op_array *op_array,
* zend_optimizer_replace_by_const() supports this. */
return 0;
case ZEND_CASE:
case ZEND_CASE_STRICT:
case ZEND_FETCH_LIST_R:
case ZEND_COPY_TMP:
case ZEND_FETCH_CLASS_NAME:
@ -558,7 +559,7 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
break;
/* In most cases IS_TMP_VAR operand may be used only once.
* The operands are usually destroyed by the opcode handler.
* ZEND_CASE and ZEND_FETCH_LIST_R are exceptions, they keeps operand
* ZEND_CASE[_STRICT] and ZEND_FETCH_LIST_R are exceptions, they keeps operand
* unchanged, and allows its reuse. these instructions
* usually terminated by ZEND_FREE that finally kills the value.
*/
@ -587,17 +588,25 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array,
}
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
case ZEND_CASE: {
case ZEND_MATCH:
case ZEND_CASE:
case ZEND_CASE_STRICT: {
zend_op *end = op_array->opcodes + op_array->last;
while (opline < end) {
if (opline->op1_type == type && opline->op1.var == var) {
if (opline->opcode == ZEND_CASE
|| opline->opcode == ZEND_SWITCH_LONG
|| opline->opcode == ZEND_SWITCH_STRING) {
if (
opline->opcode == ZEND_CASE
|| opline->opcode == ZEND_CASE_STRICT
|| opline->opcode == ZEND_SWITCH_LONG
|| opline->opcode == ZEND_SWITCH_STRING
|| opline->opcode == ZEND_MATCH
) {
zval v;
if (opline->opcode == ZEND_CASE) {
opline->opcode = ZEND_IS_EQUAL;
} else if (opline->opcode == ZEND_CASE_STRICT) {
opline->opcode = ZEND_IS_IDENTICAL;
}
ZVAL_COPY(&v, val);
if (Z_TYPE(v) == IS_STRING) {
@ -687,6 +696,7 @@ void zend_optimizer_migrate_jump(zend_op_array *op_array, zend_op *new_opline, z
break;
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
case ZEND_MATCH:
{
HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
zval *zv;
@ -731,6 +741,7 @@ void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_
break;
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
case ZEND_MATCH:
{
HashTable *jumptable = Z_ARRVAL(ZEND_OP2_LITERAL(opline));
zval *zv;
@ -1105,6 +1116,7 @@ static void zend_redo_pass_two(zend_op_array *op_array)
case ZEND_FE_FETCH_RW:
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
case ZEND_MATCH:
/* relative extended_value don't have to be changed */
break;
#endif
@ -1115,6 +1127,7 @@ static void zend_redo_pass_two(zend_op_array *op_array)
case ZEND_IS_SMALLER:
case ZEND_IS_SMALLER_OR_EQUAL:
case ZEND_CASE:
case ZEND_CASE_STRICT:
case ZEND_ISSET_ISEMPTY_CV:
case ZEND_ISSET_ISEMPTY_VAR:
case ZEND_ISSET_ISEMPTY_DIM_OBJ:
@ -1225,6 +1238,7 @@ static void zend_redo_pass_two_ex(zend_op_array *op_array, zend_ssa *ssa)
case ZEND_FE_FETCH_RW:
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
case ZEND_MATCH:
/* relative extended_value don't have to be changed */
break;
#endif
@ -1235,6 +1249,7 @@ static void zend_redo_pass_two_ex(zend_op_array *op_array, zend_ssa *ssa)
case ZEND_IS_SMALLER:
case ZEND_IS_SMALLER_OR_EQUAL:
case ZEND_CASE:
case ZEND_CASE_STRICT:
case ZEND_ISSET_ISEMPTY_CV:
case ZEND_ISSET_ISEMPTY_VAR:
case ZEND_ISSET_ISEMPTY_DIM_OBJ:

View File

@ -2120,7 +2120,8 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
&& ssa->cfg.blocks[b].start != 0
&& (op_array->opcodes[ssa->cfg.blocks[b].start - 1].opcode == ZEND_NOP
|| op_array->opcodes[ssa->cfg.blocks[b].start - 1].opcode == ZEND_SWITCH_LONG
|| op_array->opcodes[ssa->cfg.blocks[b].start - 1].opcode == ZEND_SWITCH_STRING)) {
|| op_array->opcodes[ssa->cfg.blocks[b].start - 1].opcode == ZEND_SWITCH_STRING
|| op_array->opcodes[ssa->cfg.blocks[b].start - 1].opcode == ZEND_MATCH)) {
if (!zend_jit_reset_opline(&dasm_state, op_array->opcodes + ssa->cfg.blocks[b].start)
|| !zend_jit_set_valid_ip(&dasm_state, op_array->opcodes + ssa->cfg.blocks[b].start)) {
goto jit_failure;
@ -2851,6 +2852,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
goto done;
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
case ZEND_MATCH:
if (!zend_jit_switch(&dasm_state, opline, op_array, ssa)) {
goto jit_failure;
}
@ -2898,6 +2900,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
case ZEND_OP_DATA:
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
case ZEND_MATCH:
break;
case ZEND_JMP:
if (JIT_G(opt_level) < ZEND_JIT_LEVEL_INLINE) {
@ -2920,6 +2923,7 @@ static int zend_jit(const zend_op_array *op_array, zend_ssa *ssa, const zend_op
case ZEND_RETURN_BY_REF:
case ZEND_RETURN:
case ZEND_EXIT:
case ZEND_MATCH_ERROR:
/* switch through trampoline */
case ZEND_YIELD:
case ZEND_YIELD_FROM:

View File

@ -279,6 +279,7 @@ static int zend_jit_trace_may_exit(const zend_op_array *op_array, const zend_op
case ZEND_FE_FETCH_RW:
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
case ZEND_MATCH:
/* branch opcodes */
return 1;
case ZEND_NEW:
@ -296,6 +297,7 @@ static int zend_jit_trace_may_exit(const zend_op_array *op_array, const zend_op
case ZEND_YIELD:
case ZEND_YIELD_FROM:
case ZEND_INCLUDE_OR_EVAL:
case ZEND_MATCH_ERROR:
/* unsupported */
return 1;
case ZEND_DO_FCALL:
@ -3943,6 +3945,7 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par
#if 0
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
case ZEND_MATCH:
if (!zend_jit_switch(&dasm_state, opline, op_array, op_array_ssa)) {
goto jit_failure;
}

View File

@ -11377,6 +11377,13 @@ static int zend_jit_switch(dasm_State **Dst, const zend_op *opline, const zend_o
{
HashTable *jumptable = Z_ARRVAL_P(RT_CONSTANT(opline, opline->op2));
// TODO: Implement for match instructions
if (opline->opcode == ZEND_MATCH) {
// Since the match expression doesn't have a IS_IDENTICAL/JMPNZ chain
// we can't skip the jumptable and thus can't JIT the function
return 0;
}
if (opline->op1_type == IS_CONST) {
zval *zv = RT_CONSTANT(opline, opline->op1);
zval *jump_zv;

View File

@ -0,0 +1,71 @@
--TEST--
Match expression string jump table
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.opt_debug_level=0x20000
--SKIPIF--
<?php require_once(__DIR__ . '/../skipif.inc'); ?>
--FILE--
<?php
function test($char) {
return match ($char) {
'a' => 'a',
'b', 'c' => 'b, c',
'd' => 'd',
'e', 'f' => 'e, f',
'g' => 'g',
'h', 'i' => 'h, i',
};
}
foreach (range('a', 'i') as $char) {
var_dump(test($char));
}
--EXPECTF--
$_main:
; (lines=15, args=0, vars=1, tmps=2)
; (after optimizer)
; %s
0000 INIT_FCALL 2 %d string("range")
0001 SEND_VAL string("a") 1
0002 SEND_VAL string("i") 2
0003 V2 = DO_ICALL
0004 V1 = FE_RESET_R V2 0013
0005 FE_FETCH_R V1 CV0($char) 0013
0006 INIT_FCALL 1 %d string("var_dump")
0007 INIT_FCALL 1 %d string("test")
0008 SEND_VAR CV0($char) 1
0009 V2 = DO_UCALL
0010 SEND_VAR V2 1
0011 DO_ICALL
0012 JMP 0005
0013 FE_FREE V1
0014 RETURN int(1)
LIVE RANGES:
1: 0005 - 0013 (loop)
test:
; (lines=9, args=1, vars=1, tmps=0)
; (after optimizer)
; %s
0000 CV0($char) = RECV 1
0001 MATCH CV0($char) "a": 0002, "b": 0003, "c": 0003, "d": 0004, "e": 0005, "f": 0005, "g": 0006, "h": 0007, "i": 0007, default: 0008
0002 RETURN string("a")
0003 RETURN string("b, c")
0004 RETURN string("d")
0005 RETURN string("e, f")
0006 RETURN string("g")
0007 RETURN string("h, i")
0008 MATCH_ERROR CV0($char)
string(1) "a"
string(4) "b, c"
string(4) "b, c"
string(1) "d"
string(4) "e, f"
string(4) "e, f"
string(1) "g"
string(4) "h, i"
string(4) "h, i"

View File

@ -0,0 +1,57 @@
--TEST--
Test match jump table optimizer
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.opt_debug_level=0x20000
--SKIPIF--
<?php require_once(__DIR__ . '/../skipif.inc'); ?>
--FILE--
<?php
function test() {
$x = '2';
echo match($x) {
1, 2, 3, 4, 5 => throw new RuntimeException(),
default => "No match\n",
};
}
test();
function test2() {
$x = 2;
echo match($x) {
'1', '2', '3', '4', '5' => throw new RuntimeException(),
default => "No match\n",
};
}
test2();
--EXPECTF--
$_main:
; (lines=5, args=0, vars=0, tmps=0)
; (after optimizer)
; %s
0000 INIT_FCALL 0 %d string("test")
0001 DO_UCALL
0002 INIT_FCALL 0 %d string("test2")
0003 DO_UCALL
0004 RETURN int(1)
test:
; (lines=2, args=0, vars=0, tmps=0)
; (after optimizer)
; %s
0000 ECHO string("No match
")
0001 RETURN null
test2:
; (lines=2, args=0, vars=0, tmps=0)
; (after optimizer)
; %s
0000 ECHO string("No match
")
0001 RETURN null
No match
No match

View File

@ -0,0 +1,74 @@
--TEST--
Match expression long jump table
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.opt_debug_level=0x20000
--SKIPIF--
<?php require_once(__DIR__ . '/../skipif.inc'); ?>
--FILE--
<?php
function test($char) {
return match ($char) {
1 => '1',
2, 3 => '2, 3',
4 => '4',
5, 6 => '5, 6',
7 => '7',
8, 9 => '8, 9',
default => 'default'
};
}
foreach (range(0, 10) as $char) {
var_dump(test($char));
}
--EXPECTF--
$_main:
; (lines=15, args=0, vars=1, tmps=2)
; (after optimizer)
; %s
0000 INIT_FCALL 2 %d string("range")
0001 SEND_VAL int(0) 1
0002 SEND_VAL int(10) 2
0003 V2 = DO_ICALL
0004 V1 = FE_RESET_R V2 0013
0005 FE_FETCH_R V1 CV0($char) 0013
0006 INIT_FCALL 1 %d string("var_dump")
0007 INIT_FCALL 1 %d string("test")
0008 SEND_VAR CV0($char) 1
0009 V2 = DO_UCALL
0010 SEND_VAR V2 1
0011 DO_ICALL
0012 JMP 0005
0013 FE_FREE V1
0014 RETURN int(1)
LIVE RANGES:
1: 0005 - 0013 (loop)
test:
; (lines=9, args=1, vars=1, tmps=0)
; (after optimizer)
; %s
0000 CV0($char) = RECV 1
0001 MATCH CV0($char) 1: 0002, 2: 0003, 3: 0003, 4: 0004, 5: 0005, 6: 0005, 7: 0006, 8: 0007, 9: 0007, default: 0008
0002 RETURN string("1")
0003 RETURN string("2, 3")
0004 RETURN string("4")
0005 RETURN string("5, 6")
0006 RETURN string("7")
0007 RETURN string("8, 9")
0008 RETURN string("default")
string(7) "default"
string(1) "1"
string(4) "2, 3"
string(4) "2, 3"
string(1) "4"
string(4) "5, 6"
string(4) "5, 6"
string(1) "7"
string(4) "8, 9"
string(4) "8, 9"
string(7) "default"

View File

@ -0,0 +1,93 @@
--TEST--
Match expression mixed jump table
--INI--
opcache.enable=1
opcache.enable_cli=1
opcache.opt_debug_level=0x20000
--SKIPIF--
<?php require_once(__DIR__ . '/../skipif.inc'); ?>
--FILE--
<?php
function test($value) {
return match ($value) {
1 => '1 int',
'1' => '1 string',
2 => '2 int',
'2' => '2 string',
3 => '3 int',
'3' => '3 string',
4 => '4 int',
'4' => '4 string',
5 => '5 int',
'5' => '5 string',
default => 'default',
};
}
foreach (range(0, 6) as $number) {
var_dump(test($number));
var_dump(test((string) $number));
}
--EXPECTF--
$_main:
; (lines=22, args=0, vars=1, tmps=2)
; (after optimizer)
; %s.php:1-24
0000 INIT_FCALL 2 %d string("range")
0001 SEND_VAL int(0) 1
0002 SEND_VAL int(6) 2
0003 V2 = DO_ICALL
0004 V1 = FE_RESET_R V2 0020
0005 FE_FETCH_R V1 CV0($number) 0020
0006 INIT_FCALL 1 %d string("var_dump")
0007 INIT_FCALL 1 %d string("test")
0008 SEND_VAR CV0($number) 1
0009 V2 = DO_UCALL
0010 SEND_VAR V2 1
0011 DO_ICALL
0012 INIT_FCALL 1 %d string("var_dump")
0013 INIT_FCALL 1 %d string("test")
0014 T2 = CAST (string) CV0($number)
0015 SEND_VAL T2 1
0016 V2 = DO_UCALL
0017 SEND_VAR V2 1
0018 DO_ICALL
0019 JMP 0005
0020 FE_FREE V1
0021 RETURN int(1)
LIVE RANGES:
1: 0005 - 0020 (loop)
test:
; (lines=13, args=1, vars=1, tmps=0)
; (after optimizer)
; %s.php:3-17
0000 CV0($value) = RECV 1
0001 MATCH CV0($value) 1: 0002, "1": 0003, 2: 0004, "2": 0005, 3: 0006, "3": 0007, 4: 0008, "4": 0009, 5: 0010, "5": 0011, default: 0012
0002 RETURN string("1 int")
0003 RETURN string("1 string")
0004 RETURN string("2 int")
0005 RETURN string("2 string")
0006 RETURN string("3 int")
0007 RETURN string("3 string")
0008 RETURN string("4 int")
0009 RETURN string("4 string")
0010 RETURN string("5 int")
0011 RETURN string("5 string")
0012 RETURN string("default")
string(7) "default"
string(7) "default"
string(5) "1 int"
string(8) "1 string"
string(5) "2 int"
string(8) "2 string"
string(5) "3 int"
string(8) "3 string"
string(5) "4 int"
string(8) "4 string"
string(5) "5 int"
string(8) "5 string"
string(7) "default"
string(7) "default"

View File

@ -547,6 +547,7 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra
case ZEND_FE_FETCH_RW:
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
case ZEND_MATCH:
/* relative extended_value don't have to be changed */
break;
}

View File

@ -549,6 +549,7 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc
case ZEND_FE_FETCH_RW:
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
case ZEND_MATCH:
/* relative extended_value don't have to be changed */
break;
}

View File

@ -74,7 +74,7 @@ unlink($file_name);
echo "\n*** Testing fnmatch() with other types other than files ***";
/* defining a common function */
function match( $pattern, $string ) {
function match_( $pattern, $string ) {
for( $i = 0; $i<count($pattern); $i++ ) {
echo "-- Iteration $i --\n";
for( $j = 0; $j<count($string); $j++ ) {
@ -96,7 +96,7 @@ $int_arr = array(
0xF,
0xF0000
);
match($int_arr, $int_arr);
match_($int_arr, $int_arr);
echo "\n--- With Strings ---\n";
$str_arr = array(
@ -109,7 +109,7 @@ $str_arr = array(
/* binary input */
b"string"
);
match($str_arr, $str_arr);
match_($str_arr, $str_arr);
echo "\n--- With booleans ---\n";
$bool_arr = array(
@ -123,7 +123,7 @@ $bool_arr = array(
"",
"string"
);
match($bool_arr, $bool_arr);
match_($bool_arr, $bool_arr);
echo "\n--- With NULL ---\n";
$null_arr = array(
@ -134,7 +134,7 @@ $null_arr = array(
"string",
0
);
match($null_arr, $null_arr);
match_($null_arr, $null_arr);
echo "\n*** Done ***\n";
?>

View File

@ -84,8 +84,6 @@ void tokenizer_register_constants(INIT_FUNC_ARGS) {
REGISTER_LONG_CONSTANT("T_STRING_VARNAME", T_STRING_VARNAME, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_NUM_STRING", T_NUM_STRING, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_EVAL", T_EVAL, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_INC", T_INC, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_DEC", T_DEC, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_NEW", T_NEW, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_EXIT", T_EXIT, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_IF", T_IF, CONST_CS | CONST_PERSISTENT);
@ -105,6 +103,7 @@ void tokenizer_register_constants(INIT_FUNC_ARGS) {
REGISTER_LONG_CONSTANT("T_ENDSWITCH", T_ENDSWITCH, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_CASE", T_CASE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_DEFAULT", T_DEFAULT, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_MATCH", T_MATCH, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_BREAK", T_BREAK, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_CONTINUE", T_CONTINUE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_GOTO", T_GOTO, CONST_CS | CONST_PERSISTENT);
@ -134,7 +133,7 @@ void tokenizer_register_constants(INIT_FUNC_ARGS) {
REGISTER_LONG_CONSTANT("T_INTERFACE", T_INTERFACE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_EXTENDS", T_EXTENDS, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_IMPLEMENTS", T_IMPLEMENTS, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_OBJECT_OPERATOR", T_OBJECT_OPERATOR, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_NAMESPACE", T_NAMESPACE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_LIST", T_LIST, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_ARRAY", T_ARRAY, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_CALLABLE", T_CALLABLE, CONST_CS | CONST_PERSISTENT);
@ -145,6 +144,10 @@ void tokenizer_register_constants(INIT_FUNC_ARGS) {
REGISTER_LONG_CONSTANT("T_TRAIT_C", T_TRAIT_C, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_METHOD_C", T_METHOD_C, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_FUNC_C", T_FUNC_C, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_NS_C", T_NS_C, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_INC", T_INC, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_DEC", T_DEC, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_OBJECT_OPERATOR", T_OBJECT_OPERATOR, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_COMMENT", T_COMMENT, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_DOC_COMMENT", T_DOC_COMMENT, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_OPEN_TAG", T_OPEN_TAG, CONST_CS | CONST_PERSISTENT);
@ -156,8 +159,6 @@ void tokenizer_register_constants(INIT_FUNC_ARGS) {
REGISTER_LONG_CONSTANT("T_DOLLAR_OPEN_CURLY_BRACES", T_DOLLAR_OPEN_CURLY_BRACES, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_CURLY_OPEN", T_CURLY_OPEN, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_PAAMAYIM_NEKUDOTAYIM", T_PAAMAYIM_NEKUDOTAYIM, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_NAMESPACE", T_NAMESPACE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_NS_C", T_NS_C, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_NS_SEPARATOR", T_NS_SEPARATOR, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_ELLIPSIS", T_ELLIPSIS, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_BAD_CHARACTER", T_BAD_CHARACTER, CONST_CS | CONST_PERSISTENT);
@ -227,8 +228,6 @@ char *get_token_type_name(int token_type)
case T_STRING_VARNAME: return "T_STRING_VARNAME";
case T_NUM_STRING: return "T_NUM_STRING";
case T_EVAL: return "T_EVAL";
case T_INC: return "T_INC";
case T_DEC: return "T_DEC";
case T_NEW: return "T_NEW";
case T_EXIT: return "T_EXIT";
case T_IF: return "T_IF";
@ -248,6 +247,7 @@ char *get_token_type_name(int token_type)
case T_ENDSWITCH: return "T_ENDSWITCH";
case T_CASE: return "T_CASE";
case T_DEFAULT: return "T_DEFAULT";
case T_MATCH: return "T_MATCH";
case T_BREAK: return "T_BREAK";
case T_CONTINUE: return "T_CONTINUE";
case T_GOTO: return "T_GOTO";
@ -277,7 +277,7 @@ char *get_token_type_name(int token_type)
case T_INTERFACE: return "T_INTERFACE";
case T_EXTENDS: return "T_EXTENDS";
case T_IMPLEMENTS: return "T_IMPLEMENTS";
case T_OBJECT_OPERATOR: return "T_OBJECT_OPERATOR";
case T_NAMESPACE: return "T_NAMESPACE";
case T_LIST: return "T_LIST";
case T_ARRAY: return "T_ARRAY";
case T_CALLABLE: return "T_CALLABLE";
@ -288,6 +288,10 @@ char *get_token_type_name(int token_type)
case T_TRAIT_C: return "T_TRAIT_C";
case T_METHOD_C: return "T_METHOD_C";
case T_FUNC_C: return "T_FUNC_C";
case T_NS_C: return "T_NS_C";
case T_INC: return "T_INC";
case T_DEC: return "T_DEC";
case T_OBJECT_OPERATOR: return "T_OBJECT_OPERATOR";
case T_COMMENT: return "T_COMMENT";
case T_DOC_COMMENT: return "T_DOC_COMMENT";
case T_OPEN_TAG: return "T_OPEN_TAG";
@ -299,8 +303,6 @@ char *get_token_type_name(int token_type)
case T_DOLLAR_OPEN_CURLY_BRACES: return "T_DOLLAR_OPEN_CURLY_BRACES";
case T_CURLY_OPEN: return "T_CURLY_OPEN";
case T_PAAMAYIM_NEKUDOTAYIM: return "T_DOUBLE_COLON";
case T_NAMESPACE: return "T_NAMESPACE";
case T_NS_C: return "T_NS_C";
case T_NS_SEPARATOR: return "T_NS_SEPARATOR";
case T_ELLIPSIS: return "T_ELLIPSIS";
case T_BAD_CHARACTER: return "T_BAD_CHARACTER";