14
14
#include " swift/SILOptimizer/Analysis/DominanceAnalysis.h"
15
15
#include " swift/SIL/SILArgument.h"
16
16
#include " swift/AST/SemanticAttrs.h"
17
+ #include " swift/SIL/SILModule.h"
17
18
18
19
#define DEBUG_TYPE " cold-block-info"
19
20
20
21
using namespace swift ;
21
22
23
+ ColdBlockInfo::ColdBlockInfo (DominanceAnalysis *DA,
24
+ PostDominanceAnalysis *PDA) : DA(DA), PDA(PDA) {
25
+ LLVM_DEBUG (llvm::dbgs () << " ColdBlockInfo: constructed\n " );
26
+ }
27
+
28
+ std::optional<bool > ColdBlockInfo::queryIsCold (const SILBasicBlock *BB) {
29
+ auto result = ColdBlockMap.find (BB);
30
+ if (result != ColdBlockMap.end ()) {
31
+ return result->getSecond ();
32
+ }
33
+ return std::nullopt;
34
+ }
35
+
22
36
// / Peek through an extract of Bool.value.
23
37
static SILValue getCondition (SILValue C) {
24
38
if (auto *SEI = dyn_cast<StructExtractInst>(C)) {
@@ -29,6 +43,42 @@ static SILValue getCondition(SILValue C) {
29
43
return C;
30
44
}
31
45
46
+ // / A cold terminator is one where it's unlikely to be reached, which are
47
+ // / function exits that are less-common. A cold terminator implies a cold block.
48
+ // / - 'unreachable', as it's never executed.
49
+ // / - 'throw', if throws prediction is enabled.
50
+ static bool isColdTerminator (const TermInst *term) {
51
+ switch (term->getTermKind ()) {
52
+ case TermKind::AwaitAsyncContinuationInst:
53
+ case TermKind::BranchInst:
54
+ case TermKind::CondBranchInst:
55
+ case TermKind::SwitchValueInst:
56
+ case TermKind::SwitchEnumInst:
57
+ case TermKind::SwitchEnumAddrInst:
58
+ case TermKind::DynamicMethodBranchInst:
59
+ case TermKind::CheckedCastBranchInst:
60
+ case TermKind::CheckedCastAddrBranchInst:
61
+ case TermKind::TryApplyInst:
62
+ case TermKind::YieldInst:
63
+ case TermKind::ReturnInst:
64
+ return false ;
65
+ case TermKind::ThrowInst:
66
+ case TermKind::ThrowAddrInst:
67
+ case TermKind::UnwindInst:
68
+ return term->getModule ().getOptions ().EnableThrowsPrediction ;
69
+ case TermKind::UnreachableInst:
70
+ // For now, assume it's always cold, since it's executed once.
71
+ // Not all functions in the stdlib are properly annotated as a
72
+ // "known program termination point", so we don't use
73
+ // ApplySite::isCalleeKnownProgramTerminationPoint.
74
+ return term->getModule ().getOptions ().EnableNoReturnCold ;
75
+ }
76
+ }
77
+ static bool hasColdTerminator (const SILBasicBlock *bb) {
78
+ assert (bb);
79
+ return isColdTerminator (bb->getTerminator ());
80
+ }
81
+
32
82
// / \return a BranchHint if this conditional's likely value can be inferred.
33
83
ColdBlockInfo::BranchHint ColdBlockInfo::getBranchHint (SILValue Cond,
34
84
int recursionDepth) {
@@ -135,6 +185,36 @@ static std::optional<bool> isColdProfiledEdge(const SILBasicBlock *FromBB,
135
185
return takenProbability < WARM_EDGE_MINIMUM;
136
186
}
137
187
188
+ void ColdBlockInfo::analyzeExits (SILFunction *Fn) {
189
+ assert (Fn);
190
+ LLVM_DEBUG (llvm::dbgs ()
191
+ << " ColdBlockInfo: analyzeExits of " << Fn->getName () << " \n " );
192
+
193
+ PostDominanceInfo *info = PDA->get (Fn);
194
+ auto *node = info->getRootNode ();
195
+ assert (info->isVirtualRoot (node));
196
+
197
+ SmallVector<SILBasicBlock *, 8 > scratch;
198
+ for (auto exitNode : node->children ()) {
199
+ auto exitBB = exitNode->getBlock ();
200
+
201
+ if (!hasColdTerminator (exitBB))
202
+ continue ;
203
+
204
+ ASSERT (exitBB->getNumSuccessors () == 0 && " crucial assumption must hold" );
205
+
206
+ // Mark all blocks post-dominated by this cold-exit block as cold.
207
+ info->getDescendants (exitBB, scratch);
208
+ for (auto dominatedBB : scratch) {
209
+ ColdBlockMap[dominatedBB] = true ;
210
+ LLVM_DEBUG (llvm::dbgs ()
211
+ << " ColdBlockInfo(" << Fn->getName ()
212
+ << " ): analyzeExits says bb"
213
+ << dominatedBB->getDebugID () << " is cold\n " );
214
+ }
215
+ }
216
+ }
217
+
138
218
// / \return true if the CFG path FromBB->*->ToBB is either
139
219
// / 1. directly gated by a _slowPath branch hint.
140
220
// / 2. has a low probability of being taken according to profile information.
@@ -147,6 +227,8 @@ bool ColdBlockInfo::isSlowPath(const SILBasicBlock *FromBB,
147
227
(void )DT;
148
228
#endif
149
229
230
+ assert (!queryIsCold (ToBB) && " already known temperature?" );
231
+
150
232
// Is this the edge FromBB->ToBB?
151
233
bool isDirectEdge = false ;
152
234
for (auto *succ : FromBB->getSuccessorBlocks ()) {
@@ -173,20 +255,28 @@ bool ColdBlockInfo::isSlowPath(const SILBasicBlock *FromBB,
173
255
return ToBB == CBI->getTrueBB ();
174
256
}
175
257
}
176
- }
177
258
178
- // Should we give-up here?
179
- if (recursionDepth > RecursionDepthLimit)
180
- return false ;
259
+ } else if (recursionDepth <= RecursionDepthLimit) {
260
+ // If ToBB only has cold predecessors, then the path to it is cold.
261
+ bool coldPredecessors =
262
+ llvm::all_of (ToBB->getPredecessorBlocks (), [&](auto *predBB) {
263
+ return isCold (predBB, recursionDepth+1 );
264
+ });
265
+
266
+ if (coldPredecessors)
267
+ return true ;
181
268
182
- for (auto predBB : ToBB->getPredecessorBlocks ()) {
183
- if (!isCold (predBB, recursionDepth + 1 ))
184
- return false ;
269
+ // If FromBB only has cold successors, then the path from it is cold.
270
+ bool coldSuccessors =
271
+ llvm::all_of (FromBB->getSuccessorBlocks (), [&](auto *succBB) {
272
+ return isCold (succBB, recursionDepth+1 );
273
+ });
274
+
275
+ if (coldSuccessors)
276
+ return true ;
185
277
}
186
278
187
- // All predecessors of ToBB are cold, so that implies all paths
188
- // FromBB->*->ToBB are "slow".
189
- return true ;
279
+ return false ; // No reason to believe the path is slow.
190
280
}
191
281
192
282
// / \return true if the given block is dominated by a _slowPath branch hint.
@@ -212,6 +302,23 @@ bool ColdBlockInfo::isCold(const SILBasicBlock *BB, int recursionDepth) {
212
302
DomChain.push_back (BB);
213
303
bool IsCold = false ;
214
304
Node = Node->getIDom ();
305
+
306
+ // Special case: the entry block
307
+ if (BB->isEntry ()) {
308
+ assert (!Node && " entry is dominated?" );
309
+ // If the entry block's successors are all cold, then it's cold.
310
+ if (BB->getNumSuccessors () > 0 ) {
311
+ IsCold = llvm::all_of (BB->getSuccessorBlocks (), [&](auto *succBB) {
312
+ return isCold (succBB, recursionDepth);
313
+ });
314
+ } else {
315
+ // Without any successors, we can't infer the entry block is cold, unless
316
+ // if it exits in an unusual way.
317
+ IsCold = hasColdTerminator (BB);
318
+ }
319
+ }
320
+
321
+ // Check the blocks dominating BB to see if they're cold.
215
322
while (Node) {
216
323
auto fromBB = Node->getBlock ();
217
324
auto toBB = DomChain.back ();
@@ -220,10 +327,9 @@ bool ColdBlockInfo::isCold(const SILBasicBlock *BB, int recursionDepth) {
220
327
break ;
221
328
}
222
329
223
- // If a dominator of BB is cold, then BB is cold.
224
- auto I = ColdBlockMap.find (fromBB);
225
- if (I != ColdBlockMap.end ()) {
226
- IsCold = I->second ;
330
+ // If a dominator of BB has a temperature, propagate it.
331
+ if (auto temp = queryIsCold (fromBB)) {
332
+ IsCold = *temp;
227
333
break ;
228
334
}
229
335
@@ -234,12 +340,10 @@ bool ColdBlockInfo::isCold(const SILBasicBlock *BB, int recursionDepth) {
234
340
235
341
for (auto *ChainBB : DomChain) {
236
342
ColdBlockMap[ChainBB] = IsCold;
237
-
238
- if (IsCold) {
239
- LLVM_DEBUG (llvm::dbgs ()
240
- << " ColdBlockInfo(" << BB->getParent ()->getName () << " ): "
241
- << " bb" << ChainBB->getDebugID () << " is cold\n " );
242
- }
343
+ LLVM_DEBUG (llvm::dbgs ()
344
+ << " ColdBlockInfo(" << BB->getParent ()->getName () << " ): "
345
+ << " bb" << ChainBB->getDebugID ()
346
+ << " is " << (IsCold ? " cold\n " : " warm\n " ));
243
347
}
244
348
245
349
return IsCold;
0 commit comments