[llvm] [X86] Fold XOR of VGF2P8AFFINEQB and its source (PR #198448)

Simon Pilgrim via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 1 05:32:39 PDT 2026


https://github.com/RKSimon updated https://github.com/llvm/llvm-project/pull/198448

>From 68a6ab0c4be85ea1c391299810577dcfbc8051ef Mon Sep 17 00:00:00 2001
From: Walter <walter.kruger at hotmail.com>
Date: Mon, 18 May 2026 16:36:22 +1000
Subject: [PATCH 1/5] [NFC] Baseline tests without fold

Regression check line tests without the fold, demonstrating the current code gen.
---
 llvm/test/CodeGen/X86/gfni-xor-fold-avx512.ll |  57 ++++++++++
 llvm/test/CodeGen/X86/gfni-xor-fold.ll        | 105 ++++++++++++++++++
 2 files changed, 162 insertions(+)

diff --git a/llvm/test/CodeGen/X86/gfni-xor-fold-avx512.ll b/llvm/test/CodeGen/X86/gfni-xor-fold-avx512.ll
index 414a9a849b4cc..212e026b3c7c4 100644
--- a/llvm/test/CodeGen/X86/gfni-xor-fold-avx512.ll
+++ b/llvm/test/CodeGen/X86/gfni-xor-fold-avx512.ll
@@ -62,6 +62,63 @@ define <64 x i8> @test_affine_xor_no_fold_512_variable(<64 x i8> %src1, <64 x i8
   ret <64 x i8> %xor
 }
 
+define <64 x i8> @test_affine_src_xor_fold_512(<64 x i8> %src) nounwind {
+; CHECK-LABEL: test_affine_src_xor_fold_512:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip){1to8}, %zmm0, %zmm1 # [53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53]
+; CHECK-NEXT:    vpxorq %zmm0, %zmm1, %zmm0
+; CHECK-NEXT:    retq
+  %gfni = call <64 x i8> @llvm.x86.vgf2p8affineqb.512(<64 x i8> %src, <64 x i8> splat(i8 53), i8 0)
+  %xor = xor <64 x i8> %gfni, %src
+  ret <64 x i8> %xor
+}
+
+define <64 x i8> @test_affine_src_xor_fold_alternative_matrix(<64 x i8> %src) nounwind {
+; CHECK-LABEL: test_affine_src_xor_fold_alternative_matrix:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %zmm0, %zmm1 # [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63]
+; CHECK-NEXT:    vpxorq %zmm0, %zmm1, %zmm0
+; CHECK-NEXT:    retq
+  %gfni = call <64 x i8> @llvm.x86.vgf2p8affineqb.512(<64 x i8> %src, <64 x i8> <i8 0, i8 1, i8 2, i8 3, i8 4, i8 5, i8 6, i8 7, i8 8, i8 9, i8 10, i8 11, i8 12, i8 13, i8 14, i8 15, i8 16, i8 17, i8 18, i8 19, i8 20, i8 21, i8 22, i8 23, i8 24, i8 25, i8 26, i8 27, i8 28, i8 29, i8 30, i8 31, i8 32, i8 33, i8 34, i8 35, i8 36, i8 37, i8 38, i8 39, i8 40, i8 41, i8 42, i8 43, i8 44, i8 45, i8 46, i8 47, i8 48, i8 49, i8 50, i8 51, i8 52, i8 53, i8 54, i8 55, i8 56, i8 57, i8 58, i8 59, i8 60, i8 61, i8 62, i8 63>, i8 0)
+  %xor = xor <64 x i8> %gfni, %src
+  ret <64 x i8> %xor
+}
+
+define <64 x i8> @test_affine_src_xor_nonzero_imm(<64 x i8> %src) nounwind {
+; CHECK-LABEL: test_affine_src_xor_nonzero_imm:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vgf2p8affineqb $107, {{\.?LCPI[0-9]+_[0-9]+}}(%rip){1to8}, %zmm0, %zmm1 # [53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53]
+; CHECK-NEXT:    vpxorq %zmm0, %zmm1, %zmm0
+; CHECK-NEXT:    retq
+  %gfni = call <64 x i8> @llvm.x86.vgf2p8affineqb.512(<64 x i8> %src, <64 x i8> splat(i8 53), i8 107)
+  %xor = xor <64 x i8> %gfni, %src
+  ret <64 x i8> %xor
+}
+
+define <64 x i8> @test_affine_src_xor_no_fold_multi_use(<64 x i8> %src, ptr %sink) nounwind {
+; CHECK-LABEL: test_affine_src_xor_no_fold_multi_use:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip){1to8}, %zmm0, %zmm1 # [53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53]
+; CHECK-NEXT:    vmovdqa64 %zmm1, (%rdi)
+; CHECK-NEXT:    vpxorq %zmm0, %zmm1, %zmm0
+; CHECK-NEXT:    retq
+  %gfni = call <64 x i8> @llvm.x86.vgf2p8affineqb.512(<64 x i8> %src, <64 x i8> splat(i8 53), i8 0)
+  store <64 x i8> %gfni, ptr %sink
+  %xor = xor <64 x i8> %gfni, %src
+  ret <64 x i8> %xor
+}
+
+define <64 x i8> @test_affine_src_xor_no_fold_var_matrix(<64 x i8> %src, <64 x i8> %matrix) nounwind {
+; CHECK-LABEL: test_affine_src_xor_no_fold_var_matrix:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vgf2p8affineqb $0, %zmm1, %zmm0, %zmm1
+; CHECK-NEXT:    vpxorq %zmm0, %zmm1, %zmm0
+; CHECK-NEXT:    retq
+  %gfni = call <64 x i8> @llvm.x86.vgf2p8affineqb.512(<64 x i8> %src, <64 x i8> %matrix, i8 0)
+  %xor = xor <64 x i8> %gfni, %src
+  ret <64 x i8> %xor
+}
+
 ;; Test folding XOR of two vgf2p8affineqb with same input - 512-bit
 define <64 x i8> @test_affine_affine_xor_fold_512(<64 x i8> %src, <64 x i8> %m1, <64 x i8> %m2) nounwind {
 ;
diff --git a/llvm/test/CodeGen/X86/gfni-xor-fold.ll b/llvm/test/CodeGen/X86/gfni-xor-fold.ll
index e907410ae7bab..7fd54b14a073a 100644
--- a/llvm/test/CodeGen/X86/gfni-xor-fold.ll
+++ b/llvm/test/CodeGen/X86/gfni-xor-fold.ll
@@ -143,6 +143,111 @@ define <16 x i8> @test_affine_xor_no_fold_variable(<16 x i8> %src1, <16 x i8> %s
   ret <16 x i8> %xor
 }
 
+define <16 x i8> @test_affine_src_xor_fold_128(<16 x i8> %src) nounwind {
+; AVX-LABEL: test_affine_src_xor_fold_128:
+; AVX:       # %bb.0:
+; AVX-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0, %xmm1 # [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
+; AVX-NEXT:    vpxor %xmm0, %xmm1, %xmm0
+; AVX-NEXT:    retq
+;
+; AVX512-LABEL: test_affine_src_xor_fold_128:
+; AVX512:       # %bb.0:
+; AVX512-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0, %xmm1 # [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
+; AVX512-NEXT:    vpxor %xmm0, %xmm1, %xmm0
+; AVX512-NEXT:    retq
+  %gfni = call <16 x i8> @llvm.x86.vgf2p8affineqb.128(<16 x i8> %src, <16 x i8> <i8 1, i8 2, i8 3, i8 4, i8 5, i8 6, i8 7, i8 8, i8 9, i8 10, i8 11, i8 12, i8 13, i8 14, i8 15, i8 16>, i8 0)
+  %xor = xor <16 x i8> %gfni, %src
+  ret <16 x i8> %xor
+}
+
+define <32 x i8> @test_affine_src_xor_fold_256(<32 x i8> %src) nounwind {
+; AVX-LABEL: test_affine_src_xor_fold_256:
+; AVX:       # %bb.0:
+; AVX-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %ymm0, %ymm1 # [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32]
+; AVX-NEXT:    vxorps %ymm0, %ymm1, %ymm0
+; AVX-NEXT:    retq
+;
+; AVX512-LABEL: test_affine_src_xor_fold_256:
+; AVX512:       # %bb.0:
+; AVX512-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %ymm0, %ymm1 # [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32]
+; AVX512-NEXT:    vpxor %ymm0, %ymm1, %ymm0
+; AVX512-NEXT:    retq
+  %gfni = call <32 x i8> @llvm.x86.vgf2p8affineqb.256(<32 x i8> %src, <32 x i8> <i8 1, i8 2, i8 3, i8 4, i8 5, i8 6, i8 7, i8 8, i8 9, i8 10, i8 11, i8 12, i8 13, i8 14, i8 15, i8 16, i8 17, i8 18, i8 19, i8 20, i8 21, i8 22, i8 23, i8 24, i8 25, i8 26, i8 27, i8 28, i8 29, i8 30, i8 31, i8 32>, i8 0)
+  %xor = xor <32 x i8> %gfni, %src
+  ret <32 x i8> %xor
+}
+
+define <16 x i8> @test_affine_src_xor_fold_alternative_matrix(<16 x i8> %src) nounwind {
+; AVX-LABEL: test_affine_src_xor_fold_alternative_matrix:
+; AVX:       # %bb.0:
+; AVX-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0, %xmm1 # [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
+; AVX-NEXT:    vpxor %xmm0, %xmm1, %xmm0
+; AVX-NEXT:    retq
+;
+; AVX512-LABEL: test_affine_src_xor_fold_alternative_matrix:
+; AVX512:       # %bb.0:
+; AVX512-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip){1to2}, %xmm0, %xmm1 # [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
+; AVX512-NEXT:    vpxor %xmm0, %xmm1, %xmm0
+; AVX512-NEXT:    retq
+  %gfni = call <16 x i8> @llvm.x86.vgf2p8affineqb.128(<16 x i8> %src, <16 x i8> splat(i8 1), i8 0)
+  %xor = xor <16 x i8> %gfni, %src
+  ret <16 x i8> %xor
+}
+
+define <16 x i8> @test_affine_src_xor_nonzero_imm(<16 x i8> %src) nounwind {
+; AVX-LABEL: test_affine_src_xor_nonzero_imm:
+; AVX:       # %bb.0:
+; AVX-NEXT:    vgf2p8affineqb $107, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0, %xmm1 # [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
+; AVX-NEXT:    vpxor %xmm0, %xmm1, %xmm0
+; AVX-NEXT:    retq
+;
+; AVX512-LABEL: test_affine_src_xor_nonzero_imm:
+; AVX512:       # %bb.0:
+; AVX512-NEXT:    vgf2p8affineqb $107, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0, %xmm1 # [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
+; AVX512-NEXT:    vpxor %xmm0, %xmm1, %xmm0
+; AVX512-NEXT:    retq
+  %gfni = call <16 x i8> @llvm.x86.vgf2p8affineqb.128(<16 x i8> %src, <16 x i8> <i8 1, i8 2, i8 3, i8 4, i8 5, i8 6, i8 7, i8 8, i8 9, i8 10, i8 11, i8 12, i8 13, i8 14, i8 15, i8 16>, i8 107)
+  %xor = xor <16 x i8> %gfni, %src
+  ret <16 x i8> %xor
+}
+
+define <16 x i8> @test_affine_src_xor_no_fold_multi_use(<16 x i8> %src, ptr %sink) nounwind {
+; AVX-LABEL: test_affine_src_xor_no_fold_multi_use:
+; AVX:       # %bb.0:
+; AVX-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0, %xmm1 # [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
+; AVX-NEXT:    vmovdqa %xmm1, (%rdi)
+; AVX-NEXT:    vpxor %xmm0, %xmm1, %xmm0
+; AVX-NEXT:    retq
+;
+; AVX512-LABEL: test_affine_src_xor_no_fold_multi_use:
+; AVX512:       # %bb.0:
+; AVX512-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0, %xmm1 # [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
+; AVX512-NEXT:    vmovdqa %xmm1, (%rdi)
+; AVX512-NEXT:    vpxor %xmm0, %xmm1, %xmm0
+; AVX512-NEXT:    retq
+  %gfni = call <16 x i8> @llvm.x86.vgf2p8affineqb.128(<16 x i8> %src, <16 x i8> <i8 1, i8 2, i8 3, i8 4, i8 5, i8 6, i8 7, i8 8, i8 9, i8 10, i8 11, i8 12, i8 13, i8 14, i8 15, i8 16>, i8 0)
+  store <16 x i8> %gfni, ptr %sink
+  %xor = xor <16 x i8> %gfni, %src
+  ret <16 x i8> %xor
+}
+
+define <16 x i8> @test_affine_src_xor_no_fold_var_matrix(<16 x i8> %src, <16 x i8> %matrix) nounwind {
+; AVX-LABEL: test_affine_src_xor_no_fold_var_matrix:
+; AVX:       # %bb.0:
+; AVX-NEXT:    vgf2p8affineqb $0, %xmm1, %xmm0, %xmm1
+; AVX-NEXT:    vpxor %xmm0, %xmm1, %xmm0
+; AVX-NEXT:    retq
+;
+; AVX512-LABEL: test_affine_src_xor_no_fold_var_matrix:
+; AVX512:       # %bb.0:
+; AVX512-NEXT:    vgf2p8affineqb $0, %xmm1, %xmm0, %xmm1
+; AVX512-NEXT:    vpxor %xmm0, %xmm1, %xmm0
+; AVX512-NEXT:    retq
+  %gfni = call <16 x i8> @llvm.x86.vgf2p8affineqb.128(<16 x i8> %src, <16 x i8> %matrix, i8 0)
+  %xor = xor <16 x i8> %gfni, %src
+  ret <16 x i8> %xor
+}
+
 ;; Test folding XOR of two vgf2p8affineqb with same input - 128-bit
 define <16 x i8> @test_affine_affine_xor_fold_128(<16 x i8> %src, <16 x i8> %m1, <16 x i8> %m2) nounwind {
 ;

>From 19f89827a930e51d0cfeab322c4fb468b0dc1770 Mon Sep 17 00:00:00 2001
From: Walter <walter.kruger at hotmail.com>
Date: Tue, 19 May 2026 15:31:36 +1000
Subject: [PATCH 2/5] GFNI XOR fold + update tests

- Added fold when XORing VGF2P8AFFINEQB and its source when the matrix is a constant.
- Updated tests to reflect improved code gen.
---
 llvm/lib/Target/X86/X86ISelLowering.cpp       | 35 +++++++++++++++----
 llvm/test/CodeGen/X86/gfni-xor-fold-avx512.ll |  9 ++---
 llvm/test/CodeGen/X86/gfni-xor-fold.ll        | 24 +++++--------
 3 files changed, 40 insertions(+), 28 deletions(-)

diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index b6aee44b4de2e..0e7c6d6327059 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -56435,21 +56435,44 @@ static SDValue combineXorWithGF2P8AFFINEQB(SDNode *N, const SDLoc &DL,
                                            SelectionDAG &DAG, EVT VT) {
   using namespace SDPatternMatch;
 
-  SDValue X, Y, SplatOp;
-  APInt Imm;
-  // Use sd_match for structure matching - m_Xor handles commutation
+  unsigned NumElts = VT.getVectorNumElements();
+
+  SDValue X, Y, XorOp;
+  APInt Imm, ConstUndef;
+
   if (!sd_match(N, m_Xor(m_OneUse(m_TernaryOp(X86ISD::GF2P8AFFINEQB, m_Value(X),
                                               m_Value(Y), m_ConstInt(Imm))),
-                         m_Value(SplatOp))))
+                         m_Value(XorOp))))
     return SDValue();
 
   // GF2P8AFFINEQB only operates on i8 vector types
   assert((VT == MVT::v16i8 || VT == MVT::v32i8 || VT == MVT::v64i8) &&
          "Unsupported GFNI type");
 
-  // Use X86::isConstantSplat for robust splat constant extraction
+  // Fold: GF2P8AFFINEQB(x, M) ^ x
+  //   =>  GF2P8AFFINEQB(x, M ^ IDENTITY)
+  // The "identity" is a noop. It adds a XOR by the unmodified input.
+  SmallVector<APInt> YEltBits;
+  if (X == XorOp && getTargetConstantBitsFromNode(Y, 8, ConstUndef, YEltBits,
+                                                  /*AllowWholeUndefs=*/false)) {
+    static const uint8_t IdentityMatrtix[] = {128, 64, 32, 16, 8, 4, 2, 1};
+
+    SmallVector<SDValue> BuildMatrix;
+    for (unsigned I = 0; I != NumElts; ++I) {
+      APInt MatrixRow = YEltBits[I] ^ IdentityMatrtix[I % 8];
+      BuildMatrix.push_back(DAG.getConstant(MatrixRow, DL, MVT::i8));
+    }
+
+    SDValue NewMatrix = DAG.getBuildVector(VT, DL, BuildMatrix);
+
+    return DAG.getNode(X86ISD::GF2P8AFFINEQB, DL, VT, X, NewMatrix,
+                       DAG.getTargetConstant(Imm, DL, MVT::i8));
+  }
+
+  // Fold: GF2P8AFFINEQB(x, m, Imm) ^ Splat(C)
+  //   =>  GF2P8AFFINEQB(x, m, Imm ^ C)
   APInt SplatVal;
-  if (!X86::isConstantSplat(SplatOp, SplatVal, /*AllowPartialUndefs=*/false))
+  if (!X86::isConstantSplat(XorOp, SplatVal, /*AllowPartialUndefs=*/false))
     return SDValue();
 
   uint64_t NewImm = Imm.getZExtValue() ^ SplatVal.getZExtValue();
diff --git a/llvm/test/CodeGen/X86/gfni-xor-fold-avx512.ll b/llvm/test/CodeGen/X86/gfni-xor-fold-avx512.ll
index 212e026b3c7c4..4107f07741991 100644
--- a/llvm/test/CodeGen/X86/gfni-xor-fold-avx512.ll
+++ b/llvm/test/CodeGen/X86/gfni-xor-fold-avx512.ll
@@ -65,8 +65,7 @@ define <64 x i8> @test_affine_xor_no_fold_512_variable(<64 x i8> %src1, <64 x i8
 define <64 x i8> @test_affine_src_xor_fold_512(<64 x i8> %src) nounwind {
 ; CHECK-LABEL: test_affine_src_xor_fold_512:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip){1to8}, %zmm0, %zmm1 # [53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53]
-; CHECK-NEXT:    vpxorq %zmm0, %zmm1, %zmm0
+; CHECK-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip){1to8}, %zmm0, %zmm0 # [181,117,21,37,61,49,55,52,181,117,21,37,61,49,55,52,181,117,21,37,61,49,55,52,181,117,21,37,61,49,55,52,181,117,21,37,61,49,55,52,181,117,21,37,61,49,55,52,181,117,21,37,61,49,55,52,181,117,21,37,61,49,55,52]
 ; CHECK-NEXT:    retq
   %gfni = call <64 x i8> @llvm.x86.vgf2p8affineqb.512(<64 x i8> %src, <64 x i8> splat(i8 53), i8 0)
   %xor = xor <64 x i8> %gfni, %src
@@ -76,8 +75,7 @@ define <64 x i8> @test_affine_src_xor_fold_512(<64 x i8> %src) nounwind {
 define <64 x i8> @test_affine_src_xor_fold_alternative_matrix(<64 x i8> %src) nounwind {
 ; CHECK-LABEL: test_affine_src_xor_fold_alternative_matrix:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %zmm0, %zmm1 # [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63]
-; CHECK-NEXT:    vpxorq %zmm0, %zmm1, %zmm0
+; CHECK-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %zmm0, %zmm0 # [128,65,34,19,12,1,4,6,136,73,42,27,4,9,12,14,144,81,50,3,28,17,20,22,152,89,58,11,20,25,28,30,160,97,2,51,44,33,36,38,168,105,10,59,36,41,44,46,176,113,18,35,60,49,52,54,184,121,26,43,52,57,60,62]
 ; CHECK-NEXT:    retq
   %gfni = call <64 x i8> @llvm.x86.vgf2p8affineqb.512(<64 x i8> %src, <64 x i8> <i8 0, i8 1, i8 2, i8 3, i8 4, i8 5, i8 6, i8 7, i8 8, i8 9, i8 10, i8 11, i8 12, i8 13, i8 14, i8 15, i8 16, i8 17, i8 18, i8 19, i8 20, i8 21, i8 22, i8 23, i8 24, i8 25, i8 26, i8 27, i8 28, i8 29, i8 30, i8 31, i8 32, i8 33, i8 34, i8 35, i8 36, i8 37, i8 38, i8 39, i8 40, i8 41, i8 42, i8 43, i8 44, i8 45, i8 46, i8 47, i8 48, i8 49, i8 50, i8 51, i8 52, i8 53, i8 54, i8 55, i8 56, i8 57, i8 58, i8 59, i8 60, i8 61, i8 62, i8 63>, i8 0)
   %xor = xor <64 x i8> %gfni, %src
@@ -87,8 +85,7 @@ define <64 x i8> @test_affine_src_xor_fold_alternative_matrix(<64 x i8> %src) no
 define <64 x i8> @test_affine_src_xor_nonzero_imm(<64 x i8> %src) nounwind {
 ; CHECK-LABEL: test_affine_src_xor_nonzero_imm:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    vgf2p8affineqb $107, {{\.?LCPI[0-9]+_[0-9]+}}(%rip){1to8}, %zmm0, %zmm1 # [53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53,53]
-; CHECK-NEXT:    vpxorq %zmm0, %zmm1, %zmm0
+; CHECK-NEXT:    vgf2p8affineqb $107, {{\.?LCPI[0-9]+_[0-9]+}}(%rip){1to8}, %zmm0, %zmm0 # [181,117,21,37,61,49,55,52,181,117,21,37,61,49,55,52,181,117,21,37,61,49,55,52,181,117,21,37,61,49,55,52,181,117,21,37,61,49,55,52,181,117,21,37,61,49,55,52,181,117,21,37,61,49,55,52,181,117,21,37,61,49,55,52]
 ; CHECK-NEXT:    retq
   %gfni = call <64 x i8> @llvm.x86.vgf2p8affineqb.512(<64 x i8> %src, <64 x i8> splat(i8 53), i8 107)
   %xor = xor <64 x i8> %gfni, %src
diff --git a/llvm/test/CodeGen/X86/gfni-xor-fold.ll b/llvm/test/CodeGen/X86/gfni-xor-fold.ll
index 7fd54b14a073a..d7bd66377bc3d 100644
--- a/llvm/test/CodeGen/X86/gfni-xor-fold.ll
+++ b/llvm/test/CodeGen/X86/gfni-xor-fold.ll
@@ -146,14 +146,12 @@ define <16 x i8> @test_affine_xor_no_fold_variable(<16 x i8> %src1, <16 x i8> %s
 define <16 x i8> @test_affine_src_xor_fold_128(<16 x i8> %src) nounwind {
 ; AVX-LABEL: test_affine_src_xor_fold_128:
 ; AVX:       # %bb.0:
-; AVX-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0, %xmm1 # [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
-; AVX-NEXT:    vpxor %xmm0, %xmm1, %xmm0
+; AVX-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0, %xmm0 # [129,66,35,20,13,2,5,9,137,74,43,28,5,10,13,17]
 ; AVX-NEXT:    retq
 ;
 ; AVX512-LABEL: test_affine_src_xor_fold_128:
 ; AVX512:       # %bb.0:
-; AVX512-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0, %xmm1 # [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
-; AVX512-NEXT:    vpxor %xmm0, %xmm1, %xmm0
+; AVX512-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0, %xmm0 # [129,66,35,20,13,2,5,9,137,74,43,28,5,10,13,17]
 ; AVX512-NEXT:    retq
   %gfni = call <16 x i8> @llvm.x86.vgf2p8affineqb.128(<16 x i8> %src, <16 x i8> <i8 1, i8 2, i8 3, i8 4, i8 5, i8 6, i8 7, i8 8, i8 9, i8 10, i8 11, i8 12, i8 13, i8 14, i8 15, i8 16>, i8 0)
   %xor = xor <16 x i8> %gfni, %src
@@ -163,14 +161,12 @@ define <16 x i8> @test_affine_src_xor_fold_128(<16 x i8> %src) nounwind {
 define <32 x i8> @test_affine_src_xor_fold_256(<32 x i8> %src) nounwind {
 ; AVX-LABEL: test_affine_src_xor_fold_256:
 ; AVX:       # %bb.0:
-; AVX-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %ymm0, %ymm1 # [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32]
-; AVX-NEXT:    vxorps %ymm0, %ymm1, %ymm0
+; AVX-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %ymm0, %ymm0 # [129,66,35,20,13,2,5,9,137,74,43,28,5,10,13,17,145,82,51,4,29,18,21,25,153,90,59,12,21,26,29,33]
 ; AVX-NEXT:    retq
 ;
 ; AVX512-LABEL: test_affine_src_xor_fold_256:
 ; AVX512:       # %bb.0:
-; AVX512-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %ymm0, %ymm1 # [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32]
-; AVX512-NEXT:    vpxor %ymm0, %ymm1, %ymm0
+; AVX512-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %ymm0, %ymm0 # [129,66,35,20,13,2,5,9,137,74,43,28,5,10,13,17,145,82,51,4,29,18,21,25,153,90,59,12,21,26,29,33]
 ; AVX512-NEXT:    retq
   %gfni = call <32 x i8> @llvm.x86.vgf2p8affineqb.256(<32 x i8> %src, <32 x i8> <i8 1, i8 2, i8 3, i8 4, i8 5, i8 6, i8 7, i8 8, i8 9, i8 10, i8 11, i8 12, i8 13, i8 14, i8 15, i8 16, i8 17, i8 18, i8 19, i8 20, i8 21, i8 22, i8 23, i8 24, i8 25, i8 26, i8 27, i8 28, i8 29, i8 30, i8 31, i8 32>, i8 0)
   %xor = xor <32 x i8> %gfni, %src
@@ -180,14 +176,12 @@ define <32 x i8> @test_affine_src_xor_fold_256(<32 x i8> %src) nounwind {
 define <16 x i8> @test_affine_src_xor_fold_alternative_matrix(<16 x i8> %src) nounwind {
 ; AVX-LABEL: test_affine_src_xor_fold_alternative_matrix:
 ; AVX:       # %bb.0:
-; AVX-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0, %xmm1 # [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
-; AVX-NEXT:    vpxor %xmm0, %xmm1, %xmm0
+; AVX-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0, %xmm0 # [129,65,33,17,9,5,3,0,129,65,33,17,9,5,3,0]
 ; AVX-NEXT:    retq
 ;
 ; AVX512-LABEL: test_affine_src_xor_fold_alternative_matrix:
 ; AVX512:       # %bb.0:
-; AVX512-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip){1to2}, %xmm0, %xmm1 # [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
-; AVX512-NEXT:    vpxor %xmm0, %xmm1, %xmm0
+; AVX512-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip){1to2}, %xmm0, %xmm0 # [129,65,33,17,9,5,3,0,129,65,33,17,9,5,3,0]
 ; AVX512-NEXT:    retq
   %gfni = call <16 x i8> @llvm.x86.vgf2p8affineqb.128(<16 x i8> %src, <16 x i8> splat(i8 1), i8 0)
   %xor = xor <16 x i8> %gfni, %src
@@ -197,14 +191,12 @@ define <16 x i8> @test_affine_src_xor_fold_alternative_matrix(<16 x i8> %src) no
 define <16 x i8> @test_affine_src_xor_nonzero_imm(<16 x i8> %src) nounwind {
 ; AVX-LABEL: test_affine_src_xor_nonzero_imm:
 ; AVX:       # %bb.0:
-; AVX-NEXT:    vgf2p8affineqb $107, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0, %xmm1 # [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
-; AVX-NEXT:    vpxor %xmm0, %xmm1, %xmm0
+; AVX-NEXT:    vgf2p8affineqb $107, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0, %xmm0 # [129,66,35,20,13,2,5,9,137,74,43,28,5,10,13,17]
 ; AVX-NEXT:    retq
 ;
 ; AVX512-LABEL: test_affine_src_xor_nonzero_imm:
 ; AVX512:       # %bb.0:
-; AVX512-NEXT:    vgf2p8affineqb $107, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0, %xmm1 # [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
-; AVX512-NEXT:    vpxor %xmm0, %xmm1, %xmm0
+; AVX512-NEXT:    vgf2p8affineqb $107, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0, %xmm0 # [129,66,35,20,13,2,5,9,137,74,43,28,5,10,13,17]
 ; AVX512-NEXT:    retq
   %gfni = call <16 x i8> @llvm.x86.vgf2p8affineqb.128(<16 x i8> %src, <16 x i8> <i8 1, i8 2, i8 3, i8 4, i8 5, i8 6, i8 7, i8 8, i8 9, i8 10, i8 11, i8 12, i8 13, i8 14, i8 15, i8 16>, i8 107)
   %xor = xor <16 x i8> %gfni, %src

>From 647594981c6fda5735fc1c3294e03ffecebf8f47 Mon Sep 17 00:00:00 2001
From: Walter <walter.kruger at hotmail.com>
Date: Sat, 30 May 2026 10:04:28 +1000
Subject: [PATCH 3/5] Typo + Drop static

- Fixed the typo in `IdentityMatrtix` => `IdentityMatrix`
- Removed the static keyword from it as well.
---
 llvm/lib/Target/X86/X86ISelLowering.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 0e7c6d6327059..a1fafd536b237 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -56455,11 +56455,11 @@ static SDValue combineXorWithGF2P8AFFINEQB(SDNode *N, const SDLoc &DL,
   SmallVector<APInt> YEltBits;
   if (X == XorOp && getTargetConstantBitsFromNode(Y, 8, ConstUndef, YEltBits,
                                                   /*AllowWholeUndefs=*/false)) {
-    static const uint8_t IdentityMatrtix[] = {128, 64, 32, 16, 8, 4, 2, 1};
+    const uint8_t IdentityMatrix[] = {128, 64, 32, 16, 8, 4, 2, 1};
 
     SmallVector<SDValue> BuildMatrix;
     for (unsigned I = 0; I != NumElts; ++I) {
-      APInt MatrixRow = YEltBits[I] ^ IdentityMatrtix[I % 8];
+      APInt MatrixRow = YEltBits[I] ^ IdentityMatrix[I % 8];
       BuildMatrix.push_back(DAG.getConstant(MatrixRow, DL, MVT::i8));
     }
 

>From d9fcf3151c88324312092d0b9e8a88cabc772b0d Mon Sep 17 00:00:00 2001
From: Walter <walter.kruger at hotmail.com>
Date: Sat, 30 May 2026 12:56:34 +1000
Subject: [PATCH 4/5] Move `getNumElts` to after match guard

Moved `getVectorNumElements` to after `if sd_match` guard as the parent function is meant for generic XOR folds so it can assert when used with scalar types.
---
 llvm/lib/Target/X86/X86ISelLowering.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index a1fafd536b237..85f74d4f26445 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -56435,8 +56435,6 @@ static SDValue combineXorWithGF2P8AFFINEQB(SDNode *N, const SDLoc &DL,
                                            SelectionDAG &DAG, EVT VT) {
   using namespace SDPatternMatch;
 
-  unsigned NumElts = VT.getVectorNumElements();
-
   SDValue X, Y, XorOp;
   APInt Imm, ConstUndef;
 
@@ -56449,6 +56447,8 @@ static SDValue combineXorWithGF2P8AFFINEQB(SDNode *N, const SDLoc &DL,
   assert((VT == MVT::v16i8 || VT == MVT::v32i8 || VT == MVT::v64i8) &&
          "Unsupported GFNI type");
 
+  unsigned NumElts = VT.getVectorNumElements();
+
   // Fold: GF2P8AFFINEQB(x, M) ^ x
   //   =>  GF2P8AFFINEQB(x, M ^ IDENTITY)
   // The "identity" is a noop. It adds a XOR by the unmodified input.

>From 04891e51868b3537c3e5962bcd7e7dfd117e8683 Mon Sep 17 00:00:00 2001
From: Walter <walter.kruger at hotmail.com>
Date: Sat, 30 May 2026 14:38:54 +1000
Subject: [PATCH 5/5] Safer multi-use in `gfni-operand-and-fold.ll`

A (previously) upstream test used an XOR between the input to test multi-use. This was optimized away by this pull. I changed it to instead use a store, which also better matches the other existing, safer, multi-use test.
---
 llvm/test/CodeGen/X86/gfni-operand-and-fold.ll | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/llvm/test/CodeGen/X86/gfni-operand-and-fold.ll b/llvm/test/CodeGen/X86/gfni-operand-and-fold.ll
index a16771baaff7c..666b0015fd448 100644
--- a/llvm/test/CodeGen/X86/gfni-operand-and-fold.ll
+++ b/llvm/test/CodeGen/X86/gfni-operand-and-fold.ll
@@ -103,21 +103,21 @@ define <16 x i8> @test_const_splat_on_const_matrix_multi_use(<16 x i8> %src, ptr
 }
 
 ;; Negative test: multi use would generate an additional AND
-define <16 x i8> @test_var_splat_on_const_matrix_multi_use(<16 x i8> %src, i8 %scalar) nounwind {
+define <16 x i8> @test_var_splat_on_const_matrix_multi_use(<16 x i8> %src, i8 %scalar, ptr %sink) nounwind {
 ; CHECK-LABEL: test_var_splat_on_const_matrix_multi_use:
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    vmovd %edi, %xmm1
 ; CHECK-NEXT:    vpbroadcastb %xmm1, %xmm1
-; CHECK-NEXT:    vpand %xmm1, %xmm0, %xmm0
-; CHECK-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm0, %xmm1 # [64,32,16,8,4,2,1,128,64,32,16,8,4,2,1,128]
-; CHECK-NEXT:    vpxor %xmm0, %xmm1, %xmm0
+; CHECK-NEXT:    vpand %xmm1, %xmm0, %xmm1
+; CHECK-NEXT:    vgf2p8affineqb $0, {{\.?LCPI[0-9]+_[0-9]+}}(%rip), %xmm1, %xmm0 # [64,32,16,8,4,2,1,128,64,32,16,8,4,2,1,128]
+; CHECK-NEXT:    vmovdqa %xmm1, (%rsi)
 ; CHECK-NEXT:    retq
   %inlo = insertelement <16 x i8> poison, i8 %scalar, i64 0
   %varsplat = shufflevector <16 x i8> %inlo, <16 x i8> poison, <16 x i32> zeroinitializer
   %and = and <16 x i8> %src, %varsplat
   %gfni = call <16 x i8> @llvm.x86.vgf2p8affineqb.128(<16 x i8> %and, <16 x i8> <i8 64, i8 32, i8 16, i8 8, i8 4, i8 2, i8 1, i8 128, i8 64, i8 32, i8 16, i8 8, i8 4, i8 2, i8 1, i8 128>, i8 0)
-  %xor = xor <16 x i8> %gfni, %and
-  ret <16 x i8> %xor
+  store <16 x i8> %and, ptr %sink
+  ret <16 x i8> %gfni
 }
 
 ;; Negative test: don't fold as it may increase dependency chain



More information about the llvm-commits mailing list