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