Skip to content

Commit 5bbaa60

Browse files
committed
Optimize match opcodes
Get rid of IS_IDENTICAL/JMPNZ chain when jumptable was generated.
1 parent 1152273 commit 5bbaa60

File tree

6 files changed

+814
-604
lines changed

6 files changed

+814
-604
lines changed

Zend/tests/match/013.phpt

Lines changed: 13 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ foreach (range('a', 'i') as $char) {
2626

2727
--EXPECTF--
2828
$_main:
29-
; (lines=%d, args=0, vars=1, tmps=2)
29+
; (lines=15, args=0, vars=1, tmps=2)
3030
; (after optimizer)
3131
; %s
3232
0000 INIT_FCALL 2 %d string("range")
@@ -48,40 +48,22 @@ LIVE RANGES:
4848
1: 0005 - 0013 (loop)
4949

5050
test:
51-
; (lines=%d, args=1, vars=1, tmps=1)
51+
; (lines=11, args=1, vars=1, tmps=1)
5252
; (after optimizer)
5353
; %s
5454
0000 CV0($char) = RECV 1
55-
0001 MATCH_STRING CV0($char) "a": 0020, "b": 0021, "c": 0021, "d": 0022, "e": 0023, "f": 0023, "g": 0024, "h": 0025, "i": 0025, default: 0026
56-
0002 T1 = IS_IDENTICAL CV0($char) string("a")
57-
0003 JMPNZ T1 0020
58-
0004 T1 = IS_IDENTICAL CV0($char) string("b")
59-
0005 JMPNZ T1 0021
60-
0006 T1 = IS_IDENTICAL CV0($char) string("c")
61-
0007 JMPNZ T1 0021
62-
0008 T1 = IS_IDENTICAL CV0($char) string("d")
63-
0009 JMPNZ T1 0022
64-
0010 T1 = IS_IDENTICAL CV0($char) string("e")
65-
0011 JMPNZ T1 0023
66-
0012 T1 = IS_IDENTICAL CV0($char) string("f")
67-
0013 JMPNZ T1 0023
68-
0014 T1 = IS_IDENTICAL CV0($char) string("g")
69-
0015 JMPNZ T1 0024
70-
0016 T1 = IS_IDENTICAL CV0($char) string("h")
71-
0017 JMPNZ T1 0025
72-
0018 T1 = IS_IDENTICAL CV0($char) string("i")
73-
0019 JMPZNZ T1 0026 0025
74-
0020 RETURN string("a")
75-
0021 RETURN string("b, c")
76-
0022 RETURN string("d")
77-
0023 RETURN string("e, f")
78-
0024 RETURN string("g")
79-
0025 RETURN string("h, i")
80-
0026 V1 = NEW 0 string("UnhandledMatchError")
81-
0027 DO_FCALL
82-
0028 THROW V1
55+
0001 MATCH_STRING CV0($char) "a": 0002, "b": 0003, "c": 0003, "d": 0004, "e": 0005, "f": 0005, "g": 0006, "h": 0007, "i": 0007, default: 0008
56+
0002 RETURN string("a")
57+
0003 RETURN string("b, c")
58+
0004 RETURN string("d")
59+
0005 RETURN string("e, f")
60+
0006 RETURN string("g")
61+
0007 RETURN string("h, i")
62+
0008 V1 = NEW 0 string("UnhandledMatchError")
63+
0009 DO_FCALL
64+
0010 THROW V1
8365
LIVE RANGES:
84-
1: 0027 - 0028 (new)
66+
1: 0009 - 0010 (new)
8567
string(1) "a"
8668
string(4) "b, c"
8769
string(4) "b, c"

Zend/tests/match/019.phpt

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -49,36 +49,18 @@ LIVE RANGES:
4949
1: 0005 - 0013 (loop)
5050

5151
test:
52-
; (lines=27, args=1, vars=1, tmps=1)
52+
; (lines=9, args=1, vars=1, tmps=0)
5353
; (after optimizer)
5454
; %s
5555
0000 CV0($char) = RECV 1
56-
0001 MATCH_LONG CV0($char) 1: 0020, 2: 0021, 3: 0021, 4: 0022, 5: 0023, 6: 0023, 7: 0024, 8: 0025, 9: 0025, default: 0026
57-
0002 T1 = IS_IDENTICAL CV0($char) int(1)
58-
0003 JMPNZ T1 0020
59-
0004 T1 = IS_IDENTICAL CV0($char) int(2)
60-
0005 JMPNZ T1 0021
61-
0006 T1 = IS_IDENTICAL CV0($char) int(3)
62-
0007 JMPNZ T1 0021
63-
0008 T1 = IS_IDENTICAL CV0($char) int(4)
64-
0009 JMPNZ T1 0022
65-
0010 T1 = IS_IDENTICAL CV0($char) int(5)
66-
0011 JMPNZ T1 0023
67-
0012 T1 = IS_IDENTICAL CV0($char) int(6)
68-
0013 JMPNZ T1 0023
69-
0014 T1 = IS_IDENTICAL CV0($char) int(7)
70-
0015 JMPNZ T1 0024
71-
0016 T1 = IS_IDENTICAL CV0($char) int(8)
72-
0017 JMPNZ T1 0025
73-
0018 T1 = IS_IDENTICAL CV0($char) int(9)
74-
0019 JMPZNZ T1 0026 0025
75-
0020 RETURN string("1")
76-
0021 RETURN string("2, 3")
77-
0022 RETURN string("4")
78-
0023 RETURN string("5, 6")
79-
0024 RETURN string("7")
80-
0025 RETURN string("8, 9")
81-
0026 RETURN string("default")
56+
0001 MATCH_LONG CV0($char) 1: 0002, 2: 0003, 3: 0003, 4: 0004, 5: 0005, 6: 0005, 7: 0006, 8: 0007, 9: 0007, default: 0008
57+
0002 RETURN string("1")
58+
0003 RETURN string("2, 3")
59+
0004 RETURN string("4")
60+
0005 RETURN string("5, 6")
61+
0006 RETURN string("7")
62+
0007 RETURN string("8, 9")
63+
0008 RETURN string("default")
8264
string(7) "default"
8365
string(1) "1"
8466
string(4) "2, 3"

Zend/zend_compile.c

Lines changed: 63 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5239,8 +5239,24 @@ void zend_compile_match(znode *result, zend_ast *ast) /* {{{ */
52395239

52405240
uint32_t num_conds = count_match_conds(arms);
52415241
zend_uchar jumptable_type = determine_match_jumptable_type(arms);
5242+
zend_bool uses_jumptable = jumptable_type != IS_UNDEF && should_use_jumptable(num_conds, jumptable_type);
52425243
HashTable *jumptable = NULL;
5243-
if (jumptable_type != IS_UNDEF && should_use_jumptable(num_conds, jumptable_type)) {
5244+
uint32_t *jmpnz_opnums = NULL;
5245+
5246+
for (uint32_t i = 0; i < arms->children; ++i) {
5247+
zend_ast *arm_ast = arms->child[i];
5248+
5249+
if (!arm_ast->child[0]) {
5250+
if (has_default_arm) {
5251+
CG(zend_lineno) = arm_ast->lineno;
5252+
zend_error_noreturn(E_COMPILE_ERROR,
5253+
"Match expressions may only contain one default arm");
5254+
}
5255+
has_default_arm = 1;
5256+
}
5257+
}
5258+
5259+
if (uses_jumptable) {
52445260
znode jumptable_op;
52455261

52465262
ALLOC_HASHTABLE(jumptable);
@@ -5255,53 +5271,51 @@ void zend_compile_match(znode *result, zend_ast *ast) /* {{{ */
52555271
Z_TRY_ADDREF_P(CT_CONSTANT(opline->op1));
52565272
}
52575273
opnum_match = opline - CG(active_op_array)->opcodes;
5258-
}
5259-
5260-
uint32_t cond_count = 0;
5261-
uint32_t *jmpnz_opnums = safe_emalloc(sizeof(uint32_t), num_conds, 0);
5262-
for (uint32_t i = 0; i < arms->children; ++i) {
5263-
zend_ast *arm_ast = arms->child[i];
5274+
} else {
5275+
jmpnz_opnums = safe_emalloc(sizeof(uint32_t), num_conds, 0);
5276+
int cond_count = 0;
5277+
for (uint32_t i = 0; i < arms->children; ++i) {
5278+
zend_ast *arm_ast = arms->child[i];
52645279

5265-
if (!arm_ast->child[0]) {
5266-
if (has_default_arm) {
5267-
CG(zend_lineno) = arm_ast->lineno;
5268-
zend_error_noreturn(E_COMPILE_ERROR,
5269-
"Match expressions may only contain one default arm");
5280+
if (!arm_ast->child[0]) {
5281+
continue;
52705282
}
5271-
has_default_arm = 1;
5272-
continue;
5273-
}
52745283

5275-
zend_ast_list *conds = zend_ast_get_list(arm_ast->child[0]);
5276-
for (uint32_t j = 0; j < conds->children; j++) {
5277-
zend_ast *cond_ast = conds->child[j];
5284+
zend_ast_list *conds = zend_ast_get_list(arm_ast->child[0]);
5285+
for (uint32_t j = 0; j < conds->children; j++) {
5286+
zend_ast *cond_ast = conds->child[j];
52785287

5279-
znode cond_node;
5280-
zend_compile_expr(&cond_node, cond_ast);
5288+
znode cond_node;
5289+
zend_compile_expr(&cond_node, cond_ast);
52815290

5282-
if (expr_node.op_type == IS_CONST
5283-
&& Z_TYPE(expr_node.u.constant) == IS_FALSE) {
5284-
jmpnz_opnums[cond_count] = zend_emit_cond_jump(ZEND_JMPZ, &cond_node, 0);
5285-
} else if (expr_node.op_type == IS_CONST
5286-
&& Z_TYPE(expr_node.u.constant) == IS_TRUE) {
5287-
jmpnz_opnums[cond_count] = zend_emit_cond_jump(ZEND_JMPNZ, &cond_node, 0);
5288-
} else {
5289-
zend_op *opline = zend_emit_op(NULL, ZEND_IS_IDENTICAL, &expr_node, &cond_node);
5290-
SET_NODE(opline->result, &case_node);
5291-
if (opline->op1_type == IS_CONST) {
5292-
Z_TRY_ADDREF_P(CT_CONSTANT(opline->op1));
5291+
if (expr_node.op_type == IS_CONST
5292+
&& Z_TYPE(expr_node.u.constant) == IS_FALSE) {
5293+
jmpnz_opnums[cond_count] = zend_emit_cond_jump(ZEND_JMPZ, &cond_node, 0);
5294+
} else if (expr_node.op_type == IS_CONST
5295+
&& Z_TYPE(expr_node.u.constant) == IS_TRUE) {
5296+
jmpnz_opnums[cond_count] = zend_emit_cond_jump(ZEND_JMPNZ, &cond_node, 0);
5297+
} else {
5298+
zend_op *opline = zend_emit_op(NULL, ZEND_IS_IDENTICAL, &expr_node, &cond_node);
5299+
SET_NODE(opline->result, &case_node);
5300+
if (opline->op1_type == IS_CONST) {
5301+
Z_TRY_ADDREF_P(CT_CONSTANT(opline->op1));
5302+
}
5303+
5304+
jmpnz_opnums[cond_count] = zend_emit_cond_jump(ZEND_JMPNZ, &case_node, 0);
52935305
}
52945306

5295-
jmpnz_opnums[cond_count] = zend_emit_cond_jump(ZEND_JMPNZ, &case_node, 0);
5307+
cond_count++;
52965308
}
5297-
5298-
cond_count++;
52995309
}
53005310
}
53015311

5302-
uint32_t opnum_default_jmp = zend_emit_jump(0);
5312+
uint32_t opnum_default_jmp = 0;
5313+
if (!uses_jumptable) {
5314+
opnum_default_jmp = zend_emit_jump(0);
5315+
}
5316+
53035317
zend_bool is_first_case = 1;
5304-
cond_count = 0;
5318+
uint32_t cond_count = 0;
53055319

53065320
for (uint32_t i = 0; i < arms->children; ++i) {
53075321
zend_ast *arm_ast = arms->child[i];
@@ -5312,7 +5326,10 @@ void zend_compile_match(znode *result, zend_ast *ast) /* {{{ */
53125326

53135327
for (uint32_t j = 0; j < conds->children; j++) {
53145328
zend_ast *cond_ast = conds->child[j];
5315-
zend_update_jump_target_to_next(jmpnz_opnums[cond_count]);
5329+
5330+
if (jmpnz_opnums != NULL) {
5331+
zend_update_jump_target_to_next(jmpnz_opnums[cond_count]);
5332+
}
53165333

53175334
if (jumptable) {
53185335
zval *cond_zv = zend_ast_get_zval(cond_ast);
@@ -5331,7 +5348,9 @@ void zend_compile_match(znode *result, zend_ast *ast) /* {{{ */
53315348
cond_count++;
53325349
}
53335350
} else {
5334-
zend_update_jump_target_to_next(opnum_default_jmp);
5351+
if (!uses_jumptable) {
5352+
zend_update_jump_target_to_next(opnum_default_jmp);
5353+
}
53355354

53365355
if (jumptable) {
53375356
ZEND_ASSERT(opnum_match != (uint32_t)-1);
@@ -5364,7 +5383,9 @@ void zend_compile_match(znode *result, zend_ast *ast) /* {{{ */
53645383
}
53655384

53665385
if (!has_default_arm) {
5367-
zend_update_jump_target_to_next(opnum_default_jmp);
5386+
if (!uses_jumptable) {
5387+
zend_update_jump_target_to_next(opnum_default_jmp);
5388+
}
53685389

53695390
if (jumptable) {
53705391
zend_op *opline = &CG(active_op_array)->opcodes[opnum_match];
@@ -5392,7 +5413,9 @@ void zend_compile_match(znode *result, zend_ast *ast) /* {{{ */
53925413
zval_ptr_dtor_nogc(&expr_node.u.constant);
53935414
}
53945415

5395-
efree(jmpnz_opnums);
5416+
if (jmpnz_opnums != NULL) {
5417+
efree(jmpnz_opnums);
5418+
}
53965419
}
53975420
/* }}} */
53985421

Zend/zend_vm_def.h

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8404,12 +8404,65 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(188, ZEND_SWITCH_STRING, CONST|TMPVARCV, CONST,
84048404

84058405
ZEND_VM_COLD_CONSTCONST_HANDLER(195, ZEND_MATCH_LONG, CONST|TMPVARCV, CONST, JMP_ADDR)
84068406
{
8407-
ZEND_VM_DISPATCH_TO_HANDLER(ZEND_SWITCH_LONG);
8407+
USE_OPLINE
8408+
zval *op, *jump_zv;
8409+
HashTable *jumptable;
8410+
8411+
op = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
8412+
8413+
if (Z_TYPE_P(op) != IS_LONG) {
8414+
ZVAL_DEREF(op);
8415+
if (Z_TYPE_P(op) != IS_LONG) {
8416+
/* default */
8417+
ZEND_VM_C_GOTO(default_branch);
8418+
}
8419+
}
8420+
8421+
jumptable = Z_ARRVAL_P(GET_OP2_ZVAL_PTR(BP_VAR_R));
8422+
jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(op));
8423+
if (jump_zv != NULL) {
8424+
ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv));
8425+
ZEND_VM_CONTINUE();
8426+
} else {
8427+
ZEND_VM_C_LABEL(default_branch):
8428+
/* default */
8429+
ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
8430+
ZEND_VM_CONTINUE();
8431+
}
84088432
}
84098433

84108434
ZEND_VM_COLD_CONSTCONST_HANDLER(196, ZEND_MATCH_STRING, CONST|TMPVARCV, CONST, JMP_ADDR)
84118435
{
8412-
ZEND_VM_DISPATCH_TO_HANDLER(ZEND_SWITCH_STRING);
8436+
USE_OPLINE
8437+
zval *op, *jump_zv;
8438+
HashTable *jumptable;
8439+
8440+
op = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
8441+
8442+
if (Z_TYPE_P(op) != IS_STRING) {
8443+
if (OP1_TYPE == IS_CONST) {
8444+
/* default */
8445+
ZEND_VM_C_GOTO(default_branch);
8446+
} else {
8447+
ZVAL_DEREF(op);
8448+
if (Z_TYPE_P(op) != IS_STRING) {
8449+
/* default */
8450+
ZEND_VM_C_GOTO(default_branch);
8451+
}
8452+
}
8453+
}
8454+
8455+
jumptable = Z_ARRVAL_P(GET_OP2_ZVAL_PTR(BP_VAR_R));
8456+
jump_zv = zend_hash_find_ex(jumptable, Z_STR_P(op), OP1_TYPE == IS_CONST);
8457+
if (jump_zv != NULL) {
8458+
ZEND_VM_SET_RELATIVE_OPCODE(opline, Z_LVAL_P(jump_zv));
8459+
ZEND_VM_CONTINUE();
8460+
} else {
8461+
ZEND_VM_C_LABEL(default_branch):
8462+
/* default */
8463+
ZEND_VM_SET_RELATIVE_OPCODE(opline, opline->extended_value);
8464+
ZEND_VM_CONTINUE();
8465+
}
84138466
}
84148467

84158468
ZEND_VM_COLD_CONSTCONST_HANDLER(189, ZEND_IN_ARRAY, CONST|TMP|VAR|CV, CONST, NUM)

0 commit comments

Comments
 (0)