@@ -51,6 +51,12 @@ cl::opt<bool>
5151 cl::desc (" Infer counts from stale profile data." ),
5252 cl::init(false ), cl::Hidden, cl::cat(BoltOptCategory));
5353
54+ cl::opt<unsigned > StaleMatchingMinMatchedBlock (
55+ " stale-matching-min-matched-block" ,
56+ cl::desc (" Percentage threshold of matched basic blocks at which stale "
57+ " profile inference is executed." ),
58+ cl::init(0 ), cl::Hidden, cl::cat(BoltOptCategory));
59+
5460cl::opt<unsigned > StaleMatchingMaxFuncSize (
5561 " stale-matching-max-func-size" ,
5662 cl::desc (" The maximum size of a function to consider for inference." ),
@@ -301,21 +307,21 @@ void BinaryFunction::computeBlockHashes(HashFunction HashFunction) const {
301307 BB->setHash (BlendedHashes[I].combine ());
302308 }
303309}
304-
310+ // TODO: mediate the difference between flow function construction here in BOLT
311+ // and in the compiler by splitting blocks with exception throwing calls at the
312+ // call and adding the landing pad as the successor.
305313// / Create a wrapper flow function to use with the profile inference algorithm,
306314// / and initialize its jumps and metadata.
307315FlowFunction
308316createFlowFunction (const BinaryFunction::BasicBlockOrderType &BlockOrder) {
309317 FlowFunction Func;
310318
311319 // Add a special "dummy" source so that there is always a unique entry point.
312- // Because of the extra source, for all other blocks in FlowFunction it holds
313- // that Block.Index == BB->getIndex() + 1
314320 FlowBlock EntryBlock;
315321 EntryBlock.Index = 0 ;
316322 Func.Blocks .push_back (EntryBlock);
317323
318- // Create FlowBlock for every basic block in the binary function
324+ // Create FlowBlock for every basic block in the binary function.
319325 for (const BinaryBasicBlock *BB : BlockOrder) {
320326 Func.Blocks .emplace_back ();
321327 FlowBlock &Block = Func.Blocks .back ();
@@ -325,7 +331,12 @@ createFlowFunction(const BinaryFunction::BasicBlockOrderType &BlockOrder) {
325331 " incorrectly assigned basic block index" );
326332 }
327333
328- // Create FlowJump for each jump between basic blocks in the binary function
334+ // Add a special "dummy" sink block so there is always a unique sink.
335+ FlowBlock SinkBlock;
336+ SinkBlock.Index = Func.Blocks .size ();
337+ Func.Blocks .push_back (SinkBlock);
338+
339+ // Create FlowJump for each jump between basic blocks in the binary function.
329340 std::vector<uint64_t > InDegree (Func.Blocks .size (), 0 );
330341 for (const BinaryBasicBlock *SrcBB : BlockOrder) {
331342 std::unordered_set<const BinaryBasicBlock *> UniqueSuccs;
@@ -342,6 +353,16 @@ createFlowFunction(const BinaryFunction::BasicBlockOrderType &BlockOrder) {
342353 InDegree[Jump.Target ]++;
343354 UniqueSuccs.insert (DstBB);
344355 }
356+ // TODO: set jump from exit block to landing pad to Unlikely.
357+ // If the block is an exit, add a dummy edge from it to the sink block.
358+ if (UniqueSuccs.empty ()) {
359+ Func.Jumps .emplace_back ();
360+ FlowJump &Jump = Func.Jumps .back ();
361+ Jump.Source = SrcBB->getIndex () + 1 ;
362+ Jump.Target = Func.Blocks .size () - 1 ;
363+ InDegree[Jump.Target ]++;
364+ }
365+
345366 // Collect jumps to landing pads
346367 for (const BinaryBasicBlock *DstBB : SrcBB->landing_pads ()) {
347368 // Ignoring parallel edges
@@ -358,9 +379,9 @@ createFlowFunction(const BinaryFunction::BasicBlockOrderType &BlockOrder) {
358379 }
359380
360381 // Add dummy edges to the extra sources. If there are multiple entry blocks,
361- // add an unlikely edge from 0 to the subsequent ones
382+ // add an unlikely edge from 0 to the subsequent ones. Skips the sink block.
362383 assert (InDegree[0 ] == 0 && " dummy entry blocks shouldn't have predecessors" );
363- for (uint64_t I = 1 ; I < Func.Blocks .size (); I++) {
384+ for (uint64_t I = 1 ; I < Func.Blocks .size () - 1 ; I++) {
364385 const BinaryBasicBlock *BB = BlockOrder[I - 1 ];
365386 if (BB->isEntryPoint () || InDegree[I] == 0 ) {
366387 Func.Jumps .emplace_back ();
@@ -391,11 +412,10 @@ createFlowFunction(const BinaryFunction::BasicBlockOrderType &BlockOrder) {
391412// / of the basic blocks in the binary, the count is "matched" to the block.
392413// / Similarly, if both the source and the target of a count in the profile are
393414// / matched to a jump in the binary, the count is recorded in CFG.
394- void matchWeightsByHashes (BinaryContext &BC,
395- const BinaryFunction::BasicBlockOrderType &BlockOrder,
396- const yaml::bolt::BinaryFunctionProfile &YamlBF,
397- FlowFunction &Func) {
398- assert (Func.Blocks .size () == BlockOrder.size () + 1 );
415+ size_t matchWeightsByHashes (
416+ BinaryContext &BC, const BinaryFunction::BasicBlockOrderType &BlockOrder,
417+ const yaml::bolt::BinaryFunctionProfile &YamlBF, FlowFunction &Func) {
418+ assert (Func.Blocks .size () == BlockOrder.size () + 2 );
399419
400420 std::vector<FlowBlock *> Blocks;
401421 std::vector<BlendedBlockHash> BlendedHashes;
@@ -500,6 +520,8 @@ void matchWeightsByHashes(BinaryContext &BC,
500520 Block.HasUnknownWeight = false ;
501521 Block.Weight = std::max (OutWeight[Block.Index ], InWeight[Block.Index ]);
502522 }
523+
524+ return MatchedBlocks.size ();
503525}
504526
505527// / The function finds all blocks that are (i) reachable from the Entry block
@@ -575,13 +597,19 @@ void preprocessUnreachableBlocks(FlowFunction &Func) {
575597// / Decide if stale profile matching can be applied for a given function.
576598// / Currently we skip inference for (very) large instances and for instances
577599// / having "unexpected" control flow (e.g., having no sink basic blocks).
578- bool canApplyInference (const FlowFunction &Func) {
600+ bool canApplyInference (const FlowFunction &Func,
601+ const yaml::bolt::BinaryFunctionProfile &YamlBF,
602+ const uint64_t &MatchedBlocks) {
579603 if (Func.Blocks .size () > opts::StaleMatchingMaxFuncSize)
580604 return false ;
581605
582- bool HasExitBlocks = llvm::any_of (
583- Func.Blocks , [&](const FlowBlock &Block) { return Block.isExit (); });
584- if (!HasExitBlocks)
606+ if (MatchedBlocks * 100 <
607+ opts::StaleMatchingMinMatchedBlock * YamlBF.Blocks .size ())
608+ return false ;
609+
610+ // Returns false if the artificial sink block has no predecessors meaning
611+ // there are no exit blocks.
612+ if (Func.Blocks [Func.Blocks .size () - 1 ].isEntry ())
585613 return false ;
586614
587615 return true ;
@@ -618,7 +646,7 @@ void assignProfile(BinaryFunction &BF,
618646 FlowFunction &Func) {
619647 BinaryContext &BC = BF.getBinaryContext ();
620648
621- assert (Func.Blocks .size () == BlockOrder.size () + 1 );
649+ assert (Func.Blocks .size () == BlockOrder.size () + 2 );
622650 for (uint64_t I = 0 ; I < BlockOrder.size (); I++) {
623651 FlowBlock &Block = Func.Blocks [I + 1 ];
624652 BinaryBasicBlock *BB = BlockOrder[I];
@@ -640,6 +668,9 @@ void assignProfile(BinaryFunction &BF,
640668 if (Jump->Flow == 0 )
641669 continue ;
642670
671+ // Skips the artificial sink block.
672+ if (Jump->Target == Func.Blocks .size () - 1 )
673+ continue ;
643674 BinaryBasicBlock &SuccBB = *BlockOrder[Jump->Target - 1 ];
644675 // Check if the edge corresponds to a regular jump or a landing pad
645676 if (BB->getSuccessor (SuccBB.getLabel ())) {
@@ -725,18 +756,21 @@ bool YAMLProfileReader::inferStaleProfile(
725756 const BinaryFunction::BasicBlockOrderType BlockOrder (
726757 BF.getLayout ().block_begin (), BF.getLayout ().block_end ());
727758
759+ // Tracks the number of matched blocks.
760+
728761 // Create a wrapper flow function to use with the profile inference algorithm.
729762 FlowFunction Func = createFlowFunction (BlockOrder);
730763
731764 // Match as many block/jump counts from the stale profile as possible
732- matchWeightsByHashes (BF.getBinaryContext (), BlockOrder, YamlBF, Func);
765+ size_t MatchedBlocks =
766+ matchWeightsByHashes (BF.getBinaryContext (), BlockOrder, YamlBF, Func);
733767
734768 // Adjust the flow function by marking unreachable blocks Unlikely so that
735769 // they don't get any counts assigned.
736770 preprocessUnreachableBlocks (Func);
737771
738772 // Check if profile inference can be applied for the instance.
739- if (!canApplyInference (Func))
773+ if (!canApplyInference (Func, YamlBF, MatchedBlocks ))
740774 return false ;
741775
742776 // Apply the profile inference algorithm.
0 commit comments