... iterates on the set bits in girq24_result (there is a similar loop below for girq25_result) by using __builtin_ctz to avoid checking each bit.
This compiles into rbit and clz.
clz is specified as returning 32 when no bits are set, e.g. when the value is 0.
In contrast, __builtin_c[lt]z are specified as undefined when the value is 0.
In short, it's relying on undefined behavior!
In GCC 10, we got by with a pass; the code compiled into rbit and clz, the comparison was kept, and everything chugged along happily.
With GCC 11, the optimizer takes advantage of this undefined behavior to remove the comparison (after all: there are not 33 bits in a uint32_t, and the return value after we've cleared all the bits, i.e. x==0, is undefined).
A firmware image built with GCC 11 or higher will fail to start the AP due to a watchdog timeout processing an ESPI interrupt.
It may be worthwhile to add a note somewhere to this effect.
Why?
The loop here ... https://github.com/FrameworkComputer/EmbeddedController/blob/c1d06ea51e1c4021392f1c3450fbf4701cb85c74/chip/mchp/espi.c#L1099-L1106
... iterates on the set bits in
girq24_result
(there is a similar loop below forgirq25_result
) by using__builtin_ctz
to avoid checking each bit.This compiles into
rbit
andclz
.clz
is specified as returning32
when no bits are set, e.g. when the value is 0.In contrast,
__builtin_c[lt]z
are specified as undefined when the value is 0.In short, it's relying on undefined behavior!
In GCC 10, we got by with a pass; the code compiled into
rbit
andclz
, the comparison was kept, and everything chugged along happily.With GCC 11, the optimizer takes advantage of this undefined behavior to remove the comparison (after all: there are not 33 bits in a
uint32_t
, and the return value after we've cleared all the bits, i.e.x==0
, is undefined).Compiled code for espi_mswv1_interrupt, lightly annotated
``` 000e7e90This results in an infinite loop with
bpos == 32
, and the following message printed to the console until the watchdog kicks us out: