-
-
[原创]通过示例学习Arm汇编03_移位除法
-
发表于: 2021-11-29 17:36 4697
-
今天有小伙伴遇到了关于有符号数除的2的N次幂使用移位运算加速的理解问题,这里做一下对应记录.
遇到的问题

除法的实现
早期计算机并没有设计专门的除法电路, 也就没有专门的指令. 数学家们总是可以找到高效的除法计算方法
于是就有了借助移位运算进行高效除法计算的理论基础以及对应的实现.

无符号除2的幂示例代码
1 2 3 4 5 6 | unsigned int test5(unsigned int a1){ // 无符号int除以2的倍数 // LSRS R1, R0, #12 return a1 / 4096;} |
1 2 3 4 5 6 7 8 9 10 11 | hello32:0D8AD4BE test5hello32:0D8AD4BEhello32:0D8AD4BE var_4= -4hello32:0D8AD4BEhello32:0D8AD4BE SUB SP, SP, #4hello32:0D8AD4C0 STR R0, [SP,#4+var_4] ; var_4 = r0 = 0x55hello32:0D8AD4C2 LDR R0, [SP,#4+var_4] ; r0 = var_4 = 0x55hello32:0D8AD4C4 LSRS R0, R0, #0xC ; lsrs is thumb provides the unsigned value of a register divided by a variable power of two. r0 = 0x55 >> 0xc = 0hello32:0D8AD4C6 ADD SP, SP, #4hello32:0D8AD4C8 BX LRhello32:0D8AD4C8 ; End of function test5 |
重要研究LSR指令

1 | 实例中的lsrs采用的是上面这种情况. r0 = r0(0x55) >> 0xc = 0 |

ARM64位对应指令UDIV
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | ; arm64 code.text:00000000000007A4 ; unsigned int __cdecl test5(unsigned int a1).text:00000000000007A4 EXPORT test5.text:00000000000007A4 test5 ; CODE XREF: main+54↓p.text:00000000000007A4.text:00000000000007A4 var_4 = -4.text:00000000000007A4 a1 = 0xC.text:00000000000007A4.text:00000000000007A4 ; __unwind {.text:00000000000007A4 SUB SP, SP, #0x10.text:00000000000007A8 MOV W8, #0x1000 ; w8 is tmp var.text:00000000000007AC STR W0, [SP,#0x10+var_4] ; var_4 = 0x55.text:00000000000007B0 LDR W0, [SP,#0x10+var_4] ; w8 = var_4 = 0x55.text:00000000000007B4 UDIV W0, W0, W8 ; Unsigned Divide; https://developer.arm.com/documentation/dui0801/g/A32-and-T32-Instructions/UDIV.text:00000000000007B8 ADD SP, SP, #0x10.text:00000000000007BC RET.text:00000000000007BC ; } // starts at 7A4.text:00000000000007BC ; End of function test5 |
有符号除2的N次幂示例代码2
1 2 3 4 5 6 7 8 | int test6(signed int a1){ // 有符号int除以2的倍数 // ASRS R1, R0, #31 //R0为要操作的数,如果R0为正,R1恒为0;如果R0为负,R1恒为-1; // ADD.W R0, R0, R1,LSR#20 //R0为要操作的数,R0+=R1逻辑右移一定位数,具体位数等于32-幂 // ASRS R0, R0, #12 //R0为要操作的数,R0算数右移一定位数,具体位数等于幂 return a1 / 4096;} |
1 2 3 4 5 6 7 8 9 10 11 12 13 | hello32:01DCD4BE test6hello32:01DCD4BEhello32:01DCD4BE var_4= -4hello32:01DCD4BEhello32:01DCD4BE SUB SP, SP, #4hello32:01DCD4C0 STR R0, [SP,#4+var_4] ; var_4 = r0 = 0x55hello32:01DCD4C2 LDR R0, [SP,#4+var_4] ; r0 = var_4 = 0x55hello32:01DCD4C4 ASRS R1, R0, #0x1F ; r1 = r0(0x55) >> 0x1f = 0 ; 提取符号位hello32:01DCD4C6 ADD.W R0, R0, R1,LSR#20 ; r0 = r0 + r1 >> 20; r0 = 0x55hello32:01DCD4CA ASRS R0, R0, #0xC ; r0 = r0 >> 0xc = 0hello32:01DCD4CC ADD SP, SP, #4hello32:01DCD4CE BX LRhello32:01DCD4CE ; End of function test6 |

1 | 示例程序使用上面的指令, 采用算术右移进行除法运算 |

1 2 | Q: 为什么会有R1 = R0 >> 0x1f(31)呢?A: usigned 32 is 32bit, the 32bit is flag. |
背后的原理
通过脑图我们已经知道有符号除2^N运算的公式是, 至于为什么数学家已经论证了见help link
1 2 3 4 5 6 7 8 9 10 11 | (x + (1<<N) - 1) >> N示例代码中N = 12 (0xc)ASRS R1, R0, #0x1F;ADD.W R0, R0, R1, LSR#20; 1 << N - 1 等价于 -1 >> (32 - N) ; 1 << 12 -1 等价于 -1 >> 20 |
下面是两种指令得到的结果可以看到r0和r1是相等, 至于为什么使用 -1 >> (32-N)呢? 我们看一下指令的条数就知道了, 编译器并不想浪费任何一条指令.


help link
参考书籍 <<深入理解计算机系统>>
寻找有趣人士
为了寻找有趣人士, 强哥建立了一个wx群, 欢迎交流.

[培训]传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2021-11-29 17:42
被github_yhnu编辑
,原因:
赞赏
他的文章
- [求助]如何获取设备ID 7986
- [原创]通过示例学习Arm汇编03_移位除法 4698
- [原创]通过示例学习Arm汇编02 4461
- [原创]通过示例学习Arm汇编01 6612
- [原创] 通过问题来理解linker01-为什么PT_LOAD有2段Maps文件有3段 21461
赞赏
雪币:
留言: