1 //=== lib/CodeGen/GlobalISel/AArch64PostLegalizerCombiner.cpp -------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This performs post-legalization combines on generic MachineInstrs.
11 // Any combine that this pass performs must preserve instruction legality.
12 // Combines unconcerned with legality should be handled by the
13 // PreLegalizerCombiner instead.
15 //===----------------------------------------------------------------------===//
17 #include "AArch64TargetMachine.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/GISelKnownBits.h"
22 #include "llvm/CodeGen/MachineDominators.h"
23 #include "llvm/CodeGen/MachineFunctionPass.h"
24 #include "llvm/CodeGen/TargetPassConfig.h"
25 #include "llvm/Support/Debug.h"
27 #define DEBUG_TYPE "aarch64-postlegalizer-combiner"
31 /// Represents a pseudo instruction which replaces a G_SHUFFLE_VECTOR.
33 /// Used for matching target-supported shuffles before codegen.
34 struct ShuffleVectorPseudo {
35 unsigned Opc; ///< Opcode for the instruction. (E.g. G_ZIP1)
36 Register Dst; ///< Destination register.
37 SmallVector<SrcOp, 2> SrcOps; ///< Source registers.
38 ShuffleVectorPseudo(unsigned Opc, Register Dst,
39 std::initializer_list<SrcOp> SrcOps)
40 : Opc(Opc), Dst(Dst), SrcOps(SrcOps){};
41 ShuffleVectorPseudo() {}
44 /// Check if a vector shuffle corresponds to a REV instruction with the
45 /// specified blocksize.
46 static bool isREVMask(ArrayRef<int> M, unsigned EltSize, unsigned NumElts,
48 assert((BlockSize == 16 || BlockSize == 32 || BlockSize == 64) &&
49 "Only possible block sizes for REV are: 16, 32, 64");
50 assert(EltSize != 64 && "EltSize cannot be 64 for REV mask.");
52 unsigned BlockElts = M[0] + 1;
54 // If the first shuffle index is UNDEF, be optimistic.
56 BlockElts = BlockSize / EltSize;
58 if (BlockSize <= EltSize || BlockSize != BlockElts * EltSize)
61 for (unsigned i = 0; i < NumElts; ++i) {
62 // Ignore undef indices.
65 if (static_cast<unsigned>(M[i]) !=
66 (i - i % BlockElts) + (BlockElts - 1 - i % BlockElts))
73 /// Determines if \p M is a shuffle vector mask for a UZP of \p NumElts.
74 /// Whether or not G_UZP1 or G_UZP2 should be used is stored in \p WhichResult.
75 static bool isUZPMask(ArrayRef<int> M, unsigned NumElts,
76 unsigned &WhichResult) {
77 WhichResult = (M[0] == 0 ? 0 : 1);
78 for (unsigned i = 0; i != NumElts; ++i) {
79 // Skip undef indices.
82 if (static_cast<unsigned>(M[i]) != 2 * i + WhichResult)
88 /// \return true if \p M is a zip mask for a shuffle vector of \p NumElts.
89 /// Whether or not G_ZIP1 or G_ZIP2 should be used is stored in \p WhichResult.
90 static bool isZipMask(ArrayRef<int> M, unsigned NumElts,
91 unsigned &WhichResult) {
95 // 0 means use ZIP1, 1 means use ZIP2.
96 WhichResult = (M[0] == 0 ? 0 : 1);
97 unsigned Idx = WhichResult * NumElts / 2;
98 for (unsigned i = 0; i != NumElts; i += 2) {
99 if ((M[i] >= 0 && static_cast<unsigned>(M[i]) != Idx) ||
100 (M[i + 1] >= 0 && static_cast<unsigned>(M[i + 1]) != Idx + NumElts))
107 /// \return true if a G_SHUFFLE_VECTOR instruction \p MI can be replaced with a
108 /// G_REV instruction. Returns the appropriate G_REV opcode in \p Opc.
109 static bool matchREV(MachineInstr &MI, MachineRegisterInfo &MRI,
110 ShuffleVectorPseudo &MatchInfo) {
111 assert(MI.getOpcode() == TargetOpcode::G_SHUFFLE_VECTOR);
112 ArrayRef<int> ShuffleMask = MI.getOperand(3).getShuffleMask();
113 Register Dst = MI.getOperand(0).getReg();
114 Register Src = MI.getOperand(1).getReg();
115 LLT Ty = MRI.getType(Dst);
116 unsigned EltSize = Ty.getScalarSizeInBits();
118 // Element size for a rev cannot be 64.
122 unsigned NumElts = Ty.getNumElements();
124 // Try to produce G_REV64
125 if (isREVMask(ShuffleMask, EltSize, NumElts, 64)) {
126 MatchInfo = ShuffleVectorPseudo(AArch64::G_REV64, Dst, {Src});
130 // TODO: Produce G_REV32 and G_REV16 once we have proper legalization support.
131 // This should be identical to above, but with a constant 32 and constant
136 /// \return true if a G_SHUFFLE_VECTOR instruction \p MI can be replaced with
137 /// a G_UZP1 or G_UZP2 instruction.
139 /// \param [in] MI - The shuffle vector instruction.
140 /// \param [out] Opc - Either G_UZP1 or G_UZP2 on success.
141 static bool matchUZP(MachineInstr &MI, MachineRegisterInfo &MRI,
142 ShuffleVectorPseudo &MatchInfo) {
143 assert(MI.getOpcode() == TargetOpcode::G_SHUFFLE_VECTOR);
144 unsigned WhichResult;
145 ArrayRef<int> ShuffleMask = MI.getOperand(3).getShuffleMask();
146 Register Dst = MI.getOperand(0).getReg();
147 unsigned NumElts = MRI.getType(Dst).getNumElements();
148 if (!isUZPMask(ShuffleMask, NumElts, WhichResult))
150 unsigned Opc = (WhichResult == 0) ? AArch64::G_UZP1 : AArch64::G_UZP2;
151 Register V1 = MI.getOperand(1).getReg();
152 Register V2 = MI.getOperand(2).getReg();
153 MatchInfo = ShuffleVectorPseudo(Opc, Dst, {V1, V2});
157 static bool matchZip(MachineInstr &MI, MachineRegisterInfo &MRI,
158 ShuffleVectorPseudo &MatchInfo) {
159 assert(MI.getOpcode() == TargetOpcode::G_SHUFFLE_VECTOR);
160 unsigned WhichResult;
161 ArrayRef<int> ShuffleMask = MI.getOperand(3).getShuffleMask();
162 Register Dst = MI.getOperand(0).getReg();
163 unsigned NumElts = MRI.getType(Dst).getNumElements();
164 if (!isZipMask(ShuffleMask, NumElts, WhichResult))
166 unsigned Opc = (WhichResult == 0) ? AArch64::G_ZIP1 : AArch64::G_ZIP2;
167 Register V1 = MI.getOperand(1).getReg();
168 Register V2 = MI.getOperand(2).getReg();
169 MatchInfo = ShuffleVectorPseudo(Opc, Dst, {V1, V2});
173 /// Replace a G_SHUFFLE_VECTOR instruction with a pseudo.
174 /// \p Opc is the opcode to use. \p MI is the G_SHUFFLE_VECTOR.
175 static bool applyShuffleVectorPseudo(MachineInstr &MI,
176 ShuffleVectorPseudo &MatchInfo) {
177 MachineIRBuilder MIRBuilder(MI);
178 MIRBuilder.buildInstr(MatchInfo.Opc, {MatchInfo.Dst}, MatchInfo.SrcOps);
179 MI.eraseFromParent();
183 #define AARCH64POSTLEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_DEPS
184 #include "AArch64GenPostLegalizeGICombiner.inc"
185 #undef AARCH64POSTLEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_DEPS
188 #define AARCH64POSTLEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_H
189 #include "AArch64GenPostLegalizeGICombiner.inc"
190 #undef AARCH64POSTLEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_H
192 class AArch64PostLegalizerCombinerInfo : public CombinerInfo {
194 MachineDominatorTree *MDT;
197 AArch64GenPostLegalizerCombinerHelper Generated;
199 AArch64PostLegalizerCombinerInfo(bool EnableOpt, bool OptSize, bool MinSize,
201 MachineDominatorTree *MDT)
202 : CombinerInfo(/*AllowIllegalOps*/ true, /*ShouldLegalizeIllegal*/ false,
203 /*LegalizerInfo*/ nullptr, EnableOpt, OptSize, MinSize),
205 if (!Generated.parseCommandLineOption())
206 report_fatal_error("Invalid rule identifier");
209 virtual bool combine(GISelChangeObserver &Observer, MachineInstr &MI,
210 MachineIRBuilder &B) const override;
213 bool AArch64PostLegalizerCombinerInfo::combine(GISelChangeObserver &Observer,
215 MachineIRBuilder &B) const {
217 MI.getParent()->getParent()->getSubtarget().getLegalizerInfo();
218 CombinerHelper Helper(Observer, B, KB, MDT, LI);
219 return Generated.tryCombineAll(Observer, MI, B, Helper);
222 #define AARCH64POSTLEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_CPP
223 #include "AArch64GenPostLegalizeGICombiner.inc"
224 #undef AARCH64POSTLEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_CPP
226 class AArch64PostLegalizerCombiner : public MachineFunctionPass {
230 AArch64PostLegalizerCombiner(bool IsOptNone = false);
232 StringRef getPassName() const override {
233 return "AArch64PostLegalizerCombiner";
236 bool runOnMachineFunction(MachineFunction &MF) override;
237 void getAnalysisUsage(AnalysisUsage &AU) const override;
242 } // end anonymous namespace
244 void AArch64PostLegalizerCombiner::getAnalysisUsage(AnalysisUsage &AU) const {
245 AU.addRequired<TargetPassConfig>();
246 AU.setPreservesCFG();
247 getSelectionDAGFallbackAnalysisUsage(AU);
248 AU.addRequired<GISelKnownBitsAnalysis>();
249 AU.addPreserved<GISelKnownBitsAnalysis>();
251 AU.addRequired<MachineDominatorTree>();
252 AU.addPreserved<MachineDominatorTree>();
254 MachineFunctionPass::getAnalysisUsage(AU);
257 AArch64PostLegalizerCombiner::AArch64PostLegalizerCombiner(bool IsOptNone)
258 : MachineFunctionPass(ID), IsOptNone(IsOptNone) {
259 initializeAArch64PostLegalizerCombinerPass(*PassRegistry::getPassRegistry());
262 bool AArch64PostLegalizerCombiner::runOnMachineFunction(MachineFunction &MF) {
263 if (MF.getProperties().hasProperty(
264 MachineFunctionProperties::Property::FailedISel))
266 assert(MF.getProperties().hasProperty(
267 MachineFunctionProperties::Property::Legalized) &&
268 "Expected a legalized function?");
269 auto *TPC = &getAnalysis<TargetPassConfig>();
270 const Function &F = MF.getFunction();
272 MF.getTarget().getOptLevel() != CodeGenOpt::None && !skipFunction(F);
273 GISelKnownBits *KB = &getAnalysis<GISelKnownBitsAnalysis>().get(MF);
274 MachineDominatorTree *MDT =
275 IsOptNone ? nullptr : &getAnalysis<MachineDominatorTree>();
276 AArch64PostLegalizerCombinerInfo PCInfo(EnableOpt, F.hasOptSize(),
277 F.hasMinSize(), KB, MDT);
278 Combiner C(PCInfo, TPC);
279 return C.combineMachineInstrs(MF, /*CSEInfo*/ nullptr);
282 char AArch64PostLegalizerCombiner::ID = 0;
283 INITIALIZE_PASS_BEGIN(AArch64PostLegalizerCombiner, DEBUG_TYPE,
284 "Combine AArch64 MachineInstrs after legalization", false,
286 INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
287 INITIALIZE_PASS_DEPENDENCY(GISelKnownBitsAnalysis)
288 INITIALIZE_PASS_END(AArch64PostLegalizerCombiner, DEBUG_TYPE,
289 "Combine AArch64 MachineInstrs after legalization", false,
293 FunctionPass *createAArch64PostLegalizeCombiner(bool IsOptNone) {
294 return new AArch64PostLegalizerCombiner(IsOptNone);
296 } // end namespace llvm