Skip to content

Commit f5ef1a8

Browse files
committed
Collect all jump optimisations in a single optimization pass.
Run this pass twice (after SCCP and after DCE).
1 parent 4d1ddeb commit f5ef1a8

File tree

3 files changed

+202
-245
lines changed

3 files changed

+202
-245
lines changed

ext/opcache/Optimizer/dce.c

Lines changed: 0 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -423,112 +423,6 @@ static zend_bool dce_instr(context *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
423423
return 1;
424424
}
425425

426-
// TODO Move this somewhere else (CFG simplification?)
427-
static int simplify_jumps(zend_ssa *ssa, zend_op_array *op_array) {
428-
int removed_ops = 0;
429-
zend_basic_block *block;
430-
FOREACH_BLOCK(block) {
431-
int block_num = block - ssa->cfg.blocks;
432-
zend_op *opline = &op_array->opcodes[block->start + block->len - 1];
433-
zend_ssa_op *ssa_op = &ssa->ops[block->start + block->len - 1];
434-
zval *op1;
435-
436-
if (block->len == 0) {
437-
continue;
438-
}
439-
440-
/* Convert jump-and-set into jump if result is not used */
441-
switch (opline->opcode) {
442-
case ZEND_JMPZ_EX:
443-
ZEND_ASSERT(ssa_op->result_def >= 0);
444-
if (ssa->vars[ssa_op->result_def].use_chain < 0
445-
&& ssa->vars[ssa_op->result_def].phi_use_chain == NULL) {
446-
opline->opcode = ZEND_JMPZ;
447-
opline->result_type = IS_UNUSED;
448-
zend_ssa_remove_result_def(ssa, ssa_op);
449-
}
450-
break;
451-
case ZEND_JMPNZ_EX:
452-
case ZEND_JMP_SET:
453-
ZEND_ASSERT(ssa_op->result_def >= 0);
454-
if (ssa->vars[ssa_op->result_def].use_chain < 0
455-
&& ssa->vars[ssa_op->result_def].phi_use_chain == NULL) {
456-
opline->opcode = ZEND_JMPNZ;
457-
opline->result_type = IS_UNUSED;
458-
zend_ssa_remove_result_def(ssa, ssa_op);
459-
}
460-
break;
461-
}
462-
463-
/* Convert jump-and-set to QM_ASSIGN/BOOL if the "else" branch is not taken. */
464-
switch (opline->opcode) {
465-
case ZEND_JMPZ_EX:
466-
case ZEND_JMPNZ_EX:
467-
if (block->successors_count == 1 && block->successors[0] != block_num + 1) {
468-
opline->opcode = ZEND_BOOL;
469-
}
470-
break;
471-
case ZEND_JMP_SET:
472-
case ZEND_COALESCE:
473-
if (block->successors_count == 1 && block->successors[0] != block_num + 1) {
474-
opline->opcode = ZEND_QM_ASSIGN;
475-
}
476-
break;
477-
}
478-
479-
if (opline->op1_type != IS_CONST) {
480-
continue;
481-
}
482-
483-
/* Convert constant conditional jump to unconditional jump */
484-
op1 = &ZEND_OP1_LITERAL(opline);
485-
switch (opline->opcode) {
486-
case ZEND_JMPZ:
487-
if (!zend_is_true(op1)) {
488-
literal_dtor(op1);
489-
opline->op1_type = IS_UNUSED;
490-
opline->op1.num = opline->op2.num;
491-
opline->opcode = ZEND_JMP;
492-
} else {
493-
MAKE_NOP(opline);
494-
removed_ops++;
495-
}
496-
break;
497-
case ZEND_JMPNZ:
498-
if (zend_is_true(op1)) {
499-
literal_dtor(op1);
500-
opline->op1_type = IS_UNUSED;
501-
opline->op1.num = opline->op2.num;
502-
opline->opcode = ZEND_JMP;
503-
} else {
504-
MAKE_NOP(opline);
505-
removed_ops++;
506-
}
507-
break;
508-
case ZEND_COALESCE:
509-
ZEND_ASSERT(ssa_op->result_def >= 0);
510-
if (ssa->vars[ssa_op->result_def].use_chain >= 0
511-
|| ssa->vars[ssa_op->result_def].phi_use_chain != NULL) {
512-
break;
513-
}
514-
515-
zend_ssa_remove_result_def(ssa, ssa_op);
516-
if (Z_TYPE_P(op1) != IS_NULL) {
517-
literal_dtor(op1);
518-
opline->op1_type = IS_UNUSED;
519-
opline->op1.num = opline->op2.num;
520-
opline->opcode = ZEND_JMP;
521-
opline->result_type = IS_UNUSED;
522-
} else {
523-
MAKE_NOP(opline);
524-
removed_ops++;
525-
}
526-
break;
527-
}
528-
} FOREACH_BLOCK_END();
529-
return removed_ops;
530-
}
531-
532426
static inline int get_common_phi_source(zend_ssa *ssa, zend_ssa_phi *phi) {
533427
int common_source = -1;
534428
int source;
@@ -772,7 +666,5 @@ int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, zend_bool reor
772666
}
773667
} FOREACH_PHI_END();
774668

775-
removed_ops += simplify_jumps(ssa, op_array);
776-
777669
return removed_ops;
778670
}

ext/opcache/Optimizer/dfa_pass.c

Lines changed: 190 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,27 @@ int zend_dfa_optimize_calls(zend_op_array *op_array, zend_ssa *ssa)
454454
return removed_ops;
455455
}
456456

457+
static zend_always_inline void take_successor_0(zend_ssa *ssa, int block_num, zend_basic_block *block)
458+
{
459+
if (block->successors_count == 2) {
460+
if (block->successors[1] != block->successors[0]) {
461+
zend_ssa_remove_predecessor(ssa, block_num, block->successors[1]);
462+
}
463+
block->successors_count = 1;
464+
}
465+
}
466+
467+
static zend_always_inline void take_successor_1(zend_ssa *ssa, int block_num, zend_basic_block *block)
468+
{
469+
if (block->successors_count == 2) {
470+
if (block->successors[1] != block->successors[0]) {
471+
zend_ssa_remove_predecessor(ssa, block_num, block->successors[0]);
472+
block->successors[0] = block->successors[1];
473+
}
474+
block->successors_count = 1;
475+
}
476+
}
477+
457478
static int zend_dfa_optimize_jmps(zend_op_array *op_array, zend_ssa *ssa)
458479
{
459480
int removed_ops = 0;
@@ -468,62 +489,188 @@ static int zend_dfa_optimize_jmps(zend_op_array *op_array, zend_ssa *ssa)
468489
zend_basic_block *block = &ssa->cfg.blocks[block_num];
469490
uint32_t op_num;
470491
zend_op *opline;
471-
zend_ssa_op *op;
492+
zend_ssa_op *ssa_op;
472493

473494
while (next_block_num < ssa->cfg.blocks_count
474495
&& !(ssa->cfg.blocks[next_block_num].flags & ZEND_BB_REACHABLE)) {
475496
next_block_num++;
476497
}
477498

478499
if (block->len) {
479-
if (block->successors_count == 2) {
480-
if (block->successors[0] == block->successors[1]) {
481-
op_num = block->start + block->len - 1;
482-
opline = op_array->opcodes + op_num;
483-
switch (opline->opcode) {
484-
case ZEND_JMPZ:
485-
case ZEND_JMPNZ:
486-
case ZEND_JMPZNZ:
487-
op = ssa->ops + op_num;
500+
op_num = block->start + block->len - 1;
501+
opline = op_array->opcodes + op_num;
502+
ssa_op = ssa->ops + op_num;
503+
504+
switch (opline->opcode) {
505+
case ZEND_JMP:
506+
optimize_jmp:
507+
if (block->successors[0] == next_block_num) {
508+
MAKE_NOP(opline);
509+
removed_ops++;
510+
}
511+
break;
512+
case ZEND_JMPZ:
513+
optimize_jmpz:
514+
if (opline->op1_type == IS_CONST) {
515+
if (zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) {
516+
MAKE_NOP(opline);
517+
removed_ops++;
518+
take_successor_1(ssa, block_num, block);
519+
} else {
520+
opline->opcode = ZEND_JMP;
521+
COPY_NODE(opline->op1, opline->op2);
522+
take_successor_0(ssa, block_num, block);
523+
goto optimize_jmp;
524+
}
525+
} else {
526+
if (block->successors[0] == next_block_num) {
527+
take_successor_0(ssa, block_num, block);
528+
if (opline->op1_type == IS_CV || !(OP1_INFO() & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
529+
zend_ssa_remove_instr(ssa, opline, ssa_op);
530+
removed_ops++;
531+
} else {
532+
opline->opcode = ZEND_FREE;
533+
opline->op2.num = 0;
534+
}
535+
}
536+
}
537+
break;
538+
case ZEND_JMPNZ:
539+
optimize_jmpnz:
540+
if (opline->op1_type == IS_CONST) {
541+
if (zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) {
542+
opline->opcode = ZEND_JMP;
543+
COPY_NODE(opline->op1, opline->op2);
544+
take_successor_0(ssa, block_num, block);
545+
goto optimize_jmp;
546+
} else {
547+
MAKE_NOP(opline);
548+
removed_ops++;
549+
take_successor_1(ssa, block_num, block);
550+
}
551+
} else {
552+
ZEND_ASSERT(block->successors_count == 2);
553+
if (block->successors[0] == next_block_num) {
554+
take_successor_0(ssa, block_num, block);
555+
if (opline->op1_type == IS_CV || !(OP1_INFO() & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
556+
zend_ssa_remove_instr(ssa, opline, ssa_op);
557+
removed_ops++;
558+
} else {
559+
opline->opcode = ZEND_FREE;
560+
opline->op2.num = 0;
561+
}
562+
}
563+
}
564+
break;
565+
case ZEND_JMPZNZ:
566+
if (opline->op1_type == IS_CONST) {
567+
if (zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) {
568+
zend_op *target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
569+
ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
570+
take_successor_1(ssa, block_num, block);
571+
} else {
572+
zend_op *target_opline = ZEND_OP2_JMP_ADDR(opline);
573+
ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
574+
take_successor_0(ssa, block_num, block);
575+
}
576+
opline->op1_type = IS_UNUSED;
577+
opline->extended_value = 0;
578+
opline->opcode = ZEND_JMP;
579+
goto optimize_jmp;
580+
} else {
581+
ZEND_ASSERT(block->successors_count == 2);
582+
if (block->successors[0] == block->successors[1]) {
583+
take_successor_0(ssa, block_num, block);
488584
if (block->successors[0] == next_block_num) {
489-
if (opline->op1_type & (IS_CV|IS_CONST)) {
490-
zend_ssa_remove_instr(ssa, opline, op);
491-
if (op->op1_use >= 0) {
492-
zend_ssa_unlink_use_chain(ssa, op_num, op->op1_use);
493-
op->op1_use = -1;
494-
op->op1_use_chain = -1;
495-
}
496-
MAKE_NOP(opline);
585+
if (opline->op1_type == IS_CV || !(OP1_INFO() & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
586+
zend_ssa_remove_instr(ssa, opline, ssa_op);
497587
removed_ops++;
498588
} else {
499589
opline->opcode = ZEND_FREE;
500590
opline->op2.num = 0;
501591
}
502-
} else {
503-
if (opline->op1_type & (IS_CV|IS_CONST)) {
504-
if (op->op1_use >= 0) {
505-
zend_ssa_unlink_use_chain(ssa, op_num, op->op1_use);
506-
op->op1_use = -1;
507-
op->op1_use_chain = -1;
508-
}
509-
opline->opcode = ZEND_JMP;
510-
opline->op1_type = IS_UNUSED;
511-
opline->op1.num = opline->op2.num;
592+
} else if (opline->op1_type == IS_CV || !(OP1_INFO() & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF))) {
593+
ZEND_ASSERT(ssa_op->op1_use >= 0);
594+
zend_ssa_unlink_use_chain(ssa, op_num, ssa_op->op1_use);
595+
ssa_op->op1_use = -1;
596+
ssa_op->op1_use_chain = -1;
597+
opline->opcode = ZEND_JMP;
598+
opline->op1_type = IS_UNUSED;
599+
opline->op1.num = opline->op2.num;
600+
goto optimize_jmp;
601+
}
602+
}
603+
}
604+
break;
605+
case ZEND_JMPZ_EX:
606+
if (ssa->vars[ssa_op->result_def].use_chain < 0
607+
&& ssa->vars[ssa_op->result_def].phi_use_chain == NULL) {
608+
opline->opcode = ZEND_JMPZ;
609+
opline->result_type = IS_UNUSED;
610+
zend_ssa_remove_result_def(ssa, ssa_op);
611+
goto optimize_jmpz;
612+
} else if (opline->op1_type == IS_CONST) {
613+
if (zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) {
614+
opline->opcode = ZEND_QM_ASSIGN;
615+
take_successor_1(ssa, block_num, block);
616+
}
617+
}
618+
break;
619+
case ZEND_JMPNZ_EX:
620+
if (ssa->vars[ssa_op->result_def].use_chain < 0
621+
&& ssa->vars[ssa_op->result_def].phi_use_chain == NULL) {
622+
opline->opcode = ZEND_JMPNZ;
623+
opline->result_type = IS_UNUSED;
624+
zend_ssa_remove_result_def(ssa, ssa_op);
625+
goto optimize_jmpnz;
626+
} else if (opline->op1_type == IS_CONST) {
627+
if (!zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) {
628+
opline->opcode = ZEND_QM_ASSIGN;
629+
take_successor_1(ssa, block_num, block);
630+
}
631+
}
632+
break;
633+
case ZEND_JMP_SET:
634+
if (ssa->vars[ssa_op->result_def].use_chain < 0
635+
&& ssa->vars[ssa_op->result_def].phi_use_chain == NULL) {
636+
opline->opcode = ZEND_JMPNZ;
637+
opline->result_type = IS_UNUSED;
638+
zend_ssa_remove_result_def(ssa, ssa_op);
639+
goto optimize_jmpnz;
640+
} else if (opline->op1_type == IS_CONST) {
641+
if (!zend_is_true(CT_CONSTANT_EX(op_array, opline->op1.constant))) {
642+
MAKE_NOP(opline);
643+
removed_ops++;
644+
take_successor_1(ssa, block_num, block);
645+
}
646+
}
647+
break;
648+
case ZEND_COALESCE:
649+
if (opline->op1_type == IS_CONST) {
650+
if (Z_TYPE_P(CT_CONSTANT_EX(op_array, opline->op1.constant)) == IS_NULL) {
651+
MAKE_NOP(opline);
652+
removed_ops++;
653+
take_successor_1(ssa, block_num, block);
654+
} else {
655+
zend_ssa_var *var = &ssa->vars[ssa_op->result_def];
656+
if (var->use_chain < 0 && var->phi_use_chain == NULL) {
657+
ssa_op->result_def = -1;
658+
if (opline->result_type & (IS_TMP_VAR|IS_VAR)) {
659+
zend_optimizer_remove_live_range_ex(op_array, opline->result.var, var->definition);
512660
}
661+
zend_ssa_unlink_use_chain(ssa, opline - op_array->opcodes, ssa_op->op1_use);
662+
ssa_op->op1_use = -1;
663+
ssa_op->op1_use_chain = -1;
664+
opline->opcode = ZEND_JMP;
665+
COPY_NODE(opline->op1, opline->op2);
666+
take_successor_0(ssa, block_num, block);
513667
}
514-
break;
515-
default:
516-
break;
668+
}
517669
}
518-
}
519-
} else if (block->successors_count == 1 && block->successors[0] == next_block_num) {
520-
op_num = block->start + block->len - 1;
521-
opline = op_array->opcodes + op_num;
522-
if (opline->opcode == ZEND_JMP) {
523-
MAKE_NOP(opline);
524-
removed_ops++;
525-
}
526-
}
670+
break;
671+
default:
672+
break;
673+
}
527674
}
528675

529676
block_num = next_block_num;
@@ -574,6 +721,9 @@ void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx
574721
if (dce_optimize_op_array(op_array, ssa, 0)) {
575722
remove_nops = 1;
576723
}
724+
if (zend_dfa_optimize_jmps(op_array, ssa)) {
725+
remove_nops = 1;
726+
}
577727
if (ctx->debug_level & ZEND_DUMP_AFTER_PASS_14) {
578728
zend_dump_op_array(op_array, ZEND_DUMP_SSA, "after dce pass", ssa);
579729
}

0 commit comments

Comments
 (0)