Paul Burton | 6baaead | 2019-10-08 18:22:00 +0000 | [diff] [blame] | 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 \ |
Linus Torvalds | 6ec4476 | 2020-07-08 10:48:35 -0700 | [diff] [blame] | 22 | * compile-time constant. Unfortunately clang versions \ |
| 23 | * up until 8.0 tend to miss obvious constants & cause \ |
Paul Burton | 6baaead | 2019-10-08 18:22:00 +0000 | [diff] [blame] | 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 | */ \ |
Nathan Chancellor | 9321f1a | 2020-07-10 15:34:41 -0700 | [diff] [blame] | 28 | BUILD_BUG_ON(!__builtin_constant_p(times)); \ |
Paul Burton | 6baaead | 2019-10-08 18:22:00 +0000 | [diff] [blame] | 29 | \ |
| 30 | switch (times) { \ |
Gustavo A. R. Silva | df561f66 | 2020-08-23 17:36:59 -0500 | [diff] [blame] | 31 | case 32: fn(__VA_ARGS__); fallthrough; \ |
| 32 | case 31: fn(__VA_ARGS__); fallthrough; \ |
| 33 | case 30: fn(__VA_ARGS__); fallthrough; \ |
| 34 | case 29: fn(__VA_ARGS__); fallthrough; \ |
| 35 | case 28: fn(__VA_ARGS__); fallthrough; \ |
| 36 | case 27: fn(__VA_ARGS__); fallthrough; \ |
| 37 | case 26: fn(__VA_ARGS__); fallthrough; \ |
| 38 | case 25: fn(__VA_ARGS__); fallthrough; \ |
| 39 | case 24: fn(__VA_ARGS__); fallthrough; \ |
| 40 | case 23: fn(__VA_ARGS__); fallthrough; \ |
| 41 | case 22: fn(__VA_ARGS__); fallthrough; \ |
| 42 | case 21: fn(__VA_ARGS__); fallthrough; \ |
| 43 | case 20: fn(__VA_ARGS__); fallthrough; \ |
| 44 | case 19: fn(__VA_ARGS__); fallthrough; \ |
| 45 | case 18: fn(__VA_ARGS__); fallthrough; \ |
| 46 | case 17: fn(__VA_ARGS__); fallthrough; \ |
| 47 | case 16: fn(__VA_ARGS__); fallthrough; \ |
| 48 | case 15: fn(__VA_ARGS__); fallthrough; \ |
| 49 | case 14: fn(__VA_ARGS__); fallthrough; \ |
| 50 | case 13: fn(__VA_ARGS__); fallthrough; \ |
| 51 | case 12: fn(__VA_ARGS__); fallthrough; \ |
| 52 | case 11: fn(__VA_ARGS__); fallthrough; \ |
| 53 | case 10: fn(__VA_ARGS__); fallthrough; \ |
| 54 | case 9: fn(__VA_ARGS__); fallthrough; \ |
| 55 | case 8: fn(__VA_ARGS__); fallthrough; \ |
| 56 | case 7: fn(__VA_ARGS__); fallthrough; \ |
| 57 | case 6: fn(__VA_ARGS__); fallthrough; \ |
| 58 | case 5: fn(__VA_ARGS__); fallthrough; \ |
| 59 | case 4: fn(__VA_ARGS__); fallthrough; \ |
| 60 | case 3: fn(__VA_ARGS__); fallthrough; \ |
| 61 | case 2: fn(__VA_ARGS__); fallthrough; \ |
| 62 | case 1: fn(__VA_ARGS__); fallthrough; \ |
Paul Burton | 6baaead | 2019-10-08 18:22:00 +0000 | [diff] [blame] | 63 | case 0: break; \ |
| 64 | \ |
| 65 | default: \ |
| 66 | /* \ |
| 67 | * Either the iteration count is unreasonable \ |
| 68 | * or we need to add more cases above. \ |
| 69 | */ \ |
| 70 | bad_unroll(); \ |
| 71 | break; \ |
| 72 | } \ |
| 73 | } while (0) |
| 74 | |
| 75 | #endif /* __ASM_UNROLL_H__ */ |