[llvm] [AArch64][MacroFusion] Fuse only tied AES pairs (PR #196484)

Tomer Shafir via llvm-commits llvm-commits at lists.llvm.org
Fri May 8 07:24:32 PDT 2026


https://github.com/tomershafir updated https://github.com/llvm/llvm-project/pull/196484

>From ca44c0744429afd300c1d43efd2aec6e336e8b2c Mon Sep 17 00:00:00 2001
From: tomershafir <tomer.shafir8 at gmail.com>
Date: Fri, 8 May 2026 10:52:03 +0300
Subject: [PATCH] [AArch64][MacroFusion] Fuse only tied AES pairs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This patch adds an ad-hoc check to macro fusion to only fuse AES pairs that are tied, to more explicitly avoid regressions.

In the case of full compilation - ISel captures every RAW dependent AESE/D+AES[I]MC pair (by data-dependence DAG), and applies a constraint that the pair must write to the same dest, i.e the second instruction is tied (a thing that cannot be expressed in SSA IR). However, I think it’s not clear or enforced on macro fusion. The tests in `llvm/test/CodeGen/AArch64/misched-fusion-aes.ll` may not catch an ISel change that would happen to pass, satisfying the register allocation being filechecked. Notice that in practice all subtargets effectively fuse only tied pairs. Thus, adding an explicit check to avoid inaccurate fusions. If it appears in the future that a subtarget can fuse untied pairs, we should re-address and maybe distinguish 2 subtarget features for the 2 cases.

Plus adding a test runline for latest `apple-m5`.

Note: it cannot be an assert, e.g. a partial pipeline like in `llvm/test/CodeGen/AArch64/misched-fusion-crypto-eor.mir` would fail.
---
 .../lib/Target/AArch64/AArch64MacroFusion.cpp | 19 +++++-
 .../CodeGen/AArch64/misched-fusion-aes.ll     |  1 +
 .../AArch64/misched-fusion-crypto-eor.mir     | 66 +++++++------------
 3 files changed, 43 insertions(+), 43 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64MacroFusion.cpp b/llvm/lib/Target/AArch64/AArch64MacroFusion.cpp
index 55214f3f749e4..b9153203c311d 100644
--- a/llvm/lib/Target/AArch64/AArch64MacroFusion.cpp
+++ b/llvm/lib/Target/AArch64/AArch64MacroFusion.cpp
@@ -118,6 +118,11 @@ static bool isArithmeticCbzPair(const MachineInstr *FirstMI,
 }
 
 /// AES crypto encoding or decoding.
+///
+/// ISel should always match RAW dependent AES pairs via tied pseudos,
+/// regardless of the program order, so matched pairs here should always have
+/// the same dest register (AESMCrrTied pre-RA, AESMCrr post-RA after pseudo
+/// expansion).
 static bool isAESPair(const MachineInstr *FirstMI,
                       const MachineInstr &SecondMI) {
   // Assume the 1st instr to be a wildcard if it is unspecified.
@@ -125,11 +130,21 @@ static bool isAESPair(const MachineInstr *FirstMI,
   // AES encode.
   case AArch64::AESMCrr:
   case AArch64::AESMCrrTied:
-    return FirstMI == nullptr || FirstMI->getOpcode() == AArch64::AESErr;
+    if (FirstMI == nullptr)
+      return true;
+    if (FirstMI->getOpcode() != AArch64::AESErr)
+      return false;
+    return SecondMI.getOpcode() == AArch64::AESMCrrTied ||
+           FirstMI->getOperand(0).getReg() == SecondMI.getOperand(0).getReg();
   // AES decode.
   case AArch64::AESIMCrr:
   case AArch64::AESIMCrrTied:
-    return FirstMI == nullptr || FirstMI->getOpcode() == AArch64::AESDrr;
+    if (FirstMI == nullptr)
+      return true;
+    if (FirstMI->getOpcode() != AArch64::AESDrr)
+      return false;
+    return SecondMI.getOpcode() == AArch64::AESIMCrrTied ||
+           FirstMI->getOperand(0).getReg() == SecondMI.getOperand(0).getReg();
   }
 
   return false;
diff --git a/llvm/test/CodeGen/AArch64/misched-fusion-aes.ll b/llvm/test/CodeGen/AArch64/misched-fusion-aes.ll
index dc6fa9128e933..cd1e46f187b09 100644
--- a/llvm/test/CodeGen/AArch64/misched-fusion-aes.ll
+++ b/llvm/test/CodeGen/AArch64/misched-fusion-aes.ll
@@ -20,6 +20,7 @@
 ; RUN: llc %s -o - -mtriple=aarch64-unknown -mcpu=ampere1    | FileCheck %s
 ; RUN: llc %s -o - -mtriple=aarch64-unknown -mcpu=ampere1a   | FileCheck %s
 ; RUN: llc %s -o - -mtriple=aarch64-unknown -mcpu=ampere1b   | FileCheck %s
+; RUN: llc %s -o - -mtriple=aarch64-unknown -mcpu=apple-m5   | FileCheck %s
 
 declare <16 x i8> @llvm.aarch64.crypto.aese(<16 x i8> %d, <16 x i8> %k)
 declare <16 x i8> @llvm.aarch64.crypto.aesmc(<16 x i8> %d)
diff --git a/llvm/test/CodeGen/AArch64/misched-fusion-crypto-eor.mir b/llvm/test/CodeGen/AArch64/misched-fusion-crypto-eor.mir
index e661353615726..9f78289af0c6e 100644
--- a/llvm/test/CodeGen/AArch64/misched-fusion-crypto-eor.mir
+++ b/llvm/test/CodeGen/AArch64/misched-fusion-crypto-eor.mir
@@ -17,62 +17,46 @@ body: |
     %0:fpr128 = AESErr undef $q0, undef $q1
     %1:fpr128 = AESMCrrTied %0
 
-    ; CHECK: SU(2): %2:fpr128 = AESErr undef $q2(tied-def 0), undef $q3
-    ; CHECK: Successors:
-    ; NOFUSE-NOT: SU({{.*}}): Ord
-    ; FUSEAES:    SU(3): Ord  Latency=0 Cluster
-    ; CHECK: SU(3): dead %3:fpr128 = AESMCrr %2:fpr128
-    %2:fpr128 = AESErr undef $q2, undef $q3
-    %3:fpr128 = AESMCrr %2
-
-    ; CHECK: SU(4): %4:fpr128 = AESErr %1:fpr128(tied-def 0), undef $q4
+    ; CHECK: SU(2): %2:fpr128 = AESErr %1:fpr128(tied-def 0), undef $q4
     ; CHECK: Successors:
     ; NOFUSE-NOT: SU({{.*}}): Ord
     ; FUSEAES-NOT: SU({{.*}}): Ord
-    ; FUSECRYPTO: SU(5): Ord  Latency=0 Cluster
-    ; CHECK: SU(5): dead %5:fpr128 = EORv16i8 %4:fpr128, undef $q5
-    %4:fpr128 = AESErr %1, undef $q4
-    %5:fpr128 = EORv16i8 %4, undef $q5
-
-    ; CHECK: SU(6): %6:fpr128 = AESDrr undef $q0(tied-def 0), undef $q1
-    ; CHECK: Successors:
-    ; NOFUSE-NOT: SU({{.*}}): Ord
-    ; FUSEAES:    SU(7): Ord  Latency=0 Cluster
-    ; CHECK: SU(7): %7:fpr128 = AESIMCrrTied %6:fpr128
-    %6:fpr128 = AESDrr undef $q0, undef $q1
-    %7:fpr128 = AESIMCrrTied %6
+    ; FUSECRYPTO: SU(3): Ord  Latency=0 Cluster
+    ; CHECK: SU(3): dead %3:fpr128 = EORv16i8 %2:fpr128, undef $q5
+    %2:fpr128 = AESErr %1, undef $q4
+    %3:fpr128 = EORv16i8 %2, undef $q5
 
-    ; CHECK: SU(8): %8:fpr128 = AESDrr undef $q2(tied-def 0), undef $q3
+    ; CHECK: SU(4): %4:fpr128 = AESDrr undef $q0(tied-def 0), undef $q1
     ; CHECK: Successors:
     ; NOFUSE-NOT: SU({{.*}}): Ord
-    ; FUSEAES:    SU(9): Ord  Latency=0 Cluster
-    ; CHECK: SU(9): dead %9:fpr128 = AESIMCrr %8:fpr128
-    %8:fpr128 = AESDrr undef $q2, undef $q3
-    %9:fpr128 = AESIMCrr %8
+    ; FUSEAES:    SU(5): Ord  Latency=0 Cluster
+    ; CHECK: SU(5): %5:fpr128 = AESIMCrrTied %4:fpr128
+    %4:fpr128 = AESDrr undef $q0, undef $q1
+    %5:fpr128 = AESIMCrrTied %4
 
-    ; CHECK: SU(10): %10:fpr128 = AESDrr %7:fpr128(tied-def 0), undef $q0
+    ; CHECK: SU(6): %6:fpr128 = AESDrr %5:fpr128(tied-def 0), undef $q0
     ; CHECK: Successors:
     ; NOFUSE-NOT: SU({{.*}}): Ord
     ; FUSEAES-NOT: SU({{.*}}): Ord
-    ; FUSECRYPTO: SU(11): Ord  Latency=0 Cluster
-    ; CHECK: SU(11): dead %11:fpr128 = EORv16i8 %10:fpr128, undef $q1
-    %10:fpr128 = AESDrr %7, undef $q0
-    %11:fpr128 = EORv16i8 %10, undef $q1
+    ; FUSECRYPTO: SU(7): Ord  Latency=0 Cluster
+    ; CHECK: SU(7): dead %7:fpr128 = EORv16i8 %6:fpr128, undef $q1
+    %6:fpr128 = AESDrr %5, undef $q0
+    %7:fpr128 = EORv16i8 %6, undef $q1
 
-    ; CHECK: SU(12): %12:fpr128 = PMULLv16i8 undef $q0, undef $q1
+    ; CHECK: SU(8): %8:fpr128 = PMULLv16i8 undef $q0, undef $q1
     ; CHECK: Successors:
     ; NOFUSE-NOT: SU({{.*}}): Ord
     ; FUSEAES-NOT: SU({{.*}}): Ord
-    ; FUSECRYPTO: SU(13): Ord  Latency=0 Cluster
-    ; CHECK: SU(13): dead %13:fpr128 = EORv16i8 %12:fpr128, undef $q2
-    %12:fpr128 = PMULLv16i8 undef $q0, undef $q1
-    %13:fpr128 = EORv16i8 %12, undef $q2
+    ; FUSECRYPTO: SU(9): Ord  Latency=0 Cluster
+    ; CHECK: SU(9): dead %9:fpr128 = EORv16i8 %8:fpr128, undef $q2
+    %8:fpr128 = PMULLv16i8 undef $q0, undef $q1
+    %9:fpr128 = EORv16i8 %8, undef $q2
 
-    ; CHECK: SU(14): %14:fpr128 = PMULLv8i8 undef $d0, undef $d1
+    ; CHECK: SU(10): %10:fpr128 = PMULLv8i8 undef $d0, undef $d1
     ; CHECK: Successors:
     ; NOFUSE-NOT: SU({{.*}}): Ord
     ; FUSEAES-NOT: SU({{.*}}): Ord
-    ; FUSECRYPTO: SU(15): Ord  Latency=0 Cluster
-    ; CHECK: SU(15): dead %15:fpr128 = EORv16i8 %14:fpr128, undef $q3
-    %14:fpr128 = PMULLv8i8 undef $d0, undef $d1
-    %15:fpr128 = EORv16i8 %14, undef $q3
+    ; FUSECRYPTO: SU(11): Ord  Latency=0 Cluster
+    ; CHECK: SU(11): dead %11:fpr128 = EORv16i8 %10:fpr128, undef $q3
+    %10:fpr128 = PMULLv8i8 undef $d0, undef $d1
+    %11:fpr128 = EORv16i8 %10, undef $q3



More information about the llvm-commits mailing list