mirror of
https://github.com/php/php-src.git
synced 2026-03-27 09:42:22 +01:00
Fixed incorect constant conditional jump elimination
This commit is contained in:
3
NEWS
3
NEWS
@@ -5,6 +5,9 @@ PHP NEWS
|
||||
- GD:
|
||||
. Fixed bug #75139 (libgd/gd_interpolation.c:1786: suspicious if ?). (cmb)
|
||||
|
||||
- Opcache
|
||||
. Fixed incorect constant conditional jump elimination. (Dmitry)
|
||||
|
||||
- OpenSSL
|
||||
. Automatically load OpenSSL configuration file. (Jakub Zelenka)
|
||||
|
||||
|
||||
@@ -195,6 +195,52 @@ static void zend_optimize_block(zend_basic_block *block, zend_op_array *op_array
|
||||
VAR_SOURCE(op1) = NULL;
|
||||
literal_dtor(&ZEND_OP1_LITERAL(src));
|
||||
MAKE_NOP(src);
|
||||
switch (opline->opcode) {
|
||||
case ZEND_JMPZ:
|
||||
if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
|
||||
MAKE_NOP(opline);
|
||||
DEL_SOURCE(block, block->successors[0]);
|
||||
block->successors_count = 1;
|
||||
block->successors[0] = block->successors[1];
|
||||
} else {
|
||||
opline->opcode = ZEND_JMP;
|
||||
COPY_NODE(opline->op1, opline->op2);
|
||||
DEL_SOURCE(block, block->successors[1]);
|
||||
block->successors_count = 1;
|
||||
}
|
||||
break;
|
||||
case ZEND_JMPNZ:
|
||||
if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
|
||||
opline->opcode = ZEND_JMP;
|
||||
COPY_NODE(opline->op1, opline->op2);
|
||||
DEL_SOURCE(block, block->successors[1]);
|
||||
block->successors_count = 1;
|
||||
} else {
|
||||
MAKE_NOP(opline);
|
||||
DEL_SOURCE(block, block->successors[0]);
|
||||
block->successors_count = 1;
|
||||
block->successors[0] = block->successors[1];
|
||||
}
|
||||
break;
|
||||
case ZEND_JMPZNZ:
|
||||
if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
|
||||
zend_op *target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
|
||||
ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
|
||||
DEL_SOURCE(block, block->successors[0]);
|
||||
block->successors[0] = block->successors[1];
|
||||
} else {
|
||||
zend_op *target_opline = ZEND_OP2_JMP_ADDR(opline);
|
||||
ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
|
||||
DEL_SOURCE(block, block->successors[0]);
|
||||
}
|
||||
block->successors_count = 1;
|
||||
opline->op1_type = IS_UNUSED;
|
||||
opline->extended_value = 0;
|
||||
opline->opcode = ZEND_JMP;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
zval_ptr_dtor_nogc(&c);
|
||||
}
|
||||
|
||||
@@ -234,6 +234,93 @@ static zend_bool try_replace_op1(
|
||||
zval zv;
|
||||
ZVAL_COPY(&zv, value);
|
||||
if (zend_optimizer_update_op1_const(ctx->scdf.op_array, opline, &zv)) {
|
||||
/* Reconstruct SSA */
|
||||
int num;
|
||||
zend_basic_block *block;
|
||||
|
||||
switch (opline->opcode) {
|
||||
case ZEND_JMPZ:
|
||||
if (zend_is_true(&zv)) {
|
||||
MAKE_NOP(opline);
|
||||
num = ctx->scdf.ssa->cfg.map[opline - ctx->scdf.op_array->opcodes];
|
||||
block = &ctx->scdf.ssa->cfg.blocks[num];
|
||||
if (block->successors_count == 2) {
|
||||
if (block->successors[1] != block->successors[0]) {
|
||||
zend_ssa_remove_predecessor(ctx->scdf.ssa, num, block->successors[0]);
|
||||
}
|
||||
block->successors_count = 1;
|
||||
block->successors[0] = block->successors[1];
|
||||
}
|
||||
} else {
|
||||
opline->opcode = ZEND_JMP;
|
||||
COPY_NODE(opline->op1, opline->op2);
|
||||
num = ctx->scdf.ssa->cfg.map[opline - ctx->scdf.op_array->opcodes];
|
||||
block = &ctx->scdf.ssa->cfg.blocks[num];
|
||||
if (block->successors_count == 2) {
|
||||
if (block->successors[1] != block->successors[0]) {
|
||||
zend_ssa_remove_predecessor(ctx->scdf.ssa, num, block->successors[1]);
|
||||
}
|
||||
block->successors_count = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_JMPNZ:
|
||||
if (zend_is_true(&zv)) {
|
||||
opline->opcode = ZEND_JMP;
|
||||
COPY_NODE(opline->op1, opline->op2);
|
||||
num = ctx->scdf.ssa->cfg.map[opline - ctx->scdf.op_array->opcodes];
|
||||
block = &ctx->scdf.ssa->cfg.blocks[num];
|
||||
if (block->successors_count == 2) {
|
||||
if (block->successors[1] != block->successors[0]) {
|
||||
zend_ssa_remove_predecessor(ctx->scdf.ssa, num, block->successors[1]);
|
||||
}
|
||||
block->successors_count = 1;
|
||||
}
|
||||
} else {
|
||||
MAKE_NOP(opline);
|
||||
num = ctx->scdf.ssa->cfg.map[opline - ctx->scdf.op_array->opcodes];
|
||||
block = &ctx->scdf.ssa->cfg.blocks[num];
|
||||
if (block->successors_count == 2) {
|
||||
if (block->successors[1] != block->successors[0]) {
|
||||
zend_ssa_remove_predecessor(ctx->scdf.ssa, num, block->successors[0]);
|
||||
}
|
||||
block->successors_count = 1;
|
||||
block->successors[0] = block->successors[1];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_JMPZNZ:
|
||||
if (zend_is_true(&zv)) {
|
||||
zend_op *target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
|
||||
ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
|
||||
num = ctx->scdf.ssa->cfg.map[opline - ctx->scdf.op_array->opcodes];
|
||||
block = &ctx->scdf.ssa->cfg.blocks[num];
|
||||
if (block->successors_count == 2) {
|
||||
if (block->successors[1] != block->successors[0]) {
|
||||
zend_ssa_remove_predecessor(ctx->scdf.ssa, num, block->successors[0]);
|
||||
}
|
||||
block->successors_count = 1;
|
||||
block->successors[0] = block->successors[1];
|
||||
}
|
||||
} else {
|
||||
zend_op *target_opline = ZEND_OP2_JMP_ADDR(opline);
|
||||
ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
|
||||
num = ctx->scdf.ssa->cfg.map[opline - ctx->scdf.op_array->opcodes];
|
||||
block = &ctx->scdf.ssa->cfg.blocks[num];
|
||||
if (block->successors_count == 2) {
|
||||
if (block->successors[1] != block->successors[0]) {
|
||||
zend_ssa_remove_predecessor(ctx->scdf.ssa, num, block->successors[1]);
|
||||
}
|
||||
block->successors_count = 1;
|
||||
}
|
||||
}
|
||||
opline->op1_type = IS_UNUSED;
|
||||
opline->extended_value = 0;
|
||||
opline->opcode = ZEND_JMP;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
} else {
|
||||
// TODO: check the following special cases ???
|
||||
|
||||
@@ -258,41 +258,7 @@ int zend_optimizer_update_op1_const(zend_op_array *op_array,
|
||||
zend_op *opline,
|
||||
zval *val)
|
||||
{
|
||||
zend_op *target_opline;
|
||||
|
||||
switch (opline->opcode) {
|
||||
case ZEND_JMPZ:
|
||||
if (zend_is_true(val)) {
|
||||
MAKE_NOP(opline);
|
||||
} else {
|
||||
opline->opcode = ZEND_JMP;
|
||||
COPY_NODE(opline->op1, opline->op2);
|
||||
opline->op2_type = IS_UNUSED;
|
||||
}
|
||||
zval_ptr_dtor_nogc(val);
|
||||
return 1;
|
||||
case ZEND_JMPNZ:
|
||||
if (zend_is_true(val)) {
|
||||
opline->opcode = ZEND_JMP;
|
||||
COPY_NODE(opline->op1, opline->op2);
|
||||
opline->op2_type = IS_UNUSED;
|
||||
} else {
|
||||
MAKE_NOP(opline);
|
||||
}
|
||||
zval_ptr_dtor_nogc(val);
|
||||
return 1;
|
||||
case ZEND_JMPZNZ:
|
||||
if (zend_is_true(val)) {
|
||||
target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
|
||||
} else {
|
||||
target_opline = ZEND_OP2_JMP_ADDR(opline);
|
||||
}
|
||||
ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
|
||||
opline->op1_type = IS_UNUSED;
|
||||
opline->extended_value = 0;
|
||||
opline->opcode = ZEND_JMP;
|
||||
zval_ptr_dtor_nogc(val);
|
||||
return 1;
|
||||
case ZEND_FREE:
|
||||
MAKE_NOP(opline);
|
||||
zval_ptr_dtor_nogc(val);
|
||||
|
||||
@@ -1339,6 +1339,52 @@ void zend_ssa_remove_uses_of_var(zend_ssa *ssa, int var_num) /* {{{ */
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
void zend_ssa_remove_predecessor(zend_ssa *ssa, int from, int to) /* {{{ */
|
||||
{
|
||||
zend_basic_block *next_block = &ssa->cfg.blocks[to];
|
||||
zend_ssa_block *next_ssa_block = &ssa->blocks[to];
|
||||
zend_ssa_phi *phi;
|
||||
int j;
|
||||
|
||||
/* Find at which predecessor offset this block is referenced */
|
||||
int pred_offset = -1;
|
||||
int *predecessors = &ssa->cfg.predecessors[next_block->predecessor_offset];
|
||||
|
||||
for (j = 0; j < next_block->predecessors_count; j++) {
|
||||
if (predecessors[j] == from) {
|
||||
pred_offset = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If there are duplicate successors, the predecessors may have been removed in
|
||||
* a previous iteration already. */
|
||||
if (pred_offset == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* For phis in successor blocks, remove the operands associated with this block */
|
||||
for (phi = next_ssa_block->phis; phi; phi = phi->next) {
|
||||
if (phi->pi >= 0) {
|
||||
if (phi->pi == from) {
|
||||
zend_ssa_remove_uses_of_var(ssa, phi->ssa_var);
|
||||
zend_ssa_remove_phi(ssa, phi);
|
||||
}
|
||||
} else {
|
||||
ZEND_ASSERT(phi->sources[pred_offset] >= 0);
|
||||
zend_ssa_remove_phi_source(ssa, phi, pred_offset, next_block->predecessors_count);
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove this predecessor */
|
||||
next_block->predecessors_count--;
|
||||
if (pred_offset < next_block->predecessors_count) {
|
||||
predecessors = &ssa->cfg.predecessors[next_block->predecessor_offset + pred_offset];
|
||||
memmove(predecessors, predecessors + 1, (next_block->predecessors_count - pred_offset) * sizeof(uint32_t));
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
void zend_ssa_remove_block(zend_op_array *op_array, zend_ssa *ssa, int i) /* {{{ */
|
||||
{
|
||||
zend_basic_block *block = &ssa->cfg.blocks[i];
|
||||
@@ -1369,45 +1415,7 @@ void zend_ssa_remove_block(zend_op_array *op_array, zend_ssa *ssa, int i) /* {{{
|
||||
}
|
||||
|
||||
for (s = 0; s < block->successors_count; s++) {
|
||||
zend_basic_block *next_block = &ssa->cfg.blocks[block->successors[s]];
|
||||
zend_ssa_block *next_ssa_block = &ssa->blocks[block->successors[s]];
|
||||
zend_ssa_phi *phi;
|
||||
|
||||
/* Find at which predecessor offset this block is referenced */
|
||||
int pred_offset = -1;
|
||||
predecessors = &ssa->cfg.predecessors[next_block->predecessor_offset];
|
||||
for (j = 0; j < next_block->predecessors_count; j++) {
|
||||
if (predecessors[j] == i) {
|
||||
pred_offset = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If there are duplicate successors, the predecessors may have been removed in
|
||||
* a previous iteration already. */
|
||||
if (pred_offset == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* For phis in successor blocks, remove the operands associated with this block */
|
||||
for (phi = next_ssa_block->phis; phi; phi = phi->next) {
|
||||
if (phi->pi >= 0) {
|
||||
if (phi->pi == i) {
|
||||
zend_ssa_remove_uses_of_var(ssa, phi->ssa_var);
|
||||
zend_ssa_remove_phi(ssa, phi);
|
||||
}
|
||||
} else {
|
||||
ZEND_ASSERT(phi->sources[pred_offset] >= 0);
|
||||
zend_ssa_remove_phi_source(ssa, phi, pred_offset, next_block->predecessors_count);
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove this predecessor */
|
||||
next_block->predecessors_count--;
|
||||
if (pred_offset < next_block->predecessors_count) {
|
||||
predecessors = &ssa->cfg.predecessors[next_block->predecessor_offset + pred_offset];
|
||||
memmove(predecessors, predecessors + 1, (next_block->predecessors_count - pred_offset) * sizeof(uint32_t));
|
||||
}
|
||||
zend_ssa_remove_predecessor(ssa, i, block->successors[s]);
|
||||
}
|
||||
|
||||
/* Remove successors of predecessors */
|
||||
|
||||
@@ -139,6 +139,7 @@ int zend_build_ssa(zend_arena **arena, const zend_script *script, const zend_op_
|
||||
int zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_array *op_array, zend_ssa *ssa);
|
||||
int zend_ssa_unlink_use_chain(zend_ssa *ssa, int op, int var);
|
||||
|
||||
void zend_ssa_remove_predecessor(zend_ssa *ssa, int from, int to);
|
||||
void zend_ssa_remove_instr(zend_ssa *ssa, zend_op *opline, zend_ssa_op *ssa_op);
|
||||
void zend_ssa_remove_phi(zend_ssa *ssa, zend_ssa_phi *phi);
|
||||
void zend_ssa_remove_uses_of_var(zend_ssa *ssa, int var_num);
|
||||
|
||||
18
ext/opcache/tests/jmp_elim_001.phpt
Normal file
18
ext/opcache/tests/jmp_elim_001.phpt
Normal file
@@ -0,0 +1,18 @@
|
||||
--TEST--
|
||||
Edge-cases in constant conditional jump elimination
|
||||
--SKIPIF--
|
||||
<?php if (PHP_INT_SIZE != 8) die("skip for machines with 64-bit longs"); ?>
|
||||
<?php require_once('skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
$webserver = "Apache";
|
||||
$cpuArc = "x86_64";
|
||||
$archName = (strstr($cpuArc, "64") || PHP_INT_SIZE === 8) ? "64" : "32";
|
||||
$info = array('os' => PHP_OS,
|
||||
'phpversion' => phpversion(),
|
||||
'arch' => $archName,
|
||||
'webserver' =>$webserver);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($info) . "\n";
|
||||
--EXPECT--
|
||||
{"os":"Linux","phpversion":"7.2.0-dev","arch":"64","webserver":"Apache"}
|
||||
Reference in New Issue
Block a user