看看这段代码
extern "C" long _InterlockedCompareExchange(long volatile * _Destination, long _Exchange, long _Comparand);
#define MAGIC 1
// Unlike InterlockedIncrement this function not increment from 0 to 1, but return FALSE
bool TryLock(long* pLock)
{
long Value = *pLock, NewValue;
for ( ; Value; Value = NewValue)
{
NewValue = _InterlockedCompareExchange(pLock, Value + 1, Value);
if (
#if MAGIC
NewValue == Value
#else
Value == NewValue
#endif
) return true;
}
return false;
}
如果设置 #define MAGIC 0
改变了什么?根据想法一定不能 . 但如果我们将 NewValue == Value
改为 Value == NewValue
(只是长值),则使用 CL.EXE
64位编译器 - 生成的代码严重改变了!
我尝试使用两个版本的 CL
- 最新 19.00.24210.0
和 14.00.50727.762
(超过10年 - 2006年12月)我在所有测试中都得到了绝对相同的代码 . 使用标志 cl /c /FA /O1
编译 - 所以 /O1
优化(与 /Oxs
相同的结果)
与 MAGIC 1
( NewValue == Value
)
TryLock PROC
mov eax, [pLock]
jmp @@
@@loop:
lea edx, [rax+1]
lock cmpxchg [pLock], edx
je @@exit
@@:
test eax, eax
jne @@loop
ret
@@exit:
mov al, 1
ret
TryLock ENDP
但是 MAGIC 0
( Value == NewValue
)
TryLock PROC
mov r8d, [pLock]
test r8d, r8d
je @@0
@@loop:
lea edx, [r8+1]
mov eax, r8d
lock cmpxchg [pLock], edx
cmp r8d, eax ; !!!!!!!!
je @@exit
test eax, eax
mov r8d, eax
jne @@loop
@@0:
xor al, al
ret
@@exit:
mov al, 1
ret
TryLock ENDP
代码变大,但主要是指令上的显着差异
cmp Value, NewValue
在 lock cmpxchg
之后的第二个变体 . 真的 lock cmpxchg [p], NewValue
自己设置或重置 ZF
标志和额外 cmp Value, NewValue
变得多余 . 如果我们在汇编中写,我们可以省略它,但是在 c/c++
上我们无法使用 ZF
作为条件分支 . 没有像 ifzf { /* if ZF == 1 */ } else { /* if ZF == 0 */ }
这样的语句我们需要写 if (NewValue == Value) {} else {}
,结果必须是生成的程序集中的 cmp NewValue, Value
. 但我如何为 CL
x64
(但不是 x86
!)已经发现了10年(想想所有版本)
这段代码
NewValue = _InterlockedCompareExchange(p, fn(OldValue), OldValue);
if (OldValue == NewValue) ...
转换成
mov eax, OldValue
lock cmpxchg [p], fn(OldValue)
mov NewValue, eax
cmp OldValue, eax ; !!!!
jne @@
....
但这段代码
NewValue = _InterlockedCompareExchange(p, fn(OldValue), OldValue);
if (NewValue == OldValue) ...
转换成
mov eax, OldValue
lock cmpxchg [p], fn(OldValue)
mov NewValue, eax
jne @@
...
所以 CL
理解 cmpxchg
语义并且可以做优化,但仅限于某些情况 .
我在几个测试函数中测试了这个功能,并且两个地方都得到了相同的结果(非常新旧 CL
)
extern "C" long _InterlockedCompareExchange(long volatile * _Destination, long _Exchange, long _Comparand);
typedef long (*FN)(long* pLock, long Value);
#define MAGIC 1
void TestZF1(long* pLock)
{
long Value = *pLock, NewValue;
do
{
Value++;
NewValue = _InterlockedCompareExchange(pLock, Value ^ 1, Value);
} while (
#if MAGIC
NewValue != Value
#else
Value != NewValue
#endif
);
}
long TestZF2(long* pLock, FN fn1, FN fn2)
{
long Value = *pLock, NewValue;
NewValue = _InterlockedCompareExchange(pLock, Value ^ 1, Value);
return (
#if MAGIC
NewValue == Value
#else
Value == NewValue
#endif
? fn1 : fn2) (pLock, NewValue);
}
和生成的程序集:
TestZF1 PROC
mov r8d, DWORD PTR [rcx]
@@loop:
add r8d, 1
mov edx, r8d
mov eax, r8d
xor edx, 1
lock cmpxchg [rcx], edx
IF !MAGIC
cmp r8d,eax ; ! in TestZF1 different exactly in this instruction
ENDIF
jne @@loop
ret 0
TestZF1 ENDP
IF MAGIC
TestZF2 PROC
mov r9d, [rcx]
mov eax, [rcx]
xor r9d, 1
lock cmpxchg [rcx], r9d
cmove r8, rdx
mov edx, eax
jmp r8
TestZF2 ENDP
ELSE
TestZF2 PROC
mov r10d, [rcx]
mov r9d, r10d
xor r9d, 1
mov eax, r10d
lock cmpxchg [rcx], r9d
cmp r10d, eax ; !!!!!!!!
cmove r8, rdx
mov edx, eax
jmp r8
TestZF2 ENDP
ENDIF
几个问题:
-
为什么
CL
x64
优化案例if (NewValue == Value)
但不优化if (Value == NewValue)
? -
这是有意识的,特别设计的,还是突然而且未知?
-
为什么
CL
x86
不做这个优化?我所有测试中的最小值cmp Value,NewValue
指令是否存在 -
是否可以在没有汇编程序的
c/c++
上编写代码,以便在带有CL
的x86上实现它? -
有趣 - 是另一个
c/c++
编译器对_InterlockedCompareExchange[Pointer]
进行了这种优化吗?