编辑:我完全知道函数asmCopy可能不是功能性的,我的问题更多是关于gcc在寄存器中传递参数的行为。
我正在使用构建器为arm-none-eabi-gcc的STM32CubeIDE来开发STM32H7
优化级别为-Os
我看到以下无法解释的行为。我进行了屏幕截图,以获取并行的asm和C代码。
我的C代码正在调用3个函数。第一个和第三个具有完全相同的参数。
第二个不带参数。这是它的代码:
static void Reset_Cycle_Counter(void)
{
volatile unsigned long *DWT_CYCCNT = (unsigned long *)0xE0001004;
volatile unsigned long *DWT_CONTROL = (uint32_t *)0xE0001000;
// Reset cycle counter
*DWT_CONTROL = *DWT_CONTROL & ~0x00000001 ;
*DWT_CYCCNT = 0;
*DWT_CONTROL = *DWT_CONTROL | 1 ;
}
第三个功能很特别:我正在尝试编写一些汇编代码(现在很可能是错误的)。
static void __attribute__((noinline)) asmCopy(void *dst, void *src, uint32_t bytes)
{
while (bytes--)
{
asm("ldrb r12,[r1], #1"); // src param is stored in r1, r12 can be modified without being restored after
asm("strb r12,[r0], #1"); // dst paramis stored in r0
}
}
在第一次(对memcpy的)函数调用之前,向r0,r1和r2加载了正确的值。
然后在调用第三个函数之前,正如您在下面看到的,r1和r2中的参数是错误的(qspi_addr应该为0x90000000)。
我对AAPCS(ARM上的过程调用标准)的理解是,在调用子例程之前,应将函数的参数(如果有的话)装入寄存器r0至r3。并且子例程不需要保留或还原这些寄存器。然后,第二个函数修改r1和r2是正常的。因此,我希望编译器在第三个调用之前更新r0,r1和r2。
如果我将优化代码更改为-O0,则确实可以达到预期的效果。
您怎么看?
答案 0 :(得分:3)
您不能只打开一个内联汇编块并假定r0和r1仍然包含函数参数。对此没有任何保证。如果需要使用参数,则需要正确地将其作为输入和/或输出操作数传递
static void __attribute__((noinline))
myAsmCopy(void* dst, void* src, uint32_t bytes) {
asm volatile("1: cbz %[bytes], 1f \n"
"ldrb r12, [%[src]], #1 \n"
"strb r12, [%[dst]], #1 \n"
"subs %[bytes], #1 \n"
"b 1b \n"
"1: \n"
: [dst] "+&r"(dst), [src] "+&r"(src), [bytes] "+&r"(bytes)
:
: "cc", "memory", "r12");
}
GCC在此处提供了有关内联汇编的大量文档: https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
由于您显然从未使用过任何一种方法,因此我必须对此提出强烈建议。如果“ C包含脚枪”,那么内联汇编会将6发左轮手枪和5发子弹放在您的头上。
答案 1 :(得分:0)
如果您尝试询问编译器如何对其进行存档,那么一切都会变得越来越容易
void __attribute__((noinline)) asmCopy(void *dst, void *src, uint32_t bytes)
{
while (bytes--)
{
asm("ldrb r12,[r1], #1"); // src param is stored in r1, r12 can be modified without being restored after
asm("strb r12,[r0], #1"); // dst paramis stored in r0
}
}
void __attribute__((noinline)) asmCopy1(void *dst, void *src, uint32_t bytes)
{
while (bytes--)
{
*(uint8_t *)dst++ = *(uint8_t *)src++;
}
}
和代码
asmCopy:
.L2:
adds r2, r2, #-1
bcs .L3
bx lr
.L3:
ldrb r12,[r1], #1
strb r12,[r0], #1
b .L2
asmCopy1:
subs r0, r0, #1
add r2, r2, r1
.L5:
cmp r1, r2
bne .L6
bx lr
.L6:
ldrb r3, [r1], #1 @ zero_extendqisi2
strb r3, [r0, #1]!
b .L5
答案 2 :(得分:0)
我想我找到了答案。
在我正在测试的函数中(无论是我实现的the脚,还是@Vinci的更好),传递给函数的某些参数是全局变量(用于执行某些测试的虚拟数据数组)。
我的理解是,编译器“修改”函数的原型以构建仅使用一个参数的函数。其他参数被视为常量,只是PC在函数开始时就相对加载了。
因此,我修改了代码以调用完全相同的函数,但使用了本地易失性指针,问题消失了:我可以看到寄存器r0,r1和r2加载了预期的参数。
这有意义吗?