I’m not sure if this is directly related to zig or llvm but I have strange issue with inline assembly. I have an interrupter handler and I want to use inline assembly in it. I have this code:
export fn PendSV_Handler() void {
asm volatile ("" ++
" CPSID I \n" ++
" LDR R2, [%[current_task]] \n" ++
" CMP.W R2, #0 \n" ++ // if current_task != null
" BEQ.N SpEqlNextSp \n" ++
" PUSH {R4-R11} \n" ++ // push registers r4-r11 on the stack
" STR SP, [R2, %[offset]] \n" ++ // save the current stack pointer in current_task
"SpEqlNextSp: \n" ++
" LDR SP, [%[next_task], %[offset]] \n" ++ // Set stack pointer to next_task stack pointer
" STR %[next_task], [%[current_task], #0x00] \n" ++ // Set current_task to next_task
" POP {r4-r11} \n" ++ // pop registers r4-r11
" CPSIE I \n" // enable interrupts
:
: [current_task] "l" (&OsTask.TaskControl.current_task),
[next_task] "l" (OsTask.TaskControl.next_task),
[offset] "l" (OsCore.g_stack_offset),
: "R2"
);
}
It turns into this disassembly.
When the function is entered the first thing that happens is the input parameters are moved into registers and then ‘CPSID I’ is executed. There are 6 instructions executed before ‘CPSID I’. This makes sense to me. However, ‘CPSID I’ needs to be executed immediately when the function is entered. So I changed the code so that ‘CPSID I’ is in its own inline assembly thinking that this would cause it to execute prior to the input parameters moving into registers.
export fn PendSV_Handler() void {
asm volatile ("" ++
" CPSID I \n"); // disable interrupts
asm volatile ("" ++
" LDR R2, [%[current_task]] \n" ++
" CMP.W R2, #0 \n" ++ // if current_task != null
" BEQ.N SpEqlNextSp \n" ++
" PUSH {R4-R11} \n" ++ // push registers r4-r11 on the stack
" STR SP, [R2, %[offset]] \n" ++ // save the current stack pointer in current_task
"SpEqlNextSp: \n" ++
" LDR SP, [%[next_task], %[offset]] \n" ++ // Set stack pointer to next_task stack pointer
" STR %[next_task], [%[current_task], #0x00] \n" ++ // Set current_task to next_task
" POP {r4-r11} \n" ++ // pop registers r4-r11
" CPSIE I \n" // enable interrupts
:
: [current_task] "l" (&OsTask.TaskControl.current_task),
[next_task] "l" (OsTask.TaskControl.next_task),
[offset] "l" (OsCore.g_stack_offset),
: "R2"
);
}
However, the disassembly doesn’t have ‘CPSID I’ execute first. The input parameters are still partially moved into registers first. Now there are 3 instructions that are executed before ‘CPSID I’.
I thought that the two separate inline assembly blocks would execute sequentially, but that isn’t the case. Is this intended functionality? Is there a way for me force ‘CPSID I’ to execute immediately when the function is entered?