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
16using namespace llvm;
17
18std::optional<RegOrConstant>
19AArch64GISelUtils::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(i: 1).getReg();
26 if (auto ValAndVReg = getAnyConstantVRegValWithLookThrough(
27 VReg: Src, MRI, /*LookThroughInstrs=*/true, /*LookThroughAnyExt=*/true))
28 return RegOrConstant(ValAndVReg->Value.getSExtValue());
29 return RegOrConstant(Src);
30}
31
32std::optional<int64_t>
33AArch64GISelUtils::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
41bool 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 // or with signed comparisons with the no-signed-wrap flag set
54 if (!MaybeSub || MaybeSub->getOpcode() != TargetOpcode::G_SUB ||
55 (!CmpInst::isEquality(pred: Pred) &&
56 !(CmpInst::isSigned(Pred) && MaybeSub->getFlag(Flag: MachineInstr::NoSWrap))))
57 return false;
58 auto MaybeZero =
59 getIConstantVRegValWithLookThrough(VReg: MaybeSub->getOperand(i: 1).getReg(), MRI);
60 return MaybeZero && MaybeZero->Value.getZExtValue() == 0;
61}
62
63void AArch64GISelUtils::applyEmitBZero(MachineInstr &MI,
64 MachineIRBuilder &MIRBuilder) {
65 assert(MI.getOpcode() == TargetOpcode::G_MEMSET);
66
67 MIRBuilder.setInstrAndDebugLoc(MI);
68 MIRBuilder
69 .buildInstr(Opc: TargetOpcode::G_BZERO, DstOps: {},
70 SrcOps: {MI.getOperand(i: 0), MI.getOperand(i: 2)})
71 .addImm(Val: MI.getOperand(i: 3).getImm())
72 .addMemOperand(MMO: *MI.memoperands_begin());
73 MI.eraseFromParent();
74}
75
76bool AArch64GISelUtils::matchEmitBZero(const MachineInstr &MI,
77 const MachineRegisterInfo &MRI,
78 const LibcallLoweringInfo &Libcalls,
79 bool MinSize) {
80 assert(MI.getOpcode() == TargetOpcode::G_MEMSET);
81 if (Libcalls.getLibcallImpl(Call: RTLIB::BZERO) == RTLIB::Unsupported)
82 return false;
83
84 auto Zero =
85 getIConstantVRegValWithLookThrough(VReg: MI.getOperand(i: 1).getReg(), MRI);
86 if (!Zero || Zero->Value.getSExtValue() != 0)
87 return false;
88
89 // It's not faster to use bzero rather than memset for sizes <= 256.
90 // However, it *does* save us a mov from wzr, so if we're going for
91 // minsize, use bzero even if it's slower.
92 if (!MinSize) {
93 // If the size is known, check it. If it is not known, assume using bzero is
94 // better.
95 if (auto Size = getIConstantVRegValWithLookThrough(
96 VReg: MI.getOperand(i: 2).getReg(), MRI)) {
97 if (Size->Value.getSExtValue() <= 256)
98 return false;
99 }
100 }
101 return true;
102}
103
104std::tuple<uint16_t, Register>
105AArch64GISelUtils::extractPtrauthBlendDiscriminators(Register Disc,
106 MachineRegisterInfo &MRI) {
107 Register AddrDisc = Disc;
108 uint16_t ConstDisc = 0;
109
110 if (auto ConstDiscVal = getIConstantVRegVal(VReg: Disc, MRI)) {
111 if (isUInt<16>(x: ConstDiscVal->getZExtValue())) {
112 ConstDisc = ConstDiscVal->getZExtValue();
113 AddrDisc = AArch64::NoRegister;
114 }
115 return std::make_tuple(args&: ConstDisc, args&: AddrDisc);
116 }
117
118 const MachineInstr *DiscMI = MRI.getVRegDef(Reg: Disc);
119 if (!DiscMI || DiscMI->getOpcode() != TargetOpcode::G_INTRINSIC ||
120 DiscMI->getOperand(i: 1).getIntrinsicID() != Intrinsic::ptrauth_blend)
121 return std::make_tuple(args&: ConstDisc, args&: AddrDisc);
122
123 if (auto ConstDiscVal =
124 getIConstantVRegVal(VReg: DiscMI->getOperand(i: 3).getReg(), MRI)) {
125 if (isUInt<16>(x: ConstDiscVal->getZExtValue())) {
126 ConstDisc = ConstDiscVal->getZExtValue();
127 AddrDisc = DiscMI->getOperand(i: 2).getReg();
128 }
129 }
130 return std::make_tuple(args&: ConstDisc, args&: AddrDisc);
131}
132
133void AArch64GISelUtils::changeFCMPPredToAArch64CC(
134 const CmpInst::Predicate P, AArch64CC::CondCode &CondCode,
135 AArch64CC::CondCode &CondCode2) {
136 CondCode2 = AArch64CC::AL;
137 switch (P) {
138 default:
139 llvm_unreachable("Unknown FP condition!");
140 case CmpInst::FCMP_OEQ:
141 CondCode = AArch64CC::EQ;
142 break;
143 case CmpInst::FCMP_OGT:
144 CondCode = AArch64CC::GT;
145 break;
146 case CmpInst::FCMP_OGE:
147 CondCode = AArch64CC::GE;
148 break;
149 case CmpInst::FCMP_OLT:
150 CondCode = AArch64CC::MI;
151 break;
152 case CmpInst::FCMP_OLE:
153 CondCode = AArch64CC::LS;
154 break;
155 case CmpInst::FCMP_ONE:
156 CondCode = AArch64CC::MI;
157 CondCode2 = AArch64CC::GT;
158 break;
159 case CmpInst::FCMP_ORD:
160 CondCode = AArch64CC::VC;
161 break;
162 case CmpInst::FCMP_UNO:
163 CondCode = AArch64CC::VS;
164 break;
165 case CmpInst::FCMP_UEQ:
166 CondCode = AArch64CC::EQ;
167 CondCode2 = AArch64CC::VS;
168 break;
169 case CmpInst::FCMP_UGT:
170 CondCode = AArch64CC::HI;
171 break;
172 case CmpInst::FCMP_UGE:
173 CondCode = AArch64CC::PL;
174 break;
175 case CmpInst::FCMP_ULT:
176 CondCode = AArch64CC::LT;
177 break;
178 case CmpInst::FCMP_ULE:
179 CondCode = AArch64CC::LE;
180 break;
181 case CmpInst::FCMP_UNE:
182 CondCode = AArch64CC::NE;
183 break;
184 case CmpInst::FCMP_TRUE:
185 CondCode = AArch64CC::AL;
186 break;
187 case CmpInst::FCMP_FALSE:
188 CondCode = AArch64CC::NV;
189 break;
190 }
191}
192
193void AArch64GISelUtils::changeVectorFCMPPredToAArch64CC(
194 const CmpInst::Predicate P, AArch64CC::CondCode &CondCode,
195 AArch64CC::CondCode &CondCode2, bool &Invert) {
196 Invert = false;
197 switch (P) {
198 default:
199 // Mostly the scalar mappings work fine.
200 changeFCMPPredToAArch64CC(P, CondCode, CondCode2);
201 break;
202 case CmpInst::FCMP_UNO:
203 Invert = true;
204 [[fallthrough]];
205 case CmpInst::FCMP_ORD:
206 CondCode = AArch64CC::MI;
207 CondCode2 = AArch64CC::GE;
208 break;
209 case CmpInst::FCMP_UEQ:
210 case CmpInst::FCMP_ULT:
211 case CmpInst::FCMP_ULE:
212 case CmpInst::FCMP_UGT:
213 case CmpInst::FCMP_UGE:
214 // All of the compare-mask comparisons are ordered, but we can switch
215 // between the two by a double inversion. E.g. ULE == !OGT.
216 Invert = true;
217 changeFCMPPredToAArch64CC(P: CmpInst::getInversePredicate(pred: P), CondCode,
218 CondCode2);
219 break;
220 }
221}
222