@@ -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 "
@@ -126,7 +129,7 @@ struct SplitProfile2 final : public SplitStrategy {
126
129
return BF.hasValidProfile () && hasFullProfile (BF) && !allBlocksCold (BF);
127
130
}
128
131
129
- bool keepEmpty () override { return false ; }
132
+ bool compactFragments () override { return true ; }
130
133
131
134
void fragment (const BlockIt Start, const BlockIt End) override {
132
135
for (BinaryBasicBlock *const BB : llvm::make_range (Start, End)) {
@@ -136,14 +139,59 @@ struct SplitProfile2 final : public SplitStrategy {
136
139
}
137
140
};
138
141
142
+ struct SplitCacheDirected final : public SplitStrategy {
143
+ using BasicBlockOrder = BinaryFunction::BasicBlockOrderType;
144
+
145
+ bool canSplit (const BinaryFunction &BF) override {
146
+ return BF.hasValidProfile () && hasFullProfile (BF) && !allBlocksCold (BF);
147
+ }
148
+
149
+ // When some functions are hot-warm split and others are hot-warm-cold split,
150
+ // we do not want to change the fragment numbers of the blocks in the hot-warm
151
+ // split functions.
152
+ bool compactFragments () override { return false ; }
153
+
154
+ void fragment (const BlockIt Start, const BlockIt End) override {
155
+ BasicBlockOrder BlockOrder (Start, End);
156
+ BinaryFunction &BF = *BlockOrder.front ()->getFunction ();
157
+
158
+ size_t BestSplitIndex = findSplitIndex (BF, BlockOrder);
159
+
160
+ // Assign fragments based on the computed best split index.
161
+ // All basic blocks with index up to the best split index become hot.
162
+ // All remaining blocks are warm / cold depending on if count is
163
+ // greater than 0 or not.
164
+ FragmentNum Main (0 );
165
+ FragmentNum Cold (1 );
166
+ FragmentNum Warm (2 );
167
+ for (size_t Index = 0 ; Index < BlockOrder.size (); Index++) {
168
+ BinaryBasicBlock *BB = BlockOrder[Index];
169
+ if (Index <= BestSplitIndex)
170
+ BB->setFragmentNum (Main);
171
+ else
172
+ BB->setFragmentNum (BB->getKnownExecutionCount () > 0 ? Warm : Cold);
173
+ }
174
+ }
175
+
176
+ private:
177
+ // / Find the best index for splitting. The returned value is the index of the
178
+ // / last hot basic block. Hence, "no splitting" is equivalent to returning the
179
+ // / value which is one less than the size of the function.
180
+ size_t findSplitIndex (const BinaryFunction &BF,
181
+ const BasicBlockOrder &BlockOrder) {
182
+ // Placeholder: hot-warm split after entry block.
183
+ return 0 ;
184
+ }
185
+ };
186
+
139
187
struct SplitRandom2 final : public SplitStrategy {
140
188
std::minstd_rand0 Gen;
141
189
142
190
SplitRandom2 () : Gen(opts::RandomSeed.getValue()) {}
143
191
144
192
bool canSplit (const BinaryFunction &BF) override { return true ; }
145
193
146
- bool keepEmpty () override { return false ; }
194
+ bool compactFragments () override { return true ; }
147
195
148
196
void fragment (const BlockIt Start, const BlockIt End) override {
149
197
using DiffT = typename std::iterator_traits<BlockIt>::difference_type;
@@ -170,7 +218,7 @@ struct SplitRandomN final : public SplitStrategy {
170
218
171
219
bool canSplit (const BinaryFunction &BF) override { return true ; }
172
220
173
- bool keepEmpty () override { return false ; }
221
+ bool compactFragments () override { return true ; }
174
222
175
223
void fragment (const BlockIt Start, const BlockIt End) override {
176
224
using DiffT = typename std::iterator_traits<BlockIt>::difference_type;
@@ -217,10 +265,10 @@ struct SplitRandomN final : public SplitStrategy {
217
265
struct SplitAll final : public SplitStrategy {
218
266
bool canSplit (const BinaryFunction &BF) override { return true ; }
219
267
220
- bool keepEmpty () override {
268
+ bool compactFragments () override {
221
269
// Keeping empty fragments allows us to test, that empty fragments do not
222
270
// generate symbols.
223
- return true ;
271
+ return false ;
224
272
}
225
273
226
274
void fragment (const BlockIt Start, const BlockIt End) override {
@@ -246,10 +294,26 @@ void SplitFunctions::runOnFunctions(BinaryContext &BC) {
246
294
if (!opts::SplitFunctions)
247
295
return ;
248
296
297
+ // If split strategy is not CDSplit, then a second run of the pass is not
298
+ // needed after function reordering.
299
+ if (BC.HasFinalizedFunctionOrder &&
300
+ opts::SplitStrategy != SplitFunctionsStrategy::CDSplit)
301
+ return ;
302
+
249
303
std::unique_ptr<SplitStrategy> Strategy;
250
304
bool ForceSequential = false ;
251
305
252
306
switch (opts::SplitStrategy) {
307
+ case SplitFunctionsStrategy::CDSplit:
308
+ // CDSplit runs two splitting passes: hot-cold splitting (SplitPrfoile2)
309
+ // before function reordering and hot-warm-cold splitting
310
+ // (SplitCacheDirected) after function reordering.
311
+ if (BC.HasFinalizedFunctionOrder )
312
+ Strategy = std::make_unique<SplitCacheDirected>();
313
+ else
314
+ Strategy = std::make_unique<SplitProfile2>();
315
+ opts::AggressiveSplitting = true ;
316
+ break ;
253
317
case SplitFunctionsStrategy::Profile2:
254
318
Strategy = std::make_unique<SplitProfile2>();
255
319
break ;
@@ -382,7 +446,7 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) {
382
446
CurrentFragment = BB->getFragmentNum ();
383
447
}
384
448
385
- if (!S. keepEmpty ()) {
449
+ if (S. compactFragments ()) {
386
450
FragmentNum CurrentFragment = FragmentNum::main ();
387
451
FragmentNum NewFragment = FragmentNum::main ();
388
452
for (BinaryBasicBlock *const BB : NewLayout) {
@@ -394,7 +458,7 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) {
394
458
}
395
459
}
396
460
397
- BF.getLayout ().update (NewLayout);
461
+ const bool LayoutUpdated = BF.getLayout ().update (NewLayout);
398
462
399
463
// For shared objects, invoke instructions and corresponding landing pads
400
464
// have to be placed in the same fragment. When we split them, create
@@ -404,7 +468,7 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) {
404
468
Trampolines = createEHTrampolines (BF);
405
469
406
470
// Check the new size to see if it's worth splitting the function.
407
- if (BC.isX86 () && BF. isSplit () ) {
471
+ if (BC.isX86 () && LayoutUpdated ) {
408
472
std::tie (HotSize, ColdSize) = BC.calculateEmittedSize (BF);
409
473
LLVM_DEBUG (dbgs () << " Estimated size for function " << BF
410
474
<< " post-split is <0x" << Twine::utohexstr (HotSize)
@@ -431,6 +495,11 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) {
431
495
SplitBytesCold += ColdSize;
432
496
}
433
497
}
498
+
499
+ // Fix branches if the splitting decision of the pass after function
500
+ // reordering is different from that of the pass before function reordering.
501
+ if (LayoutUpdated && BC.HasFinalizedFunctionOrder )
502
+ BF.fixBranches ();
434
503
}
435
504
436
505
SplitFunctions::TrampolineSetType
0 commit comments