我有(假设)相同代码的以下C和ASM版本。它做的是加载2个128位整数,每个由2个64位整数表示寄存器(前4 *低32位,然后4 *高32位)和ADD
/ ADC
给它们。它足够简单,而ARM / ST手册实际上给出了96bit(3 ADD
/ ADC
s)的相同示例。
对于简单调用,两个版本都有效(重复添加(1 << x++)
或1..x)。但是对于较长的测试套件,ARM组件会出现故障(电路板挂起)。 ATM我无法捕获/调试它,也无法使用任何printf()
或类似物来查找测试失败,这无论如何都是无关紧要的,因为在ASM版本中必定存在一些基本错误,因为C版本可以工作如预期的那样。
我不明白,它很简单并且非常接近C程序集输出(没有分支)。我尝试了“内存”约束(不应该需要),我尝试在寄存器中保存低位和高位64位之间的进位,然后使用ADD(C).W
添加,使用两个LDR
/ STR
而不是LDRD
/ STRD
等等。我假设电路板出现故障,因为某些添加出错并导致除以0或类似的情况。
GCC ASM在下面并使用类似的基本技术,所以我没有看到问题。
我真的只是寻找最快的添加方式,而不是专门修复代码。遗憾的是,您必须使用常量寄存器名称,因为没有约束来指定rX
和rX+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;
}
GCC C版使用“armv7m-none-eabi-gcc-4.7.2 -O3 -ggdb -fomit-frame-pointer -falign-functions = 16 -std = gnu99 -march = armv7e-m”:
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
答案 0 :(得分: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
注意拇指2指令的数量较少,除非你在拥有thumb2支持的皮质-A上,那些闪光(cortex-m)的提取(可能)很慢。我看到你正在尝试保存另外两个寄存器的推送和弹出,但是你花费了更多的时间。您可以采用上述方法并仍然重新安排加载和存储并保存这两个寄存器。
到目前为止,最低限度的测试。 printfs显示上面的单词添加,我没有看到你的代码。我仍然试图解开调用约定(请为我们更多地记录您的代码),看起来r0由调用者准备放置结果,rest在堆栈上。我正在使用stellaris启动板(cortex-m4)。