第22部分- Linux ARM汇编分支指令

第22部分- Linux ARM汇编分支指令

在32位ARM系统中,可以通过指令本身中的条件字段来支持的分支判断。 例如在T32中,有用于构建条件序列的IT(如果-则-then)指令。 A64不支持此功能,但有一组不同的特定条件说明。

 32位预测分支

源代码如下:

.text
.global main
main:
    mov r1, #123           /* r1 ← 123 */
    mov r2, #0             /* r2 ← 0 */
loop:
    cmp r1, #1             /* compare r1 and 1 */
    beq end                /* branch to end if r1 == 1 */
 
    and r3, r1, #1         /* r3 ← r1 & 1 */
    cmp r3, #0             /* compare r3 and 0 */
    bne odd                /* branch to odd if r3 != 0 */
even:
    mov r1, r1, ASR #1     /* r1 ← (r1 >> 1) */
    b end_loop
odd:
    add r1, r1, r1, LSL #1 /* r1 ← r1 + (r1 << 1) */
    add r1, r1, #1         /* r1 ← r1 + 1 */
 
end_loop:
    add r2, r2, #1         /* r2 ← r2 + 1 */
    b loop                 /* branch to loop */
 
end:
    mov r0, r2
    bx lr

as -g -o collatz.o collatz.s

gcc -o collatz collatz.o

使用减少分支的指令后,代码如下:

分支指令代码

.text
.global main
main:
    mov r1, #123           /* r1 ← 123 */
    mov r2, #0             /* r2 ← 0 */
loop:
    cmp r1, #1             /* compare r1 and 1 */
    beq end                /* branch to end if r1 == 1 */
 
    and r3, r1, #1         /* r3 ← r1 & 1 */
    cmp r3, #0             /* compare r3 and 0 */

    moveq r1, r1, ASR #1     /* r1 ← (r1 >> 1) */
    addne r1, r1, r1, LSL #1 /* r1 ← r1 + (r1 << 1) */
    addne r1, r1, #1         /* r1 ← r1 + 1 */
 
    add r2, r2, #1         /* r2 ← r2 + 1 */
    b loop                 /* branch to loop */
 
end:
    mov r0, r2
    bx lr

减少了分支目录。

as -g -o collatz-b.o collatz-b.s

gcc -o collatz-b collatz-b.o

在树莓派上安装linux-tools工具进行判断两者差异。

执行:
perf_4.9 stat --log-fd=3 --repeat=50 -e cpu-clock ./collatz 3>&1

Performance counter stats for './collatz' (50 runs):

 

          2.492647      cpu-clock (msec)          #    0.530 CPUs utilized            ( +-  0.53% )

 

       0.004699564 seconds time elapsed                                          ( +-  0.37% )
 
执行:
perf_4.9 stat --log-fd=3 --repeat=50 -e cpu-clock ./collatz-b 3>&1

Performance counter stats for './collatz-b' (50 runs):

          2.119385      cpu-clock (msec)          #    0.530 CPUs utilized            ( +-  3.64% )

       0.003997353 seconds time elapsed                                          ( +-  3.56% )

可以看到差异。

 64位预测分支

.arch armv8-a
.global _start
.text
_start:
    mov x1, #123           /* r1 ← 123 */
    mov x2, #0             /* r2 ← 0 */
loop:
    cmp x1, #1             /* compare r1 and 1 */
    beq end                /* branch to end if r1 == 1 */
 
    and x3, x1, #1         /* r3 ← r1 & 1 */
    cmp x3, #0             /* compare r3 and 0 */
    bne odd                /* branch to odd if r3 != 0 */
even:
    mov x1, x1, ASR #1     /* r1 ← (r1 >> 1) */
    b end_loop
odd:
    add x1, x1, x1, LSL #1 /* r1 ← r1 + (r1 << 1) */
    add x1, x1, #1         /* r1 ← r1 + 1 */
 
end_loop:
    add x2, x2, #1         /* r2 ← r2 + 1 */
    b loop                 /* branch to loop */
end:
    mov x0,x2
    mov x8, 93
	svc 0

as -g -o collatz64.o collatz64.s

ld -o collatz64 collatz64.o

分支优化代码

A64不支持此功能,但有一组不同的特定条件说明。

A64指令集不支持每条指令的条件执行。 指令的预定执行不能提供足够的好处来证明其大量使用操作码空间。

A64指令集仅允许有条件地执行程序流控制分支指令。 这与A32和T32相反,在A32和T32中,大多数指令都可以使用条件代码进行判断。 这些可以总结如下。

条件选择(move)

CSEL根据条件在两个寄存器之间进行选择。 无条件指令紧跟条件选择,可以代替较短的条件序列。

CSINC根据条件在两个寄存器之间进行选择。 返回第一个源寄存器或第二个源寄存器加一个。

CSINV根据条件在两个寄存器之间进行选择。 返回第一个源寄存器或反向的第二个源寄存器。

CSNEG根据条件在两个寄存器之间进行选择。 返回第一个源寄存器或取反的第二个源寄存器。

条件集

有条件地在0和1(CSET)或0和-1(CSETM)之间选择。 例如,用于在通用寄存器中将条件标志设置为布尔值或掩码。

条件比较

(CMP和CMN)如果原始条件为true,则将条件标志设置为比较结果。 如果不为真,则将条件标志设置为指定的条件标志状态。 条件比较指令对于表达嵌套或复合比较非常有用。

使用FCSEL和FCCMP指令的浮点寄存器也可以使用条件选择和条件比较。

例如:

CSINC X0, X1, X0, NE  // 如果清除零标志,则将返回寄存器X0设置为X1,否则将X0递增

CINC X0, X0, LS // 如果小于或等于(LS),则X0 = X0 + 1

CSET W0, EQ  // 如果先前的比较等于(Z = 1),则W0 = 1,否则W0=0

CSETM X0, NE // 如果不相等 X0 = -1, 否则X0 = 0

代码示例:

 if (i == 0)  r = r + 2;  else  r = r - 1;

汇编实现如下:

  CMP w0, #0            // if (i == 0)

  SUB w2, w1, #1        // r = r - 1

  ADD w1, w1, #2        // r = r + 2

  CSEL w1, w1, w2, EQ   // select between the two results

CMP命令可以更新cpsr寄存器。

优化代码

.arch armv8-a
.global _start
.text
_start:
    mov x1, #123           /* r1 ← 123 */
    mov x2, #0             /* r2 ← 0 */
loop:
    cmp x1, #1             /* compare r1 and 1 */
    beq end                /* branch to end if r1 == 1 */
    
    and x3, x1, #1         /* r3 ← r1 & 1 */
cmp x3, #0             /* compare r3 and 0 */
mov x5,x1 //保存x1到x5
    add x1, x1, x1, LSL #1 /* 奇数操作:r1 ← r1 + (r1 << 1) */
add x2, x1, #1         /*奇数操作:r1 ← r1 + 1 ,保存到x2*/
mov x4, x5, ASR #1     /* 偶数操作 保存到X4*/
CSINC X1, X4, X2, EQ //等于0,则X1=X2,否则等于X4

end_loop:
    add x2, x2, #1         /* r2 ← r2 + 1 */
    b loop                 /* branch to loop */
end:
    mov x0,x2
    mov x8, 93
	svc 0 

as -g -o collatz64o.o collatz64o.s

ld -o collatz64o collatz64o.o

运行得到结果如下:

./collatz64o

#]$echo $?

46

#perf stat --log-fd=3 --repeat=5000 -e cpu-clock ./collatz64 3>&1

Performance counter stats for './collatz64' (5000 runs):

 

          0.049073      cpu-clock (msec)          #    0.188 CPUs utilized            ( +-  0.14% )

 

       0.000260505 +- 0.000000395 seconds time elapsed  ( +-  0.15% )
#perf stat --log-fd=3 --repeat=5000 -e cpu-clock ./collatz64o 3>&1

Performance counter stats for './collatz64o' (5000 runs):

 

          0.048612      cpu-clock (msec)          #    0.191 CPUs utilized            ( +-  0.13% )

 

       0.000254531 +- 0.000000383 seconds time elapsed  ( +-  0.15% )

可以看到collatz64o有些优势。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值