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 | |
18 | using namespace llvm; |
19 | |
20 | std::optional<RegOrConstant> |
21 | AArch64GISelUtils::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 | |
34 | std::optional<int64_t> |
35 | AArch64GISelUtils::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 | |
43 | bool 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 | |
63 | bool 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 | |
99 | std::tuple<uint16_t, Register> |
100 | AArch64GISelUtils::(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 | |
128 | void 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 | |
188 | void 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 | |