llvm / llvm/llvm-project

std::optional of class annotated with clang::trivial_abi is not trivial for purpose of calls

Open
#174,179 1 comment 0 reactions 0 assignees View on GitHub
clang extension:clang
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

Consider the following ([godbolt](https://godbolt.org/z/Yd6d4Y3MM)):
```c++
#include

struct TrivialDestructor {
~TrivialDestructor() = default;
int x = 0;
};

struct [[clang::trivial_abi]] NontrivialDestructor {
~NontrivialDestructor() {}
int x = 0;
};

TrivialDestructor trivial() { return {}; }
std::optional trivial_optional() { return {}; }

NontrivialDestructor nontrivial() { return {}; }
std::optional nontrivial_optional() { return {}; }
```

NontrivialDestructor has a nontrivial destructor, but since it is annotated with clang::trivial_abi, nontrivial() is able to return the object in registers instead of on the stack and compiles exactly the same as trivial():
```
trivial():
push rbp
mov rbp, rsp
mov dword ptr [rbp - 4], 0
mov eax, dword ptr [rbp - 4]
pop rbp
ret

nontrivial():
push rbp
mov rbp, rsp
mov dword ptr [rbp - 4], 0
mov eax, dword ptr [rbp - 4]
pop rbp
ret
```

But nontrivial_optional() constructs the object on the stack, even though it could be passed around in registers like trivial_optional() does:
```
trivial_optional():
push rbp
mov rbp, rsp
sub rsp, 16
lea rdi, [rbp - 8]
call std::__1::optional::optional[abi:sqe220000]()
mov rax, qword ptr [rbp - 8]
add rsp, 16
pop rbp
ret

nontrivial_optional():
push rbp
mov rbp, rsp
sub rsp, 16
mov rax, rdi
mov qword ptr [rbp - 16], rax
mov qword ptr [rbp - 8], rdi
call std::__1::optional::optional[abi:sqe220000]()
mov rax, qword ptr [rbp - 16]
add rsp, 16
pop rbp
ret
```

The same issue was also observed for std::variant and std::expected.

Contributor guide

Open the contributing guide

Research direction

Start with the Godbolt reproducer and inspect how std::optional, std::variant, and std::expected determine their call and return ABI when their contained type has clang::trivial_abi. Compare the generated calls for TrivialDestructor and NontrivialDestructor; done means the annotated contained types are passed and returned consistently in registers where applicable, with coverage for the reported cases.

Written by the indexing model from the issue text.

Assessment

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.