Skip to content

Commit 5f6172f

Browse files
committed
[Transforms] Refactor CreateControlFlowHub (#103013)
CreateControlFlowHub is a method that redirects control flow edges from a set of incoming blocks to a set of outgoing blocks through a new set of "guard" blocks. This is now refactored into a separate file with one enhancement: The input to the method is now a set of branches rather than two sets of blocks. The original implementation reroutes every edge from incoming blocks to outgoing blocks. But it is possible that for some incoming block InBB, some successor S might be in the set of outgoing blocks, but that particular edge should not be rerouted. The new implementation makes this possible by allowing the user to specify the targets of each branch that need to be rerouted. This is needed when improving the implementation of FixIrreducible #101386. Current use in FixIrreducible does not demonstrate this finer control over the edges being rerouted. But in UnifyLoopExits, when only one successor of an exiting block is an exit block, this refinement now reroutes only the relevant control-flow through the edge; the non-exit successor is not rerouted. This results in fewer branches and PHI nodes in the hub.
1 parent 0534c4f commit 5f6172f

19 files changed

+585
-508
lines changed

llvm/include/llvm/Transforms/Utils/BasicBlockUtils.h

-75
Original file line numberDiff line numberDiff line change
@@ -602,81 +602,6 @@ bool SplitIndirectBrCriticalEdges(Function &F, bool IgnoreBlocksWithoutPHI,
602602
BranchProbabilityInfo *BPI = nullptr,
603603
BlockFrequencyInfo *BFI = nullptr);
604604

605-
/// Given a set of incoming and outgoing blocks, create a "hub" such that every
606-
/// edge from an incoming block InBB to an outgoing block OutBB is now split
607-
/// into two edges, one from InBB to the hub and another from the hub to
608-
/// OutBB. The hub consists of a series of guard blocks, one for each outgoing
609-
/// block. Each guard block conditionally branches to the corresponding outgoing
610-
/// block, or the next guard block in the chain. These guard blocks are returned
611-
/// in the argument vector.
612-
///
613-
/// Since the control flow edges from InBB to OutBB have now been replaced, the
614-
/// function also updates any PHINodes in OutBB. For each such PHINode, the
615-
/// operands corresponding to incoming blocks are moved to a new PHINode in the
616-
/// hub, and the hub is made an operand of the original PHINode.
617-
///
618-
/// Input CFG:
619-
/// ----------
620-
///
621-
/// Def
622-
/// |
623-
/// v
624-
/// In1 In2
625-
/// | |
626-
/// | |
627-
/// v v
628-
/// Foo ---> Out1 Out2
629-
/// |
630-
/// v
631-
/// Use
632-
///
633-
///
634-
/// Create hub: Incoming = {In1, In2}, Outgoing = {Out1, Out2}
635-
/// ----------------------------------------------------------
636-
///
637-
/// Def
638-
/// |
639-
/// v
640-
/// In1 In2 Foo
641-
/// | Hub | |
642-
/// | + - - | - - + |
643-
/// | ' v ' V
644-
/// +------> Guard1 -----> Out1
645-
/// ' | '
646-
/// ' v '
647-
/// ' Guard2 -----> Out2
648-
/// ' ' |
649-
/// + - - - - - + |
650-
/// v
651-
/// Use
652-
///
653-
/// Limitations:
654-
/// -----------
655-
/// 1. This assumes that all terminators in the CFG are direct branches (the
656-
/// "br" instruction). The presence of any other control flow such as
657-
/// indirectbr, switch or callbr will cause an assert.
658-
///
659-
/// 2. The updates to the PHINodes are not sufficient to restore SSA
660-
/// form. Consider a definition Def, its use Use, incoming block In2 and
661-
/// outgoing block Out2, such that:
662-
/// a. In2 is reachable from D or contains D.
663-
/// b. U is reachable from Out2 or is contained in Out2.
664-
/// c. U is not a PHINode if U is contained in Out2.
665-
///
666-
/// Clearly, Def dominates Out2 since the program is valid SSA. But when the
667-
/// hub is introduced, there is a new path through the hub along which Use is
668-
/// reachable from entry without passing through Def, and SSA is no longer
669-
/// valid. To fix this, we need to look at all the blocks post-dominated by
670-
/// the hub on the one hand, and dominated by Out2 on the other. This is left
671-
/// for the caller to accomplish, since each specific use of this function
672-
/// may have additional information which simplifies this fixup. For example,
673-
/// see restoreSSA() in the UnifyLoopExits pass.
674-
BasicBlock *CreateControlFlowHub(
675-
DomTreeUpdater *DTU, SmallVectorImpl<BasicBlock *> &GuardBlocks,
676-
const SetVector<BasicBlock *> &Predecessors,
677-
const SetVector<BasicBlock *> &Successors, const StringRef Prefix,
678-
std::optional<unsigned> MaxControlFlowBooleans = std::nullopt);
679-
680605
// Utility function for inverting branch condition and for swapping its
681606
// successors
682607
void InvertBranch(BranchInst *PBI, IRBuilderBase &Builder);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
//===- Transforms/Utils/ControlFlowUtils.h --------------------*- C++ -*---===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Utilities to manipulate the CFG and restore SSA for the new control flow.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_TRANSFORMS_UTILS_CONTROLFLOWUTILS_H
14+
#define LLVM_TRANSFORMS_UTILS_CONTROLFLOWUTILS_H
15+
16+
#include "llvm/ADT/SmallVector.h"
17+
#include "llvm/ADT/StringRef.h"
18+
19+
namespace llvm {
20+
21+
class BasicBlock;
22+
class DomTreeUpdater;
23+
24+
/// Given a set of branch descriptors [BB, Succ0, Succ1], create a "hub" such
25+
/// that the control flow from each BB to a successor is now split into two
26+
/// edges, one from BB to the hub and another from the hub to the successor. The
27+
/// hub consists of a series of guard blocks, one for each outgoing block. Each
28+
/// guard block conditionally branches to the corresponding outgoing block, or
29+
/// the next guard block in the chain. These guard blocks are returned in the
30+
/// argument vector.
31+
///
32+
/// This also updates any PHINodes in the successor. For each such PHINode, the
33+
/// operands corresponding to incoming blocks are moved to a new PHINode in the
34+
/// hub, and the hub is made an operand of the original PHINode.
35+
///
36+
/// Note that for some block BB with a conditional branch, it is not necessary
37+
/// that both successors are rerouted. The client specifies this by setting
38+
/// either Succ0 or Succ1 to nullptr, in which case, the corresponding successor
39+
/// is not rerouted.
40+
///
41+
/// Input CFG:
42+
/// ----------
43+
///
44+
/// Def
45+
/// |
46+
/// v
47+
/// In1 In2
48+
/// | |
49+
/// | |
50+
/// v v
51+
/// Foo ---> Out1 Out2
52+
/// |
53+
/// v
54+
/// Use
55+
///
56+
///
57+
/// Create hub: Incoming = {In1, In2}, Outgoing = {Out1, Out2}
58+
/// ----------------------------------------------------------
59+
///
60+
/// Def
61+
/// |
62+
/// v
63+
/// In1 In2 Foo
64+
/// | Hub | |
65+
/// | + - - | - - + |
66+
/// | ' v ' V
67+
/// +------> Guard1 -----> Out1
68+
/// ' | '
69+
/// ' v '
70+
/// ' Guard2 -----> Out2
71+
/// ' ' |
72+
/// + - - - - - + |
73+
/// v
74+
/// Use
75+
///
76+
/// Limitations:
77+
/// -----------
78+
/// 1. This assumes that all terminators in the CFG are direct branches (the
79+
/// "br" instruction). The presence of any other control flow such as
80+
/// indirectbr, switch or callbr will cause an assert.
81+
///
82+
/// 2. The updates to the PHINodes are not sufficient to restore SSA
83+
/// form. Consider a definition Def, its use Use, incoming block In2 and
84+
/// outgoing block Out2, such that:
85+
/// a. In2 is reachable from D or contains D.
86+
/// b. U is reachable from Out2 or is contained in Out2.
87+
/// c. U is not a PHINode if U is contained in Out2.
88+
///
89+
/// Clearly, Def dominates Out2 since the program is valid SSA. But when the
90+
/// hub is introduced, there is a new path through the hub along which Use is
91+
/// reachable from entry without passing through Def, and SSA is no longer
92+
/// valid. To fix this, we need to look at all the blocks post-dominated by
93+
/// the hub on the one hand, and dominated by Out2 on the other. This is left
94+
/// for the caller to accomplish, since each specific use of this function
95+
/// may have additional information which simplifies this fixup. For example,
96+
/// see restoreSSA() in the UnifyLoopExits pass.
97+
struct ControlFlowHub {
98+
struct BranchDescriptor {
99+
BasicBlock *BB;
100+
BasicBlock *Succ0;
101+
BasicBlock *Succ1;
102+
103+
BranchDescriptor(BasicBlock *BB, BasicBlock *Succ0, BasicBlock *Succ1)
104+
: BB(BB), Succ0(Succ0), Succ1(Succ1) {}
105+
};
106+
107+
void addBranch(BasicBlock *BB, BasicBlock *Succ0, BasicBlock *Succ1) {
108+
assert(BB);
109+
assert(Succ0 || Succ1);
110+
Branches.emplace_back(BB, Succ0, Succ1);
111+
}
112+
113+
BasicBlock *
114+
finalize(DomTreeUpdater *DTU, SmallVectorImpl<BasicBlock *> &GuardBlocks,
115+
const StringRef Prefix,
116+
std::optional<unsigned> MaxControlFlowBooleans = std::nullopt);
117+
118+
SmallVector<BranchDescriptor> Branches;
119+
};
120+
121+
} // end namespace llvm
122+
123+
#endif // LLVM_TRANSFORMS_UTILS_CONTROLFLOWUTILS_H

0 commit comments

Comments
 (0)