Add __attribute_const__ to ffs()-family implementations
Subsystems (17)
cover Add __attribute_const__ to ffs()-family implementations
Hi,
While tracking down a problem where constant expressions used by BUILD_BUG_ON() suddenly stopped working[1], we found that an added static initializer was convincing the compiler that it couldn't track the state of the prior statically initialized value. Tracing this down found that ffs() was used in the initializer macro, but since it wasn't marked with __attribute_const__, the compiler had to assume the function might change variable states as a side-effect (which is not true for ffs(), which provides deterministic math results).
Add KUnit tests for the family of functions and then add __attribute_const__ to all architecture implementations and wrappers.
-Kees
[1] https://github.com/KSPP/linux/issues/364
Kees Cook (17):
KUnit: Introduce ffs()-family tests
bitops: Add __attribute_const__ to generic ffs()-family
implementations
csky: Add __attribute_const__ to ffs()-family implementations
x86: Add __attribute_const__ to ffs()-family implementations
powerpc: Add __attribute_const__ to ffs()-family implementations
sh: Add __attribute_const__ to ffs()-family implementations
alpha: Add __attribute_const__ to ffs()-family implementations
hexagon: Add __attribute_const__ to ffs()-family implementations
riscv: Add __attribute_const__ to ffs()-family implementations
openrisc: Add __attribute_const__ to ffs()-family implementations
m68k: Add __attribute_const__ to ffs()-family implementations
mips: Add __attribute_const__ to ffs()-family implementations
parisc: Add __attribute_const__ to ffs()-family implementations
s390: Add __attribute_const__ to ffs()-family implementations
xtensa: Add __attribute_const__ to ffs()-family implementations
sparc: Add __attribute_const__ to ffs()-family implementations
KUnit: ffs: Validate all the __attribute_const__ annotations
lib/Kconfig.debug | 14 + lib/tests/Makefile | 1 + arch/alpha/include/asm/bitops.h | 14 +- arch/csky/include/asm/bitops.h | 8 +- arch/hexagon/include/asm/bitops.h | 10 +- arch/m68k/include/asm/bitops.h | 14 +- arch/mips/include/asm/bitops.h | 8 +- arch/openrisc/include/asm/bitops/__ffs.h | 2 +- arch/openrisc/include/asm/bitops/__fls.h | 2 +- arch/openrisc/include/asm/bitops/ffs.h | 2 +- arch/openrisc/include/asm/bitops/fls.h | 2 +- arch/parisc/include/asm/bitops.h | 6 +- arch/powerpc/include/asm/bitops.h | 4 +- arch/riscv/include/asm/bitops.h | 6 +- arch/s390/include/asm/bitops.h | 10 +- arch/sh/include/asm/bitops.h | 4 +- arch/sparc/include/asm/bitops_64.h | 8 +- arch/x86/include/asm/bitops.h | 12 +- arch/xtensa/include/asm/bitops.h | 10 +- include/asm-generic/bitops/__ffs.h | 2 +- include/asm-generic/bitops/__fls.h | 2 +- include/asm-generic/bitops/builtin-__ffs.h | 2 +- include/asm-generic/bitops/builtin-__fls.h | 2 +- include/asm-generic/bitops/builtin-fls.h | 2 +- include/asm-generic/bitops/ffs.h | 2 +- include/asm-generic/bitops/fls.h | 2 +- include/asm-generic/bitops/fls64.h | 4 +- include/linux/bitops.h | 2 +- lib/clz_ctz.c | 8 +- lib/tests/ffs_kunit.c | 566 +++++++++++++++++++++ 30 files changed, 656 insertions(+), 75 deletions(-) create mode 100644 lib/tests/ffs_kunit.c
Signature · 1 line
2.34.1
PATCH 1/17 KUnit: Introduce ffs()-family tests
Add KUnit tests for ffs()-family bit scanning functions: ffs(), __ffs(), fls(), __fls(), fls64(), __ffs64(), and ffz(). The tests validate mathematical relationships (e.g. ffs(x) == __ffs(x) + 1), and test zero values, power-of-2 patterns, maximum values, and sparse bit patterns.
Build and run tested with everything I could reach with KUnit:
$ ./tools/testing/kunit/kunit.py run --arch=x86_64 ffs $ ./tools/testing/kunit/kunit.py run --arch=i386 ffs $ ./tools/testing/kunit/kunit.py run --arch=arm64 --make_options "CROSS_COMPILE=aarch64-linux-gnu-" ffs $ ./tools/testing/kunit/kunit.py run --arch=arm --make_options "CROSS_COMPILE=arm-linux-gnueabihf-" ffs $ ./tools/testing/kunit/kunit.py run --arch=powerpc ffs $ ./tools/testing/kunit/kunit.py run --arch=powerpc32 ffs $ ./tools/testing/kunit/kunit.py run --arch=powerpcle ffs $ ./tools/testing/kunit/kunit.py run --arch=riscv --make_options "CROSS_COMPILE=riscv64-linux-gnu-" ffs $ ./tools/testing/kunit/kunit.py run --arch=riscv32 --make_options "CROSS_COMPILE=riscv64-linux-gnu-" ffs $ ./tools/testing/kunit/kunit.py run --arch=s390 --make_options "CROSS_COMPILE=s390x-linux-gnu-" ffs $ ./tools/testing/kunit/kunit.py run --arch=m68k ffs $ ./tools/testing/kunit/kunit.py run --arch=loongarch ffs $ ./tools/testing/kunit/kunit.py run --arch=mips --make_options "CROSS_COMPILE=mipsel-linux-gnu-" ffs $ ./tools/testing/kunit/kunit.py run --arch=sparc --make_options "CROSS_COMPILE=sparc64-linux-gnu-" ffs $ ./tools/testing/kunit/kunit.py run --arch=sparc64 --make_options "CROSS_COMPILE=sparc64-linux-gnu-" ffs $ ./tools/testing/kunit/kunit.py run --arch=alpha --make_options "CROSS_COMPILE=alpha-linux-gnu-" ffs $ ./tools/testing/kunit/kunit.py run --arch=sh --make_options "CROSS_COMPILE=sh4-linux-gnu-" ffs
--- lib/Kconfig.debug | 14 ++ lib/tests/Makefile | 1 + lib/tests/ffs_kunit.c | 526 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 541 insertions(+) create mode 100644 lib/tests/ffs_kunit.c
lib/Kconfig.debug +14 -0
@@ -2489,6 +2489,20 @@ config STRING_HELPERS_KUNIT_TEST depends on KUNIT default KUNIT_ALL_TESTS +config FFS_KUNIT_TEST + tristate "KUnit test ffs-family functions at runtime" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS + help + This builds KUnit tests for ffs-family bit manipulation functions + including ffs(), __ffs(), fls(), __fls(), fls64(), and __ffs64(). + + These tests validate mathematical correctness, edge case handling, + and cross-architecture consistency of bit scanning functions. + + For more information on KUnit and unit tests in general, + please refer to Documentation/dev-tools/kunit/. + config TEST_KSTRTOX tristate "Test kstrto*() family of functions at runtime"
lib/tests/Makefile +1 -0
@@ -10,6 +10,7 @@ obj-$(CONFIG_BLACKHOLE_DEV_KUNIT_TEST) += blackhole_dev_kunit.o obj-$(CONFIG_CHECKSUM_KUNIT) += checksum_kunit.o obj-$(CONFIG_CMDLINE_KUNIT_TEST) += cmdline_kunit.o obj-$(CONFIG_CPUMASK_KUNIT_TEST) += cpumask_kunit.o +obj-$(CONFIG_FFS_KUNIT_TEST) += ffs_kunit.o CFLAGS_fortify_kunit.o += $(call cc-disable-warning, unsequenced) CFLAGS_fortify_kunit.o += $(call cc-disable-warning, stringop-overread) CFLAGS_fortify_kunit.o += $(call cc-disable-warning, stringop-truncation)
lib/tests/ffs_kunit.c new file +526 -0
526 lines not on this page · raw message
Signature · 1 line
2.34.1
PATCH 2/17 bitops: Add __attribute_const__ to generic ffs()-family implementations
In reply to Kees CookWhile tracking down a problem where constant expressions used by BUILD_BUG_ON() suddenly stopped working[1], we found that an added static initializer was convincing the compiler that it couldn't track the state of the prior statically initialized value. Tracing this down found that ffs() was used in the initializer macro, but since it wasn't marked with __attribute__const__, the compiler had to assume the function might change variable states as a side-effect (which is not true for ffs(), which provides deterministic math results).
Add missing __attribute_const__ annotations to generic implementations of ffs(), __ffs(), fls(), and __fls() functions. These are pure mathematical functions that always return the same result for the same input with no side effects, making them eligible for compiler optimization.
Build tested with x86_64 defconfig using GCC 14.2.0, which should validate the implementations when used by ARM, ARM64, LoongArch, Microblaze, NIOS2, and SPARC32 architectures.
--- include/asm-generic/bitops/__ffs.h | 2 +- include/asm-generic/bitops/__fls.h | 2 +- include/asm-generic/bitops/builtin-__ffs.h | 2 +- include/asm-generic/bitops/builtin-__fls.h | 2 +- include/asm-generic/bitops/builtin-fls.h | 2 +- include/asm-generic/bitops/ffs.h | 2 +- include/asm-generic/bitops/fls.h | 2 +- include/asm-generic/bitops/fls64.h | 4 ++-- include/linux/bitops.h | 2 +- lib/clz_ctz.c | 8 ++++---- 10 files changed, 14 insertions(+), 14 deletions(-)
include/asm-generic/bitops/__ffs.h +1 -1
@@ -10,7 +10,7 @@* * Undefined if no bit exists, so code should check against 0 first. */ -static __always_inline unsigned int generic___ffs(unsigned long word) +static __always_inline __attribute_const__ unsigned int generic___ffs(unsigned long word) {unsigned int num =0 ;
include/asm-generic/bitops/__fls.h +1 -1
@@ -10,7 +10,7 @@* * Undefined if no set bit exists, so code should check against 0 first. */ -static __always_inline unsigned int generic___fls(unsigned long word) +static __always_inline __attribute_const__ unsigned int generic___fls(unsigned long word) {unsigned int num =BITS_PER_LONG -1 ;
include/asm-generic/bitops/builtin-__ffs.h +1 -1
@@ -8,7 +8,7 @@* * Undefined if no bit exists, so code should check against 0 first. */ -static __always_inline unsigned int __ffs(unsigned long word) +static __always_inline __attribute_const__ unsigned int __ffs(unsigned long word) {return __builtin_ctzl (word); }
include/asm-generic/bitops/builtin-__fls.h +1 -1
@@ -8,7 +8,7 @@* * Undefined if no set bit exists, so code should check against 0 first. */ -static __always_inline unsigned int __fls(unsigned long word) +static __always_inline __attribute_const__ unsigned int __fls(unsigned long word) {return (sizeof (word) *8 ) -1 -__builtin_clzl (word); }
include/asm-generic/bitops/builtin-fls.h +1 -1
@@ -9,7 +9,7 @@* This is defined the same way as ffs. * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. */ -static __always_inline int fls(unsigned int x) +static __always_inline __attribute_const__ int fls(unsigned int x) {return x ?sizeof (x) *8 -__builtin_clz (x) :0 ; }
include/asm-generic/bitops/ffs.h +1 -1
@@ -10,7 +10,7 @@* the libc and compiler builtin ffs routines, therefore * differs in spirit from ffz (man ffs). */ -static inline int generic_ffs (int x) +static inline __attribute_const__ int generic_ffs(int x) {int r =1 ;
include/asm-generic/bitops/fls.h +1 -1
@@ -10,7 +10,7 @@* Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. */ -static __always_inline int generic_fls(unsigned int x) +static __always_inline __attribute_const__ int generic_fls(unsigned int x) {int r =32 ;
include/asm-generic/bitops/fls64.h +2 -2
@@ -16,7 +16,7 @@* at position 64. */ #if BITS_PER_LONG ==32 -static __always_inline int fls64(__u64 x) +static __always_inline __attribute_const__ int fls64(__u64 x) {__u32 h = x >>32 ;if (h)
@@ -24,7 +24,7 @@ static __always_inline int fls64(__u64 x)return fls (x); }#elif BITS_PER_LONG ==64 -static __always_inline int fls64(__u64 x) +static __always_inline __attribute_const__ int fls64(__u64 x) {if (x ==0 )return 0 ;
include/linux/bitops.h +1 -1
@@ -267,7 +267,7 @@ static inline int parity8(u8 val)* The result is not defined if no bits are set, so check that @word * is non-zero before calling this. */ -static inline unsigned int __ffs64 (u64 word) +static inline __attribute_const__ unsigned int __ffs64(u64 word) {#if BITS_PER_LONG ==32 if (((u32 )word) ==0UL )
lib/clz_ctz.c +4 -4
@@ -15,28 +15,28 @@#include <linux/kernel.h> int __weak __ctzsi2 (int val); -int __weak __ctzsi2 (int val) +int __weak __attribute_const__ __ctzsi2(int val) {return __ffs (val); }EXPORT_SYMBOL (__ctzsi2);int __weak __clzsi2 (int val); -int __weak __clzsi2 (int val) +int __weak __attribute_const__ __clzsi2(int val) {return 32 -fls (val); }EXPORT_SYMBOL (__clzsi2);int __weak __clzdi2 (u64 val); -int __weak __clzdi2 (u64 val) +int __weak __attribute_const__ __clzdi2(u64 val) {return 64 -fls64 (val); }EXPORT_SYMBOL (__clzdi2);int __weak __ctzdi2 (u64 val); -int __weak __ctzdi2 (u64 val) +int __weak __attribute_const__ __ctzdi2(u64 val) {return __ffs64 (val); }
Signature · 1 line
2.34.1
PATCH 3/17 csky: Add __attribute_const__ to ffs()-family implementations
In reply to Kees CookWhile tracking down a problem where constant expressions used by BUILD_BUG_ON() suddenly stopped working[1], we found that an added static initializer was convincing the compiler that it couldn't track the state of the prior statically initialized value. Tracing this down found that ffs() was used in the initializer macro, but since it wasn't marked with __attribute__const__, the compiler had to assume the function might change variable states as a side-effect (which is not true for ffs(), which provides deterministic math results).
Add missing __attribute_const__ annotations to C-SKY's implementations of ffs(), __ffs(), fls(), and __fls() functions. These are pure mathematical functions that always return the same result for the same input with no side effects, making them eligible for compiler optimization.
Build tested ARCH=csky defconfig with GCC csky-linux 15.1.0.
--- arch/csky/include/asm/bitops.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
arch/csky/include/asm/bitops.h +4 -4
@@ -9,7 +9,7 @@/* * asm-generic/bitops/ffs.h */ -static inline int ffs (int x) +static inline __attribute_const__ int ffs(int x) {if (!x)return 0 ;
@@ -26,7 +26,7 @@ static inline int ffs(int x)/* * asm-generic/bitops/__ffs.h */ -static __always_inline unsigned long __ffs (unsigned long x) +static __always_inline __attribute_const__ unsigned long __ffs(unsigned long x) { asmvolatile ( "brev %0\n"
@@ -39,7 +39,7 @@ static __always_inline unsigned long __ffs(unsigned long x)/* * asm-generic/bitops/fls.h */ -static __always_inline int fls(unsigned int x) +static __always_inline __attribute_const__ int fls(unsigned int x) { asmvolatile ( "ff1 %0\n"
@@ -52,7 +52,7 @@ static __always_inline int fls(unsigned int x)/* * asm-generic/bitops/__fls.h */ -static __always_inline unsigned long __fls (unsigned long x) +static __always_inline __attribute_const__ unsigned long __fls(unsigned long x) {return fls (x) -1 ; }
Signature · 1 line
2.34.1
Re: [PATCH 03/17] csky: Add __attribute_const__ to ffs()-family implementations
Quoted 9 lines · Kees Cook
While tracking down a problem where constant expressions used by
BUILD_BUG_ON()suddenly stopped working[1], we found that an added static initializer was convincing the compiler that it couldn't track the state of the prior statically initialized value. Tracing this down found thatffs()was used in the initializer macro, but since it wasn't marked with__attribute__const__, the compiler had to assume the function might change variable states as a side-effect (which is not true forffs(), which provides deterministic math results).
Quoted 4 lines
Add missing
__attribute_const__annotations to C-SKY's implementations offfs(),__ffs(),fls(), and__fls()functions. These are pure mathematical functions that always return the same result for the same input with no side effects, making them eligible for compiler optimization.
LGTM.
Quoted 53 lines
Build tested ARCH=csky defconfig with GCC csky-linux 15.1.0.
Signed-off-by Kees Cook <[email protected]>--- arch/csky/include/asm/bitops.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)arch/csky/include/asm/bitops.h +4
-4@@ -9,7 +9,7 @@/* * asm-generic/bitops/ffs.h */ -static inline int ffs (int x) +static inline __attribute_const__ int ffs(int x) {if (!x)return 0 ;@@ -26,7 +26,7 @@ static inline int ffs(int x)/* * asm-generic/bitops/__ffs.h */ -static __always_inline unsigned long __ffs (unsigned long x) +static __always_inline __attribute_const__ unsigned long __ffs(unsigned long x) { asmvolatile ( "brev %0\n"@@ -39,7 +39,7 @@ static __always_inline unsigned long __ffs(unsigned long x)/* * asm-generic/bitops/fls.h */ -static __always_inline int fls(unsigned int x) +static __always_inline __attribute_const__ int fls(unsigned int x) { asmvolatile ( "ff1 %0\n"@@ -52,7 +52,7 @@ static __always_inline int fls(unsigned int x)/* * asm-generic/bitops/__fls.h */ -static __always_inline unsigned long __fls (unsigned long x) +static __always_inline __attribute_const__ unsigned long __fls(unsigned long x) {return fls (x) -1 ; }Signature · 1 line
2.34.1
PATCH 4/17 x86: Add __attribute_const__ to ffs()-family implementations
In reply to Kees CookWhile tracking down a problem where constant expressions used by BUILD_BUG_ON() suddenly stopped working[1], we found that an added static initializer was convincing the compiler that it couldn't track the state of the prior statically initialized value. Tracing this down found that ffs() was used in the initializer macro, but since it wasn't marked with __attribute__const__, the compiler had to assume the function might change variable states as a side-effect (which is not true for ffs(), which provides deterministic math results).
Add missing __attribute_const__ annotations to x86's implementations of variable__ffs(), variable_ffz(), __fls(), variable_ffs(), and fls() functions. These are pure mathematical functions that always return the same result for the same input with no side effects, making them eligible for compiler optimization.
Build tested ARCH=x86_64 defconfig with GCC gcc 14.2.0.
--- arch/x86/include/asm/bitops.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
arch/x86/include/asm/bitops.h +6 -6
@@ -246,7 +246,7 @@ arch_test_bit_acquire(unsigned long nr, const volatile unsigned long *addr)variable_test_bit (nr, addr); } -static __always_inline unsigned long variable__ffs (unsigned long word) +static __always_inline __attribute_const__ unsigned long variable__ffs(unsigned long word) { asm("tzcnt %1,%0" : "=r" (word)
@@ -265,7 +265,7 @@ static __always_inline unsigned long variable__ffs(unsigned long word) (unsigned long)__builtin_ctzl(word) : \ variable__ffs(word)) -static __always_inline unsigned long variable_ffz (unsigned long word) +static __always_inline __attribute_const__ unsigned long variable_ffz(unsigned long word) {return variable__ffs (~word); }
@@ -287,7 +287,7 @@ static __always_inline unsigned long variable_ffz(unsigned long word)* * Undefined if no set bit exists, so code should check against 0 first. */ -static __always_inline unsigned long __fls (unsigned long word) +static __always_inline __attribute_const__ unsigned long __fls(unsigned long word) {if (__builtin_constant_p (word))return BITS_PER_LONG -1 -__builtin_clzl (word);
@@ -301,7 +301,7 @@ static __always_inline unsigned long __fls(unsigned long word)#undef ADDR#ifdef __KERNEL__ -static __always_inline int variable_ffs(int x) +static __always_inline __attribute_const__ int variable_ffs(int x) {int r;
@@ -355,7 +355,7 @@ static __always_inline int variable_ffs(int x)* set bit if value is nonzero. The last (most significant) bit is * at position 32. */ -static __always_inline int fls(unsigned int x) +static __always_inline __attribute_const__ int fls(unsigned int x) {int r;
@@ -400,7 +400,7 @@ static __always_inline int fls(unsigned int x)* at position 64. */ #ifdef CONFIG_X86_64 -static __always_inline int fls64(__u64 x) +static __always_inline __attribute_const__ int fls64(__u64 x) {int bitpos =-1 ;
Signature · 1 line
2.34.1
PATCH 5/17 powerpc: Add __attribute_const__ to ffs()-family implementations
In reply to Kees CookWhile tracking down a problem where constant expressions used by BUILD_BUG_ON() suddenly stopped working[1], we found that an added static initializer was convincing the compiler that it couldn't track the state of the prior statically initialized value. Tracing this down found that ffs() was used in the initializer macro, but since it wasn't marked with __attribute__const__, the compiler had to assume the function might change variable states as a side-effect (which is not true for ffs(), which provides deterministic math results).
Add missing __attribute_const__ annotations to PowerPC's implementations of fls() function. These are pure mathematical functions that always return the same result for the same input with no side effects, making them eligible for compiler optimization.
Build tested ARCH=powerpc defconfig with GCC powerpc-linux-gnu 14.2.0.
--- arch/powerpc/include/asm/bitops.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arch/powerpc/include/asm/bitops.h +2 -2
@@ -276,7 +276,7 @@ static inline void arch___clear_bit_unlock(int nr, volatile unsigned long *addr)* fls: find last (most-significant) bit set. * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. */ -static __always_inline int fls(unsigned int x) +static __always_inline __attribute_const__ int fls(unsigned int x) {int lz;
@@ -294,7 +294,7 @@ static __always_inline int fls(unsigned int x)* 32-bit fls calls. */ #ifdef CONFIG_PPC64 -static __always_inline int fls64(__u64 x) +static __always_inline __attribute_const__ int fls64(__u64 x) {int lz;
Signature · 1 line
2.34.1
Re: [PATCH 05/17] powerpc: Add __attribute_const__ to ffs()-family implementations
Quoted 9 lines · Kees Cook
While tracking down a problem where constant expressions used by
BUILD_BUG_ON()suddenly stopped working[1], we found that an added static initializer was convincing the compiler that it couldn't track the state of the prior statically initialized value. Tracing this down found thatffs()was used in the initializer macro, but since it wasn't marked with__attribute__const__, the compiler had to assume the function might change variable states as a side-effect (which is not true forffs(), which provides deterministic math results).
Quoted 7 lines
Add missing
__attribute_const__annotations to PowerPC's implementations offls()function. These are pure mathematical functions that always return the same result for the same input with no side effects, making them eligible for compiler optimization.Build tested ARCH=powerpc defconfig with GCC powerpc-linux-gnu 14.2.0.
Also tested with gcc 8.1.
Quoted 28 lines
Signed-off-by Kees Cook <[email protected]>--- arch/powerpc/include/asm/bitops.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)arch/powerpc/include/asm/bitops.h +2
-2@@ -276,7 +276,7 @@ static inline void arch___clear_bit_unlock(int nr, volatile unsigned long *addr)* fls: find last (most-significant) bit set. * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. */ -static __always_inline int fls(unsigned int x) +static __always_inline __attribute_const__ int fls(unsigned int x) {int lz;@@ -294,7 +294,7 @@ static __always_inline int fls(unsigned int x)* 32-bit fls calls. */ #ifdef CONFIG_PPC64 -static __always_inline int fls64(__u64 x) +static __always_inline __attribute_const__ int fls64(__u64 x) {int lz;
Re: [PATCH 05/17] powerpc: Add __attribute_const__ to ffs()-family implementations
Hi!
Quoted 1 line · Madhavan Srinivasan
making them eligible for compiler optimization.
You can instead use GCC for this. __builtin_ffs () exists since 2003, and has this attribute built-in, as well as tens of other optimisations that the kernel thing misses.
Of course using existing stuff instead of cobbling together something half working prevents you from having a lot of fun ;-)
Segher
Re: [PATCH 05/17] powerpc: Add __attribute_const__ to ffs()-family implementations
Quoted 4 lines · Segher Boessenkool
Quoted 1 line · Madhavan Srinivasan
making them eligible for compiler optimization.
You can instead use GCC for this.
__builtin_ffs() exists since 2003,
Erm, 1992 actually, but stuff has moved around since then :-)
Quoted 5 lines
and has this attribute built-in, as well as tens of other optimisations that the kernel thing misses.
Of course using existing stuff instead of cobbling together something half working prevents you from having a lot of fun ;-)
Segher
PATCH 6/17 sh: Add __attribute_const__ to ffs()-family implementations
In reply to Kees CookWhile tracking down a problem where constant expressions used by BUILD_BUG_ON() suddenly stopped working[1], we found that an added static initializer was convincing the compiler that it couldn't track the state of the prior statically initialized value. Tracing this down found that ffs() was used in the initializer macro, but since it wasn't marked with __attribute__const__, the compiler had to assume the function might change variable states as a side-effect (which is not true for ffs(), which provides deterministic math results).
Add missing __attribute_const__ annotations to SH's implementations of __ffs() and ffz() functions. These are pure mathematical functions that always return the same result for the same input with no side effects, making them eligible for compiler optimization.
Build tested ARCH=sh defconfig with GCC sh4-linux-gnu 14.2.0.
--- arch/sh/include/asm/bitops.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arch/sh/include/asm/bitops.h +2 -2
@@ -24,7 +24,7 @@#include <asm-generic/bitops/non-atomic.h> #endif -static inline unsigned long ffz (unsigned long word) +static inline unsigned long __attribute_const__ ffz (unsigned long word) {unsigned long result;
@@ -44,7 +44,7 @@ static inline unsigned long ffz(unsigned long word)* * Undefined if no bit exists, so code should check against 0 first. */ -static inline unsigned long __ffs (unsigned long word) +static inline __attribute_const__ unsigned long __ffs (unsigned long word) {unsigned long result;
Signature · 1 line
2.34.1
PATCH 7/17 alpha: Add __attribute_const__ to ffs()-family implementations
In reply to Kees CookWhile tracking down a problem where constant expressions used by BUILD_BUG_ON() suddenly stopped working[1], we found that an added static initializer was convincing the compiler that it couldn't track the state of the prior statically initialized value. Tracing this down found that ffs() was used in the initializer macro, but since it wasn't marked with __attribute__const__, the compiler had to assume the function might change variable states as a side-effect (which is not true for ffs(), which provides deterministic math results).
Add missing __attribute_const__ annotations to Alpha's implementations of __ffs(), ffs(), fls64(), __fls(), fls(), and ffz() functions. These are pure mathematical functions that always return the same result for the same input with no side effects, making them eligible for compiler optimization.
Build tested ARCH=alpha defconfig with GCC alpha-linux-gnu 14.2.0.
--- arch/alpha/include/asm/bitops.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
arch/alpha/include/asm/bitops.h +7 -7
@@ -328,7 +328,7 @@ static inline unsigned long ffz_b(unsigned long x)return sum; } -static inline unsigned long ffz (unsigned long word) +static inline unsigned long __attribute_const__ ffz (unsigned long word) {#if defined(CONFIG_ALPHA_EV6 ) && defined(CONFIG_ALPHA_EV67 )/* Whee. EV67 can calculate it directly. */
@@ -348,7 +348,7 @@ static inline unsigned long ffz(unsigned long word)/* * __ffs = Find First set bit in word. Undefined if no set bit exists. */ -static inline unsigned long __ffs (unsigned long word) +static inline __attribute_const__ unsigned long __ffs (unsigned long word) {#if defined(CONFIG_ALPHA_EV6 ) && defined(CONFIG_ALPHA_EV67 )/* Whee. EV67 can calculate it directly. */
@@ -373,7 +373,7 @@ static inline unsigned long __ffs(unsigned long word)* differs in spirit from the above __ffs. */ -static inline int ffs (int word) +static inline __attribute_const__ int ffs(int word) {int result =__ffs (word) +1 ;return word ? result :0 ;
@@ -383,14 +383,14 @@ static inline int ffs(int word)* fls: find last bit set. */ #if defined(CONFIG_ALPHA_EV6 ) && defined(CONFIG_ALPHA_EV67 ) -static inline int fls64 (unsigned long word) +static inline __attribute_const__ int fls64(unsigned long word) {return 64 -__kernel_ctlz (word); }#else extern const unsigned char __flsm1_tab[256 ]; -static inline int fls64 (unsigned long x) +static inline __attribute_const__ int fls64(unsigned long x) {unsigned long t, a, r;
@@ -403,12 +403,12 @@ static inline int fls64(unsigned long x) }#endif -static inline unsigned long __fls (unsigned long x) +static inline __attribute_const__ unsigned long __fls (unsigned long x) {return fls64 (x) -1 ; } -static inline int fls (unsigned int x) +static inline __attribute_const__ int fls(unsigned int x) {return fls64 (x); }
Signature · 1 line
2.34.1
PATCH 8/17 hexagon: Add __attribute_const__ to ffs()-family implementations
In reply to Kees CookWhile tracking down a problem where constant expressions used by BUILD_BUG_ON() suddenly stopped working[1], we found that an added static initializer was convincing the compiler that it couldn't track the state of the prior statically initialized value. Tracing this down found that ffs() was used in the initializer macro, but since it wasn't marked with __attribute__const__, the compiler had to assume the function might change variable states as a side-effect (which is not true for ffs(), which provides deterministic math results).
Add missing __attribute_const__ annotations to Hexagon's implementations of fls(), ffs(), __ffs(), __fls(), and ffz() functions. These are pure mathematical functions that always return the same result for the same input with no side effects, making them eligible for compiler optimization.
Build tested ARCH=hexagon defconfig with Clang 21.0.0git (LLVM=1).
--- arch/hexagon/include/asm/bitops.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
arch/hexagon/include/asm/bitops.h +5 -5
@@ -200,7 +200,7 @@ arch_test_bit_acquire(unsigned long nr, const volatile unsigned long *addr)* * Undefined if no zero exists, so code should check against ~0UL first. */ -static inline long ffz (int x) +static inline long __attribute_const__ ffz (int x) {int r;
@@ -217,7 +217,7 @@ static inline long ffz(int x)* This is defined the same way as ffs. * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. */ -static inline int fls (unsigned int x) +static inline __attribute_const__ int fls(unsigned int x) {int r;
@@ -238,7 +238,7 @@ static inline int fls(unsigned int x)* the libc and compiler builtin ffs routines, therefore * differs in spirit from the above ffz (man ffs). */ -static inline int ffs (int x) +static inline __attribute_const__ int ffs(int x) {int r;
@@ -260,7 +260,7 @@ static inline int ffs(int x)* bits_per_long assumed to be 32 * numbering starts at 0 I think (instead of 1 like ffs) */ -static inline unsigned long __ffs (unsigned long word) +static inline __attribute_const__ unsigned long __ffs (unsigned long word) {int num;
@@ -278,7 +278,7 @@ static inline unsigned long __ffs(unsigned long word)* Undefined if no set bit exists, so code should check against 0 first. * bits_per_long assumed to be 32 */ -static inline unsigned long __fls (unsigned long word) +static inline __attribute_const__ unsigned long __fls (unsigned long word) {int num;
Signature · 1 line
2.34.1
PATCH 9/17 riscv: Add __attribute_const__ to ffs()-family implementations
In reply to Kees CookWhile tracking down a problem where constant expressions used by BUILD_BUG_ON() suddenly stopped working[1], we found that an added static initializer was convincing the compiler that it couldn't track the state of the prior statically initialized value. Tracing this down found that ffs() was used in the initializer macro, but since it wasn't marked with __attribute__const__, the compiler had to assume the function might change variable states as a side-effect (which is not true for ffs(), which provides deterministic math results).
Add missing __attribute_const__ annotations to RISC-V's implementations of variable__ffs(), variable__fls(), and variable_ffs() functions. These are pure mathematical functions that always return the same result for the same input with no side effects, making them eligible for compiler optimization.
Build tested ARCH=riscv defconfig with GCC riscv64-linux-gnu 14.2.0.
--- arch/riscv/include/asm/bitops.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
arch/riscv/include/asm/bitops.h +3 -3
@@ -45,7 +45,7 @@#error "Unexpected BITS_PER_LONG"#endif -static __always_inline unsigned long variable__ffs (unsigned long word) +static __always_inline __attribute_const__ unsigned long variable__ffs(unsigned long word) { asm goto(ALTERNATIVE("j %l[legacy]", "nop", 0, RISCV_ISA_EXT_ZBB, 1)
@@ -74,7 +74,7 @@ static __always_inline unsigned long variable__ffs(unsigned long word) (unsigned long)__builtin_ctzl(word) : \ variable__ffs(word)) -static __always_inline unsigned long variable__fls (unsigned long word) +static __always_inline __attribute_const__ unsigned long variable__fls(unsigned long word) { asm goto(ALTERNATIVE("j %l[legacy]", "nop", 0, RISCV_ISA_EXT_ZBB, 1)
@@ -103,7 +103,7 @@ static __always_inline unsigned long variable__fls(unsigned long word) (unsigned long)(BITS_PER_LONG - 1 - __builtin_clzl(word)) : \ variable__fls(word)) -static __always_inline int variable_ffs(int x) +static __always_inline __attribute_const__ int variable_ffs(int x) { asm goto(ALTERNATIVE("j %l[legacy]", "nop", 0, RISCV_ISA_EXT_ZBB, 1)
Signature · 1 line
2.34.1
Re: [PATCH 09/17] riscv: Add __attribute_const__ to ffs()-family implementations
Hi Kees,
Quoted 45 lines · Kees Cook
While tracking down a problem where constant expressions used by
BUILD_BUG_ON()suddenly stopped working[1], we found that an added static initializer was convincing the compiler that it couldn't track the state of the prior statically initialized value. Tracing this down found thatffs()was used in the initializer macro, but since it wasn't marked with__attribute__const__, the compiler had to assume the function might change variable states as a side-effect (which is not true forffs(), which provides deterministic math results).Add missing
__attribute_const__annotations to RISC-V's implementations ofvariable__ffs(),variable__fls(), andvariable_ffs()functions. These are pure mathematical functions that always return the same result for the same input with no side effects, making them eligible for compiler optimization.Build tested ARCH=riscv defconfig with GCC riscv64-linux-gnu 14.2.0.
Signed-off-by Kees Cook <[email protected]>--- arch/riscv/include/asm/bitops.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)arch/riscv/include/asm/bitops.h +2
-2@@ -45,7 +45,7 @@#error "Unexpected BITS_PER_LONG"#endif -static __always_inline unsigned long variable__ffs (unsigned long word) +static __always_inline __attribute_const__ unsigned long variable__ffs(unsigned long word) { asm goto(ALTERNATIVE("j %l[legacy]", "nop", 0, RISCV_ISA_EXT_ZBB, 1)@@ -74,7 +74,7 @@ static __always_inline unsigned long variable__ffs(unsigned long word) (unsigned long)__builtin_ctzl(word) : \ variable__ffs(word)) -static __always_inline unsigned long variable__fls (unsigned long word) +static __always_inline __attribute_const__ unsigned long variable__fls(unsigned long word) { asm goto(ALTERNATIVE("j %l[legacy]", "nop", 0, RISCV_ISA_EXT_ZBB, 1)
arch/riscv/include/asm/bitops.h static __always_inline unsigned long variable__fls(unsigned long word) · line 109
Quoted 8 lines
arch/riscv/include/asm/bitops.h +1
-1@@ -103,7 +103,7 @@ static __always_inline unsigned long variable__fls(unsigned long word) (unsigned long)(BITS_PER_LONG - 1 - __builtin_clzl(word)) : \ variable__fls(word)) -static __always_inline int variable_ffs(int x) +static __always_inline __attribute_const__ int variable_ffs(int x) { asm goto(ALTERNATIVE("j %l[legacy]", "nop", 0, RISCV_ISA_EXT_ZBB, 1)
Thanks,
Alex
PATCH 10/17 openrisc: Add __attribute_const__ to ffs()-family implementations
In reply to Kees CookWhile tracking down a problem where constant expressions used by BUILD_BUG_ON() suddenly stopped working[1], we found that an added static initializer was convincing the compiler that it couldn't track the state of the prior statically initialized value. Tracing this down found that ffs() was used in the initializer macro, but since it wasn't marked with __attribute__const__, the compiler had to assume the function might change variable states as a side-effect (which is not true for ffs(), which provides deterministic math results).
Add missing __attribute_const__ annotations to OpenRISC's implementations of ffs(), __ffs(), fls(), and __fls() functions. These are pure mathematical functions that always return the same result for the same input with no side effects, making them eligible for compiler optimization.
Build tested ARCH=openrisc defconfig with GCC or1k-linux 15.1.0.
--- arch/openrisc/include/asm/bitops/__ffs.h | 2 +- arch/openrisc/include/asm/bitops/__fls.h | 2 +- arch/openrisc/include/asm/bitops/ffs.h | 2 +- arch/openrisc/include/asm/bitops/fls.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-)
arch/openrisc/include/asm/bitops/__ffs.h +1 -1
@@ -11,7 +11,7 @@#ifdef CONFIG_OPENRISC_HAVE_INST_FF1 -static inline unsigned long __ffs (unsigned long x) +static inline __attribute_const__ unsigned long __ffs (unsigned long x) {int ret;
arch/openrisc/include/asm/bitops/__fls.h +1 -1
@@ -11,7 +11,7 @@#ifdef CONFIG_OPENRISC_HAVE_INST_FL1 -static inline unsigned long __fls (unsigned long x) +static inline __attribute_const__ unsigned long __fls (unsigned long x) {int ret;
arch/openrisc/include/asm/bitops/ffs.h +1 -1
@@ -10,7 +10,7 @@#ifdef CONFIG_OPENRISC_HAVE_INST_FF1 -static inline int ffs (int x) +static inline __attribute_const__ int ffs(int x) {int ret;
arch/openrisc/include/asm/bitops/fls.h +1 -1
@@ -11,7 +11,7 @@#ifdef CONFIG_OPENRISC_HAVE_INST_FL1 -static inline int fls (unsigned int x) +static inline __attribute_const__ int fls(unsigned int x) {int ret;
Signature · 1 line
2.34.1
Re: [PATCH 10/17] openrisc: Add __attribute_const__ to ffs()-family implementations
Quoted 9 lines · Kees Cook
While tracking down a problem where constant expressions used by
BUILD_BUG_ON()suddenly stopped working[1], we found that an added static initializer was convincing the compiler that it couldn't track the state of the prior statically initialized value. Tracing this down found thatffs()was used in the initializer macro, but since it wasn't marked with__attribute__const__, the compiler had to assume the function might change variable states as a side-effect (which is not true forffs(), which provides deterministic math results).
Quoted 6 lines
Add missing
__attribute_const__annotations to OpenRISC's implementations offfs(),__ffs(),fls(), and__fls()functions. These are pure mathematical functions that always return the same result for the same input with no side effects, making them eligible for compiler optimization.Build tested ARCH=openrisc defconfig with GCC or1k-linux 15.1.0.
THis looks ok to me.
Quoted 2 lines
Signed-off-by Kees Cook <[email protected]>
Quoted 63 lines
--- arch/openrisc/include/asm/bitops/__ffs.h | 2 +- arch/openrisc/include/asm/bitops/__fls.h | 2 +- arch/openrisc/include/asm/bitops/ffs.h | 2 +- arch/openrisc/include/asm/bitops/fls.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-)arch/openrisc/include/asm/bitops/__ffs.h +1
-1@@ -11,7 +11,7 @@#ifdef CONFIG_OPENRISC_HAVE_INST_FF1 -static inline unsigned long __ffs (unsigned long x) +static inline __attribute_const__ unsigned long __ffs (unsigned long x) {int ret;arch/openrisc/include/asm/bitops/__fls.h +1
-1@@ -11,7 +11,7 @@#ifdef CONFIG_OPENRISC_HAVE_INST_FL1 -static inline unsigned long __fls (unsigned long x) +static inline __attribute_const__ unsigned long __fls (unsigned long x) {int ret;arch/openrisc/include/asm/bitops/ffs.h +1
-1@@ -10,7 +10,7 @@#ifdef CONFIG_OPENRISC_HAVE_INST_FF1 -static inline int ffs (int x) +static inline __attribute_const__ int ffs(int x) {int ret;arch/openrisc/include/asm/bitops/fls.h +1
-1@@ -11,7 +11,7 @@#ifdef CONFIG_OPENRISC_HAVE_INST_FL1 -static inline int fls (unsigned int x) +static inline __attribute_const__ int fls(unsigned int x) {int ret;Signature · 1 line
2.34.1
PATCH 11/17 m68k: Add __attribute_const__ to ffs()-family implementations
In reply to Kees CookWhile tracking down a problem where constant expressions used by BUILD_BUG_ON() suddenly stopped working[1], we found that an added static initializer was convincing the compiler that it couldn't track the state of the prior statically initialized value. Tracing this down found that ffs() was used in the initializer macro, but since it wasn't marked with __attribute__const__, the compiler had to assume the function might change variable states as a side-effect (which is not true for ffs(), which provides deterministic math results).
Add missing __attribute_const__ annotations to M68K's implementations of ffs(), __ffs(), fls(), __fls(), and ffz() functions. These are pure mathematical functions that always return the same result for the same input with no side effects, making them eligible for compiler optimization.
Build tested ARCH=m68k defconfig with GCC m68k-linux-gnu 14.2.0.
--- arch/m68k/include/asm/bitops.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
arch/m68k/include/asm/bitops.h +7 -7
@@ -465,7 +465,7 @@ static inline int find_next_bit(const unsigned long *vaddr, int size,* ffz = Find First Zero in word. Undefined if no zero exists, * so code should check against ~0UL first.. */ -static inline unsigned long ffz (unsigned long word) +static inline unsigned long __attribute_const__ ffz (unsigned long word) {int res;
@@ -488,7 +488,7 @@ static inline unsigned long ffz(unsigned long word)*/ #if (defined(__mcfisaaplus__) || defined(__mcfisac__)) && \ !defined(CONFIG_M68000 ) -static inline unsigned long __ffs (unsigned long x) +static inline __attribute_const__ unsigned long __ffs (unsigned long x) { __asm__ __volatile__ ("bitrev %0; ff1 %0" : "=d" (x)
@@ -496,7 +496,7 @@ static inline unsigned long __ffs(unsigned long x)return x; } -static inline int ffs (int x) +static inline __attribute_const__ int ffs(int x) {if (!x)return 0 ;
@@ -518,7 +518,7 @@ static inline int ffs(int x)* the libc and compiler builtin ffs routines, therefore * differs in spirit from the above ffz (man ffs). */ -static inline int ffs (int x) +static inline __attribute_const__ int ffs(int x) {int cnt;
@@ -528,7 +528,7 @@ static inline int ffs(int x)return 32 - cnt; } -static inline unsigned long __ffs (unsigned long x) +static inline __attribute_const__ unsigned long __ffs (unsigned long x) {return ffs (x) -1 ; }
@@ -536,7 +536,7 @@ static inline unsigned long __ffs(unsigned long x)/* * fls: find last bit set. */ -static inline int fls (unsigned int x) +static inline __attribute_const__ int fls(unsigned int x) {int cnt;
@@ -546,7 +546,7 @@ static inline int fls(unsigned int x)return 32 - cnt; } -static inline unsigned long __fls (unsigned long x) +static inline __attribute_const__ unsigned long __fls (unsigned long x) {return fls (x) -1 ; }
Signature · 1 line
2.34.1
Re: [PATCH 11/17] m68k: Add __attribute_const__ to ffs()-family implementations
Quoted 15 lines · Kees Cook
While tracking down a problem where constant expressions used by
BUILD_BUG_ON()suddenly stopped working[1], we found that an added static initializer was convincing the compiler that it couldn't track the state of the prior statically initialized value. Tracing this down found thatffs()was used in the initializer macro, but since it wasn't marked with__attribute__const__, the compiler had to assume the function might change variable states as a side-effect (which is not true forffs(), which provides deterministic math results).Add missing
__attribute_const__annotations to M68K's implementations offfs(),__ffs(),fls(),__fls(), andffz()functions. These are pure mathematical functions that always return the same result for the same input with no side effects, making them eligible for compiler optimization.
Quoted 4 lines
Build tested ARCH=m68k defconfig with GCC m68k-linux-gnu 14.2.0.
Signed-off-by Kees Cook <[email protected]>
# ffs: pass:9 fail:0 skip:0 total:9
Gr{oetje,eeting}s,
Geert
Signature · 5 lines
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected] In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds
PATCH 12/17 mips: Add __attribute_const__ to ffs()-family implementations
In reply to Kees CookWhile tracking down a problem where constant expressions used by BUILD_BUG_ON() suddenly stopped working[1], we found that an added static initializer was convincing the compiler that it couldn't track the state of the prior statically initialized value. Tracing this down found that ffs() was used in the initializer macro, but since it wasn't marked with __attribute__const__, the compiler had to assume the function might change variable states as a side-effect (which is not true for ffs(), which provides deterministic math results).
Add missing __attribute_const__ annotations to MIPS's implementations of ffs(), __ffs(), fls(), and __fls() functions. These are pure mathematical functions that always return the same result for the same input with no side effects, making them eligible for compiler optimization.
Build tested ARCH=mips defconfig with GCC mipsel-linux-gnu 14.2.0.
--- arch/mips/include/asm/bitops.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
arch/mips/include/asm/bitops.h +4 -4
@@ -327,7 +327,7 @@ static inline void __clear_bit_unlock(unsigned long nr, volatile unsigned long ** Return the bit position (0..63) of the most significant 1 bit in a word * Returns -1 if no 1 bit exists */ -static __always_inline unsigned long __fls (unsigned long word) +static __always_inline __attribute_const__ unsigned long __fls(unsigned long word) {int num;
@@ -393,7 +393,7 @@ static __always_inline unsigned long __fls(unsigned long word)* Returns 0..SZLONG-1 * Undefined if no bit exists, so code should check against 0 first. */ -static __always_inline unsigned long __ffs (unsigned long word) +static __always_inline __attribute_const__ unsigned long __ffs(unsigned long word) {return __fls (word & -word); }
@@ -405,7 +405,7 @@ static __always_inline unsigned long __ffs(unsigned long word)* This is defined the same way as ffs. * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. */ -static inline int fls (unsigned int x) +static inline __attribute_const__ int fls(unsigned int x) {int r;
@@ -458,7 +458,7 @@ static inline int fls(unsigned int x)* the libc and compiler builtin ffs routines, therefore * differs in spirit from the below ffz (man ffs). */ -static inline int ffs (int word) +static inline __attribute_const__ int ffs(int word) {if (!word)return 0 ;
Signature · 1 line
2.34.1
PATCH 13/17 parisc: Add __attribute_const__ to ffs()-family implementations
In reply to Kees CookWhile tracking down a problem where constant expressions used by BUILD_BUG_ON() suddenly stopped working[1], we found that an added static initializer was convincing the compiler that it couldn't track the state of the prior statically initialized value. Tracing this down found that ffs() was used in the initializer macro, but since it wasn't marked with __attribute__const__, the compiler had to assume the function might change variable states as a side-effect (which is not true for ffs(), which provides deterministic math results).
Add missing __attribute_const__ annotations to PARISC's implementations of ffs(), __ffs(), and fls() functions. These are pure mathematical functions that always return the same result for the same input with no side effects, making them eligible for compiler optimization.
Build tested ARCH=parisc defconfig with GCC hppa-linux-gnu 14.2.0.
--- arch/parisc/include/asm/bitops.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
arch/parisc/include/asm/bitops.h +3 -3
@@ -123,7 +123,7 @@ static __inline__ int test_and_change_bit(int nr, volatile unsigned long * addr)* cycles for each mispredicted branch. */ -static __inline__unsigned long __ffs (unsigned long x) +static __inline____attribute_const__ unsigned long __ffs (unsigned long x) {unsigned long ret;
@@ -161,7 +161,7 @@ static __inline__ unsigned long __ffs(unsigned long x)* This is defined the same way as the libc and compiler builtin * ffs routines, therefore differs in spirit from the above ffz (man ffs). */ -static __inline__int ffs (int x) +static __inline__ __attribute_const__ int ffs(int x) {return x ? (__ffs ((unsigned long )x) +1 ) :0 ; }
@@ -171,7 +171,7 @@ static __inline__ int ffs(int x)* fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. */ -static __inline__int fls (unsigned int x) +static __inline__ __attribute_const__ int fls(unsigned int x) {int ret;if (!x)
Signature · 1 line
2.34.1
Re: [PATCH 13/17] parisc: Add __attribute_const__ to ffs()-family implementations
Quoted 14 lines · Kees Cook
While tracking down a problem where constant expressions used by
BUILD_BUG_ON()suddenly stopped working[1], we found that an added static initializer was convincing the compiler that it couldn't track the state of the prior statically initialized value. Tracing this down found thatffs()was used in the initializer macro, but since it wasn't marked with__attribute__const__, the compiler had to assume the function might change variable states as a side-effect (which is not true forffs(), which provides deterministic math results).Add missing
__attribute_const__annotations to PARISC's implementations offfs(),__ffs(), andfls()functions. These are pure mathematical functions that always return the same result for the same input with no side effects, making them eligible for compiler optimization.
Quoted 7 lines
Build tested ARCH=parisc defconfig with GCC hppa-linux-gnu 14.2.0.
Signed-off-by Kees Cook <[email protected]>--- arch/parisc/include/asm/bitops.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
PATCH 14/17 s390: Add __attribute_const__ to ffs()-family implementations
In reply to Kees CookWhile tracking down a problem where constant expressions used by BUILD_BUG_ON() suddenly stopped working[1], we found that an added static initializer was convincing the compiler that it couldn't track the state of the prior statically initialized value. Tracing this down found that ffs() was used in the initializer macro, but since it wasn't marked with __attribute__const__, the compiler had to assume the function might change variable states as a side-effect (which is not true for ffs(), which provides deterministic math results).
Add missing __attribute_const__ annotations to S390's implementations of ffs(), __ffs(), fls(), and __fls() functions. These are pure mathematical functions that always return the same result for the same input with no side effects, making them eligible for compiler optimization.
Build tested ARCH=s390 defconfig with GCC s390x-linux-gnu 14.2.0.
--- arch/s390/include/asm/bitops.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
arch/s390/include/asm/bitops.h +5 -5
@@ -179,7 +179,7 @@ static inline unsigned char __flogr(unsigned long word)* * Undefined if no bit exists, so code should check against 0 first. */ -static inline unsigned long __ffs (unsigned long word) +static inline __attribute_const__ unsigned long __ffs (unsigned long word) {return __flogr (-word & word) ^ (BITS_PER_LONG -1 ); }
@@ -191,7 +191,7 @@ static inline unsigned long __ffs(unsigned long word)* This is defined the same way as the libc and * compiler builtin ffs routines (man ffs). */ -static inline int ffs (int word) +static inline __attribute_const__ int ffs(int word) {unsigned long mask =2 *BITS_PER_LONG -1 ;unsigned int val = (unsigned int )word;
@@ -205,7 +205,7 @@ static inline int ffs(int word)* * Undefined if no set bit exists, so code should check against 0 first. */ -static inline unsigned long __fls (unsigned long word) +static inline __attribute_const__ unsigned long __fls (unsigned long word) {return __flogr (word) ^ (BITS_PER_LONG -1 ); }
@@ -221,7 +221,7 @@ static inline unsigned long __fls(unsigned long word)* set bit if value is nonzero. The last (most significant) bit is * at position 64. */ -static inline int fls64 (unsigned long word) +static inline __attribute_const__ int fls64(unsigned long word) {unsigned long mask =2 *BITS_PER_LONG -1 ;
@@ -235,7 +235,7 @@ static inline int fls64(unsigned long word)* This is defined the same way as ffs. * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. */ -static inline int fls (unsigned int word) +static inline __attribute_const__ int fls(unsigned int word) {return fls64 (word); }
Signature · 1 line
2.34.1
Re: [PATCH 14/17] s390: Add __attribute_const__ to ffs()-family implementations
Quoted 14 lines · Kees Cook
While tracking down a problem where constant expressions used by
BUILD_BUG_ON()suddenly stopped working[1], we found that an added static initializer was convincing the compiler that it couldn't track the state of the prior statically initialized value. Tracing this down found thatffs()was used in the initializer macro, but since it wasn't marked with__attribute__const__, the compiler had to assume the function might change variable states as a side-effect (which is not true forffs(), which provides deterministic math results).Add missing
__attribute_const__annotations to S390's implementations offfs(),__ffs(),fls(), and__fls()functions. These are pure mathematical functions that always return the same result for the same input with no side effects, making them eligible for compiler optimization.
Quoted 7 lines
Build tested ARCH=s390 defconfig with GCC s390x-linux-gnu 14.2.0.
Signed-off-by Kees Cook <[email protected]>--- arch/s390/include/asm/bitops.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
PATCH 15/17 xtensa: Add __attribute_const__ to ffs()-family implementations
In reply to Kees CookWhile tracking down a problem where constant expressions used by BUILD_BUG_ON() suddenly stopped working[1], we found that an added static initializer was convincing the compiler that it couldn't track the state of the prior statically initialized value. Tracing this down found that ffs() was used in the initializer macro, but since it wasn't marked with __attribute__const__, the compiler had to assume the function might change variable states as a side-effect (which is not true for ffs(), which provides deterministic math results).
Add missing __attribute_const__ annotations to Xtensa's implementations of ffs(), __ffs(), fls(), __fls(), ffz() functions. These are pure mathematical functions that always return the same result for the same input with no side effects, making them eligible for compiler optimization.
Build tested ARCH=xtensa defconfig with GCC xtensa-linux 15.1.0.
--- arch/xtensa/include/asm/bitops.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
arch/xtensa/include/asm/bitops.h +5 -5
@@ -37,7 +37,7 @@ static inline unsigned long __cntlz (unsigned long x)* bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1). */ -static inline int ffz (unsigned long x) +static inline int __attribute_const__ ffz(unsigned long x) {return 31 -__cntlz (~x & -~x); }
@@ -46,7 +46,7 @@ static inline int ffz(unsigned long x)* __ffs: Find first bit set in word. Return 0 for bit 0 */ -static inline unsigned long __ffs (unsigned long x) +static inline __attribute_const__ unsigned long __ffs (unsigned long x) {return 31 -__cntlz (x & -x); }
@@ -57,7 +57,7 @@ static inline unsigned long __ffs(unsigned long x)* differs in spirit from the above ffz (man ffs). */ -static inline int ffs (unsigned long x) +static inline __attribute_const__ int ffs(unsigned long x) {return 32 -__cntlz (x & -x); }
@@ -67,7 +67,7 @@ static inline int ffs(unsigned long x)* Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32. */ -static inline int fls (unsigned int x) +static inline __attribute_const__ int fls (unsigned int x) {return 32 -__cntlz (x); }
@@ -78,7 +78,7 @@ static inline int fls (unsigned int x)* * Undefined if no set bit exists, so code should check against 0 first. */ -static inline unsigned long __fls (unsigned long word) +static inline __attribute_const__ unsigned long __fls (unsigned long word) {return 31 -__cntlz (word); }
Signature · 1 line
2.34.1
PATCH 16/17 sparc: Add __attribute_const__ to ffs()-family implementations
In reply to Kees CookWhile tracking down a problem where constant expressions used by BUILD_BUG_ON() suddenly stopped working[1], we found that an added static initializer was convincing the compiler that it couldn't track the state of the prior statically initialized value. Tracing this down found that ffs() was used in the initializer macro, but since it wasn't marked with __attribute__const__, the compiler had to assume the function might change variable states as a side-effect (which is not true for ffs(), which provides deterministic math results).
Add missing __attribute_const__ annotations to sparc64's implementations of ffs(), __ffs(), fls(), and __fls() functions. These are pure mathematical functions that always return the same result for the same input with no side effects, making them eligible for compiler optimization.
Build tested ARCH=sparc defconfig with GCC sparc64-linux-gnu 14.2.0.
--- arch/sparc/include/asm/bitops_64.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
arch/sparc/include/asm/bitops_64.h +4 -4
@@ -23,8 +23,8 @@ void set_bit(unsigned long nr, volatile unsigned long *addr);void clear_bit (unsigned long nr,volatile unsigned long *addr);void change_bit (unsigned long nr,volatile unsigned long *addr); -int fls (unsigned int word); -int __fls (unsigned long word); +int __attribute_const__ fls(unsigned int word); +int __attribute_const__ __fls(unsigned long word);#include <asm-generic/bitops/non-atomic.h>
@@ -32,8 +32,8 @@ int __fls(unsigned long word);#ifdef __KERNEL__ -int ffs (int x); -unsigned long __ffs (unsigned long ); +int __attribute_const__ ffs(int x); +unsigned long __attribute_const__ __ffs (unsigned long );#include <asm-generic/bitops/ffz.h> #include <asm-generic/bitops/sched.h>
Signature · 1 line
2.34.1
PATCH 17/17 KUnit: ffs: Validate all the __attribute_const__ annotations
In reply to Kees CookWhile tracking down a problem where constant expressions used by BUILD_BUG_ON() suddenly stopped working[1], we found that an added static initializer was convincing the compiler that it couldn't track the state of the prior statically initialized value. Tracing this down found that ffs() was used in the initializer macro, but since it wasn't marked with __attribute_const__, the compiler had to assume the function might change variable states as a side-effect (which is not true for ffs(), which provides deterministic math results).
Validate all the __attibute_const__ annotations were found for all architectures by reproducing the specific problem encountered in the original bug report.
Build and run tested with everything I could reach with KUnit:
$ ./tools/testing/kunit/kunit.py run --arch=x86_64 ffs $ ./tools/testing/kunit/kunit.py run --arch=i386 ffs $ ./tools/testing/kunit/kunit.py run --arch=arm64 --make_options "CROSS_COMPILE=aarch64-linux-gnu-" ffs $ ./tools/testing/kunit/kunit.py run --arch=arm --make_options "CROSS_COMPILE=arm-linux-gnueabi-" ffs $ ./tools/testing/kunit/kunit.py run --arch=powerpc ffs $ ./tools/testing/kunit/kunit.py run --arch=powerpc32 ffs $ ./tools/testing/kunit/kunit.py run --arch=powerpcle ffs $ ./tools/testing/kunit/kunit.py run --arch=m68k ffs $ ./tools/testing/kunit/kunit.py run --arch=loongarch ffs $ ./tools/testing/kunit/kunit.py run --arch=s390 --make_options "CROSS_COMPILE=s390x-linux-gnu-" ffs $ ./tools/testing/kunit/kunit.py run --arch=riscv --make_options "CROSS_COMPILE=riscv64-linux-gnu-" ffs $ ./tools/testing/kunit/kunit.py run --arch=riscv32 --make_options "CROSS_COMPILE=riscv64-linux-gnu-" ffs $ ./tools/testing/kunit/kunit.py run --arch=sparc --make_options "CROSS_COMPILE=sparc64-linux-gnu-" ffs $ ./tools/testing/kunit/kunit.py run --arch=sparc64 --make_options "CROSS_COMPILE=sparc64-linux-gnu-" ffs $ ./tools/testing/kunit/kunit.py run --arch=alpha --make_options "CROSS_COMPILE=alpha-linux-gnu-" ffs $ ./tools/testing/kunit/kunit.py run --arch=sh --make_options "CROSS_COMPILE=sh4-linux-gnu-" ffs
--- lib/tests/ffs_kunit.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+)
lib/tests/ffs_kunit.c +40 -0
@@ -496,6 +496,46 @@ static void ffz_edge_cases_test(struct kunit *test) } } +/* +* To have useful build error output, split the tests into separate +* functions so it's clear which are missing __attribute_const__. +*/ +#define CREATE_WRAPPER (func) \ +static noinline bool build_test_##func(void) \ +{ \ + int init_##func = 32; \ + int result_##func = func(6); \ + \ +/* Does the static initializer vanish after calling func? */ \ + BUILD_BUG_ON(init_##func < 32); \ + \ +/* "Consume" the results so optimizer doesn't drop them. */ \ + barrier_data(&init_##func); \ + barrier_data(&result_##func); \ + \ + return true; \ +} +CREATE_WRAPPER (ffs) +CREATE_WRAPPER (fls ) +CREATE_WRAPPER (__ffs) +CREATE_WRAPPER (__fls) +CREATE_WRAPPER (ffz) +#undef CREATE_WRAPPER + +/* +* Make sure that __attribute_const__ has be applied to all the +* functions. This is a regression test for: +* https://github.com/KSPP/linux/issues/364 +*/ +static void ffs_attribute_const_test(struct kunit *test) +{ +KUNIT_EXPECT_TRUE (test,build_test_ffs ()); +KUNIT_EXPECT_TRUE (test,build_test_fls ()); +KUNIT_EXPECT_TRUE (test,build_test___ffs ()); +KUNIT_EXPECT_TRUE (test,build_test___fls ()); +KUNIT_EXPECT_TRUE (test,build_test_ffz ()); +} +/* * KUnit test case definitions */
Signature · 1 line
2.34.1
Quoted 29 lines · Kees Cook
Hi,
While tracking down a problem where constant expressions used by
BUILD_BUG_ON()suddenly stopped working[1], we found that an added static initializer was convincing the compiler that it couldn't track the state of the prior statically initialized value. Tracing this down found thatffs()was used in the initializer macro, but since it wasn't marked with__attribute_const__, the compiler had to assume the function might change variable states as a side-effect (which is not true forffs(), which provides deterministic math results).Add KUnit tests for the family of functions and then add
__attribute_const__to all architecture implementations and wrappers.-Kees
[1] https://github.com/KSPP/linux/issues/364
Kees Cook (17):
KUnit: Introduce ffs()-family tests
bitops: Add__attribute_const__to generic ffs()-family
implementations
csky: Add__attribute_const__to ffs()-family implementations
x86: Add__attribute_const__to ffs()-family implementations
powerpc: Add__attribute_const__to ffs()-family implementations
sh: Add__attribute_const__to ffs()-family implementations
alpha: Add__attribute_const__to ffs()-family implementations
hexagon: Add__attribute_const__to ffs()-family implementations
riscv: Add__attribute_const__to ffs()-family implementations
Quoted 8 lines
openrisc: Add
__attribute_const__to ffs()-family implementations
m68k: Add__attribute_const__to ffs()-family implementations
mips: Add__attribute_const__to ffs()-family implementations
parisc: Add__attribute_const__to ffs()-family implementations
s390: Add__attribute_const__to ffs()-family implementations
xtensa: Add__attribute_const__to ffs()-family implementations
sparc: Add__attribute_const__to ffs()-family implementations
KUnit: ffs: Validate all the__attribute_const__annotations
This series was applied to riscv/linux.git (for-next) by Kees Cook <[email protected]>:
Hello:
This series was applied to riscv/linux.git (for-next) by Kees Cook <[email protected]>:
Quoted 11 lines · On Mon, 4 Aug 2025 09:43:56 -0700 you wrote:
Hi,
While tracking down a problem where constant expressions used by
BUILD_BUG_ON()suddenly stopped working[1], we found that an added static initializer was convincing the compiler that it couldn't track the state of the prior statically initialized value. Tracing this down found thatffs()was used in the initializer macro, but since it wasn't marked with__attribute_const__, the compiler had to assume the function might change variable states as a side-effect (which is not true forffs(), which provides deterministic math results).
Quoted 1 line
[...]
Here is the summary with links:
- [01/17] KUnit: Introduce ffs()-family tests
(no matching commit)
- [02/17] bitops: Add __attribute_const__ to generic ffs()-family implementations (no matching commit)
- [03/17] csky: Add __attribute_const__ to ffs()-family implementations (no matching commit)
- [04/17] x86: Add __attribute_const__ to ffs()-family implementations (no matching commit)
- [05/17] powerpc: Add __attribute_const__ to ffs()-family implementations (no matching commit)
- [06/17] sh: Add __attribute_const__ to ffs()-family implementations (no matching commit)
- [07/17] alpha: Add __attribute_const__ to ffs()-family implementations (no matching commit)
- [08/17] hexagon: Add __attribute_const__ to ffs()-family implementations (no matching commit)
- [09/17] riscv: Add __attribute_const__ to ffs()-family implementations
https://git.kernel.org/riscv/c/c51c26e687a6
- [10/17] openrisc: Add __attribute_const__ to ffs()-family implementations (no matching commit)
- [11/17] m68k: Add __attribute_const__ to ffs()-family implementations (no matching commit)
- [12/17] mips: Add __attribute_const__ to ffs()-family implementations (no matching commit)
- [13/17] parisc: Add __attribute_const__ to ffs()-family implementations (no matching commit)
- [14/17] s390: Add __attribute_const__ to ffs()-family implementations (no matching commit)
- [15/17] xtensa: Add __attribute_const__ to ffs()-family implementations (no matching commit)
- [16/17] sparc: Add __attribute_const__ to ffs()-family implementations (no matching commit)
- [17/17] KUnit: ffs: Validate all the __attribute_const__ annotations (no matching commit)
You are awesome, thank you!
Signature · 2 lines
Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html