diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp index 16f91bb50e717c..b5f2cd0e61d064 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 99d1a833a5d15f..f0d72c054b829d 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> ) + 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 ad22eed4508b22..0cc7aa693012b7 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: