Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
将CMPXCHG8B封装在GCC内联汇编中的正确方法,32位_Gcc_Assembly_Inline Assembly_X86 - Fatal编程技术网

将CMPXCHG8B封装在GCC内联汇编中的正确方法,32位

将CMPXCHG8B封装在GCC内联汇编中的正确方法,32位,gcc,assembly,inline-assembly,x86,Gcc,Assembly,Inline Assembly,X86,我正在尝试为ia32的CMPXCHG8B编写GCC内联asm。不,我不能使用\u同步\u bool\u比较\u和\u交换。它必须使用和不使用-fPIC 到目前为止,我所做的最好的(编辑:毕竟不起作用,请参阅下面我自己的答案了解详细信息)是 但我不确定这是否正确 由于PIC的原因,我无法对ebx值执行“b”((int32)(set&0xFFFFFFFF)),但显然寄存器asm(“ebx”)变量被编译器接受 BONUS:ret变量用于分支,因此代码最终如下所示: cmpxchg8b [edi]; s

我正在尝试为ia32的CMPXCHG8B编写GCC内联asm。不,我不能使用
\u同步\u bool\u比较\u和\u交换
。它必须使用和不使用-fPIC

到目前为止,我所做的最好的(编辑:毕竟不起作用,请参阅下面我自己的答案了解详细信息)是

但我不确定这是否正确

由于PIC的原因,我无法对ebx值执行
“b”((int32)(set&0xFFFFFFFF))
,但显然
寄存器asm(“ebx”)
变量被编译器接受

BONUS:ret变量用于分支,因此代码最终如下所示:

cmpxchg8b [edi];
setz cl;
cmp cl, 0;
je foo;
了解如何描述输出操作数,使其成为:

cmpxchg8b [edi]
jz foo
?

谢谢。

这是我的:

bool
spin_lock(int64_t* lock, int64_t thread_id, int tries)
{
    register int32_t pic_hack asm("ebx") = thread_id & 0xffffffff;
retry:
    if (tries-- > 0) {
        asm goto ("lock cmpxchg8b %0; jnz %l[retry]"
                  :
                  : "m" (*lock), "A" ((int64_t) 0),
                    "c" ((int32_t) (thread_id >> 32)), "r" (pic_hack)
                  :
                  : retry);
        return true;
    }
    return false;
}

它使用了GCC4.5新增的
asm goto
特性,允许从内联程序集跳转到C标签。(哦,我看到你关于必须支持gcc旧版本的评论。哦,好吧。我试过了。:-P)

令人惊讶的是,在某些情况下,问题中的代码片段仍然会出错:如果在EBX寄存器设置为

寄存器asm
之前,第0个asm操作数可以通过EBX(PIC)间接寻址,然后,gcc在将操作数分配给
set&0xFFFFFFFF
后,继续通过EBX加载操作数

这就是我现在正在努力实现的代码:(编辑:避免推/弹出)


这里的想法是在关闭EBX之前加载操作数,同时在为CMPXCHG8B设置EBX值时避免任何间接寻址。我为操作数的下半部分修复了硬寄存器ESI,因为如果我不修复,GCC可以随意重用任何其他已经占用的寄存器,只要它可以证明值相等。EDI寄存器是手动保存的,因为简单地将其添加到被删除的寄存器列表中会导致GCC“无法重新加载”,这可能是由于寄存器压力高。保存EDI时避免了PUSH/POP,因为其他操作数可能是ESP寻址的。

以下内容如何,在一个小测试中似乎对我有效:

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
或类似功能)。发件人:

寻找一种方法来访问汇编指令留下的条件代码是很自然的想法。然而,当我们试图实现它时,我们发现没有办法使它可靠地工作。问题是,输出操作数可能需要重新加载,这将导致附加的“存储”指令。在大多数机器上,这些指令会在测试之前改变条件代码。普通的“测试”和“比较”指令不会出现此问题,因为它们没有任何输出操作数

编辑:在使用
%N
输入值的同时,我找不到任何指定修改堆栈的方法的源代码(古代链接说“您甚至可以将寄存器推到堆栈上,使用它们,然后再放回去。”但示例中没有输入)

但也可以不通过将值固定到其他寄存器:

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;
}

编译器内部不与-fPIC一起工作的事实只是一个彻底的编译器错误:当你不得不处理一个坏掉的编译器时,它很糟糕,所以你可能想把自己放在该错误的CC列表上。你在IA32上使用
-fPIC
?我很好奇为什么。@Gabe-这在编写共享库对象时是最重要的。Ulrich Drepper在这方面有一篇很好的论文:@Crash:这是一篇有趣的论文,但它似乎没有提到PIC在IA32上的相对优势。我知道它在链接方面有优势——加载速度更快,占用的内存更少——但共享库通常每个进程只加载一次,现在的计算机往往有千兆字节的内存。我可以理解为什么您希望在相对便宜的x64或IA64上使用PIC,但IA32(x86)呢?@Crashworks,即使不存在该缺陷,我也无法使用内置代码:(必须支持非常旧的编译器。谢谢!我喜欢我的代码已经与你的代码非常相似,尽管我当然不能使用asm goto。不过有几个问题:1)为什么*锁操作数仅为输入而不是输入/输出?2)为什么EFLAGS不在已关闭的寄存器列表中?@Laurynas:1<代码>asm goto不能有任何输出限制(当前限制可能在以后的gcc版本中删除);因为我不“关心”锁的当前值(我们不尝试递归锁定;-),所以这是可以接受的。2.因为
asm goto
的示例中也没有它(是的,它也有条件跳转),所以我假设
asm goto
默认情况下会有一个clobbered标志。对于未来的读者:gcc对x86和x86-64的机器定义使得每个内联asm语句都隐式包含一个
“cc”
clobber。您永远不需要为x86 asm显式编写一个。asm语句确实会修改内容,因此它需要使用
“memory”
clobber,并在它可以更改的所有reg上注册clobber。此外,为了实际使用,在重试情况下,通常最好在
暂停
+非锁定加载上旋转,而不是用
锁定
ed指令敲打(导致争用,从而延迟线程尝试解锁)。我知道您仅将
cmpxchg8b
用于自旋锁作为示例来演示
asm goto
,但不需要将cmpxchg用于自旋锁。非常感谢。我看到的一个问题与我的代码段相同:编译器可能通过ESP寻址%0,但无法判断ESP是否已通过push/pop更改。另外,谢谢你提供的信息。输出中的条件代码,它已经证实了我的怀疑。我不知道
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;
}
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;
}