xref: /freebsd-src/contrib/llvm-project/llvm/lib/Target/AArch64/GISel/AArch64GlobalISelUtils.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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