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
18using namespace llvm;
19
20std::optional<RegOrConstant>
21AArch64GISelUtils::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(i: 1).getReg();
28 if (auto ValAndVReg =
29 getAnyConstantVRegValWithLookThrough(VReg: MI.getOperand(i: 1).getReg(), MRI))
30 return RegOrConstant(ValAndVReg->Value.getSExtValue());
31 return RegOrConstant(Src);
32}
33
34std::optional<int64_t>
35AArch64GISelUtils::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
43bool 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: Pred))
57 return false;
58 auto MaybeZero =
59 getIConstantVRegValWithLookThrough(VReg: MaybeSub->getOperand(i: 1).getReg(), MRI);
60 return MaybeZero && MaybeZero->Value.getZExtValue() == 0;
61}
62
63bool 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(Call: RTLIB::BZERO))
70 return false;
71 auto Zero =
72 getIConstantVRegValWithLookThrough(VReg: MI.getOperand(i: 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 VReg: MI.getOperand(i: 2).getReg(), MRI)) {
84 if (Size->Value.getSExtValue() <= 256)
85 return false;
86 }
87 }
88
89 MIRBuilder.setInstrAndDebugLoc(MI);
90 MIRBuilder
91 .buildInstr(Opc: TargetOpcode::G_BZERO, DstOps: {},
92 SrcOps: {MI.getOperand(i: 0), MI.getOperand(i: 2)})
93 .addImm(Val: MI.getOperand(i: 3).getImm())
94 .addMemOperand(MMO: *MI.memoperands_begin());
95 MI.eraseFromParent();
96 return true;
97}
98
99std::tuple<uint16_t, Register>
100AArch64GISelUtils::extractPtrauthBlendDiscriminators(Register Disc,
101 MachineRegisterInfo &MRI) {
102 Register AddrDisc = Disc;
103 uint16_t ConstDisc = 0;
104
105 if (auto ConstDiscVal = getIConstantVRegVal(VReg: Disc, MRI)) {
106 if (isUInt<16>(x: ConstDiscVal->getZExtValue())) {
107 ConstDisc = ConstDiscVal->getZExtValue();
108 AddrDisc = AArch64::NoRegister;
109 }
110 return std::make_tuple(args&: ConstDisc, args&: AddrDisc);
111 }
112
113 const MachineInstr *DiscMI = MRI.getVRegDef(Reg: Disc);
114 if (!DiscMI || DiscMI->getOpcode() != TargetOpcode::G_INTRINSIC ||
115 DiscMI->getOperand(i: 1).getIntrinsicID() != Intrinsic::ptrauth_blend)
116 return std::make_tuple(args&: ConstDisc, args&: AddrDisc);
117
118 if (auto ConstDiscVal =
119 getIConstantVRegVal(VReg: DiscMI->getOperand(i: 3).getReg(), MRI)) {
120 if (isUInt<16>(x: ConstDiscVal->getZExtValue())) {
121 ConstDisc = ConstDiscVal->getZExtValue();
122 AddrDisc = DiscMI->getOperand(i: 2).getReg();
123 }
124 }
125 return std::make_tuple(args&: ConstDisc, args&: AddrDisc);
126}
127
128void 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
188void 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(P: CmpInst::getInversePredicate(pred: P), CondCode,
213 CondCode2);
214 break;
215 }
216}
217