llvm / llvm/llvm-project

[RISCV] Consecutive field copy + zeroing memset not merged into wide LMUL vector ops (vs AArch64)

Open
#223,906 1 comment 0 reactions 1 assignee Claimed by @wangpc-pp View on GitHub
backend:RISC-V llvm:SelectionDAG llvm:SLPVectorizer missed-optimization
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

### Summary

For a small struct move (copy N pointer-sized fields, then zero the source range), the RISC-V backend leaves several `VL=2 e64 m1` vector ops plus scalar tails, whereas AArch64 emits a single wide store + an 8B scalar tail. On RISC-V the 40-byte clear becomes two `VL=2` `vse64.v` instead of one `VL=4 (e64, m2)`, and the 32-byte copy stays `VL=2` instead of widening.

Godbolt: https://godbolt.org/z/44TjfEEov

### Actual (RISC-V, `-march=rva23u64`)

```asm
iobuf_move_assign:
addi a2, a1, 16
vsetivli zero, 2, e64, m1, ta, ma
vle64.v v8, (a2)
addi a3, a0, 16
vse64.v v8, (a3)
ld a3, 32(a1)
addi a4, a1, 40
sd a3, 32(a0)
vle64.v v8, (a4)
vmv.v.i v9, 0
addi a0, a0, 40
vse64.v v8, (a0)
addi a0, a1, 32
vse64.v v9, (a0) ; two VL=2 zero stores instead of one VL=4
sd zero, 48(a1)
vse64.v v9, (a2)
ret
```

### Expected (AArch64 shape, `-mcpu=neoverse-v3`)

```asm
iobuf_move_assign:
ldp q0, q1, [x1, #16] ; 32B copy, one wide load/store pair
stp q0, q1, [x0, #16]
movi v0.2d, #0
ldr x8, [x1, #48] ; 8B scalar tail
str x8, [x0, #48]
str xzr, [x1, #48]
stp q0, q0, [x1, #16] ; 32B clear, one wide store
ret
```

i.e. one wide (32B) vector op + an 8B scalar tail for both the copy and the clear.

### Analysis (two independent root causes)

1. **Zeroing stores don’t widen (DAG store-merging).**
`getOptimalMemOpType` lowers the zeroing `memset` to `LMUL1` (`v2i64`) stores and relies on DAG store-merging to grow them. `RISCVTargetLowering::storeOfVectorConstantIsCheap` gates on `NumElem >= 4`, where `NumElem` counts *MemVT-sized* stores; with a `v2i64` MemVT this needs 8 e64 elements before merging is “cheap”, so a 32B zero region stays two `VL=2 m1` stores instead of one `VL=4 m2`.

2. **Field copy doesn’t vectorize (SLP cost model).**
AArch64 SLP packs the copy into `<2 x ptr>` + `<2 x i64>`; RISC-V SLP leaves it scalar. RVV load/store has no immediate-offset addressing, so a `VL=2` copy is only break-even at the instruction level, and the SLP cost model can’t see the *downstream* `m1→m2` merge that would make widening a win. With `noalias`/`restrict` on the pointers RISC-V does form the wide `m2` copy; under may-alias it stays scalar (correctness lower bound preserved).

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.