Search code examples
c++compiler-optimizationescape-analysis

Escape Analysis with std::vector in C++


I am wondering there is any optimization option in Clang or GCC for escape analysis on std::vector in C++. Since std::vector<int> in the example below does not require the actual data of v to be allocated in the heap or stack. Compiler can actually allocate v.data() on stack for better performance.

  1. Assume that Clang/GCC does not do escape analysis, is there any particular motivation to not to use escape analysis?

  2. Assume that Clang/GCC does escape analysis, why value of v.data() and &x so different?

#include<cstdio>
#include<vector>
int main() {
    int x = 0;
    std::vector<int> v(3, 0);
    std::printf("&x: %p\n", &x);
    //std::printf("&v: %p\n", &v); // we intentionally don't print the pointer to v here.
    std::printf("v.data(): %p\n", v.data());
    return x + v[0]; // we want compiler not to optimize everything out
}

Expected result

&x: <some address>
v.data(): <some address> + 4

Actual result from Clang and GCC

[*****@localhost test]$ g++ test.cc -O3
[khanh@localhost test]$ ./a.out 
&x: 0x7ffe2af5a59c
v.data(): 0xadde70
[*****@localhost test]$ clang++ test.cc -O3
[*****@localhost test]$ ./a.out 
&x: 0x7fff66ce1ab4
v.data(): 0xfeee70

Thanks!


Solution

  • There exists escape analysis on Clang compiler.

    Sample code: from @geza https://godbolt.org/z/N1GLUI

    int fn(int a, int b, int c) {
        int *t = new int[3];
    
        t[0] = a;
        t[1] = b;
        t[2] = c;
    
        int r = t[0]+t[1]+t[2];
    
        delete[] t;
    
        return r;
    }
    

    GCC

    fn(int, int, int):
      push r12
      mov r12d, edx
      push rbp
      mov ebp, esi
      push rbx
      mov ebx, edi
      mov edi, 12
      call operator new[](unsigned long)
      mov DWORD PTR [rax], ebx
      add ebx, ebp
      mov rdi, rax
      mov DWORD PTR [rax+4], ebp
      mov DWORD PTR [rax+8], r12d
      add r12d, ebx
      call operator delete[](void*)
      mov eax, r12d
      pop rbx
      pop rbp
      pop r12
      ret
    

    Clang

    fn(int, int, int):                               # @fn(int, int, int)
            lea     eax, [rdi + rsi]
            add     eax, edx
            ret