16 同步与互斥(二) 内联汇编

发布时间 2023-04-02 16:36:44作者: 人民广场的二道贩子

汇编参考
GCC工具链使用

1 引入

内联汇编即在C函数中使用汇编去实现某些功能。

为什么需要在C代码中引入汇编?

  • C语言在大多数的时候效率都很高,但在某些情况下直接使用汇编效率会更加高效
  • 部分功能通过C语言很难实现,这部分代码就需要使用汇编去实现(实现某些功能

在C文件中使用汇编存在两种方法

  • .s文件去实现某个功能函数,在C文件中去调用(缺点:需要另外建一个文件)
  • 在C文件中直接使用汇编代码去实现

1.1 示例

1.1.1 C语言实现add

通过C语言实现add

#include <stdio.h>

int add(int a, int b)
{
    return a + b;
}

int main(int argc, char **argv)
{
    int a = 1;
    int b = 2;
    int sum;

    sum = add(a, b);

    printf("sum:%d\n", sum);
    return 0;
}

arm-linux-objdump -D test > test.dis

00010410 <add>:
   10410:       e52db004        push    {fp}            ; (str fp, [sp, #-4]!)
   10414:       e28db000        add     fp, sp, #0
   10418:       e24dd00c        sub     sp, sp, #12
   1041c:       e50b0008        str     r0, [fp, #-8]
   10420:       e50b100c        str     r1, [fp, #-12]
   10424:       e51b2008        ldr     r2, [fp, #-8]
   10428:       e51b300c        ldr     r3, [fp, #-12]
   1042c:       e0823003        add     r3, r2, r3			//这句实现add
   10430:       e1a00003        mov     r0, r3
   10434:       e28bd000        add     sp, fp, #0
   10438:       e49db004        pop     {fp}            ; (ldr fp, [sp], #4)
   1043c:       e12fff1e        bx      lr

通过反汇编文件中add信息可以看出add函数有很多操作入栈,出栈的操作所以效率并不是极致的

2 内联汇编语法

  • asm

    一般写作__asm__,表示这是一段内联汇编

  • asm-qualifiers

    存在三个值:volatile(常用)、inline、goto

  • AssmblerTemplate

    汇编指令。用""双引号包含,用\n分开

    "mov %0, %1\n"
    
  • OutputOperands

    输出结果保存在这里

    格式:

    [ [asmSymbolicName] ] constraint (cvariablename)

    • asmSymbolicName

      可以不写

    • constraint

      表示约束

      常用值

      constraint 描述
      m memory operand,表示传入的地址,需要CPU支持的地址
      r register operand,使用这个寄存器保存
      i immediate integer operand,传入一个立即数

      数据修饰

      constraint Modifier Characters 描述
      = 内联汇编会去修改;即写
      + 即能读能写
      & earlyclobber;gcc尚未使用完全部的输入操作数时,就产生了某些输出操作

      关于earlyclobber(&)

      在内联汇编中没有指定使用的具体寄存器。这会导致某个寄存器会被反复使用,前面使用过此寄存器保存的值,会因为后面再次使用而被修改

      此时使用earlyclobber(&)则是告诉编译器对于某个操作它所使用的寄存器不能跟其他操作数共用寄存器

      示例

      "=&r"(sum)

      %0的操作不能和其他%1%2等共用寄存器

    • cvariablename

      变量名;为输出指定一个C左值

    • 示例.

      "=r" (sum)

      汇编中会将r寄存器中的值写入sum变量

  • InputOperands

    格式(基本与OutputOperands相同):

    [ [asmSymbolicName] ] constraint (cexpression)

    • cexpression

      这里是表达式

    • 示例

      "r"(a), "r"(b)

      a放入某个寄存器r,将b放入某个寄存器r;可以使用%0%1调用

  • Clobbers

    声明汇编中会修改的事件(寄存器,内存)

    Clobbers 描述
    cc 表示汇编代码会修改寄存器
    memory 表示汇编代码除开InputOperands和OutputOperands外还会操作内存

OutputOperands和InputOperands引用

:后开始依次为%0 %1 %2 ...

3 内联汇编实现add

int add(int a, int b)
{
    int sum;

    __asm__ volatile (
        "add %0, %1, %2"
        :"=&r"(sum)
        :"r"(a), "r"(b)
        :"cc"
    );

    return sum;
}

没使用&

1042c:       e0833002        add     r3, r3, r2

使用&

1042c:       e0823001        add     r3, r2, r1