Open 0775a785-4f8d-448e-9da0-14f81adf2169 opened 6 years ago
https://godbolt.org/g/6yZEKf (compiled with -O3)
char* variableSize(long n) { auto p = new char[n](); for (int i = 0; i < n; i++) { p[i] = 0xff; } return p; }
char* small() { return variableSize(8); }
char* large() { return variableSize(10'000); }
The variableSize() case generates two calls two memset with the second being conditional on n > 0:
variableSize(long): # @variableSize(long) push r14 push rbx push rax mov rbx, rdi call operator new[](unsigned long) mov r14, rax xor esi, esi mov rdi, rax mov rdx, rbx call memset test rbx, rbx jle .LBB0_2 mov esi, 255 mov rdi, r14 mov rdx, rbx call memset .LBB0_2: mov rax, r14 add rsp, 8 pop rbx pop r14 ret
Note that when clang can see the allocation size it does elide the initial memset:
small(): # @small() push rax mov edi, 8 call operator new[](unsigned long) mov qword ptr [rax], -1 pop rcx ret large(): # @large() push rbx mov edi, 10000 call operator new[](unsigned long) mov rbx, rax mov esi, 255 mov edx, 10000 mov rdi, rax call memset mov rax, rbx pop rbx ret
MSVC and ICC both also handle this poorly: https://godbolt.org/g/i4wMYa
https://developercommunity.visualstudio.com/content/problem/246786/poor-codegen-for-value-init-followed-by-explicit-i.html
Related g++ bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85680
Extended Description
https://godbolt.org/g/6yZEKf (compiled with -O3)
char* variableSize(long n) { auto p = new char[n](); for (int i = 0; i < n; i++) { p[i] = 0xff; } return p; }
char* small() { return variableSize(8); }
char* large() { return variableSize(10'000); }
The variableSize() case generates two calls two memset with the second being conditional on n > 0:
variableSize(long): # @variableSize(long) push r14 push rbx push rax mov rbx, rdi call operator new[](unsigned long) mov r14, rax xor esi, esi mov rdi, rax mov rdx, rbx call memset test rbx, rbx jle .LBB0_2 mov esi, 255 mov rdi, r14 mov rdx, rbx call memset .LBB0_2: mov rax, r14 add rsp, 8 pop rbx pop r14 ret
Note that when clang can see the allocation size it does elide the initial memset:
small(): # @small() push rax mov edi, 8 call operator new[](unsigned long) mov qword ptr [rax], -1 pop rcx ret large(): # @large() push rbx mov edi, 10000 call operator new[](unsigned long) mov rbx, rax mov esi, 255 mov edx, 10000 mov rdi, rax call memset mov rax, rbx pop rbx ret