【问题标题】:Error : Invalid Character '(' in mnemonic错误:助记符中的字符“(”无效
【发布时间】:2021-05-11 08:06:33
【问题描述】:

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

错误:助记符中的字符“(”无效

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   $0, %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++ 实现相同的功能。

【问题讨论】:

  • 该代码中有七个(s。编译器指的是哪一个?
  • 您是否考虑过使用某人向您推荐的atomic builtins last time?至于当前的问题,lock (%rdi) 对我来说看起来不太正确,该论点不应该在 cmpxchg16b 之后进行吗?
  • @dratenik 在windows中命令是lock cmpxchg16b [rdi](在delphi中)它在Linux上转换为c++,所以在Linux中src和dest对于mov来说是相反的,但我不太确定锁 。也许你是对的。
  • @dratenik 我尝试了原子内置函数,他们努力获得 8 字节(64 位)的值,但我需要 16 字节(128 位),这是我无法使用原子函数实现的。比如上面的代码中,dest参数应该是16个字节。
  • @molbdnilo 当前指向指令“mov 8(%rdx), %rcx\n”

标签: c++ linux 64-bit


【解决方案1】:

这里的问题是关于 Invalid Character '(' in mnemonic 的其他答案。

但是,OP 的代码存在许多问题之外的问题。这是(我认为是)解决这个问题的两种更好的方法。请注意,我已经更改了参数的顺序并将它们转换为 const。

这个继续使用inline asm,但是使用Extended asm而不是Basic。虽然我属于 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 页面(向下滚动到 x86 系列)和 flag output constraints 的描述(再次,向下滚动到 x86 系列 em>)。

作为替代方案,此代码使用 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

如果有人想摆弄,here'sgodbolt 链接。

【讨论】:

  • 谢谢@David。我们已经使用“__atomic_compare_exchange_n”实现了相同的功能,但很高兴看到您提供的更详细的解释。
  • 很高兴看到您使用了内置功能。确保您查看拆卸。如果您看到的是函数调用而不是 cmpxchg16b,则您可能对性能不满意。
【解决方案2】:

这段代码有很多问题,我不相信我告诉你如何解决具体问题是对你有任何好处。

但简短的回答是

"lock   (%rdi), cmpxchg16b\n"

应该是

"lock   cmpxchg16b (%rdi)\n"

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

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

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

【讨论】:

  • 谢谢@David。它可以编译,但让我试试 atomic 内置函数,因为我在使用 Gcc 7.5 版编译时使用 __int128 遇到的一些错误将很快向您发布更新。
  • 还意味着:您使用 rcx、rdx、r8 作为参数的位置。很确定这是 Windows 的约定,而 Linux 做了一些不同的事情。使用内置函数的另一个原因。
猜你喜欢
  • 2020-06-09
  • 1970-01-01
  • 2015-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-31
  • 2018-11-09
相关资源
最近更新 更多