Cleanup JMP optimization pass

This commit is contained in:
Dmitry Stogov 2019-10-09 22:52:09 +03:00
parent b53b2f3e82
commit 78a1c8c811

View File

@ -32,15 +32,24 @@
#include "zend_vm.h" #include "zend_vm.h"
/* we use "jmp_hitlist" to avoid infinity loops during jmp optimization */ /* we use "jmp_hitlist" to avoid infinity loops during jmp optimization */
#define CHECK_JMP(target, label) \ static zend_always_inline int in_hitlist(zend_op *target, zend_op **jmp_hitlist, int jmp_hitlist_count)
do { \ {
for (i=0; i<jmp_hitlist_count; i++) { \ int i;
if (jmp_hitlist[i] == target) { \
goto label; \ for (i = 0; i < jmp_hitlist_count; i++) {
} \ if (jmp_hitlist[i] == target) {
} \ return 1;
}
}
return 0;
}
#define CHECK_LOOP(target) \
if (EXPECTED(!in_hitlist(target, jmp_hitlist, jmp_hitlist_count))) { \
jmp_hitlist[jmp_hitlist_count++] = target; \ jmp_hitlist[jmp_hitlist_count++] = target; \
} while (0) } else { \
break; \
}
void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx) void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx)
{ {
@ -49,7 +58,6 @@ void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx)
zend_op *target; zend_op *target;
zend_op **jmp_hitlist; zend_op **jmp_hitlist;
int jmp_hitlist_count; int jmp_hitlist_count;
int i;
ALLOCA_FLAG(use_heap); ALLOCA_FLAG(use_heap);
jmp_hitlist = (zend_op**)do_alloca(sizeof(zend_op*)*op_array->last, use_heap); jmp_hitlist = (zend_op**)do_alloca(sizeof(zend_op*)*op_array->last, 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 */ /* convert JMP L1 ... L1: JMP L2 to JMP L2 .. L1: JMP L2 */
target = ZEND_OP1_JMP_ADDR(opline); target = ZEND_OP1_JMP_ADDR(opline);
while (target->opcode == ZEND_JMP) { while (1) {
target = ZEND_OP1_JMP_ADDR(target); if (target->opcode == ZEND_JMP) {
CHECK_JMP(target, done_jmp_optimization); target = ZEND_OP1_JMP_ADDR(target);
ZEND_SET_OP_JMP_ADDR(opline, opline->op1, 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 */ /* 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; jmp_hitlist_count = 0;
target = ZEND_OP2_JMP_ADDR(opline); target = ZEND_OP2_JMP_ADDR(opline);
while (target->opcode == ZEND_JMP) { while (1) {
target = ZEND_OP1_JMP_ADDR(target); if (target->opcode == ZEND_JMP) {
CHECK_JMP(target, done_jmp_optimization); target = ZEND_OP1_JMP_ADDR(target);
ZEND_SET_OP_JMP_ADDR(opline, opline->op2, 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; break;
@ -123,14 +145,14 @@ void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx)
/* plain JMP */ /* plain JMP */
/* JMPZ(X,L1), L1: JMP(L2) => JMPZ(X,L2), L1: JMP(L2) */ /* JMPZ(X,L1), L1: JMP(L2) => JMPZ(X,L2), L1: JMP(L2) */
target = ZEND_OP1_JMP_ADDR(target); target = ZEND_OP1_JMP_ADDR(target);
CHECK_JMP(target, done_jmp_optimization); CHECK_LOOP(target);
ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target); ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target);
} else if (target->opcode == opline->opcode && } else if (target->opcode == opline->opcode &&
SAME_VAR(opline->op1, target->op1)) { SAME_VAR(opline->op1, target->op1)) {
/* same opcode and same var as this opcode */ /* same opcode and same var as this opcode */
/* JMPZ(X,L1), L1: JMPZ(X,L2) => JMPZ(X,L2), L1: JMPZ(X,L2) */ /* JMPZ(X,L1), L1: JMPZ(X,L2) => JMPZ(X,L2), L1: JMPZ(X,L2) */
target = ZEND_OP2_JMP_ADDR(target); target = ZEND_OP2_JMP_ADDR(target);
CHECK_JMP(target, done_jmp_optimization); CHECK_LOOP(target);
ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target); ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target);
} else if (target->opcode == INV_COND(opline->opcode) && } else if (target->opcode == INV_COND(opline->opcode) &&
SAME_VAR(opline->op1, target->op1)) { 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 */ /* JMPNZ_EX */
target = ZEND_OFFSET_TO_OPLINE(target, target->extended_value); 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); ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target);
} else { } else {
break; break;
@ -177,7 +202,7 @@ void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx)
/* plain JMP */ /* plain JMP */
/* JMPZ_EX(X,L1), L1: JMP(L2) => JMPZ_EX(X,L2), L1: JMP(L2) */ /* JMPZ_EX(X,L1), L1: JMP(L2) => JMPZ_EX(X,L2), L1: JMP(L2) */
target = ZEND_OP1_JMP_ADDR(target); target = ZEND_OP1_JMP_ADDR(target);
CHECK_JMP(target, done_jmp_optimization); CHECK_LOOP(target);
ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target); ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target);
} else if (target->opcode == opline->opcode-3 && } else if (target->opcode == opline->opcode-3 &&
(SAME_VAR(target->op1, opline->result) || (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 /* convert T=JMPZ_EX(X,L1), L1: JMPZ(T,L2) to
JMPZ_EX(X,L2) */ JMPZ_EX(X,L2) */
target = ZEND_OP2_JMP_ADDR(target); target = ZEND_OP2_JMP_ADDR(target);
CHECK_JMP(target, done_jmp_optimization); CHECK_LOOP(target);
ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target); ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target);
} else if (target->opcode == opline->opcode && } else if (target->opcode == opline->opcode &&
(SAME_VAR(target->op1, opline->result) || (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 /* convert T=JMPZ_EX(X,L1), L1: T=JMPZ_EX(T,L2) to
JMPZ_EX(X,L2) */ JMPZ_EX(X,L2) */
target = ZEND_OP2_JMP_ADDR(target); target = ZEND_OP2_JMP_ADDR(target);
CHECK_JMP(target, done_jmp_optimization); CHECK_LOOP(target);
ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target); ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target);
} else if (target->opcode == ZEND_JMPZNZ && } else if (target->opcode == ZEND_JMPZNZ &&
SAME_VAR(target->op1, opline->result)) { 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 */ /* JMPNZ_EX */
target = ZEND_OFFSET_TO_OPLINE(target, target->extended_value); 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); ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target);
} else if (target->opcode == INV_EX_COND(opline->opcode) && } else if (target->opcode == INV_EX_COND(opline->opcode) &&
(SAME_VAR(target->op1, opline->result) || (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))) { SAME_VAR(target->op1, opline->op1))) {
/* convert Y = JMPZ_EX(X,L1), L1: Z = BOOL(Y) to /* convert Y = JMPZ_EX(X,L1), L1: Z = BOOL(Y) to
Z = JMPZ_EX(X,L1+1) */ 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; opline->result.var = target->result.var;
target = target + 1; target = target + 1;
ZEND_SET_OP_JMP_ADDR(opline, opline->op2, 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 { } else {
break; break;
} }
} /* while */ }
/* convert L: T = JMPZ_EX X,L+1 to T = BOOL(X) */ /* convert L: T = JMPZ_EX X,L+1 to T = BOOL(X) */
if (target == opline + 1) { 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; opline->op2.num = 0;
break; break;
} }
break; break;
case ZEND_JMPZNZ: 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) */ /* JMPZNZ(X,L1,L2), L1: JMP(L3) => JMPZNZ(X,L3,L2), L1: JMP(L3) */
target = ZEND_OP2_JMP_ADDR(opline); target = ZEND_OP2_JMP_ADDR(opline);
while (target->opcode == ZEND_JMP) { while (1) {
target = ZEND_OP1_JMP_ADDR(target); if (target->opcode == ZEND_JMP) {
CHECK_JMP(target, continue_jmpznz_optimization); target = ZEND_OP1_JMP_ADDR(target);
ZEND_SET_OP_JMP_ADDR(opline, opline->op2, 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); target = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
while (target->opcode == ZEND_JMP) { while (1) {
target = ZEND_OP1_JMP_ADDR(target); if (target->opcode == ZEND_JMP) {
CHECK_JMP(target, done_jmp_optimization); target = ZEND_OP1_JMP_ADDR(target);
opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, 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; break;
} }
done_jmp_optimization:
opline++; opline++;
} }
free_alloca(jmp_hitlist, use_heap); free_alloca(jmp_hitlist, use_heap);