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(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 | |
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: 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 | |
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(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 | |
97 | std::tuple<uint16_t, Register> |
98 | AArch64GISelUtils::(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 | |
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(P: CmpInst::getInversePredicate(pred: P), CondCode, |
211 | CondCode2); |
212 | break; |
213 | } |
214 | } |
215 | |