Re: [PATCH] include/linux/compiler*.h: Use feature checking instead of version checks for attributes

From: Miguel Ojeda
Date: Tue Aug 28 2018 - 11:04:19 EST


Hi Nick,

On Mon, Aug 27, 2018 at 7:43 PM, Nick Desaulniers
<ndesaulniers@xxxxxxxxxx> wrote:
> On Sun, Aug 26, 2018 at 10:58 AM Miguel Ojeda
> <miguel.ojeda.sandonis@xxxxxxxxx> wrote:
>>
>> Instead of using version checks per-compiler to define (or not) each attribute,
>> use __has_attribute to test for them, following the cleanup started with
>> commit 815f0ddb346c ("include/linux/compiler*.h: make compiler-*.h mutually exclusive").
>>
>> All the attributes that are fairly common/standard (i.e. those that do not
>> require extra logic to define them) have been moved to a new file
>> include/linux/compiler_attributes.h. The attributes have been sorted
>> and divided between "required" and "optional".
>
> Nice! Thanks Miguel. Regarding sorting, I'm happy with that. In
> fact, some of the comments can be removed IMO, as the attributes have
> common definitions in the docs (maybe an added link to the gcc and
> clang attribute docs at the top of the file rather than per attribute
> comments).

Thanks for the review!

I thought about that, although there isn't a single page with them in
GCC (we could group them by type though: function ones, variable
ones... and then link to those). On the other hand, maybe writing a
Doc/ file is better and allows us to write as much as one would like
about each of them (and a link to each page compiler's page about it,
etc.). I think in the end the Doc/ file might be the best, in order
not to crowd the header.

>
>>
>> Further, attributes that are already supported in gcc >= 4.6 and recent clang
>> were simply made to be required (instead of testing for them):
>> * always_inline
>> * const (pure was already "required", by the way)
>> * gnu_inline
>
> There's an important test for gnu_inline that isn't checking that it's
> supported, but rather what the implicit behavior is depending on which
> C standard is being used. It's important not to remove that.

Hm... I actually thought it was not available at some point before 4.6
and removed the #ifdef. The comment even says it is featuring
detecting it so that the old GCC inlining is used; but it shouldn't
matter if you always use it, no?

I just went looking for more info in d03db2bc2 ("compiler-gcc.h: Add
__attribute__((gnu_inline)) to all inline declarations") and if I
understood the commit message, the problem is compiling with implicit
new standard in newer compilers which trigger the C90 behavior, while
we need the old one --- but if we use gnu_inline, we are getting it
regardless.

I am sure I am missing something, but I think a clarification is
needed (and in the code comment as well) -- a bit off-topic, anyway.

[Also, I wouldn't define an attribute or not depending on some other
condition. I would, instead, define something some other symbol with
that logic (i.e. instead of using "__gnu_inline", because that is
lying -- it is not using the attribute even if the compiler supports
it).]

>
>>
>> Finally, some other bits were cleaned up in the process:
>> * __optimize: removed (unused in the whole kernel tree)
>
> A+ for removing dead code. I also don't see it used anywhere.
>
>> * __must_be_array: removed from -gcc and -clang (identical), moved to _types
>
> Yep, uses a builtin (we should add guards for that, later, in a
> similar style change that guards the use of builtins). Looks good.
>
>> (it depends on the unconditionally used __builtin_types_compatible_p
>> * Removes unneeded underscores on the attributes' names
>
> That doesn't sound right, but lets see what you mean by that.

Some attributes used the __name__ syntax (i.e. inside the double
parenthesis), others didn't. I simplified by removing all the extra
underscores.

>
>>
>> There are some things that can be further cleaned up afterwards:
>> * __attribute_const__: rename to __const
>
> This doesn't look correct to me; the kernel is full of call sites for
> __attribute_const__. You can't rename the definition without renaming

Of course it is full of use sites! That is why I said it is a possible
cleanup for *afterwards* this patch :-)

> all of the call sites (and that would be too big a change for this
> patch, IMO). Skip the rename, and it also looks like you just removed
> it outright (Oops).

Not sure what you mean by this (?). The attribute is still there unchanged.

>
>> * __noretpoline: avoid checking for defined(__notrepoline)
>> * __compiletime_warning/error: they are in two different places,
>> -gcc and compiler.h.
>> * sparse' attributes could potentially go into the end of attributes.h
>> too (as another separate section).
>>
>> Compile-tested an x86 allmodconfig for a while with gcc 8.2.0 and 4.6.4.
>
> It's important to test changes to compiler-clang.h with clang. ;)

I would agree if the clang build wasn't broken to begin with. ;)

>
>>
>> Cc: Eli Friedman <efriedma@xxxxxxxxxxxxxx>
>> Cc: Christopher Li <sparse@xxxxxxxxxxx>
>> Cc: Kees Cook <keescook@xxxxxxxxxxxx>
>> Cc: Ingo Molnar <mingo@xxxxxxxxxx>
>> Cc: Geert Uytterhoeven <geert@xxxxxxxxxxxxxx>
>> Cc: Arnd Bergmann <arnd@xxxxxxxx>
>> Cc: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>
>> Cc: Masahiro Yamada <yamada.masahiro@xxxxxxxxxxxxx>
>> Cc: Joe Perches <joe@xxxxxxxxxxx>
>> Cc: Dominique Martinet <asmadeus@xxxxxxxxxxxxx>
>> Cc: Nick Desaulniers <ndesaulniers@xxxxxxxxxx>
>> Cc: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx>
>> Signed-off-by: Miguel Ojeda <miguel.ojeda.sandonis@xxxxxxxxx>
>> ---
>> *Seems* to work, but note that I did not finish the entire allmodconfig :)
>>
>> A few things could be splitted into their own patch, but I kept it
>> as one for simplicity for a first review.
>>
>> These files are becoming no-headaches-readable again, yay.
>
> A+
>
>>
>> include/linux/compiler-clang.h | 5 --
>> include/linux/compiler-gcc.h | 60 ----------------
>> include/linux/compiler-intel.h | 6 --
>> include/linux/compiler.h | 4 --
>> include/linux/compiler_attributes.h | 105 ++++++++++++++++++++++++++++
>> include/linux/compiler_types.h | 91 ++++--------------------
>> 6 files changed, 118 insertions(+), 153 deletions(-)
>> create mode 100644 include/linux/compiler_attributes.h
>>
>> diff --git a/include/linux/compiler-clang.h b/include/linux/compiler-clang.h
>> index b1ce500fe8b3..3e7dafb3ea80 100644
>> --- a/include/linux/compiler-clang.h
>> +++ b/include/linux/compiler-clang.h
>> @@ -21,8 +21,6 @@
>> #define __SANITIZE_ADDRESS__
>> #endif
>>
>> -#define __no_sanitize_address __attribute__((no_sanitize("address")))
>> -
>> /*
>> * Not all versions of clang implement the the type-generic versions
>> * of the builtin overflow checkers. Fortunately, clang implements
>> @@ -41,6 +39,3 @@
>> * compilers, like ICC.
>> */
>> #define barrier() __asm__ __volatile__("" : : : "memory")
>> -#define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))
>> -#define __assume_aligned(a, ...) \
>> - __attribute__((__assume_aligned__(a, ## __VA_ARGS__)))
>> diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h
>> index 763bbad1e258..dde3daae6287 100644
>> --- a/include/linux/compiler-gcc.h
>> +++ b/include/linux/compiler-gcc.h
>> @@ -68,13 +68,6 @@
>> */
>> #define uninitialized_var(x) x = x
>>
>> -#ifdef __CHECKER__
>> -#define __must_be_array(a) 0
>> -#else
>> -/* &a[0] degrades to a pointer: a different type from an array */
>> -#define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))
>> -#endif
>> -
>> #ifdef RETPOLINE
>> #define __noretpoline __attribute__((indirect_branch("keep")))
>> #endif
>> @@ -95,8 +88,6 @@
>>
>> #define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
>>
>> -#define __optimize(level) __attribute__((__optimize__(level)))
>> -
>> #define __compiletime_object_size(obj) __builtin_object_size(obj, 0)
>>
>> #ifndef __CHECKER__
>> @@ -133,9 +124,6 @@
>> __builtin_unreachable(); \
>> } while (0)
>>
>> -/* Mark a function definition as prohibited from being cloned. */
>> -#define __noclone __attribute__((__noclone__, __optimize__("no-tracer")))
>> -
>> #if defined(RANDSTRUCT_PLUGIN) && !defined(__CHECKER__)
>> #define __randomize_layout __attribute__((randomize_layout))
>> #define __no_randomize_layout __attribute__((no_randomize_layout))
>> @@ -144,32 +132,6 @@
>> #define randomized_struct_fields_end } __randomize_layout;
>> #endif
>>
>> -/*
>> - * When used with Link Time Optimization, gcc can optimize away C functions or
>> - * variables which are referenced only from assembly code. __visible tells the
>> - * optimizer that something else uses this function or variable, thus preventing
>> - * this.
>> - */
>> -#define __visible __attribute__((externally_visible))
>> -
>> -/* gcc version specific checks */
>> -
>> -#if GCC_VERSION >= 40900 && !defined(__CHECKER__)
>> -/*
>> - * __assume_aligned(n, k): Tell the optimizer that the returned
>> - * pointer can be assumed to be k modulo n. The second argument is
>> - * optional (default 0), so we use a variadic macro to make the
>> - * shorthand.
>> - *
>> - * Beware: Do not apply this to functions which may return
>> - * ERR_PTRs. Also, it is probably unwise to apply it to functions
>> - * returning extra information in the low bits (but in that case the
>> - * compiler should see some alignment anyway, when the return value is
>> - * massaged by 'flags = ptr & 3; ptr &= ~3;').
>> - */
>> -#define __assume_aligned(a, ...) __attribute__((__assume_aligned__(a, ## __VA_ARGS__)))
>> -#endif
>> -
>> /*
>> * GCC 'asm goto' miscompiles certain code sequences:
>> *
>> @@ -201,32 +163,10 @@
>> #define KASAN_ABI_VERSION 3
>> #endif
>>
>> -#if GCC_VERSION >= 40902
>> -/*
>> - * Tell the compiler that address safety instrumentation (KASAN)
>> - * should not be applied to that function.
>> - * Conflicts with inlining: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67368
>> - */
>> -#define __no_sanitize_address __attribute__((no_sanitize_address))
>> -#endif
>> -
>> #if GCC_VERSION >= 50100
>> -/*
>> - * Mark structures as requiring designated initializers.
>> - * https://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html
>> - */
>> -#define __designated_init __attribute__((designated_init))
>> #define COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW 1
>> #endif
>>
>> -#if !defined(__noclone)
>> -#define __noclone /* not needed */
>> -#endif
>> -
>> -#if !defined(__no_sanitize_address)
>> -#define __no_sanitize_address
>> -#endif
>> -
>> /*
>> * Turn individual warnings and errors on and off locally, depending
>> * on version.
>> diff --git a/include/linux/compiler-intel.h b/include/linux/compiler-intel.h
>> index 4c7f9befa9f6..fb9e77fc65ec 100644
>> --- a/include/linux/compiler-intel.h
>> +++ b/include/linux/compiler-intel.h
>> @@ -37,9 +37,3 @@
>> /* icc has this, but it's called _bswap16 */
>> #define __HAVE_BUILTIN_BSWAP16__
>> #define __builtin_bswap16 _bswap16
>> -
>> -/* The following are for compatibility with GCC, from compiler-gcc.h,
>> - * and may be redefined here because they should not be shared with other
>> - * compilers, like clang.
>> - */
>> -#define __visible __attribute__((externally_visible))
>> diff --git a/include/linux/compiler.h b/include/linux/compiler.h
>> index 681d866efb1e..7c0157d50964 100644
>> --- a/include/linux/compiler.h
>> +++ b/include/linux/compiler.h
>> @@ -301,10 +301,6 @@ static inline void *offset_to_ptr(const int *off)
>>
>> #endif /* __ASSEMBLY__ */
>>
>> -#ifndef __optimize
>> -# define __optimize(level)
>> -#endif
>> -
>> /* Compile time object size, -1 for unknown */
>> #ifndef __compiletime_object_size
>> # define __compiletime_object_size(obj) -1
>> diff --git a/include/linux/compiler_attributes.h b/include/linux/compiler_attributes.h
>> new file mode 100644
>> index 000000000000..af8c8413d136
>> --- /dev/null
>> +++ b/include/linux/compiler_attributes.h
>> @@ -0,0 +1,105 @@
>> +#ifndef __LINUX_COMPILER_ATTRIBUTES_H
>> +#define __LINUX_COMPILER_ATTRIBUTES_H
>> +
>> +/* This file is meant to be sorted. */
>> +
>> +/*
>> + * Required attributes: your compiler must support these.
>> + */
>> +#define __alias(symbol) __attribute__((alias(#symbol)))
>> +#define __aligned(x) __attribute__((aligned(x)))
>> +#define __aligned_largest __attribute__((aligned))
>> +#define __always_inline inline __attribute__((always_inline))
>> +#define __always_unused __attribute__((unused))
>> +#define __attribute_const__ __attribute__((const))
>> +#define __cold __attribute__((cold))
>> +#define __gnu_inline __attribute__((gnu_inline))
>> +#define __malloc __attribute__((malloc))
>> +#define __maybe_unused __attribute__((unused))
>> +#define __mode(x) __attribute__((mode(x)))
>> +#define noinline __attribute__((noinline))
>> +#define __noreturn __attribute__((noreturn))
>> +#define __packed __attribute__((packed))
>> +#define __printf(a, b) __attribute__((format(printf, a, b)))
>> +#define __pure __attribute__((pure))
>> +#define __scanf(a, b) __attribute__((format(scanf, a, b)))
>> +#define __section(S) __attribute__((section(#S)))
>> +#define __used __attribute__((used))
>> +#define __weak __attribute__((weak))
>> +
>> +/*
>> + * Optional attributes: your compiler may or may not support them.
>> + *
>> + * To check for them, we use __has_attribute, which is supported on gcc >= 5,
>> + * clang >= 2.9 and icc >= 17. In the meantime, to support 4.6 <= gcc < 5,
>> + * we implement it by hand.
>> + */
>> +#ifndef __has_attribute
>> +#define __has_attribute(x) __GCC46_has_attribute_##x
>> +#define __GCC46_has_attribute_assume_aligned 0
>> +#define __GCC46_has_attribute_designated_init 0
>> +#define __GCC46_has_attribute_externally_visible 1
>> +#define __GCC46_has_attribute_noclone 1
>> +#define __GCC46_has_attribute_optimize 1
>> +#define __GCC46_has_attribute_no_sanitize_address 0
>> +#endif
>> +
>> +/*
>> + * __assume_aligned(n, k): Tell the optimizer that the returned
>> + * pointer can be assumed to be k modulo n. The second argument is
>> + * optional (default 0), so we use a variadic macro to make the
>> + * shorthand.
>> + *
>> + * Beware: Do not apply this to functions which may return
>> + * ERR_PTRs. Also, it is probably unwise to apply it to functions
>> + * returning extra information in the low bits (but in that case the
>> + * compiler should see some alignment anyway, when the return value is
>> + * massaged by 'flags = ptr & 3; ptr &= ~3;').
>> + */
>> +#if __has_attribute(assume_aligned)
>> +#define __assume_aligned(a, ...) __attribute__((assume_aligned(a, ## __VA_ARGS__)))
>> +#else
>> +#define __assume_aligned(a, ...)
>> +#endif
>> +
>> +/*
>> + * Mark structures as requiring designated initializers.
>> + * https://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html
>> + */
>> +#if __has_attribute(designated_init)
>> +#define __designated_init __attribute__((designated_init))
>> +#else
>> +#define __designated_init
>> +#endif
>> +
>> +/*
>> + * When used with Link Time Optimization, gcc can optimize away C functions or
>> + * variables which are referenced only from assembly code. __visible tells the
>> + * optimizer that something else uses this function or variable, thus preventing
>> + * this.
>> + */
>> +#if __has_attribute(externally_visible)
>> +#define __visible __attribute__((externally_visible))
>> +#else
>> +#define __visible
>> +#endif
>> +
>> +/* Mark a function definition as prohibited from being cloned. */
>> +#if __has_attribute(noclone) && __has_attribute(optimize)
>> +#define __noclone __attribute__((noclone, optimize("no-tracer")))
>> +#else
>> +#define __noclone
>> +#endif
>> +
>> +/*
>> + * Tell the compiler that address safety instrumentation (KASAN)
>> + * should not be applied to that function.
>> + * Conflicts with inlining: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67368
>> + */
>> +#if __has_attribute(no_sanitize_address)
>> +#define __no_sanitize_address __attribute__((no_sanitize_address))
>> +#else
>> +#define __no_sanitize_address
>> +#endif
>> +
>> +#endif /* __LINUX_COMPILER_ATTRIBUTES_H */
>> diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h
>> index 3525c179698c..8cd622bedec4 100644
>> --- a/include/linux/compiler_types.h
>> +++ b/include/linux/compiler_types.h
>> @@ -54,6 +54,9 @@ extern void __chk_io_ptr(const volatile void __iomem *);
>>
>> #ifdef __KERNEL__
>>
>> +/* Attributes */
>> +#include <linux/compiler_attributes.h>
>> +
>> /* Compiler specific macros. */
>> #ifdef __clang__
>> #include <linux/compiler-clang.h>
>> @@ -78,12 +81,6 @@ extern void __chk_io_ptr(const volatile void __iomem *);
>> #include <asm/compiler.h>
>> #endif
>>
>> -/*
>> - * Generic compiler-independent macros required for kernel
>> - * build go below this comment. Actual compiler/compiler version
>> - * specific implementations come from the above header files
>> - */
>> -
>> struct ftrace_branch_data {
>> const char *func;
>> const char *file;
>> @@ -119,10 +116,6 @@ struct ftrace_likely_data {
>> * compilers. We don't consider that to be an error, so set them to nothing.
>> * For example, some of them are for compiler specific plugins.
>> */
>> -#ifndef __designated_init
>> -# define __designated_init
>> -#endif
>> -
>> #ifndef __latent_entropy
>> # define __latent_entropy
>> #endif
>> @@ -140,17 +133,6 @@ struct ftrace_likely_data {
>> # define randomized_struct_fields_end
>> #endif
>>
>> -#ifndef __visible
>> -#define __visible
>> -#endif
>> -
>> -/*
>> - * Assume alignment of return value.
>> - */
>> -#ifndef __assume_aligned
>> -#define __assume_aligned(a, ...)
>> -#endif
>> -
>> /* Are two types/vars the same type (ignoring qualifiers)? */
>> #define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
>>
>> @@ -159,14 +141,6 @@ struct ftrace_likely_data {
>> (sizeof(t) == sizeof(char) || sizeof(t) == sizeof(short) || \
>> sizeof(t) == sizeof(int) || sizeof(t) == sizeof(long))
>>
>> -#ifndef __attribute_const__
>> -#define __attribute_const__ __attribute__((__const__))
>> -#endif
>> -
>> -#ifndef __noclone
>> -#define __noclone
>> -#endif
>> -
>> /* Helpers for emitting diagnostics in pragmas. */
>> #ifndef __diag
>> #define __diag(string)
>> @@ -186,34 +160,6 @@ struct ftrace_likely_data {
>> #define __diag_error(compiler, version, option, comment) \
>> __diag_ ## compiler(version, error, option)
>>
>> -/*
>> - * From the GCC manual:
>> - *
>> - * Many functions have no effects except the return value and their
>> - * return value depends only on the parameters and/or global
>> - * variables. Such a function can be subject to common subexpression
>> - * elimination and loop optimization just as an arithmetic operator
>> - * would be.
>> - * [...]
>> - */
>> -#define __pure __attribute__((pure))
>> -#define __aligned(x) __attribute__((aligned(x)))
>> -#define __aligned_largest __attribute__((aligned))
>> -#define __printf(a, b) __attribute__((format(printf, a, b)))
>> -#define __scanf(a, b) __attribute__((format(scanf, a, b)))
>> -#define __maybe_unused __attribute__((unused))
>> -#define __always_unused __attribute__((unused))
>> -#define __mode(x) __attribute__((mode(x)))
>> -#define __malloc __attribute__((__malloc__))
>> -#define __used __attribute__((__used__))
>> -#define __noreturn __attribute__((noreturn))
>> -#define __packed __attribute__((packed))
>> -#define __weak __attribute__((weak))
>> -#define __alias(symbol) __attribute__((alias(#symbol)))
>> -#define __cold __attribute__((cold))
>> -#define __section(S) __attribute__((__section__(#S)))
>> -
>> -
>> #ifdef CONFIG_ENABLE_MUST_CHECK
>> #define __must_check __attribute__((warn_unused_result))
>> #else
>> @@ -228,18 +174,6 @@ struct ftrace_likely_data {
>>
>> #define __compiler_offsetof(a, b) __builtin_offsetof(a, b)
>>
>> -/*
>> - * Feature detection for gnu_inline (gnu89 extern inline semantics). Either
>> - * __GNUC_STDC_INLINE__ is defined (not using gnu89 extern inline semantics,
>> - * and we opt in to the gnu89 semantics), or __GNUC_STDC_INLINE__ is not
>> - * defined so the gnu89 semantics are the default.
>> - */
>> -#ifdef __GNUC_STDC_INLINE__
>> -# define __gnu_inline __attribute__((gnu_inline))
>> -#else
>> -# define __gnu_inline
>> -#endif
>> -
>> /*
>> * Force always-inline if the user requests it so via the .config.
>> * GCC does not warn about unused static inline functions for
>> @@ -254,19 +188,13 @@ struct ftrace_likely_data {
>> */
>> #if !defined(CONFIG_ARCH_SUPPORTS_OPTIMIZED_INLINING) || \
>> !defined(CONFIG_OPTIMIZE_INLINING)
>> -#define inline \
>> - inline __attribute__((always_inline, unused)) notrace __gnu_inline
>> +#define inline inline __attribute__((always_inline, unused)) notrace __gnu_inline
>> #else
>> -#define inline inline __attribute__((unused)) notrace __gnu_inline
>> +#define inline inline __attribute__((unused)) notrace __gnu_inline
>> #endif
>>
>> #define __inline__ inline
>> -#define __inline inline
>> -#define noinline __attribute__((noinline))
>> -
>> -#ifndef __always_inline
>> -#define __always_inline inline __attribute__((always_inline))
>> -#endif
>> +#define __inline inline
>
> All of the changes to inline should not be removed, see above. It's
> important to make this work correctly regardless of C standard used.
>

See above.

>>
>> /*
>> * Rather then using noinline to prevent stack consumption, use
>> @@ -274,4 +202,11 @@ struct ftrace_likely_data {
>> */
>> #define noinline_for_stack noinline
>>
>> +#ifdef __CHECKER__
>> +#define __must_be_array(a) 0
>> +#else
>> +/* &a[0] degrades to a pointer: a different type from an array */
>> +#define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))
>> +#endif
>> +
>> #endif /* __LINUX_COMPILER_TYPES_H */
>> --
>> 2.17.1
>>
>
> With the above changes requested, I'm super happy with the spirit of
> this patch, and look forward to a v2. Thanks again Miguel!

Thanks again for the very thorough review!

Cheers,
Miguel

> --
> Thanks,
> ~Nick Desaulniers