Error : Invalid Character '(' in mnemonic

Error : Invalid Character '(' in mnemonic

您好,我正在尝试使用 gcc 7.5 版本在 Linux 上编译以下汇编代码,但不知何故出现错误

Error : Invalid Character '(' in mnemonic

bool InterlockedCompareAndStore128(int *dest,int *newVal,int *oldVal)
{
asm(
  "push  %rbx\n"
  "push  %rdi\n"

  "mov   %rcx, %rdi\n"         // ptr to dest -> RDI
  "mov   8(%rdx), %rcx\n"      // newVal -> RCX:RBX
  "mov   (%rdx), %rbx\n"
  "mov   8(%r8), %rdx\n"       // oldVal -> RDX:RAX
  "mov   (%r8), %rax\n"
  "lock   (%rdi), cmpxchg16b\n"

  "mov   [=10=], %rax\n"
  "jnz    exit\n"
  "inc1   %rax\n"
  "exit:;\n"

  "pop   %rdi\n"
  "pop   %rbx\n"
  );
}

任何人都可以建议如何解决这个问题。检查了汇编代码的许多在线链接和教程,但无法解决确切的问题。 提前感谢您的帮助。

在Windows中我可以看到上述函数的实现是:

function InterlockedCompareExchange128;
asm
      .PUSHNV RBX
      MOV   R10,RCX
      MOV   RBX,R8
      MOV   RCX,RDX
      MOV   RDX,[R9+8]
      MOV   RAX,[R9]
 LOCK CMPXCHG16B [R10]
      MOV   [R9+8],RDX
      MOV   [R9],RAX
      SETZ  AL
      MOVZX EAX, AL
end;

对于 PUSHNV ,我在 Linux 上找不到与此相关的任何内容。所以,基本上我试图在 Linux.

上用 c++ 实现相同的功能

此代码存在许多问题,我不认为告诉您如何解决具体问题对您有任何帮助。

但简短的回答是

"lock   (%rdi), cmpxchg16b\n"

应该是

"lock   cmpxchg16b (%rdi)\n"

Tada,现在可以编译了。好吧,如果 inc1 是一个真正的指令。

但是我不禁注意到这里的指针是int *,也就是4个字节,而不是16个。而且这个函数没有被声明为naked。使用 Extended asm 将使您不必手动推动所有这些寄存器,从而使这段代码比需要的慢很多。

但最重要的是,您应该真正使用 builtins,例如 __atomic_compare_exchange,因为内联 asm 容易出错,不可移植,而且 真的 很难维护。

这里的问题是关于 Invalid Character '(' in mnemonic 的,另一个答案解决了这个问题。

然而,OP 的代码除了这个问题之外还有很多问题。这是(我认为的)解决这个问题的两种更好的方法。请注意,我已经更改了参数的顺序并将它们变为常量。

这个继续使用内联汇编,但使用 Extended asm instead of Basic. While I'm of the don't use inline asm 思想流派,这可能有用或至少有教育意义。

bool InterlockedCompareAndStore128B(__int64 *dest, const __int64 *oldVal, const __int64 *newVal)
{
    bool result;
    __int64 ovl = oldVal[0];
    __int64 ovh = oldVal[1];

    asm volatile ("lock cmpxchg16b %[ptr]"
        : "=@ccz" (result), [ptr] "+m" (*dest),
          "+d" (ovh), "+a" (ovl)
        : "c" (newVal[1]), "b" (newVal[0])
        : "cc", "memory");

        // cmpxchg16b changes rdx:rax to the current value in dest.  Useful if you need
        // to loop until you succeed, but OP's code doesn't save the values, so I'm
        // just following that spec.
        //oldVal[0] = ovl;
        //oldVal[1] = ovh;

        return result;
}

除了解决原代码的问题外,还可以内联,更短。这些约束可能会使其更难阅读,但只有 1 行 asm 的事实可能有助于抵消这一点。如果您想了解约束的含义,请查看 this page (scroll down to x86 family) and the description of flag output constraints(同样,向下滚动 x86 系列 )。

作为替代方案,此代码使用内置的 gcc 并允许编译器生成适当的 asm 指令。请注意,为了获得最佳效果,必须使用 -mcx16 构建。

bool InterlockedCompareAndStore128C(__int128 *dest, const __int128 *oldVal, const __int128 *newVal)
{
    // While a sensible person would use __atomic_compare_exchange_n and let gcc generate
    // cmpxchg16b, gcc decided they needed to turn this into a big hairy function call:
    // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80878
    // In short, if someone wants to compare/exchange against readonly memory, you can't just
    // use cmpxchg16b cuz it would crash.  Why would anyone try to exchange memory that can't
    // be written to? Apparently because it's expected to *not* crash if the compare fails 
    // and nothing gets written.  So no one gets to use that 1 line instruction and everyone 
    // gets an entire routine (that uses MUTEX instead of lockfree) to support this absurd
    // border case.  Sounds dumb to me, but that's where things stand as of 2021-05-07.

    // Use the legacy function instead.
    bool b = __sync_bool_compare_and_swap(dest, *oldVal, *newVal);

    return b;
}

对于人群中的 kibizters,这里是 -m64 -O3 -mcx16 为最后一个生成的代码:

InterlockedCompareAndStore128C(__int128*, __int128 const*, __int128 const*):
    mov     rcx, rdx
    push    rbx
    mov     rax, QWORD PTR [rsi]
    mov     rbx, QWORD PTR [rcx]
    mov     rdx, QWORD PTR [rsi+8]
    mov     rcx, QWORD PTR [rcx+8]
    lock cmpxchg16b XMMWORD PTR [rdi]
    pop     rbx
    sete    al
    ret

如果有人想fiddle,here's神箭link。