强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

GCC 完全指南 / 12 - 汇编输出与内联汇编

12 - 汇编输出与内联汇编

学习如何查看 GCC 生成的汇编代码,在 C/C++ 中使用内联汇编,理解 AT&T 与 Intel 语法差异。


12.1 生成汇编输出

基本用法

# 生成汇编文件
gcc -S main.c -o main.s

# 使用 Intel 语法(x86)
gcc -S -masm=intel main.c -o main.s

# 查看汇编与源码对应(-fverbose-asm)
gcc -S -fverbose-asm main.c -o main.s

# 带优化的汇编
gcc -S -O2 main.c -o main_opt.s

# 对比不同优化级别的汇编输出
diff <(gcc -S -O0 main.c -o -) <(gcc -S -O2 main.c -o -)

查看编译器优化效果

# 查看自动向量化是否生效
gcc -S -O2 -ftree-vectorizer-verbose=2 main.c -o main.s 2>&1

# 查看内联决策
gcc -S -O2 -fdump-ipa-inline main.c
# 生成 .inline 文件

# 查看所有优化 pass 的效果
gcc -S -O2 -fdump-tree-all main.c
# 生成大量 .tree.* 文件

12.2 AT&T 与 Intel 语法对比

语法差异

特性AT&T 语法(GCC 默认)Intel 语法(NASM/YASM)
操作数顺序src, destdest, src
寄存器前缀%raxrax
立即数前缀$4242
内存引用(%rax)8(%rax)[rax][rax+8]
操作数大小后缀movl, movqdword ptr, qword ptr
段偏移%gs:0x10gs:[0x10]

AT&T 语法示例

# AT&T 语法(GCC 默认输出)
main:
    pushq   %rbp
    movq    %rsp, %rbp
    subq    $16, %rsp
    movl    $42, -4(%rbp)      # 局部变量
    movl    -4(%rbp), %eax
    addl    $8, %eax
    leave
    ret

Intel 语法示例

; Intel 语法(-masm=intel)
main:
    push    rbp
    mov     rbp, rsp
    sub     rsp, 16
    mov     DWORD PTR [rbp-4], 42    ; 局部变量
    mov     eax, DWORD PTR [rbp-4]
    add     eax, 8
    leave
    ret

GCC 生成 Intel 语法

# 生成 Intel 语法的汇编
gcc -S -masm=intel main.c -o main.s

# 使用 Intel 语法反汇编
objdump -d -M intel main.o

12.3 常见汇编模式识别

函数序言和尾声

# 标准函数序言 (Function Prologue)
func:
    pushq   %rbp              # 保存旧的帧指针
    movq    %rsp, %rbp        # 建立新的帧指针
    subq    $32, %rsp         # 为局部变量分配栈空间

# 标准函数尾声 (Function Epilogue)
    leave                     # 等价于 movq %rbp, %rsp; popq %rbp
    ret                       # 返回

参数传递(x86-64 System V ABI)

# 前 6 个整数参数通过寄存器传递
# 第 1 个参数: %rdi
# 第 2 个参数: %rsi
# 第 3 个参数: %rdx
# 第 4 个参数: %rcx
# 第 5 个参数: %r8
# 第 6 个参数: %r9
# 更多参数: 栈(从右到左入栈)

# 浮点参数: %xmm0 - %xmm7

# 返回值: %rax(整数)/ %xmm0(浮点)

if-else 结构

// C 源码
int max(int a, int b) {
    if (a > b) return a;
    else return b;
}
# 对应汇编
max:
    cmp     %esi, %edi        # 比较 a 和 b
    mov     %esi, %eax        # 预设返回 b
    cmovge  %edi, %eax        # 如果 a >= b,返回 a
    ret

循环结构

// C 源码
int sum(int n) {
    int s = 0;
    for (int i = 0; i < n; i++) s += i;
    return s;
}
# -O2 优化后的汇编
sum:
    test    %edi, %edi
    jle     .L3
    leal    -1(%rdi), %eax
    imull   %edi, %eax
    shrl    $31, %eax
    addl    %edi, %eax
    sarl    %eax
    ret
.L3:
    xorl    %eax, %eax
    ret
# 编译器将循环优化为 n*(n-1)/2 的公式!

12.4 内联汇编基础

基本语法

// 基本内联汇编
asm("指令");

// 扩展内联汇编(带约束)
asm("模板"
    : 输出操作数      // 可选
    : 输入操作数      // 可选
    : 破坏列表);      // 可选

简单示例

#include <stdio.h>

// 获取当前 CPU 周期计数
static inline uint64_t rdtsc(void) {
    uint32_t lo, hi;
    asm volatile("rdtsc"
        : "=a"(lo), "=d"(hi)  // 输出: lo → eax, hi → edx
        :                      // 无输入
        :                      // 无额外破坏
    );
    return ((uint64_t)hi << 32) | lo;
}

int main(void) {
    uint64_t start = rdtsc();
    // ... 某些操作 ...
    uint64_t end = rdtsc();
    printf("Cycles: %lu\n", end - start);
    return 0;
}

常用约束

约束x86-64 含义
"r"任意通用寄存器
"a"%rax/%eax/%ax/%al
"b"%rbx/%ebx
"c"%rcx/%ecx
"d"%rdx/%edx
"S"%rsi/%esi
"D"%rdi/%edi
"i"立即数
"m"内存操作数
"g"寄存器、内存或立即数
"0"与第 0 个操作数相同的位置

输出和输入约束前缀

前缀说明
"="只写(覆盖旧值)
"+读写
"&"早期破坏(输出操作数不与输入重叠)

示例:原子操作

// 原子比较并交换
static inline int atomic_cas(int *ptr, int old_val, int new_val) {
    int result;
    asm volatile(
        "lock cmpxchgl %2, %1"
        : "=a"(result), "+m"(*ptr)
        : "r"(new_val), "0"(old_val)
        : "memory"
    );
    return result;
}

// 原子递增
static inline int atomic_inc(int *ptr) {
    int result;
    asm volatile(
        "lock xaddl %0, %1"
        : "=r"(result), "+m"(*ptr)
        : "0"(1)
        : "memory"
    );
    return result;
}

内存屏障

// 编译器屏障(阻止编译器重排)
#define barrier() asm volatile("" ::: "memory")

// 完整内存屏障
#define mb() asm volatile("mfence" ::: "memory")
#define rmb() asm volatile("lfence" ::: "memory")
#define wmb() asm volatile("sfence" ::: "memory")

12.5 内联汇编高级技巧

多指令模板

// 交换两个值(使用 XOR 交换)
void swap_xor(int *a, int *b) {
    asm volatile(
        "movl (%0), %%eax\n\t"
        "xorl (%1), %%eax\n\t"
        "movl %%eax, (%0)\n\t"
        "xorl (%1), %%eax\n\t"
        "movl %%eax, (%1)\n\t"
        "xorl (%0), %%eax\n\t"
        "movl %%eax, (%0)"
        :
        : "r"(a), "r"(b)
        : "eax", "memory"
    );
}

条件码约束

// 使用 GCC 内联汇编中的条件码
int check_sign(int x) {
    int result;
    asm(
        "testl %1, %1\n\t"
        "setg %b0"              // 设置 result 如果 x > 0
        : "=q"(result)
        : "r"(x)
        : "cc"                  // 告诉编译器条件码被修改
    );
    return result;
}

12.6 GCC Built-in 函数(内联汇编的替代)

很多场景不需要直接写内联汇编,GCC 提供了内置函数。

#include <stdint.h>

// 位操作
int leading_zeros = __builtin_clz(x);    // 前导零计数
int trailing_zeros = __builtin_ctz(x);   // 尾随零计数
int popcount = __builtin_popcount(x);    // 置位计数

// 字节序转换
uint32_t swapped = __builtin_bswap32(x);

// 溢出检查
int overflow = __builtin_add_overflow(a, b, &result);

// 不可达标记
__builtin_unreachable();

// 内存操作
__builtin_memcpy(dst, src, n);
__builtin_memset(dst, val, n);

// CPU 特性检测
if (__builtin_cpu_supports("avx2")) { ... }

12.7 实用汇编分析技巧

对比不同编译器的输出

# GCC 输出
gcc -S -O2 -masm=intel main.c -o gcc_output.s

# Clang 输出
clang -S -O2 -masm=intel main.c -o clang_output.s

# 在线工具: https://godbolt.org/

查看特定函数的汇编

# 反汇编特定函数
objdump -d --disassemble=my_function main.o

# 反汇编并显示源码
objdump -d -S main.o

# 只反汇编 .text 段
objdump -d -j .text main.o

# 使用 Intel 语法反汇编
objdump -d -M intel main.o

Compiler Explorer

Godbolt Compiler Explorer (https://godbolt.org):
  - 在线查看不同编译器/优化级别的汇编输出
  - 源码与汇编高亮对应
  - 支持 GCC、Clang、MSVC 等
  - 支持多种架构 (x86、ARM、RISC-V)
  - 支持不同的汇编语法风格

要点回顾

要点核心内容
-S 选项生成汇编代码,-fverbose-asm 添加注释
AT&T vs Intel操作数顺序相反,AT&T 用 %$ 前缀
内联汇编asm volatile("模板" : 输出 : 输入 : 破坏)
约束"r" 寄存器、"m" 内存、"a" %rax
Built-in__builtin_clz__builtin_popcount 等替代内联汇编
Compiler Explorergodbolt.org 在线查看汇编

注意事项

volatile 很重要: 内联汇编不加 volatile 可能被编译器优化掉。

列出所有破坏的寄存器: 如果汇编修改了寄存器但未在破坏列表中声明,会导致难以调试的 bug。

"memory" 破坏: 如果汇编修改了内存,必须在破坏列表中加上 "memory"

优先使用 Built-in: __builtin_* 函数让编译器有更好的优化机会,可移植性也更好。


扩展阅读


下一步

13 - GCC 扩展特性:了解 GCC 独有的扩展属性、内置函数和向量化支持。