简单的ADD/ADC ARM汇编失败

4
我有以下C和ASM版本的代码(据说是相同的代码)。它的作用是将由两个64位整数表示的2个128位整数加载到寄存器中(先是4*低32位,然后是4*高32位),并进行ADD/ADC运算。这段代码非常简单,ARM/ST手册实际上给出了相同的96位示例(使用3个ADD/ADC)。
对于简单调用,两个版本都可以工作(重复添加(1 << x++)或1..x)。但是对于更长的测试套件,ARM汇编失败(板子挂起)。目前我没有能力陷阱/调试它,并且不能使用任何printf()之类的函数来找到测试失败的原因,而这些也是无关紧要的,因为必须存在某些基本故障在ASM版本中,而C版本按预期工作。
我不明白,它非常简单,非常接近C汇编输出(sans branching)。我尝试了“内存”约束条件(不应该需要),尝试在低64位和高64位之间保存进位并稍后加上,使用ADD(C).W,对齐,使用两个LDR/STR而不是LDRD/STRD等。我认为板子因为某些加法出错导致,可能会出现除以0之类的结果。以下是GCC ASM版本的代码,使用类似的基本技术,因此我不明白问题所在。
我真的只是想寻找最快的方法来进行加法运算,而不仅仅是修复那段代码。很遗憾,您必须使用常量寄存器名称,因为没有约束规定如何指定rXrX+1。此外,在编译过程中要使用的寄存器数量无法像GCC一样多,因为会用尽它们。
typedef struct I128 {
    int64_t high;
    uint64_t low;
} I128;

I128 I128add(I128 a, const I128 b) {
#if defined(USEASM) && defined(ARMx)
    __asm(
            "LDRD %%r2, %%r3, %[alo]\n"
            "LDRD %%r4, %%r5, %[blo]\n"
            "ADDS %%r2, %%r2, %%r4\n"
            "ADCS %%r3, %%r3, %%r5\n"
            "STRD %%r2, %%r3, %[alo]\n"

            "LDRD %%r2, %%r3, %[ahi]\n"
            "LDRD %%r4, %%r5, %[bhi]\n"
            "ADCS %%r2, %%r2, %%r4\n"
            "ADC %%r3, %%r3, %%r5\n"
            "STRD %%r2, %%r3, %[ahi]\n"
            : [alo] "+m" (a.low), [ahi] "+m" (a.high)
            : [blo] "m" (b.low), [bhi] "m" (b.high)
            : "r2", "r3", "r4", "r5", "cc"
            );
    return a;
#else
    // faster to use temp than saving low and adding to a directly
    I128 r = {a.high + b.high, a.low + b.low};
    // check for overflow of low 64 bits, add carry to high
    // avoid conditionals
    //r.high += r.low < a.low || r.low < b.low;
    // actually gcc produces faster code with conditionals
    if(r.low < a.low || r.low < b.low) ++r.high;
    return r;
}

使用 "armv7m-none-eabi-gcc-4.7.2 -O3 -ggdb -fomit-frame-pointer -falign-functions=16 -std=gnu99 -march=armv7e-m" 的GCC C版本:

b082        sub sp, #8
e92d 0ff0   stmdb   sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
a908        add r1, sp, #32
e881 000c   stmia.w r1, {r2, r3}
e9dd 890e   ldrd    r8, r9, [sp, #56]   ; 0x38
e9dd 670a   ldrd    r6, r7, [sp, #40]   ; 0x28
e9dd 2308   ldrd    r2, r3, [sp, #32]
e9dd 450c   ldrd    r4, r5, [sp, #48]   ; 0x30
eb16 0a08   adds.w  sl, r6, r8
eb47 0b09   adc.w   fp, r7, r9
1912        adds    r2, r2, r4
eb43 0305   adc.w   r3, r3, r5
45bb        cmp fp, r7
bf08        it  eq
45b2        cmpeq   sl, r6
d303        bcc.n   8012c9a <I128add+0x3a>
45cb        cmp fp, r9
bf08        it  eq
45c2        cmpeq   sl, r8
d204        bcs.n   8012ca4 <I128add+0x44>
2401        movs    r4, #1
2500        movs    r5, #0
1912        adds    r2, r2, r4
eb43 0305   adc.w   r3, r3, r5
e9c0 2300   strd    r2, r3, [r0]
e9c0 ab02   strd    sl, fp, [r0, #8]
e8bd 0ff0   ldmia.w sp!, {r4, r5, r6, r7, r8, r9, sl, fp}
b002        add sp, #8
4770        bx  lr

我失败的ASM版本:

b082        sub sp, #8                                                                                  
b430        push    {r4, r5}                                                                            
a902        add r1, sp, #8                                                                              
e881 000c   stmia.w r1, {r2, r3}                                                                        
e9dd 2304   ldrd    r2, r3, [sp, #16]                                                                   
e9dd 4508   ldrd    r4, r5, [sp, #32]                                                                   
1912        adds    r2, r2, r4                                                                          
416b        adcs    r3, r5                                                                              
e9cd 2304   strd    r2, r3, [sp, #16]                                                                   
e9dd 2302   ldrd    r2, r3, [sp, #8]                                                                    
e9dd 4506   ldrd    r4, r5, [sp, #24]                                                                   
4162        adcs    r2, r4                                                                              
eb43 0305   adc.w   r3, r3, r5                                                                          
e9cd 2302   strd    r2, r3, [sp, #8]                                                                    
4604        mov r4, r0                                                                                  
c90f        ldmia   r1, {r0, r1, r2, r3}                                                                
e884 000f   stmia.w r4, {r0, r1, r2, r3}                                                                
4620        mov r0, r4                                                                                  
bc30        pop {r4, r5}                                                                                
b002        add sp, #8                                                                                  
4770        bx  lr                                                                                      

至少使用 -O2 进行编译。 - auselen
在最后一个中使用adcs而不是adc可以节省另外半个指令字。 - old_timer
@auselen:它已经在 -O3 上了。 - sbl
@dwelch:谢谢,我没注意到!我猜不使用S(无标志更新)会更快,但一旦它工作了,我会尝试。 - sbl
原始的Thumb指令总是更新标志位(ARM版本带有S),而Thumb2扩展(32位而不是16位)允许选择(在这种情况下,为了避免标志位,需要使用32位指令而不是16位指令,代价更高)。 - old_timer
显示剩余2条评论
1个回答

1

我对你的代码还不是很熟悉,但它似乎也没有起作用,不确定原因。不过,很容易修补编译器生成的代码来处理进位:

I128 I128add(I128 a, const I128 b) {

    I128 r = {a.high + b.high, a.low + b.low};
    return r;
}

成为

000001e4 <I128add>:
 1e4:   b082        sub sp, #8
 1e6:   b4f0        push    {r4, r5, r6, r7}
 1e8:   e9dd 4506   ldrd    r4, r5, [sp, #24]
 1ec:   a904        add r1, sp, #16
 1ee:   e881 000c   stmia.w r1, {r2, r3}
 1f2:   e9dd 230a   ldrd    r2, r3, [sp, #40]   ; 0x28
 1f6:   1912        adds    r2, r2, r4
 1f8:   eb43 0305   adc.w   r3, r3, r5
 1fc:   e9dd 6704   ldrd    r6, r7, [sp, #16]
 200:   e9dd 4508   ldrd    r4, r5, [sp, #32]
 204:   1936        adds    r6, r6, r4
 206:   eb47 0705   adc.w   r7, r7, r5
 20a:   e9c0 6700   strd    r6, r7, [r0]
 20e:   e9c0 2302   strd    r2, r3, [r0, #8]
 212:   bcf0        pop {r4, r5, r6, r7}
 214:   b002        add sp, #8
 216:   4770        bx  lr

修复了广告

.thumb_func
.globl test2
test2:
    sub sp, #8
    push    {r4, r5, r6, r7}
    ldrd    r4, r5, [sp, #24]
    add r1, sp, #16
    stmia r1, {r2, r3}
    ldrd    r2, r3, [sp, #40]
    add r2, r4
    adc r3, r5
    ldrd    r6, r7, [sp, #16]
    ldrd    r4, r5, [sp, #32]
    adc r6, r4
    adc r7, r5
    strd    r6, r7, [r0]
    strd    r2, r3, [r0, #8]
    pop {r4, r5, r6, r7}
    add sp, #8
    bx  lr

最终结果

00000024 <test2>:
  24:   b082        sub sp, #8
  26:   b4f0        push    {r4, r5, r6, r7}
  28:   e9dd 4506   ldrd    r4, r5, [sp, #24]
  2c:   a904        add r1, sp, #16
  2e:   c10c        stmia   r1!, {r2, r3}
  30:   e9dd 230a   ldrd    r2, r3, [sp, #40]   ; 0x28
  34:   1912        adds    r2, r2, r4
  36:   416b        adcs    r3, r5
  38:   e9dd 6704   ldrd    r6, r7, [sp, #16]
  3c:   e9dd 4508   ldrd    r4, r5, [sp, #32]
  40:   4166        adcs    r6, r4
  42:   416f        adcs    r7, r5
  44:   e9c0 6700   strd    r6, r7, [r0]
  48:   e9c0 2302   strd    r2, r3, [r0, #8]
  4c:   bcf0        pop {r4, r5, r6, r7}
  4e:   b002        add sp, #8
  50:   4770        bx  lr

请注意,thumb2指令的数量较少,除非您使用支持thumb2的cortex-A,否则从flash(cortex-m)中获取数据会很慢。我看到您正在尝试保存两个寄存器的推送和弹出,但这会增加更多的数据获取。您可以采用上述方法,重新排列加载和存储,并保存这两个寄存器。

目前进行了最小化测试。通过printfs显示,上半部分的字词在增加,而您的代码中没有看到这一点。我仍在尝试解开调用约定(请为我们记录您的代码),看起来r0由调用者准备好以放置结果,其余内容在堆栈上。我正在使用stellaris launchpad(cortex-m4)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接