xref: /linux/lib/raid/xor/x86/xor-avx512.c (revision dd8845db6d05e81532110c271d5f1cef3975916a)
1*c0a44d17SEric Biggers // SPDX-License-Identifier: GPL-2.0-or-later
2*c0a44d17SEric Biggers /*
3*c0a44d17SEric Biggers  * AVX-512 optimized implementation of xor_gen()
4*c0a44d17SEric Biggers  *
5*c0a44d17SEric Biggers  * Copyright 2026 Google LLC
6*c0a44d17SEric Biggers  */
7*c0a44d17SEric Biggers 
8*c0a44d17SEric Biggers #include <linux/types.h>
9*c0a44d17SEric Biggers #include <asm/fpu/api.h>
10*c0a44d17SEric Biggers #include "xor_impl.h"
11*c0a44d17SEric Biggers #include "xor_arch.h"
12*c0a44d17SEric Biggers 
13*c0a44d17SEric Biggers /*
14*c0a44d17SEric Biggers  * Implementation notes:
15*c0a44d17SEric Biggers  *
16*c0a44d17SEric Biggers  * Unrolling by the number of buffers (2-5) is very important.
17*c0a44d17SEric Biggers  *
18*c0a44d17SEric Biggers  * Unrolling by length is less important, especially when using register-indexed
19*c0a44d17SEric Biggers  * addressing with negative indices from the end of the buffers.  That approach
20*c0a44d17SEric Biggers  * results in just two loop control instructions being needed per iteration,
21*c0a44d17SEric Biggers  * regardless of the number of buffers.
22*c0a44d17SEric Biggers  *
23*c0a44d17SEric Biggers  * In fact, benchmarks showed that the 2 and 3 buffer cases require only 2x
24*c0a44d17SEric Biggers  * unrolling by length, while the 4 and 5 buffer cases don't require any
25*c0a44d17SEric Biggers  * unrolling by length.  Benchmarks also showed that the register-indexed
26*c0a44d17SEric Biggers  * addressing isn't a bottleneck either; i.e., we can't do any better by
27*c0a44d17SEric Biggers  * incrementing the pointers as we go along, even with more unrolling.
28*c0a44d17SEric Biggers  */
29*c0a44d17SEric Biggers 
xor_avx512_2(long bytes,u8 * p1,const u8 * p2)30*c0a44d17SEric Biggers static void xor_avx512_2(long bytes, u8 *p1, const u8 *p2)
31*c0a44d17SEric Biggers {
32*c0a44d17SEric Biggers 	long i = -bytes;
33*c0a44d17SEric Biggers 
34*c0a44d17SEric Biggers 	asm volatile("1: vmovdqa64 (%1,%0), %%zmm0\n"
35*c0a44d17SEric Biggers 		     "vmovdqa64 64(%1,%0), %%zmm1\n"
36*c0a44d17SEric Biggers 		     "vpxorq (%2,%0), %%zmm0, %%zmm0\n"
37*c0a44d17SEric Biggers 		     "vpxorq 64(%2,%0), %%zmm1, %%zmm1\n"
38*c0a44d17SEric Biggers 		     "vmovdqa64 %%zmm0, (%1,%0)\n"
39*c0a44d17SEric Biggers 		     "vmovdqa64 %%zmm1, 64(%1,%0)\n"
40*c0a44d17SEric Biggers 		     "add $128, %0\n"
41*c0a44d17SEric Biggers 		     "jnz 1b\n"
42*c0a44d17SEric Biggers 		     : "+&r"(i)
43*c0a44d17SEric Biggers 		     : "r"(p1 + bytes), "r"(p2 + bytes)
44*c0a44d17SEric Biggers 		     : "memory", "cc");
45*c0a44d17SEric Biggers }
46*c0a44d17SEric Biggers 
xor_avx512_3(long bytes,u8 * p1,const u8 * p2,const u8 * p3)47*c0a44d17SEric Biggers static void xor_avx512_3(long bytes, u8 *p1, const u8 *p2, const u8 *p3)
48*c0a44d17SEric Biggers {
49*c0a44d17SEric Biggers 	long i = -bytes;
50*c0a44d17SEric Biggers 
51*c0a44d17SEric Biggers 	asm volatile("1: vmovdqa64 (%1,%0), %%zmm0\n"
52*c0a44d17SEric Biggers 		     "vmovdqa64 64(%1,%0), %%zmm1\n"
53*c0a44d17SEric Biggers 		     "vmovdqa64 (%2,%0), %%zmm2\n"
54*c0a44d17SEric Biggers 		     "vmovdqa64 64(%2,%0), %%zmm3\n"
55*c0a44d17SEric Biggers 		     "vpternlogq $0x96, (%3,%0), %%zmm2, %%zmm0\n"
56*c0a44d17SEric Biggers 		     "vpternlogq $0x96, 64(%3,%0), %%zmm3, %%zmm1\n"
57*c0a44d17SEric Biggers 		     "vmovdqa64 %%zmm0, (%1,%0)\n"
58*c0a44d17SEric Biggers 		     "vmovdqa64 %%zmm1, 64(%1,%0)\n"
59*c0a44d17SEric Biggers 		     "add $128, %0\n"
60*c0a44d17SEric Biggers 		     "jnz 1b\n"
61*c0a44d17SEric Biggers 		     : "+&r"(i)
62*c0a44d17SEric Biggers 		     : "r"(p1 + bytes), "r"(p2 + bytes), "r"(p3 + bytes)
63*c0a44d17SEric Biggers 		     : "memory", "cc");
64*c0a44d17SEric Biggers }
65*c0a44d17SEric Biggers 
xor_avx512_4(long bytes,u8 * p1,const u8 * p2,const u8 * p3,const u8 * p4)66*c0a44d17SEric Biggers static void xor_avx512_4(long bytes, u8 *p1, const u8 *p2, const u8 *p3,
67*c0a44d17SEric Biggers 			 const u8 *p4)
68*c0a44d17SEric Biggers {
69*c0a44d17SEric Biggers 	long i = -bytes;
70*c0a44d17SEric Biggers 
71*c0a44d17SEric Biggers 	asm volatile("1: vmovdqa64 (%1,%0), %%zmm0\n"
72*c0a44d17SEric Biggers 		     "vmovdqa64 (%2,%0), %%zmm1\n"
73*c0a44d17SEric Biggers 		     "vpxorq (%3,%0), %%zmm0, %%zmm0\n"
74*c0a44d17SEric Biggers 		     "vpternlogq $0x96, (%4,%0), %%zmm1, %%zmm0\n"
75*c0a44d17SEric Biggers 		     "vmovdqa64 %%zmm0, (%1,%0)\n"
76*c0a44d17SEric Biggers 		     "add $64, %0\n"
77*c0a44d17SEric Biggers 		     "jnz 1b\n"
78*c0a44d17SEric Biggers 		     : "+&r"(i)
79*c0a44d17SEric Biggers 		     : "r"(p1 + bytes), "r"(p2 + bytes), "r"(p3 + bytes),
80*c0a44d17SEric Biggers 		       "r"(p4 + bytes)
81*c0a44d17SEric Biggers 		     : "memory", "cc");
82*c0a44d17SEric Biggers }
83*c0a44d17SEric Biggers 
xor_avx512_5(long bytes,u8 * p1,const u8 * p2,const u8 * p3,const u8 * p4,const u8 * p5)84*c0a44d17SEric Biggers static void xor_avx512_5(long bytes, u8 *p1, const u8 *p2, const u8 *p3,
85*c0a44d17SEric Biggers 			 const u8 *p4, const u8 *p5)
86*c0a44d17SEric Biggers {
87*c0a44d17SEric Biggers 	long i = -bytes;
88*c0a44d17SEric Biggers 
89*c0a44d17SEric Biggers 	asm volatile("1: vmovdqa64 (%1,%0), %%zmm0\n"
90*c0a44d17SEric Biggers 		     "vmovdqa64 (%2,%0), %%zmm1\n"
91*c0a44d17SEric Biggers 		     "vpternlogq $0x96, (%3,%0), %%zmm1, %%zmm0\n"
92*c0a44d17SEric Biggers 		     "vmovdqa64 (%4,%0), %%zmm1\n"
93*c0a44d17SEric Biggers 		     "vpternlogq $0x96, (%5,%0), %%zmm1, %%zmm0\n"
94*c0a44d17SEric Biggers 		     "vmovdqa64 %%zmm0, (%1,%0)\n"
95*c0a44d17SEric Biggers 		     "add $64, %0\n"
96*c0a44d17SEric Biggers 		     "jnz 1b\n"
97*c0a44d17SEric Biggers 		     : "+&r"(i)
98*c0a44d17SEric Biggers 		     : "r"(p1 + bytes), "r"(p2 + bytes), "r"(p3 + bytes),
99*c0a44d17SEric Biggers 		       "r"(p4 + bytes), "r"(p5 + bytes)
100*c0a44d17SEric Biggers 		     : "memory", "cc");
101*c0a44d17SEric Biggers }
102*c0a44d17SEric Biggers 
103*c0a44d17SEric Biggers DO_XOR_BLOCKS(avx512_inner, xor_avx512_2, xor_avx512_3, xor_avx512_4,
104*c0a44d17SEric Biggers 	      xor_avx512_5);
105*c0a44d17SEric Biggers 
106*c0a44d17SEric Biggers /*
107*c0a44d17SEric Biggers  * Preconditions: bytes is a nonzero multiple of 512, and all buffers are
108*c0a44d17SEric Biggers  * 64-byte aligned.
109*c0a44d17SEric Biggers  */
xor_gen_avx512(void * dest,void ** srcs,unsigned int src_cnt,unsigned int bytes)110*c0a44d17SEric Biggers static void xor_gen_avx512(void *dest, void **srcs, unsigned int src_cnt,
111*c0a44d17SEric Biggers 			   unsigned int bytes)
112*c0a44d17SEric Biggers {
113*c0a44d17SEric Biggers 	kernel_fpu_begin();
114*c0a44d17SEric Biggers 	xor_gen_avx512_inner(dest, srcs, src_cnt, bytes);
115*c0a44d17SEric Biggers 	asm volatile("vzeroupper");
116*c0a44d17SEric Biggers 	kernel_fpu_end();
117*c0a44d17SEric Biggers }
118*c0a44d17SEric Biggers 
119*c0a44d17SEric Biggers struct xor_block_template xor_block_avx512 = {
120*c0a44d17SEric Biggers 	.name = "avx512",
121*c0a44d17SEric Biggers 	.xor_gen = xor_gen_avx512,
122*c0a44d17SEric Biggers };
123