有些内联程序集代码有问题。我知道该怎么做,但我错过了“怎么做”!
我有一个校验和函数“几乎”工作:
static unsigned long cksum_unroll( unsigned short **w, int *mlen)
{
int len;
unsigned short *w0;
unsigned long sum=0;
len = *mlen;
w0 = *w;
while( len >= 8) {
asm volatile (
"ldmia %[w0]!, {v1, v2}\n\t"
"adds %[sum], %[sum], v1\n\t"
"adcs %[sum], %[sum], v2\n\t"
"adcs %[sum], %[sum], #0"
: [sum] "+r" (sum) : [w0] "r" (w0)
);
len -= 8;
}
*mlen = len;
*w = w0;
return (sum);
}
我想,我的问题出在以下几行:“[sum]“+r”(sum):[w0]“r”(w0)
在第一个装配线上,ldmia正确地处理w0(当执行该行时,r4、r5和w0中的数据将递增)。但是w0的增量值不会保存在某个地方,当代码循环时,w0的原始值将再次加载(请参阅下面的程序集代码)。
我想我应该把w0的值存储在“:[sum]“+r”(sum):[w0]“r”(w0)”行上,但我不知道如何……
下面是函数的内联汇编部分的反汇编代码:
注意:
len is stored at r11, #-16
w0 is stored at r11, #-20
sum is stored at r11, #-24
已编译,以下代码:
asm volatile (
"ldmia %[w0]!, {v1, v2}\n\t"
"adds %[sum], %[sum], v1\n\t"
"adcs %[sum], %[sum], v2\n\t"
"adcs %[sum], %[sum], #0"
: [sum] "+r" (sum) : [w0] "r" (w0)
);
len -= 8;
生成:
00031910: ldr r3, [r11, #-20]
00031914: ldr r2, [r11, #-24]
00031918: mov r4, r2
0003191c: ldm r3!, {r4, r5}
00031920: adds r4, r4, r4
00031924: adcs r4, r4, r5
00031928: adcs r4, r4, #0
0003192c: str r4, [r11, #-24]
00031930: ldr r3, [r11, #-16]
00031934: sub r3, r3, #8
00031938: str r3, [r11, #-16]
如您所见,我想在第31928行和第3192C行之间添加类似“str r3,[r11,-20]”的内容,因为当程序循环到第31910行时,r3将加载初始值r3……
我认为这对堆栈溢出社区的内联汇编专家来说是一个简单的选择!
顺便说一下,我正在研究ARM7TDMI处理器(但这可能与这个问题无关…)
提前谢谢!
编辑:
为了验证我的想法,我测试了以下内容:
asm volatile (
"ldmia %[w0]!, {v1, v2}\n\t"
"adds %[sum], %[sum], v1\n\t"
"adcs %[sum], %[sum], v2\n\t"
"adcs %[sum], %[sum], #0\n\t"
"str %[w0], [r11, #-20]"
: [sum] "+r" (sum) : [w0] "r" (w0)
);
这很管用。也许这就是解决方案,但是我该用什么来代替“r11,20”,如果我修改这个函数,它可能会改变?
最佳答案
问题似乎是您将w0
指定为输入操作数,而实际上它应该是读写输出操作数,如sum
。此外,您需要指定它在使用这些寄存器时会删除v1和v2(否则,gcc可能会将一些其他var放入这些regs中,并希望它们被保留)。
所以你应该:
asm volatile (
"ldmia %[w0]!, {v1, v2}\n\t"
"adds %[sum], %[sum], v1\n\t"
"adcs %[sum], %[sum], v2\n\t"
"adcs %[sum], %[sum], #0"
: [sum] "+r" (sum) , [w0] "+r" (w0) : : "v1", "v2"
);
也就是说,两个读写输入/输出操作数,没有独占的输入操作数,以及两个寄存器缓冲区