【问题标题】:How does one do integer (signed or unsigned) division on ARM?如何在 ARM 上进行整数(有符号或无符号)除法?
【发布时间】:2012-01-10 23:49:21
【问题描述】:

我正在研究 Cortex-A8 和 Cortex-A9。我知道有些架构没有整数除法,但是除了转换为浮点数、除法、转换为整数之外,最好的方法是什么?或者这确实是最好的解决方案?

干杯! = )

【问题讨论】:

  • 当然编译器将支持软件模式下的整数除法,即使硬件中不存在。我怀疑那些高规格芯片没有整数除法。我认为 ATMega(如 Arduino)缺少它。
  • ARM 上整数除法的汇编指令不存在。
  • 要么转换为浮点数,要么使用展开的 3 操作码模式进行手动除法。
  • @Phonon: 不接受SDIVUDIV? Cortex-A8 是 ARMv7,但从infocenter.arm.com/help/topic/com.arm.doc.qrc0001m/… 看来只支持部分处理器。
  • ARMv7-R、ARMv7VE,否则在 ARMv7-A 中可选的是 SDIV 和 UDIV。您必须查看为该内核购买的选项和/或查看您正在使用的特定内核的 TRM。或者只是编码指令,执行它,看看你是否得到一个未定义的指令错误......

标签: assembly arm integer-division instruction-set cortex-a8


【解决方案1】:

通过执行 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

【讨论】:

  • 这里有舍入错误。对于无符号 32 位除以 N = 7,我们有 n = 2 和 A = 2454267026.28...如果我们向下舍入我们选择的 A 的值,那么它给出的结果对于“4294967292 / 7”来说太小了。如果我们将它四舍五入,那么它给出的结果对于“4294967291 / 7”来说太大了。只有当 A 的精确值的小数部分小于 0.5 时才会发生这种情况,因此它适用于 N 的大约一半值(如 3、5 或 1625)。
【解决方案2】:

其他地方的一些复制意大利面用于整数除法: 基本上,每位 3 条指令。来自this 网站,虽然我在很多其他地方也看到过。 This 网站也有一个不错的版本,总体上可能更快。


@ 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)

【讨论】:

  • 这是每位 3 条指令,但不是每位 3 个周期。每个步骤中的所有指令都立即依赖于前一个标志设置,这意味着取决于内核的结果延迟 3-4 个周期。这可能需要每步 9-12 个周期,总共约 360 个周期。
  • 听起来不错。如果你可以摆动它,逆乘固定点总是一个更好的选择。
【解决方案3】:

编译器通常在其库中包含一个除法器,例如 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 它应该全部自动工作,您不需要像我那样做。其他编译器也不需要像我在上面的冒险游戏中那样做。

【讨论】:

    【解决方案4】:

    我编写了自己的例程来执行未签名的除法,因为我在网络上找不到未签名的版本。我需要将 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

    【讨论】:

      【解决方案5】:

      我为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(高位字)和r0(低位字)中的64位被除数(有符号或无符号),以及r2中的32位除数。它们在r0 中返回商,在r1 中返回余数,因此您可以在C header 中将它们定义为extern,返回一个64 位整数,然后屏蔽商和余数。错误(除以 0 或溢出)由绝对值大于或等于除数绝对值的余数指示。有符号除法算法使用被除数和除数符号区分大小写;它不会首先转换为正整数,因为这不会正确检测所有溢出条件。

      【讨论】:

        猜你喜欢
        • 2021-01-26
        • 1970-01-01
        • 1970-01-01
        • 2022-11-29
        • 1970-01-01
        • 2013-06-02
        • 2010-09-19
        • 2013-10-02
        • 2013-02-04
        相关资源
        最近更新 更多