[AArch64] Fix scheduler crash in fusion code.
[llvm-project.git] / llvm / lib / Target / AArch64 / AArch64MacroFusion.cpp
blobf51c27c62dfb79c5cb83a754f0f53d76c72d19f7
1 //===- AArch64MacroFusion.cpp - AArch64 Macro Fusion ----------------------===//
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 /// \file This file contains the AArch64 implementation of the DAG scheduling
10 /// mutation to pair instructions back to back.
12 //===----------------------------------------------------------------------===//
14 #include "AArch64MacroFusion.h"
15 #include "AArch64Subtarget.h"
16 #include "llvm/CodeGen/MacroFusion.h"
17 #include "llvm/CodeGen/TargetInstrInfo.h"
19 using namespace llvm;
21 /// CMN, CMP, TST followed by Bcc
22 static bool isArithmeticBccPair(const MachineInstr *FirstMI,
23 const MachineInstr &SecondMI, bool CmpOnly) {
24 if (SecondMI.getOpcode() != AArch64::Bcc)
25 return false;
27 // Assume the 1st instr to be a wildcard if it is unspecified.
28 if (FirstMI == nullptr)
29 return true;
31 // If we're in CmpOnly mode, we only fuse arithmetic instructions that
32 // discard their result.
33 if (CmpOnly && FirstMI->getOperand(0).isReg() &&
34 !(FirstMI->getOperand(0).getReg() == AArch64::XZR ||
35 FirstMI->getOperand(0).getReg() == AArch64::WZR)) {
36 return false;
39 switch (FirstMI->getOpcode()) {
40 case AArch64::ADDSWri:
41 case AArch64::ADDSWrr:
42 case AArch64::ADDSXri:
43 case AArch64::ADDSXrr:
44 case AArch64::ANDSWri:
45 case AArch64::ANDSWrr:
46 case AArch64::ANDSXri:
47 case AArch64::ANDSXrr:
48 case AArch64::SUBSWri:
49 case AArch64::SUBSWrr:
50 case AArch64::SUBSXri:
51 case AArch64::SUBSXrr:
52 case AArch64::BICSWrr:
53 case AArch64::BICSXrr:
54 return true;
55 case AArch64::ADDSWrs:
56 case AArch64::ADDSXrs:
57 case AArch64::ANDSWrs:
58 case AArch64::ANDSXrs:
59 case AArch64::SUBSWrs:
60 case AArch64::SUBSXrs:
61 case AArch64::BICSWrs:
62 case AArch64::BICSXrs:
63 // Shift value can be 0 making these behave like the "rr" variant...
64 return !AArch64InstrInfo::hasShiftedReg(*FirstMI);
67 return false;
70 /// ALU operations followed by CBZ/CBNZ.
71 static bool isArithmeticCbzPair(const MachineInstr *FirstMI,
72 const MachineInstr &SecondMI) {
73 if (SecondMI.getOpcode() != AArch64::CBZW &&
74 SecondMI.getOpcode() != AArch64::CBZX &&
75 SecondMI.getOpcode() != AArch64::CBNZW &&
76 SecondMI.getOpcode() != AArch64::CBNZX)
77 return false;
79 // Assume the 1st instr to be a wildcard if it is unspecified.
80 if (FirstMI == nullptr)
81 return true;
83 switch (FirstMI->getOpcode()) {
84 case AArch64::ADDWri:
85 case AArch64::ADDWrr:
86 case AArch64::ADDXri:
87 case AArch64::ADDXrr:
88 case AArch64::ANDWri:
89 case AArch64::ANDWrr:
90 case AArch64::ANDXri:
91 case AArch64::ANDXrr:
92 case AArch64::EORWri:
93 case AArch64::EORWrr:
94 case AArch64::EORXri:
95 case AArch64::EORXrr:
96 case AArch64::ORRWri:
97 case AArch64::ORRWrr:
98 case AArch64::ORRXri:
99 case AArch64::ORRXrr:
100 case AArch64::SUBWri:
101 case AArch64::SUBWrr:
102 case AArch64::SUBXri:
103 case AArch64::SUBXrr:
104 return true;
105 case AArch64::ADDWrs:
106 case AArch64::ADDXrs:
107 case AArch64::ANDWrs:
108 case AArch64::ANDXrs:
109 case AArch64::SUBWrs:
110 case AArch64::SUBXrs:
111 case AArch64::BICWrs:
112 case AArch64::BICXrs:
113 // Shift value can be 0 making these behave like the "rr" variant...
114 return !AArch64InstrInfo::hasShiftedReg(*FirstMI);
117 return false;
120 /// AES crypto encoding or decoding.
121 static bool isAESPair(const MachineInstr *FirstMI,
122 const MachineInstr &SecondMI) {
123 // Assume the 1st instr to be a wildcard if it is unspecified.
124 switch (SecondMI.getOpcode()) {
125 // AES encode.
126 case AArch64::AESMCrr:
127 case AArch64::AESMCrrTied:
128 return FirstMI == nullptr || FirstMI->getOpcode() == AArch64::AESErr;
129 // AES decode.
130 case AArch64::AESIMCrr:
131 case AArch64::AESIMCrrTied:
132 return FirstMI == nullptr || FirstMI->getOpcode() == AArch64::AESDrr;
135 return false;
138 /// AESE/AESD/PMULL + EOR.
139 static bool isCryptoEORPair(const MachineInstr *FirstMI,
140 const MachineInstr &SecondMI) {
141 if (SecondMI.getOpcode() != AArch64::EORv16i8)
142 return false;
144 // Assume the 1st instr to be a wildcard if it is unspecified.
145 if (FirstMI == nullptr)
146 return true;
148 switch (FirstMI->getOpcode()) {
149 case AArch64::AESErr:
150 case AArch64::AESDrr:
151 case AArch64::PMULLv16i8:
152 case AArch64::PMULLv8i8:
153 case AArch64::PMULLv1i64:
154 case AArch64::PMULLv2i64:
155 return true;
158 return false;
161 static bool isAdrpAddPair(const MachineInstr *FirstMI,
162 const MachineInstr &SecondMI) {
163 // Assume the 1st instr to be a wildcard if it is unspecified.
164 if ((FirstMI == nullptr || FirstMI->getOpcode() == AArch64::ADRP) &&
165 SecondMI.getOpcode() == AArch64::ADDXri)
166 return true;
167 return false;
170 /// Literal generation.
171 static bool isLiteralsPair(const MachineInstr *FirstMI,
172 const MachineInstr &SecondMI) {
173 // Assume the 1st instr to be a wildcard if it is unspecified.
174 // 32 bit immediate.
175 if ((FirstMI == nullptr || FirstMI->getOpcode() == AArch64::MOVZWi) &&
176 (SecondMI.getOpcode() == AArch64::MOVKWi &&
177 SecondMI.getOperand(3).getImm() == 16))
178 return true;
180 // Lower half of 64 bit immediate.
181 if((FirstMI == nullptr || FirstMI->getOpcode() == AArch64::MOVZXi) &&
182 (SecondMI.getOpcode() == AArch64::MOVKXi &&
183 SecondMI.getOperand(3).getImm() == 16))
184 return true;
186 // Upper half of 64 bit immediate.
187 if ((FirstMI == nullptr ||
188 (FirstMI->getOpcode() == AArch64::MOVKXi &&
189 FirstMI->getOperand(3).getImm() == 32)) &&
190 (SecondMI.getOpcode() == AArch64::MOVKXi &&
191 SecondMI.getOperand(3).getImm() == 48))
192 return true;
194 return false;
197 /// Fuse address generation and loads or stores.
198 static bool isAddressLdStPair(const MachineInstr *FirstMI,
199 const MachineInstr &SecondMI) {
200 switch (SecondMI.getOpcode()) {
201 case AArch64::STRBBui:
202 case AArch64::STRBui:
203 case AArch64::STRDui:
204 case AArch64::STRHHui:
205 case AArch64::STRHui:
206 case AArch64::STRQui:
207 case AArch64::STRSui:
208 case AArch64::STRWui:
209 case AArch64::STRXui:
210 case AArch64::LDRBBui:
211 case AArch64::LDRBui:
212 case AArch64::LDRDui:
213 case AArch64::LDRHHui:
214 case AArch64::LDRHui:
215 case AArch64::LDRQui:
216 case AArch64::LDRSui:
217 case AArch64::LDRWui:
218 case AArch64::LDRXui:
219 case AArch64::LDRSBWui:
220 case AArch64::LDRSBXui:
221 case AArch64::LDRSHWui:
222 case AArch64::LDRSHXui:
223 case AArch64::LDRSWui:
224 // Assume the 1st instr to be a wildcard if it is unspecified.
225 if (FirstMI == nullptr)
226 return true;
228 switch (FirstMI->getOpcode()) {
229 case AArch64::ADR:
230 return SecondMI.getOperand(2).getImm() == 0;
231 case AArch64::ADRP:
232 return true;
236 return false;
239 /// Compare and conditional select.
240 static bool isCCSelectPair(const MachineInstr *FirstMI,
241 const MachineInstr &SecondMI) {
242 // 32 bits
243 if (SecondMI.getOpcode() == AArch64::CSELWr) {
244 // Assume the 1st instr to be a wildcard if it is unspecified.
245 if (FirstMI == nullptr)
246 return true;
248 if (FirstMI->definesRegister(AArch64::WZR))
249 switch (FirstMI->getOpcode()) {
250 case AArch64::SUBSWrs:
251 return !AArch64InstrInfo::hasShiftedReg(*FirstMI);
252 case AArch64::SUBSWrx:
253 return !AArch64InstrInfo::hasExtendedReg(*FirstMI);
254 case AArch64::SUBSWrr:
255 case AArch64::SUBSWri:
256 return true;
260 // 64 bits
261 if (SecondMI.getOpcode() == AArch64::CSELXr) {
262 // Assume the 1st instr to be a wildcard if it is unspecified.
263 if (FirstMI == nullptr)
264 return true;
266 if (FirstMI->definesRegister(AArch64::XZR))
267 switch (FirstMI->getOpcode()) {
268 case AArch64::SUBSXrs:
269 return !AArch64InstrInfo::hasShiftedReg(*FirstMI);
270 case AArch64::SUBSXrx:
271 case AArch64::SUBSXrx64:
272 return !AArch64InstrInfo::hasExtendedReg(*FirstMI);
273 case AArch64::SUBSXrr:
274 case AArch64::SUBSXri:
275 return true;
279 return false;
282 // Arithmetic and logic.
283 static bool isArithmeticLogicPair(const MachineInstr *FirstMI,
284 const MachineInstr &SecondMI) {
285 if (AArch64InstrInfo::hasShiftedReg(SecondMI))
286 return false;
288 switch (SecondMI.getOpcode()) {
289 // Arithmetic
290 case AArch64::ADDWrr:
291 case AArch64::ADDXrr:
292 case AArch64::SUBWrr:
293 case AArch64::SUBXrr:
294 case AArch64::ADDWrs:
295 case AArch64::ADDXrs:
296 case AArch64::SUBWrs:
297 case AArch64::SUBXrs:
298 // Logic
299 case AArch64::ANDWrr:
300 case AArch64::ANDXrr:
301 case AArch64::BICWrr:
302 case AArch64::BICXrr:
303 case AArch64::EONWrr:
304 case AArch64::EONXrr:
305 case AArch64::EORWrr:
306 case AArch64::EORXrr:
307 case AArch64::ORNWrr:
308 case AArch64::ORNXrr:
309 case AArch64::ORRWrr:
310 case AArch64::ORRXrr:
311 case AArch64::ANDWrs:
312 case AArch64::ANDXrs:
313 case AArch64::BICWrs:
314 case AArch64::BICXrs:
315 case AArch64::EONWrs:
316 case AArch64::EONXrs:
317 case AArch64::EORWrs:
318 case AArch64::EORXrs:
319 case AArch64::ORNWrs:
320 case AArch64::ORNXrs:
321 case AArch64::ORRWrs:
322 case AArch64::ORRXrs:
323 // Assume the 1st instr to be a wildcard if it is unspecified.
324 if (FirstMI == nullptr)
325 return true;
327 // Arithmetic
328 switch (FirstMI->getOpcode()) {
329 case AArch64::ADDWrr:
330 case AArch64::ADDXrr:
331 case AArch64::ADDSWrr:
332 case AArch64::ADDSXrr:
333 case AArch64::SUBWrr:
334 case AArch64::SUBXrr:
335 case AArch64::SUBSWrr:
336 case AArch64::SUBSXrr:
337 return true;
338 case AArch64::ADDWrs:
339 case AArch64::ADDXrs:
340 case AArch64::ADDSWrs:
341 case AArch64::ADDSXrs:
342 case AArch64::SUBWrs:
343 case AArch64::SUBXrs:
344 case AArch64::SUBSWrs:
345 case AArch64::SUBSXrs:
346 return !AArch64InstrInfo::hasShiftedReg(*FirstMI);
348 break;
350 // Arithmetic, setting flags.
351 case AArch64::ADDSWrr:
352 case AArch64::ADDSXrr:
353 case AArch64::SUBSWrr:
354 case AArch64::SUBSXrr:
355 case AArch64::ADDSWrs:
356 case AArch64::ADDSXrs:
357 case AArch64::SUBSWrs:
358 case AArch64::SUBSXrs:
359 // Assume the 1st instr to be a wildcard if it is unspecified.
360 if (FirstMI == nullptr)
361 return true;
363 // Arithmetic, not setting flags.
364 switch (FirstMI->getOpcode()) {
365 case AArch64::ADDWrr:
366 case AArch64::ADDXrr:
367 case AArch64::SUBWrr:
368 case AArch64::SUBXrr:
369 return true;
370 case AArch64::ADDWrs:
371 case AArch64::ADDXrs:
372 case AArch64::SUBWrs:
373 case AArch64::SUBXrs:
374 return !AArch64InstrInfo::hasShiftedReg(*FirstMI);
376 break;
379 return false;
382 /// \brief Check if the instr pair, FirstMI and SecondMI, should be fused
383 /// together. Given SecondMI, when FirstMI is unspecified, then check if
384 /// SecondMI may be part of a fused pair at all.
385 static bool shouldScheduleAdjacent(const TargetInstrInfo &TII,
386 const TargetSubtargetInfo &TSI,
387 const MachineInstr *FirstMI,
388 const MachineInstr &SecondMI) {
389 const AArch64Subtarget &ST = static_cast<const AArch64Subtarget&>(TSI);
391 // All checking functions assume that the 1st instr is a wildcard if it is
392 // unspecified.
393 if (ST.hasCmpBccFusion() || ST.hasArithmeticBccFusion()) {
394 bool CmpOnly = !ST.hasArithmeticBccFusion();
395 if (isArithmeticBccPair(FirstMI, SecondMI, CmpOnly))
396 return true;
398 if (ST.hasArithmeticCbzFusion() && isArithmeticCbzPair(FirstMI, SecondMI))
399 return true;
400 if (ST.hasFuseAES() && isAESPair(FirstMI, SecondMI))
401 return true;
402 if (ST.hasFuseCryptoEOR() && isCryptoEORPair(FirstMI, SecondMI))
403 return true;
404 if (ST.hasFuseAdrpAdd() && isAdrpAddPair(FirstMI, SecondMI))
405 return true;
406 if (ST.hasFuseLiterals() && isLiteralsPair(FirstMI, SecondMI))
407 return true;
408 if (ST.hasFuseAddress() && isAddressLdStPair(FirstMI, SecondMI))
409 return true;
410 if (ST.hasFuseCCSelect() && isCCSelectPair(FirstMI, SecondMI))
411 return true;
412 if (ST.hasFuseArithmeticLogic() && isArithmeticLogicPair(FirstMI, SecondMI))
413 return true;
415 return false;
418 std::unique_ptr<ScheduleDAGMutation>
419 llvm::createAArch64MacroFusionDAGMutation() {
420 return createMacroFusionDAGMutation(shouldScheduleAdjacent);