LLVM 23.0.0git
AArch64MacroFusion.cpp
Go to the documentation of this file.
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.
11//
12//===----------------------------------------------------------------------===//
13
14#include "AArch64MacroFusion.h"
15#include "AArch64Subtarget.h"
18
19using namespace llvm;
20
21/// CMN, CMP, TST followed by Bcc
22static bool isArithmeticBccPair(const MachineInstr *FirstMI,
23 const MachineInstr &SecondMI, bool CmpOnly) {
24 if (SecondMI.getOpcode() != AArch64::Bcc)
25 return false;
26
27 // Assume the 1st instr to be a wildcard if it is unspecified.
28 if (FirstMI == nullptr)
29 return true;
30
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;
37 }
38
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);
65 }
66
67 return false;
68}
69
70/// ALU operations followed by CBZ/CBNZ.
71static 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;
78
79 // Assume the 1st instr to be a wildcard if it is unspecified.
80 if (FirstMI == nullptr)
81 return true;
82
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);
115 }
116
117 return false;
118}
119
120/// AES crypto encoding or decoding.
121static 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;
133 }
134
135 return false;
136}
137
138/// AESE/AESD/PMULL + EOR.
139static bool isCryptoEORPair(const MachineInstr *FirstMI,
140 const MachineInstr &SecondMI) {
141 if (SecondMI.getOpcode() != AArch64::EORv16i8)
142 return false;
143
144 // Assume the 1st instr to be a wildcard if it is unspecified.
145 if (FirstMI == nullptr)
146 return true;
147
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;
156 }
157
158 return false;
159}
160
161static 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;
168}
169
170/// Literal generation.
171static 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;
179
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;
185
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;
193
194 return false;
195}
196
197/// Fuse address generation and loads or stores.
198static 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;
227
228 switch (FirstMI->getOpcode()) {
229 case AArch64::ADR:
230 return SecondMI.getOperand(2).getImm() == 0;
231 case AArch64::ADRP:
232 return true;
233 }
234 }
235
236 return false;
237}
238
239/// Compare and conditional select.
240static bool isCmpCSelPair(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;
247
248 if (FirstMI->definesRegister(AArch64::WZR, /*TRI=*/nullptr))
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;
257 }
258 }
259
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;
265
266 if (FirstMI->definesRegister(AArch64::XZR, /*TRI=*/nullptr))
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;
276 }
277 }
278
279 return false;
280}
281
282/// Floating-point compare and floating-point conditional select.
283static bool isFCmpFCSelPair(const MachineInstr *FirstMI,
284 const MachineInstr &SecondMI) {
285 switch (SecondMI.getOpcode()) {
286 case AArch64::FCSELSrrr:
287 case AArch64::FCSELDrrr:
288 case AArch64::FCSELHrrr:
289 break;
290 default:
291 return false;
292 }
293
294 // Assume the 1st instr to be a wildcard if it is unspecified.
295 if (FirstMI == nullptr)
296 return true;
297
298 switch (FirstMI->getOpcode()) {
299 case AArch64::FCMPSrr:
300 case AArch64::FCMPDrr:
301 case AArch64::FCMPESrr:
302 case AArch64::FCMPEDrr:
303 case AArch64::FCMPHrr:
304 case AArch64::FCMPEHrr:
305 return true;
306 default:
307 return false;
308 }
309}
310
311/// Compare and cset.
312static bool isCmpCSetPair(const MachineInstr *FirstMI,
313 const MachineInstr &SecondMI) {
314 if ((SecondMI.getOpcode() == AArch64::CSINCWr &&
315 SecondMI.getOperand(1).getReg() == AArch64::WZR &&
316 SecondMI.getOperand(2).getReg() == AArch64::WZR) ||
317 (SecondMI.getOpcode() == AArch64::CSINCXr &&
318 SecondMI.getOperand(1).getReg() == AArch64::XZR &&
319 SecondMI.getOperand(2).getReg() == AArch64::XZR)) {
320 // Assume the 1st instr to be a wildcard if it is unspecified.
321 if (FirstMI == nullptr)
322 return true;
323
324 if (FirstMI->definesRegister(AArch64::WZR, /*TRI=*/nullptr) ||
325 FirstMI->definesRegister(AArch64::XZR, /*TRI=*/nullptr))
326 switch (FirstMI->getOpcode()) {
327 case AArch64::SUBSWrs:
328 case AArch64::SUBSXrs:
329 return !AArch64InstrInfo::hasShiftedReg(*FirstMI);
330 case AArch64::SUBSWrx:
331 case AArch64::SUBSXrx:
332 case AArch64::SUBSXrx64:
333 return !AArch64InstrInfo::hasExtendedReg(*FirstMI);
334 case AArch64::SUBSWri:
335 case AArch64::SUBSWrr:
336 case AArch64::SUBSXri:
337 case AArch64::SUBSXrr:
338 return true;
339 }
340 }
341
342 return false;
343}
344
345// Arithmetic and logic.
346static bool isArithmeticLogicPair(const MachineInstr *FirstMI,
347 const MachineInstr &SecondMI) {
348 if (AArch64InstrInfo::hasShiftedReg(SecondMI))
349 return false;
350
351 switch (SecondMI.getOpcode()) {
352 // Arithmetic
353 case AArch64::ADDWrr:
354 case AArch64::ADDXrr:
355 case AArch64::SUBWrr:
356 case AArch64::SUBXrr:
357 case AArch64::ADDWrs:
358 case AArch64::ADDXrs:
359 case AArch64::SUBWrs:
360 case AArch64::SUBXrs:
361 // Logic
362 case AArch64::ANDWrr:
363 case AArch64::ANDXrr:
364 case AArch64::BICWrr:
365 case AArch64::BICXrr:
366 case AArch64::EONWrr:
367 case AArch64::EONXrr:
368 case AArch64::EORWrr:
369 case AArch64::EORXrr:
370 case AArch64::ORNWrr:
371 case AArch64::ORNXrr:
372 case AArch64::ORRWrr:
373 case AArch64::ORRXrr:
374 case AArch64::ANDWrs:
375 case AArch64::ANDXrs:
376 case AArch64::BICWrs:
377 case AArch64::BICXrs:
378 case AArch64::EONWrs:
379 case AArch64::EONXrs:
380 case AArch64::EORWrs:
381 case AArch64::EORXrs:
382 case AArch64::ORNWrs:
383 case AArch64::ORNXrs:
384 case AArch64::ORRWrs:
385 case AArch64::ORRXrs:
386 // Assume the 1st instr to be a wildcard if it is unspecified.
387 if (FirstMI == nullptr)
388 return true;
389
390 // Arithmetic
391 switch (FirstMI->getOpcode()) {
392 case AArch64::ADDWrr:
393 case AArch64::ADDXrr:
394 case AArch64::ADDSWrr:
395 case AArch64::ADDSXrr:
396 case AArch64::SUBWrr:
397 case AArch64::SUBXrr:
398 case AArch64::SUBSWrr:
399 case AArch64::SUBSXrr:
400 return true;
401 case AArch64::ADDWrs:
402 case AArch64::ADDXrs:
403 case AArch64::ADDSWrs:
404 case AArch64::ADDSXrs:
405 case AArch64::SUBWrs:
406 case AArch64::SUBXrs:
407 case AArch64::SUBSWrs:
408 case AArch64::SUBSXrs:
409 return !AArch64InstrInfo::hasShiftedReg(*FirstMI);
410 }
411 break;
412
413 // Arithmetic, setting flags.
414 case AArch64::ADDSWrr:
415 case AArch64::ADDSXrr:
416 case AArch64::SUBSWrr:
417 case AArch64::SUBSXrr:
418 case AArch64::ADDSWrs:
419 case AArch64::ADDSXrs:
420 case AArch64::SUBSWrs:
421 case AArch64::SUBSXrs:
422 // Assume the 1st instr to be a wildcard if it is unspecified.
423 if (FirstMI == nullptr)
424 return true;
425
426 // Arithmetic, not setting flags.
427 switch (FirstMI->getOpcode()) {
428 case AArch64::ADDWrr:
429 case AArch64::ADDXrr:
430 case AArch64::SUBWrr:
431 case AArch64::SUBXrr:
432 return true;
433 case AArch64::ADDWrs:
434 case AArch64::ADDXrs:
435 case AArch64::SUBWrs:
436 case AArch64::SUBXrs:
437 return !AArch64InstrInfo::hasShiftedReg(*FirstMI);
438 }
439 break;
440 }
441
442 return false;
443}
444
445// "(A + B) + 1" or "(A - B) - 1"
446static bool isAddSub2RegAndConstOnePair(const MachineInstr *FirstMI,
447 const MachineInstr &SecondMI) {
448 bool NeedsSubtract = false;
449
450 // The 2nd instr must be an add-immediate or subtract-immediate.
451 switch (SecondMI.getOpcode()) {
452 case AArch64::SUBWri:
453 case AArch64::SUBXri:
454 NeedsSubtract = true;
455 [[fallthrough]];
456 case AArch64::ADDWri:
457 case AArch64::ADDXri:
458 break;
459
460 default:
461 return false;
462 }
463
464 // The immediate in the 2nd instr must be "1".
465 if (!SecondMI.getOperand(2).isImm() || SecondMI.getOperand(2).getImm() != 1) {
466 return false;
467 }
468
469 // Assume the 1st instr to be a wildcard if it is unspecified.
470 if (FirstMI == nullptr) {
471 return true;
472 }
473
474 switch (FirstMI->getOpcode()) {
475 case AArch64::SUBWrs:
476 case AArch64::SUBXrs:
477 if (AArch64InstrInfo::hasShiftedReg(*FirstMI))
478 return false;
479 [[fallthrough]];
480 case AArch64::SUBWrr:
481 case AArch64::SUBXrr:
482 if (NeedsSubtract) {
483 return true;
484 }
485 break;
486
487 case AArch64::ADDWrs:
488 case AArch64::ADDXrs:
489 if (AArch64InstrInfo::hasShiftedReg(*FirstMI))
490 return false;
491 [[fallthrough]];
492 case AArch64::ADDWrr:
493 case AArch64::ADDXrr:
494 if (!NeedsSubtract) {
495 return true;
496 }
497 break;
498 }
499
500 return false;
501}
502
503/// \brief Check if the instr pair, FirstMI and SecondMI, should be fused
504/// together. Given SecondMI, when FirstMI is unspecified, then check if
505/// SecondMI may be part of a fused pair at all.
507 const TargetSubtargetInfo &TSI,
508 const MachineInstr *FirstMI,
509 const MachineInstr &SecondMI) {
510 const AArch64Subtarget &ST = static_cast<const AArch64Subtarget&>(TSI);
511
512 // All checking functions assume that the 1st instr is a wildcard if it is
513 // unspecified.
514 if (ST.hasCmpBccFusion() || ST.hasArithmeticBccFusion()) {
515 bool CmpOnly = !ST.hasArithmeticBccFusion();
516 if (isArithmeticBccPair(FirstMI, SecondMI, CmpOnly))
517 return true;
518 }
519 if (ST.hasArithmeticCbzFusion() && isArithmeticCbzPair(FirstMI, SecondMI))
520 return true;
521 if (ST.hasFuseAES() && isAESPair(FirstMI, SecondMI))
522 return true;
523 if (ST.hasFuseCryptoEOR() && isCryptoEORPair(FirstMI, SecondMI))
524 return true;
525 if (ST.hasFuseAdrpAdd() && isAdrpAddPair(FirstMI, SecondMI))
526 return true;
527 if (ST.hasFuseLiterals() && isLiteralsPair(FirstMI, SecondMI))
528 return true;
529 if (ST.hasFuseAddress() && isAddressLdStPair(FirstMI, SecondMI))
530 return true;
531 if (ST.hasFuseCmpCSel() && isCmpCSelPair(FirstMI, SecondMI))
532 return true;
533 if (ST.hasFuseFCmpFCSel() && isFCmpFCSelPair(FirstMI, SecondMI))
534 return true;
535 if (ST.hasFuseCmpCSet() && isCmpCSetPair(FirstMI, SecondMI))
536 return true;
537 if (ST.hasFuseArithmeticLogic() && isArithmeticLogicPair(FirstMI, SecondMI))
538 return true;
539 if (ST.hasFuseAddSub2RegAndConstOne() &&
540 isAddSub2RegAndConstOnePair(FirstMI, SecondMI))
541 return true;
542
543 return false;
544}
545
546std::unique_ptr<ScheduleDAGMutation>
static bool isFCmpFCSelPair(const MachineInstr *FirstMI, const MachineInstr &SecondMI)
Floating-point compare and floating-point conditional select.
static bool isAddSub2RegAndConstOnePair(const MachineInstr *FirstMI, const MachineInstr &SecondMI)
static bool isCmpCSelPair(const MachineInstr *FirstMI, const MachineInstr &SecondMI)
Compare and conditional select.
static bool isArithmeticBccPair(const MachineInstr *FirstMI, const MachineInstr &SecondMI, bool CmpOnly)
CMN, CMP, TST followed by Bcc.
static bool isAddressLdStPair(const MachineInstr *FirstMI, const MachineInstr &SecondMI)
Fuse address generation and loads or stores.
static bool isArithmeticCbzPair(const MachineInstr *FirstMI, const MachineInstr &SecondMI)
ALU operations followed by CBZ/CBNZ.
static bool shouldScheduleAdjacent(const TargetInstrInfo &TII, const TargetSubtargetInfo &TSI, const MachineInstr *FirstMI, const MachineInstr &SecondMI)
Check if the instr pair, FirstMI and SecondMI, should be fused together.
static bool isAESPair(const MachineInstr *FirstMI, const MachineInstr &SecondMI)
AES crypto encoding or decoding.
static bool isCmpCSetPair(const MachineInstr *FirstMI, const MachineInstr &SecondMI)
Compare and cset.
static bool isAdrpAddPair(const MachineInstr *FirstMI, const MachineInstr &SecondMI)
static bool isArithmeticLogicPair(const MachineInstr *FirstMI, const MachineInstr &SecondMI)
static bool isCryptoEORPair(const MachineInstr *FirstMI, const MachineInstr &SecondMI)
AESE/AESD/PMULL + EOR.
static bool isLiteralsPair(const MachineInstr *FirstMI, const MachineInstr &SecondMI)
Literal generation.
const HexagonInstrInfo * TII
Representation of each machine instruction.
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
bool definesRegister(Register Reg, const TargetRegisterInfo *TRI) const
Return true if the MachineInstr fully defines the specified register.
const MachineOperand & getOperand(unsigned i) const
int64_t getImm() const
bool isReg() const
isReg - Tests if this is a MO_Register operand.
bool isImm() const
isImm - Tests if this is a MO_Immediate operand.
Register getReg() const
getReg - Returns the register number.
TargetInstrInfo - Interface to description of machine instruction set.
TargetSubtargetInfo - Generic base class for all target subtargets.
This is an optimization pass for GlobalISel generic memory operations.
Definition Types.h:26
LLVM_ABI std::unique_ptr< ScheduleDAGMutation > createMacroFusionDAGMutation(ArrayRef< MacroFusionPredTy > Predicates, bool BranchOnly=false)
Create a DAG scheduling mutation to pair instructions back to back for instructions that benefit acco...
std::unique_ptr< ScheduleDAGMutation > createAArch64MacroFusionDAGMutation()
Note that you have to add: DAG.addMutation(createAArch64MacroFusionDAGMutation()); to AArch64TargetMa...
static bool shouldScheduleAdjacent(const TargetInstrInfo &TII, const TargetSubtargetInfo &TSI, const MachineInstr *FirstMI, const MachineInstr &SecondMI)
Check if the instr pair, FirstMI and SecondMI, should be fused together.