Comments (10)
Status: got it! -- llvm/llvm-project#85277
Once the fix is merged to LLVM, I should be able to push it to rustc within a couple of days.
from avr-hal.
Minimized:
#![no_std]
#![no_main]
use avr_device::interrupt;
use panic_halt as _;
#[arduino_hal::entry]
fn main() -> ! {
let dp = arduino_hal::Peripherals::take().unwrap();
let pins = arduino_hal::pins!(dp);
let mut serial = arduino_hal::default_serial!(dp, pins, 57600);
unsafe {
interrupt::enable();
}
let irq = interrupt::disable_save();
let counter = core::hint::black_box(123u64);
_ = ufmt::uwriteln!(serial, "{}", counter);
if irq.enabled() {
_ = ufmt::uwriteln!(serial, "irq? 1");
} else {
_ = ufmt::uwriteln!(serial, "irq? 0");
}
loop {
//
}
}
As it is, it prints irq? 0
(which is wrong), but changing 123u64
to 123u32
brings back irq? 1
, so it looks like the registers get mixed up somewhere, investigating 🔍
from avr-hal.
Alright, this one seems to be a proper bug in the LLVM codegen (i.e. doesn't seem to be related to the __udivdi3
etc. intrinsics this time 😄).
Minimized even more (by inlining stuff done by uwriteln!()
etc.) - prints D
, while it should print E
:
#![no_std]
#![no_main]
use avr_device::interrupt;
use panic_halt as _;
#[arduino_hal::entry]
fn main() -> ! {
unsafe {
interrupt::enable();
}
let irq = interrupt::disable_save();
let counter = core::hint::black_box(12u64); // at least a two-digit number is required to trigger the bug
let mut buf = [0; 3];
core::hint::black_box(fmt_num(&mut buf, counter));
if irq.enabled() {
report_irq_enabled();
} else {
report_irq_disabled();
}
loop {
//
}
}
fn fmt_num(buf: &mut [u8], mut n: u64) -> &[u8] { // corresponds to uxx!() from ufmt
let mut i = 0;
loop {
unsafe {
*buf.get_unchecked_mut(i) = (n % 10) as u8;
}
n /= 10;
if n == 0 {
break;
} else {
i += 1;
}
}
if i > 2 { // branch not taken, but required for the bug to occur
panic!("ayy");
}
buf
}
// separated from `main()` to make following the assembly easier
#[inline(never)]
fn report_irq_enabled() {
let dp = arduino_hal::Peripherals::take().unwrap();
let pins = arduino_hal::pins!(dp);
let mut serial = arduino_hal::default_serial!(dp, pins, 57600);
serial.write_byte(b'E');
serial.write_byte(b'\n');
}
// ditto
#[inline(never)]
fn report_irq_disabled() {
let dp = arduino_hal::Peripherals::take().unwrap();
let pins = arduino_hal::pins!(dp);
let mut serial = arduino_hal::default_serial!(dp, pins, 57600);
serial.write_byte(b'D');
serial.write_byte(b'\n');
}
If we inspect the assembly (e.g. by running avr-objdump
or gdb
on the *.elf
file), we'll see four important places:
202: 78 94 sei // `interrupt::enable()`
204: 8f b7 in r24, 0x3f // `interrupt::disable_save()`, remembering previous irq state into `r24`
206: 89 83 std Y+1, r24 // saves `irq` into memory, at address Y+1
/* ... a couple of register-heavy calculations ...
(hence `irq` gets moved from registers into the memory before) */
26e: 49 81 ldd r20, Y+1 // loads `irq` back from the memory into register r20
/* ... some other calculations, taken when the `loop` inside `fmt_num()`
does a *second* round (or third, fourth etc.): */
29a: 19 01 movw r2, r18
29c: a4 01 movw r20, r8 // whoops, this clobbers r20!
29e: b3 01 movw r22, r6
/* ... back in `main()`, after `fmt_num()` completes:
`if irq.enabled()`, checked assuming that `irq` is stored inside `r20`, but
if the `loop` inside `fmt_num()` does a second round, it will clobber `r20`,
making this check invalid! */
312: 44 23 and r20, r20
314: 1a f0 brmi .+6
316: 0e 94 9c 00 call 0x138 ; `report_irq_disabled();`
31a: 02 c0 rjmp .+4
31c: 0e 94 53 00 call 0xa6 ; `report_irq_enabled();`
320: ff cf rjmp .-2
Tracking further 🔍
from avr-hal.
fyi, toolchain's already fixed (compiling with nightly-2024-03-22
produces a correct binary) 🙂
from avr-hal.
Update: I was running cargo run --release
(out of habit), running without the --release
flag seems to fix it. Still strange...
from avr-hal.
I've found a workaround by adding #[inline(never)]
which works with the release build, as it seems the critical section closure gets optimized away into something else...
avr_device::interrupt::free(
#[inline(never)]
|_| {
ufmt::uwriteln!(
serial,
"Counter: {}, interrupts enabled: {}",
counter,
int_en
)
.unwrap_infallible();
},
);
from avr-hal.
I'm suspecting this is a compilation / linker issue rather than crate-level issue.
If I change
[profile.release]
panic = "abort"
codegen-units = 1
debug = true
lto = true
opt-level = "s"
to
[profile.release]
panic = "abort"
codegen-units = 2 # anything > 1
debug = true
opt-level = "s"
my example works again.
Similarly, if I enable either overflow-checks
or debug-assertions
, it also works:
[profile.release]
panic = "abort"
codegen-units = 1
debug = true
lto = true
opt-level = "s"
overflow-checks = true # enable either one (or both)
debug-assertions = true # enable either one (or both)
from avr-hal.
Paging @Patryk27, maybe you know something here?
Otherwise we'll need to look at the generated assembly to find out where this is falling apart. My first guess would be that some u64 intrinsics are not working correctly...
from avr-hal.
Judging by the behavior, this does look like a codegen bug, but I don't recall anything that could cause it (in this case the intrinsics should be fine, but always worth to take a look there, considering the history 😄) - I'll also try to take a look during the week.
from avr-hal.
Nice, then it's time to bump the compiler version again :)
from avr-hal.
Related Issues (20)
- Panic while trying to write to serial using `ufmt::uwriteln` HOT 4
- Add configurable board options to ravedude HOT 3
- Get pin number HOT 6
- Program freezes if compiled in debug mode.
- ravedude should check rust target against selected MCU HOT 3
- Document clock type design in more detail HOT 1
- cargo build - error[E0658] - proc_macro::Literal::byte_character(byte) HOT 9
- can't find crate for `core` HOT 12
- cargo build - error[E0658] - proc_macro::Literal::byte_character(byte) HOT 4
- Very weird error when compiling a very basic program: __addsf3 multiple defenitions HOT 9
- Ravedude freezes on programmer uploading HOT 5
- Arduino Nano: avrdude error: programmer is not responding HOT 11
- Watchdog intterupt mode HOT 1
- [Solved] Cannot find `pwm` in `embedded_hal` HOT 2
- peripherals type alias HOT 2
- Enhance `avr_hal_generic::renamed_pins! {}` to also generate type aliases for each pin
- tier 3 target and broken HOT 6
- Utilising the `atmega2560`'s "USART in SPI Mode" HOT 10
- Issue with math calculations with floats to serial port output HOT 6
- Incorrect ADC readings after toolchain bump HOT 15
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from avr-hal.