https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113665
Bug ID: 113665
Summary: Regular for Loop results in Endless Loop with -O2
Product: gcc
Version: 11.4.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c++
Assignee: unassigned at gcc dot gnu.org
Reporter: christoph at muppetnet dot net
Target Milestone: ---
Compiling the example code below with GCC 11.4.0 (I actually encountered this
bug in all versions >= 11 which I tried but not in older ones) and -O2 (other
optimization levels seem to work) results in an endless loop and finally a
segmentation fault due to an out-of-bounds access.
We can clearly argue about issues in the implementation of the bitfield class
(it is based on a real implementation of such a class I encountered in existing
codebase) but the main problem here is that the simple for loop in bug() is
generated into an endless loop. I think regardless of how the test method is
implemented, this loop should always terminate correctly.
#include <cstdint>
#include <cstddef>
#include <array>
#include <climits>
template<size_t S>
class bitfield
{
private:
using Element = uint8_t;
static constexpr uint32_t C = (S - 1u) / 8u;
private:
std::array<Element, C + 1> m_Array;
public:
bool test(size_t i) const
{
if (i >= S){
return false;
}
const size_t index = static_cast<uint32_t>(i) / 8u;
const Element bitmask = 1u << (i % 8u);
return (m_Array[index] & bitmask) != 0u;
}
};
void bug2(const bitfield<0x120u> &b)
{
if (!b.test(1u))
{
volatile int test = 1;
}
}
void bug(const bitfield<0x250u> &b)
{
for (uint16_t i = 0u; i < 0x250u; i++)
{
// this loop seems to not properly terminate
if (!b.test(i))
{
volatile int test = i;
}
}
}
int main()
{
bitfield<0x250u> b;
bug(b);
return 0;
}
Looking at the generated assembler code (in this example ARM64 generated in
godbolt.org, the same issue is also present on my x86-64 machine) we can see,
that the check for i < 0x250 is completely lost. Actually, at .L9 we do the
increment of the loop variable and then unconditionally jump back to .L10 for
the next iteration.
bug(bitfield<592ul> const&):
sub sp, sp, #16
mov w2, 0
mov w4, 1
.L10:
lsr w3, w2, 3
and w1, w2, 7
lsl w1, w4, w1
ldrb w3, [x0, w3, uxtw]
and w1, w1, w3
tst w1, 255
bne .L9
str w2, [sp, 12]
.L9:
add w2, w2, 1
b .L10
During testing of different variants of the code I encountered that there seem
to be different (but totally unexpected) ways to solve the issue:
- adding -fno-tree-vrp or -fno-guess-branch-probability
- deleting method bug2
- changing the type of i to size_t