gpt4 book ai didi

c - gcc 在内联 asm 中错误地重用寄存器

转载 作者:太空宇宙 更新时间:2023-11-04 07:10:20 24 4
gpt4 key购买 nike

我在 C 程序中为 Cortex-M4 实现了一个简单的延迟循环宏:

#define DELAY_CYCLES (F_CPU / 3000000) //F_CPU is 72000000
#define delayUS(n) __asm__ volatile( \
"1: subs %0, #1 \n" \
"bne 1b \n" \
: /* no outputs */ \
: "r" (n * DELAY_CYCLES) /* input */ \
: "0" /* clobbers */ \
)

这会延迟 n 微秒(假设中断被禁用)。大多数情况下,它工作正常。但是,我发现它在使用它两次的函数中无法正常工作:

static void test(uint8_t num) {
digitalWrite(12, 1);
delayUS(10);
digitalWrite(13, 1);
delayUS(10);
digitalWrite(12, 0);
digitalWrite(13, 0);
}

(这是一个实际使用 num 的函数,但是在调试这个问题时被剥离到这个。它也被内联到 main 中,因此标签在拆卸。)

这里发生的是对 delayUS() 的第二次调用从未完成。检查生成的程序集显示了问题:

 528:   2701        movs    r7, #1
52a: 6037 str r7, [r6, #0] ;digitalWrite(12, 1)
52c: 23f0 movs r3, #240 ;delayUS(10); 10 * DELAY_CYCLES = 240
52e: 3b01 subs r3, #1
530: d1fd bne.n 52e <main+0x4a>

532: 4c0d ldr r4, [pc, #52]
534: 6027 str r7, [r4, #0] ;digitalWrite(13, 1)

536: 3b01 subs r3, #1 ;delayUS(10), but r3 is still 0
538: d1fd bne.n 536 <main+0x52>

53a: 2300 movs r3, #0
53c: 6033 str r3, [r6, #0] ;digitalWrite(12, 0)

出于某种原因,gcc 在第二个延迟循环中使用它之前不会重新初始化 r3,因此它不会延迟 240 次迭代(10µs),而是延迟 2^32(大约3 分钟)。

有了这个变体,问题就消失了:

__attribute__((used)) static int dummy;
#define delayUS(n) __asm__ volatile( \
"1: subs %0, #1 \n" \
"bne 1b \n" \
: "=r" (dummy) /* no outputs */ \
: "0" (n * DELAY_CYCLES) /* input */ \
: "0" /* clobbers */ \
)

生成更正确的代码:

 528:   2701        movs    r7, #1
52a: 23f0 movs r3, #240 ;r3 = 10 * DELAY_CYCLES
52c: 6037 str r7, [r6, #0] ;digitalWrite(12, 1)
52e: 461a mov r2, r3 ;r2 = r3

530: 3a01 subs r2, #1 ;delayUS(r2)
532: d1fd bne.n 530 <main+0x4c>

534: 4c0d ldr r4, [pc, #52]
536: 6027 str r7, [r4, #0] ;digitalWrite(13, 1)

538: 3b01 subs r3, #1 ;delayUS(r3)
53a: d1fd bne.n 538 <main+0x54>

53c: 4a0c ldr r2, [pc, #48]
53e: 6013 str r3, [r2, #0] ;digitalWrite(12, 0)

在这里,它正确地意识到延迟循环破坏了它的输入寄存器,因此在没有初始化它的情况下不会重新使用 r3(它使用 r2 作为其中之一而不是循环。)

那么,当 gcc 列在破坏列表中时,为什么 gcc 不承认以前的版本也破坏了它的输入?

最佳答案

问题在于“clobbers”列表是寄存器名称列表,或者特殊字符串“cc”和“memory”。因为没有名为“0”的寄存器,所以在 clobbers 列表中有这个是没有意义的。不幸的是 gcc 没有给你一个警告。相反,正如 gcc 文档所指出的那样:

Warning: Do not modify the contents of input-only operands (except for inputs tied to outputs). The compiler assumes that on exit from the asm statement these operands contain the same values as they had before executing the statement. It is not possible to use clobbers to inform the compiler that the values in these inputs are changing. One common work-around is to tie the changing input variable to an output variable that never gets used.

此解决方法是您的第二个示例所做的,也是它起作用的原因。为了正确起见,您可能还应该将“cc”添加到 clobbers 列表(当您修改标志时),并且您最好删除“0”,因为它没有意义。

关于c - gcc 在内联 asm 中错误地重用寄存器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28931625/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com