- 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)
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);
}
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 ???
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);
}
/* }}} */
+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];
}
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 */
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);
--- /dev/null
+--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"}