添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

最近在学习原子变量底层实现的过程中,看到了一些 在 C/C++ 中嵌入汇编代码 的例子,因此学习了一下相关的语法规则,有了这篇总结,便于将来查阅。

注意:本文仅适用于 GCC,并且 x86-64 汇编采用 AT&T 语法,不适用于 ARM64(如 Apple Silicon 芯片)。MSVC 内嵌汇编的语法可能有所不同。

1. 基本语法

在 C/C++ 的语言规范中,有一个 asm 关键字,它便是用来在 C/C++ 代码中内嵌汇编代码的。其基本语法如下:

asm("assembly code");
__asm__("assembly code");

可以认为上面两种语法没有差别,因此后文都采取更简洁的 asm 形式。

一个完整的例子:

#include <stdio.h>
int main() {
  /* 把 10 个 20 加起来,将结果存储到 eax 寄存器中。 */
  asm("movl $10, %eax;"
      "movl $20, %ebx;"
      "addl %ebx, %eax;");
  return 0;

可以看到,基本就是只需要用 asm 把汇编代码包起来就可以了。

2. 扩展语法

除了能执行一些简单的机器指令外,更多时候我们需要在 C++ 和汇编之间进行「数据沟通」,即「在汇编代码中读写 C++ 代码中的变量」。此时就需要用到扩展语法,它允许我们指定每条指令的「输入输出」,其基本形式如下:

asm ("assembly code"
     : output operands                  /* optional */
     : input operands                   /* optional */
     : list of clobbered registers      /* optional */

即,在汇编后面用冒号 : 增加一些可选的输出操作数、输入操作数以及「会被操作搞乱(clobbered)的寄存器」。

接下来看几个例子就能明白了:

asm("movl %%eax, %0;" : "=r"(val));

这段代码的意思是eax 寄存器的内容存储到 val 变量中。更多语法细节:

  • 这里额外指定了 1 个输出操作数——"=r"(val),前面的字符串 "=r" 被称为操作数约束,后面在括号里放一个 C++ 表达式,代表某个变量:
  • r 约束的意思是 val 操作数只能被存储在通用寄存器中;
  • = 的意思是该操作数会被写入
  • %0 就代指第一个操作数(从 0 开始)。
  • 这里引用寄存器的方式变成了使用 2 个 %:%%eax,这是为了与 %0 做出区分。
  • 另一个更复杂的例子:对两个整数求和,将结果存储在第三个数中:

    #include <cstdio>
    int main() {
      int a = 2;
      int b = 3;
      int y;
      asm("movl %1, %%eax;"
          "movl %2, %%ebx;"
          "addl %%ebx, %%eax;"
          "movl %%eax, %0;"
          : "=r"(y)        /* output */
          : "r"(a), "r"(b) /* inputs */
          : "%eax", "%ebx" /* clobbered registers */
      printf("y=%d\n", y); 
      return 0;
    

    运行结果:

    除了使用序号 %0 %1 等来引用操作数外,还可以给操作数指定名字,这样在汇编代码中引用时会方便一些:

    asm("movl %[a], %%eax;"
        "movl %[b], %%ebx;"
        "addl %%ebx, %%eax;"
        "movl %%eax, %[y];"
        : [y] "=r"(y)        /* output */
        : [a] "r"(a), [b] "r"(b) /* inputs */
        : "%eax", "%ebx" /* clobbered registers */
    

    在上述代码中,汇编代码中引用变量时,使用的是 %[name] 的形式,而在指定输入输出时,使用的是 [name] 的形式。

    如果能看懂上面这段代码,那么绝大多数 C++ 内嵌汇编应该都能读懂了。如果想了解更多用法,请参考 GCC 官方文档: Using Assembly Language with C (Using the GNU Compiler Collection (GCC)) .

  • Using Inline Assembly in C/C++ - CodeProject
  • Using Assembly Language with C (Using the GNU Compiler Collection (GCC))
  •