llvm / llvm/llvm-project

Calls to functions with `returns_twice` do not clobber callee-saved registers

Open
#188,494 5 comments 0 reactions 0 assignees View on GitHub
clang:attributes diverges-from:gcc
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

When a function with `returns_twice` is called, callee-saved registers are not invalidated in clang, leading to surprising behavior.

[This snippet in Compiler Explorer](https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAMzwBtMA7AQwFtMQByARg9KtQYEAysib0QXACx8BBAKoBnTAAUAHpwAMvAFYTStJg1AB9U8lJL6yAngGVG6AMKpaAVxYMQAJgDspBwAyeAyYAHLuAEaYxCAAzKQADqgKhLYMzm4e3n5JKTYCQSHhLFEx8ZaY1mlCBEzEBBnunr4WmFb5DDV1BIVhkdFxFrX1jVktCsM9wX0lA7EAlBaorsTI7ByYqgTRDADUwQS7wLSoEWLGE65UVACksQBCNxoAgk/PpkwEBMR4Ea7bpigxEwBBWDAUxgIAHc8Gt5vM3gA3VB4dC7fioCDI1EIh5vN4HXYsJjBCAIl43HyPF67Wn7QS7JjGE6iWi7O4AESOJzOtAuoOud2pzzp6NQmNxwtFwNBxD2TJZYnZXnu3NO50ugrxFJ8HI4i1onAArLxPBwtKRUJxHLsFMtVphlbEeKQCJp9YsANZxWIAOliAcDQaDADZ9JxJKb3ZbOLwFCANK73Ys4LAkGgWAk6NFyJQM1n6DFkMAuEavHw6NtiPGIBFoxFgnUAJ6cF0N5jEJsAeQi2kqbu4vAzbEEXYYtBb5t4WD%2BwEcYlo8cHpCwxKM4inK7wwKqiMwS4tm0q/3WLoObWjtF%2BxGbziw0e%2BeBYrd4e%2BIEWSmA5mDXx2CoCnRYqAMYAFAANTwTAoS7BJGBfGRBBEMR2CkBD5CUNRo10Lh9EMEwzH0X540gRZUASDolwAWi7WJeFQN8fiwYiyVado0nsBgnBcJoJHLQJpmKUoQAATkSZJUgEUZPC4ctcgkhhekEgZRIqKoBC6EZuKyGTWP7apJkU/oYhUyYpN4oZukM2ZjMWO0VjWCQDWNKNNytDhdlUAAOENKJDSQjmQZBdlLX0vF2CBcEIEgnS4eZeAHLR4VIBBMCYLAYhY70A39YNcoDMNDQ4SNSGfLgNETM0LTcuMEyTQDSFTRAUFQTNszICgIHzNqUGQAwjC8LwyorWgqxrOtN3bZt4Mmzsez7ax4OHRgCDHCdoxnVw5wXJcXVXPCNwtfAdxsPcD14I9kBPeDz0Ki0rwiG9OzvdYLUfZ9lzfD8lG/X8ryMZM%2BBA8DIOg2CzRdfhENZFDpEh9CVHUTddHLPr8OMcx7uY0jyLSKiaLohjUX3eBbLaPS7AgBwzJw/iiiMvQ5I6amxLyNIrKEnDVI6DSGi06TdLUzoDIE%2BnOdMvm9AmSyResxylnslCnI4E1SEqujOA87zfP83q8N2AbfS4X0NHCyKiGIGK4rqxKvR9HK8uDcMipcqrYwsWqEo9J2vBd9WOHi5NFjfFI7EkIA) compares the compilation output of GCC and clang. You can see that in clang output some registers are still live after a call to a function with `returns_twice` attribute:

```asm
main:
push rbp
push rbx
push rax
mov rbx, qword ptr [rip + global_stuff@GOTPCREL]
mov ebp, dword ptr [rbx]
call foo@PLT ;; <- this function returns twice
mov eax, ebp ;; ebp is still live!
add eax, dword ptr [rbx] ;; rbx is still live!
add rsp, 8
pop rbx
pop rbp
ret
```

In GCC output the result of the first read is correctly stashed on the stack:

```asm
main:
sub rsp, 24
mov eax, DWORD PTR global_stuff[rip] ;; first read
mov DWORD PTR [rsp+12], eax ;; stash
call foo
mov eax, DWORD PTR [rsp+12] ;; get the stashed value
add eax, DWORD PTR global_stuff[rip]
add rsp, 24
ret
```

Commenting out the attribute removes the stashing in GCC and does not affect clang output (modulo some register shuffling).

This issue was encountered when we tried to roll our own `setjmp` to work around the bug https://github.com/llvm/llvm-project/issues/72908.

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.