Skip to content

Commit

Permalink
Optimize match opcodes
Browse files Browse the repository at this point in the history
Get rid of IS_IDENTICAL/JMPNZ chain when jumptable was generated.
  • Loading branch information
iluuu1994 committed Apr 18, 2020
1 parent 1152273 commit 1284e21
Show file tree
Hide file tree
Showing 6 changed files with 814 additions and 604 deletions.
44 changes: 13 additions & 31 deletions Zend/tests/match/013.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ foreach (range('a', 'i') as $char) {

--EXPECTF--
$_main:
; (lines=%d, args=0, vars=1, tmps=2)
; (lines=15, args=0, vars=1, tmps=2)
; (after optimizer)
; %s
0000 INIT_FCALL 2 %d string("range")
Expand All @@ -48,40 +48,22 @@ LIVE RANGES:
1: 0005 - 0013 (loop)

test:
; (lines=%d, args=1, vars=1, tmps=1)
; (lines=11, args=1, vars=1, tmps=1)
; (after optimizer)
; %s
0000 CV0($char) = RECV 1
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
0002 T1 = IS_IDENTICAL CV0($char) string("a")
0003 JMPNZ T1 0020
0004 T1 = IS_IDENTICAL CV0($char) string("b")
0005 JMPNZ T1 0021
0006 T1 = IS_IDENTICAL CV0($char) string("c")
0007 JMPNZ T1 0021
0008 T1 = IS_IDENTICAL CV0($char) string("d")
0009 JMPNZ T1 0022
0010 T1 = IS_IDENTICAL CV0($char) string("e")
0011 JMPNZ T1 0023
0012 T1 = IS_IDENTICAL CV0($char) string("f")
0013 JMPNZ T1 0023
0014 T1 = IS_IDENTICAL CV0($char) string("g")
0015 JMPNZ T1 0024
0016 T1 = IS_IDENTICAL CV0($char) string("h")
0017 JMPNZ T1 0025
0018 T1 = IS_IDENTICAL CV0($char) string("i")
0019 JMPZNZ T1 0026 0025
0020 RETURN string("a")
0021 RETURN string("b, c")
0022 RETURN string("d")
0023 RETURN string("e, f")
0024 RETURN string("g")
0025 RETURN string("h, i")
0026 V1 = NEW 0 string("UnhandledMatchError")
0027 DO_FCALL
0028 THROW V1
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
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 V1 = NEW 0 string("UnhandledMatchError")
0009 DO_FCALL
0010 THROW V1
LIVE RANGES:
1: 0027 - 0028 (new)
1: 0009 - 0010 (new)
string(1) "a"
string(4) "b, c"
string(4) "b, c"
Expand Down
36 changes: 9 additions & 27 deletions Zend/tests/match/019.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -49,36 +49,18 @@ LIVE RANGES:
1: 0005 - 0013 (loop)

test:
; (lines=27, args=1, vars=1, tmps=1)
; (lines=9, args=1, vars=1, tmps=0)
; (after optimizer)
; %s
0000 CV0($char) = RECV 1
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
0002 T1 = IS_IDENTICAL CV0($char) int(1)
0003 JMPNZ T1 0020
0004 T1 = IS_IDENTICAL CV0($char) int(2)
0005 JMPNZ T1 0021
0006 T1 = IS_IDENTICAL CV0($char) int(3)
0007 JMPNZ T1 0021
0008 T1 = IS_IDENTICAL CV0($char) int(4)
0009 JMPNZ T1 0022
0010 T1 = IS_IDENTICAL CV0($char) int(5)
0011 JMPNZ T1 0023
0012 T1 = IS_IDENTICAL CV0($char) int(6)
0013 JMPNZ T1 0023
0014 T1 = IS_IDENTICAL CV0($char) int(7)
0015 JMPNZ T1 0024
0016 T1 = IS_IDENTICAL CV0($char) int(8)
0017 JMPNZ T1 0025
0018 T1 = IS_IDENTICAL CV0($char) int(9)
0019 JMPZNZ T1 0026 0025
0020 RETURN string("1")
0021 RETURN string("2, 3")
0022 RETURN string("4")
0023 RETURN string("5, 6")
0024 RETURN string("7")
0025 RETURN string("8, 9")
0026 RETURN string("default")
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
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"
Expand Down
103 changes: 63 additions & 40 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -5239,8 +5239,24 @@ void zend_compile_match(znode *result, zend_ast *ast) /* {{{ */

uint32_t num_conds = count_match_conds(arms);
zend_uchar jumptable_type = determine_match_jumptable_type(arms);
zend_bool uses_jumptable = jumptable_type != IS_UNDEF && should_use_jumptable(num_conds, jumptable_type);
HashTable *jumptable = NULL;
if (jumptable_type != IS_UNDEF && should_use_jumptable(num_conds, jumptable_type)) {
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);
Expand All @@ -5255,53 +5271,51 @@ void zend_compile_match(znode *result, zend_ast *ast) /* {{{ */
Z_TRY_ADDREF_P(CT_CONSTANT(opline->op1));
}
opnum_match = opline - CG(active_op_array)->opcodes;
}

uint32_t cond_count = 0;
uint32_t *jmpnz_opnums = safe_emalloc(sizeof(uint32_t), num_conds, 0);
for (uint32_t i = 0; i < arms->children; ++i) {
zend_ast *arm_ast = arms->child[i];
} else {
jmpnz_opnums = safe_emalloc(sizeof(uint32_t), num_conds, 0);
int cond_count = 0;
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");
if (!arm_ast->child[0]) {
continue;
}
has_default_arm = 1;
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_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);
znode cond_node;
zend_compile_expr(&cond_node, cond_ast);

if (expr_node.op_type == IS_CONST
&& Z_TYPE(expr_node.u.constant) == IS_FALSE) {
jmpnz_opnums[cond_count] = zend_emit_cond_jump(ZEND_JMPZ, &cond_node, 0);
} else if (expr_node.op_type == IS_CONST
&& Z_TYPE(expr_node.u.constant) == IS_TRUE) {
jmpnz_opnums[cond_count] = zend_emit_cond_jump(ZEND_JMPNZ, &cond_node, 0);
} else {
zend_op *opline = zend_emit_op(NULL, ZEND_IS_IDENTICAL, &expr_node, &cond_node);
SET_NODE(opline->result, &case_node);
if (opline->op1_type == IS_CONST) {
Z_TRY_ADDREF_P(CT_CONSTANT(opline->op1));
if (expr_node.op_type == IS_CONST
&& Z_TYPE(expr_node.u.constant) == IS_FALSE) {
jmpnz_opnums[cond_count] = zend_emit_cond_jump(ZEND_JMPZ, &cond_node, 0);
} else if (expr_node.op_type == IS_CONST
&& Z_TYPE(expr_node.u.constant) == IS_TRUE) {
jmpnz_opnums[cond_count] = zend_emit_cond_jump(ZEND_JMPNZ, &cond_node, 0);
} else {
zend_op *opline = zend_emit_op(NULL, ZEND_IS_IDENTICAL, &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);
}

jmpnz_opnums[cond_count] = zend_emit_cond_jump(ZEND_JMPNZ, &case_node, 0);
cond_count++;
}

cond_count++;
}
}

uint32_t opnum_default_jmp = zend_emit_jump(0);
uint32_t opnum_default_jmp;
if (!uses_jumptable) {
opnum_default_jmp = zend_emit_jump(0);
}

zend_bool is_first_case = 1;
cond_count = 0;
uint32_t cond_count = 0;

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

for (uint32_t j = 0; j < conds->children; j++) {
zend_ast *cond_ast = conds->child[j];
zend_update_jump_target_to_next(jmpnz_opnums[cond_count]);

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);
Expand All @@ -5331,7 +5348,9 @@ void zend_compile_match(znode *result, zend_ast *ast) /* {{{ */
cond_count++;
}
} else {
zend_update_jump_target_to_next(opnum_default_jmp);
if (!uses_jumptable) {
zend_update_jump_target_to_next(opnum_default_jmp);
}

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

if (!has_default_arm) {
zend_update_jump_target_to_next(opnum_default_jmp);
if (!uses_jumptable) {
zend_update_jump_target_to_next(opnum_default_jmp);
}

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

efree(jmpnz_opnums);
if (jmpnz_opnums != NULL) {
efree(jmpnz_opnums);
}
}
/* }}} */

Expand Down
57 changes: 55 additions & 2 deletions Zend/zend_vm_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -8404,12 +8404,65 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(188, ZEND_SWITCH_STRING, CONST|TMPVARCV, CONST,

ZEND_VM_COLD_CONSTCONST_HANDLER(195, ZEND_MATCH_LONG, CONST|TMPVARCV, CONST, JMP_ADDR)
{
ZEND_VM_DISPATCH_TO_HANDLER(ZEND_SWITCH_LONG);
USE_OPLINE
zval *op, *jump_zv;
HashTable *jumptable;

op = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);

if (Z_TYPE_P(op) != IS_LONG) {
ZVAL_DEREF(op);
if (Z_TYPE_P(op) != IS_LONG) {
/* default */
ZEND_VM_C_GOTO(default_branch);
}
}

jumptable = Z_ARRVAL_P(GET_OP2_ZVAL_PTR(BP_VAR_R));
jump_zv = zend_hash_index_find(jumptable, Z_LVAL_P(op));
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_CONSTCONST_HANDLER(196, ZEND_MATCH_STRING, CONST|TMPVARCV, CONST, JMP_ADDR)
{
ZEND_VM_DISPATCH_TO_HANDLER(ZEND_SWITCH_STRING);
USE_OPLINE
zval *op, *jump_zv;
HashTable *jumptable;

op = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);

if (Z_TYPE_P(op) != IS_STRING) {
if (OP1_TYPE == IS_CONST) {
/* default */
ZEND_VM_C_GOTO(default_branch);
} else {
ZVAL_DEREF(op);
if (Z_TYPE_P(op) != IS_STRING) {
/* default */
ZEND_VM_C_GOTO(default_branch);
}
}
}

jumptable = Z_ARRVAL_P(GET_OP2_ZVAL_PTR(BP_VAR_R));
jump_zv = zend_hash_find_ex(jumptable, Z_STR_P(op), OP1_TYPE == IS_CONST);
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_CONSTCONST_HANDLER(189, ZEND_IN_ARRAY, CONST|TMP|VAR|CV, CONST, NUM)
Expand Down
Loading

0 comments on commit 1284e21

Please sign in to comment.