【发布时间】:2018-05-02 12:49:20
【问题描述】:
我正在尝试了解这条 testb 指令 (x86-64) 的作用。
testb $1, %al
这里 1 美元的价值是多少。是全一个 (0xFF) 还是一个 1 (0x1)?
该程序集由 clang 为以下程序生成:
#include <atomic>
std::atomic<bool> flag_atomic{false};
extern void f1();
extern void f2();
void foo() {
bool b = flag_atomic.load(std::memory_order_relaxed);
if (b == false) {
f1();
} else {
f2();
}
}
与(clang++ -s test.cpp -O3)相关的程序集如下:
Lcfi2:
.cfi_def_cfa_register %rbp
movb _flag_atomic(%rip), %al
testb $1, %al ; <<<<------------
jne LBB0_2
【问题讨论】:
-
boperand-size 是 byte,而不是 bit,所以$1是一个只设置了低位的字节。奇怪的是,clang 使用单独的加载而不是带有内存操作数的test;该函数不再使用该值,因此无需将其保存在寄存器中。或者实际上,英特尔上的immediate + RIP-relative wouldn't micro-fuse。它也可能无法进行宏融合,因此您最终可能会在英特尔上从test $1, _flag_atomic(%rip) / jnz获得 3 个融合域微指令。 -
请注意,
movb已经是一个微融合负载 + 合并到 RAX 的低字节,但 on recent Intel。movzbl会避免错误的依赖。所以 clang 已经有 3 个未融合域的 uops(其中 2 个来自movb),但这种方式总共只有 2 个融合域的 uops。 -
是的,由于某种原因,这只发生在 atomic_load 上,因为标量负载 clang 会在不使用
%al的情况下生成cmpb。 -
啊,是的,
atomic在编译器中得到了特殊处理,并且(如volatile)有时会结束优化器。当前的编译器甚至没有优化重复的原子负载,尽管当前的标准确实允许这样做:Can and does the compiler optimize out two atomic loads?,尤其是我对Why don't compilers merge redundant std::atomic writes?的回答 -
感谢分享资源。很有用!我经常使用godbolt顺便说一句。 PS:gcc.gnu.org/bugzilla/show_bug.cgi?id=85610
标签: c++ assembly x86 clang x86-64