#include <iostream> using namespace std; int voidret(int i) { if (i < 0) { i++; } else { i--; } return voidret(i); } int main() { auto i = voidret(10); }clang++ -O3 result:
2030951640
g++ -O3 result:
indifinite
clang++ -O3 assembly:
voidret(int): # @voidret(int) ret main: # @main xor eax, eax ret _GLOBAL__sub_I_example.cpp: # @_GLOBAL__sub_I_example.cpp push rax mov edi, offset std::__ioinit call std::ios_base::Init::Init() [complete object constructor] mov edi, offset std::ios_base::Init::~Init() [complete object destructor] mov esi, offset std::__ioinit mov edx, offset __dso_handle pop rax jmp __cxa_atexit # TAILCALL
g++ -O3 assembly:
voidret(int): .L2: jmp .L2 main: mov edi, 10 call voidret(int) _GLOBAL__sub_I_voidret(int): sub rsp, 8 mov edi, OFFSET FLAT:_ZStL8__ioinit call std::ios_base::Init::Init() [complete object constructor] mov edx, OFFSET FLAT:__dso_handle mov esi, OFFSET FLAT:_ZStL8__ioinit mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev add rsp, 8 jmp __cxa_atexitReasoning:
https://stackoverflow.com/questions/18478078/clang-infinite-tail-recursion-optimization
quote:
While both g++ and clang++ are able to compile C++98 and C++11 code, clang++ was designed from the start as a C++11 compiler and has some C++11 behaviors embedded in its DNA.
With C++11 the C++ standard became thread aware, and that means that now there are some specific thread behavior. In particular 6.8.2.2 states:
The implementation may assume that any thread will eventually do one of the following:
- terminate,
- make a call to a library I/O function,
- perform an access through a volatile glvalue, or
- perform a synchronization operation or an atomic operation.
And that is precisely what clang++ is doing when optimizing. It sees that the function has no side effects and removes it even if it does not terminate.
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.