diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp index a5135b78bded9..82061607f2e53 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp @@ -1355,48 +1355,52 @@ static bool areCFlagsAccessedBetweenInstrs( return false; } -/// optimizePTestInstr - Attempt to remove a ptest of a predicate-generating -/// operation which could set the flags in an identical manner -bool AArch64InstrInfo::optimizePTestInstr( - MachineInstr *PTest, unsigned MaskReg, unsigned PredReg, - const MachineRegisterInfo *MRI) const { - auto *Mask = MRI->getUniqueVRegDef(MaskReg); - auto *Pred = MRI->getUniqueVRegDef(PredReg); - auto NewOp = Pred->getOpcode(); - bool OpChanged = false; - +std::optional +AArch64InstrInfo::canRemovePTestInstr(MachineInstr *PTest, MachineInstr *Mask, + MachineInstr *Pred, + const MachineRegisterInfo *MRI) const { unsigned MaskOpcode = Mask->getOpcode(); unsigned PredOpcode = Pred->getOpcode(); bool PredIsPTestLike = isPTestLikeOpcode(PredOpcode); bool PredIsWhileLike = isWhileOpcode(PredOpcode); - if (isPTrueOpcode(MaskOpcode) && (PredIsPTestLike || PredIsWhileLike) && - getElementSizeForOpcode(MaskOpcode) == - getElementSizeForOpcode(PredOpcode) && - Mask->getOperand(1).getImm() == 31) { + if (PredIsWhileLike) { + // For PTEST(PG, PG), PTEST is redundant when PG is the result of a WHILEcc + // instruction and the condition is "any" since WHILcc does an implicit + // PTEST(ALL, PG) check and PG is always a subset of ALL. + if ((Mask == Pred) && PTest->getOpcode() == AArch64::PTEST_PP_ANY) + return PredOpcode; + // For PTEST(PTRUE_ALL, WHILE), if the element size matches, the PTEST is // redundant since WHILE performs an implicit PTEST with an all active - // mask. Must be an all active predicate of matching element size. + // mask. + if (isPTrueOpcode(MaskOpcode) && Mask->getOperand(1).getImm() == 31 && + getElementSizeForOpcode(MaskOpcode) == + getElementSizeForOpcode(PredOpcode)) + return PredOpcode; + + return {}; + } + + if (PredIsPTestLike) { + // For PTEST(PG, PG), PTEST is redundant when PG is the result of an + // instruction that sets the flags as PTEST would and the condition is + // "any" since PG is always a subset of the governing predicate of the + // ptest-like instruction. + if ((Mask == Pred) && PTest->getOpcode() == AArch64::PTEST_PP_ANY) + return PredOpcode; // For PTEST(PTRUE_ALL, PTEST_LIKE), the PTEST is redundant if the - // PTEST_LIKE instruction uses the same all active mask and the element - // size matches. If the PTEST has a condition of any then it is always - // redundant. - if (PredIsPTestLike) { + // the element size matches and either the PTEST_LIKE instruction uses + // the same all active mask or the condition is "any". + if (isPTrueOpcode(MaskOpcode) && Mask->getOperand(1).getImm() == 31 && + getElementSizeForOpcode(MaskOpcode) == + getElementSizeForOpcode(PredOpcode)) { auto PTestLikeMask = MRI->getUniqueVRegDef(Pred->getOperand(1).getReg()); - if (Mask != PTestLikeMask && PTest->getOpcode() != AArch64::PTEST_PP_ANY) - return false; + if (Mask == PTestLikeMask || PTest->getOpcode() == AArch64::PTEST_PP_ANY) + return PredOpcode; } - // Fallthough to simply remove the PTEST. - } else if ((Mask == Pred) && (PredIsPTestLike || PredIsWhileLike) && - PTest->getOpcode() == AArch64::PTEST_PP_ANY) { - // For PTEST(PG, PG), PTEST is redundant when PG is the result of an - // instruction that sets the flags as PTEST would. This is only valid when - // the condition is any. - - // Fallthough to simply remove the PTEST. - } else if (PredIsPTestLike) { // For PTEST(PG, PTEST_LIKE(PG, ...)), the PTEST is redundant since the // flags are set based on the same mask 'PG', but PTEST_LIKE must operate // on 8-bit predicates like the PTEST. Otherwise, for instructions like @@ -1421,55 +1425,66 @@ bool AArch64InstrInfo::optimizePTestInstr( // identical regardless of element size. auto PTestLikeMask = MRI->getUniqueVRegDef(Pred->getOperand(1).getReg()); uint64_t PredElementSize = getElementSizeForOpcode(PredOpcode); - if ((Mask != PTestLikeMask) || - (PredElementSize != AArch64::ElementSizeB && - PTest->getOpcode() != AArch64::PTEST_PP_ANY)) - return false; + if (Mask == PTestLikeMask && (PredElementSize == AArch64::ElementSizeB || + PTest->getOpcode() == AArch64::PTEST_PP_ANY)) + return PredOpcode; - // Fallthough to simply remove the PTEST. - } else { - // If OP in PTEST(PG, OP(PG, ...)) has a flag-setting variant change the - // opcode so the PTEST becomes redundant. - switch (PredOpcode) { - case AArch64::AND_PPzPP: - case AArch64::BIC_PPzPP: - case AArch64::EOR_PPzPP: - case AArch64::NAND_PPzPP: - case AArch64::NOR_PPzPP: - case AArch64::ORN_PPzPP: - case AArch64::ORR_PPzPP: - case AArch64::BRKA_PPzP: - case AArch64::BRKPA_PPzPP: - case AArch64::BRKB_PPzP: - case AArch64::BRKPB_PPzPP: - case AArch64::RDFFR_PPz: { - // Check to see if our mask is the same. If not the resulting flag bits - // may be different and we can't remove the ptest. - auto *PredMask = MRI->getUniqueVRegDef(Pred->getOperand(1).getReg()); - if (Mask != PredMask) - return false; - break; - } - case AArch64::BRKN_PPzP: { - // BRKN uses an all active implicit mask to set flags unlike the other - // flag-setting instructions. - // PTEST(PTRUE_B(31), BRKN(PG, A, B)) -> BRKNS(PG, A, B). - if ((MaskOpcode != AArch64::PTRUE_B) || - (Mask->getOperand(1).getImm() != 31)) - return false; - break; - } - case AArch64::PTRUE_B: - // PTEST(OP=PTRUE_B(A), OP) -> PTRUES_B(A) - break; - default: - // Bail out if we don't recognize the input - return false; - } + return {}; + } - NewOp = convertToFlagSettingOpc(PredOpcode); - OpChanged = true; + // If OP in PTEST(PG, OP(PG, ...)) has a flag-setting variant change the + // opcode so the PTEST becomes redundant. + switch (PredOpcode) { + case AArch64::AND_PPzPP: + case AArch64::BIC_PPzPP: + case AArch64::EOR_PPzPP: + case AArch64::NAND_PPzPP: + case AArch64::NOR_PPzPP: + case AArch64::ORN_PPzPP: + case AArch64::ORR_PPzPP: + case AArch64::BRKA_PPzP: + case AArch64::BRKPA_PPzPP: + case AArch64::BRKB_PPzP: + case AArch64::BRKPB_PPzPP: + case AArch64::RDFFR_PPz: { + // Check to see if our mask is the same. If not the resulting flag bits + // may be different and we can't remove the ptest. + auto *PredMask = MRI->getUniqueVRegDef(Pred->getOperand(1).getReg()); + if (Mask != PredMask) + return {}; + break; } + case AArch64::BRKN_PPzP: { + // BRKN uses an all active implicit mask to set flags unlike the other + // flag-setting instructions. + // PTEST(PTRUE_B(31), BRKN(PG, A, B)) -> BRKNS(PG, A, B). + if ((MaskOpcode != AArch64::PTRUE_B) || + (Mask->getOperand(1).getImm() != 31)) + return {}; + break; + } + case AArch64::PTRUE_B: + // PTEST(OP=PTRUE_B(A), OP) -> PTRUES_B(A) + break; + default: + // Bail out if we don't recognize the input + return {}; + } + + return convertToFlagSettingOpc(PredOpcode); +} + +/// optimizePTestInstr - Attempt to remove a ptest of a predicate-generating +/// operation which could set the flags in an identical manner +bool AArch64InstrInfo::optimizePTestInstr( + MachineInstr *PTest, unsigned MaskReg, unsigned PredReg, + const MachineRegisterInfo *MRI) const { + auto *Mask = MRI->getUniqueVRegDef(MaskReg); + auto *Pred = MRI->getUniqueVRegDef(PredReg); + unsigned PredOpcode = Pred->getOpcode(); + auto NewOp = canRemovePTestInstr(PTest, Mask, Pred, MRI); + if (!NewOp) + return false; const TargetRegisterInfo *TRI = &getRegisterInfo(); @@ -1482,9 +1497,9 @@ bool AArch64InstrInfo::optimizePTestInstr( // as they are prior to PTEST. Sometimes this requires the tested PTEST // operand to be replaced with an equivalent instruction that also sets the // flags. - Pred->setDesc(get(NewOp)); PTest->eraseFromParent(); - if (OpChanged) { + if (*NewOp != PredOpcode) { + Pred->setDesc(get(*NewOp)); bool succeeded = UpdateOperandRegClass(*Pred); (void)succeeded; assert(succeeded && "Operands have incompatible register classes!"); diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.h b/llvm/lib/Target/AArch64/AArch64InstrInfo.h index f434799c3982b..792e0c3063b10 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.h +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.h @@ -572,6 +572,9 @@ class AArch64InstrInfo final : public AArch64GenInstrInfo { bool optimizePTestInstr(MachineInstr *PTest, unsigned MaskReg, unsigned PredReg, const MachineRegisterInfo *MRI) const; + std::optional + canRemovePTestInstr(MachineInstr *PTest, MachineInstr *Mask, + MachineInstr *Pred, const MachineRegisterInfo *MRI) const; }; struct UsedNZCV {