History

compiler: Use __typeof_unqual__() for __unqual_scalar_typeof()

The recent changes to get_unaligned() resulted in a new sparse warning: net/rds/ib_cm.c:96:35: sparse: sparse: incorrect type in argument 1 (different modifiers) @@ expected void * @@ got restricted __be64 const * @@ net/rds/ib_cm.c:96:35: sparse: expected void * net/rds/ib_cm.c:96:35: sparse: got restricted __be64 const * The updated get_unaligned_t() uses __unqual_scalar_typeof() to get an unqualified type. This works correctly for the compilers, but fails for sparse when the data type is __be64 (or any other __beNN variant). On sparse runs (C=[12]) __beNN types are annotated with __attribute__((bitwise)). That annotation allows sparse to detect incompatible operations on __beNN variables, but it also prevents sparse from evaluating the _Generic() in __unqual_scalar_typeof() and map __beNN to a unqualified scalar type, so it ends up with the default, i.e. the original qualified type of a 'const __beNN' pointer. That then ends up as the first pointer argument to builtin_memcpy(), which obviously causes the above sparse warnings. The sparse git tree supports typeof_unqual() now, which allows to use it instead of the _Generic() based __unqual_scalar_typeof(). With that sparse correctly evaluates the unqualified type and keeps the __beNN logic intact. The downside is that this requires a top of tree sparse build and an old sparse version will emit a metric ton of incomprehensible error messages before it dies with a segfault. Therefore implement a sanity check which validates that the checker is available and capable of handling typeof_unqual(). Emit a warning if not so the user can take informed action. [ tglx: Move the evaluation of USE_TYPEOF_UNQUAL to compiler_types.h so it is set before use and implement the sanity checker ]

Peter Zijlstra committed 10 months ago

Commit fd69b2f1 parent 759a1f94 files · +40 −10Browse files

Unified · Split

4 files
  1. Makefile +8 −0
  2. include/linux/compiler.h +0 −10
  3. include/linux/compiler_types.h +13 −0
  4. scripts/checker-valid.sh +19 −0
Makefile+8−0
@@ -1187,6 +1187,14 @@ CHECKFLAGS += $(if $(CONFIG_CPU_BIG_ENDIAN),-mbig-endian,-mlittle-endian) · 1186 unmodified lines
11871187 # the checker needs the correct machine size
11881188 CHECKFLAGS += $(if $(CONFIG_64BIT),-m64,-m32)
11891189
1190+# Validate the checker is available and functional
1191+ifneq ($(KBUILD_CHECKSRC), 0)
1192+ ifneq ($(shell $(srctree)/scripts/checker-valid.sh $(CHECK) $(CHECKFLAGS)), 1)
1193+ $(warning C=$(KBUILD_CHECKSRC) specified, but $(CHECK) is not available or not up to date)
1194+ KBUILD_CHECKSRC = 0
1195+ endif
1196+endif
1197+
11901198 # Default kernel image to build when no specific target is given.
11911199 # KBUILD_IMAGE may be overruled on the command line or
11921200 # set in the environment
include/linux/compiler.h+0−10
@@ -230,16 +230,6 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val, · 229 unmodified lines
230230 __BUILD_BUG_ON_ZERO_MSG(!__is_noncstr(p), \
231231 "must be non-C-string (not NUL-terminated)")
232232
233−/*
234− * Use __typeof_unqual__() when available.
235− *
236− * XXX: Remove test for __CHECKER__ once
237− * sparse learns about __typeof_unqual__().
238− */
239−#if CC_HAS_TYPEOF_UNQUAL && !defined(__CHECKER__)
240−# define USE_TYPEOF_UNQUAL 1
241−#endif
242−
243233 /*
244234 * Define TYPEOF_UNQUAL() to use __typeof_unqual__() as typeof
245235 * operator when available, to return an unqualified type of the exp.
include/linux/compiler_types.h+13−0
@@ -562,6 +562,14 @@ struct ftrace_likely_data { · 561 unmodified lines
562562 #define asm_inline asm
563563 #endif
564564
565+#ifndef __ASSEMBLY__
566+/*
567+ * Use __typeof_unqual__() when available.
568+ */
569+#if CC_HAS_TYPEOF_UNQUAL || defined(__CHECKER__)
570+# define USE_TYPEOF_UNQUAL 1
571+#endif
572+
565573 /* Are two types/vars the same type (ignoring qualifiers)? */
566574 #define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
567575
@@ -569,6 +577,7 @@ struct ftrace_likely_data { · 1 unmodified line
569577 * __unqual_scalar_typeof(x) - Declare an unqualified scalar type, leaving
570578 * non-scalar types unchanged.
571579 */
580+#ifndef USE_TYPEOF_UNQUAL
572581 /*
573582 * Prefer C11 _Generic for better compile-times and simpler code. Note: 'char'
574583 * is not type-compatible with 'signed char', and we define a separate case.
@@ -586,6 +595,10 @@ struct ftrace_likely_data { · 11 unmodified lines
586595 __scalar_type_to_expr_cases(long), \
587596 __scalar_type_to_expr_cases(long long), \
588597 default: (x)))
598+#else
599+#define __unqual_scalar_typeof(x) __typeof_unqual__(x)
600+#endif
601+#endif /* !__ASSEMBLY__ */
589602
590603 /* Is this type a native word size -- useful for atomic operations */
591604 #define __native_word(t) \
scripts/checker-valid.sh+19−0
@@ -0,0 +1,19 @@
1+#!/bin/sh -eu
2+# SPDX-License-Identifier: GPL-2.0
3+
4+[ ! -x "$(command -v "$1")" ] && exit 1
5+
6+tmp_file=$(mktemp)
7+trap "rm -f $tmp_file" EXIT
8+
9+cat << EOF >$tmp_file
10+static inline int u(const int *q)
11+{
12+ __typeof_unqual__(*q) v = *q;
13+ return v;
14+}
15+EOF
16+
17+# sparse happily exits with 0 on error so validate
18+# there is none on stderr. Use awk as grep is a pain with sh -e
19+$@ $tmp_file 2>&1 | awk -v c=1 '/error/{c=0}END{print c}'