xref: /freebsd/contrib/llvm-project/llvm/lib/Target/AArch64/GISel/AArch64GlobalISelUtils.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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>
getAArch64VectorSplat(const MachineInstr & MI,const MachineRegisterInfo & MRI)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>
getAArch64VectorSplatScalar(const MachineInstr & MI,const MachineRegisterInfo & MRI)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 
isCMN(const MachineInstr * MaybeSub,const CmpInst::Predicate & Pred,const MachineRegisterInfo & MRI)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 
tryEmitBZero(MachineInstr & MI,MachineIRBuilder & MIRBuilder,bool MinSize)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>
extractPtrauthBlendDiscriminators(Register Disc,MachineRegisterInfo & MRI)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 
changeFCMPPredToAArch64CC(const CmpInst::Predicate P,AArch64CC::CondCode & CondCode,AArch64CC::CondCode & CondCode2)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 
changeVectorFCMPPredToAArch64CC(const CmpInst::Predicate P,AArch64CC::CondCode & CondCode,AArch64CC::CondCode & CondCode2,bool & Invert)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