16
16
// ===----------------------------------------------------------------------===//
17
17
18
18
#include " CoroInternal.h"
19
+ #include " MaterializationUtils.h"
19
20
#include " SpillUtils.h"
20
21
#include " SuspendCrossingInfo.h"
21
22
#include " llvm/ADT/BitVector.h"
22
- #include " llvm/ADT/PostOrderIterator.h"
23
23
#include " llvm/ADT/ScopeExit.h"
24
24
#include " llvm/ADT/SmallString.h"
25
25
#include " llvm/Analysis/StackLifetime.h"
36
36
#include " llvm/Transforms/Utils/Local.h"
37
37
#include " llvm/Transforms/Utils/PromoteMemToReg.h"
38
38
#include < algorithm>
39
- #include < deque>
40
39
#include < optional>
41
40
42
41
using namespace llvm ;
43
42
44
43
extern cl::opt<bool > UseNewDbgInfoFormat;
45
44
46
- // The "coro-suspend-crossing" flag is very noisy. There is another debug type,
47
- // "coro-frame", which results in leaner debug spew.
48
- #define DEBUG_TYPE " coro-suspend-crossing"
49
-
50
- namespace {
51
-
52
- // RematGraph is used to construct a DAG for rematerializable instructions
53
- // When the constructor is invoked with a candidate instruction (which is
54
- // materializable) it builds a DAG of materializable instructions from that
55
- // point.
56
- // Typically, for each instruction identified as re-materializable across a
57
- // suspend point, a RematGraph will be created.
58
- struct RematGraph {
59
- // Each RematNode in the graph contains the edges to instructions providing
60
- // operands in the current node.
61
- struct RematNode {
62
- Instruction *Node;
63
- SmallVector<RematNode *> Operands;
64
- RematNode () = default ;
65
- RematNode (Instruction *V) : Node(V) {}
66
- };
67
-
68
- RematNode *EntryNode;
69
- using RematNodeMap =
70
- SmallMapVector<Instruction *, std::unique_ptr<RematNode>, 8 >;
71
- RematNodeMap Remats;
72
- const std::function<bool (Instruction &)> &MaterializableCallback;
73
- SuspendCrossingInfo &Checker;
74
-
75
- RematGraph (const std::function<bool (Instruction &)> &MaterializableCallback,
76
- Instruction *I, SuspendCrossingInfo &Checker)
77
- : MaterializableCallback(MaterializableCallback), Checker(Checker) {
78
- std::unique_ptr<RematNode> FirstNode = std::make_unique<RematNode>(I);
79
- EntryNode = FirstNode.get ();
80
- std::deque<std::unique_ptr<RematNode>> WorkList;
81
- addNode (std::move (FirstNode), WorkList, cast<User>(I));
82
- while (WorkList.size ()) {
83
- std::unique_ptr<RematNode> N = std::move (WorkList.front ());
84
- WorkList.pop_front ();
85
- addNode (std::move (N), WorkList, cast<User>(I));
86
- }
87
- }
88
-
89
- void addNode (std::unique_ptr<RematNode> NUPtr,
90
- std::deque<std::unique_ptr<RematNode>> &WorkList,
91
- User *FirstUse) {
92
- RematNode *N = NUPtr.get ();
93
- if (Remats.count (N->Node ))
94
- return ;
95
-
96
- // We haven't see this node yet - add to the list
97
- Remats[N->Node ] = std::move (NUPtr);
98
- for (auto &Def : N->Node ->operands ()) {
99
- Instruction *D = dyn_cast<Instruction>(Def.get ());
100
- if (!D || !MaterializableCallback (*D) ||
101
- !Checker.isDefinitionAcrossSuspend (*D, FirstUse))
102
- continue ;
103
-
104
- if (Remats.count (D)) {
105
- // Already have this in the graph
106
- N->Operands .push_back (Remats[D].get ());
107
- continue ;
108
- }
109
-
110
- bool NoMatch = true ;
111
- for (auto &I : WorkList) {
112
- if (I->Node == D) {
113
- NoMatch = false ;
114
- N->Operands .push_back (I.get ());
115
- break ;
116
- }
117
- }
118
- if (NoMatch) {
119
- // Create a new node
120
- std::unique_ptr<RematNode> ChildNode = std::make_unique<RematNode>(D);
121
- N->Operands .push_back (ChildNode.get ());
122
- WorkList.push_back (std::move (ChildNode));
123
- }
124
- }
125
- }
126
-
127
- #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
128
- static std::string getBasicBlockLabel (const BasicBlock *BB) {
129
- if (BB->hasName ())
130
- return BB->getName ().str ();
131
-
132
- std::string S;
133
- raw_string_ostream OS (S);
134
- BB->printAsOperand (OS, false );
135
- return OS.str ().substr (1 );
136
- }
137
-
138
- void dump () const {
139
- dbgs () << " Entry (" ;
140
- dbgs () << getBasicBlockLabel (EntryNode->Node ->getParent ());
141
- dbgs () << " ) : " << *EntryNode->Node << " \n " ;
142
- for (auto &E : Remats) {
143
- dbgs () << *(E.first ) << " \n " ;
144
- for (RematNode *U : E.second ->Operands )
145
- dbgs () << " " << *U->Node << " \n " ;
146
- }
147
- }
148
- #endif
149
- };
150
- } // end anonymous namespace
151
-
152
- namespace llvm {
153
-
154
- template <> struct GraphTraits <RematGraph *> {
155
- using NodeRef = RematGraph::RematNode *;
156
- using ChildIteratorType = RematGraph::RematNode **;
157
-
158
- static NodeRef getEntryNode (RematGraph *G) { return G->EntryNode ; }
159
- static ChildIteratorType child_begin (NodeRef N) {
160
- return N->Operands .begin ();
161
- }
162
- static ChildIteratorType child_end (NodeRef N) { return N->Operands .end (); }
163
- };
164
-
165
- } // end namespace llvm
166
-
167
- #undef DEBUG_TYPE // "coro-suspend-crossing"
168
45
#define DEBUG_TYPE " coro-frame"
169
46
170
47
namespace {
@@ -268,15 +145,6 @@ static void dumpSpills(StringRef Title, const coro::SpillInfo &Spills) {
268
145
I->dump ();
269
146
}
270
147
}
271
- static void dumpRemats (
272
- StringRef Title,
273
- const SmallMapVector<Instruction *, std::unique_ptr<RematGraph>, 8 > &RM) {
274
- dbgs () << " ------------- " << Title << " --------------\n " ;
275
- for (const auto &E : RM) {
276
- E.second ->dump ();
277
- dbgs () << " --\n " ;
278
- }
279
- }
280
148
281
149
static void dumpAllocas (const SmallVectorImpl<coro::AllocaInfo> &Allocas) {
282
150
dbgs () << " ------------- Allocas --------------\n " ;
@@ -1634,93 +1502,6 @@ static void rewritePHIs(Function &F) {
1634
1502
rewritePHIs (*BB);
1635
1503
}
1636
1504
1637
- // / Default materializable callback
1638
- // Check for instructions that we can recreate on resume as opposed to spill
1639
- // the result into a coroutine frame.
1640
- bool coro::defaultMaterializable (Instruction &V) {
1641
- return (isa<CastInst>(&V) || isa<GetElementPtrInst>(&V) ||
1642
- isa<BinaryOperator>(&V) || isa<CmpInst>(&V) || isa<SelectInst>(&V));
1643
- }
1644
-
1645
- // For each instruction identified as materializable across the suspend point,
1646
- // and its associated DAG of other rematerializable instructions,
1647
- // recreate the DAG of instructions after the suspend point.
1648
- static void rewriteMaterializableInstructions (
1649
- const SmallMapVector<Instruction *, std::unique_ptr<RematGraph>, 8 >
1650
- &AllRemats) {
1651
- // This has to be done in 2 phases
1652
- // Do the remats and record the required defs to be replaced in the
1653
- // original use instructions
1654
- // Once all the remats are complete, replace the uses in the final
1655
- // instructions with the new defs
1656
- typedef struct {
1657
- Instruction *Use;
1658
- Instruction *Def;
1659
- Instruction *Remat;
1660
- } ProcessNode;
1661
-
1662
- SmallVector<ProcessNode> FinalInstructionsToProcess;
1663
-
1664
- for (const auto &E : AllRemats) {
1665
- Instruction *Use = E.first ;
1666
- Instruction *CurrentMaterialization = nullptr ;
1667
- RematGraph *RG = E.second .get ();
1668
- ReversePostOrderTraversal<RematGraph *> RPOT (RG);
1669
- SmallVector<Instruction *> InstructionsToProcess;
1670
-
1671
- // If the target use is actually a suspend instruction then we have to
1672
- // insert the remats into the end of the predecessor (there should only be
1673
- // one). This is so that suspend blocks always have the suspend instruction
1674
- // as the first instruction.
1675
- auto InsertPoint = &*Use->getParent ()->getFirstInsertionPt ();
1676
- if (isa<AnyCoroSuspendInst>(Use)) {
1677
- BasicBlock *SuspendPredecessorBlock =
1678
- Use->getParent ()->getSinglePredecessor ();
1679
- assert (SuspendPredecessorBlock && " malformed coro suspend instruction" );
1680
- InsertPoint = SuspendPredecessorBlock->getTerminator ();
1681
- }
1682
-
1683
- // Note: skip the first instruction as this is the actual use that we're
1684
- // rematerializing everything for.
1685
- auto I = RPOT.begin ();
1686
- ++I;
1687
- for (; I != RPOT.end (); ++I) {
1688
- Instruction *D = (*I)->Node ;
1689
- CurrentMaterialization = D->clone ();
1690
- CurrentMaterialization->setName (D->getName ());
1691
- CurrentMaterialization->insertBefore (InsertPoint);
1692
- InsertPoint = CurrentMaterialization;
1693
-
1694
- // Replace all uses of Def in the instructions being added as part of this
1695
- // rematerialization group
1696
- for (auto &I : InstructionsToProcess)
1697
- I->replaceUsesOfWith (D, CurrentMaterialization);
1698
-
1699
- // Don't replace the final use at this point as this can cause problems
1700
- // for other materializations. Instead, for any final use that uses a
1701
- // define that's being rematerialized, record the replace values
1702
- for (unsigned i = 0 , E = Use->getNumOperands (); i != E; ++i)
1703
- if (Use->getOperand (i) == D) // Is this operand pointing to oldval?
1704
- FinalInstructionsToProcess.push_back (
1705
- {Use, D, CurrentMaterialization});
1706
-
1707
- InstructionsToProcess.push_back (CurrentMaterialization);
1708
- }
1709
- }
1710
-
1711
- // Finally, replace the uses with the defines that we've just rematerialized
1712
- for (auto &R : FinalInstructionsToProcess) {
1713
- if (auto *PN = dyn_cast<PHINode>(R.Use )) {
1714
- assert (PN->getNumIncomingValues () == 1 && " unexpected number of incoming "
1715
- " values in the PHINode" );
1716
- PN->replaceAllUsesWith (R.Remat );
1717
- PN->eraseFromParent ();
1718
- continue ;
1719
- }
1720
- R.Use ->replaceUsesOfWith (R.Def , R.Remat );
1721
- }
1722
- }
1723
-
1724
1505
// Splits the block at a particular instruction unless it is the first
1725
1506
// instruction in the block with a single predecessor.
1726
1507
static BasicBlock *splitBlockIfNotFirst (Instruction *I, const Twine &Name) {
@@ -1741,10 +1522,6 @@ static void splitAround(Instruction *I, const Twine &Name) {
1741
1522
splitBlockIfNotFirst (I->getNextNode (), " After" + Name);
1742
1523
}
1743
1524
1744
- static bool isSuspendBlock (BasicBlock *BB) {
1745
- return isa<AnyCoroSuspendInst>(BB->front ());
1746
- }
1747
-
1748
1525
// / After we split the coroutine, will the given basic block be along
1749
1526
// / an obvious exit path for the resumption function?
1750
1527
static bool willLeaveFunctionImmediatelyAfter (BasicBlock *BB,
@@ -1754,7 +1531,7 @@ static bool willLeaveFunctionImmediatelyAfter(BasicBlock *BB,
1754
1531
if (depth == 0 ) return false ;
1755
1532
1756
1533
// If this is a suspend block, we're about to exit the resumption function.
1757
- if (isSuspendBlock (BB))
1534
+ if (coro:: isSuspendBlock (BB))
1758
1535
return true ;
1759
1536
1760
1537
// Recurse into the successors.
@@ -1995,7 +1772,8 @@ static void sinkLifetimeStartMarkers(Function &F, coro::Shape &Shape,
1995
1772
DomSet.insert (&F.getEntryBlock ());
1996
1773
for (auto *CSI : Shape.CoroSuspends ) {
1997
1774
BasicBlock *SuspendBlock = CSI->getParent ();
1998
- assert (isSuspendBlock (SuspendBlock) && SuspendBlock->getSingleSuccessor () &&
1775
+ assert (coro::isSuspendBlock (SuspendBlock) &&
1776
+ SuspendBlock->getSingleSuccessor () &&
1999
1777
" should have split coro.suspend into its own block" );
2000
1778
DomSet.insert (SuspendBlock->getSingleSuccessor ());
2001
1779
}
@@ -2227,68 +2005,6 @@ void coro::salvageDebugInfo(
2227
2005
}
2228
2006
}
2229
2007
2230
- static void doRematerializations (
2231
- Function &F, SuspendCrossingInfo &Checker,
2232
- const std::function<bool (Instruction &)> &MaterializableCallback) {
2233
- if (F.hasOptNone ())
2234
- return ;
2235
-
2236
- coro::SpillInfo Spills;
2237
-
2238
- // See if there are materializable instructions across suspend points
2239
- // We record these as the starting point to also identify materializable
2240
- // defs of uses in these operations
2241
- for (Instruction &I : instructions (F)) {
2242
- if (!MaterializableCallback (I))
2243
- continue ;
2244
- for (User *U : I.users ())
2245
- if (Checker.isDefinitionAcrossSuspend (I, U))
2246
- Spills[&I].push_back (cast<Instruction>(U));
2247
- }
2248
-
2249
- // Process each of the identified rematerializable instructions
2250
- // and add predecessor instructions that can also be rematerialized.
2251
- // This is actually a graph of instructions since we could potentially
2252
- // have multiple uses of a def in the set of predecessor instructions.
2253
- // The approach here is to maintain a graph of instructions for each bottom
2254
- // level instruction - where we have a unique set of instructions (nodes)
2255
- // and edges between them. We then walk the graph in reverse post-dominator
2256
- // order to insert them past the suspend point, but ensure that ordering is
2257
- // correct. We also rely on CSE removing duplicate defs for remats of
2258
- // different instructions with a def in common (rather than maintaining more
2259
- // complex graphs for each suspend point)
2260
-
2261
- // We can do this by adding new nodes to the list for each suspend
2262
- // point. Then using standard GraphTraits to give a reverse post-order
2263
- // traversal when we insert the nodes after the suspend
2264
- SmallMapVector<Instruction *, std::unique_ptr<RematGraph>, 8 > AllRemats;
2265
- for (auto &E : Spills) {
2266
- for (Instruction *U : E.second ) {
2267
- // Don't process a user twice (this can happen if the instruction uses
2268
- // more than one rematerializable def)
2269
- if (AllRemats.count (U))
2270
- continue ;
2271
-
2272
- // Constructor creates the whole RematGraph for the given Use
2273
- auto RematUPtr =
2274
- std::make_unique<RematGraph>(MaterializableCallback, U, Checker);
2275
-
2276
- LLVM_DEBUG (dbgs () << " ***** Next remat group *****\n " ;
2277
- ReversePostOrderTraversal<RematGraph *> RPOT (RematUPtr.get ());
2278
- for (auto I = RPOT.begin (); I != RPOT.end ();
2279
- ++I) { (*I)->Node ->dump (); } dbgs ()
2280
- << " \n " ;);
2281
-
2282
- AllRemats[U] = std::move (RematUPtr);
2283
- }
2284
- }
2285
-
2286
- // Rewrite materializable instructions to be materialized at the use
2287
- // point.
2288
- LLVM_DEBUG (dumpRemats (" Materializations" , AllRemats));
2289
- rewriteMaterializableInstructions (AllRemats);
2290
- }
2291
-
2292
2008
void coro::normalizeCoroutine (Function &F, coro::Shape &Shape,
2293
2009
TargetTransformInfo &TTI) {
2294
2010
// Don't eliminate swifterror in async functions that won't be split.
@@ -2324,8 +2040,8 @@ void coro::normalizeCoroutine(Function &F, coro::Shape &Shape,
2324
2040
IRBuilder<> Builder (AsyncEnd);
2325
2041
SmallVector<Value *, 8 > Args (AsyncEnd->args ());
2326
2042
auto Arguments = ArrayRef<Value *>(Args).drop_front (3 );
2327
- auto *Call = createMustTailCall (AsyncEnd-> getDebugLoc (), MustTailCallFn,
2328
- TTI, Arguments, Builder);
2043
+ auto *Call = coro:: createMustTailCall (
2044
+ AsyncEnd-> getDebugLoc (), MustTailCallFn, TTI, Arguments, Builder);
2329
2045
splitAround (Call, " MustTailCall.Before.CoroEnd" );
2330
2046
}
2331
2047
}
0 commit comments