-
Notifications
You must be signed in to change notification settings - Fork 14.1k
Description
Hello,
The code is here.
Basically, it consists of:
static mut CTR: RefCell<u8> = RefCell::new(0);
#[avr_device::interrupt(atmega328p)]
fn TIMER0_OVF() {
interrupt::free(|_cs| unsafe {
let ctr = CTR.get_mut();
let mut inc = *ctr + 1;
if inc > 254 {
inc = 1;
}
*ctr = inc
});
}
#[avr_device::entry]
fn main() -> ! {
....
unsafe { asm!("444444:"); }
loop {
let wait_qty: u8;
unsafe { wait_qty = *CTR.get_mut(); };
if wait_qty > 0 {
dp.PORTD.portd.modify(|_, w| w.pd2().set_bit());
}
else {
//unsafe { asm!("nop") }
}
}
}I expected to see this happen:
Basically, at the start of the program, CTR is 0 and thus the else branch is executed inside the loop.
After some time, an interrupt is raised, and CTR is set to 1, and the if branch should be run (and an LED turned on).
Instead, this happened:
The LED never lights up. However, if I add an instruction in the else branch (like a nop), then the code runs as expected (the LED turns on afterwards)
Meta
rustc --version --verbose:
Compiling mega328-test v0.1.0 (/home/frank/src/github.com/Rahix/avr-device/examples/atmega328p)
rustc 1.77.0-nightly (e51e98dde 2023-12-31)
binary: rustc
commit-hash: e51e98dde6a60637b6a71b8105245b629ac3fe77
commit-date: 2023-12-31
host: x86_64-unknown-linux-gnu
release: 1.77.0-nightly
LLVM version: 17.0.6
Finished dev [optimized + debuginfo] target(s) in 0.06s
For the sake of simplicity, I created two examples:
- GOOD, with a
nopin the else branch -> LED turns on. - BAD with nothing on the else branch -> LED stays off.
I tried to debug the compilation, and I see that:
MIR
The runtime optimized after MIR (006.000) looks okay on both cases:
GOOD:
MIR excerpt
bb14: {
StorageLive(_25);
StorageLive(_26);
StorageLive(_27);
_27 = const {alloc1: *mut RefCell<u8>};
_26 = &mut (*_27);
_25 = RefCell::<u8>::get_mut(move _26) -> [return: bb15, unwind unreachable];
}
bb15: {
StorageDead(_26);
_24 = (*_25);
StorageDead(_27);
StorageDead(_25);
StorageLive(_28);
_28 = Gt(_24, const 0_u8);
switchInt(move _28) -> [0: bb19, otherwise: bb16];
}
bb16: {
StorageLive(_30);
StorageLive(_31);
StorageLive(_32);
_32 = &(_1.9: avr_device::atmega328p::PORTD);
_31 = <PORTD as Deref>::deref(move _32) -> [return: bb17, unwind unreachable];
}
bb17: {
StorageDead(_32);
_30 = &((*_31).2: avr_device::generic::Reg<avr_device::atmega328p::portd::portd::PORTD_SPEC>);
_29 = Reg::<PORTD_SPEC>::modify::<[closure@src/main.rs:58:35: 58:41]>(move _30, const ZeroSized: [closure@src/main.rs:58:35: 58:41]) -> [return: bb18, unwind unreachable];
}
bb18: {
StorageDead(_30);
StorageDead(_31);
goto -> bb20;
}
bb19: {
asm!("444444:", options((empty))) -> [return: bb20, unwind unreachable];
}
bb20: {
StorageDead(_28);
goto -> bb14;
}BAD:
MIR excerpt
bb14: {
StorageLive(_25);
StorageLive(_26);
StorageLive(_27);
_27 = const {alloc1: *mut RefCell<u8>};
_26 = &mut (*_27);
_25 = RefCell::<u8>::get_mut(move _26) -> [return: bb15, unwind unreachable];
}
bb15: {
StorageDead(_26);
_24 = (*_25);
StorageDead(_27);
StorageDead(_25);
StorageLive(_28);
_28 = Gt(_24, const 0_u8);
switchInt(move _28) -> [0: bb19, otherwise: bb16];
}
bb16: {
StorageLive(_30);
StorageLive(_31);
StorageLive(_32);
_32 = &(_1.9: avr_device::atmega328p::PORTD);
_31 = <PORTD as Deref>::deref(move _32) -> [return: bb17, unwind unreachable];
}
bb17: {
StorageDead(_32);
_30 = &((*_31).2: avr_device::generic::Reg<avr_device::atmega328p::portd::portd::PORTD_SPEC>);
_29 = Reg::<PORTD_SPEC>::modify::<[closure@src/main.rs:58:35: 58:41]>(move _30, const ZeroSized: [closure@src/main.rs:58:35: 58:41]) -> [return: bb18, unwind unreachable];
}
bb18: {
StorageDead(_30);
StorageDead(_31);
goto -> bb19;
}
bb19: {
StorageDead(_28);
goto -> bb14;
}Basically, the diff is in bb18/bb19
LLVM-IR:
GOOD:
LLVM-IR excerpt
br label %bb14, !dbg !3347
bb14: ; preds = %bb19, %bb16, %"_ZN4core6option15Option$LT$T$GT$6unwrap17h450249f892692cf5E.exit"
%wait_qty = load i8, ptr @_ZN12mega328_test3CTR17hd094fa9ca712b679E.0, align 1, !dbg !3349, !noundef !358
call addrspace(1) void @llvm.dbg.value(metadata i8 %wait_qty, metadata !2947, metadata !DIExpression()), !dbg !3350
%_28.not = icmp eq i8 %wait_qty, 0, !dbg !3351
br i1 %_28.not, label %bb19, label %bb16, !dbg !3351BAD:
LLVM-IR excerpt
%wait_qty.pre1 = load i8, ptr @_ZN12mega328_test3CTR17hd094fa9ca712b679E.0, align 1, !dbg !3349
br label %bb14, !dbg !3347
bb14: ; preds = %bb14, %bb16, %"_ZN4core6option15Option$LT$T$GT$6unwrap17h450249f892692cf5E.exit"
%wait_qty = phi i8 [ %wait_qty.pre1, %"_ZN4core6option15Option$LT$T$GT$6unwrap17h450249f892692cf5E.exit" ], [ %wait_qty.pre, %bb16 ], [ 0, %bb14 ], !dbg !3349
call addrspace(1) void @llvm.dbg.value(metadata i8 %wait_qty, metadata !2947, metadata !DIExpression()), !dbg !3350
%_28.not = icmp eq i8 %wait_qty, 0, !dbg !3351
br i1 %_28.not, label %bb14, label %bb16, !dbg !3351I don't really know LLVM, but the phi i8 that uses [0, %bb14] looks strange to me, making the condition after always true ?
Assembly
BAD:
loop {
let wait_qty: u8;
unsafe { wait_qty = *CTR.get_mut(); };
24: 80 91 00 00 lds r24, 0x0000 ; 0x800000 <__SREG__+0x7fffc1>
if wait_qty > 0 {
28: 80 30 cpi r24, 0x00 ; 0
2a: 81 2d mov r24, r1
2c: 01 f0 breq .+0 ; 0x2e <_ZN12mega328_test20__avr_device_rt_main17h8ca9fc0fd7a09522E+0x2e>
2e: 5a 9a sbi 0x0b, 2 ; 11
30: 00 c0 rjmp .+0 ; 0x32 <_ZN12mega328_test20__avr_device_rt_main17h8ca9fc0fd7a09522E+0x32>To be honest I don't really understand instruction 24; why would it try to load from 0x0000 ?
What do you think ?
Many thanks for your help !