1.环境及优化项
采用-O2优化选项,通过arm处理器架构下的gcc编译器编译用例生成汇编码查看其生成的指令。至于为什么用O2选项,是因为在某些用例中,加入-O3选项之后,arm处理器架构下gcc编译器生成的汇编会变得更加复杂(比如一个简单的循环)。
2.ARM指令格式
基本格式
<opcode> { <cond>} {S} <Rd> , <Rn> , {<opcode2>}
其中,<>内的项是必须的,{}内的项是可选的,比如<opcode>是指令助记符,是必须的,而{<cond>}为 指令执行条件,是可选的,如果不写则使用默认条件AL(无条件执行)。
格式 |
含义 |
Opcode |
指令助记符,如LDR,STR 等 |
Cond |
执行条件,如EQ,NE 等 |
S |
是否影响CPSR 寄存器的值,书写时影响CPSR,否则不影响 |
Rd |
目标寄存器 |
Rn |
第一个操作数的寄存器 |
operand2 |
第二个操作数 |
指令格式举例如下
LDR R0,[R1] ; 读取R1 地址上的存储器单元内容,执行条件AL(无条件执行)
ADDS R1,R1,#1 ; 加法指令,R1+1=R1 影响CPSR 寄存器,带有S
SUBNES R1,R1,#0xD; 条件执行减法运算(NE),R1-0xD=>R1,影响CPSR 寄存器,带有S
Arm存储器访问指令
ARM 处理器是加载/存储体系结构的典型的RISC处理器,对存储器的访问只能使用加载和存储指令实现。ARM 的加载/存储指令是可以实现字、半字、无符/有符字节操作。
ARM 处理器是冯诺依曼存储结构,程序空间、RAM 空间及IO 映射空间统一编址,除对RAM 操作以外,对外围IO、程序数据的访问均要通过加载/存储指令进行。
这里以最常用的LDR和STR为例:
LDR 和STR:
加载/存储字和无符号字节指令。使用单一数据传送指令(STR和LDR)来装载和存储单一字节或字的数据从寄存器到内存。LDR指令用于从内存中读取数据放入寄存器中;STR指令用于将寄存器中的数据保存到内存。
指令格式如下:
LDR {cond} Rd, <地址>;
加载指定地址上的数据(字),放入Rd 中。
STR {cond} Rd, <地址>;
存储数据(字)到指定地址的存储单元,要存储的数据在Rd中.
其中,cond为条件。
LDR/STR 指令寻址是非常灵活的,由两部分组成,一部分为一个基址寄存器,可以为任一个通用寄存器,另一部分为一个地址偏移量。地址偏移量有以下3 种格式:
(1) 立即数。立即数可以是一个无符号数值,这个数据可以加到基址寄存器,也可以从基址寄存器中减去这个数值。指令举例如下:
LDR R1, [R0,#0x12] ;
将R0+0x12 地址处的数据读出,保存到R1中(R0 的值不变)
LDR R1, [R0,#-0x12];
将R0-0x12 地址处的数据读出,保存到R1中(R0 的值不变)
LDR R1,[R0] ;将R0 地址处的数据读出,保存到R1 中(零偏移)
(2)寄存器。寄存器中的数值可以加到基址寄存器,也可以从基址寄存器中减去这个数值。指令举例值。指令举例如下:
LDR R1, [R0,R2] ;
将R0+R2 地址的数据计读出,保存到R1中(R0 的值不变)
LDR R1, [R0,-R2] ;
将R0-R2 地址处的数据计读出,保存到R1中(R0 的值不变)
(3)寄存器及移位常数。寄存器移位后的值可以加到基址寄存器,也可以从基址寄存器中减去这个数值。指令举例如下:
LDR R1, [R0,R2,LSL #2] ;
将R0+R2*4地址处的数据读出,保存到R1中(R0,R2的值不变)
3.mov指令、ldr指令
生成的汇编test1.s如下所示:
ARM生成对应指令 |
指令作用 |
Mov |
将立即数2存储到w2寄存器 |
Adrp |
相当于取了一个页的基址,以此来偏移寻址 |
Str |
将w2寄存器中的数据存到x1中 |
Ret |
main函数结束时的返回值 |
现在来看一下arm指令集中的mov指令:
ARM中的mov指令属于数据处理指令中的一种,数据处理指令大致可分为3 类:数据传送指令(如MOV),算术逻辑运算指令(如ADD,SUM,AND),比较指令(如CMP)。数据处理指令只能对寄存器的内容进行操作。
所有ARM 数据处理指令均可选择使用S 后缀,以影响状态标志。比较指令CMP、CMN、TST和TEQ不需要后缀S,它们会直接影响状态标志。
MOV
数据传送指令。将立即数或寄存器(operant2)传送到目标寄存器Rd,可用于移位运算等操作。指令格式如下:
MOV {cond} {S} Rd, operand2
MOV 指令举例如下:
MOV R1 #0x10 ; R1=0x10
MOV R0, R1 ; R0=R1
MOVS R3, R1, LSL #2 ; R3=R1<<2,并影响标志位
在这里先讲一下arm编译器中mov指令与ldr指令的区别:
MOV是从一个寄存器或者移位的寄存器或者立即数的值传递到另外一个寄存器。从本质上是寄存器到寄存器的传递,可以传递立即数.
在ARM官方的手册中,mov指令一共有五种用法。
第一种:
第一种用法就是上述用例中的用法,将立即数2存到w2寄存器中,这里的mov指令就等同于一条add(immediate)指令。
在该条mov指令中有如下介绍:
MOV<Wd|WSP>, <Wn|WSP>
is equivalent to
ADD<Wd|WSP>, <Wn|WSP>, #<imm>{, <shift>}
意思就是wn+#imm=wd
这里的imm一共有12位。
第二种:
这里的mov是取一个反向的16位立即数,等价于MOVN指令。
第三种:
这里的mov是取一个反向的16位立即数,等价于MOVN指令。
第四种:
这里的mov指令是取一个bitmask立即数,等价于ORR(immediate)指令。
第五种:
这里的mov指令是寄存器到寄存器之间的传递,等价于ORR(shifted register)指令。
LDR
LDR是普通的装载指令,ARM是RISC结构,数据从内存到CPU之间的移动只能通过L/S指令来完成,也就是ldr/str指令。LDR装载指令也可以装入立即数,这里说一下只能用ldr指令的情况——想把数据从内存中某处读取到寄存器中,只能使用ldr。如下面的一个例子test2,定义两个全局变量,并将其中一个变量赋值给另一个变量:
生成的汇编test2.s如下所示:
在这个例子中可以发现,若是数据从内存装入到寄存器,则会使用ldr指令。