博客
关于我
GCC 内联汇编
阅读量:432 次
发布时间:2019-03-06

本文共 2879 字,大约阅读时间需要 9 分钟。

GNU C 内联汇编详解

内联汇编(Inline Assembly)是 GNU C 提供的一项强大特性,允许程序员在 C 代码中嵌入汇编代码。通过内联汇编,程序员可以结合 C 和汇编语言的优势,实现更高效的代码生成。然而,内联汇编的使用需要程序员对编译器的代码生成机制有深刻理解,否则可能导致代码错误或性能问题。本文将从基础到高级内容,详细介绍 GNU C 的内联汇编特性。


一、基本内联汇编

基本内联汇编是 GCC 对内联汇编的最简陋支持,主要用于理解其工作原理。基本内联汇编的语法非常简单,形式为:

asm [修饰符] ( 汇编指令 );

这里的修饰符包括 volatileinline,用于控制编译器对汇编代码的处理方式。asm 是 GNU C 提供的关键字,用于标识内联汇编块。需要注意的是,使用 asm 关键字时,若启用了 ISO C 的编译选项(如 -std=c99),则代码可能无法通过编译。因此,程序员通常使用 __asm__ 替代 asm,以确保兼容 ISO C 标准。

示例代码

__asm__ __volatile__(    "movq %rax, %rdi \n\t"      // 将 rax 寄存器的值移动到 rdi 寄存器    "movq %rbx, %rsi \n\t";     // 将 rbx 寄存器的值移动到 rsi 寄存器);

这个代码将 rax 和 rbx 寄存器的值分别移动到 rdi 和 rsi 寄存器中。编译器会直接将这些汇编指令插入到生成的机器代码中,而不会对这些代码进行优化或解析。

示例效果

编译后,反汇编结果会显示这些汇编指令被直接插入到代码中。然而,由于编译器与程序员之间缺乏信息交流,基本内联汇编的使用非常不灵活,容易导致代码错误。


二、拓展内联汇编

拓展内联汇编(Extended Inline Assembly)通过增加对编译器的信息交互,解决了基本内联汇编的局限性。程序员可以通过提供更多信息,指导编译器生成更高质量的代码。拓展内联汇编的语法结构更为复杂,形式为:

asm [修饰符] ( [汇编模板] : [输出变量] : [输入变量] : [修改的寄存器] );

核心原理

  • 信息交互:拓展内联汇编要求程序员明确指出汇编代码对输入、输出和修改寄存器的依赖关系。
  • 输出变量:程序员可以指定输出变量的分配方式,如分配到寄存器或内存。
  • 输入变量:程序员可以指定输入变量的来源,如从寄存器或内存中获取。
  • 修改列表:程序员需要告知编译器汇编代码可能修改了哪些寄存器或内存位置。
  • 示例代码

    int bittest(unsigned long long val, unsigned long long bit) {    int ret;    __asm__ __volatile__(        "movl $0, %0 \n\t"       // %0 代表 ret 寄存器        "btq %2, %1 \n\t"       // %1 代表 val 寄存器,%2 代表 bit 寄存器        "jnc %l1 \n\t"          // %l1 是 C 标签        "movl $1, %0 \n\t"       // 如果 CF 标记为 1,设置 ret 为 1        "dec %1 \n\t"            // 减少 val 的值以便返回        : "=&rm" (ret)          // ret 是输出变量,必须为左值        : "r" (val), "r" (bit)  // val 和 bit 是输入变量        : "cc", "memory", "%l1"; // 可能修改的寄存器和内存    );    return ret;}

    这个代码通过拓展内联汇编,检查 val 的特定位移位是否为 1。编译器会根据程序员提供的信息,确保 ret 变量的正确生成和返回。


    三、约束与修饰符

    1. 约束(Constraints)

    约束用于指定输入输出变量的位置和类型。常见约束包括:

    • r:通用寄存器
    • i:整型字面值
    • n:立即数
    • g:任意寄存器、内存或立即数
    • m:内存地址

    2. 修饰符(Modifiers)

    修饰符用于控制输出变量的行为,常见修饰符包括:

    • =:表示输出变量是只写的。
    • +:表示输出变量是可读写的。
    • &:表示输出变量与输入变量不重叠。
    • %:表示输出变量可以交换次序。

    示例代码

    __asm__ __volatile__(    "mov %1, %0 \n\t"       // %0 是输出变量,%1 是输入变量    : "+r" (output)         // 输出变量分配到寄存器中    : "r" (input)            // 输入变量分配到寄存器中    : "cc", "memory";        // 可能修改的寄存器和内存);

    四、输入与输出

    输入列表

    输入列表用于指定汇编代码的输入变量。语法形式为:

    [asmSymbolicName] constraint (cexpression)

    输出列表

    输出列表用于指定汇编代码的输出变量。语法形式为:

    [asmSymbolicName] constraint (cvariablename)

    五、修改列表

    修改列表用于告知编译器汇编代码可能修改了哪些寄存器或内存位置。语法形式为:

    : Clobber (cexpression)

    六、特殊寄存器与平台特性

    1. 标记寄存器

    在某些平台(如 x86),GCC 支持将标记寄存器中的标记输出到 C 变量中。标记寄存器通常用于检查条件(如除法的异常标志)。

    2. 平台特性

    • x86:支持 a(ax)、b(bx)、c(cx)、d(dx)、S(si)、D(di)等寄存器。
    • RISC-V:支持 f(浮点寄存器)、I(12 位立即数)、J(整数 0)、K(5 位无符号立即数)、A(地址寄存器)。

    七、寄存器变量

    寄存器变量是 ISO C 标准中对寄存器分配的特性扩展。程序员可以通过以下语法指定变量分配到特定寄存器中:

    register type cvariable asm ("寄存器名");

    示例代码

    register unsigned long long sum asm ("rax");  // 将 sum 分配到 rax 寄存器

    八、总结

  • 原则

    • 尽量避免使用宏和伪指令。
    • 使用 = constraint 时,不假设输入变量已被加载到寄存器中。
    • 只有在必要时才修改输入变量。
    • 尽可能提供全面信息。
    • 注意输出与输入的重叠问题,使用 & constraint 解决。
  • 注意事项

    • 小心打字错误,避免段错误。
    • 避免使用指令的操作对象类型错误。
  • 通过合理使用 GNU C 的内联汇编特性,程序员可以显著提升代码性能和可读性。

    转载地址:http://jmdyz.baihongyu.com/

    你可能感兴趣的文章
    Openlayers中多图层遮挡时调整图层上下顺序
    查看>>
    Openlayers中将某个feature置于最上层
    查看>>
    Openlayers中点击地图获取坐标并输出
    查看>>
    Openlayers中设置定时绘制和清理直线图层
    查看>>
    Openlayers图文版实战,vue项目从0到1做基础配置
    查看>>
    Openlayers实战:modifystart、modifyend互动示例
    查看>>
    Openlayers实战:判断共享单车是否在电子围栏内
    查看>>
    Openlayers实战:加载Bing地图
    查看>>
    Openlayers实战:绘制图形,导出geojson文件
    查看>>
    Openlayers实战:绘制图形,导出KML文件
    查看>>
    Openlayers实战:绘制多边形,导出CSV文件
    查看>>
    Openlayers实战:绘制带箭头的线
    查看>>
    Openlayers实战:输入WKT数据,输出GML、Polyline、GeoJSON格式数据
    查看>>
    Openlayers高级交互(10/20):绘制矩形,截取对应部分的地图并保存
    查看>>
    Openlayers高级交互(11/20):显示带箭头的线段轨迹,箭头居中
    查看>>
    Openlayers高级交互(14/20):汽车移动轨迹动画(开始、暂停、结束)
    查看>>
    Openlayers高级交互(15/20):显示海量多边形,10ms加载完成
    查看>>
    Openlayers高级交互(16/20):两个多边形的交集、差集、并集处理
    查看>>
    Openlayers高级交互(17/20):通过坐标显示多边形,计算出最大幅宽
    查看>>
    Openlayers高级交互(19/20): 地图上点击某处,列表中显示对应位置
    查看>>