@@ -92,6 +92,9 @@ static cl::opt<SplitFunctionsStrategy> SplitStrategy(
9292 cl::values(clEnumValN(SplitFunctionsStrategy::Profile2, " profile2" ,
9393 " split each function into a hot and cold fragment "
9494 " 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" )),
9598 cl::values(clEnumValN(
9699 SplitFunctionsStrategy::Random2, " random2" ,
97100 " split each function into a hot and cold fragment at a randomly chosen "
@@ -126,7 +129,7 @@ struct SplitProfile2 final : public SplitStrategy {
126129 return BF.hasValidProfile () && hasFullProfile (BF) && !allBlocksCold (BF);
127130 }
128131
129- bool keepEmpty () override { return false ; }
132+ bool compactFragments () override { return true ; }
130133
131134 void fragment (const BlockIt Start, const BlockIt End) override {
132135 for (BinaryBasicBlock *const BB : llvm::make_range (Start, End)) {
@@ -136,14 +139,59 @@ struct SplitProfile2 final : public SplitStrategy {
136139 }
137140};
138141
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+
139187struct SplitRandom2 final : public SplitStrategy {
140188 std::minstd_rand0 Gen;
141189
142190 SplitRandom2 () : Gen(opts::RandomSeed.getValue()) {}
143191
144192 bool canSplit (const BinaryFunction &BF) override { return true ; }
145193
146- bool keepEmpty () override { return false ; }
194+ bool compactFragments () override { return true ; }
147195
148196 void fragment (const BlockIt Start, const BlockIt End) override {
149197 using DiffT = typename std::iterator_traits<BlockIt>::difference_type;
@@ -170,7 +218,7 @@ struct SplitRandomN final : public SplitStrategy {
170218
171219 bool canSplit (const BinaryFunction &BF) override { return true ; }
172220
173- bool keepEmpty () override { return false ; }
221+ bool compactFragments () override { return true ; }
174222
175223 void fragment (const BlockIt Start, const BlockIt End) override {
176224 using DiffT = typename std::iterator_traits<BlockIt>::difference_type;
@@ -217,10 +265,10 @@ struct SplitRandomN final : public SplitStrategy {
217265struct SplitAll final : public SplitStrategy {
218266 bool canSplit (const BinaryFunction &BF) override { return true ; }
219267
220- bool keepEmpty () override {
268+ bool compactFragments () override {
221269 // Keeping empty fragments allows us to test, that empty fragments do not
222270 // generate symbols.
223- return true ;
271+ return false ;
224272 }
225273
226274 void fragment (const BlockIt Start, const BlockIt End) override {
@@ -246,10 +294,26 @@ void SplitFunctions::runOnFunctions(BinaryContext &BC) {
246294 if (!opts::SplitFunctions)
247295 return ;
248296
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+
249303 std::unique_ptr<SplitStrategy> Strategy;
250304 bool ForceSequential = false ;
251305
252306 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 ;
253317 case SplitFunctionsStrategy::Profile2:
254318 Strategy = std::make_unique<SplitProfile2>();
255319 break ;
@@ -382,7 +446,7 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) {
382446 CurrentFragment = BB->getFragmentNum ();
383447 }
384448
385- if (!S. keepEmpty ()) {
449+ if (S. compactFragments ()) {
386450 FragmentNum CurrentFragment = FragmentNum::main ();
387451 FragmentNum NewFragment = FragmentNum::main ();
388452 for (BinaryBasicBlock *const BB : NewLayout) {
@@ -394,7 +458,7 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) {
394458 }
395459 }
396460
397- BF.getLayout ().update (NewLayout);
461+ const bool LayoutUpdated = BF.getLayout ().update (NewLayout);
398462
399463 // For shared objects, invoke instructions and corresponding landing pads
400464 // have to be placed in the same fragment. When we split them, create
@@ -404,7 +468,7 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) {
404468 Trampolines = createEHTrampolines (BF);
405469
406470 // Check the new size to see if it's worth splitting the function.
407- if (BC.isX86 () && BF. isSplit () ) {
471+ if (BC.isX86 () && LayoutUpdated ) {
408472 std::tie (HotSize, ColdSize) = BC.calculateEmittedSize (BF);
409473 LLVM_DEBUG (dbgs () << " Estimated size for function " << BF
410474 << " post-split is <0x" << Twine::utohexstr (HotSize)
@@ -431,6 +495,11 @@ void SplitFunctions::splitFunction(BinaryFunction &BF, SplitStrategy &S) {
431495 SplitBytesCold += ColdSize;
432496 }
433497 }
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 ();
434503}
435504
436505SplitFunctions::TrampolineSetType
0 commit comments