5555//
5656// TODO: maybe handle TBNZ/TBZ the same way as CMP when used instead for "a < 0"
5757// TODO: For cross-block:
58- // - handle other conditional instructions (e.g. CSET)
5958// - allow second branching to be anything if it doesn't require adjusting
6059//
60+ // Cross-block optimizeCrossBlock() handles four head/true-successor
61+ // combinations:
62+ // Bcc (head) + Bcc (true) -- original case
63+ // Select (head) + Bcc (true) -- head ends with CSEL/CSET/etc.
64+ // Bcc (head) + Select (true) -- true-successor ends with CSEL/CSET/etc.
65+ // Select (head) + Select (true) -- both blocks end with a select
66+ //
6167// ===----------------------------------------------------------------------===//
6268
6369#include " AArch64.h"
@@ -136,6 +142,8 @@ class AArch64ConditionOptimizerImpl {
136142 bool tryOptimizePair (CmpCondPair &First, CmpCondPair &Second);
137143 bool optimizeIntraBlock (MachineBasicBlock &MBB );
138144 bool optimizeCrossBlock (MachineBasicBlock &HBB );
145+ std::pair<MachineInstr *, AArch64CC::CondCode>
146+ findCondConsumer (MachineBasicBlock *MBB );
139147};
140148
141149class AArch64ConditionOptimizerLegacy : public MachineFunctionPass {
@@ -603,7 +611,94 @@ bool AArch64ConditionOptimizerImpl::optimizeIntraBlock(MachineBasicBlock &MBB) {
603611 return Changed;
604612}
605613
606- // Optimizes CMP+Bcc pairs across two basic blocks in the dominator tree.
614+ // Finds the last valid conditional consumer in MBB and returns it together
615+ // with its condition code. Handles two cases:
616+ //
617+ // 1. Bcc terminator: if the block ends with a Bcc, analyzeBranch extracts
618+ // the condition code directly from the branch operands.
619+ //
620+ // 2. Select-family instruction (CSET/CSEL/CSINC/CSINV/CSNEG): scans
621+ // backward past terminators to find the sole non-branch NZCV consumer,
622+ // verifying there is no interfering NZCV read or write between it and
623+ // the CMP that produces the flags.
624+ //
625+ // Returns {nullptr, Invalid} if no suitable consumer is found or if any
626+ // safety check fails.
627+ std::pair<MachineInstr *, AArch64CC::CondCode>
628+ AArch64ConditionOptimizerImpl::findCondConsumer (MachineBasicBlock *MBB ) {
629+ // Case 1: block ends with a Bcc terminator.
630+ if (MachineInstr *BrMI = getBccTerminator (MBB )) {
631+ SmallVector<MachineOperand, 4 > CondOperands;
632+ MachineBasicBlock *TBBDest = nullptr , *FBBDest = nullptr ;
633+ if (TII ->analyzeBranch (*MBB , TBBDest, FBBDest, CondOperands))
634+ return {nullptr , AArch64CC::Invalid};
635+ AArch64CC::CondCode CC = parseCondCode (CondOperands);
636+ if (CC == AArch64CC::Invalid)
637+ return {nullptr , AArch64CC::Invalid};
638+ return {BrMI, CC };
639+ }
640+
641+ // Case 2: no Bcc terminator — scan backward for a select-family instruction
642+ // (CSET/CSEL/CSINC/CSINV/CSNEG) that is the sole NZCV consumer in the block.
643+ MachineInstr *Found = nullptr ;
644+ AArch64CC::CondCode FoundCC = AArch64CC::Invalid;
645+
646+ for (MachineInstr &MI : reverse (*MBB )) {
647+ // Skip terminators (e.g. an unconditional branch at the end of the block)
648+ // and debug instructions, which carry no real semantics.
649+ if (MI .isTerminator () || MI .isDebugInstr ())
650+ continue ;
651+
652+ if (!Found) {
653+ // We have not yet found the select. Keep scanning backward.
654+
655+ // If something writes NZCV before we find a select, the flags at that
656+ // point are not from the CMP we are looking for. Stop searching.
657+ if (MI .modifiesRegister (AArch64::NZCV , /* TRI=*/ nullptr ))
658+ return {nullptr , AArch64CC::Invalid};
659+
660+ // findCondCodeUseOperandIdxForBranchOrSelect returns the operand index
661+ // of the condition code for any branch or select-family instruction, or
662+ // -1 if the instruction does not use a condition code.
663+ // We exclude branches because getBccTerminator already handles those;
664+ // we only want non-branch conditionals: CSET, CSEL, CSINC, CSINV, CSNEG.
665+ int CCOpIdx =
666+ AArch64InstrInfo::findCondCodeUseOperandIdxForBranchOrSelect (MI );
667+ if (CCOpIdx >= 0 && !MI .isBranch ()) {
668+ Found = &MI ;
669+ FoundCC = (AArch64CC::CondCode)(int )MI .getOperand (CCOpIdx).getImm ();
670+ continue ;
671+ }
672+
673+ // Any other instruction that reads NZCV (but is not a select) means the
674+ // flags are consumed by something we do not understand. Stop searching.
675+ if (MI .readsRegister (AArch64::NZCV , /* TRI=*/ nullptr ))
676+ return {nullptr , AArch64CC::Invalid};
677+
678+ } else {
679+ // We already found a select. Now verify there is no second NZCV reader
680+ // between the found select and the CMP. If there is, the CMP feeds two
681+ // consumers and cannot be safely adjusted.
682+ if (MI .readsRegister (AArch64::NZCV , /* TRI=*/ nullptr ))
683+ return {nullptr , AArch64CC::Invalid};
684+
685+ if (MI .modifiesRegister (AArch64::NZCV , /* TRI=*/ nullptr )) {
686+ // A CMP instruction is the flag producer we are looking for; stop
687+ // scanning. findAdjustableCmp will locate it from CondMI.
688+ if (isCmpInstruction (MI .getOpcode ()))
689+ break ;
690+ // Any other NZCV writer means the select is not reading from the CMP
691+ // we would find further back.
692+ return {nullptr , AArch64CC::Invalid};
693+ }
694+ }
695+ }
696+ return {Found, FoundCC};
697+ }
698+
699+ // Optimizes CMP+conditional pairs across two basic blocks in the dominator
700+ // tree. The conditional consumer in each block may be a Bcc terminator or a
701+ // select-family instruction (CSEL/CSET/CSINC/CSINV/CSNEG).
607702bool AArch64ConditionOptimizerImpl::optimizeCrossBlock (MachineBasicBlock &HBB ) {
608703 SmallVector<MachineOperand, 4 > HeadCondOperands;
609704 MachineBasicBlock *TBB = nullptr , *FBB = nullptr ;
@@ -616,45 +711,37 @@ bool AArch64ConditionOptimizerImpl::optimizeCrossBlock(MachineBasicBlock &HBB) {
616711 return false ;
617712 }
618713
619- SmallVector<MachineOperand, 4 > TrueCondOperands;
620- MachineBasicBlock *TBB_TBB = nullptr , *TBB_FBB = nullptr ;
621- if (TII ->analyzeBranch (*TBB , TBB_TBB , TBB_FBB , TrueCondOperands)) {
714+ // Find the conditional consumer(Bcc or select-family) and its condition
715+ // code in each block. findCondConsumer() handles both cases uniformly.
716+ auto [HeadCondMI, HeadCondCode] = findCondConsumer (&HBB );
717+ if (!HeadCondMI)
622718 return false ;
623- }
624719
625- MachineInstr *HeadBrMI = getBccTerminator (&HBB );
626- MachineInstr *TrueBrMI = getBccTerminator (TBB );
627- if (!HeadBrMI || !TrueBrMI)
720+ auto [TrueCondMI, TrueCondCode] = findCondConsumer (TBB );
721+ if (!TrueCondMI)
628722 return false ;
629723
630724 // Since we may modify cmps in these blocks, make sure NZCV does not live out.
631725 if (nzcvLivesOut (&HBB ) || nzcvLivesOut (TBB ))
632726 return false ;
633727
634- // Find the CMPs controlling each branch
635- MachineInstr *HeadCmpMI = findAdjustableCmp (HeadBrMI );
636- MachineInstr *TrueCmpMI = findAdjustableCmp (TrueBrMI );
728+ // Find the CMPs controlling each conditional.
729+ MachineInstr *HeadCmpMI = findAdjustableCmp (HeadCondMI );
730+ MachineInstr *TrueCmpMI = findAdjustableCmp (TrueCondMI );
637731 if (!HeadCmpMI || !TrueCmpMI)
638732 return false ;
639733
640734 if (!registersMatch (HeadCmpMI, TrueCmpMI))
641735 return false ;
642736
643- AArch64CC::CondCode HeadCondCode = parseCondCode (HeadCondOperands);
644- AArch64CC::CondCode TrueCondCode = parseCondCode (TrueCondOperands);
645- if (HeadCondCode == AArch64CC::CondCode::Invalid ||
646- TrueCondCode == AArch64CC::CondCode::Invalid) {
647- return false ;
648- }
649-
650737 LLVM_DEBUG (dbgs () << " Checking cross-block pair: "
651738 << AArch64CC::getCondCodeName (HeadCondCode) << " #"
652739 << HeadCmpMI->getOperand (2 ).getImm () << " , "
653740 << AArch64CC::getCondCodeName (TrueCondCode) << " #"
654741 << TrueCmpMI->getOperand (2 ).getImm () << ' \n ' );
655742
656- CmpCondPair Head{HeadCmpMI, HeadBrMI , HeadCondCode};
657- CmpCondPair True{TrueCmpMI, TrueBrMI , TrueCondCode};
743+ CmpCondPair Head{HeadCmpMI, HeadCondMI , HeadCondCode};
744+ CmpCondPair True{TrueCmpMI, TrueCondMI , TrueCondCode};
658745
659746 return tryOptimizePair (Head, True);
660747}
0 commit comments