| Register | ABI | Discription | Attribution |
|---|---|---|---|
| x0 | zero | 硬件常数0 | N/A |
| x1 | ra | 返回地址 | Caller |
| x2 | sp | 栈指针 | Callee |
| x3 | gp | 全局指针 | - |
| x4 | tp | 线程指针 | - |
| x5-x7 | t0-t2 | 临时变量 | Caller |
| x8 | s0/fp | 保存寄存器/帧指针 | Callee |
| x9 | s1 | 保存寄存器 | Callee |
| x10-x11 | a0-a1 | 函数参数/返回值 | Caller |
| x12-x17 | a2-a7 | 函数参数 | Caller |
| x18-x27 | s2-s11 | 保存寄存器 | Callee |
| x28-x31 | t3-t6 | 临时变量 | Caller |
| f0-f7 | ft0-ft7 | 浮点临时变量 | Caller |
| f8-f9 | fs0-fs1 | 浮点保存寄存器 | Callee |
| f10-f11 | fa0-fa1 | 浮点函数参数/返回值 | Caller |
| f12-f17 | fa2-fa7 | 浮点函数参数 | Caller |
| f18-f27 | fs2-fs11 | 浮点保存寄存器 | Callee |
| f28-f31 | ft8-ft11 | 浮点临时变量 | Caller |
| label: | opcode | operands | comment |
|---|---|---|---|
| 标签 | 操作码 | 操作数 | 注释 |
I型指令
| imm[11:0] | rs1 | funct3 | rd | opcode |
|---|---|---|---|---|
| [31:20] | [19:15] | [14:12] | [11:7] | [6:0] |
U型指令
| imm[31:12] | rd | opcode |
|---|---|---|
| [31:12] | [11:7] | [6:0] |
S型指令
| imm[11:5] | rs2 | rs1 | imm[4:0] | rd | opcode |
|---|---|---|---|---|---|
| [31:25] | [24:20] | [19:15] | [14:12] | [11:7] | [6:0] |
R型指令
| funct7 | rs2 | rs1 | funct3 | rd | opcode |
|---|---|---|---|---|---|
| [31:25] | [24:20] | [19:15] | [14:12] | [11:7] | [6:0] |
B型指令
J型指令
说明:为了展示指令之间的层次结构,在这里使用了多级无序列表,每个指令后的括号即该指令的英文全称,方便理解相似指令在不同环境下的运用,而大写部分则是指令缩写所提取的字母,另外划有删除线代表本身不存在但具有分支应用的指令;后续也可能会画一个树表作为补充。
add
ADD: | |
add rd,rs |
等效于rd = rd + rs
ADDI: | |
addi rd,rs1,imm |
or
xor(eXclusive OR)
shift
sub(SUBtract)
lui(Load Upper Immediate)
将一个16位的立即数填入寄存器的高16位中,低16位则补零
auipc(Add Upper Immediate to PC)
slt(Set Less Than)
load
store
小于等于、大于判断可以通过颠倒操作数位置来实现,注意逻辑和边际问题即可。
站在芯片运行优化的角度,设计的比较条件应该尽可能倾向于程序顺序执行,以减少在判断跳转之间的时间消耗。
BRANCH: | |
opcode rs1,rs2,LABEL; |
fence
break
call
csr
实际上是由基本指令组合而成
NOP: | |
nop |
LI: | |
li rd,imm; |
.global(.globl)
伪操作,用于定义全局符号,使链接过程中能被其他程序文件可见,类似于extern
.local
伪操作,用于定义局部符号,仅当前程序文件可见
.file
伪操作,用于指示汇编器该汇编程序的逻辑文件名
| opcode | oprands |
|---|---|
| .file | "filename" |
.section
.type
.align
.zero
.byte
.float
.word
.string
.equ
.weak
伪操作,同时创建符号并定义为弱属性。如果该符号已定义,则设置此符号为弱属性
如果符号的属性为弱,则可无需定义具体内容,常用于预留空符号并通过汇编语法检查。当另外存在一个属性为强的同名符号实体时会自动覆盖并链接
.pushsection - .popsection
.marco - .endm
宏
.comm(.common)
.option
以符号 # 或 ; 作为分隔号,当行分隔号以后的部分会被视作注释
指目标寄存器,实际代码中可以是t0、t1等
在C语言文件中嵌入汇编代码,除对固定地址寄存器并直接进行操作外,需要将变量等参数传入汇编程序中,才能保证操作有效,想要传入参数,需要通过操作符列表来完成
asm
为GCC的关键字,表示进行内联汇编操作,也可以使用前后各带两个下划线的__asm__,_asm_是GCC关键字asm的宏定义。
volatile
或者_volatile_、_volatile_或volatile,均为宏定义。如果添加了该关键字,则通知编译器避免对后续括号内添加汇编程序进行任何优化,以保持其原状
汇编指令列表
即需要嵌入的汇编指令,每条指令必须被双引号括起来(作为字符串),两条指令之前必须以“\n”或者“;”作为分隔符。如果没有添加分隔符,相连的字符串将会被合并成为一个字符串。内部语法与汇编程序一样
%
后面跟标签则为指定,若为数字则是从操作符列表顺序选择
[ ]
方括号中的符号名,用于将内联汇编程序中使用的操作数和此操作符通过名称绑定起来。标签名称随意,但不能重复
“ ”
引号中的限制字符串,用于约束此操作数变量的属性,常用的约束如:
r
代表使用编译器自动分配的寄存器来存储该操作数变量
m
字母“m”代表使用内存地址来存储该操作数变量,如果同时指明“rm”则编译器自动选择最优方案
=
对于输出操作数而言,等号代表输出变量用作输出,原来的值会被新值替换,此约束不适用于输入操作数
+
代表输出变量不仅作为输出,还作为输入,此约束不适用于输入操作数
( )
圆括号中填入C/C++变量或者表达式
输出操作符之间需使用逗号分割
输出资源表: [ ]"=r"( ), [ ]"=r"( ), ···
输入资源表: [ ]"r"( ), [ ]"r"( ), ···
影响资源表: " ", "", ···
用于告知编译器当前内联汇编语句可能会对某些寄存器或内存进行修改,使得编译器在优化时将其因素考虑进去
%[字符]中明确的符号命名指定之外,还可以使用%数字的方式进行隐含指定。从0开始,依次表示操作符列表中所有的输出操作数与输入操作数。int abc = 10,cba = 20; | |
printf("%d & %d\n",abc,cba); | |
asm volatile( | |
"mv %[da],%3\n" | |
"add %2,%1,%0\n" | |
"li %3,12\n" | |
: [da]"=r"(abc), "=r"(cba) // Working | |
: "r"(abc), [db]"r"(cba) // Working | |
: "t1", "t2"//, "x7" | |
); | |
printf("%d & %d\n",abc,cba); |
输出结果为:
10 20 (修改前) | |
40 12 (修改后) |