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