1fe6060f1SDimitry Andric //===- AArch64GlobalISelUtils.cpp --------------------------------*- C++ -*-==// 2fe6060f1SDimitry Andric // 3fe6060f1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4fe6060f1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5fe6060f1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6fe6060f1SDimitry Andric // 7fe6060f1SDimitry Andric //===----------------------------------------------------------------------===// 8fe6060f1SDimitry Andric /// \file Implementations of AArch64-specific helper functions used in the 9fe6060f1SDimitry Andric /// GlobalISel pipeline. 10fe6060f1SDimitry Andric //===----------------------------------------------------------------------===// 11fe6060f1SDimitry Andric #include "AArch64GlobalISelUtils.h" 12fe6060f1SDimitry Andric #include "AArch64InstrInfo.h" 13fe6060f1SDimitry Andric #include "llvm/CodeGen/GlobalISel/Utils.h" 14fe6060f1SDimitry Andric #include "llvm/CodeGen/TargetLowering.h" 15fe6060f1SDimitry Andric #include "llvm/IR/InstrTypes.h" 16fe6060f1SDimitry Andric #include "llvm/Support/raw_ostream.h" 17fe6060f1SDimitry Andric 18fe6060f1SDimitry Andric using namespace llvm; 19fe6060f1SDimitry Andric 20bdd1243dSDimitry Andric std::optional<RegOrConstant> 21fe6060f1SDimitry Andric AArch64GISelUtils::getAArch64VectorSplat(const MachineInstr &MI, 22fe6060f1SDimitry Andric const MachineRegisterInfo &MRI) { 23fe6060f1SDimitry Andric if (auto Splat = getVectorSplat(MI, MRI)) 24fe6060f1SDimitry Andric return Splat; 25fe6060f1SDimitry Andric if (MI.getOpcode() != AArch64::G_DUP) 26bdd1243dSDimitry Andric return std::nullopt; 27fe6060f1SDimitry Andric Register Src = MI.getOperand(1).getReg(); 28fe6060f1SDimitry Andric if (auto ValAndVReg = 29349cc55cSDimitry Andric getAnyConstantVRegValWithLookThrough(MI.getOperand(1).getReg(), MRI)) 30fe6060f1SDimitry Andric return RegOrConstant(ValAndVReg->Value.getSExtValue()); 31fe6060f1SDimitry Andric return RegOrConstant(Src); 32fe6060f1SDimitry Andric } 33fe6060f1SDimitry Andric 34bdd1243dSDimitry Andric std::optional<int64_t> 35fe6060f1SDimitry Andric AArch64GISelUtils::getAArch64VectorSplatScalar(const MachineInstr &MI, 36fe6060f1SDimitry Andric const MachineRegisterInfo &MRI) { 37fe6060f1SDimitry Andric auto Splat = getAArch64VectorSplat(MI, MRI); 38fe6060f1SDimitry Andric if (!Splat || Splat->isReg()) 39bdd1243dSDimitry Andric return std::nullopt; 40fe6060f1SDimitry Andric return Splat->getCst(); 41fe6060f1SDimitry Andric } 42fe6060f1SDimitry Andric 43fe6060f1SDimitry Andric bool AArch64GISelUtils::isCMN(const MachineInstr *MaybeSub, 44fe6060f1SDimitry Andric const CmpInst::Predicate &Pred, 45fe6060f1SDimitry Andric const MachineRegisterInfo &MRI) { 46fe6060f1SDimitry Andric // Match: 47fe6060f1SDimitry Andric // 48fe6060f1SDimitry Andric // %sub = G_SUB 0, %y 49fe6060f1SDimitry Andric // %cmp = G_ICMP eq/ne, %sub, %z 50fe6060f1SDimitry Andric // 51fe6060f1SDimitry Andric // Or 52fe6060f1SDimitry Andric // 53fe6060f1SDimitry Andric // %sub = G_SUB 0, %y 54fe6060f1SDimitry Andric // %cmp = G_ICMP eq/ne, %z, %sub 55fe6060f1SDimitry Andric if (!MaybeSub || MaybeSub->getOpcode() != TargetOpcode::G_SUB || 56fe6060f1SDimitry Andric !CmpInst::isEquality(Pred)) 57fe6060f1SDimitry Andric return false; 58fe6060f1SDimitry Andric auto MaybeZero = 59349cc55cSDimitry Andric getIConstantVRegValWithLookThrough(MaybeSub->getOperand(1).getReg(), MRI); 60fe6060f1SDimitry Andric return MaybeZero && MaybeZero->Value.getZExtValue() == 0; 61fe6060f1SDimitry Andric } 62fe6060f1SDimitry Andric 63fe6060f1SDimitry Andric bool AArch64GISelUtils::tryEmitBZero(MachineInstr &MI, 64fe6060f1SDimitry Andric MachineIRBuilder &MIRBuilder, 65fe6060f1SDimitry Andric bool MinSize) { 66fe6060f1SDimitry Andric assert(MI.getOpcode() == TargetOpcode::G_MEMSET); 67fe6060f1SDimitry Andric MachineRegisterInfo &MRI = *MIRBuilder.getMRI(); 68fe6060f1SDimitry Andric auto &TLI = *MIRBuilder.getMF().getSubtarget().getTargetLowering(); 69fe6060f1SDimitry Andric if (!TLI.getLibcallName(RTLIB::BZERO)) 70fe6060f1SDimitry Andric return false; 71349cc55cSDimitry Andric auto Zero = 72349cc55cSDimitry Andric getIConstantVRegValWithLookThrough(MI.getOperand(1).getReg(), MRI); 73fe6060f1SDimitry Andric if (!Zero || Zero->Value.getSExtValue() != 0) 74fe6060f1SDimitry Andric return false; 75fe6060f1SDimitry Andric 76fe6060f1SDimitry Andric // It's not faster to use bzero rather than memset for sizes <= 256. 77fe6060f1SDimitry Andric // However, it *does* save us a mov from wzr, so if we're going for 78fe6060f1SDimitry Andric // minsize, use bzero even if it's slower. 79fe6060f1SDimitry Andric if (!MinSize) { 80fe6060f1SDimitry Andric // If the size is known, check it. If it is not known, assume using bzero is 81fe6060f1SDimitry Andric // better. 82349cc55cSDimitry Andric if (auto Size = getIConstantVRegValWithLookThrough( 83349cc55cSDimitry Andric MI.getOperand(2).getReg(), MRI)) { 84fe6060f1SDimitry Andric if (Size->Value.getSExtValue() <= 256) 85fe6060f1SDimitry Andric return false; 86fe6060f1SDimitry Andric } 87fe6060f1SDimitry Andric } 88fe6060f1SDimitry Andric 89fe6060f1SDimitry Andric MIRBuilder.setInstrAndDebugLoc(MI); 90fe6060f1SDimitry Andric MIRBuilder 91fe6060f1SDimitry Andric .buildInstr(TargetOpcode::G_BZERO, {}, 92fe6060f1SDimitry Andric {MI.getOperand(0), MI.getOperand(2)}) 93fe6060f1SDimitry Andric .addImm(MI.getOperand(3).getImm()) 94fe6060f1SDimitry Andric .addMemOperand(*MI.memoperands_begin()); 95fe6060f1SDimitry Andric MI.eraseFromParent(); 96fe6060f1SDimitry Andric return true; 97fe6060f1SDimitry Andric } 98fe6060f1SDimitry Andric 99*0fca6ea1SDimitry Andric std::tuple<uint16_t, Register> 100*0fca6ea1SDimitry Andric AArch64GISelUtils::extractPtrauthBlendDiscriminators(Register Disc, 101*0fca6ea1SDimitry Andric MachineRegisterInfo &MRI) { 102*0fca6ea1SDimitry Andric Register AddrDisc = Disc; 103*0fca6ea1SDimitry Andric uint16_t ConstDisc = 0; 104*0fca6ea1SDimitry Andric 105*0fca6ea1SDimitry Andric if (auto ConstDiscVal = getIConstantVRegVal(Disc, MRI)) { 106*0fca6ea1SDimitry Andric if (isUInt<16>(ConstDiscVal->getZExtValue())) { 107*0fca6ea1SDimitry Andric ConstDisc = ConstDiscVal->getZExtValue(); 108*0fca6ea1SDimitry Andric AddrDisc = AArch64::NoRegister; 109*0fca6ea1SDimitry Andric } 110*0fca6ea1SDimitry Andric return std::make_tuple(ConstDisc, AddrDisc); 111*0fca6ea1SDimitry Andric } 112*0fca6ea1SDimitry Andric 113*0fca6ea1SDimitry Andric const MachineInstr *DiscMI = MRI.getVRegDef(Disc); 114*0fca6ea1SDimitry Andric if (!DiscMI || DiscMI->getOpcode() != TargetOpcode::G_INTRINSIC || 115*0fca6ea1SDimitry Andric DiscMI->getOperand(1).getIntrinsicID() != Intrinsic::ptrauth_blend) 116*0fca6ea1SDimitry Andric return std::make_tuple(ConstDisc, AddrDisc); 117*0fca6ea1SDimitry Andric 118*0fca6ea1SDimitry Andric if (auto ConstDiscVal = 119*0fca6ea1SDimitry Andric getIConstantVRegVal(DiscMI->getOperand(3).getReg(), MRI)) { 120*0fca6ea1SDimitry Andric if (isUInt<16>(ConstDiscVal->getZExtValue())) { 121*0fca6ea1SDimitry Andric ConstDisc = ConstDiscVal->getZExtValue(); 122*0fca6ea1SDimitry Andric AddrDisc = DiscMI->getOperand(2).getReg(); 123*0fca6ea1SDimitry Andric } 124*0fca6ea1SDimitry Andric } 125*0fca6ea1SDimitry Andric return std::make_tuple(ConstDisc, AddrDisc); 126*0fca6ea1SDimitry Andric } 127*0fca6ea1SDimitry Andric 128fe6060f1SDimitry Andric void AArch64GISelUtils::changeFCMPPredToAArch64CC( 129fe6060f1SDimitry Andric const CmpInst::Predicate P, AArch64CC::CondCode &CondCode, 130fe6060f1SDimitry Andric AArch64CC::CondCode &CondCode2) { 131fe6060f1SDimitry Andric CondCode2 = AArch64CC::AL; 132fe6060f1SDimitry Andric switch (P) { 133fe6060f1SDimitry Andric default: 134fe6060f1SDimitry Andric llvm_unreachable("Unknown FP condition!"); 135fe6060f1SDimitry Andric case CmpInst::FCMP_OEQ: 136fe6060f1SDimitry Andric CondCode = AArch64CC::EQ; 137fe6060f1SDimitry Andric break; 138fe6060f1SDimitry Andric case CmpInst::FCMP_OGT: 139fe6060f1SDimitry Andric CondCode = AArch64CC::GT; 140fe6060f1SDimitry Andric break; 141fe6060f1SDimitry Andric case CmpInst::FCMP_OGE: 142fe6060f1SDimitry Andric CondCode = AArch64CC::GE; 143fe6060f1SDimitry Andric break; 144fe6060f1SDimitry Andric case CmpInst::FCMP_OLT: 145fe6060f1SDimitry Andric CondCode = AArch64CC::MI; 146fe6060f1SDimitry Andric break; 147fe6060f1SDimitry Andric case CmpInst::FCMP_OLE: 148fe6060f1SDimitry Andric CondCode = AArch64CC::LS; 149fe6060f1SDimitry Andric break; 150fe6060f1SDimitry Andric case CmpInst::FCMP_ONE: 151fe6060f1SDimitry Andric CondCode = AArch64CC::MI; 152fe6060f1SDimitry Andric CondCode2 = AArch64CC::GT; 153fe6060f1SDimitry Andric break; 154fe6060f1SDimitry Andric case CmpInst::FCMP_ORD: 155fe6060f1SDimitry Andric CondCode = AArch64CC::VC; 156fe6060f1SDimitry Andric break; 157fe6060f1SDimitry Andric case CmpInst::FCMP_UNO: 158fe6060f1SDimitry Andric CondCode = AArch64CC::VS; 159fe6060f1SDimitry Andric break; 160fe6060f1SDimitry Andric case CmpInst::FCMP_UEQ: 161fe6060f1SDimitry Andric CondCode = AArch64CC::EQ; 162fe6060f1SDimitry Andric CondCode2 = AArch64CC::VS; 163fe6060f1SDimitry Andric break; 164fe6060f1SDimitry Andric case CmpInst::FCMP_UGT: 165fe6060f1SDimitry Andric CondCode = AArch64CC::HI; 166fe6060f1SDimitry Andric break; 167fe6060f1SDimitry Andric case CmpInst::FCMP_UGE: 168fe6060f1SDimitry Andric CondCode = AArch64CC::PL; 169fe6060f1SDimitry Andric break; 170fe6060f1SDimitry Andric case CmpInst::FCMP_ULT: 171fe6060f1SDimitry Andric CondCode = AArch64CC::LT; 172fe6060f1SDimitry Andric break; 173fe6060f1SDimitry Andric case CmpInst::FCMP_ULE: 174fe6060f1SDimitry Andric CondCode = AArch64CC::LE; 175fe6060f1SDimitry Andric break; 176fe6060f1SDimitry Andric case CmpInst::FCMP_UNE: 177fe6060f1SDimitry Andric CondCode = AArch64CC::NE; 178fe6060f1SDimitry Andric break; 1793a079333SDimitry Andric case CmpInst::FCMP_TRUE: 1803a079333SDimitry Andric CondCode = AArch64CC::AL; 1813a079333SDimitry Andric break; 1823a079333SDimitry Andric case CmpInst::FCMP_FALSE: 1833a079333SDimitry Andric CondCode = AArch64CC::NV; 1843a079333SDimitry Andric break; 185fe6060f1SDimitry Andric } 186fe6060f1SDimitry Andric } 187fe6060f1SDimitry Andric 188fe6060f1SDimitry Andric void AArch64GISelUtils::changeVectorFCMPPredToAArch64CC( 189fe6060f1SDimitry Andric const CmpInst::Predicate P, AArch64CC::CondCode &CondCode, 190fe6060f1SDimitry Andric AArch64CC::CondCode &CondCode2, bool &Invert) { 191fe6060f1SDimitry Andric Invert = false; 192fe6060f1SDimitry Andric switch (P) { 193fe6060f1SDimitry Andric default: 194fe6060f1SDimitry Andric // Mostly the scalar mappings work fine. 195fe6060f1SDimitry Andric changeFCMPPredToAArch64CC(P, CondCode, CondCode2); 196fe6060f1SDimitry Andric break; 197fe6060f1SDimitry Andric case CmpInst::FCMP_UNO: 198fe6060f1SDimitry Andric Invert = true; 199bdd1243dSDimitry Andric [[fallthrough]]; 200fe6060f1SDimitry Andric case CmpInst::FCMP_ORD: 201fe6060f1SDimitry Andric CondCode = AArch64CC::MI; 202fe6060f1SDimitry Andric CondCode2 = AArch64CC::GE; 203fe6060f1SDimitry Andric break; 204fe6060f1SDimitry Andric case CmpInst::FCMP_UEQ: 205fe6060f1SDimitry Andric case CmpInst::FCMP_ULT: 206fe6060f1SDimitry Andric case CmpInst::FCMP_ULE: 207fe6060f1SDimitry Andric case CmpInst::FCMP_UGT: 208fe6060f1SDimitry Andric case CmpInst::FCMP_UGE: 209fe6060f1SDimitry Andric // All of the compare-mask comparisons are ordered, but we can switch 210fe6060f1SDimitry Andric // between the two by a double inversion. E.g. ULE == !OGT. 211fe6060f1SDimitry Andric Invert = true; 212fe6060f1SDimitry Andric changeFCMPPredToAArch64CC(CmpInst::getInversePredicate(P), CondCode, 213fe6060f1SDimitry Andric CondCode2); 214fe6060f1SDimitry Andric break; 215fe6060f1SDimitry Andric } 216fe6060f1SDimitry Andric } 217