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>
getAArch64VectorSplat(const MachineInstr & MI,const MachineRegisterInfo & MRI)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>
getAArch64VectorSplatScalar(const MachineInstr & MI,const MachineRegisterInfo & MRI)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
isCMN(const MachineInstr * MaybeSub,const CmpInst::Predicate & Pred,const MachineRegisterInfo & MRI)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
tryEmitBZero(MachineInstr & MI,MachineIRBuilder & MIRBuilder,bool MinSize)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>
extractPtrauthBlendDiscriminators(Register Disc,MachineRegisterInfo & MRI)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
changeFCMPPredToAArch64CC(const CmpInst::Predicate P,AArch64CC::CondCode & CondCode,AArch64CC::CondCode & CondCode2)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
changeVectorFCMPPredToAArch64CC(const CmpInst::Predicate P,AArch64CC::CondCode & CondCode,AArch64CC::CondCode & CondCode2,bool & Invert)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