STM32 相同的 while 循环代码但编译为不同的汇编代码
STM32 same while loop code but compiled to different assembly code
我正在 stm32F411RE 板 (Cortex-M4) 上学习 RTOS。我使用 MDK uVision v5。我遇到一个C代码的问题while loop。以下代码在我的项目和讲师的项目(在 Udemy 上)中完全相同,但是,在编译两个项目(在我的 PC 上)之后,汇编代码看起来不同。我想问一下这有什么不同。谢谢。
void osSignalWait(int32_t *semaphore)
{
__disable_irq();
while(*semaphore <=0)
{
__disable_irq();
__enable_irq();
}
*semaphore -= 0x01;
__enable_irq();
}
在调试视图中(见图),如果条件不匹配,则不会加载实际值 LDR r1,[r0, #0x00] 和然后做比较。相反,它比较并去执行 while 循环内的命令。
我的代码编译如下
100: void osSignalWait(int32_t *semaphore)
101: {
0x08001566 4770 BX lr
102: __disable_irq();
103: while(*semaphore <=0)
104: {
0x08001568 B672 CPSID I
101: {
102: __disable_irq();
103: while(*semaphore <=0)
104: {
0x0800156A 6801 LDR r1,[r0,#0x00]
0x0800156C E001 B 0x08001572
105: __disable_irq();
0x0800156E B672 CPSID I
106: __enable_irq();
107: }
108: *semaphore -= 0x01;
0x08001570 B662 CPSIE I
0x08001572 2900 CMP r1,#0x00
0x08001574 DDFB BLE 0x0800156E
0x08001576 1E49 SUBS r1,r1,#1
109: __enable_irq();
0x08001578 6001 STR r1,[r0,#0x00]
0x0800157A B662 CPSIE I
110: }
如果我编译讲师(在 Udemy 上)的代码(在我的 PC 上使用他的项目),汇编代码看起来会有所不同(具有完全相同的 while 循环代码)。它会再次加载真实值并进行比较。
下面编译的讲师代码(在我的电脑上编译)
100: void osSignalWait(int32_t *semaphore)
101: {
0x08000CDE 4770 BX lr
102: __disable_irq();
0x08000CE0 B672 CPSID I
103: while(*semaphore <=0)
104: {
0x08000CE2 E001 B 0x08000CE8
105: __disable_irq();
0x08000CE4 B672 CPSID I
106: __enable_irq();
107: }
0x08000CE6 B662 CPSIE I
0x08000CE8 6801 LDR r1,[r0,#0x00]
0x08000CEA 2900 CMP r1,#0x00
0x08000CEC DDFA BLE 0x08000CE4
108: *semaphore -= 0x01;
0x08000CEE 6801 LDR r1,[r0,#0x00]
0x08000CF0 1E49 SUBS r1,r1,#1
0x08000CF2 6001 STR r1,[r0,#0x00]
109: __enable_irq();
110:
111:
0x08000CF4 B662 CPSIE I
112: }
因为你没有告诉编译器 semaphore
可以在这个函数的执行过程中改变,你的编译器已经决定优化你的代码并只加载一次信号量的值,同时使用它的副本循环,最后只写结果。正如现在所写的那样,编译器没有理由认为这可能有害。
要通知编译器一个变量可以在函数外改变,在那个函数执行过程中,请使用volatile
关键字,见:
https://en.cppreference.com/w/c/language/volatile
在这种情况下,您的代码将变为:
void osSignalWait(volatile int32_t *semaphore)
{
__disable_irq();
while(*semaphore <=0)
{
__disable_irq(); // Note: I think the order is wrong...
__enable_irq();
}
*semaphore -= 0x01;
__enable_irq();
}
顺便说一句,调用 __disable_irq
两次(一次在 while 循环之前,然后在循环开始时)然后 __enable_irq
似乎有点靠不住,你不是说启用(和做某事)然后在 while 循环中禁用?
这是众所周知的 keil 过度优化错误。多次举报。有内存破坏,它应该每次都读取内存。
这里是一个 clobbers 工作的例子
#include <stdint.h>
unsigned x;
volatile unsigned y;
int foo()
{
while(x < 1000);
}
int bar()
{
while(x < 1000) asm("":::"memory");
}
foo:
ldr r3, .L5
ldr r3, [r3]
cmp r3, #1000
bxcs lr
.L3:
b .L3
.L5:
.word x
bar:
ldr r1, .L11
ldr r2, .L11+4
ldr r3, [r1]
cmp r3, r2
bxhi lr
.L9:
ldr r3, [r1]
cmp r3, r2
bls .L9
bx lr
.L11:
.word x
.word 999
我正在 stm32F411RE 板 (Cortex-M4) 上学习 RTOS。我使用 MDK uVision v5。我遇到一个C代码的问题while loop。以下代码在我的项目和讲师的项目(在 Udemy 上)中完全相同,但是,在编译两个项目(在我的 PC 上)之后,汇编代码看起来不同。我想问一下这有什么不同。谢谢。
void osSignalWait(int32_t *semaphore)
{
__disable_irq();
while(*semaphore <=0)
{
__disable_irq();
__enable_irq();
}
*semaphore -= 0x01;
__enable_irq();
}
在调试视图中(见图),如果条件不匹配,则不会加载实际值 LDR r1,[r0, #0x00] 和然后做比较。相反,它比较并去执行 while 循环内的命令。
100: void osSignalWait(int32_t *semaphore)
101: {
0x08001566 4770 BX lr
102: __disable_irq();
103: while(*semaphore <=0)
104: {
0x08001568 B672 CPSID I
101: {
102: __disable_irq();
103: while(*semaphore <=0)
104: {
0x0800156A 6801 LDR r1,[r0,#0x00]
0x0800156C E001 B 0x08001572
105: __disable_irq();
0x0800156E B672 CPSID I
106: __enable_irq();
107: }
108: *semaphore -= 0x01;
0x08001570 B662 CPSIE I
0x08001572 2900 CMP r1,#0x00
0x08001574 DDFB BLE 0x0800156E
0x08001576 1E49 SUBS r1,r1,#1
109: __enable_irq();
0x08001578 6001 STR r1,[r0,#0x00]
0x0800157A B662 CPSIE I
110: }
如果我编译讲师(在 Udemy 上)的代码(在我的 PC 上使用他的项目),汇编代码看起来会有所不同(具有完全相同的 while 循环代码)。它会再次加载真实值并进行比较。
100: void osSignalWait(int32_t *semaphore)
101: {
0x08000CDE 4770 BX lr
102: __disable_irq();
0x08000CE0 B672 CPSID I
103: while(*semaphore <=0)
104: {
0x08000CE2 E001 B 0x08000CE8
105: __disable_irq();
0x08000CE4 B672 CPSID I
106: __enable_irq();
107: }
0x08000CE6 B662 CPSIE I
0x08000CE8 6801 LDR r1,[r0,#0x00]
0x08000CEA 2900 CMP r1,#0x00
0x08000CEC DDFA BLE 0x08000CE4
108: *semaphore -= 0x01;
0x08000CEE 6801 LDR r1,[r0,#0x00]
0x08000CF0 1E49 SUBS r1,r1,#1
0x08000CF2 6001 STR r1,[r0,#0x00]
109: __enable_irq();
110:
111:
0x08000CF4 B662 CPSIE I
112: }
因为你没有告诉编译器 semaphore
可以在这个函数的执行过程中改变,你的编译器已经决定优化你的代码并只加载一次信号量的值,同时使用它的副本循环,最后只写结果。正如现在所写的那样,编译器没有理由认为这可能有害。
要通知编译器一个变量可以在函数外改变,在那个函数执行过程中,请使用volatile
关键字,见:
https://en.cppreference.com/w/c/language/volatile
在这种情况下,您的代码将变为:
void osSignalWait(volatile int32_t *semaphore)
{
__disable_irq();
while(*semaphore <=0)
{
__disable_irq(); // Note: I think the order is wrong...
__enable_irq();
}
*semaphore -= 0x01;
__enable_irq();
}
顺便说一句,调用 __disable_irq
两次(一次在 while 循环之前,然后在循环开始时)然后 __enable_irq
似乎有点靠不住,你不是说启用(和做某事)然后在 while 循环中禁用?
这是众所周知的 keil 过度优化错误。多次举报。有内存破坏,它应该每次都读取内存。
这里是一个 clobbers 工作的例子
#include <stdint.h>
unsigned x;
volatile unsigned y;
int foo()
{
while(x < 1000);
}
int bar()
{
while(x < 1000) asm("":::"memory");
}
foo:
ldr r3, .L5
ldr r3, [r3]
cmp r3, #1000
bxcs lr
.L3:
b .L3
.L5:
.word x
bar:
ldr r1, .L11
ldr r2, .L11+4
ldr r3, [r1]
cmp r3, r2
bxhi lr
.L9:
ldr r3, [r1]
cmp r3, r2
bls .L9
bx lr
.L11:
.word x
.word 999