CMPXCHG——忽略 ZF 标志安全吗?
CMPXCHG – safe to ignore the ZF flag?
cmpxchg
的操作伪代码如下(Intel® 64 and IA-32 Architectures Software Developer's Manual, Volume 2A: Instruction Set Reference, A-M, 2010):
IF accumulator = DEST
THEN
ZF ← 1;
DEST ← SRC;
ELSE
ZF ← 0;
accumulator ← DEST;
FI;
至少在第一眼看来,累加器会更改其值 如果(且仅当) ZF = 0
。
那么,是安全还是完全忽略ZF,只看累加器值的变化来判断操作是否成功?
换句话说,我可以安全地使用变体 #2 而不是 #1 吗?
#1:
mov eax, r8d
lock cmpxchg [rdx], ecx
jz @success
#2:
mov eax, r8d
lock cmpxchg [rdx], ecx
cmp eax, r8d
jz @success
我的意思是,有没有一些非常特殊的情况,只看ZF就可以真正显示操作是否成功?这可能是一个微不足道的问题,但无锁多任务几乎不可能调试,所以我必须 101% 确定。
我认为你的推理是正确的。
将指令浪费到 re-generate ZF 不会导致正确性问题,如果 cmp 可以与 JCC 融合,则只会花费 code-size。不过,与仅使用 EAX 中的旧值进行替换相比,还需要额外的寄存器。
这可能就是为什么 GNU C 的 old-style __sync
builtins(已被采用 memory-order 参数的 __atomic
内置函数废弃)仅提供 __sync_val_compare_and_swap
和 __sync_bool_compare_and_swap
return 值 或 布尔成功结果,没有一个内置函数 return 两者都是。
(较新的 __atomic_compare_exchange_n
更像是 C11/C++11 API,通过引用更新 expected
,并且 return ing a bool
。这可以让 GCC 不会浪费带有 cmp
的指令。)
cmpxchg
的操作伪代码如下(Intel® 64 and IA-32 Architectures Software Developer's Manual, Volume 2A: Instruction Set Reference, A-M, 2010):
IF accumulator = DEST
THEN
ZF ← 1;
DEST ← SRC;
ELSE
ZF ← 0;
accumulator ← DEST;
FI;
至少在第一眼看来,累加器会更改其值 如果(且仅当) ZF = 0
。
那么,是安全还是完全忽略ZF,只看累加器值的变化来判断操作是否成功?
换句话说,我可以安全地使用变体 #2 而不是 #1 吗?
#1:
mov eax, r8d
lock cmpxchg [rdx], ecx
jz @success
#2:
mov eax, r8d
lock cmpxchg [rdx], ecx
cmp eax, r8d
jz @success
我的意思是,有没有一些非常特殊的情况,只看ZF就可以真正显示操作是否成功?这可能是一个微不足道的问题,但无锁多任务几乎不可能调试,所以我必须 101% 确定。
我认为你的推理是正确的。
将指令浪费到 re-generate ZF 不会导致正确性问题,如果 cmp 可以与 JCC 融合,则只会花费 code-size。不过,与仅使用 EAX 中的旧值进行替换相比,还需要额外的寄存器。
这可能就是为什么 GNU C 的 old-style __sync
builtins(已被采用 memory-order 参数的 __atomic
内置函数废弃)仅提供 __sync_val_compare_and_swap
和 __sync_bool_compare_and_swap
return 值 或 布尔成功结果,没有一个内置函数 return 两者都是。
(较新的 __atomic_compare_exchange_n
更像是 C11/C++11 API,通过引用更新 expected
,并且 return ing a bool
。这可以让 GCC 不会浪费带有 cmp
的指令。)