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

Reply via email to