xref: /linux/arch/mips/include/asm/unroll.h (revision 9a6b55ac)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 #ifndef __ASM_UNROLL_H__
3 #define __ASM_UNROLL_H__
4 
5 /*
6  * Explicitly unroll a loop, for use in cases where doing so is performance
7  * critical.
8  *
9  * Ideally we'd rely upon the compiler to provide this but there's no commonly
10  * available means to do so. For example GCC's "#pragma GCC unroll"
11  * functionality would be ideal but is only available from GCC 8 onwards. Using
12  * -funroll-loops is an option but GCC tends to make poor choices when
13  * compiling our string functions. -funroll-all-loops leads to massive code
14  * bloat, even if only applied to the string functions.
15  */
16 #define unroll(times, fn, ...) do {				\
17 	extern void bad_unroll(void)				\
18 		__compiletime_error("Unsupported unroll");	\
19 								\
20 	/*							\
21 	 * We can't unroll if the number of iterations isn't	\
22 	 * compile-time constant. Unfortunately GCC versions	\
23 	 * up until 4.6 tend to miss obvious constants & cause	\
24 	 * this check to fail, even though they go on to	\
25 	 * generate reasonable code for the switch statement,	\
26 	 * so we skip the sanity check for those compilers.	\
27 	 */							\
28 	BUILD_BUG_ON((CONFIG_GCC_VERSION >= 40700 ||		\
29 		      CONFIG_CLANG_VERSION >= 80000) &&		\
30 		     !__builtin_constant_p(times));		\
31 								\
32 	switch (times) {					\
33 	case 32: fn(__VA_ARGS__); /* fall through */		\
34 	case 31: fn(__VA_ARGS__); /* fall through */		\
35 	case 30: fn(__VA_ARGS__); /* fall through */		\
36 	case 29: fn(__VA_ARGS__); /* fall through */		\
37 	case 28: fn(__VA_ARGS__); /* fall through */		\
38 	case 27: fn(__VA_ARGS__); /* fall through */		\
39 	case 26: fn(__VA_ARGS__); /* fall through */		\
40 	case 25: fn(__VA_ARGS__); /* fall through */		\
41 	case 24: fn(__VA_ARGS__); /* fall through */		\
42 	case 23: fn(__VA_ARGS__); /* fall through */		\
43 	case 22: fn(__VA_ARGS__); /* fall through */		\
44 	case 21: fn(__VA_ARGS__); /* fall through */		\
45 	case 20: fn(__VA_ARGS__); /* fall through */		\
46 	case 19: fn(__VA_ARGS__); /* fall through */		\
47 	case 18: fn(__VA_ARGS__); /* fall through */		\
48 	case 17: fn(__VA_ARGS__); /* fall through */		\
49 	case 16: fn(__VA_ARGS__); /* fall through */		\
50 	case 15: fn(__VA_ARGS__); /* fall through */		\
51 	case 14: fn(__VA_ARGS__); /* fall through */		\
52 	case 13: fn(__VA_ARGS__); /* fall through */		\
53 	case 12: fn(__VA_ARGS__); /* fall through */		\
54 	case 11: fn(__VA_ARGS__); /* fall through */		\
55 	case 10: fn(__VA_ARGS__); /* fall through */		\
56 	case 9: fn(__VA_ARGS__); /* fall through */		\
57 	case 8: fn(__VA_ARGS__); /* fall through */		\
58 	case 7: fn(__VA_ARGS__); /* fall through */		\
59 	case 6: fn(__VA_ARGS__); /* fall through */		\
60 	case 5: fn(__VA_ARGS__); /* fall through */		\
61 	case 4: fn(__VA_ARGS__); /* fall through */		\
62 	case 3: fn(__VA_ARGS__); /* fall through */		\
63 	case 2: fn(__VA_ARGS__); /* fall through */		\
64 	case 1: fn(__VA_ARGS__); /* fall through */		\
65 	case 0: break;						\
66 								\
67 	default:						\
68 		/*						\
69 		 * Either the iteration count is unreasonable	\
70 		 * or we need to add more cases above.		\
71 		 */						\
72 		bad_unroll();					\
73 		break;						\
74 	}							\
75 } while (0)
76 
77 #endif /* __ASM_UNROLL_H__ */
78