@@ -99,6 +99,20 @@ Register* chaseAssignOperand(Register* value) {
9999 return value;
100100}
101101
102+ Instr* collapseTrivialPhi (Phi& phi) {
103+ Register* value = phi.isTrivial ();
104+ if (value == nullptr ) {
105+ return nullptr ;
106+ }
107+ Register* output = phi.output ();
108+ // A trivial Phi that only references itself can never be initialized, so use
109+ // a LoadConst<Bottom> to signify that.
110+ if (chaseAssignOperand (value) == output) {
111+ return LoadConst::create (output, TBottom);
112+ }
113+ return Assign::create (output, value);
114+ }
115+
102116RegUses collectDirectRegUses (Function& func) {
103117 RegUses uses;
104118 for (auto & block : func.cfg .blocks ) {
@@ -616,42 +630,154 @@ void reflowTypes(Function& func, BasicBlock* start) {
616630 }
617631}
618632
619- void removeTrampolineBlocks (Function& func) {
620- auto cfg = &func.cfg ;
633+ namespace {
634+
635+ // Get the block that `block` unconditionally jumps to, or nullptr if it ends in
636+ // anything other than a Branch.
637+ BasicBlock* branchTarget (BasicBlock& block) {
638+ Instr* term = block.getTerminator ();
639+ if (term == nullptr || term->opcode () != Opcode::kBranch ) {
640+ return nullptr ;
641+ }
642+ return static_cast <Branch*>(term)->target ();
643+ }
644+
645+ // Absorb the sole successor of `block` into it, deleting the successor. Return
646+ // false if `block` doesn't end a linear A -> B pair.
647+ bool absorbSuccessor (Function& func, BasicBlock& block) {
648+ // Identify linear blocks, A -> B.
649+ BasicBlock* target = branchTarget (block);
650+ if (target == nullptr || target == &block || target->empty () ||
651+ target->inEdges ().size () != 1 ) {
652+ return false ;
653+ }
654+ // The entry block's instructions have to run first, so it can never be
655+ // absorbed into one of its own successors.
656+ if (target == func.cfg .entry_block ) {
657+ return false ;
658+ }
659+
660+ // Any phis in B are trivial because B only has one predecessor. They can't
661+ // move to the end of A as phis only live at the start of a block, so collapse
662+ // them into assignments in place.
663+ for (auto it = target->begin (); it != target->end ();) {
664+ Instr& instr = *it;
665+ ++it;
666+ if (!instr.isPhi ()) {
667+ break ;
668+ }
669+ Instr* new_instr = collapseTrivialPhi (static_cast <Phi&>(instr));
670+ JIT_THROW_IF (
671+ new_instr == nullptr ,
672+ " Non-trivial Phi '{}' in bb {} of {}, which only has one predecessor" ,
673+ instr,
674+ target->id ,
675+ func.fullname );
676+ target->replace (instr, *new_instr);
677+ delete &instr;
678+ }
679+
680+ // Drop the branch, then append all instructions from B onto A. The branch
681+ // has to go before B does, it owns the last edge pointing at B.
682+ Instr* branch = block.getTerminator ();
683+ branch->unlink ();
684+ delete branch;
685+ while (!target->empty ()) {
686+ block.append (target->pop_front ());
687+ }
688+
689+ // The successors of B might still have phis that refer to it. Retarget them
690+ // to A.
691+ Instr* new_term = block.getTerminator ();
692+ for (std::size_t i = 0 , n = new_term->numEdges (); i < n; ++i) {
693+ new_term->successor (i)->fixupPhis (target, &block);
694+ }
621695
622- std::vector<BasicBlock*> trampolines;
623- for (auto & block : cfg->blocks ) {
624- if (!block.isTrampoline ()) {
696+ // B can now be deleted.
697+ func.cfg .removeBlock (target);
698+ delete target;
699+ return true ;
700+ }
701+
702+ // A trampoline block does nothing but jump to another block. Snapshots are
703+ // ignored as they're only metadata.
704+ bool isTrampoline (BasicBlock& block) {
705+ for (Instr& instr : block) {
706+ if (instr.isSnapshot ()) {
625707 continue ;
626708 }
627- BasicBlock* succ = block.successor (0 );
628- // if this is the entry block and its successor has multiple
629- // predecessors, don't remove it; it's necessary to maintain isolated
630- // entries
631- if (&block == cfg->entry_block ) {
632- if (succ->inEdges ().size () > 1 ) {
633- continue ;
634- } else {
635- cfg->entry_block = succ;
636- }
709+ if (!instr.isBranch ()) {
710+ return false ;
637711 }
638- // Update all predecessors to jump directly to our successor
639- block.retargetPreds (succ);
640- // Finish splicing the trampoline out of the cfg
641- block.setSuccessor (0 , nullptr );
642- trampolines.emplace_back (&block);
712+ BasicBlock* succ = instr.successor (0 );
713+ // Don't treat a block as a trampoline if its successor has Phis, this
714+ // block may be necessary to pass a specific value to one of them. That's
715+ // correct but conservative: it's often safe to eliminate such trampolines,
716+ // but it needs more involved analysis.
717+ return succ != &block && (succ->empty () || !succ->front ().isPhi ());
643718 }
719+ // Empty block.
720+ return false ;
721+ }
644722
645- for (auto & block : trampolines) {
646- cfg->removeBlock (block);
647- delete block;
723+ // Splice a trampoline block out of the CFG by pointing all of its predecessors
724+ // at its successor, deleting the block. Return false if `block` isn't a
725+ // trampoline.
726+ //
727+ // This is the other half of a linear A -> B merge: it applies when B has other
728+ // predecessors and so can't be absorbed into A.
729+ bool spliceTrampoline (Function& func, BasicBlock& block) {
730+ // Keep the entry block around, it's needed to maintain an isolated entry.
731+ // When its successor only has one predecessor absorbSuccessor() handles it.
732+ if (&block == func.cfg .entry_block || !isTrampoline (block)) {
733+ return false ;
734+ }
735+
736+ block.retargetPreds (block.successor (0 ));
737+ block.setSuccessor (0 , nullptr );
738+ func.cfg .removeBlock (&block);
739+ delete █
740+ return true ;
741+ }
742+
743+ // Run a single merge pass over every block in the CFG, returning true if any
744+ // blocks were removed.
745+ bool mergeLinearBlocksOnce (Function& func) {
746+ bool changed = false ;
747+ for (auto it = func.cfg .blocks .begin (); it != func.cfg .blocks .end ();) {
748+ BasicBlock& block = *it;
749+
750+ // Keep absorbing successors, chains of them collapse into a single block.
751+ // This only ever unlinks the successor, never `block`, so the iterator
752+ // stays valid.
753+ while (absorbSuccessor (func, block)) {
754+ changed = true ;
755+ }
756+
757+ // Splicing deletes `block`, so step past it first.
758+ ++it;
759+ changed |= spliceTrampoline (func, block);
648760 }
761+ return changed;
762+ }
763+
764+ } // namespace
649765
650- simplifyRedundantCondBranches (cfg);
766+ bool mergeLinearBlocks (Function& func) {
767+ bool changed = false ;
768+ for (bool modified = true ; modified;) {
769+ // Folding CondBranch<X, X> into Branch<X> leaves X with one fewer
770+ // predecessor, which can make it a merge candidate. Splicing trampolines
771+ // out below is what tends to create these.
772+ simplifyRedundantCondBranches (&func.cfg );
773+ modified = mergeLinearBlocksOnce (func);
774+ changed |= modified;
775+ }
651776
652- if (trampolines. size () > 0 ) {
777+ if (changed ) {
653778 func.invalidateDomTree ();
654779 }
780+ return changed;
655781}
656782
657783bool removeUnreachableBlocks (Function& func) {
0 commit comments