ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

GCC内联汇编

2022-06-05 18:32:03  阅读:200  来源: 互联网

标签:汇编 GCC 操作数 gcc 指令 flags 寄存器 内联


1. gcc内联汇编格式

__asm_- __volatile__(指令部: 输出部: 输入部: 损坏部)

gcc内联汇编在处理器变量和寄存器上提供了一个模板和一些约束条件:

(1) 在指令部(Assembler Template)中数字前加上%,如%0、%1等,表示需要使用寄存器的样板操作数。若指令部中用到几个不同的操作数,就说明有几个变量需要和寄存器结合。

(2) 输出部(Output Operands) 用于描述在指令部中可以修改的C语言变量以及约束条件。每个输出约束通常以"="或"+"号开头,"="号表示被修饰的操作数只有可写属性,"+"号表示被修饰的操作数具有可读可写属性。然后是一个字母(表示对操作数类型的说明),接着是关于变量结合的约束。输出部可以是空的。

(3) 输入部(Input Operands) 用来描述在指令部只读取的C语言变量及约束条件。输入部描述的参数只有只读属性,不要试图修改输入部参数的内容,因为gcc编译器假定输入部的参数内容在内嵌汇编之前和之后都是一致的。在输入部中不能使用"="号或者"+"号约束条件,否则就会编译报错。输入部可以是空的。

(4) 损坏部(Clobbers) 一般以"memory"结束。"memory"告诉gcc编译器,内联汇编代码改变了内存中的值,强迫编译器在执行该汇编代码前存储所有缓存的值,在执行完汇编代码之后重新加载该值,目的是防止编译乱序。"cc"表示内嵌代码修改了状态寄存器的相关标志位。


2. 例子

(1) 例1:

static inline unsigned long arch_local_irq_save(void)
{
    unsigned long flags;

    asm volatile(
    "mrs    %0, daif", //读取PSTAT寄存器中的DAIF域到flags变量
    "msr    daifset, #2", //关闭IRQ
    : "=r" (flags)
    :
    : "memory");

    return flags;
}

先看输出部,%0 操作数对应 "=r"(flags),即flags变量,其中"="表示被修饰的操作数的属性是只写。"r"表示使用一个通用寄存器。

接着看输入部,上例中输入部为空,没有指定参数。最后看损坏部,以"memory"结束。

该函数主要用于把PSTATE寄存器中的DAIF域保存到临时变量flags中,然后关闭IRQ。

在输出部和输入部使用"%"来表示参数序号,比"%0"表示第一个参数,"%1"表示第2个参数。

(2) 例2:

为了增强代码的可读性,可以使用汇编符号名字来替代%表示的操作数,如下面add函数:

int add(int i, int j)
{
    int ret;

    asm volatile(
    "add    %w[result], %w[input_i], %w[input_j]"
    : [result] "=r" (ret)
    : [input_i] "r" (i), [input_j] "r" (j)
    );

    return ret;
}

书上Demo编译验证不通过,报错:

$ gcc main.c -o pp
main.c: Assembler messages:
main.c:8: Error: number of operands mismatch for `add'

上述是个简单的gcc内联汇编的例子,主要功能是将i和j的值相加返回结果。先看输出部,表示只定义了一个操作数。"[result]"表示定义了一个汇编符号操作数,符号名为result,它对应"=r"(ret),使用函数中定义的ret变量。在汇编代码中对应%w[result],其中w表示ARM64中的32位通用寄存器。再看输入部,定义了两个操作数,同样使用汇编符号操作数的方式来定义。第一个汇编符号操作数是input_i对应形参i,第二个汇编符号操作数是input_j对应形参j。

 

3. gcc内联汇编操作符和修饰符

操作符/修饰符    说明
=                被修饰的操作数只写
+                被修饰的操作数具有可读可写属性
&                被修饰的操作数只能作为输出

 

4. ARM64特有操作符和修饰符

操作符/修饰符    说明
k                SP寄存器
w                浮点寄存器、SIMD、SVE寄存器
Upl                使用P0到P7中任意一个SVE寄存器
Upa                使用P0到P15中任意一个SVE寄存器
Input            整数,常常用于ADD指令
J                整数,常常用于SUB指令
K                整数,常常用于32位逻辑指令
L                整数,常常用于64位逻辑指令
M                整数,常常用于32位的MOV指令
N                整数,常常用于64位的MOV指令
S                绝对符号地址或标签引用
Y                浮点数,其值为0
Z                整数,其值为0
Ush                表示一个符号的PC相对偏移量的高位部分(大于等于12bit的部分),这个PC相对偏移介于0-4GB
Q                表示没有使用偏移量的单一寄存器的内存地址
Ump                一个适用于SI/DI/SF和DF模式下的加载-存储指令的内存地址。

 

参考:《奔跑吧Linux内核》第2版,此书不怎么样。

 

标签:汇编,GCC,操作数,gcc,指令,flags,寄存器,内联
来源: https://www.cnblogs.com/hellokitty2/p/16344565.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有