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 =
27 getAnyConstantVRegValWithLookThrough(VReg: MI.getOperand(i: 1).getReg(), MRI))
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 if (!MaybeSub || MaybeSub->getOpcode() != TargetOpcode::G_SUB ||
54 !CmpInst::isEquality(pred: Pred))
55 return false;
56 auto MaybeZero =
57 getIConstantVRegValWithLookThrough(VReg: MaybeSub->getOperand(i: 1).getReg(), MRI);
58 return MaybeZero && MaybeZero->Value.getZExtValue() == 0;
59}
60
61bool 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(Call: RTLIB::BZERO))
68 return false;
69 auto Zero =
70 getIConstantVRegValWithLookThrough(VReg: MI.getOperand(i: 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 VReg: MI.getOperand(i: 2).getReg(), MRI)) {
82 if (Size->Value.getSExtValue() <= 256)
83 return false;
84 }
85 }
86
87 MIRBuilder.setInstrAndDebugLoc(MI);
88 MIRBuilder
89 .buildInstr(Opc: TargetOpcode::G_BZERO, DstOps: {},
90 SrcOps: {MI.getOperand(i: 0), MI.getOperand(i: 2)})
91 .addImm(Val: MI.getOperand(i: 3).getImm())
92 .addMemOperand(MMO: *MI.memoperands_begin());
93 MI.eraseFromParent();
94 return true;
95}
96
97std::tuple<uint16_t, Register>
98AArch64GISelUtils::extractPtrauthBlendDiscriminators(Register Disc,
99 MachineRegisterInfo &MRI) {
100 Register AddrDisc = Disc;
101 uint16_t ConstDisc = 0;
102
103 if (auto ConstDiscVal = getIConstantVRegVal(VReg: Disc, MRI)) {
104 if (isUInt<16>(x: ConstDiscVal->getZExtValue())) {
105 ConstDisc = ConstDiscVal->getZExtValue();
106 AddrDisc = AArch64::NoRegister;
107 }
108 return std::make_tuple(args&: ConstDisc, args&: AddrDisc);
109 }
110
111 const MachineInstr *DiscMI = MRI.getVRegDef(Reg: Disc);
112 if (!DiscMI || DiscMI->getOpcode() != TargetOpcode::G_INTRINSIC ||
113 DiscMI->getOperand(i: 1).getIntrinsicID() != Intrinsic::ptrauth_blend)
114 return std::make_tuple(args&: ConstDisc, args&: AddrDisc);
115
116 if (auto ConstDiscVal =
117 getIConstantVRegVal(VReg: DiscMI->getOperand(i: 3).getReg(), MRI)) {
118 if (isUInt<16>(x: ConstDiscVal->getZExtValue())) {
119 ConstDisc = ConstDiscVal->getZExtValue();
120 AddrDisc = DiscMI->getOperand(i: 2).getReg();
121 }
122 }
123 return std::make_tuple(args&: ConstDisc, args&: AddrDisc);
124}
125
126void 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
186void 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(P: CmpInst::getInversePredicate(pred: P), CondCode,
211 CondCode2);
212 break;
213 }
214}
215