@@ -92,6 +92,9 @@ static cl::opt<SplitFunctionsStrategy> SplitStrategy(
92
92
cl::values(clEnumValN(SplitFunctionsStrategy::Profile2, " profile2" ,
93
93
" split each function into a hot and cold fragment "
94
94
" using profiling information" )),
95
+ cl::values(clEnumValN(SplitFunctionsStrategy::CDSplit, " cdsplit" ,
96
+ " split each function into a hot, warm, and cold "
97
+ " fragment using profiling information" )),
95
98
cl::values(clEnumValN(
96
99
SplitFunctionsStrategy::Random2, " random2" ,
97
100
" split each function into a hot and cold fragment at a randomly chosen "
@@ -106,6 +109,11 @@ static cl::opt<SplitFunctionsStrategy> SplitStrategy(
106
109
" fragment contains exactly a single basic block" )),
107
110
cl::desc(" strategy used to partition blocks into fragments" ),
108
111
cl::cat(BoltOptCategory));
112
+
113
+ bool threeWaySplit () {
114
+ return opts::SplitFunctions &&
115
+ opts::SplitStrategy == SplitFunctionsStrategy::CDSplit;
116
+ }
109
117
} // namespace opts
110
118
111
119
namespace {
@@ -126,7 +134,12 @@ struct SplitProfile2 final : public SplitStrategy {
126
134
return BF.hasValidProfile () && hasFullProfile (BF) && !allBlocksCold (BF);
127
135
}
128
136
129
- bool keepEmpty () override { return false ; }
137
+ bool keepEmpty () override {
138
+ return opts::SplitStrategy != SplitFunctionsStrategy::CDSplit ? false
139
+ : true ;
140
+ }
141
+
142
+ bool autoReversal () override { return true ; }
130
143
131
144
void fragment (const BlockIt Start, const BlockIt End) override {
132
145
for (BinaryBasicBlock *const BB : llvm::make_range (Start, End)) {
@@ -136,6 +149,55 @@ struct SplitProfile2 final : public SplitStrategy {
136
149
}
137
150
};
138
151
152
+ struct SplitCacheDirected final : public SplitStrategy {
153
+ BinaryContext &BC;
154
+ using BasicBlockOrder = BinaryFunction::BasicBlockOrderType;
155
+
156
+ explicit SplitCacheDirected (BinaryContext &BC) : BC(BC) {}
157
+
158
+ bool canSplit (const BinaryFunction &BF) override {
159
+ return BF.hasValidProfile () && hasFullProfile (BF) && !allBlocksCold (BF);
160
+ }
161
+
162
+ bool keepEmpty () override { return true ; }
163
+
164
+ // This strategy does not require that the new hot fragment size strictly
165
+ // decreases after splitting.
166
+ bool autoReversal () override { return false ; }
167
+
168
+ void fragment (const BlockIt Start, const BlockIt End) override {
169
+ BasicBlockOrder BlockOrder (Start, End);
170
+ BinaryFunction &BF = *BlockOrder.front ()->getFunction ();
171
+
172
+ size_t BestSplitIndex = findSplitIndex (BF, BlockOrder);
173
+
174
+ // Assign fragments based on the computed best split index.
175
+ // All basic blocks with index up to the best split index become hot.
176
+ // All remaining blocks are warm / cold depending on if count is
177
+ // greater than 0 or not.
178
+ FragmentNum Main (0 );
179
+ FragmentNum Warm (1 );
180
+ FragmentNum Cold (2 );
181
+ for (size_t Index = 0 ; Index < BlockOrder.size (); Index++) {
182
+ BinaryBasicBlock *BB = BlockOrder[Index];
183
+ if (Index <= BestSplitIndex)
184
+ BB->setFragmentNum (Main);
185
+ else
186
+ BB->setFragmentNum (BB->getKnownExecutionCount () > 0 ? Warm : Cold);
187
+ }
188
+ }
189
+
190
+ private:
191
+ // / Find the best index for splitting. The returned value is the index of the
192
+ // / last hot basic block. Hence, "no splitting" is equivalent to returning the
193
+ // / value which is one less than the size of the function.
194
+ size_t findSplitIndex (const BinaryFunction &BF,
195
+ const BasicBlockOrder &BlockOrder) {
196
+ // Placeholder: hot-warm split after entry block.
197
+ return 0 ;
198
+ }
199
+ };
200
+
139
201
struct SplitRandom2 final : public SplitStrategy {
140
202
std::minstd_rand0 Gen;
141
203
@@ -145,6 +207,8 @@ struct SplitRandom2 final : public SplitStrategy {
145
207
146
208
bool keepEmpty () override { return false ; }
147
209
210
+ bool autoReversal () override { return true ; }
211
+
148
212
void fragment (const BlockIt Start, const BlockIt End) override {
149
213
using DiffT = typename std::iterator_traits<BlockIt>::difference_type;
150
214
const DiffT NumBlocks = End - Start;
@@ -172,6 +236,8 @@ struct SplitRandomN final : public SplitStrategy {
172
236
173
237
bool keepEmpty () override { return false ; }
174
238
239
+ bool autoReversal () override { return true ; }
240
+
175
241
void fragment (const BlockIt Start, const BlockIt End) override {
176
242
using DiffT = typename std::iterator_traits<BlockIt>::difference_type;
177
243
const DiffT NumBlocks = End - Start;
@@ -223,6 +289,8 @@ struct SplitAll final : public SplitStrategy {
223
289
return true ;
224
290
}
225
291
292
+ bool autoReversal () override { return true ; }
293
+
226
294
void fragment (const BlockIt Start, const BlockIt End) override {
227
295
unsigned Fragment = 0 ;
228
296
for (BinaryBasicBlock *const BB : llvm::make_range (Start, End))
@@ -250,6 +318,16 @@ void SplitFunctions::runOnFunctions(BinaryContext &BC) {
250
318
bool ForceSequential = false ;
251
319
252
320
switch (opts::SplitStrategy) {
321
+ case SplitFunctionsStrategy::CDSplit:
322
+ // CDSplit runs two splitting passes: hot-cold splitting (SplitPrfoile2)
323
+ // before function reordering and hot-warm-cold splitting
324
+ // (SplitCacheDirected) after function reordering.
325
+ if (BC.HasFinalizedFunctionOrder )
326
+ Strategy = std::make_unique<SplitCacheDirected>(BC);
327
+ else
328
+ Strategy = std::make_unique<SplitProfile2>();
329
+ opts::AggressiveSplitting = true ;
330
+ break ;
253
331
case SplitFunctionsStrategy::Profile2:
254
332
Strategy = std::make_unique<SplitProfile2>();
255
333
break ;
@@ -409,8 +487,10 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) {
409
487
LLVM_DEBUG (dbgs () << " Estimated size for function " << BF
410
488
<< " post-split is <0x" << Twine::utohexstr (HotSize)
411
489
<< " , 0x" << Twine::utohexstr (ColdSize) << " >\n " );
412
- if (alignTo (OriginalHotSize, opts::SplitAlignThreshold) <=
413
- alignTo (HotSize, opts::SplitAlignThreshold) + opts::SplitThreshold) {
490
+ if (S.autoReversal () &&
491
+ alignTo (OriginalHotSize, opts::SplitAlignThreshold) <=
492
+ alignTo (HotSize, opts::SplitAlignThreshold) +
493
+ opts::SplitThreshold) {
414
494
if (opts::Verbosity >= 2 ) {
415
495
outs () << " BOLT-INFO: Reversing splitting of function "
416
496
<< formatv (" {0}:\n {1:x}, {2:x} -> {3:x}\n " , BF, HotSize,
0 commit comments