为什么 gcc 不消除对 `_tls_get_addr()` 的连续调用?

lol*_*pop 5 c++ multithreading gcc thread-local-storage

我有使用 thread_local 缓冲区的代码,类似于:

int func() {
    thread_local std::vector<int> buffer;

    buffer.resize(0);
    for (int i = 0; i < 10000; i++) {
        buffer.push_back(i);
    }

    return processing(buffer);
}
Run Code Online (Sandbox Code Playgroud)

在分析我的代码时,我注意到 gcc 在循环体内调用了一个调用_tls_get_addr(),以便访问buffer. 循环体的Godbolt反汇编如下所示:

        lea     rbx, -20[rbp]
        data16  lea rdi, f()::buffer@tlsgd[rip]
        .value  0x6666
        rex64
        call    __tls_get_addr@PLT  ; <- This call!
        mov     rsi, rbx
        mov     rdi, rax
        call    std::vector<int, std::allocator<int> >::push_back(int const&)@PLT
Run Code Online (Sandbox Code Playgroud)

这些调用大大减慢了循环速度。我可以手动使用此版本并参考:

        lea     rbx, -20[rbp]
        data16  lea rdi, f()::buffer@tlsgd[rip]
        .value  0x6666
        rex64
        call    __tls_get_addr@PLT  ; <- This call!
        mov     rsi, rbx
        mov     rdi, rax
        call    std::vector<int, std::allocator<int> >::push_back(int const&)@PLT
Run Code Online (Sandbox Code Playgroud)

这消除了调用_tls_get_addr()并解决了缓慢问题。我必须为每个这样的变量手动执行此操作,这似乎很愚蠢。为什么gcc不会自动缓存结果_tls_get_addr()?Clang似乎能够使用来做到这一点-O3,因此它表明这是 gcc 尚未实现的合法优化。是这样吗?

将 tls 模型更改为 init-exec 也消除了这些调用,但我的库通常作为 Python 扩展动态加载,所以我的理解是在这种情况下这是不可能的。