llvm / llvm/llvm-project

[AArch64] LLVM fails to elide memcpy to return slot for neon st1x4

Open
#191,038 5 comments 0 reactions 0 assignees View on GitHub
backend:AArch64 llvm:optimizations
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.