Hello,

Apologies in advance if this is not proper;
I never had submissions to this mailing list before.

GCC miscompiles the attached reduced C++ program at -O2: it emits an aligned 16-byte SIMD store (movaps) to a provably-unaligned address, causing a SIGSEGV
at runtime. The code is valid (no UB) and works on GCC 14, Clang, GCC -O3,
and -O2 -fno-tree-vectorize.

I prepared a demo on compiler-explorer (https://godbolt.org/z/qcMre9j56)

ENVIRONMENT
  GCC version : 16.2.0 (Debian 16.2.0-1)  [also reproduced on 15.2.0-17]
  System      : x86_64-linux-gnu, Linux, x86_64
  Configure   : --prefix=/usr --with-pkgversion='Debian 16.2.0-1'
                --enable-threads=posix --enable-default-pie
                --enable-checking=release --build=x86_64-linux-gnu
                --host=x86_64-linux-gnu --target=x86_64-linux-gnu

COMPILE COMMAND
  g++ -std=c++23 -O2 repro.cpp

EXPECTED BEHAVIOUR
  Prints "done" and exits 0. It ia a reduced version from a library, so it
  is left without any meaningful functionality.

ACTUAL BEHAVIOUR
  SIGSEGV. Faulting instruction (gdb):
      movaps %xmm0,(%rdx)
  with %rdx % 16 == 12, i.e. a 16-byte-aligned store to an address that is
  never 16-byte aligned. Backtrace: Builder::build at the leaf padding-fill
  loop, called from main().

OBSERVED MATRIX
  g++-14 -O2                       : OK
  g++-15 -O2                       : SIGSEGV
  g++-16 -O2                       : SIGSEGV
  clang++ -O2                      : OK
  g++-16 -O3                       : OK
  g++-16 -O2 -fno-tree-vectorize   : OK
  g++-16 -O2 -D_GLIBCXX_ASSERTIONS : SIGSEGV

```cpp
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <span>
#include <vector>
#include <algorithm>

static constexpr std::size_t D = 3;
static constexpr std::size_t LEAF = 32;
static constexpr float INF = 1e15f;

struct LeafBucket {
    alignas(64) float coords[D][LEAF];
    uint32_t ids[LEAF];
};

struct FatPoint {
    float coords[D];
    unsigned payload_id;
};

struct Builder {
    std::span<FatPoint> temp_pts;
    std::span<float> vals;
    std::span<std::uint64_t> dims;
    std::span<LeafBucket> buckets;
    std::size_t B;

    void build(std::size_t start, std::size_t end, std::size_t node_idx, std::size_t current_buckets) {
        const std::size_t size = end - start;
        if (size == 0) return;
        if (node_idx >= B - 1) {
            const std::size_t bucket_idx = node_idx - (B - 1);
            for (std::size_t i = 0; i < size; ++i) {
                for (std::size_t d = 0; d < D; ++d)
                    buckets[bucket_idx].coords[d][i] = temp_pts[start + i].coords[d];                 buckets[bucket_idx].ids[i] = temp_pts[start + i].payload_id;
            }
            // Padding-fill loop below is the faulting loop.
            for (std::size_t i = size; i < LEAF; ++i) {
                for (std::size_t d = 0; d < D; ++d)
                    buckets[bucket_idx].coords[d][i] = INF;
                buckets[bucket_idx].ids[i] = 0xFFFFFFFFu;
            }
            return;
        }
        const std::size_t mid = start + current_buckets;
        std::nth_element(temp_pts.begin() + start, temp_pts.begin() + mid, temp_pts.begin() + end,             [](const FatPoint& a, const FatPoint& b){ return a.coords[0] < b.coords[0]; });
        build(start, mid, 2 * node_idx + 1, 0);
        build(mid, end, 2 * node_idx + 2, 0);
    }
};

int main() {
    std::vector<LeafBucket> buckets(1);
    std::vector<FatPoint> pts(3);
    std::vector<float> vals(0);
    std::vector<std::uint64_t> dims(0);
    for (std::size_t i = 0; i < 3; ++i)
        for (std::size_t d = 0; d < D; ++d) pts[i].coords[d] = (float)i;
    Builder b{pts, vals, dims, buckets, 1};
    b.build(0, 3, 0, 1);
    std::printf("done\n");
    return 0;
}
```

Reply via email to