RISC-V架构学习——C语言内嵌汇编总结

2023-09-16 17:46:25

1、C语言内嵌汇编的作用

(1)优化:对于特别重要代码进行优化,出于性能的考虑;
(2)C语言需要借助汇编指令来实现特殊功能。比如:C语言中访问系统寄存器就需要借助CSR指令;

2、基础内嵌汇编

2.1、基础内嵌汇编格式

asm asm-qualifiers(AssemblerInstructions)
关键字含义
asm这是内嵌汇编的关键字,表明这是一个GNU扩展
asm-qualifiers修饰词,比如:volatile、inline
AssemblerInstructions要内嵌的汇编语句,如果是多条汇编语句指令,需要使用"\n\t"来换行

(1)基础内嵌汇编指令支持带参数;
(2)gcc编译器不会去解析内嵌汇编指令,当做一个字符串处理;

2.2、基础内嵌汇编举例

#同时内嵌多条汇编指令
asm( "pushl %eax\n\t"
 "movl $0,%eax\n\t"
"popl %eax");

# 也可以将多条的内嵌汇编语句拆开写,效果一样
asm("movl %eax,%ebx");
asm("xorl %ebx,%edx");
asm("movl $0,_booga);

3、扩展内嵌汇编

3.1、扩展内嵌汇编的格式

3.1.1、格式说明

asm关键字 修饰词(
	指令部
	:输出部
	:输入部
	:损坏部
	:GotoLables(goto修饰时才有该部))
关键字含义
asm关键字扩展汇编指令的关键字:__asm__
指令部要内嵌的汇编指令,可以是一条或者多条
输出部用于描述在指令部中可以被修改的C语言变量以及约束条件
输入部用于描述在指令部中只能被读取访问的C语言变量以及约束条件
损坏部告诉编译器内嵌汇编可能带来的影响

3.1.2、修饰词

修饰词含义
volatile用于关闭gcc优化,可参考博客:《C语言中volatile关键字详解以及常见的面试问题》
inline用于内联,gcc会把汇编代码编译成尽可能短的代码
goto用于在汇编代码里跳转到C语言的标签处

3.1.3、内嵌汇编操作符号/修饰符

操作符/修饰符含义
=被修饰的操作数是只写属性
+被修饰的操作数具有可读可写属性
&被修饰的操作数只能作为输出,这个操作数在输入参数的指令执行完成之后才能写入

输出部通常用“=”或者“+”修饰符;输入部分则不能用“=”或者“+”约束条件,否则编译器会报错,因为输入部是用来描述只能读取的C语言变量,不能具有写属性;

3.1.4、操作数约束符

操作符/修饰符含义
p内存地址
m内存变量
r通用寄存器
o内存地址,基地址寻址
i立即数
V内存变量,不允许偏移的内存操作数
n立即数

3.1.5、损坏部介绍

关键字含义
memory告诉编译器,内嵌汇编代码改变了内存中的值,执行完汇编代码后重新加载该值
cc告诉编译器,内嵌汇编代码修改了状态寄存器的相关标志位

3.1.6、指令部的参数表示

3.1.6.1 、用"前缀% + 数字"表示变量

asm volatile(
"add %0, %1, %2" 
: "=r"(res)
: "r"(i), "r"(j)
);

(1)%0对于"=r"(res),%1对应"r"(i),%2对应"r"(j),内嵌汇编的功能:把i+j的结果写到res中;
(2)"r"修饰词,表示该变量需要使用一个通用寄存器;
总结:用%+数字来引用后面输入部和输出部的参数;

3.1.6.2 、用汇编符号来表示变量

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

在输出部和输入部定义变量时就绑定符号,然后在指令部就可以通过符号来引用变量,提高代码的可读性;

3.1.7、goto修饰词介绍

asm goto(
	"addi %0, %0, -1\n"
	"beqz %0, %1[label]\n"
	:
	: "r"(a)
	: "memory"
	: label);
	
	return 0;

label:
	printf("11111\n");

(1)输出部必须是空的。goto是用于跳转功能,在满足某个条件下进行跳转,没有输出数据的必要;
(2)相较于其他情况,goto修饰的情况下多了标签部,表明最后要跳转的标签处;
(3)上面内嵌汇编的功能:当变量a是1时,则跳转到标签label处;

3.2、扩展汇编实例分析

//读取csr寄存器的宏
#define read_csr(csr)						\
({								\
	register unsigned long __v;				\
	__asm__ __volatile__ ("csrr %0, " #csr			\
			      : "=r" (__v) :			\
			      : "memory");			\
	__v;							\
})

unsigned long val;
val = read_csr(mstatus);

//将上面的代码按宏定义展开
val = ({ register unsigned long __v; \
		__asm__ __volatile__ ("csrr %0, " "mstatus" : "=r" (__v) : : "memory");\
		 __v; });

3.3、内嵌汇编和宏结合

//用ATOMIC_OP宏定义内嵌汇编的函数,摘抄自linux源码
#define ATOMIC_OP(op, asm_op, I, asm_type, c_type, prefix)		\
static __always_inline							\
void arch_atomic##prefix##_##op(c_type i, atomic##prefix##_t *v)	\
{									\
	__asm__ __volatile__ (						\
		"	amo" #asm_op "." #asm_type " zero, %1, %0"	\
		: "+A" (v->counter)					\
		: "r" (I)						\
		: "memory");						\
}	

#define ATOMIC_OPS(op, asm_op, I)					\
        ATOMIC_OP (op, asm_op, I, w, int,   )				\
        ATOMIC_OP (op, asm_op, I, d, s64, 64)


ATOMIC_OPS(add, add,  i)

//将上面的宏展开
static __always_inline void arch_atomic_add(int i, atomic_t *v)
{
	__asm__ __volatile__ ( "amoadd.w " zero, %1, %0" 
		: "+A" (v->counter) : 
		"r" (i)
		 : "memory"); 
}
更多推荐

深入学习 Redis Cluster - 基于 Docker、DockerCompose 搭建 Redis 集群,处理故障、扩容方案

目录一、基于Docker、DockerCompose搭建Redis集群1.1、前言1.2、编写shell脚本1.3、执行shell脚本,创建集群配置文件1.4、编写docker-compose.yml文件1.5、启动容器1.6、构建集群1.7、使用集群1.8、如果集群中,有节点挂了,怎么办?二、集群故障、扩容处理2.1

Selenium常见问题解析

1、元素定位失败:在使用Selenium自动化测试时,最常见的问题之一是无法正确地定位元素,这可能导致后续操作失败。解决方法包括使用不同的定位方式(如xpath、CSSselector、id等),等待页面加载完全后再进行操作,或者增加元素定位的鲁棒性。举个例子:假设我们要定位一个登录页面的“用户名”输入框,但是该输入框

docker四种网络模式

文章目录一.为什么要了解docker网络二.docker网络理论三.docker的四类网络模式3.1bridge模式3.2host模式3.3container模式3.4none模式四.bridge模式下容器的通信4.1防火墙开启状态4.2防火墙关闭状态一.为什么要了解docker网络当你开始大规模使用Docker时,你

Docker基础-namespace

Docker-namespacenamespace基础命令dd命令mkfsdfmountunsharepid隔离试验mount隔离namespacenamespace是Linux内核用来隔离内核资源的方式。通过namespace可以让一些进程只能看到与自己相关的一部分资源,而另外一些进程也只能看到与它们自己相关的资源,

Spark 框架概述

目录一、Spark是什么1.1统一分析引擎?二、Spark风雨十年​三、SparkVSHadoop(MapReduce)3.1面试题:Hadoop的基于进程的计算和Spark基于线程方式优缺点?四、Spark四大特点​4.1速度快4.2易于使用4.3通用性强​4.4运行方式五、Spark框架模块5.1介绍5.2Spar

Python+Selenium定位不到元素常见原因及解决办法(报:NoSuchElementException)

这篇文章主要介绍了Python+Selenium定位不到元素常见原因及解决办法(报:NoSuchElementException),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧在做web应用的自动化测试时,定位元素是必不可少的,这个过程经常会碰到定

虹科分享 | 软件供应链攻击如何工作?如何评估软件供应链安全?

说到应用程序和软件,关键词是“更多”。在数字经济需求的推动下,从简化业务运营到创造创新的新收入机会,企业越来越依赖应用程序。云本地应用程序开发更是火上浇油。然而,情况是双向的:这些应用程序通常更复杂,使用的开放源代码比以往任何时候都包含更多的漏洞。此外,威胁行为者正在创造和使用更多的攻击方法和技术,通常是组合在一起的。

华为云云耀云服务器L实例评测|部署功能强大的监控和可视化工具Grafana

应用场景Grafana介绍Grafana是一个功能强大的监控和可视化工具,适用于各种行业和应用场景,如IT运维监控、网络监控、能源管理、金融市场分析等。它提供了灵活的数据源支持、强大的可视化功能和告警机制,以及注释和过滤功能,使得用户能够更好地理解和分析实时数据。下面的监控查询面板都是使用Grafana制作的。一个用于

Rust的注释与文档

rust中//!和///有什么区别?在Rust中,//!和///是特殊注释语法,用于文档注释(DocumentationComments)。它们用于编写文档,并生成Rust代码的API文档。//!用于编写模块级别的文档注释,通常放置在模块的开头。它允许您编写与整个模块相关的文档。这些注释会被Rust编译器解析,生成与模

zookeeper集群

一,zookeeper定义Zookeeper是一个开源的分布式的,为分布式框架提供协调服务的Apache项目。二,zookeeper工作机制Zookeeper从设计模式角度来理解:是一个基于观察者模式设计的分布式服务管理框架,它负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,Zooke

论文笔记 DETR

detr摘要和引言2020论文facebook不需要proposal,不需要基于anchor的先验知识(比如预训练的模型),也不需要NMS进行筛选,直接端到端不需要后处理利用transformer的全局建模能力,看成集合预测问题,不会输出很多冗余的框,直接端到端,不需要NMS,简化了训练和部署NMS:非极大值抑制,抑制

热文推荐