xref: /llvm-project/llvm/lib/Target/AArch64/GISel/AArch64GlobalISelUtils.cpp (revision 1e25c921d523151e6ed2ffe86029ea2e2b267a6c)
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