zephyr: Spurious `warning: division by zero is undefined` on `K_THREAD_DEFINE` when building with clang
Describe the bug
Hi, bumped into this compiler warning on K_THREAD_DEFINE since https://github.com/zephyrproject-rtos/zephyr/pull/62900 got merged. One of our project that is building for an stm32 with LLVM started failing with a compiler warning:
/mnt/host/source/src/platform/ec/zephyr/shim/src/tasks.c:51:1251: error: division by zero is undefined [-Werror,-Wdivision-by-zero]
struct z_thread_stack_element __attribute__((section("." "noinit" "." "\"EC_BASE/ec/zephyr/shim/src/tasks.c\"" "." "21"))) __attribute__((__aligned__(((((0x40) > (0)) ? (0x40) : (0)))))) _k_thread_stack_RWSIG[(((((unsigned long)(1536) + ((unsigned long)(8) - 1)) / (unsigned long)(8)) * (unsigned long)(8)) + ((size_t)0x40))]; struct k_thread _k_thread_obj_RWSIG; __attribute__((__aligned__(__alignof(struct _static_thread_data)))) struct _static_thread_data _k_thread_data_RWSIG __attribute__((section("." "__static_thread_data" "." "static" "." "_k_thread_data_RWSIG_"))) __attribute__((__used__)) = { .init_thread = (&_k_thread_obj_RWSIG), .init_stack = (_k_thread_stack_RWSIG), .init_stack_size = (1536), .init_entry = (k_thread_entry_t)rwsig_task, .init_p1 = (void *)0, .init_p2 = (void *)((void*)0), .init_p3 = (void *)((void*)0), .init_prio = ((EC_TASK_PRIO_COUNT - EC_TASK_RWSIG_PRIO - 1)), .init_options = (0), .init_delay = ((k_timeout_t) { .ticks = (((-1)) == (-1) ? ((k_ticks_t) -1) : ((k_ticks_t)((1) ? ( ((10000) == (1000)) ? (uint64_t) (((((-1)) > (0)) ? ((-1)) : (0))) : ((1000) > (10000) && (1000) % (10000) == 0U) ? ((uint64_t) (((((-1)) > (0)) ? ((-1)) : (0))) + ((0) ? ((1000) / (10000)) / 2 : (1) ? ((1000) / (10000)) - 1 : 0)) / ((1000) / (10000)) : ((10000) > (1000) && (10000) % (1000) == 0U) ? (uint64_t) (((((-1)) > (0)) ? ((-1)) : (0)))*((10000) / (1000)) : ((((((365 * 24ULL * 3600ULL * 1000) + (4294967295U) - 1) / (4294967295U)) * 10000) <= 4294967295U) ? (((uint64_t) (((((-1)) > (0)) ? ((-1)) : (0)))*(10000) + ((0) ? (1000) / 2 : (1) ? (1000) - 1 : 0)) / (1000)) : (((uint64_t) (((((-1)) > (0)) ? ((-1)) : (0))) / (1000))*(10000) + (((uint64_t) (((((-1)) > (0)) ? ((-1)) : (0))) % (1000))*(10000) + ((0) ? (1000) / 2 : (1) ? (1000) - 1 : 0)) / (1000))) ) : (((uint64_t) (((((-1)) > (0)) ? ((-1)) : (0))) / (1000))*(10000) + (((uint64_t) (((((-1)) > (0)) ? ((-1)) : (0))) % (1000))*(10000) + ((0) ? (1000) / 2 : (1) ? (1000) - 1 : 0)) / (1000)) ))) }), .init_name = "RWSIG", }; const k_tid_t RWSIG = (k_tid_t)&_k_thread_obj_RWSIG;
^ ~~~~~~~~~~~~~~~~~~
The cause seems to be the introduction of SYS_TIMEOUT_MS in Z_THREAD_INITIALIZER, the macro eventually expands into some call of z_tmcvt_64 which includes a / (Z_HZ_ms / Z_HZ_cyc), that becomes a / 0 in most tickless setups since Z_HZ_ms = 1000 and Z_HZ_cyc = CONFIG_SYS_CLOCK_TICKS_PER_SEC = 10000. The ternary picks the valid value and GCC is happy with that, but clang seems to be firing a warning.
I managed to reproduce this with this example:
#include <stdio.h>
int ticks = 0 ? 1 / 0 : 0 ;
int main(int argc, char **argv)
{
printf("%d\n", ticks);
}
$ gcc -Wall -o test test.c
$ clang -Wall -o test test.c
test.c:3:19: warning: division by zero is undefined [-Wdivision-by-zero]
int ticks = 0 ? 1 / 0 : 0 ;
^ ~
1 warning generated.
Note that for some reason the warning does not fire if the condition is moved inside the function.
The CI did not catch it because the clang build that we run is all for native_posix and runs with SYS_CLOCK_TICKS_PER_SEC = 100, but this can be reproduced upstream fairly easily with:
$ export ZEPHYR_TOOLCHAIN_VARIANT=llvm
$ west build -p -b native_posix_64 samples/basic/threads -DCONFIG_SYS_CLOCK_TICKS_PER_SEC=10000
...
/usr/local/google/home/fabiobaltieri/zephyrproject/zephyr/samples/basic/threads/src/main.c:111:1: warning: division by zero is undefined [-Wdivision-by-zero]
K_THREAD_DEFINE(blink0_id, STACKSIZE, blink0, NULL, NULL, NULL,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
...
To get around this in our application we ended up selectively disabling the warning (https://chromium-review.googlesource.com/4907933), but that’s obviously not a scalable workaround. Unfortunately the way our CI is setup does not give us much visibility into it, but there’s probably many users compling with clang for tickless systems and I think this should be looked into before the release.
I tried to look into the https://github.com/zephyrproject-rtos/zephyr/blob/main/include/zephyr/sys/time_units.h definitions but can’t quite see a decent workaround, looking for more ideas around this.
To Reproduce
$ export ZEPHYR_TOOLCHAIN_VARIANT=llvm
$ west build -p -b native_posix_64 samples/basic/threads -DCONFIG_SYS_CLOCK_TICKS_PER_SEC=10000
Expected behavior No warning
Impact Build fail if warning as treated as errors.
Environment (please complete the following information):
- OS: Linux
- Toolchain: clang 14.0.6 and 17.0.0
About this issue
- Original URL
- State: closed
- Created 9 months ago
- Comments: 15 (10 by maintainers)
Commits related to this issue
- sys/time_units.h: cap z_tmcvt_int_div_64 divisor to 1 The "/ (__from_hz) / (__to_hz)" element of z_tmcvt_int_div_64 often result in a "/ 0" on tickless systems, which results in a compiler warning wh... — committed to fabiobaltieri/zephyr by fabiobaltieri 9 months ago
- sys/time_units.h: cap z_tmcvt_int_div_64 divisor to 1 The "/ (__from_hz) / (__to_hz)" element of z_tmcvt_int_div_64 often result in a "/ 0" on tickless systems, which results in a compiler warning wh... — committed to fabiobaltieri/zephyr by fabiobaltieri 9 months ago
- sys/time_units.h: cap z_tmcvt_int_div_xx divisor to 1 The "/ (__from_hz) / (__to_hz)" element of z_tmcvt_int_div_xx often result in a "/ 0" on tickless systems, which results in a compiler warning wh... — committed to fabiobaltieri/zephyr by fabiobaltieri 9 months ago
Nope - we just earned a “confirmed” label - see https://github.com/llvm/llvm-project/issues/68339
Nice.
I’d bet they’ll come back with chapter-and-verse from the standard explaining why the compiler should work this way. That’s the usual result of filing compiler issues.
As far as I can determine, the compiler is complaining about a divide-by-zero in a branch which will not be taken.
Take a look at this code:
I’ve submitted a PR which works around the warning by using 1 when the value would be zero. It cannot affect the result because this code is in a conditional branch where from_hz (1000) is strictly greater than to_hz (10000).
The warning is back. Bigger hammer submitted: