Reputation: 45
I'm learning RTOS on stm32F411RE board (Cortex-M4). I use MDK uVision v5. I encounter a problem of C code while loop. The code in the following is exactly the same in my project and the instructor's project (on Udemy), however, after compiling both project (on my PC), the assembly code look's different. I want to ask what makes this different. Thank you.
void osSignalWait(int32_t *semaphore)
{
__disable_irq();
while(*semaphore <=0)
{
__disable_irq();
__enable_irq();
}
*semaphore -= 0x01;
__enable_irq();
}
In the debug view (see image), if the condition does not match, it does not go to load the real value LDR r1,[r0, #0x00] and then do the comparison. Instead, it compares and goes to execute the command inside the while loop.
My code compiled below
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: }
If I compile the instructor's (on Udemy) code (on my PC using his project), the assembly code look's different ( with exactly the same while loop code). It would load the real value again and do the comparison.
Instructor's code compiled below (Compiled on my PC)
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: }
Upvotes: 3
Views: 944
Reputation: 1304
Since you aren't telling the compiler semaphore
can change during the execution of this function, your compiler has decided to optimize your code and load the value of semaphore only once and use its copy in the while loop, then only write the result in the end. As it is written now, there's no reason for the compiler to assume this could be harmful.
To notify the compiler a variable can change outside the function, during the execution of that function, please use the volatile
keyword, see:
https://en.cppreference.com/w/c/language/volatile
In that case, your code would become:
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();
}
By the way, calling __disable_irq
twice (once before the while loop, then at the start inside the loop) then __enable_irq
seems a bit wonky, don't you mean enable (and do something) then disable within the while loop?
Upvotes: 4
Reputation: 67820
This very well known keil over optimisation bug. Reported many times. Having memory clobber it should read the memory every time.
Here is an example how clobbers work
#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
Upvotes: 0