我正在研究 Cortex-A8 和 Cortex-A9。我知道有些架构没有整数除法,但是除了转换为浮点数、除法、转换为整数之外,最好的方法是什么?或者这确实是最好的解决方案?
干杯! = )
我正在研究 Cortex-A8 和 Cortex-A9。我知道有些架构没有整数除法,但是除了转换为浮点数、除法、转换为整数之外,最好的方法是什么?或者这确实是最好的解决方案?
干杯! = )
通过执行 64 位乘法和右移可以快速完成除以常数值,例如,如下所示:
LDR R3, =0xA151C331
UMULL R3, R2, R1, R3
MOV R0, R2,LSR#10
这里R1除以1625,计算如下:64bitreg(R2:R3) = R1*0xA151C331,那么结果就是高位32bit右移10:
R1*0xA151C331/2^(32+10) = R1*0.00061538461545751488 = R1/1624.99999980
您可以根据以下公式计算自己的常数:
x / N == (x*A)/2^(32+n) --> A = 2^(32+n)/N
选择最大的 n,其中 A < 2^32
一些从其他地方复制过来的整数除法:基本上,每位 3 条指令。从这个网站,虽然我在很多其他地方也看到过。 这个网站也有一个不错的版本,一般来说可能更快。
@ Entry r0: numerator (lo) must be signed positive
@ r2: deniminator (den) must be non-zero and signed negative
idiv:
lo .req r0; hi .req r1; den .req r2
mov hi, #0 @ hi = 0
adds lo, lo, lo
.rept 32 @ repeat 32 times
adcs hi, den, hi, lsl #1
subcc hi, hi, den
adcs lo, lo, lo
.endr
mov pc, lr @ return
@ Exit r0: quotient (lo)
@ r1: remainder (hi)
编译器通常在其库中包含一个除法,例如 gcclib 我从 gcc 中提取它们并直接使用它们:
https://github.com/dwelch67/stm32vld/然后 stm32f4d/adventure/gcclib
浮动和返回可能不是最好的解决方案。您可以尝试一下,看看它有多快……这是一个乘法,但也可以很容易地使它成为一个除法:
https://github.com/dwelch67/stm32vld/然后 stm32f4d/float01/vectors.s
我没有时间来看看有多快/多慢。了解我在上面使用的是 cortex-m,而您正在谈论的是 cortex-a,频谱的不同端,类似的浮点指令和 gcc lib 的东西是相似的,对于 cortex-m,我必须为拇指构建,但你可以就像手臂一样容易构建。实际上,使用 gcc 它应该全部自动工作,您不需要像我那样做。其他编译器也不需要像我在上面的冒险游戏中那样做。
我编写了自己的例程来执行未签名的除法,因为我在网络上找不到未签名的版本。我需要将 64 位值与 32 位值相除以获得 32 位结果。
内部循环不如上面提供的有符号解决方案高效,但这确实支持无符号算术。如果分子 (hi) 的高位部分小于分母 (den),则此例程执行 32 位除法,否则执行完整的 64 位除法 (hi:lo/den)。结果在 lo。
cmp hi, den // if hi < den do 32 bits, else 64 bits
bpl do64bits
REPT 32
adds lo, lo, lo // shift numerator through carry
adcs hi, hi, hi
subscc work, hi, den // if carry not set, compare
subcs hi, hi, den // if carry set, subtract
addcs lo, lo, #1 // if carry set, and 1 to quotient
ENDR
mov r0, lo // move result into R0
mov pc, lr // return
do64bits:
mov top, #0
REPT 64
adds lo, lo, lo // shift numerator through carry
adcs hi, hi, hi
adcs top, top, top
subscc work, top, den // if carry not set, compare
subcs top, top, den // if carry set, subtract
addcs lo, lo, #1 // if carry set, and 1 to quotient
ENDR
mov r0, lo // move result into R0
mov pc, lr // return
可以添加对边界条件和 2 的幂的额外检查。完整的细节可以在http://www.idwiz.co.za/Tips%20and%20Tricks/Divide.htm找到
ARM GNU
我为汇编程序编写了以下函数。如果您没有udiv/sdiv
支持机器的 CPU,只需在任一函数中剪掉前几行直到“0:”标签。
.arm
.cpu cortex-a7
.syntax unified
.type udiv,%function
.globl udiv
udiv: tst r1,r1
bne 0f
udiv r3,r0,r2
mls r1,r2,r3,r0
mov r0,r3
bx lr
0: cmp r1,r2
movhs r1,r2
bxhs lr
mvn r3,0
1: adds r0,r0
adcs r1,r1
cmpcc r1,r2
subcs r1,r2
orrcs r0,1
lsls r3,1
bne 1b
bx lr
.size udiv,.-udiv
.type sdiv,%function
.globl sdiv
sdiv: teq r1,r0,ASR 31
bne 0f
sdiv r3,r0,r2
mls r1,r2,r3,r0
mov r0,r3
bx lr
0: mov r3,2
adds r0,r0
and r3,r3,r1,LSR 30
adcs r1,r1
orr r3,r3,r2,LSR 31
movvs r1,r2
ldrvc pc,[pc,r3,LSL 2]
bx lr
.int 1f
.int 3f
.int 5f
.int 11f
1: cmp r1,r2
movge r1,r2
bxge lr
mvn r3,1
2: adds r0,r0
adcs r1,r1
cmpvc r1,r2
subge r1,r2
orrge r0,1
lsls r3,1
bne 2b
bx lr
3: cmn r1,r2
movge r1,r2
bxge lr
mvn r3,1
4: adds r0,r0
adcs r1,r1
cmnvc r1,r2
addge r1,r2
orrge r0,1
lsls r3,1
bne 4b
rsb r0,0
bx lr
5: cmn r1,r2
blt 6f
tsteq r0,r0
bne 7f
6: mov r1,r2
bx lr
7: mvn r3,1
8: adds r0,r0
adcs r1,r1
cmnvc r1,r2
blt 9f
tsteq r0,r3
bne 10f
9: add r1,r2
orr r0,1
10: lsls r3,1
bne 8b
rsb r0,0
bx lr
11: cmp r1,r2
blt 12f
tsteq r0,r0
bne 13f
12: mov r1,r2
bx lr
13: mvn r3,1
14: adds r0,r0
adcs r1,r1
cmpvc r1,r2
blt 15f
tsteq r0,r3
bne 16f
15: sub r1,r2
orr r0,1
16: lsls r3,1
bne 14b
bx lr
有两个函数,udiv
用于无符号整数除法和sdiv
用于有符号整数除法。r1
他们都期望(高位字)和(低位字)中的 64 位被除数(有符号或无符号)r0
,以及 .中的 32 位除数r2
。它们返回 in 的商r0
和 in 的余数r1
,因此您可以在 a 中将它们定义C header
为extern
返回一个 64 位整数,然后屏蔽掉商和余数。错误(除以 0 或溢出)由绝对值大于或等于除数绝对值的余数指示。有符号除法算法使用被除数和除数符号区分大小写;它不会首先转换为正整数,因为这不会正确检测所有溢出条件。