Skip to content

Avoid GCC -Warray-bounds false positive on fixed-size shape copies - #2946

Merged
JohanMabille merged 1 commit into
xtensor-stack:masterfrom
gouarin:fix-array-bounds
Nov 2, 2026
Merged

JohanMabille merged 1 commit into
xtensor-stack:masterfrom
gouarin:fix-array-bounds

Conversation

@gouarin

@gouarin gouarin commented Oct 29, 2026 •

Copy link
Copy Markdown
Member

Description

GCC 13 and 15 emit -Warray-bounds at -O2/-O3 on ordinary code using fixed-rank containers, for example assigning 0.5 * (even + odd) to a 1-D xtensor view. The warning comes from a std::copy between two std::array<std::size_t, 1> when the source is a member at a non-zero offset. GCC does not fold the length of the range and warns on the memmove branch that libstdc++ keeps for ranges of more than one element. That branch never runs. Reproducer without xtensor:

struct holder { std::size_t m_size; std::array<std::size_t, 1> m_shape; };
std::array<std::size_t, 1> f(const holder& h)
{
    std::array<std::size_t, 1> s;
    std::copy(h.m_shape.cbegin(), h.m_shape.cend(), s.begin());
    return s;
}

These copies now use std::copy_n(src.begin(), src.size(), dst.begin()). size() is a constant for std::array, so GCC drops the branch. At each site the destination is sized from the same source, so the copied elements do not change. Changed sites: xfunction::broadcast_shape, the xbroadcast constructor, flip (shape and strides), and average with multi-dimensional weights. std::copy(data(), data() + size()) and std::ranges::copy still warn.

No unit test, since this is a compile-time warning and CI does not build with -Wall on GCC. A file using 1-D, 2-D and 3-D views, reductions, broadcasting, flip and xtensor_fixed now compiles without warnings with GCC 13.4 and 15.3 at -O2 and -O3 with -Wall -Wextra, with and without xsimd.

GCC 13 and 15 emit -Warray-bounds at -O2/-O3 on a std::copy from a
std::array<std::size_t, 1> member into another std::array<std::size_t, 1>.
When the array is a member at a non-zero offset, GCC does not fold the
length of the source range and warns on the memmove branch that libstdc++
keeps for ranges of more than one element. That branch never runs. User
code as simple as assigning 0.5 * (even + odd) to a 1-D view gets the
warning.

Minimal reproducer without xtensor:

    struct holder { std::size_t m_size; std::array<std::size_t, 1> m_shape; };
    std::array<std::size_t, 1> f(const holder& h)
    {
        std::array<std::size_t, 1> s;
        std::copy(h.m_shape.cbegin(), h.m_shape.cend(), s.begin());
        return s;
    }

std::copy_n takes the count directly, and size() is a constant for
std::array, so GCC drops the branch. In each changed site the destination
is sized from the same source, so the number of copied elements does not
change. Changed sites: xfunction::broadcast_shape, the xbroadcast
constructor, flip (shape and strides) and average with multi-dimensional
weights.
@codspeed

codspeed Bot commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

1 warning

⚠️ Unable to measure the execution of 1 benchmark

This benchmark measured no execution time, so its result is not comparable and counts as unchanged. This usually happens when the compiler removes the code under test.

Prevent compiler optimizations

See affected benchmarks

✅ 255 untouched benchmarks


Comparing gouarin:fix-array-bounds (5a967e3) with master (57f3d07)

Open in CodSpeed

@JohanMabille
JohanMabille merged commit 5d75ad3 into xtensor-stack:master Nov 2, 2026
41 checks passed
gouarin added a commit to hpc-maths/samurai that referenced this pull request Nov 4, 2026
Revert xt::noalias on result += in detail::portion_impl and on the |= in
p4est-simple-2d. It was there only to hide a GCC 15 -Warray-bounds false
positive on the shape copy in xtensor, fixed in xtensor by
xtensor-stack/xtensor#2946 for its next release. These two warnings stay
until then.
gouarin added a commit to hpc-maths/samurai that referenced this pull request Nov 4, 2026
… the demos (#787)

## Description

With GCC 15 (conda-forge 15.3), a Release build warns in
`stl_algobase.h`:

```
warning: 'void* __builtin_memcpy(void*, const void*, long unsigned int)' forming offset 8 is out of the bounds [0, 8] of object 'shape' with type 'std::array<long unsigned int, 1>' [-Warray-bounds=]
```

It is a false positive: libstdc++ 15 copies more than one element with
`memmove` and one element with a single store. When xtensor or HighFive
copies a one-element `std::array` shape, GCC does not fold the distance
between its two ends and warns on the `memmove` branch, which never
runs. ASan and UBSan runs of the `portion` and `restart` tests on `main`
are clean.

| Warning in | Cause | Change |
|---|---|---|
| `test_restart`, `test_restart_include`, 10 FV demos |
`/mesh/origin_point` read into a 1D `xt::xtensor` | go through a
`std::array`; same file layout |
| `test_restart` under MPI | `copy_op` assigns with `=`, which keeps a
broadcast fallback | `noalias` |
| `tutorial-set-operator`, `tutorial-burgers1d-step-2`, `-3`, `-5` | 1D
expressions such as `u(0, i) = 0.5 * (u(1, 2 * i) + u(1, 2 * i + 1))` |
`xt::noalias(u(0, i)) = ...`; one expression with a sign for the step 5
prediction; tutorial pages updated |
| `pablo-bubble-2d` | `xt::xtensor` built from `xt::random` expressions
| `xt::random::rand` with bounds, bubbles moved in a loop |

Two warnings stay on purpose: `result += ...` in `detail::portion_impl`
(`test_portion`) and the `|=` in `p4est-simple-2d`. They come from shape
copies in xtensor (`xfunction::broadcast_shape`, the `xbroadcast`
constructor), fixed by xtensor-stack/xtensor#2946 for the next xtensor
release. Adding `noalias` to a compound assignment just to hide them is
not wanted.

Apart from `p4est-simple-2d`, the GCC 15 demo build, sequential and MPI,
has no warnings left. With their default options, the changed demos
write the same HDF5 files as `main` (`h5diff`).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants