汇编(四)if else 与 switch

549 阅读4分钟

if else 判断

void func(int a,int b){
    if (a > b) {
        g = a;
    }else{
        g = b;
    }
}

汇编代码如下:

_func:
// 拉伸栈空间
00000001000067a4         sub        sp, sp, #0x10  ; CODE XREF=_main+28
// w0、w1 入栈
00000001000067a8         str        w0, [sp, #0xc]
00000001000067ac         str        w1, [sp, #0x8]
// w8 = sc,w9 = s8
00000001000067b0         ldr        w8, [sp, #0xc]
00000001000067b4         ldr        w9, [sp, #0x8]
// w8与w9相减,(会修改状态寄存器)
00000001000067b8         subs       w8, w8, w9
// 如果是小于等于则跳转loc_1000067d0
00000001000067bc         b.le       loc_1000067d0

00000001000067c0         ldr        w8, [sp, #0xc]
00000001000067c4         adrp       x9, #0x100008000
// g = sc
00000001000067c8         str        w8,[x9,#0xd88]   ; _g
// 跳转loc_1000067dc
00000001000067cc         b          loc_1000067dc

// w8 < w9
                     loc_1000067d0:
00000001000067d0         ldr        w8, [sp, #0x8]; CODE XREF=_func+24
00000001000067d4         adrp       x9, #0x100008000
// g = s8
00000001000067d8         str        w8, [x9, #0xd88]  ; _g

// 栈平衡
                     loc_1000067dc:
00000001000067dc         add        sp, sp, #0x10 ; CODE XREF=_func+40
00000001000067e0         ret

cmp(Compare)比较指令

CMP 把一个寄存器的内容和另一个寄存器的内容或立即数进行比较,但不存储结果,只是正确的更改标志(CMP 后面跟的是 B.LE ,即else的条件)
一般 CMP 做完判断后会进行跳转,后面通常会跟上 B 指令。

BL 标号:跳转到标号处执行
B.LT 标号:比较结果是小于(less than ),执行标号,否则不跳转
B.LE 标号:比较结果是小于等于(less than or equal to),执行标号,否则不跳转 B.GT 标号:比较结果是大于(greater than),执行标号,否则不跳转
B.GE 标号:比较结果是大于等于(greater than or equal to),执行标号,否则不跳转
B.EQ 标号:比较结果是等于,执行标号,否则不跳转
B.NE 标号:比较结果是不等于(not equal),执行标号,否则不跳转
B.HI 标号:比较结果是无符号大于,执行标号,否则不跳转
B.HS 标号:比较结果是无符号大于等于,执行标号,否则不跳转

switch

判断条件为3个,则采用同 if、else 相同的寄存器比较操作。

void funcA(int a){
    switch (a) {
        case 1:
            printf("打坐");
            break;
        case 2:
            printf("加红");
            break;
        case 3:
            printf("加蓝");
            break;
        default:
            printf("啥都不干");
            break;
    }
}

汇编代码如下: image.png

判断条件大于3个,则会去查内部的表:

void funcA(int a){
    switch (a) {
        case 1:
            printf("打坐");
            break;
        case 2:
            printf("加红");
            break;
        case 3:
            printf("加蓝");
            break;
        case 4:
            printf("摆摊");
            break;
        default:
            printf("啥都不干");
            break;
    }
}

汇编代码如下: image.png

x8(其实是一张表)地址加上4 取出来放进 x10,也就是00 00 00 20 image.png

修改 case 值,发现表的长度变了:

void funcA(int a){
    switch (a) {
        case 5:
            printf("打坐");
            break;
        case 2:
            printf("加红");
            break;
        case 7:
            printf("加蓝");
            break;
        case 8:
            printf("打坐");
            break;
        default:
            printf("啥都不干");
            break;
    }
}

image.png

如果 case 跨度偏大,编译器还是会采取寄存器比较的方案。

void funcA(int a){
    switch (a) {
        case 10:
            printf("打坐");
            break;
        case 221:
            printf("加红");
            break;
        case 399:
            printf("加蓝");
            break;
        case 4888:
            printf("打坐");
            break;
        default:
            printf("啥都不干");
            break;
    }
}

image.png

总结

1、假设 switch 语句的分支比较少时(例如3,少于4的时候没有意义),没有必要使用次结构,相当于 if-else。 2、各个分支常量的差值较大时,编译器会在效率还是内存进行取舍,这时编译器还是会编译成类似于if-else的结构。 3、在分支比较多的时候,在编译的时候会生成一个表(跳转表每个地址四个字节)。