From 78a1c8c811d4d1e60ffe937102401cb7f69504a2 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 9 Oct 2019 22:52:09 +0300 Subject: [PATCH] Cleanup JMP optimization pass --- ext/opcache/Optimizer/pass3.c | 122 +++++++++++++++++++++++----------- 1 file changed, 85 insertions(+), 37 deletions(-) diff --git a/ext/opcache/Optimizer/pass3.c b/ext/opcache/Optimizer/pass3.c index 6e406f11276..de2acc6dbc9 100644 --- a/ext/opcache/Optimizer/pass3.c +++ b/ext/opcache/Optimizer/pass3.c @@ -32,15 +32,24 @@ #include "zend_vm.h" /* we use "jmp_hitlist" to avoid infinity loops during jmp optimization */ -#define CHECK_JMP(target, label) \ - do { \ - for (i=0; ilast, use_heap); @@ -64,10 +72,17 @@ void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx) /* convert JMP L1 ... L1: JMP L2 to JMP L2 .. L1: JMP L2 */ target = ZEND_OP1_JMP_ADDR(opline); - while (target->opcode == ZEND_JMP) { - target = ZEND_OP1_JMP_ADDR(target); - CHECK_JMP(target, done_jmp_optimization); - ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target); + while (1) { + if (target->opcode == ZEND_JMP) { + target = ZEND_OP1_JMP_ADDR(target); + CHECK_LOOP(target); + ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target); + } else if (target->opcode == ZEND_JMP) { + target = target + 1; + ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target); + } else { + break; + } } /* convert L: JMP L+1 to NOP */ @@ -106,10 +121,17 @@ void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx) jmp_hitlist_count = 0; target = ZEND_OP2_JMP_ADDR(opline); - while (target->opcode == ZEND_JMP) { - target = ZEND_OP1_JMP_ADDR(target); - CHECK_JMP(target, done_jmp_optimization); - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target); + while (1) { + if (target->opcode == ZEND_JMP) { + target = ZEND_OP1_JMP_ADDR(target); + CHECK_LOOP(target); + ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target); + } else if (target->opcode == ZEND_JMP) { + target = target + 1; + ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target); + } else { + break; + } } break; @@ -123,14 +145,14 @@ void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx) /* plain JMP */ /* JMPZ(X,L1), L1: JMP(L2) => JMPZ(X,L2), L1: JMP(L2) */ target = ZEND_OP1_JMP_ADDR(target); - CHECK_JMP(target, done_jmp_optimization); + CHECK_LOOP(target); ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target); } else if (target->opcode == opline->opcode && SAME_VAR(opline->op1, target->op1)) { /* same opcode and same var as this opcode */ /* JMPZ(X,L1), L1: JMPZ(X,L2) => JMPZ(X,L2), L1: JMPZ(X,L2) */ target = ZEND_OP2_JMP_ADDR(target); - CHECK_JMP(target, done_jmp_optimization); + CHECK_LOOP(target); ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target); } else if (target->opcode == INV_COND(opline->opcode) && SAME_VAR(opline->op1, target->op1)) { @@ -146,7 +168,10 @@ void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx) /* JMPNZ_EX */ target = ZEND_OFFSET_TO_OPLINE(target, target->extended_value); } - CHECK_JMP(target, done_jmp_optimization); + CHECK_LOOP(target); + ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target); + } else if (target->opcode == ZEND_JMP) { + target = target + 1; ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target); } else { break; @@ -177,7 +202,7 @@ void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx) /* plain JMP */ /* JMPZ_EX(X,L1), L1: JMP(L2) => JMPZ_EX(X,L2), L1: JMP(L2) */ target = ZEND_OP1_JMP_ADDR(target); - CHECK_JMP(target, done_jmp_optimization); + CHECK_LOOP(target); ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target); } else if (target->opcode == opline->opcode-3 && (SAME_VAR(target->op1, opline->result) || @@ -185,7 +210,7 @@ void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx) /* convert T=JMPZ_EX(X,L1), L1: JMPZ(T,L2) to JMPZ_EX(X,L2) */ target = ZEND_OP2_JMP_ADDR(target); - CHECK_JMP(target, done_jmp_optimization); + CHECK_LOOP(target); ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target); } else if (target->opcode == opline->opcode && (SAME_VAR(target->op1, opline->result) || @@ -194,7 +219,7 @@ void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx) /* convert T=JMPZ_EX(X,L1), L1: T=JMPZ_EX(T,L2) to JMPZ_EX(X,L2) */ target = ZEND_OP2_JMP_ADDR(target); - CHECK_JMP(target, done_jmp_optimization); + CHECK_LOOP(target); ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target); } else if (target->opcode == ZEND_JMPZNZ && SAME_VAR(target->op1, opline->result)) { @@ -205,7 +230,7 @@ void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx) /* JMPNZ_EX */ target = ZEND_OFFSET_TO_OPLINE(target, target->extended_value); } - CHECK_JMP(target, done_jmp_optimization); + CHECK_LOOP(target); ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target); } else if (target->opcode == INV_EX_COND(opline->opcode) && (SAME_VAR(target->op1, opline->result) || @@ -228,13 +253,25 @@ void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx) SAME_VAR(target->op1, opline->op1))) { /* convert Y = JMPZ_EX(X,L1), L1: Z = BOOL(Y) to Z = JMPZ_EX(X,L1+1) */ + + /* NOTE: This optimization pattern is not safe, but works, */ + /* because result of JMPZ_EX instruction */ + /* is not used on the following path and */ + /* should be used once on the branch path. */ + /* */ + /* The pattern works well only if jums processed in */ + /* direct order, otherwise it breakes JMPZ_EX */ + /* sequences too early. */ opline->result.var = target->result.var; target = target + 1; ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target); + } else if (target->opcode == ZEND_JMP) { + target = target + 1; + ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target); } else { break; } - } /* while */ + } /* convert L: T = JMPZ_EX X,L+1 to T = BOOL(X) */ if (target == opline + 1) { @@ -242,7 +279,6 @@ void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx) opline->op2.num = 0; break; } - break; case ZEND_JMPZNZ: @@ -250,22 +286,34 @@ void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx) /* JMPZNZ(X,L1,L2), L1: JMP(L3) => JMPZNZ(X,L3,L2), L1: JMP(L3) */ target = ZEND_OP2_JMP_ADDR(opline); - while (target->opcode == ZEND_JMP) { - target = ZEND_OP1_JMP_ADDR(target); - CHECK_JMP(target, continue_jmpznz_optimization); - ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target); + while (1) { + if (target->opcode == ZEND_JMP) { + target = ZEND_OP1_JMP_ADDR(target); + CHECK_LOOP(target); + ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target); + } else if (target->opcode == ZEND_JMP) { + target = target + 1; + ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target); + } else { + break; + } } -continue_jmpznz_optimization: - /* JMPZNZ(X,L1,L2), L2: JMP(L3) => JMPZNZ(X,L1,L3), L2: JMP(L3) */ + target = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); - while (target->opcode == ZEND_JMP) { - target = ZEND_OP1_JMP_ADDR(target); - CHECK_JMP(target, done_jmp_optimization); - opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, target); + while (1) { + if (target->opcode == ZEND_JMP) { + target = ZEND_OP1_JMP_ADDR(target); + CHECK_LOOP(target); + opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, target); + } else if (target->opcode == ZEND_JMP) { + target = target + 1; + opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, target); + } else { + break; + } } break; } -done_jmp_optimization: opline++; } free_alloca(jmp_hitlist, use_heap);