ARM汇编:常见指令与函数

本文探讨了ARM汇编语言中的常见指令和函数。重点介绍了ARM指令的条件码,强调了在传递值时16字节对齐及栈的使用。函数调用时,每个函数都有独立的栈帧,详细阐述了参数如何入栈。同时,讨论了switch语句在不同情况下的编译优化策略,包括编译成if-else结构或生成匹配表的方式。

常用汇编指令

MOV    X1,X0         //将寄存器X0的值传入寄存器X1
ADD    X0,X0, X1     //寄存器X0和X1的值相加后传入X0
SUB    X0,X0,X1     //相减

AND    X0,X0,#0xF    //X0的值与0xF相位与后的值传到X0
ORR    X0,X0,#0xF    //或
EOR    X0,X0,#0xF    //异或

LDR    X1,[sp,#0x8]   // 内存->寄存器
LDP  x29, x30, [sp, #0x10]    

STR X0, [sp, #0x8]     //寄存器->内存
STP  x29, x30, [sp, #0x10] 

CBZ  //Compare and Branch Zero,比较为零就跳转
CBNZ //比较非零就跳转
CMP  //比较指令,相当于SUBS,影响CPSR 

B/BR  //B代表跳转到标号的位置,L代表LR寄存器,要返回的地址存放在LR寄存器。
BLR   // 跳转到指令后边跟随寄存器中保存的地址
RET  //子程序返回指令,返回地址默认保存在LR寄存器

ADRP X10,1 //Address Page,把PC指针的后12位清零,把1左移12位然后两者相加

MOV指令只能用于寄存器之间传值,寄存器和内存间传值通过LDR和STR,数据往高地址读写。ARM64以16字节对齐,入栈、出栈要使用STP/LDP。

ARM指令条件码

ARM指令集
ARM指令都是带条件的,默认是AL无条件执行,31-28位是条件码。具体的条件码如下:

操作码条件码助记符标志含义
0000EQZ=1相等
0001NEZ=0不相等
0010CS/HS(Carry Set/High or Same)C=1无符号数大于或等于
0011CC/LO(Carry Clear/Lower)C=0无符号数小于
0100MI(Minus)N=1负数
0101PL(Plus)N=0正数或零
0110VS(Overflow Set)V=1溢出
0111VC(Overflow Clear)V=0没有溢出
1000HI(High)C=1,Z=0无符号数大于
1001LS(Lower or Same)C=0,Z=1无符号数小于或等于
1010GE(Greater or Equal)N=V有符号数大于或等于
1011LT(Less Than)N!=V有符号数小于
1100GT(Greater Than)Z=0,N=V有符号数大于
1101LE(Less or Equal)Z=1,N!=V有符号数小于或等于
1110AL-无条件执行(默认)
1111NV-从不执行

函数

每次函数调用,都为函数开辟一块空间,称为栈帧。栈是从高地址向低地址延伸的,每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息。

  • 函数的参数一般从x0~x7寄存器,当参数超过8个时,参数会入栈
  • 返回值存放到x0
  • 函数的局部变量存放在栈里面。

常见的函数调用开辟和恢复的栈空间:

sub    sp, sp, #0x40             ; 拉伸0x40(64字节)空间
stp    x29, x30, [sp, #0x30]     ;x29、x30 寄存器入栈保护
add    x29, sp, #0x30            ; x29指向栈帧的底部
... 
ldp    x29, x30, [sp, #0x30]     ;恢复x29、x30 寄存器的值
add    sp, sp, #0x40             ; 栈平衡
ret

switch

  1. switch语句的分支比较少时(小于4个)相当于if-else。
  2. 各个分支常量的差值较大时,编译器会在效率和内存间进行取舍,编译器还是会编译成类似于if-else的结构。
  3. 在分支比较多,分支差值也不大时,在编译时会生成一个表,通过表来进行匹配。
    void testSwitch(int num) {
    	switch (num) {
        case 0:
            break;
        case 1:
            break;
        case 2:
            break;
        case 40:
            break;
        case 45:
            break;
        default:
            break;
    	}
    }
    
    testSwiftch(5);
    
    汇编代码
    RuntimeLeaning`testSwitch:
    ->  0x1005be404 <+0>:  sub    sp, sp, #0x10             ; =0x10    16位对齐,一个int参数,所以拉伸16字节空间
    0x1005be408 <+4>:  str    w0, [sp, #0xc]        ; 把w0中的值存在testSwitch的栈中
    0x1005be40c <+8>:  ldr    w8, [sp, #0xc]     
    0x1005be410 <+12>: subs   w8, w8, #0x0              ; num差值 =  num-最小case值,影响cpsr
    0x1005be414 <+16>: mov    x9, x8   
    0x1005be418 <+20>: ubfx   x9, x9, #0, #32    ;   保留X9的0~32位,其他位补0,前面只用了32位,这里为了消除影响,所以把高32位清零。
    0x1005be41c <+24>: cmp    x9, #0x2d                 ; 0x2d = 最大case值 - 最小case值,让num差值与它比较就可以知道num是否在最小~最大case间
    0x1005be420 <+28>: str    x9, [sp]
    0x1005be424 <+32>: b.hi   0x1005be454               ; 无符号大于,如果大于说明不在case取值范围,跳转default
    0x1005be428 <+36>: adrp   x8, 0      					
    0x1005be42c <+40>: add    x8, x8, #0x45c            ; =0x45c    找到基址
    0x1005be430 <+44>: ldr    x11, [sp]						; 从sp中取出num差值
    0x1005be434 <+48>: ldrsw  x10, [x8, x11, lsl #2]  ; x11中是num差值,因为是int占4字节,所以要左移2相当于乘4
    0x1005be438 <+52>: add    x9, x8, x10    ;拿到应该跳转的case地址
    0x1005be43c <+56>: br     x9              ;跳转x9中地址
    0x1005be440 <+60>: b      0x1005be454               ; <+80> at main.m:54:1
    0x1005be444 <+64>: b      0x1005be454               ; <+80> at main.m:54:1
    0x1005be448 <+68>: b      0x1005be454               ; <+80> at main.m:54:1
    0x1005be44c <+72>: b      0x1005be454               ; <+80> at main.m:54:1
    0x1005be450 <+76>: b      0x1005be454               ; <+80> at main.m:54:1
    0x1005be454 <+80>: add    sp, sp, #0x10             ; =0x10 
    0x1005be458 <+84>: ret   
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值