ARM64基础4:在C语言中嵌入ARM64汇编代码

如题,有时为了提高部分代码运行性能,可以将部分C代码实现,用汇编改写(自己编写常比编译器优化效果更好)

1.先看案例:


#include <stdio.h>                                                                                          

static int compare_data(int a, int b)
{
	int val;
	__asm__ __volatile__ (
	    "cmp %1, %2\n"
	    "csel %0, %1, %2, hi\n"
	    : "+r" (val)
	    : "r" (a), "r" (b) 
	    : "memory");

	return val;
}

int main()
{
	int val;

	val = compare_data(5, 6); 
	printf("big data: %d\n", val);

	val = compare_data(6, 4); 
	printf("big data: %d\n", val);
	
	return 0;
}            

编译运行:

aarch64-linux-gnu-gcc -o main main.c --static -g
# ./main
big data: 6
big data: 6

2.语法解析:

C/C++中嵌入汇编,常用格式如下:

__asm__ [__volatile__] ( assembler template 
           : [output operand list]             /* optional */
           : [input operand list]              /* optional */
           : [clobbered register list]         /* optional */
           );

关键字__asm__:其实也可以写成“asm”。但是“asm”并不是所有版本的GCC编译器都支持的,而且可能有命名冲突问题,所以用“asm”的话,兼容性更好。

关键字__volatile__:也可以写“volatile”,理由同上;__volatile__是可选的,作用是禁止编译器对后面汇编指令再进行优化。一般自己写的汇编,考虑性能,已经做过优化,编译器再优化的话,可能效果反而更差,所以通常还是带上这个关键字;

括号里:是真正的汇编代码,主要有四部分组成,第一部分是具体的汇编代码,是必须的;其他三个为辅助参数,可选;各部分之间用冒号“:”分割,即使参数为空,也要加冒号;

2.1 assembler template:

所有汇编代码必须用双引号括起来,如果多行汇编代码,每一条语句都要用双引号括起来,代码后面加上换行符("\n"或"\n\t"),具体形式如下:

__asm__ __volatile__ ( "instruction 1\n\t" 
           "instruction 2\n\t"
           ......
           "last instruction"
           );

注:即使一行汇编代码也没有,也要传入空字符串"",否则会报错;

2.2 [output/input operand list] :输出/入参数列表;

分别对应汇编与C语言交互的C表达式,若有多个,用逗号隔开;

在前面汇编中,还可以用"%n",直接引用后面的操作数;

__asm__("mov %0, %1, ror #1" 
            : "=r"(result) 
            : "r"(value)
            );

这里%0代表第一个操作数,result变量;
%1代表第二个操作数,即value变量;

操作数格式:

每一个操作数,最多由四部分组成:

[name]"modifier+constraint"(C expression)

name:别名,可选;
modifier:修改符,要用双引号括起来;
constraint:限定符,要用双引号括起来;
c表达式:用小括号括起来;

(1)如果指定了别名的话,那在汇编模板中,引用该变量,就可以使用别名,增加可读性,例如:

int x=10, y;
__asm__ ("mov %[in],%[out]"
   : [out]"=r"(y)
   : [in]"r"(x)
   :
);

(2) 说说限定符,操作数在这里的作用是将C语言定义的变量与汇编中使用的变量进行一 一对应,但并不是所有的汇编指令都可以接受任何类型变量,因此汇编器需要知道这些变量到底用在什么地方,传递前做一些转换。常用限定符如下表

限定符ARM指令集含义
r通用寄存器
f浮点寄存器
m内存地址

(3)修改符号,在限定符之前,可选,为空的话,表明该操作数是只读的。
GCC定义了三个修改符,分别是:

修改符含义
=只写操作数,一般用于输出操作数中
+可读且可写
&寄存器只能用于输出

如果想让一个C变量既作为输入操作数,也作为输出操作数的话,可以使用“+”限定符,并且这个操作数只需要在输出操作数列表中列出就行了。例如:

__asm__("mov %0, %0, ror #1" 
        : "=r"(y)
        : "0"(y)
        );

&”:为了避免编译器优化输出和输入使用同一个寄存器,可在输出操作数中使用“&”修改符,明确告诉编译器,代表输出操作数的寄存器一定不能使用输入操作数已经使用过的寄存器。

2.3 修改寄存器列表

为保持寄存器,内存数据一致性,提供三个类型

类型作用
r0…r15告诉编译器汇编代码中修改了通用寄存器r0…r15
cc告诉编译器汇编代码会导致CPU状态位的改变
memory告诉编译器汇编代码会读取或修改内存中某个地址存放的值

添加“memory”之后,编译器会在执行汇编代码之前,保证将保存在寄存器中,没有更新到内存中的值全部都写入到内存中。
此列表中的每一项都要用双引号("")括起来,每项之间要用逗号(“,”)分割。

  • 1
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值