Busy loop is optimized out, preventing blinking
pusherofbrooms opened this issue · 10 comments
After setting up my cross compiler as per the avr-rust/rust readme, and compiling the unmolested blink program, I upload to my arduino uno clone, and the LED (PORTB 5 or arduino pin 13) doesn't blink, or even come on. reversing the order of the writes doesn't seem to help, but if I add a "loop {}" after setting PORTB to 0xFF, the led will stay lit as I expect.
Is it possible that small_delay is getting optimized away? If I do something like
loop {
for _ in 0..100000 {
unsafe { write_volatile(PORTB, 0x00) }
}
for _ in 0..100000 {
unsafe { write_volatile(PORTB, 0xFF) }
}
}
I get a proper fast blink.
I was reading rust-lang/rust#28728 about some loops being optimized away in LLVM. One comment suggested adding unsafe { asm!("" :::: "volatile")}
to the loop to prevent its removal, and this seems to work:
loop {
for _ in 0..400000 {
unsafe { asm!("" :::: "volatile")}
}
unsafe { write_volatile(PORTB, out) }
out = out ^ 0xff;
}
Hmm. Can you post the LLVM IR that your version of the program generates? The write_volatile
should be marking the operations in a similar fashion to the volatile
marker.
I built with RUSTFLAGS="--emit=llvm-ir" XARGO_RUST_SRC=/home/jjorgens/dev/rust/avr/rust rustup run avr-toolchain xargo build --target avr-atmega328p --release
The IIR output is pretty short:
; ModuleID = 'blink.cgu-0.rs'
source_filename = "blink.cgu-0.rs"
target datalayout = "e-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8"
target triple = "avr-unknown-unknown"
; Function Attrs: norecurse noreturn nounwind uwtable
define void @main() unnamed_addr #0 {
start:
store volatile i8 -1, i8* inttoptr (i16 36 to i8*), align 4
br label %bb1
bb1: ; preds = %bb1, %start
store volatile i8 -1, i8* inttoptr (i16 37 to i8*), align 1
store volatile i8 0, i8* inttoptr (i16 37 to i8*), align 1
br label %bb1
}
; Function Attrs: norecurse nounwind readnone uwtable
define void @rust_eh_personality({}* nocapture, {}* nocapture) unnamed_addr #1 {
start:
ret void
}
attributes #0 = { norecurse noreturn nounwind uwtable }
attributes #1 = { norecurse nounwind readnone uwtable }
!llvm.module.flags = !{!0}
!0 = !{i32 1, !"PIE Level", i32 2}
If I were inclined to quick fix without digging into the root of the problem, the diff might look like
diff --git a/src/main.rs b/src/main.rs
index 9e01951..3165c9a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,5 +1,5 @@
#![feature(lang_items, unwind_attributes)]
-
+#![feature(asm)]
#![no_std]
#![no_main]
@@ -28,7 +28,9 @@ pub extern fn main() {
/// A small busy loop.
fn small_delay() {
- for _ in 0..10000 { }
+ for _ in 0..400000 {
+ unsafe { asm!("" :::: "volatile")}
+ }
}
// These do not need to be in a module, but we group them here for clarity.
I changed 10000 to 400000 so I actually see the blink.
Oh! I totally confused this with my own blink repo which uses timers instead of a busy loop. No wonder I was so confused.
I think the root cause here is that I suggested that people start compiling in release mode, as opposed to the original debug mode. Of course the optimizer sees that loop and throws it away 😇However, we weren't able to build in debug mode due to the inherent code bloat. Hmm.
I don't think a busy loop is really the correct end solution, but if we want to prevent it from being removed, your solution is closest.
I think you can submit your change (moving the feature
with the others in #![feature(lang_items, unwind_attributes)]
) as a fix for now!
Busy loop probably isn't right, but it's a pretty common first step in embedded land as an instructional aid as far as i can tell.
I've raised avr-rust/rust-legacy-fork#85 so that we can develop a proper delay library.
Now that #4 has been merged, can this issue be closed?
Closing is fine. I have my blink.