assembly - 了解 cmpxchg8b/cmpxchg16b 操作

标签 assembly x86-64 intel instructions compare-and-swap

此指令的 SDM 文本包含以下 block :

This instruction can be used with a LOCK prefix to allow the instruction to be executed atomically. To simplify the interface to the processor’s bus, the destination operand receives a write cycle without regard to the result of the comparison. The destination operand is written back if the comparison fails; otherwise, the source operand is written into the destination.

我很难理解最后一句话(但也可能是整个段落)

  • 目标操作数写回...回什么?
  • ...;否则,源操作数被写入目标 什么是源操作数?是 ECX:EBX 吗?据我所知,这条 CAS 指令只接受一个操作数(内存目标)。

如果有人可以重新措辞和/或解释有关无条件写入的这一点,我们将不胜感激。

最佳答案

与常规 cmpxchg r/m32, r32 的措辞比较(它有一个明确的而不是隐含的来源)并且它应该更有意义,特别是比较手册条目顶部表格表格中的简短描述。我已经用 dst、src 和 implicit 进行了注释。请注意,Intel 语法通常是 op dst, src

  • cmpxchg r/m64, r64:比较 RAX (implicit) 与 r/m64 (dst)。如果相等,则设置 ZF 并将 r64 (src) 加载到 r/m64 (dst)。否则,清除 ZF 并将 r/m64 (dst) 加载到 RAX (隐式)

  • cmpxchg16b m128 比较 RDX:RAX 与 m128 (dst)。如果相等,则设置 ZF 并将 RCX:RBX 加载到 m128 (dst)。否则,清除 ZF 并将 m128 加载到 RDX:RAX。

是的,没错,英特尔的手册使用“加载”来描述存储到内存。 (对于 cmpxchg 来说有点合理,其中目标可以 是一个寄存器,而对于 cmpxchg16b 则完全不是。)

但无论如何,记住这些实现会有所帮助:

m64.compare_exchange_strong(expected=RAX, desired=r64);
m128.compare_exchange_strong(expected=RDX:RAX, desired=RCX:RBX);

(就 C++ std::atomic 而言。要真正成为原子,它们需要 lock 前缀,否则它是非原子 RMW。C++ 只会编译lock cmpxchg/lock cmpxchg16b,主流编译器永远不会解锁 cmpxchg。)

The destination operand is written back ... back to what?

目的地的旧值(刚刚加载)被写回。这意味着 cmpxchg16b 始终 是一个写入,例如始终将页面的脏标志标记为脏。 (Does cmpxchg write destination cache line on failure? If not, is it better than xchg for spinlock? 询问它是否真的在微架构上弄脏了 CAS 故障时的缓存行。我假设是这样,但还没有检查。)

这在历史上对旧 CPU 上的 lock 前缀很重要,其中有一个外部 LOCK# pin,lock cmpxchg 实际上为整个加载+存储对断言。现代 CPU 只是在受影响的缓存行上持有一个缓存锁,用于可缓存内存上的对齐锁 CAS。这就是为什么手册说“为了简化与处理器总线的接口(interface),目标操作数接收一个写周期而不考虑比较的结果。


The destination operand is written back if the comparison fails; otherwise, the source operand is written into the destination. (The processor never produces a locked read without also producing a locked write.)

这整段是英特尔编写cmpxchg16b条目时从cmpxchg手册条目复制粘贴的;它在 CX16 上下文中不太清楚,因为它有 2 个隐式操作数而不是显式源和读写 RAX。它没有定义术语“源操作数”。

在前面的描述中,它确实定义了该指令的“目标操作数”术语

Compares the 64-bit value in EDX:EAX (or 128-bit value in RDX:RAX if operand size is 128 bits) with the operand (destination operand)

“操作数”表示显式 操作数。这显然是什么意思,因为它是唯一可以是内存的东西,所以它必须是被比较的东西之一。以及来自英语运作方式等的其他线索/原因。

所以“目标操作数”确实得到了明确的定义,但是在一条总共有 3 个操作数的指令中,不定义就说“源操作数”是很糟糕的。正如我所说,这显然是英特尔文档编写者复制/粘贴的结果。

这不是一个严重的问题;我们知道指令的基本要点,操作部分使实际发生的事情 100% 清楚。

关于assembly - 了解 cmpxchg8b/cmpxchg16b 操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63163839/

相关文章:

c - Intel Core i7 处理器和缓存行为

assembly - 炸弹实验室阶段_4

c++ - 使用英特尔 TBB 在并发 C++ 代码中阻止/卡住

assembly - 汇编代码中的段错误

assembly - 在 GCC 内联汇编中影响内存操作数寻址模式的早期破坏者的不正确行为的具体示例?

c++ - GCC w/inline assembly & -Ofast 为内存操作数生成额外代码

assembly - 引用没有美元符号的常量是否有不同的含义?

c++ - 我应该多担心英特尔 C++ 编译器为 AMD 发出次优代码?

debugging - 为什么调试时寄存器值没有改变?

c - 从 c 调用汇编函数