[llvm] e93a9e0 - [SelectionDAG] Expand CLMUL of zero-extended all-ones operand via parallel prefix XOR (#207339)

via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 8 03:59:49 PDT 2026


Author: Jan Schultke
Date: 2026-09-08T10:59:44Z
New Revision: e93a9e0d9936dce610967bb9be3d42f0f3d1493a

URL: https://github.com/llvm/llvm-project/commit/e93a9e0d9936dce610967bb9be3d42f0f3d1493a
DIFF: https://github.com/llvm/llvm-project/commit/e93a9e0d9936dce610967bb9be3d42f0f3d1493a.diff

LOG: [SelectionDAG] Expand CLMUL of zero-extended all-ones operand via parallel prefix XOR (#207339)

Fixes #204870

Added: 
    

Modified: 
    llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
    llvm/test/CodeGen/X86/clmul-vector.ll
    llvm/test/CodeGen/X86/clmul.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
index 16f91bb50e717..b5f2cd0e61d06 100644
--- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
@@ -8962,16 +8962,29 @@ SDValue TargetLowering::expandCLMUL(SDNode *Node, SelectionDAG &DAG) const {
       }
     }
 
-    // Special case: clmul(X, ~0) is equivalent to a "parallel prefix XOR" or
-    // "bitwise parity" operation.
-    if (isAllOnesOrAllOnesSplat(Y)) {
-      SDValue R = X;
-      for (unsigned I = 1; I < BW; I <<= 1) {
-        SDValue ShAmt = DAG.getShiftAmountConstant(I, VT, DL);
-        SDValue Shifted = DAG.getNode(ISD::SHL, DL, VT, R, ShAmt);
-        R = DAG.getNode(ISD::XOR, DL, VT, R, Shifted);
-      }
-      return R;
+    // Special case: clmul(X, Y) where Y is a known constant (splat) that forms
+    // a contiguous block of trailing ones whose length N is a power of two
+    // (e.g. i8 0xFF, i8 0x0F, ...) or equal to the operand width. In this
+    // special case, clmul(X, Y) is equivalent to a "parallel prefix XOR" or
+    // "bitwise parity" operation on X.
+    //
+    // Note: This special currently dose NOT apply when the mask is neither a
+    // power of two nor equal to the operand width because the loop inside
+    // behaves as if the mask was bit-ceiled, and "undoing" the XOR with parts
+    // of that CLMUL is a recursive problem (e.g. CLMUL with a 20-bit mask
+    // requires correction XOR with CLMUL with 12-bit mask).
+    if (auto *C = isConstOrConstSplat(Y, /*AllowUndefs=*/true)) {
+      const APInt &YVal = C->getAPIntValue();
+      unsigned N = YVal.countr_one();
+      if (YVal.isAllOnes() || (YVal.isMask() && isPowerOf2_32(N))) {
+        SDValue R = X;
+        for (unsigned I = 1; I < N; I <<= 1) {
+          SDValue ShAmt = DAG.getShiftAmountConstant(I, VT, DL);
+          SDValue Shifted = DAG.getNode(ISD::SHL, DL, VT, R, ShAmt);
+          R = DAG.getNode(ISD::XOR, DL, VT, R, Shifted);
+        }
+        return R;
+      }
     }
 
     // NOTE: If you change this expansion, please update the cost model

diff  --git a/llvm/test/CodeGen/X86/clmul-vector.ll b/llvm/test/CodeGen/X86/clmul-vector.ll
index 99d1a833a5d15..f0d72c054b829 100644
--- a/llvm/test/CodeGen/X86/clmul-vector.ll
+++ b/llvm/test/CodeGen/X86/clmul-vector.ll
@@ -6457,6 +6457,113 @@ define <4 x i32> @clmul_v4i32_allones(<4 x i32> %x) nounwind {
   ret <4 x i32> %r
 }
 
+define <4 x i32> @clmul_v4i32_zext_allones(<4 x i16> %x) nounwind {
+; SSE2-NOPCLMUL-LABEL: clmul_v4i32_zext_allones:
+; SSE2-NOPCLMUL:       # %bb.0:
+; SSE2-NOPCLMUL-NEXT:    pxor %xmm1, %xmm1
+; SSE2-NOPCLMUL-NEXT:    punpcklwd {{.*#+}} xmm0 = xmm0[0],xmm1[0],xmm0[1],xmm1[1],xmm0[2],xmm1[2],xmm0[3],xmm1[3]
+; SSE2-NOPCLMUL-NEXT:    movdqa %xmm0, %xmm1
+; SSE2-NOPCLMUL-NEXT:    paddd %xmm1, %xmm1
+; SSE2-NOPCLMUL-NEXT:    pxor %xmm1, %xmm0
+; SSE2-NOPCLMUL-NEXT:    movdqa %xmm0, %xmm1
+; SSE2-NOPCLMUL-NEXT:    pslld $2, %xmm1
+; SSE2-NOPCLMUL-NEXT:    pxor %xmm1, %xmm0
+; SSE2-NOPCLMUL-NEXT:    movdqa %xmm0, %xmm1
+; SSE2-NOPCLMUL-NEXT:    pslld $4, %xmm1
+; SSE2-NOPCLMUL-NEXT:    pxor %xmm1, %xmm0
+; SSE2-NOPCLMUL-NEXT:    movdqa %xmm0, %xmm1
+; SSE2-NOPCLMUL-NEXT:    pslld $8, %xmm1
+; SSE2-NOPCLMUL-NEXT:    pxor %xmm1, %xmm0
+; SSE2-NOPCLMUL-NEXT:    retq
+;
+; SSE42-NOPCLMUL-LABEL: clmul_v4i32_zext_allones:
+; SSE42-NOPCLMUL:       # %bb.0:
+; SSE42-NOPCLMUL-NEXT:    pmovzxwd {{.*#+}} xmm0 = xmm0[0],zero,xmm0[1],zero,xmm0[2],zero,xmm0[3],zero
+; SSE42-NOPCLMUL-NEXT:    movdqa %xmm0, %xmm1
+; SSE42-NOPCLMUL-NEXT:    paddd %xmm1, %xmm1
+; SSE42-NOPCLMUL-NEXT:    pxor %xmm0, %xmm1
+; SSE42-NOPCLMUL-NEXT:    movdqa %xmm1, %xmm0
+; SSE42-NOPCLMUL-NEXT:    pslld $2, %xmm0
+; SSE42-NOPCLMUL-NEXT:    pxor %xmm1, %xmm0
+; SSE42-NOPCLMUL-NEXT:    movdqa %xmm0, %xmm1
+; SSE42-NOPCLMUL-NEXT:    pslld $4, %xmm1
+; SSE42-NOPCLMUL-NEXT:    pxor %xmm0, %xmm1
+; SSE42-NOPCLMUL-NEXT:    movdqa %xmm1, %xmm0
+; SSE42-NOPCLMUL-NEXT:    pslld $8, %xmm0
+; SSE42-NOPCLMUL-NEXT:    pxor %xmm1, %xmm0
+; SSE42-NOPCLMUL-NEXT:    retq
+;
+; SSE2-PCLMUL-LABEL: clmul_v4i32_zext_allones:
+; SSE2-PCLMUL:       # %bb.0:
+; SSE2-PCLMUL-NEXT:    pxor %xmm1, %xmm1
+; SSE2-PCLMUL-NEXT:    punpcklwd {{.*#+}} xmm0 = xmm0[0],xmm1[0],xmm0[1],xmm1[1],xmm0[2],xmm1[2],xmm0[3],xmm1[3]
+; SSE2-PCLMUL-NEXT:    movdqa {{.*#+}} xmm1 = [65535,65535,65535,65535]
+; SSE2-PCLMUL-NEXT:    movdqa %xmm0, %xmm2
+; SSE2-PCLMUL-NEXT:    pclmulqdq $17, %xmm1, %xmm2
+; SSE2-PCLMUL-NEXT:    movdqa %xmm0, %xmm3
+; SSE2-PCLMUL-NEXT:    psrlq $32, %xmm3
+; SSE2-PCLMUL-NEXT:    movdqa {{.*#+}} xmm4 = [255,255,0,0,0,0,0,0,255,255,0,0,0,0,0,0]
+; SSE2-PCLMUL-NEXT:    movdqa %xmm3, %xmm5
+; SSE2-PCLMUL-NEXT:    pclmulqdq $17, %xmm4, %xmm5
+; SSE2-PCLMUL-NEXT:    punpckldq {{.*#+}} xmm2 = xmm2[0],xmm5[0],xmm2[1],xmm5[1]
+; SSE2-PCLMUL-NEXT:    pclmulqdq $0, %xmm1, %xmm0
+; SSE2-PCLMUL-NEXT:    pclmulqdq $0, %xmm4, %xmm3
+; SSE2-PCLMUL-NEXT:    punpckldq {{.*#+}} xmm0 = xmm0[0],xmm3[0],xmm0[1],xmm3[1]
+; SSE2-PCLMUL-NEXT:    punpckldq {{.*#+}} xmm0 = xmm0[0],xmm2[0],xmm0[1],xmm2[1]
+; SSE2-PCLMUL-NEXT:    retq
+;
+; SSE42-PCLMUL-LABEL: clmul_v4i32_zext_allones:
+; SSE42-PCLMUL:       # %bb.0:
+; SSE42-PCLMUL-NEXT:    pmovzxwd {{.*#+}} xmm0 = xmm0[0],zero,xmm0[1],zero,xmm0[2],zero,xmm0[3],zero
+; SSE42-PCLMUL-NEXT:    pmovsxbw {{.*#+}} xmm1 = [65535,0,65535,0,65535,0,65535,0]
+; SSE42-PCLMUL-NEXT:    movdqa %xmm0, %xmm2
+; SSE42-PCLMUL-NEXT:    pclmulqdq $17, %xmm1, %xmm2
+; SSE42-PCLMUL-NEXT:    movdqa %xmm0, %xmm3
+; SSE42-PCLMUL-NEXT:    psrlq $32, %xmm3
+; SSE42-PCLMUL-NEXT:    pmovzxwq {{.*#+}} xmm4 = [65535,65535]
+; SSE42-PCLMUL-NEXT:    movdqa %xmm3, %xmm5
+; SSE42-PCLMUL-NEXT:    pclmulqdq $17, %xmm4, %xmm5
+; SSE42-PCLMUL-NEXT:    punpckldq {{.*#+}} xmm2 = xmm2[0],xmm5[0],xmm2[1],xmm5[1]
+; SSE42-PCLMUL-NEXT:    pclmulqdq $0, %xmm1, %xmm0
+; SSE42-PCLMUL-NEXT:    pclmulqdq $0, %xmm4, %xmm3
+; SSE42-PCLMUL-NEXT:    punpckldq {{.*#+}} xmm0 = xmm0[0],xmm3[0],xmm0[1],xmm3[1]
+; SSE42-PCLMUL-NEXT:    punpckldq {{.*#+}} xmm0 = xmm0[0],xmm2[0],xmm0[1],xmm2[1]
+; SSE42-PCLMUL-NEXT:    retq
+;
+; AVX2-LABEL: clmul_v4i32_zext_allones:
+; AVX2:       # %bb.0:
+; AVX2-NEXT:    vpmovzxwd {{.*#+}} xmm0 = xmm0[0],zero,xmm0[1],zero,xmm0[2],zero,xmm0[3],zero
+; AVX2-NEXT:    vpbroadcastd {{.*#+}} xmm1 = [65535,65535,65535,65535]
+; AVX2-NEXT:    vpclmulqdq $17, %xmm1, %xmm0, %xmm2
+; AVX2-NEXT:    vpsrlq $32, %xmm0, %xmm3
+; AVX2-NEXT:    vpbroadcastq {{.*#+}} xmm4 = [255,255,0,0,0,0,0,0,255,255,0,0,0,0,0,0]
+; AVX2-NEXT:    vpclmulqdq $17, %xmm4, %xmm3, %xmm5
+; AVX2-NEXT:    vpunpckldq {{.*#+}} xmm2 = xmm2[0],xmm5[0],xmm2[1],xmm5[1]
+; AVX2-NEXT:    vpclmulqdq $0, %xmm1, %xmm0, %xmm0
+; AVX2-NEXT:    vpclmulqdq $0, %xmm4, %xmm3, %xmm1
+; AVX2-NEXT:    vpunpckldq {{.*#+}} xmm0 = xmm0[0],xmm1[0],xmm0[1],xmm1[1]
+; AVX2-NEXT:    vpunpckldq {{.*#+}} xmm0 = xmm0[0],xmm2[0],xmm0[1],xmm2[1]
+; AVX2-NEXT:    retq
+;
+; AVX512-LABEL: clmul_v4i32_zext_allones:
+; AVX512:       # %bb.0:
+; AVX512-NEXT:    vpmovzxwd {{.*#+}} xmm0 = xmm0[0],zero,xmm0[1],zero,xmm0[2],zero,xmm0[3],zero
+; AVX512-NEXT:    vpbroadcastd {{.*#+}} xmm1 = [65535,65535,65535,65535]
+; AVX512-NEXT:    vpclmulqdq $17, %xmm1, %xmm0, %xmm2
+; AVX512-NEXT:    vpsrlq $32, %xmm0, %xmm3
+; AVX512-NEXT:    vpbroadcastq {{.*#+}} xmm4 = [255,255,0,0,0,0,0,0,255,255,0,0,0,0,0,0]
+; AVX512-NEXT:    vpclmulqdq $17, %xmm4, %xmm3, %xmm5
+; AVX512-NEXT:    vpunpckldq {{.*#+}} xmm2 = xmm2[0],xmm5[0],xmm2[1],xmm5[1]
+; AVX512-NEXT:    vpclmulqdq $0, %xmm1, %xmm0, %xmm0
+; AVX512-NEXT:    vpclmulqdq $0, %xmm4, %xmm3, %xmm1
+; AVX512-NEXT:    vpunpckldq {{.*#+}} xmm0 = xmm0[0],xmm1[0],xmm0[1],xmm1[1]
+; AVX512-NEXT:    vpunpckldq {{.*#+}} xmm0 = xmm0[0],xmm2[0],xmm0[1],xmm2[1]
+; AVX512-NEXT:    retq
+  %x32 = zext <4 x i16> %x to <4 x i32>
+  %r = call <4 x i32> @llvm.clmul.v4i32(<4 x i32> %x32, <4 x i32> <i32 65535, i32 65535, i32 65535, i32 65535>)
+  ret <4 x i32> %r
+}
+
 define <2 x i64> @clmul_v2i64_allones(<2 x i64> %x) nounwind {
 ; SSE-NOPCLMUL-LABEL: clmul_v2i64_allones:
 ; SSE-NOPCLMUL:       # %bb.0:

diff  --git a/llvm/test/CodeGen/X86/clmul.ll b/llvm/test/CodeGen/X86/clmul.ll
index ad22eed4508b2..0cc7aa693012b 100644
--- a/llvm/test/CodeGen/X86/clmul.ll
+++ b/llvm/test/CodeGen/X86/clmul.ll
@@ -3748,6 +3748,48 @@ define i32 @clmul_i32_allones(i32 %x) nounwind {
   ret i32 %r
 }
 
+define i32 @clmul_i32_zext_allones(i16 %x) nounwind {
+; SCALAR-LABEL: clmul_i32_zext_allones:
+; SCALAR:       # %bb.0:
+; SCALAR-NEXT:    movzwl %di, %eax
+; SCALAR-NEXT:    leal (%rax,%rax), %ecx
+; SCALAR-NEXT:    xorl %eax, %ecx
+; SCALAR-NEXT:    leal (,%rcx,4), %eax
+; SCALAR-NEXT:    xorl %ecx, %eax
+; SCALAR-NEXT:    movl %eax, %ecx
+; SCALAR-NEXT:    shll $4, %ecx
+; SCALAR-NEXT:    xorl %eax, %ecx
+; SCALAR-NEXT:    movl %ecx, %eax
+; SCALAR-NEXT:    shll $8, %eax
+; SCALAR-NEXT:    xorl %ecx, %eax
+; SCALAR-NEXT:    retq
+;
+; SSE-PCLMUL-LABEL: clmul_i32_zext_allones:
+; SSE-PCLMUL:       # %bb.0:
+; SSE-PCLMUL-NEXT:    movzwl %di, %eax
+; SSE-PCLMUL-NEXT:    movl $65535, %ecx # imm = 0xFFFF
+; SSE-PCLMUL-NEXT:    movq %rcx, %xmm0
+; SSE-PCLMUL-NEXT:    movd %eax, %xmm1
+; SSE-PCLMUL-NEXT:    pclmulqdq $0, %xmm0, %xmm1
+; SSE-PCLMUL-NEXT:    movq %xmm1, %rax
+; SSE-PCLMUL-NEXT:    # kill: def $eax killed $eax killed $rax
+; SSE-PCLMUL-NEXT:    retq
+;
+; AVX-LABEL: clmul_i32_zext_allones:
+; AVX:       # %bb.0:
+; AVX-NEXT:    movzwl %di, %eax
+; AVX-NEXT:    movl $65535, %ecx # imm = 0xFFFF
+; AVX-NEXT:    vmovq %rcx, %xmm0
+; AVX-NEXT:    vmovd %eax, %xmm1
+; AVX-NEXT:    vpclmulqdq $0, %xmm0, %xmm1, %xmm0
+; AVX-NEXT:    vmovq %xmm0, %rax
+; AVX-NEXT:    # kill: def $eax killed $eax killed $rax
+; AVX-NEXT:    retq
+  %x32 = zext i16 %x to i32
+  %r = call i32 @llvm.clmul.i32(i32 %x32, i32 65535)
+  ret i32 %r
+}
+
 define i64 @clmul_i64_allones(i64 %x) nounwind {
 ; SCALAR-LABEL: clmul_i64_allones:
 ; SCALAR:       # %bb.0:


        


More information about the llvm-commits mailing list