llvm / llvm/llvm-project

[Xtensa][clang++] Struct types of equal or lesser size than a word are always returned on the stack

Open
#219,811 0 comments 0 reactions 0 assignees View on GitHub
ABI backend:Xtensa
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

When compiling both C and C++ for Xtensa, small (<= word/4 bytes) struct types are always returned via invisible reference. This is irrespective of the C++ notions of when an invisible reference return are required.

This can be reproduced with a very simple sample like:

```c++
struct Test {
unsigned v_;
};

Test test() {
return {.v_=1};
}
```

Which results in:

```llvm
%struct.Test = type { i32 }

; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
define dso_local void @_Z4testv(ptr dead_on_unwind noalias nocapture writable writeonly sret(%struct.Test) align 4 initi
alizes((0, 4)) %0) local_unnamed_addr #0 {
store i32 1, ptr %0, align 4, !tbaa !2
ret void
}
```

and the assembly:

```
00000000 <_Z4testv>:
0: 004136 entry a1, 32
3: 180c movi.n a8, 1
5: 0289 s32i.n a8, a2, 0
7: f01d retw.n
```

---

This is a deviation from the ABI defined by the Xtensa ISA which reads (in section 10.1.5 *Return Values in AR Registers*):
> Values of four words or less are returned in registers. The callee places the return value in registers beginning with AR[2] and continuing up to (and including) AR[5], depending on the size of the value. [...] Values larger than four words are returned by invisible reference. The caller passes a pointer as an invisible first argument and the callee stores the return value in the memory referenced by the pointer.

Notably, this is **not consistent** with other structure handling with clang.
A struct with size greater than a word (even by a single byte) and less than or equal to 4 words does correctly follow the ABI.

This breaks compatibility with, e.g., GCC-compiled code (which respects this ABI), and is a minor(?) pessimization.

As an (not incredibly necessary, but somewhat amusing) illustration:

```c++
#include

template
auto test() {
std::array ret;
ret.fill(0);
return ret;
}

template auto test<4>();
template auto test<5>();
```

produces quite different code:

```
00000000 <_Z4testILj4EEDav>:
0: 004136 entry a1, 32
3: 080c movi.n a8, 0
5: 034282 s8i a8, a2, 3
8: 024282 s8i a8, a2, 2
b: 014282 s8i a8, a2, 1
e: 004282 s8i a8, a2, 0
11: f01d retw.n

00000000 <_Z4testILj5EEDav>:
0: 004136 entry a1, 32
3: 020c movi.n a2, 0
5: 023d mov.n a3, a2
7: f01d retw.n
```

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the issue with clang++ for an Xtensa target using the small Test struct and the std::array examples, then compare the LLVM IR and assembly with the quoted Xtensa ABI rules. Done means C and C++ structs of four words or fewer are returned in AR[2]–AR[5], while larger values still use an invisible reference, preserving GCC compatibility.

Written by the indexing model from the issue text.

Assessment

Tech stack
c, cpp
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.