鲲鹏社区首页
中文
注册
我要评分
文档获取效率
文档正确性
内容完整性
文档易理解
在线提单
论坛求助

基本分支判断

分支结构是程序中基础、实用且频繁出现的模块,即通过判断条件结果选择不同的程序向前路径以实现不同的功能逻辑。

  • B.cond系列

    在Arm64汇编指令中,有一个CPSR寄存器称之为程序状态寄存器,有N\Z\C\V四个标志位存在其中,ARM汇编主要通过这些标志位的标记和判断实现分支结构的功能,其基本用法结构:

    CMP/CCMP/ADDS/SUBS/ANDS/TST… … //改写状态寄存器的指令

    B.cond label //条件状态判断,满足则跳转至label所在位置

    B为跳转指令,Cond表示跳转指令的条件代码后缀,不同标志表示了跳转需要满足的不同条件,具体如表1所示。B后面不跟随Cond标志,则表示无条件跳转至label地址。

    表1 状态标志位表

    编码

    助记符

    描述

    标记

    0000

    EQ

    运算结果相等为1

    Z==1

    0001

    NE

    运算结果不等为0

    Z==0

    0010

    HS/CS

    无符号高或者相同进位,发生进位为1

    C==1

    0011

    LO/CC

    无符号低清零,发生借位为0

    C==0

    0100

    MI

    负数为1

    N==1

    0101

    PL

    非负数为0

    N==0

    0110

    VS

    有符号溢出为1

    V==1

    0111

    VC

    没有溢出为0

    V==0

    1000

    HI

    无符号>

    C==1 && Z==0

    1001

    LS

    无符号<=

    !( C==1 && Z==0)

    1010

    GE

    带符号>=

    N==V

    1011

    LT

    带符号<

    N!=V

    1100

    GT

    带符号>

    Z==0 && N==V

    1101

    LE

    带符号<=

    !( Z==0 && N==V)

    1110

    AL

    无条件执行

    Any

    1111

    NV

    下面表格更直观显示了常见C实现的分支结构如何用Arm64汇编进行替代:

    条件判断类型

    C程序实现

    汇编实现

    单一条件判断

    /* Assumptions: Cond A: a > b */

    If (Cond A){

    Part 1

    } else {

    Part 2

    }

    End

    /* Assume the values of a and b are stored in registers x1 and x2. Note that value of b can be a variable loaded from its address or a constant. */

    ldr x1, [a_addr]

    ldr x2, [b_addr]

    cmp x1, x2

    b.le Part2

    (Part1:)

    ... ...

    b End

    Part2:

    ... ...

    End:

    … …

    双重条件判断

    /* Assumptions: Cond A: a > b \

    Cond B: a > c */

    If (Cond A && Cond B){

    Part 1

    } else {

    Part 2

    }

    End

    /* Assume the values of a, b and c are stored in registers x1, x2 and x3. */

    ldr x1, [a_addr]

    ldr x2, [b_addr]

    ldr x3, [c_addr]

    cmp x1, x2

    ccmp x1, x3, 0, hi

    b.hi Part1

    Part2:

    ... ...

    b End

    Part1:

    ... ...

    End

  • 特殊判断指令

    通过B.cond可以满足所有的判断逻辑,但有时候对于一些简单的判断和特殊情况的判断,Arm64提供了部分指令来支持,如下表所示。

    指令

    使用范例

    功能描述

    CBZ

    CBZ X1, label

    如果x1 == 0,则跳转到label

    CBNZ

    CBNZ X1, label

    如果x1 != 0,则跳转到label

    TBZ

    TBZ X1, #3, label

    如果x1寄存器的第三位等于0,则跳转到label

    TBNZ

    TBNZ X1, #3, label

    如果x1寄存器的第三位不等于0,则跳转到label

    这里我们特别来想象一下TBZ/TBNZ的使用场景。指令功能仅描述了其可以用于判断某个值的某位是否等于0,结合计算机用保存数据的最小单位是bit,可以进一步判断该值与2n值之间的关系。

    C程序实现

    汇编实现

    /* No assumption */

    if (x1 >= 64) {

    part1

    } else if (x1 >= 32) {

    part2

    } else if (x1 >= 16) {

    part3

    } else {

    part 4

    }

    tbnz x1, #4, Part3 /* over16 */

    ... ...

    b end

    Part3:

    tbnz x1, #5, Part2 /* over32 */

    ... ...

    b end

    Part2:

    tbnz x1, #6, Part1 /* over64 */

    ... ...

    b end

    Part1:

    ... ...

    end:

    ... ...

    /* Assumption: x1 < 128 */

    if (x1 >= 64) {

    part1

    } else if (x1 >= 32) {

    part2

    } else if (x1 >= 16) {

    part3

    } else {

    part 4

    }

    /* Ensure the x1 is less than 128, which means the bit value higher that 6th bit equals 0. */

    cmp x1, 128

    b.ge end

    tbz x1, #6, Part2 /* 64less */

    ... ...

    b end

    Part2:

    tbz x1, #5, Part3 /* 32less */

    ... ...

    b end

    Part3:

    tbz x1, #4, Part4 /* 16less */

    ... ...

    b end

    Part4:

    ... ...

    end:

    ... ...

    如上述代码所示,TBNZ可用于连续判断进入条件值大于等于2n的分支,而TBZ可用于连续判断进入条件值小于2n的分支。需要注意的是,TBZ判断某一位为0时,并不意味着该值就一定小于2n,有可能高位存在1而使整个值更大,所以在使用TBZ判断时,首先应该限制条件值的范围小于2n+1

  • 判断分支结果选择指令CSEL

    分支结构的增加是因为不同情况下需要处理不同的过程,一般都会引入判断指令和跳转指令进而可能影响流水线顺畅程度。但当这个过程相对比较简单而且最后通过赋值到某些寄存器作为结束,那么可以考虑使用CSEL指令,避免了分支跳转。其最简单的应该就类似于三目运算符,如下表所示。

    C程序实现

    汇编实现

    int a = 3;

    int b = 4;

    int c = (a > b) ? a : b;

    /* Assume the values of a and b stored in addr_a and addr_b, and the values of c will be stored in addr_c*/

    ... ...

    ldr vala, [addr_a]

    ldr valb, [addr_b]

    cmp vala, valb

    csel valc, vala, valb, ge

    str valc, [addr_c]

    ... ...