[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