@@ -775,46 +775,86 @@ bool DataAggregator::doInterBranch(BinaryFunction *FromFunc,
775775
776776bool DataAggregator::doBranch (uint64_t From, uint64_t To, uint64_t Count,
777777 uint64_t Mispreds) {
778- bool IsReturn = false ;
779- auto handleAddress = [&](uint64_t &Addr, bool IsFrom) -> BinaryFunction * {
780- if (BinaryFunction *Func = getBinaryFunctionContainingAddress (Addr)) {
781- Addr -= Func->getAddress ();
782- if (IsFrom) {
783- auto checkReturn = [&](auto MaybeInst) {
784- IsReturn = MaybeInst && BC->MIB ->isReturn (*MaybeInst);
785- };
786- if (Func->hasInstructions ())
787- checkReturn (Func->getInstructionAtOffset (Addr));
788- else
789- checkReturn (Func->disassembleInstructionAtOffset (Addr));
790- }
778+ // Returns whether \p Offset in \p Func contains a return instruction.
779+ auto checkReturn = [&](const BinaryFunction &Func, const uint64_t Offset) {
780+ auto isReturn = [&](auto MI) { return MI && BC->MIB ->isReturn (*MI); };
781+ return Func.hasInstructions ()
782+ ? isReturn (Func.getInstructionAtOffset (Offset))
783+ : isReturn (Func.disassembleInstructionAtOffset (Offset));
784+ };
791785
792- if (BAT)
793- Addr = BAT->translate (Func->getAddress (), Addr, IsFrom);
786+ // Returns whether \p Offset in \p Func corresponds to a call continuation
787+ // fallthrough block.
788+ auto checkCallCont = [&](BinaryFunction &Func, const uint64_t Offset) {
789+ // Note the use of MCInstrAnalysis: no call continuation for a tail call.
790+ auto isCall = [&](auto MI) { return MI && BC->MIA ->isCall (*MI); };
791+
792+ // No call continuation at a function start.
793+ if (!Offset)
794+ return false ;
795+
796+ // FIXME: support BAT case where the function might be in empty state
797+ // (split fragments declared non-simple).
798+ if (!Func.hasCFG ())
799+ return false ;
800+
801+ // The offset should not be an entry point or a landing pad.
802+ const BinaryBasicBlock *ContBB = Func.getBasicBlockAtOffset (Offset);
803+ if (!ContBB || ContBB->isEntryPoint () || ContBB->isLandingPad ())
804+ return false ;
805+
806+ // Check that preceding instruction is a call.
807+ const BinaryBasicBlock *CallBB =
808+ Func.getBasicBlockContainingOffset (Offset - 1 );
809+ if (!CallBB || CallBB == ContBB)
810+ return false ;
811+ return isCall (CallBB->getLastNonPseudoInstr ());
812+ };
794813
795- if (BinaryFunction *ParentFunc = getBATParentFunction (*Func)) {
796- Func = ParentFunc;
797- if (IsFrom)
798- NumColdSamples += Count;
799- }
814+ // Mutates \p Addr to an offset into the containing function, performing BAT
815+ // offset translation and parent lookup.
816+ //
817+ // Returns the containing function (or BAT parent) and whether the address
818+ // corresponds to a return (if \p IsFrom) or a call continuation (otherwise).
819+ auto handleAddress = [&](uint64_t &Addr, bool IsFrom) {
820+ BinaryFunction *Func = getBinaryFunctionContainingAddress (Addr);
821+ if (!Func)
822+ return std::pair{Func, false };
800823
801- return Func;
802- }
803- return nullptr ;
804- };
824+ Addr -= Func->getAddress ();
805825
806- BinaryFunction *FromFunc = handleAddress (From, /* IsFrom=*/ true );
807- // Record returns as call->call continuation fall-through.
808- if (IsReturn) {
809- LBREntry First{To - 1 , To - 1 , false };
810- LBREntry Second{To, To, false };
811- return doTrace (First, Second, Count);
812- }
826+ bool IsRetOrCallCont =
827+ IsFrom ? checkReturn (*Func, Addr) : checkCallCont (*Func, Addr);
828+
829+ if (BAT)
830+ Addr = BAT->translate (Func->getAddress (), Addr, IsFrom);
831+
832+ BinaryFunction *ParentFunc = getBATParentFunction (*Func);
833+ if (!ParentFunc)
834+ return std::pair{Func, IsRetOrCallCont};
813835
814- BinaryFunction *ToFunc = handleAddress (To, /* IsFrom=*/ false );
836+ if (IsFrom)
837+ NumColdSamples += Count;
838+
839+ return std::pair{ParentFunc, IsRetOrCallCont};
840+ };
841+
842+ uint64_t ToOrig = To;
843+ auto [FromFunc, IsReturn] = handleAddress (From, /* IsFrom=*/ true );
844+ auto [ToFunc, IsCallCont] = handleAddress (To, /* IsFrom=*/ false );
815845 if (!FromFunc && !ToFunc)
816846 return false ;
817847
848+ // Record call to continuation trace.
849+ if (IsCallCont && FromFunc != ToFunc) {
850+ LBREntry First{ToOrig - 1 , ToOrig - 1 , false };
851+ LBREntry Second{ToOrig, ToOrig, false };
852+ return doTrace (First, Second, Count);
853+ }
854+ // Ignore returns.
855+ if (IsReturn)
856+ return true ;
857+
818858 // Treat recursive control transfers as inter-branches.
819859 if (FromFunc == ToFunc && To != 0 ) {
820860 recordBranch (*FromFunc, From, To, Count, Mispreds);
@@ -916,24 +956,6 @@ DataAggregator::getFallthroughsInTrace(BinaryFunction &BF,
916956 if (!FromBB || !ToBB)
917957 return std::nullopt ;
918958
919- // Adjust FromBB if the first LBR is a return from the last instruction in
920- // the previous block (that instruction should be a call).
921- if (From == FromBB->getOffset () && !BF.containsAddress (FirstLBR.From ) &&
922- !FromBB->isEntryPoint () && !FromBB->isLandingPad ()) {
923- const BinaryBasicBlock *PrevBB =
924- BF.getLayout ().getBlock (FromBB->getIndex () - 1 );
925- if (PrevBB->getSuccessor (FromBB->getLabel ())) {
926- const MCInst *Instr = PrevBB->getLastNonPseudoInstr ();
927- if (Instr && BC.MIB ->isCall (*Instr))
928- FromBB = PrevBB;
929- else
930- LLVM_DEBUG (dbgs () << " invalid incoming LBR (no call): " << FirstLBR
931- << ' \n ' );
932- } else {
933- LLVM_DEBUG (dbgs () << " invalid incoming LBR: " << FirstLBR << ' \n ' );
934- }
935- }
936-
937959 // Fill out information for fall-through edges. The From and To could be
938960 // within the same basic block, e.g. when two call instructions are in the
939961 // same block. In this case we skip the processing.
0 commit comments