[llvm] [X86] Fold XOR of two VGF2P8AFFINEQB instructions with same matrix (PR #199146)

Simon Pilgrim via llvm-commits llvm-commits at lists.llvm.org
Sun Jun 14 07:37:49 PDT 2026


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

>From 0b89351f7e8be6d68ae413e64c9b7b12d4d2841c Mon Sep 17 00:00:00 2001
From: Walter <walter.kruger at hotmail.com>
Date: Fri, 22 May 2026 11:09:17 +1000
Subject: [PATCH 1/3] [NFC] Tests without fold

Added regression test coverage to reflect current codegen without the fold.
---
 llvm/test/CodeGen/X86/gfni-xor-fold-avx512.ll |  58 +++++++--
 llvm/test/CodeGen/X86/gfni-xor-fold.ll        | 114 +++++++++++++++---
 2 files changed, 149 insertions(+), 23 deletions(-)

diff --git a/llvm/test/CodeGen/X86/gfni-xor-fold-avx512.ll b/llvm/test/CodeGen/X86/gfni-xor-fold-avx512.ll
index 414a9a849b4cc..b809589792196 100644
--- a/llvm/test/CodeGen/X86/gfni-xor-fold-avx512.ll
+++ b/llvm/test/CodeGen/X86/gfni-xor-fold-avx512.ll
@@ -63,9 +63,9 @@ define <64 x i8> @test_affine_xor_no_fold_512_variable(<64 x i8> %src1, <64 x i8
 }
 
 ;; 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 {
+define <64 x i8> @test_affine_affine_xor_same_source_fold_512(<64 x i8> %src, <64 x i8> %m1, <64 x i8> %m2) nounwind {
 ;
-; CHECK-LABEL: test_affine_affine_xor_fold_512:
+; CHECK-LABEL: test_affine_affine_xor_same_source_fold_512:
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    vpxorq %zmm2, %zmm1, %zmm1
 ; CHECK-NEXT:    vgf2p8affineqb $89, %zmm1, %zmm0, %zmm0
@@ -77,9 +77,9 @@ define <64 x i8> @test_affine_affine_xor_fold_512(<64 x i8> %src, <64 x i8> %m1,
 }
 
 ;; Test with non-zero immediates - 512-bit
-define <64 x i8> @test_affine_affine_xor_fold_512_nonzero(<64 x i8> %src, <64 x i8> %m1, <64 x i8> %m2) nounwind {
+define <64 x i8> @test_affine_affine_xor_same_source_fold_512_nonzero(<64 x i8> %src, <64 x i8> %m1, <64 x i8> %m2) nounwind {
 ;
-; CHECK-LABEL: test_affine_affine_xor_fold_512_nonzero:
+; CHECK-LABEL: test_affine_affine_xor_same_source_fold_512_nonzero:
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    vpxorq %zmm2, %zmm1, %zmm1
 ; CHECK-NEXT:    vgf2p8affineqb $15, %zmm1, %zmm0, %zmm0
@@ -91,9 +91,9 @@ define <64 x i8> @test_affine_affine_xor_fold_512_nonzero(<64 x i8> %src, <64 x
 }
 
 ;; Test commutative XOR - 512-bit
-define <64 x i8> @test_affine_affine_xor_fold_512_commutative(<64 x i8> %src, <64 x i8> %m1, <64 x i8> %m2) nounwind {
+define <64 x i8> @test_affine_affine_xor_same_source_fold_512_commutative(<64 x i8> %src, <64 x i8> %m1, <64 x i8> %m2) nounwind {
 ;
-; CHECK-LABEL: test_affine_affine_xor_fold_512_commutative:
+; CHECK-LABEL: test_affine_affine_xor_same_source_fold_512_commutative:
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    vpxorq %zmm1, %zmm2, %zmm1
 ; CHECK-NEXT:    vgf2p8affineqb $166, %zmm1, %zmm0, %zmm0
@@ -105,9 +105,9 @@ define <64 x i8> @test_affine_affine_xor_fold_512_commutative(<64 x i8> %src, <6
 }
 
 ;; Negative test: multi-use should not fold - 512-bit
-define <64 x i8> @test_affine_affine_xor_no_fold_512_multi_use(<64 x i8> %src, <64 x i8> %m1, <64 x i8> %m2, ptr %out) nounwind {
+define <64 x i8> @test_affine_affine_xor_same_source_no_fold_512_multi_use(<64 x i8> %src, <64 x i8> %m1, <64 x i8> %m2, ptr %out) nounwind {
 ;
-; CHECK-LABEL: test_affine_affine_xor_no_fold_512_multi_use:
+; CHECK-LABEL: test_affine_affine_xor_same_source_no_fold_512_multi_use:
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    vgf2p8affineqb $23, %zmm1, %zmm0, %zmm1
 ; CHECK-NEXT:    vgf2p8affineqb $200, %zmm2, %zmm0, %zmm0
@@ -121,6 +121,48 @@ define <64 x i8> @test_affine_affine_xor_no_fold_512_multi_use(<64 x i8> %src, <
   ret <64 x i8> %xor
 }
 
+;; Test folding XOR of two vgf2p8affineqb with same matrix
+define <64 x i8> @test_affine_affine_same_matrix_fold_512(<64 x i8> %src1, <64 x i8> %src2, <64 x i8> %m) nounwind {
+; CHECK-LABEL: test_affine_affine_same_matrix_fold_512:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vgf2p8affineqb $0, %zmm2, %zmm0, %zmm0
+; CHECK-NEXT:    vgf2p8affineqb $0, %zmm2, %zmm1, %zmm1
+; CHECK-NEXT:    vpxorq %zmm1, %zmm0, %zmm0
+; CHECK-NEXT:    retq
+  %gfni1 = call <64 x i8> @llvm.x86.vgf2p8affineqb.512(<64 x i8> %src1, <64 x i8> %m, i8 0)
+  %gfni2 = call <64 x i8> @llvm.x86.vgf2p8affineqb.512(<64 x i8> %src2, <64 x i8> %m, i8 0)
+  %xor = xor <64 x i8> %gfni1, %gfni2
+  ret <64 x i8> %xor
+}
+
+define <64 x i8> @test_affine_affine_same_matrix_fold_512_immediate(<64 x i8> %src1, <64 x i8> %src2, <64 x i8> %m) nounwind {
+; CHECK-LABEL: test_affine_affine_same_matrix_fold_512_immediate:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vgf2p8affineqb $183, %zmm2, %zmm0, %zmm0
+; CHECK-NEXT:    vgf2p8affineqb $53, %zmm2, %zmm1, %zmm1
+; CHECK-NEXT:    vpxorq %zmm1, %zmm0, %zmm0
+; CHECK-NEXT:    retq
+  %gfni1 = call <64 x i8> @llvm.x86.vgf2p8affineqb.512(<64 x i8> %src1, <64 x i8> %m, i8 183)
+  %gfni2 = call <64 x i8> @llvm.x86.vgf2p8affineqb.512(<64 x i8> %src2, <64 x i8> %m, i8 53)
+  %xor = xor <64 x i8> %gfni1, %gfni2
+  ret <64 x i8> %xor
+}
+
+define <64 x i8> @test_affine_affine_same_matrix_no_fold_512_multi_use(<64 x i8> %src1, <64 x i8> %src2, <64 x i8> %m, ptr %sink) nounwind {
+; CHECK-LABEL: test_affine_affine_same_matrix_no_fold_512_multi_use:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vgf2p8affineqb $23, %zmm2, %zmm0, %zmm0
+; CHECK-NEXT:    vgf2p8affineqb $200, %zmm2, %zmm1, %zmm1
+; CHECK-NEXT:    vmovdqa64 %zmm0, (%rdi)
+; CHECK-NEXT:    vpxorq %zmm1, %zmm0, %zmm0
+; CHECK-NEXT:    retq
+  %gfni1 = call <64 x i8> @llvm.x86.vgf2p8affineqb.512(<64 x i8> %src1, <64 x i8> %m, i8 23)
+  %gfni2 = call <64 x i8> @llvm.x86.vgf2p8affineqb.512(<64 x i8> %src2, <64 x i8> %m, i8 200)
+  store <64 x i8> %gfni1, ptr %sink
+  %xor = xor <64 x i8> %gfni1, %gfni2
+  ret <64 x i8> %xor
+}
+
 ;; Negative test: different inputs should not fold - 512-bit
 define <64 x i8> @test_affine_affine_xor_no_fold_512_different_inputs(<64 x i8> %src1, <64 x i8> %src2, <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..e8153ae2c47a1 100644
--- a/llvm/test/CodeGen/X86/gfni-xor-fold.ll
+++ b/llvm/test/CodeGen/X86/gfni-xor-fold.ll
@@ -144,15 +144,15 @@ define <16 x i8> @test_affine_xor_no_fold_variable(<16 x i8> %src1, <16 x i8> %s
 }
 
 ;; 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 {
+define <16 x i8> @test_affine_affine_xor_same_source_fold_128(<16 x i8> %src, <16 x i8> %m1, <16 x i8> %m2) nounwind {
 ;
-; AVX-LABEL: test_affine_affine_xor_fold_128:
+; AVX-LABEL: test_affine_affine_xor_same_source_fold_128:
 ; AVX:       # %bb.0:
 ; AVX-NEXT:    vpxor %xmm2, %xmm1, %xmm1
 ; AVX-NEXT:    vgf2p8affineqb $89, %xmm1, %xmm0, %xmm0
 ; AVX-NEXT:    retq
 ;
-; AVX512-LABEL: test_affine_affine_xor_fold_128:
+; AVX512-LABEL: test_affine_affine_xor_same_source_fold_128:
 ; AVX512:       # %bb.0:
 ; AVX512-NEXT:    vpxor %xmm2, %xmm1, %xmm1
 ; AVX512-NEXT:    vgf2p8affineqb $89, %xmm1, %xmm0, %xmm0
@@ -164,15 +164,15 @@ define <16 x i8> @test_affine_affine_xor_fold_128(<16 x i8> %src, <16 x i8> %m1,
 }
 
 ;; Test with non-zero immediates - 128-bit
-define <16 x i8> @test_affine_affine_xor_fold_128_nonzero(<16 x i8> %src, <16 x i8> %m1, <16 x i8> %m2) nounwind {
+define <16 x i8> @test_affine_affine_xor_same_source_fold_128_nonzero(<16 x i8> %src, <16 x i8> %m1, <16 x i8> %m2) nounwind {
 ;
-; AVX-LABEL: test_affine_affine_xor_fold_128_nonzero:
+; AVX-LABEL: test_affine_affine_xor_same_source_fold_128_nonzero:
 ; AVX:       # %bb.0:
 ; AVX-NEXT:    vpxor %xmm2, %xmm1, %xmm1
 ; AVX-NEXT:    vgf2p8affineqb $15, %xmm1, %xmm0, %xmm0
 ; AVX-NEXT:    retq
 ;
-; AVX512-LABEL: test_affine_affine_xor_fold_128_nonzero:
+; AVX512-LABEL: test_affine_affine_xor_same_source_fold_128_nonzero:
 ; AVX512:       # %bb.0:
 ; AVX512-NEXT:    vpxor %xmm2, %xmm1, %xmm1
 ; AVX512-NEXT:    vgf2p8affineqb $15, %xmm1, %xmm0, %xmm0
@@ -184,15 +184,15 @@ define <16 x i8> @test_affine_affine_xor_fold_128_nonzero(<16 x i8> %src, <16 x
 }
 
 ;; Test commutative XOR - 128-bit
-define <16 x i8> @test_affine_affine_xor_fold_128_commutative(<16 x i8> %src, <16 x i8> %m1, <16 x i8> %m2) nounwind {
+define <16 x i8> @test_affine_affine_xor_same_source_fold_128_commutative(<16 x i8> %src, <16 x i8> %m1, <16 x i8> %m2) nounwind {
 ;
-; AVX-LABEL: test_affine_affine_xor_fold_128_commutative:
+; AVX-LABEL: test_affine_affine_xor_same_source_fold_128_commutative:
 ; AVX:       # %bb.0:
 ; AVX-NEXT:    vpxor %xmm1, %xmm2, %xmm1
 ; AVX-NEXT:    vgf2p8affineqb $166, %xmm1, %xmm0, %xmm0
 ; AVX-NEXT:    retq
 ;
-; AVX512-LABEL: test_affine_affine_xor_fold_128_commutative:
+; AVX512-LABEL: test_affine_affine_xor_same_source_fold_128_commutative:
 ; AVX512:       # %bb.0:
 ; AVX512-NEXT:    vpxor %xmm1, %xmm2, %xmm1
 ; AVX512-NEXT:    vgf2p8affineqb $166, %xmm1, %xmm0, %xmm0
@@ -204,9 +204,9 @@ define <16 x i8> @test_affine_affine_xor_fold_128_commutative(<16 x i8> %src, <1
 }
 
 ;; Negative test: multi-use should not fold - 128-bit
-define <16 x i8> @test_affine_affine_xor_no_fold_multi_use(<16 x i8> %src, <16 x i8> %m1, <16 x i8> %m2, ptr %out) nounwind {
+define <16 x i8> @test_affine_affine_xor_same_source_no_fold_multi_use(<16 x i8> %src, <16 x i8> %m1, <16 x i8> %m2, ptr %out) nounwind {
 ;
-; AVX-LABEL: test_affine_affine_xor_no_fold_multi_use:
+; AVX-LABEL: test_affine_affine_xor_same_source_no_fold_multi_use:
 ; AVX:       # %bb.0:
 ; AVX-NEXT:    vgf2p8affineqb $23, %xmm1, %xmm0, %xmm1
 ; AVX-NEXT:    vgf2p8affineqb $200, %xmm2, %xmm0, %xmm0
@@ -214,7 +214,7 @@ define <16 x i8> @test_affine_affine_xor_no_fold_multi_use(<16 x i8> %src, <16 x
 ; AVX-NEXT:    vpxor %xmm0, %xmm1, %xmm0
 ; AVX-NEXT:    retq
 ;
-; AVX512-LABEL: test_affine_affine_xor_no_fold_multi_use:
+; AVX512-LABEL: test_affine_affine_xor_same_source_no_fold_multi_use:
 ; AVX512:       # %bb.0:
 ; AVX512-NEXT:    vgf2p8affineqb $23, %xmm1, %xmm0, %xmm1
 ; AVX512-NEXT:    vgf2p8affineqb $200, %xmm2, %xmm0, %xmm0
@@ -228,6 +228,70 @@ define <16 x i8> @test_affine_affine_xor_no_fold_multi_use(<16 x i8> %src, <16 x
   ret <16 x i8> %xor
 }
 
+;; Test folding XOR of two vgf2p8affineqb with same matrix
+define <16 x i8> @test_affine_affine_same_matrix_fold_128(<16 x i8> %src1, <16 x i8> %src2, <16 x i8> %m) nounwind {
+; AVX-LABEL: test_affine_affine_same_matrix_fold_128:
+; AVX:       # %bb.0:
+; AVX-NEXT:    vgf2p8affineqb $0, %xmm2, %xmm0, %xmm0
+; AVX-NEXT:    vgf2p8affineqb $0, %xmm2, %xmm1, %xmm1
+; AVX-NEXT:    vpxor %xmm1, %xmm0, %xmm0
+; AVX-NEXT:    retq
+;
+; AVX512-LABEL: test_affine_affine_same_matrix_fold_128:
+; AVX512:       # %bb.0:
+; AVX512-NEXT:    vgf2p8affineqb $0, %xmm2, %xmm0, %xmm0
+; AVX512-NEXT:    vgf2p8affineqb $0, %xmm2, %xmm1, %xmm1
+; AVX512-NEXT:    vpxor %xmm1, %xmm0, %xmm0
+; AVX512-NEXT:    retq
+  %gfni1 = call <16 x i8> @llvm.x86.vgf2p8affineqb.128(<16 x i8> %src1, <16 x i8> %m, i8 0)
+  %gfni2 = call <16 x i8> @llvm.x86.vgf2p8affineqb.128(<16 x i8> %src2, <16 x i8> %m, i8 0)
+  %xor = xor <16 x i8> %gfni1, %gfni2
+  ret <16 x i8> %xor
+}
+
+define <16 x i8> @test_affine_affine_same_matrix_fold_immediate(<16 x i8> %src1, <16 x i8> %src2, <16 x i8> %m) nounwind {
+; AVX-LABEL: test_affine_affine_same_matrix_fold_immediate:
+; AVX:       # %bb.0:
+; AVX-NEXT:    vgf2p8affineqb $183, %xmm2, %xmm0, %xmm0
+; AVX-NEXT:    vgf2p8affineqb $53, %xmm2, %xmm1, %xmm1
+; AVX-NEXT:    vpxor %xmm1, %xmm0, %xmm0
+; AVX-NEXT:    retq
+;
+; AVX512-LABEL: test_affine_affine_same_matrix_fold_immediate:
+; AVX512:       # %bb.0:
+; AVX512-NEXT:    vgf2p8affineqb $183, %xmm2, %xmm0, %xmm0
+; AVX512-NEXT:    vgf2p8affineqb $53, %xmm2, %xmm1, %xmm1
+; AVX512-NEXT:    vpxor %xmm1, %xmm0, %xmm0
+; AVX512-NEXT:    retq
+  %gfni1 = call <16 x i8> @llvm.x86.vgf2p8affineqb.128(<16 x i8> %src1, <16 x i8> %m, i8 183)
+  %gfni2 = call <16 x i8> @llvm.x86.vgf2p8affineqb.128(<16 x i8> %src2, <16 x i8> %m, i8 53)
+  %xor = xor <16 x i8> %gfni1, %gfni2
+  ret <16 x i8> %xor
+}
+
+define <16 x i8> @test_affine_affine_same_matrix_no_fold_multi_use(<16 x i8> %src1, <16 x i8> %src2, <16 x i8> %m, ptr %sink) nounwind {
+; AVX-LABEL: test_affine_affine_same_matrix_no_fold_multi_use:
+; AVX:       # %bb.0:
+; AVX-NEXT:    vgf2p8affineqb $23, %xmm2, %xmm0, %xmm0
+; AVX-NEXT:    vgf2p8affineqb $200, %xmm2, %xmm1, %xmm1
+; AVX-NEXT:    vmovdqa %xmm0, (%rdi)
+; AVX-NEXT:    vpxor %xmm1, %xmm0, %xmm0
+; AVX-NEXT:    retq
+;
+; AVX512-LABEL: test_affine_affine_same_matrix_no_fold_multi_use:
+; AVX512:       # %bb.0:
+; AVX512-NEXT:    vgf2p8affineqb $23, %xmm2, %xmm0, %xmm0
+; AVX512-NEXT:    vgf2p8affineqb $200, %xmm2, %xmm1, %xmm1
+; AVX512-NEXT:    vmovdqa %xmm0, (%rdi)
+; AVX512-NEXT:    vpxor %xmm1, %xmm0, %xmm0
+; AVX512-NEXT:    retq
+  %gfni1 = call <16 x i8> @llvm.x86.vgf2p8affineqb.128(<16 x i8> %src1, <16 x i8> %m, i8 23)
+  %gfni2 = call <16 x i8> @llvm.x86.vgf2p8affineqb.128(<16 x i8> %src2, <16 x i8> %m, i8 200)
+  store <16 x i8> %gfni1, ptr %sink
+  %xor = xor <16 x i8> %gfni1, %gfni2
+  ret <16 x i8> %xor
+}
+
 ;; Negative test: different inputs should not fold - 128-bit
 define <16 x i8> @test_affine_affine_xor_no_fold_different_inputs(<16 x i8> %src1, <16 x i8> %src2, <16 x i8> %m1, <16 x i8> %m2) nounwind {
 ;
@@ -251,15 +315,15 @@ define <16 x i8> @test_affine_affine_xor_no_fold_different_inputs(<16 x i8> %src
 }
 
 ;; Test 256-bit vectors
-define <32 x i8> @test_affine_affine_xor_fold_256(<32 x i8> %src, <32 x i8> %m1, <32 x i8> %m2) nounwind {
+define <32 x i8> @test_affine_affine_xor_same_source_fold_256(<32 x i8> %src, <32 x i8> %m1, <32 x i8> %m2) nounwind {
 ;
-; AVX-LABEL: test_affine_affine_xor_fold_256:
+; AVX-LABEL: test_affine_affine_xor_same_source_fold_256:
 ; AVX:       # %bb.0:
 ; AVX-NEXT:    vxorps %ymm2, %ymm1, %ymm1
 ; AVX-NEXT:    vgf2p8affineqb $89, %ymm1, %ymm0, %ymm0
 ; AVX-NEXT:    retq
 ;
-; AVX512-LABEL: test_affine_affine_xor_fold_256:
+; AVX512-LABEL: test_affine_affine_xor_same_source_fold_256:
 ; AVX512:       # %bb.0:
 ; AVX512-NEXT:    vpxor %ymm2, %ymm1, %ymm1
 ; AVX512-NEXT:    vgf2p8affineqb $89, %ymm1, %ymm0, %ymm0
@@ -269,3 +333,23 @@ define <32 x i8> @test_affine_affine_xor_fold_256(<32 x i8> %src, <32 x i8> %m1,
   %xor = xor <32 x i8> %gfni1, %gfni2
   ret <32 x i8> %xor
 }
+
+define <32 x i8> @test_affine_affine_same_matrix_fold_256(<32 x i8> %src1, <32 x i8> %src2, <32 x i8> %m) nounwind {
+; AVX-LABEL: test_affine_affine_same_matrix_fold_256:
+; AVX:       # %bb.0:
+; AVX-NEXT:    vgf2p8affineqb $42, %ymm2, %ymm0, %ymm0
+; AVX-NEXT:    vgf2p8affineqb $115, %ymm2, %ymm1, %ymm1
+; AVX-NEXT:    vxorps %ymm1, %ymm0, %ymm0
+; AVX-NEXT:    retq
+;
+; AVX512-LABEL: test_affine_affine_same_matrix_fold_256:
+; AVX512:       # %bb.0:
+; AVX512-NEXT:    vgf2p8affineqb $42, %ymm2, %ymm0, %ymm0
+; AVX512-NEXT:    vgf2p8affineqb $115, %ymm2, %ymm1, %ymm1
+; AVX512-NEXT:    vpxor %ymm1, %ymm0, %ymm0
+; AVX512-NEXT:    retq
+  %gfni1 = call <32 x i8> @llvm.x86.vgf2p8affineqb.256(<32 x i8> %src1, <32 x i8> %m, i8 42)
+  %gfni2 = call <32 x i8> @llvm.x86.vgf2p8affineqb.256(<32 x i8> %src2, <32 x i8> %m, i8 115)
+  %xor = xor <32 x i8> %gfni1, %gfni2
+  ret <32 x i8> %xor
+}

>From ff5512491fc9c9d81fb052eba0395d19a31f396a Mon Sep 17 00:00:00 2001
From: Walter <walter.kruger at hotmail.com>
Date: Fri, 22 May 2026 11:17:32 +1000
Subject: [PATCH 2/3] XOR GFNI fold + Update tests

Added the fold between two gf2p8affineqb instructions that have the same matrix. Also updated tests to reflect the improved codegen.
---
 llvm/lib/Target/X86/X86ISelLowering.cpp       | 41 +++++++++++++------
 llvm/test/CodeGen/X86/gfni-xor-fold-avx512.ll |  6 +--
 llvm/test/CodeGen/X86/gfni-xor-fold.ll        | 18 +++-----
 3 files changed, 37 insertions(+), 28 deletions(-)

diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 6694441468bb4..ea3fb80609dd9 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -56352,29 +56352,46 @@ static SDValue combineXorWithGF2P8AFFINEQB(SDNode *N, const SDLoc &DL,
                      DAG.getTargetConstant(NewImm, DL, MVT::i8));
 }
 
-// Fold: vgf2p8affineqb(x, m1, i1) ^ vgf2p8affineqb(x, m2, i2)
-//   =>  vgf2p8affineqb(x, m1 ^ m2, i1 ^ i2)
-// The matrix in vgf2p8affineqb determines which bits of the input are XORed
-// together. XORing two affine transformations of the same input can be folded
-// by XORing both their matrices and immediates together.
+// Given that vgf2p8affineqb performs a XOR permutation, two affines that share
+// a operand can be reassociated through a standalone XOR.
 static SDValue combineXorWithTwoGF2P8AFFINEQB(SDNode *N, const SDLoc &DL,
                                               SelectionDAG &DAG, EVT VT) {
   using namespace SDPatternMatch;
 
-  SDValue X0, Y0, Y1;
+  SDValue X0, X1, Y0, Y1;
   APInt Imm0, Imm1;
   // Use sd_match for structure matching - m_Xor handles commutation
-  // Match: GF2P8AFFINEQB(x, m1, i1) ^ GF2P8AFFINEQB(x, m2, i2)
-  if (!sd_match(
-          N, m_Xor(m_OneUse(m_TernaryOp(X86ISD::GF2P8AFFINEQB, m_Value(X0),
-                                        m_Value(Y0), m_ConstInt(Imm0))),
-                   m_OneUse(m_TernaryOp(X86ISD::GF2P8AFFINEQB, m_Deferred(X0),
-                                        m_Value(Y1), m_ConstInt(Imm1))))))
+  if (!sd_match(N,
+                m_Xor(m_OneUse(m_TernaryOp(X86ISD::GF2P8AFFINEQB, m_Value(X0),
+                                           m_Value(Y0), m_ConstInt(Imm0))),
+                      m_OneUse(m_TernaryOp(X86ISD::GF2P8AFFINEQB, m_Value(X1),
+                                           m_Value(Y1), m_ConstInt(Imm1))))))
     return SDValue();
 
   assert((VT == MVT::v16i8 || VT == MVT::v32i8 || VT == MVT::v64i8) &&
          "Unsupported GFNI type");
 
+  // Fold: GF2P8AFFINEQB(x0, m, i1) ^ GF2P8AFFINEQB(x1, m, i2)
+  //   =>  GF2P8AFFINEQB(x0 ^ x1, m, i1 ^ i2)
+  // This instruction performs an XOR permutation of the input, which is
+  // associative. Therefore XORing before permuting is equivalent.
+  if (Y0 == Y1) {
+    uint64_t NewImm = Imm0.getZExtValue() ^ Imm1.getZExtValue();
+
+    SDValue NewSrc = DAG.getNode(ISD::XOR, DL, VT, X0, X1);
+
+    return DAG.getNode(X86ISD::GF2P8AFFINEQB, DL, VT, NewSrc, Y0,
+                       DAG.getTargetConstant(NewImm, DL, MVT::i8));
+  }
+
+  // Fold: vgf2p8affineqb(x, m1, i1) ^ vgf2p8affineqb(x, m2, i2)
+  //   =>  vgf2p8affineqb(x, m1 ^ m2, i1 ^ i2)
+  // The matrix in vgf2p8affineqb determines which bits of the input are XORed
+  // together. XORing two affine transformations of the same input can be folded
+  // by XORing both their matrices and immediates together.
+  if (X0 != X1)
+    return SDValue();
+
   uint64_t NewImm = Imm0.getZExtValue() ^ Imm1.getZExtValue();
 
   SDValue NewMatrix = DAG.getNode(ISD::XOR, DL, VT, Y0, Y1);
diff --git a/llvm/test/CodeGen/X86/gfni-xor-fold-avx512.ll b/llvm/test/CodeGen/X86/gfni-xor-fold-avx512.ll
index b809589792196..b2194c115b0d8 100644
--- a/llvm/test/CodeGen/X86/gfni-xor-fold-avx512.ll
+++ b/llvm/test/CodeGen/X86/gfni-xor-fold-avx512.ll
@@ -125,9 +125,8 @@ define <64 x i8> @test_affine_affine_xor_same_source_no_fold_512_multi_use(<64 x
 define <64 x i8> @test_affine_affine_same_matrix_fold_512(<64 x i8> %src1, <64 x i8> %src2, <64 x i8> %m) nounwind {
 ; CHECK-LABEL: test_affine_affine_same_matrix_fold_512:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    vgf2p8affineqb $0, %zmm2, %zmm0, %zmm0
-; CHECK-NEXT:    vgf2p8affineqb $0, %zmm2, %zmm1, %zmm1
 ; CHECK-NEXT:    vpxorq %zmm1, %zmm0, %zmm0
+; CHECK-NEXT:    vgf2p8affineqb $0, %zmm2, %zmm0, %zmm0
 ; CHECK-NEXT:    retq
   %gfni1 = call <64 x i8> @llvm.x86.vgf2p8affineqb.512(<64 x i8> %src1, <64 x i8> %m, i8 0)
   %gfni2 = call <64 x i8> @llvm.x86.vgf2p8affineqb.512(<64 x i8> %src2, <64 x i8> %m, i8 0)
@@ -138,9 +137,8 @@ define <64 x i8> @test_affine_affine_same_matrix_fold_512(<64 x i8> %src1, <64 x
 define <64 x i8> @test_affine_affine_same_matrix_fold_512_immediate(<64 x i8> %src1, <64 x i8> %src2, <64 x i8> %m) nounwind {
 ; CHECK-LABEL: test_affine_affine_same_matrix_fold_512_immediate:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    vgf2p8affineqb $183, %zmm2, %zmm0, %zmm0
-; CHECK-NEXT:    vgf2p8affineqb $53, %zmm2, %zmm1, %zmm1
 ; CHECK-NEXT:    vpxorq %zmm1, %zmm0, %zmm0
+; CHECK-NEXT:    vgf2p8affineqb $130, %zmm2, %zmm0, %zmm0
 ; CHECK-NEXT:    retq
   %gfni1 = call <64 x i8> @llvm.x86.vgf2p8affineqb.512(<64 x i8> %src1, <64 x i8> %m, i8 183)
   %gfni2 = call <64 x i8> @llvm.x86.vgf2p8affineqb.512(<64 x i8> %src2, <64 x i8> %m, i8 53)
diff --git a/llvm/test/CodeGen/X86/gfni-xor-fold.ll b/llvm/test/CodeGen/X86/gfni-xor-fold.ll
index e8153ae2c47a1..8620ef0a38cde 100644
--- a/llvm/test/CodeGen/X86/gfni-xor-fold.ll
+++ b/llvm/test/CodeGen/X86/gfni-xor-fold.ll
@@ -232,16 +232,14 @@ define <16 x i8> @test_affine_affine_xor_same_source_no_fold_multi_use(<16 x i8>
 define <16 x i8> @test_affine_affine_same_matrix_fold_128(<16 x i8> %src1, <16 x i8> %src2, <16 x i8> %m) nounwind {
 ; AVX-LABEL: test_affine_affine_same_matrix_fold_128:
 ; AVX:       # %bb.0:
-; AVX-NEXT:    vgf2p8affineqb $0, %xmm2, %xmm0, %xmm0
-; AVX-NEXT:    vgf2p8affineqb $0, %xmm2, %xmm1, %xmm1
 ; AVX-NEXT:    vpxor %xmm1, %xmm0, %xmm0
+; AVX-NEXT:    vgf2p8affineqb $0, %xmm2, %xmm0, %xmm0
 ; AVX-NEXT:    retq
 ;
 ; AVX512-LABEL: test_affine_affine_same_matrix_fold_128:
 ; AVX512:       # %bb.0:
-; AVX512-NEXT:    vgf2p8affineqb $0, %xmm2, %xmm0, %xmm0
-; AVX512-NEXT:    vgf2p8affineqb $0, %xmm2, %xmm1, %xmm1
 ; AVX512-NEXT:    vpxor %xmm1, %xmm0, %xmm0
+; AVX512-NEXT:    vgf2p8affineqb $0, %xmm2, %xmm0, %xmm0
 ; AVX512-NEXT:    retq
   %gfni1 = call <16 x i8> @llvm.x86.vgf2p8affineqb.128(<16 x i8> %src1, <16 x i8> %m, i8 0)
   %gfni2 = call <16 x i8> @llvm.x86.vgf2p8affineqb.128(<16 x i8> %src2, <16 x i8> %m, i8 0)
@@ -252,16 +250,14 @@ define <16 x i8> @test_affine_affine_same_matrix_fold_128(<16 x i8> %src1, <16 x
 define <16 x i8> @test_affine_affine_same_matrix_fold_immediate(<16 x i8> %src1, <16 x i8> %src2, <16 x i8> %m) nounwind {
 ; AVX-LABEL: test_affine_affine_same_matrix_fold_immediate:
 ; AVX:       # %bb.0:
-; AVX-NEXT:    vgf2p8affineqb $183, %xmm2, %xmm0, %xmm0
-; AVX-NEXT:    vgf2p8affineqb $53, %xmm2, %xmm1, %xmm1
 ; AVX-NEXT:    vpxor %xmm1, %xmm0, %xmm0
+; AVX-NEXT:    vgf2p8affineqb $130, %xmm2, %xmm0, %xmm0
 ; AVX-NEXT:    retq
 ;
 ; AVX512-LABEL: test_affine_affine_same_matrix_fold_immediate:
 ; AVX512:       # %bb.0:
-; AVX512-NEXT:    vgf2p8affineqb $183, %xmm2, %xmm0, %xmm0
-; AVX512-NEXT:    vgf2p8affineqb $53, %xmm2, %xmm1, %xmm1
 ; AVX512-NEXT:    vpxor %xmm1, %xmm0, %xmm0
+; AVX512-NEXT:    vgf2p8affineqb $130, %xmm2, %xmm0, %xmm0
 ; AVX512-NEXT:    retq
   %gfni1 = call <16 x i8> @llvm.x86.vgf2p8affineqb.128(<16 x i8> %src1, <16 x i8> %m, i8 183)
   %gfni2 = call <16 x i8> @llvm.x86.vgf2p8affineqb.128(<16 x i8> %src2, <16 x i8> %m, i8 53)
@@ -337,16 +333,14 @@ define <32 x i8> @test_affine_affine_xor_same_source_fold_256(<32 x i8> %src, <3
 define <32 x i8> @test_affine_affine_same_matrix_fold_256(<32 x i8> %src1, <32 x i8> %src2, <32 x i8> %m) nounwind {
 ; AVX-LABEL: test_affine_affine_same_matrix_fold_256:
 ; AVX:       # %bb.0:
-; AVX-NEXT:    vgf2p8affineqb $42, %ymm2, %ymm0, %ymm0
-; AVX-NEXT:    vgf2p8affineqb $115, %ymm2, %ymm1, %ymm1
 ; AVX-NEXT:    vxorps %ymm1, %ymm0, %ymm0
+; AVX-NEXT:    vgf2p8affineqb $89, %ymm2, %ymm0, %ymm0
 ; AVX-NEXT:    retq
 ;
 ; AVX512-LABEL: test_affine_affine_same_matrix_fold_256:
 ; AVX512:       # %bb.0:
-; AVX512-NEXT:    vgf2p8affineqb $42, %ymm2, %ymm0, %ymm0
-; AVX512-NEXT:    vgf2p8affineqb $115, %ymm2, %ymm1, %ymm1
 ; AVX512-NEXT:    vpxor %ymm1, %ymm0, %ymm0
+; AVX512-NEXT:    vgf2p8affineqb $89, %ymm2, %ymm0, %ymm0
 ; AVX512-NEXT:    retq
   %gfni1 = call <32 x i8> @llvm.x86.vgf2p8affineqb.256(<32 x i8> %src1, <32 x i8> %m, i8 42)
   %gfni2 = call <32 x i8> @llvm.x86.vgf2p8affineqb.256(<32 x i8> %src2, <32 x i8> %m, i8 115)

>From 430132468a07a96fdbae10084b5278524023e166 Mon Sep 17 00:00:00 2001
From: Walter <walter.kruger at hotmail.com>
Date: Sun, 14 Jun 2026 11:37:55 +1000
Subject: [PATCH 3/3] Rename matrix variables

Renamed the matrix SDValue variables `Y => M` to make it consistent with the comments. Also renamed one comment to start from zero.
---
 llvm/lib/Target/X86/X86ISelLowering.cpp | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index ea3fb80609dd9..48640af67b39f 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -56358,14 +56358,14 @@ static SDValue combineXorWithTwoGF2P8AFFINEQB(SDNode *N, const SDLoc &DL,
                                               SelectionDAG &DAG, EVT VT) {
   using namespace SDPatternMatch;
 
-  SDValue X0, X1, Y0, Y1;
+  SDValue X0, X1, M0, M1;
   APInt Imm0, Imm1;
   // Use sd_match for structure matching - m_Xor handles commutation
   if (!sd_match(N,
                 m_Xor(m_OneUse(m_TernaryOp(X86ISD::GF2P8AFFINEQB, m_Value(X0),
-                                           m_Value(Y0), m_ConstInt(Imm0))),
+                                           m_Value(M0), m_ConstInt(Imm0))),
                       m_OneUse(m_TernaryOp(X86ISD::GF2P8AFFINEQB, m_Value(X1),
-                                           m_Value(Y1), m_ConstInt(Imm1))))))
+                                           m_Value(M1), m_ConstInt(Imm1))))))
     return SDValue();
 
   assert((VT == MVT::v16i8 || VT == MVT::v32i8 || VT == MVT::v64i8) &&
@@ -56375,17 +56375,17 @@ static SDValue combineXorWithTwoGF2P8AFFINEQB(SDNode *N, const SDLoc &DL,
   //   =>  GF2P8AFFINEQB(x0 ^ x1, m, i1 ^ i2)
   // This instruction performs an XOR permutation of the input, which is
   // associative. Therefore XORing before permuting is equivalent.
-  if (Y0 == Y1) {
+  if (M0 == M1) {
     uint64_t NewImm = Imm0.getZExtValue() ^ Imm1.getZExtValue();
 
     SDValue NewSrc = DAG.getNode(ISD::XOR, DL, VT, X0, X1);
 
-    return DAG.getNode(X86ISD::GF2P8AFFINEQB, DL, VT, NewSrc, Y0,
+    return DAG.getNode(X86ISD::GF2P8AFFINEQB, DL, VT, NewSrc, M0,
                        DAG.getTargetConstant(NewImm, DL, MVT::i8));
   }
 
-  // Fold: vgf2p8affineqb(x, m1, i1) ^ vgf2p8affineqb(x, m2, i2)
-  //   =>  vgf2p8affineqb(x, m1 ^ m2, i1 ^ i2)
+  // Fold: vgf2p8affineqb(x, m0, i1) ^ vgf2p8affineqb(x, m1, i2)
+  //   =>  vgf2p8affineqb(x, m0 ^ m1, i1 ^ i2)
   // The matrix in vgf2p8affineqb determines which bits of the input are XORed
   // together. XORing two affine transformations of the same input can be folded
   // by XORing both their matrices and immediates together.
@@ -56394,7 +56394,7 @@ static SDValue combineXorWithTwoGF2P8AFFINEQB(SDNode *N, const SDLoc &DL,
 
   uint64_t NewImm = Imm0.getZExtValue() ^ Imm1.getZExtValue();
 
-  SDValue NewMatrix = DAG.getNode(ISD::XOR, DL, VT, Y0, Y1);
+  SDValue NewMatrix = DAG.getNode(ISD::XOR, DL, VT, M0, M1);
 
   return DAG.getNode(X86ISD::GF2P8AFFINEQB, DL, VT, X0, NewMatrix,
                      DAG.getTargetConstant(NewImm, DL, MVT::i8));



More information about the llvm-commits mailing list