我正在尝试为ia32的CMPXCHG8B编写GCC内联asm。不,我不能使用__sync_bool_compare_and_swap
。它必须与-fPIC和不与-fPIC一起使用。
到目前为止,我最好的(编辑:毕竟行不通,有关详细信息,请参阅下面的我自己的答案)是
register int32 ebx_val asm("ebx")= set & 0xFFFFFFFF;
asm ("lock; cmpxchg8b %0;"
"setz %1;"
: "+m" (*a), "=q" (ret), "+A" (*cmp)
: "r" (ebx_val), "c" ((int32)(set >> 32))
: "flags")
但是我不确定这是否正确。
由于PIC,我无法对ebx_val执行
"b" ((int32)(set & 0xFFFFFFFF))
,但显然register asm("ebx")
变量已被编译器接受。奖励:ret变量用于分支,因此代码最终看起来像这样:
cmpxchg8b [edi];
setz cl;
cmp cl, 0;
je foo;
任何想法如何描述输出操作数,使其变为:
cmpxchg8b [edi]
jz foo
?
谢谢。
最佳答案
以下内容怎么样?在一个小测试中,以下内容似乎对我有用:
int sbcas(uint64_t* ptr, uint64_t oldval, uint64_t newval)
{
int changed = 0;
__asm__ (
"push %%ebx\n\t" // -fPIC uses ebx, so save it
"mov %5, %%ebx\n\t" // load ebx with needed value
"lock\n\t"
"cmpxchg8b %0\n\t" // perform CAS operation
"setz %%al\n\t" // eax potentially modified anyway
"movzx %%al, %1\n\t" // store result of comparison in 'changed'
"pop %%ebx\n\t" // restore ebx
: "+m" (*ptr), "=r" (changed)
: "d" ((uint32_t)(oldval >> 32)), "a" ((uint32_t)(oldval & 0xffffffff)), "c" ((uint32_t)(newval >> 32)), "r" ((uint32_t)(newval & 0xffffffff))
: "flags", "memory"
);
return changed;
}
如果这也被错误编译,您能否提供一个触发此行为的小片段?
关于奖励问题,我认为不可能使用
cmpxchg8b
指令中的条件代码在汇编程序块之后分支(除非您使用asm goto
或类似功能)。从GNU C Language Extensions:寻找一种方法来访问汇编指令留下的条件代码是很自然的想法。但是,当我们尝试实现此功能时,我们找不到使它可靠运行的方法。问题在于输出操作数可能需要重新加载,这将导致其他后续的“存储”指令。在大多数机器上,这些指令会在没有时间对其进行测试之前更改条件代码。普通的“测试”和“比较”指令不会出现此问题,因为它们没有任何输出操作数。
编辑:我找不到任何指定一种方式或其他方式的源,也可以使用
%N
输入值来修改堆栈(This古代链接说“您甚至可以将寄存器推入堆栈,请使用它们,然后放回去。”,但该示例没有输入)。但是应该可以不将值固定在其他寄存器上而这样做:
int sbcas(uint64_t* ptr, uint64_t oldval, uint64_t newval)
{
int changed = 0;
__asm__ (
"push %%ebx\n\t" // -fPIC uses ebx
"mov %%edi, %%ebx\n\t" // load ebx with needed value
"lock\n\t"
"cmpxchg8b (%%esi)\n\t"
"setz %%al\n\t" // eax potentially modified anyway
"movzx %%al, %1\n\t"
"pop %%ebx\n\t"
: "+S" (ptr), "=a" (changed)
: "0" (ptr), "d" ((uint32_t)(oldval >> 32)), "a" ((uint32_t)(oldval & 0xffffffff)), "c" ((uint32_t)(newval >> 32)), "D" ((uint32_t)(newval & 0xffffffff))
: "flags", "memory"
);
return changed;
}