consider the following code, which inspects interrupt status before/after actually disabling all CPU interrupts:
export var mask: u32 = 0;
export var flag: bool = false;
export fn intrTest() void {
mask = get_PRIMASK(); // correctly retrieves 'primask' register
flag = isEnabled(); // incorrectly returns true, regardless of 'primask'
asm volatile ("cpsid i" ::: "memory"); // disable interrupts
mask = get_PRIMASK(); // ditto
flag = isEnabled(); // ditto
}
pub fn get_PRIMASK() u32 {
const m: u32 = 0;
asm volatile (
\\mrs %[m], primask
:
: [m] "r" (m),
: "memory"
);
return m;
}
pub fn isEnabled() bool {
return (get_PRIMASK() == 0);
}
[[ i’m targeting a cortex M0+… here’s a godbolt link… ]]
the results of compilation under zig 0.13.0 are as follows:
intrTest:
movs r0, #0
mrs r0, primask
ldr r1, .LCPI0_0
str r0, [r1, #4]
mrs r0, primask
movs r2, #1
strb r2, [r1]
cpsid i
mrs r0, primask
str r0, [r1, #4]
mrs r0, primask
strb r2, [r1]
bx lr
.LCPI0_0:
.long .L_MergedGlobals
i’m suspicious of the movs r2, #1
instruction, as it effectively “asserts” that the primask
register equals 0 – isEnabled
in my source code…
continuing further, the strb r2, [r1]
instruction that occurs after interrupts are disabled with cpsid i
simply stores the same value of 1…
while there is a mrs r0, primask
instruction at the beginning of my isEnabled
function, isn’t it strange that there’s no subsequent code that compares this value to 0???
while i originally though the adjacent calls to get_PRIMASK
were giving the compiler problems here, a smaller example that only uses isEnabled
exhibits the same problem… for whatever reason, the results of get_PRIMASK
inside isEnabled
are simply being swallowed…
i just recently discovered this problem in much larger codebase – where the generated code had the same anomaly… the results are easily inspected using a debugger, logic analyzer, etc…
needless to say, having isEnabled
always return true is leading to all sorts race conditions in my embedded apps