gcc 在这里对每个线程一次 运行 这段代码做了什么?

What is gcc doing here to run this code once per thread?

我只是 运行 跨过这项技术,每个线程 运行 代码一次。我不知道它在最低级别上是如何工作的。特别是,fs 指向什么? .zero 8 是什么意思?标识符是 @tpoff 是有原因的吗?

int foo();

void bar()
{
    thread_local static auto _ = foo();
}

输出(带-O2):

bar():
        cmp     BYTE PTR fs:guard variable for bar()::_@tpoff, 0
        je      .L8
        ret
.L8:
        sub     rsp, 8
        call    foo()
        mov     BYTE PTR fs:guard variable for bar()::_@tpoff, 1
        add     rsp, 8
        ret
guard variable for bar()::_:
        .zero   8

fs 段基址是线程本地存储的地址(至少在 x86-64 Linux 上)。

.zero 8 保留 8 个字节的零(大概在 BSS 中)。查看 GAS 手册:https://sourceware.org/binutils/docs/as/Zero.html, links in https://whosebug.com/tags/x86/info.

@tpoff大概意思是相对于线程本地存储来解决它,可能代表线程偏移量,我不知道。


它的其余部分看起来类似于 gcc 通常为需要 运行 时间初始化程序的 static 局部变量所做的事情:它每次进入函数时都会检查的保护变量,落空在已经初始化的情况下。

1 字节保护变量在线程本地存储中。实际的 _ 本身被优化掉了,因为它从未被读取。 请注意 foo returns.

之后没有存储 eax

顺便说一句,_ 是一个奇怪的(坏的)变量名选择。容易错过,可能留给实现使用。


它在这里有一个很好的优化:通常(对于非线程局部static int var = foo();)如果它发现守卫变量还没有被初始化,它需要一种线程安全的方式来确保只有一个线程实际上进行初始化(本质上是获取锁)。

但是这里每个线程都有自己的守卫变量(并且应该运行foo()第一次不管其他线程在做什么)所以它不需要调用 run_once 函数来获得互斥。

(抱歉,我的回答很简短,稍后我可能会用一个关于非线程本地 static 局部变量的 https://godbolt.org/ 的示例来扩展它。或者找到一个关于它的 SO Q&A。)