Skip to content

Commit a314444

Browse files
committed
ColdBlockInfo: overhaul analysis pass
The old analysis pass doesn't take into account profile data, nor does it consider post-dominance. It primarily dealt with _fastPath/_slowPath. A block that is dominated by a cold block is itself cold. That's true whether it's forwards or backwards dominance. We can also consider a call to any `Never` returning function as a cold-exit, though the block(s) leading up to that call may be executed frequently because of concurrency. For now, I'm ignoring the concurrency case and assuming it's cold. To make use of this "no return" prediction, use the `-enable-noreturn-prediction` flag, which is currently off by default.
1 parent 1c3ef6c commit a314444

File tree

13 files changed

+597
-126
lines changed

13 files changed

+597
-126
lines changed

include/swift/AST/SILOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ class SILOptions {
122122
/// within the optimizer. This influences static branch prediction.
123123
bool EnableThrowsPrediction = false;
124124

125+
/// Controls whether to say that blocks ending in an 'unreachable' are cold.
126+
bool EnableNoReturnCold = false;
127+
125128
/// Should we run any SIL performance optimizations
126129
///
127130
/// Useful when you want to enable -O LLVM opts but not -O SIL opts.

include/swift/Basic/ProfileCounter.h

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ class ProfileCounter {
2727
uint64_t count;
2828

2929
public:
30-
explicit ProfileCounter() : count(UINT64_MAX) {}
31-
ProfileCounter(uint64_t Count) : count(Count) {
30+
explicit constexpr ProfileCounter() : count(UINT64_MAX) {}
31+
constexpr ProfileCounter(uint64_t Count) : count(Count) {
3232
if (Count == UINT64_MAX) {
3333
count = UINT64_MAX - 1;
3434
}
@@ -40,6 +40,22 @@ class ProfileCounter {
4040
return count;
4141
}
4242
explicit operator bool() const { return hasValue(); }
43+
44+
/// Saturating addition of another counter to this one, meaning that overflow
45+
/// is avoided. If overflow would have happened, this function returns true
46+
/// and the maximum representable value will be set in this counter.
47+
bool add_saturating(ProfileCounter other) {
48+
assert(hasValue() && other.hasValue());
49+
50+
// Will we go over the max representable value by adding other?
51+
if (count > ((UINT64_MAX-1) - other.count)) {
52+
count = UINT64_MAX - 1;
53+
return true;
54+
}
55+
56+
count += other.count;
57+
return false;
58+
}
4359
};
4460
} // end namespace swift
4561

include/swift/Option/FrontendOptions.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,11 @@ def enable_throws_prediction : Flag<["-"], "enable-throws-prediction">,
545545
def disable_throws_prediction : Flag<["-"], "disable-throws-prediction">,
546546
HelpText<"Disables optimization assumption that functions rarely throw errors.">;
547547

548+
def enable_noreturn_prediction : Flag<["-"], "enable-noreturn-prediction">,
549+
HelpText<"Enables optimization assumption that calls to no-return functions are cold.">;
550+
def disable_noreturn_prediction : Flag<["-"], "disable-noreturn-prediction">,
551+
HelpText<"Disables optimization assumption that calls to no-return functions are cold.">;
552+
548553
def disable_access_control : Flag<["-"], "disable-access-control">,
549554
HelpText<"Don't respect access control restrictions">;
550555
def enable_access_control : Flag<["-"], "enable-access-control">,

include/swift/SILOptimizer/Analysis/ColdBlockInfo.h

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,52 +15,70 @@
1515

1616
#include "llvm/ADT/DenseMap.h"
1717
#include "swift/SIL/SILValue.h"
18+
#include <bitset>
1819

1920
namespace swift {
2021
class DominanceAnalysis;
22+
class PostDominanceAnalysis;
2123
class SILBasicBlock;
24+
class CondBranchInst;
2225

2326
/// Cache a set of basic blocks that have been determined to be cold or hot.
2427
///
2528
/// This does not inherit from SILAnalysis because it is not worth preserving
2629
/// across passes.
2730
class ColdBlockInfo {
31+
public:
32+
// Represents the temperatures of edges flowing into a block.
33+
//
34+
// T = "top" -- both warm and cold edges
35+
// / \
36+
// Warm Cold
37+
// \ /
38+
// B = "bottom" -- neither warm or cold edges
39+
struct State {
40+
// Each state kind, as an integer, is its position in any bit vectors.
41+
enum Temperature {
42+
Warm = 0,
43+
Cold = 1
44+
};
45+
46+
// Number of states, excluding Top or Bottom, in this flow problem.
47+
static constexpr unsigned NumStates = 2;
48+
};
49+
50+
using Energy = std::bitset<State::NumStates>;
51+
52+
private:
2853
DominanceAnalysis *DA;
54+
PostDominanceAnalysis *PDA;
2955

30-
/// Each block in this map has been determined to be either cold or hot.
31-
llvm::DenseMap<const SILBasicBlock*, bool> ColdBlockMap;
56+
/// Each block in this map has been determined to be cold and/or warm.
57+
llvm::DenseMap<const SILBasicBlock*, Energy> EnergyMap;
58+
bool changedMap = false;
3259

3360
// This is a cache and shouldn't be copied around.
3461
ColdBlockInfo(const ColdBlockInfo &) = delete;
3562
ColdBlockInfo &operator=(const ColdBlockInfo &) = delete;
63+
LLVM_DUMP_METHOD void dump() const;
3664

37-
/// Tri-value return code for checking branch hints.
38-
enum BranchHint : unsigned {
39-
None,
40-
LikelyTrue,
41-
LikelyFalse
42-
};
43-
44-
enum {
45-
RecursionDepthLimit = 3
46-
};
65+
void resetToCold(const SILBasicBlock *BB);
66+
void set(const SILBasicBlock *BB, State::Temperature temp);
4767

48-
BranchHint getBranchHint(SILValue Cond, int recursionDepth);
68+
using ExpectedValue = std::optional<bool>;
4969

50-
bool isSlowPath(const SILBasicBlock *FromBB, const SILBasicBlock *ToBB,
51-
int recursionDepth);
70+
void setExpectedCondition(CondBranchInst *CondBranch, ExpectedValue value);
5271

53-
bool isCold(const SILBasicBlock *BB,
54-
int recursionDepth);
72+
ExpectedValue searchForExpectedValue(SILValue Cond,
73+
unsigned recursionDepth = 0);
74+
void searchForExpectedValue(CondBranchInst *CondBranch);
5575

5676
public:
57-
ColdBlockInfo(DominanceAnalysis *DA): DA(DA) {}
77+
ColdBlockInfo(DominanceAnalysis *DA, PostDominanceAnalysis *PDA);
5878

59-
bool isSlowPath(const SILBasicBlock *FromBB, const SILBasicBlock *ToBB) {
60-
return isSlowPath(FromBB, ToBB, 0);
61-
}
79+
void analyze(SILFunction *F);
6280

63-
bool isCold(const SILBasicBlock *BB) { return isCold(BB, 0); }
81+
bool isCold(const SILBasicBlock *BB) const;
6482
};
6583
} // end namespace swift
6684

include/swift/SILOptimizer/Utils/PerformanceInlinerUtils.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,7 @@ class ShortestPathAnalysis {
429429
return;
430430

431431
BlockInfoStorage.resize(numBlocks);
432+
CBI.analyze(F);
432433

433434
// First step: compute the length of the blocks.
434435
unsigned BlockIdx = 0;

lib/DriverTool/sil_opt_main.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,10 @@ struct SILOptOptions {
293293
EnableThrowsPrediction = llvm::cl::opt<bool>("enable-throws-prediction",
294294
llvm::cl::desc("Enables optimization assumption that functions rarely throw errors."));
295295

296+
llvm::cl::opt<bool>
297+
EnableNoReturnCold = llvm::cl::opt<bool>("enable-noreturn-prediction",
298+
llvm::cl::desc("Enables optimization assumption that calls to no-return functions are cold."));
299+
296300
llvm::cl::opt<bool>
297301
EnableMoveInoutStackProtection = llvm::cl::opt<bool>("enable-move-inout-stack-protector",
298302
llvm::cl::desc("Enable the stack protector by moving values to temporaries."));
@@ -852,6 +856,7 @@ int sil_opt_main(ArrayRef<const char *> argv, void *MainAddr) {
852856
SILOpts.EnableSpeculativeDevirtualization = options.EnableSpeculativeDevirtualization;
853857
SILOpts.EnableAsyncDemotion = options.EnableAsyncDemotion;
854858
SILOpts.EnableThrowsPrediction = options.EnableThrowsPrediction;
859+
SILOpts.EnableNoReturnCold = options.EnableNoReturnCold;
855860
SILOpts.IgnoreAlwaysInline = options.IgnoreAlwaysInline;
856861
SILOpts.EnableOSSAModules = options.EnableOSSAModules;
857862
SILOpts.EnableSILOpaqueValues = options.EnableSILOpaqueValues;

lib/Frontend/CompilerInvocation.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2661,6 +2661,9 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args,
26612661
Opts.EnableThrowsPrediction = Args.hasFlag(
26622662
OPT_enable_throws_prediction, OPT_disable_throws_prediction,
26632663
Opts.EnableThrowsPrediction);
2664+
Opts.EnableNoReturnCold = Args.hasFlag(
2665+
OPT_enable_noreturn_prediction, OPT_disable_noreturn_prediction,
2666+
Opts.EnableNoReturnCold);
26642667
Opts.EnableActorDataRaceChecks |= Args.hasFlag(
26652668
OPT_enable_actor_data_race_checks,
26662669
OPT_disable_actor_data_race_checks, /*default=*/false);

0 commit comments

Comments
 (0)