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