[AArch64] LLVM fails to elide memcpy to return slot for neon st1x4
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
I tried this code:
https://godbolt.org/z/z1TKEfKnv
```ll
declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1)
declare void @llvm.aarch64.neon.st1x4.v16i8.p0(<16 x i8>, <16 x i8>, <16 x i8>, <16 x i8>, ptr)
define void @foo(ptr sret([64 x i8]) align 64 %ret, <16 x i8> %a, <16 x i8> %b, <16 x i8> %c, <16 x i8> %d) {
call void @llvm.aarch64.neon.st1x4.v16i8.p0(<16 x i8> %a, <16 x i8> %b, <16 x i8> %c, <16 x i8> %d, ptr %ret)
ret void
}
define void @bar(ptr sret([64 x i8]) align 64 %ret, <16 x i8> %a, <16 x i8> %b, <16 x i8> %c, <16 x i8> %d) {
%e = alloca [64 x i8], align 64
call void @llvm.aarch64.neon.st1x4.v16i8.p0(<16 x i8> %a, <16 x i8> %b, <16 x i8> %c, <16 x i8> %d, ptr %e)
call void @llvm.memcpy.p0.p0.i64(ptr %ret, ptr %e, i64 64, i1 false)
ret void
}
```
Both functions invoke the st1x4 intrinsic, the only difference being that the second one first stores to a local, and then memcpys it to the return slot. I would expect LLVM to elide the stack allocation, and immediately store to the return slot. However, it appears this is not the case (using clang 22.1.0, with `-O3 --target=aarch64`):
```asm
foo:
st1 { v0.16b, v1.16b, v2.16b, v3.16b }, [x8]
ret
bar:
stp x29, x30, [sp, #-16]!
sub x9, sp, #112
mov x29, sp
and sp, x9, #0xffffffffffffffc0
mov x9, sp
st1 { v0.16b, v1.16b, v2.16b, v3.16b }, [x9]
ldp q0, q1, [sp]
stp q0, q1, [x8]
ldp q0, q2, [sp, #32]
stp q0, q2, [x8, #32]
mov sp, x29
ldp x29, x30, [sp], #16
ret
```
I discovered this when playing around with rustc; it appears that rustc always performs the store to a local, whereas clang immediately constructs the store to the return slot.
Contributor guide
Research direction
Start with the Godbolt LLVM IR reproducer and compare the generated AArch64 assembly for @foo and @bar using clang with -O3 and --target=aarch64. Trace the optimization and AArch64 lowering paths responsible for the st1x4 intrinsic and memcpy; done means the local allocation and copy in @bar are eliminated while preserving the direct return-slot store.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100