1616// ===----------------------------------------------------------------------===//
1717
1818#include " CoroInternal.h"
19+ #include " MaterializationUtils.h"
1920#include " SpillUtils.h"
2021#include " SuspendCrossingInfo.h"
2122#include " llvm/ADT/BitVector.h"
22- #include " llvm/ADT/PostOrderIterator.h"
2323#include " llvm/ADT/ScopeExit.h"
2424#include " llvm/ADT/SmallString.h"
2525#include " llvm/Analysis/StackLifetime.h"
3636#include " llvm/Transforms/Utils/Local.h"
3737#include " llvm/Transforms/Utils/PromoteMemToReg.h"
3838#include < algorithm>
39- #include < deque>
4039#include < optional>
4140
4241using namespace llvm ;
4342
4443extern cl::opt<bool > UseNewDbgInfoFormat;
4544
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"
16845#define DEBUG_TYPE " coro-frame"
16946
17047namespace {
@@ -268,15 +145,6 @@ static void dumpSpills(StringRef Title, const coro::SpillInfo &Spills) {
268145 I->dump ();
269146 }
270147}
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- }
280148
281149static void dumpAllocas (const SmallVectorImpl<coro::AllocaInfo> &Allocas) {
282150 dbgs () << " ------------- Allocas --------------\n " ;
@@ -1634,93 +1502,6 @@ static void rewritePHIs(Function &F) {
16341502 rewritePHIs (*BB);
16351503}
16361504
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-
17241505// Splits the block at a particular instruction unless it is the first
17251506// instruction in the block with a single predecessor.
17261507static BasicBlock *splitBlockIfNotFirst (Instruction *I, const Twine &Name) {
@@ -1741,10 +1522,6 @@ static void splitAround(Instruction *I, const Twine &Name) {
17411522 splitBlockIfNotFirst (I->getNextNode (), " After" + Name);
17421523}
17431524
1744- static bool isSuspendBlock (BasicBlock *BB) {
1745- return isa<AnyCoroSuspendInst>(BB->front ());
1746- }
1747-
17481525// / After we split the coroutine, will the given basic block be along
17491526// / an obvious exit path for the resumption function?
17501527static bool willLeaveFunctionImmediatelyAfter (BasicBlock *BB,
@@ -1754,7 +1531,7 @@ static bool willLeaveFunctionImmediatelyAfter(BasicBlock *BB,
17541531 if (depth == 0 ) return false ;
17551532
17561533 // If this is a suspend block, we're about to exit the resumption function.
1757- if (isSuspendBlock (BB))
1534+ if (coro:: isSuspendBlock (BB))
17581535 return true ;
17591536
17601537 // Recurse into the successors.
@@ -1995,7 +1772,8 @@ static void sinkLifetimeStartMarkers(Function &F, coro::Shape &Shape,
19951772 DomSet.insert (&F.getEntryBlock ());
19961773 for (auto *CSI : Shape.CoroSuspends ) {
19971774 BasicBlock *SuspendBlock = CSI->getParent ();
1998- assert (isSuspendBlock (SuspendBlock) && SuspendBlock->getSingleSuccessor () &&
1775+ assert (coro::isSuspendBlock (SuspendBlock) &&
1776+ SuspendBlock->getSingleSuccessor () &&
19991777 " should have split coro.suspend into its own block" );
20001778 DomSet.insert (SuspendBlock->getSingleSuccessor ());
20011779 }
@@ -2227,68 +2005,6 @@ void coro::salvageDebugInfo(
22272005 }
22282006}
22292007
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-
22922008void coro::normalizeCoroutine (Function &F, coro::Shape &Shape,
22932009 TargetTransformInfo &TTI) {
22942010 // Don't eliminate swifterror in async functions that won't be split.
@@ -2324,8 +2040,8 @@ void coro::normalizeCoroutine(Function &F, coro::Shape &Shape,
23242040 IRBuilder<> Builder (AsyncEnd);
23252041 SmallVector<Value *, 8 > Args (AsyncEnd->args ());
23262042 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);
23292045 splitAround (Call, " MustTailCall.Before.CoroEnd" );
23302046 }
23312047 }
0 commit comments