1//=== lib/CodeGen/GlobalISel/AArch64PreLegalizerCombiner.cpp --------------===//
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//
9// This pass does combining of machine instructions at the generic MI level,
10// before the legalizer.
11//
12//===----------------------------------------------------------------------===//
13
14#include "AArch64.h"
15#include "AArch64GlobalISelUtils.h"
16#include "AArch64TargetMachine.h"
17#include "llvm/CodeGen/GlobalISel/CSEInfo.h"
18#include "llvm/CodeGen/GlobalISel/Combiner.h"
19#include "llvm/CodeGen/GlobalISel/CombinerHelper.h"
20#include "llvm/CodeGen/GlobalISel/CombinerInfo.h"
21#include "llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h"
22#include "llvm/CodeGen/GlobalISel/GISelValueTracking.h"
23#include "llvm/CodeGen/GlobalISel/MIPatternMatch.h"
24#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
25#include "llvm/CodeGen/GlobalISel/Utils.h"
26#include "llvm/CodeGen/LibcallLoweringInfo.h"
27#include "llvm/CodeGen/MachineDominators.h"
28#include "llvm/CodeGen/MachineFunction.h"
29#include "llvm/CodeGen/MachineFunctionAnalysisManager.h"
30#include "llvm/CodeGen/MachineFunctionPass.h"
31#include "llvm/CodeGen/MachinePassManager.h"
32#include "llvm/CodeGen/MachineRegisterInfo.h"
33#include "llvm/IR/Instructions.h"
34#include <memory>
35
36#define GET_GICOMBINER_DEPS
37#include "AArch64GenPreLegalizeGICombiner.inc"
38#undef GET_GICOMBINER_DEPS
39
40#define DEBUG_TYPE "aarch64-prelegalizer-combiner"
41
42using namespace llvm;
43using namespace MIPatternMatch;
44
45#define GET_GICOMBINER_TYPES
46#include "AArch64GenPreLegalizeGICombiner.inc"
47#undef GET_GICOMBINER_TYPES
48
49namespace {
50
51/// Try to match a G_ICMP of a G_TRUNC with zero, in which the truncated bits
52/// are sign bits. In this case, we can transform the G_ICMP to directly compare
53/// the wide value with a zero.
54bool matchICmpRedundantTrunc(MachineInstr &MI, MachineRegisterInfo &MRI,
55 GISelValueTracking *VT, Register &MatchInfo) {
56 assert(MI.getOpcode() == TargetOpcode::G_ICMP && VT);
57
58 auto Pred = (CmpInst::Predicate)MI.getOperand(i: 1).getPredicate();
59 if (!ICmpInst::isEquality(P: Pred))
60 return false;
61
62 Register LHS = MI.getOperand(i: 2).getReg();
63 LLT LHSTy = MRI.getType(Reg: LHS);
64 if (!LHSTy.isScalar())
65 return false;
66
67 Register RHS = MI.getOperand(i: 3).getReg();
68 Register WideReg;
69
70 if (!mi_match(R: LHS, MRI, P: m_GTrunc(Src: m_Reg(R&: WideReg))) ||
71 !mi_match(R: RHS, MRI, P: m_SpecificICst(RequestedValue: 0)))
72 return false;
73
74 LLT WideTy = MRI.getType(Reg: WideReg);
75 if (VT->computeNumSignBits(R: WideReg) <=
76 WideTy.getSizeInBits() - LHSTy.getSizeInBits())
77 return false;
78
79 MatchInfo = WideReg;
80 return true;
81}
82
83void applyICmpRedundantTrunc(MachineInstr &MI, MachineRegisterInfo &MRI,
84 MachineIRBuilder &Builder,
85 GISelChangeObserver &Observer, Register &WideReg) {
86 assert(MI.getOpcode() == TargetOpcode::G_ICMP);
87
88 LLT WideTy = MRI.getType(Reg: WideReg);
89 // We're going to directly use the wide register as the LHS, and then use an
90 // equivalent size zero for RHS.
91 Builder.setInstrAndDebugLoc(MI);
92 auto WideZero = Builder.buildConstant(Res: WideTy, Val: 0);
93 Observer.changingInstr(MI);
94 MI.getOperand(i: 2).setReg(WideReg);
95 MI.getOperand(i: 3).setReg(WideZero.getReg(Idx: 0));
96 Observer.changedInstr(MI);
97}
98
99/// \returns true if it is possible to fold a constant into a G_GLOBAL_VALUE.
100///
101/// e.g.
102///
103/// %g = G_GLOBAL_VALUE @x -> %g = G_GLOBAL_VALUE @x + cst
104bool matchFoldGlobalOffset(MachineInstr &MI, MachineRegisterInfo &MRI,
105 std::pair<uint64_t, uint64_t> &MatchInfo) {
106 assert(MI.getOpcode() == TargetOpcode::G_GLOBAL_VALUE);
107 MachineFunction &MF = *MI.getMF();
108 auto &GlobalOp = MI.getOperand(i: 1);
109 auto *GV = GlobalOp.getGlobal();
110 if (GV->isThreadLocal())
111 return false;
112
113 // Don't allow anything that could represent offsets etc.
114 if (MF.getSubtarget<AArch64Subtarget>().ClassifyGlobalReference(
115 GV, TM: MF.getTarget()) != AArch64II::MO_NO_FLAG)
116 return false;
117
118 // Look for a G_GLOBAL_VALUE only used by G_PTR_ADDs against constants:
119 //
120 // %g = G_GLOBAL_VALUE @x
121 // %ptr1 = G_PTR_ADD %g, cst1
122 // %ptr2 = G_PTR_ADD %g, cst2
123 // ...
124 // %ptrN = G_PTR_ADD %g, cstN
125 //
126 // Identify the *smallest* constant. We want to be able to form this:
127 //
128 // %offset_g = G_GLOBAL_VALUE @x + min_cst
129 // %g = G_PTR_ADD %offset_g, -min_cst
130 // %ptr1 = G_PTR_ADD %g, cst1
131 // ...
132 Register Dst = MI.getOperand(i: 0).getReg();
133 uint64_t MinOffset = -1ull;
134 for (auto &UseInstr : MRI.use_nodbg_instructions(Reg: Dst)) {
135 if (UseInstr.getOpcode() != TargetOpcode::G_PTR_ADD)
136 return false;
137 auto Cst = getIConstantVRegValWithLookThrough(
138 VReg: UseInstr.getOperand(i: 2).getReg(), MRI);
139 if (!Cst)
140 return false;
141 MinOffset = std::min(a: MinOffset, b: Cst->Value.getZExtValue());
142 }
143
144 // Require that the new offset is larger than the existing one to avoid
145 // infinite loops.
146 uint64_t CurrOffset = GlobalOp.getOffset();
147 uint64_t NewOffset = MinOffset + CurrOffset;
148 if (NewOffset <= CurrOffset)
149 return false;
150
151 // Check whether folding this offset is legal. It must not go out of bounds of
152 // the referenced object to avoid violating the code model, and must be
153 // smaller than 2^20 because this is the largest offset expressible in all
154 // object formats. (The IMAGE_REL_ARM64_PAGEBASE_REL21 relocation in COFF
155 // stores an immediate signed 21 bit offset.)
156 //
157 // This check also prevents us from folding negative offsets, which will end
158 // up being treated in the same way as large positive ones. They could also
159 // cause code model violations, and aren't really common enough to matter.
160 if (NewOffset >= (1 << 20))
161 return false;
162
163 Type *T = GV->getValueType();
164 if (!T->isSized() ||
165 NewOffset > GV->getDataLayout().getTypeAllocSize(Ty: T))
166 return false;
167 MatchInfo = std::make_pair(x&: NewOffset, y&: MinOffset);
168 return true;
169}
170
171void applyFoldGlobalOffset(MachineInstr &MI, MachineRegisterInfo &MRI,
172 MachineIRBuilder &B, GISelChangeObserver &Observer,
173 std::pair<uint64_t, uint64_t> &MatchInfo) {
174 // Change:
175 //
176 // %g = G_GLOBAL_VALUE @x
177 // %ptr1 = G_PTR_ADD %g, cst1
178 // %ptr2 = G_PTR_ADD %g, cst2
179 // ...
180 // %ptrN = G_PTR_ADD %g, cstN
181 //
182 // To:
183 //
184 // %offset_g = G_GLOBAL_VALUE @x + min_cst
185 // %g = G_PTR_ADD %offset_g, -min_cst
186 // %ptr1 = G_PTR_ADD %g, cst1
187 // ...
188 // %ptrN = G_PTR_ADD %g, cstN
189 //
190 // Then, the original G_PTR_ADDs should be folded later on so that they look
191 // like this:
192 //
193 // %ptrN = G_PTR_ADD %offset_g, cstN - min_cst
194 uint64_t Offset, MinOffset;
195 std::tie(args&: Offset, args&: MinOffset) = MatchInfo;
196 B.setInstrAndDebugLoc(*std::next(x: MI.getIterator()));
197 Observer.changingInstr(MI);
198 auto &GlobalOp = MI.getOperand(i: 1);
199 auto *GV = GlobalOp.getGlobal();
200 GlobalOp.ChangeToGA(GV, Offset, TargetFlags: GlobalOp.getTargetFlags());
201 Register Dst = MI.getOperand(i: 0).getReg();
202 Register NewGVDst = MRI.cloneVirtualRegister(VReg: Dst);
203 MI.getOperand(i: 0).setReg(NewGVDst);
204 Observer.changedInstr(MI);
205 B.buildPtrAdd(
206 Res: Dst, Op0: NewGVDst,
207 Op1: B.buildConstant(Res: LLT::scalar(SizeInBits: 64), Val: -static_cast<int64_t>(MinOffset)));
208}
209
210// Combines vecreduce_add(mul(ext(x), ext(y))) -> vecreduce_add([us]dot(x, y))
211// Or vecreduce_add(ext(mul(ext(x), ext(y)))) -> vecreduce_add([us]dot(x, y))
212// Or vecreduce_add(ext(x)) -> vecreduce_add([us]dot(x, 1))
213// Similar to performVecReduceAddCombine in SelectionDAG
214bool matchExtAddvToDotAddv(MachineInstr &MI, MachineRegisterInfo &MRI,
215 const AArch64Subtarget &STI,
216 std::tuple<Register, Register, bool> &MatchInfo) {
217 assert(MI.getOpcode() == TargetOpcode::G_VECREDUCE_ADD &&
218 "Expected a G_VECREDUCE_ADD instruction");
219 assert(STI.hasDotProd() && "Target should have Dot Product feature");
220
221 MachineInstr *I1 = getDefIgnoringCopies(Reg: MI.getOperand(i: 1).getReg(), MRI);
222 Register DstReg = MI.getOperand(i: 0).getReg();
223 Register MidReg = I1->getOperand(i: 0).getReg();
224 LLT DstTy = MRI.getType(Reg: DstReg);
225 LLT MidTy = MRI.getType(Reg: MidReg);
226 if (DstTy.getScalarSizeInBits() != 32 || MidTy.getScalarSizeInBits() != 32)
227 return false;
228
229 // Detect mul(ext, ext) with symmetric ext's. If I1Opc is G_ZEXT or G_SEXT
230 // then the ext's must match the same opcode. It is set to the ext opcode on
231 // output.
232 auto tryMatchingMulOfExt = [&MRI](MachineInstr *MI, Register &Out1,
233 Register &Out2, unsigned &I1Opc) {
234 // If result of this has more than 1 use, then there is no point in creating
235 // a dot instruction
236 if (!MRI.hasOneNonDBGUse(RegNo: MI->getOperand(i: 0).getReg()))
237 return false;
238
239 MachineInstr *ExtMI1 =
240 getDefIgnoringCopies(Reg: MI->getOperand(i: 1).getReg(), MRI);
241 MachineInstr *ExtMI2 =
242 getDefIgnoringCopies(Reg: MI->getOperand(i: 2).getReg(), MRI);
243 LLT Ext1DstTy = MRI.getType(Reg: ExtMI1->getOperand(i: 0).getReg());
244 LLT Ext2DstTy = MRI.getType(Reg: ExtMI2->getOperand(i: 0).getReg());
245
246 if (ExtMI1->getOpcode() != ExtMI2->getOpcode() || Ext1DstTy != Ext2DstTy)
247 return false;
248 if ((I1Opc == TargetOpcode::G_ZEXT || I1Opc == TargetOpcode::G_SEXT) &&
249 I1Opc != ExtMI1->getOpcode())
250 return false;
251 Out1 = ExtMI1->getOperand(i: 1).getReg();
252 Out2 = ExtMI2->getOperand(i: 1).getReg();
253 I1Opc = ExtMI1->getOpcode();
254 return true;
255 };
256
257 LLT SrcTy;
258 unsigned I1Opc = I1->getOpcode();
259 if (I1Opc == TargetOpcode::G_MUL) {
260 Register Out1, Out2;
261 if (!tryMatchingMulOfExt(I1, Out1, Out2, I1Opc))
262 return false;
263 SrcTy = MRI.getType(Reg: Out1);
264 std::get<0>(t&: MatchInfo) = Out1;
265 std::get<1>(t&: MatchInfo) = Out2;
266 } else if (I1Opc == TargetOpcode::G_ZEXT || I1Opc == TargetOpcode::G_SEXT) {
267 Register I1Op = I1->getOperand(i: 1).getReg();
268 MachineInstr *M = getDefIgnoringCopies(Reg: I1Op, MRI);
269 Register Out1, Out2;
270 if (M->getOpcode() == TargetOpcode::G_MUL &&
271 tryMatchingMulOfExt(M, Out1, Out2, I1Opc)) {
272 SrcTy = MRI.getType(Reg: Out1);
273 std::get<0>(t&: MatchInfo) = Out1;
274 std::get<1>(t&: MatchInfo) = Out2;
275 } else {
276 SrcTy = MRI.getType(Reg: I1Op);
277 std::get<0>(t&: MatchInfo) = I1Op;
278 std::get<1>(t&: MatchInfo) = 0;
279 }
280 } else {
281 return false;
282 }
283
284 if (I1Opc == TargetOpcode::G_ZEXT)
285 std::get<2>(t&: MatchInfo) = 0;
286 else if (I1Opc == TargetOpcode::G_SEXT)
287 std::get<2>(t&: MatchInfo) = 1;
288 else
289 return false;
290
291 if (SrcTy.getScalarSizeInBits() != 8 || SrcTy.getNumElements() % 8 != 0)
292 return false;
293
294 return true;
295}
296
297void applyExtAddvToDotAddv(MachineInstr &MI, MachineRegisterInfo &MRI,
298 MachineIRBuilder &Builder,
299 GISelChangeObserver &Observer,
300 const AArch64Subtarget &STI,
301 std::tuple<Register, Register, bool> &MatchInfo) {
302 assert(MI.getOpcode() == TargetOpcode::G_VECREDUCE_ADD &&
303 "Expected a G_VECREDUCE_ADD instruction");
304 assert(STI.hasDotProd() && "Target should have Dot Product feature");
305
306 // Initialise the variables
307 unsigned DotOpcode =
308 std::get<2>(t&: MatchInfo) ? AArch64::G_SDOT : AArch64::G_UDOT;
309 Register Ext1SrcReg = std::get<0>(t&: MatchInfo);
310
311 // If there is one source register, create a vector of 0s as the second
312 // source register
313 Register Ext2SrcReg;
314 if (std::get<1>(t&: MatchInfo) == 0)
315 Ext2SrcReg = Builder.buildConstant(Res: MRI.getType(Reg: Ext1SrcReg), Val: 1)
316 ->getOperand(i: 0)
317 .getReg();
318 else
319 Ext2SrcReg = std::get<1>(t&: MatchInfo);
320
321 // Find out how many DOT instructions are needed
322 LLT SrcTy = MRI.getType(Reg: Ext1SrcReg);
323 LLT MidTy;
324 unsigned NumOfDotMI;
325 if (SrcTy.getNumElements() % 16 == 0) {
326 NumOfDotMI = SrcTy.getNumElements() / 16;
327 MidTy = LLT::fixed_vector(NumElements: 4, ScalarTy: LLT::integer(SizeInBits: 32));
328 } else if (SrcTy.getNumElements() % 8 == 0) {
329 NumOfDotMI = SrcTy.getNumElements() / 8;
330 MidTy = LLT::fixed_vector(NumElements: 2, ScalarTy: LLT::integer(SizeInBits: 32));
331 } else {
332 llvm_unreachable("Source type number of elements is not multiple of 8");
333 }
334
335 // Handle case where one DOT instruction is needed
336 if (NumOfDotMI == 1) {
337 auto Zeroes = Builder.buildConstant(Res: MidTy, Val: 0)->getOperand(i: 0).getReg();
338 auto Dot = Builder.buildInstr(Opc: DotOpcode, DstOps: {MidTy},
339 SrcOps: {Zeroes, Ext1SrcReg, Ext2SrcReg});
340 Builder.buildVecReduceAdd(Dst: MI.getOperand(i: 0), Src: Dot->getOperand(i: 0));
341 } else {
342 // If not pad the last v8 element with 0s to a v16
343 SmallVector<Register, 4> Ext1UnmergeReg;
344 SmallVector<Register, 4> Ext2UnmergeReg;
345 if (SrcTy.getNumElements() % 16 != 0) {
346 SmallVector<Register> Leftover1;
347 SmallVector<Register> Leftover2;
348
349 // Split the elements into v16i8 and v8i8
350 LLT MainTy = LLT::fixed_vector(NumElements: 16, ScalarTy: LLT::integer(SizeInBits: 8));
351 LLT LeftoverTy1, LeftoverTy2;
352 if (!extractParts(Reg: Ext1SrcReg, RegTy: MRI.getType(Reg: Ext1SrcReg), MainTy,
353 LeftoverTy&: LeftoverTy1, VRegs&: Ext1UnmergeReg, LeftoverVRegs&: Leftover1, MIRBuilder&: Builder, MRI) ||
354 !extractParts(Reg: Ext2SrcReg, RegTy: MRI.getType(Reg: Ext2SrcReg), MainTy,
355 LeftoverTy&: LeftoverTy2, VRegs&: Ext2UnmergeReg, LeftoverVRegs&: Leftover2, MIRBuilder&: Builder, MRI)) {
356 llvm_unreachable("Unable to split this vector properly");
357 }
358
359 // Pad the leftover v8i8 vector with register of 0s of type v8i8
360 auto v8Zeroes =
361 Builder.buildConstant(Res: LLT::fixed_vector(NumElements: 8, ScalarTy: LLT::integer(SizeInBits: 8)), Val: 0);
362
363 Ext1UnmergeReg.push_back(
364 Elt: Builder
365 .buildMergeLikeInstr(Res: LLT::fixed_vector(NumElements: 16, ScalarTy: LLT::integer(SizeInBits: 8)),
366 Ops: {Leftover1[0], v8Zeroes})
367 .getReg(Idx: 0));
368 Ext2UnmergeReg.push_back(
369 Elt: Builder
370 .buildMergeLikeInstr(Res: LLT::fixed_vector(NumElements: 16, ScalarTy: LLT::integer(SizeInBits: 8)),
371 Ops: {Leftover2[0], v8Zeroes})
372 .getReg(Idx: 0));
373
374 } else {
375 // Unmerge the source vectors to v16i8
376 unsigned SrcNumElts = SrcTy.getNumElements();
377 extractParts(Reg: Ext1SrcReg, Ty: LLT::fixed_vector(NumElements: 16, ScalarTy: LLT::integer(SizeInBits: 8)),
378 NumParts: SrcNumElts / 16, VRegs&: Ext1UnmergeReg, MIRBuilder&: Builder, MRI);
379 extractParts(Reg: Ext2SrcReg, Ty: LLT::fixed_vector(NumElements: 16, ScalarTy: LLT::integer(SizeInBits: 8)),
380 NumParts: SrcNumElts / 16, VRegs&: Ext2UnmergeReg, MIRBuilder&: Builder, MRI);
381 }
382
383 // Build the UDOT instructions
384 SmallVector<Register, 2> DotReg;
385 unsigned NumElements = 0;
386 for (unsigned i = 0; i < Ext1UnmergeReg.size(); i++) {
387 LLT ZeroesLLT;
388 // Check if it is 16 or 8 elements. Set Zeroes to the according size
389 if (MRI.getType(Reg: Ext1UnmergeReg[i]).getNumElements() == 16) {
390 ZeroesLLT = LLT::fixed_vector(NumElements: 4, ScalarTy: LLT::integer(SizeInBits: 32));
391 NumElements += 4;
392 } else {
393 ZeroesLLT = LLT::fixed_vector(NumElements: 2, ScalarTy: LLT::integer(SizeInBits: 32));
394 NumElements += 2;
395 }
396 auto Zeroes = Builder.buildConstant(Res: ZeroesLLT, Val: 0);
397 DotReg.push_back(
398 Elt: Builder
399 .buildInstr(Opc: DotOpcode, DstOps: {ZeroesLLT},
400 SrcOps: {Zeroes, Ext1UnmergeReg[i], Ext2UnmergeReg[i]})
401 .getReg(Idx: 0));
402 }
403
404 // Merge the output
405 auto ConcatMI = Builder.buildConcatVectors(
406 Res: LLT::fixed_vector(NumElements, ScalarTy: LLT::integer(SizeInBits: 32)), Ops: DotReg);
407
408 // Put it through a vector reduction
409 Builder.buildVecReduceAdd(Dst: MI.getOperand(i: 0).getReg(),
410 Src: ConcatMI->getOperand(i: 0).getReg());
411 }
412
413 // Erase the dead instructions
414 MI.eraseFromParent();
415}
416
417// Matches {U/S}ADDV(ext(x)) => {U/S}ADDLV(x)
418// Ensure that the type coming from the extend instruction is the right size
419bool matchExtUaddvToUaddlv(MachineInstr &MI, MachineRegisterInfo &MRI,
420 std::pair<Register, bool> &MatchInfo) {
421 assert(MI.getOpcode() == TargetOpcode::G_VECREDUCE_ADD &&
422 "Expected G_VECREDUCE_ADD Opcode");
423
424 // Check if the last instruction is an extend
425 MachineInstr *ExtMI = getDefIgnoringCopies(Reg: MI.getOperand(i: 1).getReg(), MRI);
426 auto ExtOpc = ExtMI->getOpcode();
427
428 if (ExtOpc == TargetOpcode::G_ZEXT)
429 std::get<1>(in&: MatchInfo) = 0;
430 else if (ExtOpc == TargetOpcode::G_SEXT)
431 std::get<1>(in&: MatchInfo) = 1;
432 else
433 return false;
434
435 // Check if the source register is a valid type
436 Register ExtSrcReg = ExtMI->getOperand(i: 1).getReg();
437 LLT ExtSrcTy = MRI.getType(Reg: ExtSrcReg);
438 LLT DstTy = MRI.getType(Reg: MI.getOperand(i: 0).getReg());
439 if (ExtSrcTy.getScalarSizeInBits() * 2 > DstTy.getScalarSizeInBits())
440 return false;
441 if ((DstTy.getScalarSizeInBits() == 16 &&
442 ExtSrcTy.getNumElements() % 8 == 0 && ExtSrcTy.getNumElements() < 256) ||
443 (DstTy.getScalarSizeInBits() == 32 &&
444 ExtSrcTy.getNumElements() % 4 == 0) ||
445 (DstTy.getScalarSizeInBits() == 64 &&
446 ExtSrcTy.getNumElements() % 4 == 0)) {
447 std::get<0>(in&: MatchInfo) = ExtSrcReg;
448 return true;
449 }
450 return false;
451}
452
453void applyExtUaddvToUaddlv(MachineInstr &MI, MachineRegisterInfo &MRI,
454 MachineIRBuilder &B, GISelChangeObserver &Observer,
455 std::pair<Register, bool> &MatchInfo) {
456 assert(MI.getOpcode() == TargetOpcode::G_VECREDUCE_ADD &&
457 "Expected G_VECREDUCE_ADD Opcode");
458
459 unsigned Opc = std::get<1>(in&: MatchInfo) ? AArch64::G_SADDLV : AArch64::G_UADDLV;
460 Register SrcReg = std::get<0>(in&: MatchInfo);
461 Register DstReg = MI.getOperand(i: 0).getReg();
462 LLT SrcTy = MRI.getType(Reg: SrcReg);
463 LLT DstTy = MRI.getType(Reg: DstReg);
464
465 // If SrcTy has more elements than expected, split them into multiple
466 // instructions and sum the results
467 LLT MainTy;
468 SmallVector<Register, 1> WorkingRegisters;
469 unsigned SrcScalSize = SrcTy.getScalarSizeInBits();
470 unsigned SrcNumElem = SrcTy.getNumElements();
471 if ((SrcScalSize == 8 && SrcNumElem > 16) ||
472 (SrcScalSize == 16 && SrcNumElem > 8) ||
473 (SrcScalSize == 32 && SrcNumElem > 4)) {
474
475 LLT LeftoverTy;
476 SmallVector<Register, 4> LeftoverRegs;
477 if (SrcScalSize == 8)
478 MainTy = LLT::fixed_vector(NumElements: 16, ScalarTy: LLT::integer(SizeInBits: 8));
479 else if (SrcScalSize == 16)
480 MainTy = LLT::fixed_vector(NumElements: 8, ScalarTy: LLT::integer(SizeInBits: 16));
481 else if (SrcScalSize == 32)
482 MainTy = LLT::fixed_vector(NumElements: 4, ScalarTy: LLT::integer(SizeInBits: 32));
483 else
484 llvm_unreachable("Source's Scalar Size not supported");
485
486 // Extract the parts and put each extracted sources through U/SADDLV and put
487 // the values inside a small vec
488 extractParts(Reg: SrcReg, RegTy: SrcTy, MainTy, LeftoverTy, VRegs&: WorkingRegisters,
489 LeftoverVRegs&: LeftoverRegs, MIRBuilder&: B, MRI);
490 llvm::append_range(C&: WorkingRegisters, R&: LeftoverRegs);
491 } else {
492 WorkingRegisters.push_back(Elt: SrcReg);
493 MainTy = SrcTy;
494 }
495
496 unsigned MidScalarSize = MainTy.getScalarSizeInBits() * 2;
497 LLT MidScalarLLT = LLT::integer(SizeInBits: MidScalarSize);
498 Register ZeroReg = B.buildConstant(Res: LLT::integer(SizeInBits: 64), Val: 0).getReg(Idx: 0);
499 for (unsigned I = 0; I < WorkingRegisters.size(); I++) {
500 // If the number of elements is too small to build an instruction, extend
501 // its size before applying addlv
502 LLT WorkingRegTy = MRI.getType(Reg: WorkingRegisters[I]);
503 if ((WorkingRegTy.getScalarSizeInBits() == 8) &&
504 (WorkingRegTy.getNumElements() == 4)) {
505 WorkingRegisters[I] =
506 B.buildInstr(Opc: std::get<1>(in&: MatchInfo) ? TargetOpcode::G_SEXT
507 : TargetOpcode::G_ZEXT,
508 DstOps: {LLT::fixed_vector(NumElements: 4, ScalarTy: LLT::integer(SizeInBits: 16))},
509 SrcOps: {WorkingRegisters[I]})
510 .getReg(Idx: 0);
511 }
512
513 // Generate the {U/S}ADDLV instruction, whose output is always double of the
514 // Src's Scalar size
515 LLT AddlvTy = MidScalarSize <= 32 ? LLT::fixed_vector(NumElements: 4, ScalarTy: LLT::integer(SizeInBits: 32))
516 : LLT::fixed_vector(NumElements: 2, ScalarTy: LLT::integer(SizeInBits: 64));
517 Register AddlvReg =
518 B.buildInstr(Opc, DstOps: {AddlvTy}, SrcOps: {WorkingRegisters[I]}).getReg(Idx: 0);
519
520 // The output from {U/S}ADDLV gets placed in the lowest lane of a v4i32 or
521 // v2i64 register.
522 // i16, i32 results uses v4i32 registers
523 // i64 results uses v2i64 registers
524 // Therefore we have to extract/truncate the the value to the right type
525 if (MidScalarSize == 32 || MidScalarSize == 64) {
526 WorkingRegisters[I] = B.buildInstr(Opc: AArch64::G_EXTRACT_VECTOR_ELT,
527 DstOps: {MidScalarLLT}, SrcOps: {AddlvReg, ZeroReg})
528 .getReg(Idx: 0);
529 } else {
530 Register ExtractReg =
531 B.buildInstr(Opc: AArch64::G_EXTRACT_VECTOR_ELT, DstOps: {LLT::integer(SizeInBits: 32)},
532 SrcOps: {AddlvReg, ZeroReg})
533 .getReg(Idx: 0);
534 WorkingRegisters[I] =
535 B.buildTrunc(Res: {MidScalarLLT}, Op: {ExtractReg}).getReg(Idx: 0);
536 }
537 }
538
539 Register OutReg;
540 if (WorkingRegisters.size() > 1) {
541 OutReg = B.buildAdd(Dst: MidScalarLLT, Src0: WorkingRegisters[0], Src1: WorkingRegisters[1])
542 .getReg(Idx: 0);
543 for (unsigned I = 2; I < WorkingRegisters.size(); I++) {
544 OutReg = B.buildAdd(Dst: MidScalarLLT, Src0: OutReg, Src1: WorkingRegisters[I]).getReg(Idx: 0);
545 }
546 } else {
547 OutReg = WorkingRegisters[0];
548 }
549
550 if (DstTy.getScalarSizeInBits() > MidScalarSize) {
551 // Handle the scalar value if the DstTy's Scalar Size is more than double
552 // Src's ScalarType
553 B.buildInstr(Opc: std::get<1>(in&: MatchInfo) ? TargetOpcode::G_SEXT
554 : TargetOpcode::G_ZEXT,
555 DstOps: {DstReg}, SrcOps: {OutReg});
556 } else {
557 B.buildCopy(Res: DstReg, Op: OutReg);
558 }
559
560 MI.eraseFromParent();
561}
562
563// Pushes ADD/SUB/MUL through extend instructions to decrease the number of
564// extend instruction at the end by allowing selection of {s|u}addl sooner
565// i32 add(i32 ext i8, i32 ext i8) => i32 ext(i16 add(i16 ext i8, i16 ext i8))
566bool matchPushAddSubExt(MachineInstr &MI, MachineRegisterInfo &MRI,
567 Register DstReg, Register SrcReg1, Register SrcReg2) {
568 assert((MI.getOpcode() == TargetOpcode::G_ADD ||
569 MI.getOpcode() == TargetOpcode::G_SUB ||
570 MI.getOpcode() == TargetOpcode::G_MUL) &&
571 "Expected a G_ADD, G_SUB or G_MUL instruction\n");
572
573 // Deal with vector types only
574 LLT DstTy = MRI.getType(Reg: DstReg);
575 if (!DstTy.isVector())
576 return false;
577
578 // Return true if G_{S|Z}EXT instruction is more than 2* source
579 Register ExtDstReg = MI.getOperand(i: 1).getReg();
580 LLT Ext1SrcTy = MRI.getType(Reg: SrcReg1);
581 LLT Ext2SrcTy = MRI.getType(Reg: SrcReg2);
582 unsigned ExtDstScal = MRI.getType(Reg: ExtDstReg).getScalarSizeInBits();
583 unsigned Ext1SrcScal = Ext1SrcTy.getScalarSizeInBits();
584 if (((Ext1SrcScal == 8 && ExtDstScal == 32) ||
585 ((Ext1SrcScal == 8 || Ext1SrcScal == 16) && ExtDstScal == 64)) &&
586 Ext1SrcTy == Ext2SrcTy)
587 return true;
588
589 return false;
590}
591
592void applyPushAddSubExt(MachineInstr &MI, MachineRegisterInfo &MRI,
593 MachineIRBuilder &B, bool isSExt, Register DstReg,
594 Register SrcReg1, Register SrcReg2) {
595 LLT SrcTy = MRI.getType(Reg: SrcReg1);
596 LLT MidTy = SrcTy.changeElementSize(NewEltSize: SrcTy.getScalarSizeInBits() * 2);
597 unsigned Opc = isSExt ? TargetOpcode::G_SEXT : TargetOpcode::G_ZEXT;
598 Register Ext1Reg = B.buildInstr(Opc, DstOps: {MidTy}, SrcOps: {SrcReg1}).getReg(Idx: 0);
599 Register Ext2Reg = B.buildInstr(Opc, DstOps: {MidTy}, SrcOps: {SrcReg2}).getReg(Idx: 0);
600 Register AddReg =
601 B.buildInstr(Opc: MI.getOpcode(), DstOps: {MidTy}, SrcOps: {Ext1Reg, Ext2Reg}).getReg(Idx: 0);
602
603 // G_SUB has to sign-extend the result.
604 // G_ADD needs to sext from sext and can sext or zext from zext, and G_MUL
605 // needs to use the original opcode so the original opcode is used for both.
606 if (MI.getOpcode() == TargetOpcode::G_ADD ||
607 MI.getOpcode() == TargetOpcode::G_MUL)
608 B.buildInstr(Opc, DstOps: {DstReg}, SrcOps: {AddReg});
609 else
610 B.buildSExt(Res: DstReg, Op: AddReg);
611
612 MI.eraseFromParent();
613}
614
615bool matchSimplifyUADDO(MachineInstr &MI, MachineRegisterInfo &MRI,
616 std::pair<Register, Register> &MatchInfo) {
617 // Try simplify G_UADDO with 8 or 16 bit operands to wide G_ADD and TBNZ if
618 // result is only used in the no-overflow case. It is restricted to cases
619 // where we know that the high-bits of the operands are 0. If there's an
620 // overflow, then the 9th or 17th bit must be set, which can be checked
621 // using TBNZ.
622 //
623 // Change (for UADDOs on 8 and 16 bits):
624 //
625 // %z0 = G_ASSERT_ZEXT _
626 // %op0 = G_TRUNC %z0
627 // %z1 = G_ASSERT_ZEXT _
628 // %op1 = G_TRUNC %z1
629 // %val, %cond = G_UADDO %op0, %op1
630 // G_BRCOND %cond, %error.bb
631 //
632 // error.bb:
633 // (no successors and no uses of %val)
634 //
635 // To:
636 //
637 // %z0 = G_ASSERT_ZEXT _
638 // %z1 = G_ASSERT_ZEXT _
639 // %add = G_ADD %z0, %z1
640 // %val = G_TRUNC %add
641 // %bit = G_AND %add, 1 << scalar-size-in-bits(%op1)
642 // %cond = G_ICMP NE, %bit, 0
643 // G_BRCOND %cond, %error.bb
644
645 MachineOperand *DefOp0 = MRI.getOneDef(Reg: MI.getOperand(i: 2).getReg());
646 MachineOperand *DefOp1 = MRI.getOneDef(Reg: MI.getOperand(i: 3).getReg());
647 Register Op0Wide;
648 Register Op1Wide;
649 if (!mi_match(R: DefOp0->getParent(), MRI, P: m_GTrunc(Src: m_Reg(R&: Op0Wide))) ||
650 !mi_match(R: DefOp1->getParent(), MRI, P: m_GTrunc(Src: m_Reg(R&: Op1Wide))))
651 return false;
652 LLT WideTy0 = MRI.getType(Reg: Op0Wide);
653 LLT WideTy1 = MRI.getType(Reg: Op1Wide);
654 Register ResVal = MI.getOperand(i: 0).getReg();
655 LLT OpTy = MRI.getType(Reg: ResVal);
656 unsigned OpTySize = OpTy.getScalarSizeInBits();
657 // First check that the G_TRUNC feeding the G_UADDO are no-ops, because the
658 // inputs have been zero-extended.
659 if (!mi_match(R: Op0Wide, MRI,
660 P: m_GAssertZext(Src: m_Reg(), Imm: m_SpecificImm(RequestedValue: OpTySize))) ||
661 !mi_match(R: Op1Wide, MRI, P: m_GAssertZext(Src: m_Reg(), Imm: m_SpecificImm(RequestedValue: OpTySize))))
662 return false;
663
664 // Only scalar UADDO with either 8 or 16 bit operands are handled.
665 if (!WideTy0.isScalar() || !WideTy1.isScalar() || WideTy0 != WideTy1 ||
666 OpTySize >= WideTy0.getScalarSizeInBits() ||
667 (OpTySize != 8 && OpTySize != 16))
668 return false;
669
670 // The overflow-status result must be used by a branch only.
671 Register ResStatus = MI.getOperand(i: 1).getReg();
672 if (!MRI.hasOneNonDBGUse(RegNo: ResStatus))
673 return false;
674 MachineInstr *CondUser = &*MRI.use_instr_nodbg_begin(RegNo: ResStatus);
675 if (CondUser->getOpcode() != TargetOpcode::G_BRCOND)
676 return false;
677
678 // Make sure the computed result is only used in the no-overflow blocks.
679 MachineBasicBlock *CurrentMBB = MI.getParent();
680 MachineBasicBlock *FailMBB = CondUser->getOperand(i: 1).getMBB();
681 if (!FailMBB->succ_empty() || CondUser->getParent() != CurrentMBB)
682 return false;
683 if (any_of(Range: MRI.use_nodbg_instructions(Reg: ResVal),
684 P: [&MI, FailMBB, CurrentMBB](MachineInstr &I) {
685 return &MI != &I &&
686 (I.getParent() == FailMBB || I.getParent() == CurrentMBB);
687 }))
688 return false;
689
690 MatchInfo = {Op0Wide, Op1Wide};
691 return true;
692}
693
694void applySimplifyUADDO(MachineInstr &MI, MachineRegisterInfo &MRI,
695 MachineIRBuilder &B, GISelChangeObserver &Observer,
696 const CombinerHelper &Helper,
697 const std::pair<Register, Register> &MatchInfo) {
698 Register Op0Wide = MatchInfo.first;
699 Register Op1Wide = MatchInfo.second;
700 Register ResVal = MI.getOperand(i: 0).getReg();
701 Register ResStatus = MI.getOperand(i: 1).getReg();
702 unsigned OpTySize = MRI.getType(Reg: ResVal).getScalarSizeInBits();
703
704 // Remove G_UADDO.
705 B.setInstrAndDebugLoc(*MI.getNextNode());
706 MI.eraseFromParent();
707
708 // Emit wide add.
709 Register AddDst = MRI.cloneVirtualRegister(VReg: Op0Wide);
710 B.buildInstr(Opc: TargetOpcode::G_ADD, DstOps: {AddDst}, SrcOps: {Op0Wide, Op1Wide});
711
712 // Emit check of the 9th or 17th bit and update users (the branch). This will
713 // later be folded to TBNZ.
714 Register CondBit = MRI.cloneVirtualRegister(VReg: Op0Wide);
715 B.buildAnd(
716 Dst: CondBit, Src0: AddDst,
717 Src1: B.buildConstant(Res: LLT::integer(SizeInBits: 32), Val: OpTySize == 8 ? 1 << 8 : 1 << 16));
718 B.buildICmp(Pred: CmpInst::ICMP_NE, Res: ResStatus, Op0: CondBit,
719 Op1: B.buildConstant(Res: LLT::integer(SizeInBits: 32), Val: 0));
720
721 // Update ZEXts users of the result value. Because all uses are in the
722 // no-overflow case, we know that the top bits are 0 and we can ignore ZExts.
723 B.buildZExtOrTrunc(Res: ResVal, Op: AddDst);
724 for (MachineOperand &U : make_early_inc_range(Range: MRI.use_operands(Reg: ResVal))) {
725 Register WideReg;
726 if (mi_match(R: U.getParent(), MRI, P: m_GZExt(Src: m_Reg(R&: WideReg)))) {
727 auto OldR = U.getParent()->getOperand(i: 0).getReg();
728 Observer.erasingInstr(MI&: *U.getParent());
729 U.getParent()->eraseFromParent();
730 Helper.replaceRegWith(MRI, FromReg: OldR, ToReg: AddDst);
731 }
732 }
733}
734
735class AArch64PreLegalizerCombinerImpl : public Combiner {
736protected:
737 const CombinerHelper Helper;
738 const AArch64PreLegalizerCombinerImplRuleConfig &RuleConfig;
739 const AArch64Subtarget &STI;
740 const LibcallLoweringInfo &Libcalls;
741
742public:
743 AArch64PreLegalizerCombinerImpl(
744 MachineFunction &MF, CombinerInfo &CInfo, GISelValueTracking &VT,
745 GISelCSEInfo *CSEInfo,
746 const AArch64PreLegalizerCombinerImplRuleConfig &RuleConfig,
747 const AArch64Subtarget &STI, const LibcallLoweringInfo &Libcalls,
748 MachineDominatorTree *MDT, const LegalizerInfo *LI);
749
750 static const char *getName() { return "AArch6400PreLegalizerCombiner"; }
751
752 bool tryCombineAll(MachineInstr &I) const override;
753
754 bool tryCombineAllImpl(MachineInstr &I) const;
755
756private:
757#define GET_GICOMBINER_CLASS_MEMBERS
758#include "AArch64GenPreLegalizeGICombiner.inc"
759#undef GET_GICOMBINER_CLASS_MEMBERS
760};
761
762#define GET_GICOMBINER_IMPL
763#include "AArch64GenPreLegalizeGICombiner.inc"
764#undef GET_GICOMBINER_IMPL
765
766AArch64PreLegalizerCombinerImpl::AArch64PreLegalizerCombinerImpl(
767 MachineFunction &MF, CombinerInfo &CInfo, GISelValueTracking &VT,
768 GISelCSEInfo *CSEInfo,
769 const AArch64PreLegalizerCombinerImplRuleConfig &RuleConfig,
770 const AArch64Subtarget &STI, const LibcallLoweringInfo &Libcalls,
771 MachineDominatorTree *MDT, const LegalizerInfo *LI)
772 : Combiner(MF, CInfo, &VT, CSEInfo),
773 Helper(Observer, B, /*IsPreLegalize*/ true, &VT, MDT, LI),
774 RuleConfig(RuleConfig), STI(STI), Libcalls(Libcalls),
775#define GET_GICOMBINER_CONSTRUCTOR_INITS
776#include "AArch64GenPreLegalizeGICombiner.inc"
777#undef GET_GICOMBINER_CONSTRUCTOR_INITS
778{
779}
780
781bool AArch64PreLegalizerCombinerImpl::tryCombineAll(MachineInstr &MI) const {
782 if (tryCombineAllImpl(I&: MI))
783 return true;
784
785 return false;
786}
787
788bool runCombiner(MachineFunction &MF, GISelCSEInfo *CSEInfo,
789 GISelValueTracking *VT, MachineDominatorTree *MDT,
790 const LibcallLoweringInfo &Libcalls,
791 const AArch64PreLegalizerCombinerImplRuleConfig &RuleConfig,
792 bool EnableOpt) {
793 const AArch64Subtarget &ST = MF.getSubtarget<AArch64Subtarget>();
794 const auto *LI = ST.getLegalizerInfo();
795
796 const Function &F = MF.getFunction();
797
798 CombinerInfo CInfo(/*AllowIllegalOps=*/true, /*ShouldLegalizeIllegal=*/false,
799 /*LegalizerInfo=*/nullptr, EnableOpt, F.hasOptSize(),
800 F.hasMinSize());
801 // Disable fixed-point iteration to reduce compile-time
802 CInfo.MaxIterations = 1;
803 CInfo.ObserverLvl = CombinerInfo::ObserverLevel::SinglePass;
804 // This is the first Combiner, so the input IR might contain dead
805 // instructions.
806 CInfo.EnableFullDCE = true;
807 AArch64PreLegalizerCombinerImpl Impl(MF, CInfo, *VT, CSEInfo, RuleConfig, ST,
808 Libcalls, MDT, LI);
809 return Impl.combineMachineInstrs();
810}
811
812// Pass boilerplate
813// ================
814
815class AArch64PreLegalizerCombinerLegacy : public MachineFunctionPass {
816public:
817 static char ID;
818
819 AArch64PreLegalizerCombinerLegacy();
820
821 StringRef getPassName() const override {
822 return "AArch64PreLegalizerCombiner";
823 }
824
825 bool runOnMachineFunction(MachineFunction &MF) override;
826
827 void getAnalysisUsage(AnalysisUsage &AU) const override;
828
829private:
830 AArch64PreLegalizerCombinerImplRuleConfig RuleConfig;
831};
832} // end anonymous namespace
833
834void AArch64PreLegalizerCombinerLegacy::getAnalysisUsage(
835 AnalysisUsage &AU) const {
836 AU.setPreservesCFG();
837 getSelectionDAGFallbackAnalysisUsage(AU);
838 AU.addRequired<GISelValueTrackingAnalysisLegacy>();
839 AU.addPreserved<GISelValueTrackingAnalysisLegacy>();
840 AU.addRequired<MachineDominatorTreeWrapperPass>();
841 AU.addRequired<GISelCSEAnalysisWrapperPass>();
842 AU.addPreserved<GISelCSEAnalysisWrapperPass>();
843 AU.addRequired<LibcallLoweringInfoWrapper>();
844 MachineFunctionPass::getAnalysisUsage(AU);
845}
846
847AArch64PreLegalizerCombinerLegacy::AArch64PreLegalizerCombinerLegacy()
848 : MachineFunctionPass(ID) {
849 if (!RuleConfig.parseCommandLineOption())
850 report_fatal_error(reason: "Invalid rule identifier");
851}
852
853bool AArch64PreLegalizerCombinerLegacy::runOnMachineFunction(
854 MachineFunction &MF) {
855 if (MF.getProperties().hasFailedISel())
856 return false;
857 // Enable CSE.
858 GISelCSEAnalysisWrapper &Wrapper =
859 getAnalysis<GISelCSEAnalysisWrapperPass>().getCSEWrapper();
860 auto *CSEInfo =
861 &Wrapper.get(CSEOpt: getStandardCSEConfigForOpt(Level: MF.getTarget().getOptLevel()));
862
863 const AArch64Subtarget &ST = MF.getSubtarget<AArch64Subtarget>();
864 const LibcallLoweringInfo &Libcalls =
865 getAnalysis<LibcallLoweringInfoWrapper>().getLibcallLowering(
866 M: *MF.getFunction().getParent(), Subtarget: ST);
867
868 GISelValueTracking *VT =
869 &getAnalysis<GISelValueTrackingAnalysisLegacy>().get(MF);
870 MachineDominatorTree *MDT =
871 &getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree();
872 bool EnableOpt = MF.getTarget().getOptLevel() != CodeGenOptLevel::None &&
873 !skipFunction(F: MF.getFunction());
874 return runCombiner(MF, CSEInfo, VT, MDT, Libcalls, RuleConfig, EnableOpt);
875}
876
877char AArch64PreLegalizerCombinerLegacy::ID = 0;
878INITIALIZE_PASS_BEGIN(AArch64PreLegalizerCombinerLegacy, DEBUG_TYPE,
879 "Combine AArch64 machine instrs before legalization",
880 false, false)
881INITIALIZE_PASS_DEPENDENCY(GISelValueTrackingAnalysisLegacy)
882INITIALIZE_PASS_DEPENDENCY(GISelCSEAnalysisWrapperPass)
883INITIALIZE_PASS_DEPENDENCY(LibcallLoweringInfoWrapper)
884INITIALIZE_PASS_END(AArch64PreLegalizerCombinerLegacy, DEBUG_TYPE,
885 "Combine AArch64 machine instrs before legalization", false,
886 false)
887
888AArch64PreLegalizerCombinerPass::AArch64PreLegalizerCombinerPass()
889 : RuleConfig(
890 std::make_unique<AArch64PreLegalizerCombinerImplRuleConfig>()) {
891 if (!RuleConfig->parseCommandLineOption())
892 reportFatalUsageError(reason: "invalid rule identifier");
893}
894
895AArch64PreLegalizerCombinerPass::AArch64PreLegalizerCombinerPass(
896 AArch64PreLegalizerCombinerPass &&) = default;
897
898AArch64PreLegalizerCombinerPass::~AArch64PreLegalizerCombinerPass() = default;
899
900PreservedAnalyses
901AArch64PreLegalizerCombinerPass::run(MachineFunction &MF,
902 MachineFunctionAnalysisManager &MFAM) {
903 if (MF.getProperties().hasFailedISel())
904 return PreservedAnalyses::all();
905
906 auto *CSEInfo = MFAM.getResult<GISelCSEAnalysis>(IR&: MF).get();
907 GISelValueTracking &VT = MFAM.getResult<GISelValueTrackingAnalysis>(IR&: MF);
908 MachineDominatorTree &MDT = MFAM.getResult<MachineDominatorTreeAnalysis>(IR&: MF);
909
910 const AArch64Subtarget &ST = MF.getSubtarget<AArch64Subtarget>();
911 auto &MAMProxy =
912 MFAM.getResult<ModuleAnalysisManagerMachineFunctionProxy>(IR&: MF);
913 const ModuleLibcallLoweringInfo *LibcallResult =
914 MAMProxy.getCachedResult<LibcallLoweringModuleAnalysis>(
915 IR&: *MF.getFunction().getParent());
916 if (!LibcallResult)
917 reportFatalUsageError(reason: "LibcallLoweringModuleAnalysis result not available");
918
919 const LibcallLoweringInfo &Libcalls = getLibcallLowering(ModuleInfo: *LibcallResult, Subtarget: ST);
920
921 bool EnableOpt = MF.getTarget().getOptLevel() != CodeGenOptLevel::None;
922
923 if (!runCombiner(MF, CSEInfo, VT: &VT, MDT: &MDT, Libcalls, RuleConfig: *RuleConfig, EnableOpt))
924 return PreservedAnalyses::all();
925
926 PreservedAnalyses PA = getMachineFunctionPassPreservedAnalyses();
927 PA.preserveSet<CFGAnalyses>();
928 PA.preserve<GISelValueTrackingAnalysis>();
929 PA.preserve<GISelCSEAnalysis>();
930 return PA;
931}
932
933namespace llvm {
934FunctionPass *createAArch64PreLegalizerCombiner() {
935 return new AArch64PreLegalizerCombinerLegacy();
936}
937} // end namespace llvm
938