https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127227

            Bug ID: 127227
           Summary: [arm] out-of-range negative literal pool reference for
                    Neon vldr (*neon_movv2si alternative 4)
           Product: gcc
           Version: 16.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: target
          Assignee: unassigned at gcc dot gnu.org
          Reporter: rudi at heitbaum dot com
  Target Milestone: ---

## Description

GCC 16.2.0 emits a `vldr` whose PC-relative offset to its literal pool entry is
-1024, which is 4 bytes outside the +/-1020 range encodable in the instruction,
so the assembler rejects its own input:

```
$ arm-linux-gnueabihf-gcc -march=armv7ve -mtune=cortex-a17 -mabi=aapcs-linux \
    -mfloat-abi=hard -mfpu=neon-vfpv4 -O2 -fPIC -fomit-frame-pointer -w -c
bug.c
Assembler messages:
{standard input}:979: Error: co-processor offset out of range
```

The attached `bug.c` is reduced with cvise from
`src/panfrost/compiler/midgard/midgard_compile.c` in mesa 26.2.2.

The offending instruction, from `-S -dp`:

```asm
        vldr    d16, .L104+24   @ 360   [c=8 l=8]  *neon_movv4si/4
        vldr    d17, .L104+32
        vldr    d18, .L104+16   @ 367   [c=8 l=4]  *neon_movv2si/4     <-- line
979
```

The faulting one is `*neon_mov<mode>` for `VDXMOV`, alternative 4 (`w <- Uni`,
load from the literal pool), whose pool-range attributes in
`config/arm/neon.md`
are:

```
  (set_attr "arm_pool_range"     "*,*,*,*,1020,*,*,1020,*,*")
  (set_attr "thumb2_pool_range"  "*,*,*,*,1018,*,*,1018,*,*")
  (set_attr "neg_pool_range"     "*,*,*,*,1004,*,*,1004,*,*")
```

## Measured distance

Marking the pool label and the faulting instruction with global symbols and
assembling shows the reference is over-range by exactly one word:

```
MARK_POOL (.L104) = 0x950
MARK_INSN (vldr)  = 0xd58

referenced entry  = .L104 + 16  = 0x960
PC at the vldr    = 0xd58 + 8   = 0xd60
offset            = 0x960 - 0xd60 = -0x400 = -1024      (limit -1020)
```

The instruction-to-entry distance is 1016 bytes backwards, which already
exceeds
the 1004 that `neg_pool_range` declares for this alternative, so the minipool
placement pass looks to be under-counting the distance rather than the
attribute
being wrong for this alternative.

The same measurement on the unreduced mesa file gives an identical result:
pool at 0x1fd8, `vldr` at 0x23f0, entry `.L902+32` at 0x1ff8, offset -1024.

Backward references to the same pool that do assemble get progressively further
away right up to the limit; in the unreduced file the last good one is 744
bytes
back:

```
 BA0B5FED    vldr  d16, .L902+32      @ 0xBA * 4 = 744 bytes back, ok
 000B5FED    vldr  d16, .L902+32      @ over range, offset field emitted as 0
```

## Possibly related

Immediately before the faulting instruction:

```asm
        vldr    d16, .L104+24   @ 360   [c=8 l=8]  *neon_movv4si/4
        vldr    d17, .L104+32
```

`*neon_mov<mode>` for `VQXMOV` alternative 4 has `length` 8 and emits two
`vldr`
via `output_move_neon`, where the second needs 4 more bytes of range than the
first. That is the same shape as the MVE issue fixed in PR 121810
(r16-4060-g2e1c12409662c20f45b0e4dabaadff033ef674f7, "arm: mve: fix out-of
range
literal pool for a const_vector"). I have not confirmed whether this is the
cause of the mis-placement here or an independent latent problem, but the error
is off by exactly the 4 bytes that case is about.

## Versions

Only the vanilla 16.2.0 build is a clean data point; the distro compilers below
are Debian/Ubuntu builds with different configure defaults (notably
`-fstack-protector-strong` on by default), so their codegen differs for reasons
unrelated to this bug.

| compiler | result |
|---|---|
| GCC 16.2.0, vanilla, `--target=arm-linux-gnueabihf --with-arch=armv7ve
--with-float=hard --with-fpu=neon-vfpv4 --with-abi=aapcs-linux` | **fails** |
| GCC 16.0.1 20260322 (Ubuntu `gcc-16-arm-linux-gnueabihf`) | compiles |
| GCC 15.2.0 (Ubuntu `gcc-15-arm-linux-gnueabihf`) | compiles |

Because the failure depends on the exact distance between the pool and the
reference, "compiles" on the other versions means only that their layout stays
inside the range, not that the bug is absent. This looks latent rather than a
fresh regression: the same source file compiles under 16.2.0 when taken from
mesa 26.2.1 and fails when taken from mesa 26.2.2, and the file is
byte-identical between those two releases -- only the generated nir headers it
includes changed, which shifts the code layout.

## Workaround

For downstream, `-fno-tree-slp-vectorize` removes the 64-bit vector constants
that make the pools large, taking the worst pool distance in this translation
unit from over 1020 bytes down to 624. `-O1`, `-Os` and `-mfpu=vfpv4` also
avoid it, in each case by not generating VFP pool loads here.

Reply via email to