Description
I tried this code on AVR (arduino Uno = atmega328p) with opt-level="z":
let pixels = [0xf800u16, 0x07e0, 0x001f];
let mut buf = [0u16; 3];
{
let mut i = 0;
for pixel in pixels.map(u16::to_be) {
buf[i] = pixel;
i += 1;
}
let _ = uwriteln!(serial, "debug buf {:?} mapped", buf);
}
I expected to see this happen:
debug buf [248, 57351, 7936] mapped
Instead, this happened:
debug buf [0, 57568, 7967] mapped
An alternate code that emits the correct output is
{
for (i, pixel) in pixels.iter().enumerate() {
buf[i] = pixel.to_be();
}
let _ = uwriteln!(serial, "debug buf {:?} enumerate", buf);
}
This compiler error is corrupting the pixel stream transmitted via SPI to an ST7789 display from an Arduino Uno. What appears to be happening is that instead of the bytes being swapped, the LSB is being copied to the MSB. I do not have the expertise to decompile and examine the assembly code.
Meta
rustc --version --verbose
:
rustc 1.62.0-nightly (88860d547 2022-05-09)
binary: rustc
commit-hash: 88860d5474a32f507dde8fba8df35fd2064f11b9
commit-date: 2022-05-09
host: x86_64-unknown-linux-gnu
release: 1.62.0-nightly
LLVM version: 14.0.1
The malfunction also occurs with cargo +nightly
.
rustc +nightly --version --verbose
rustc 1.63.0-nightly (b31f9cc22 2022-06-15)
binary: rustc
commit-hash: b31f9cc22bcd720b37ddf927afe378108a5b9a54
commit-date: 2022-06-15
host: x86_64-unknown-linux-gnu
release: 1.63.0-nightly
LLVM version: 14.0.5
The full code can be cloned from https://github.com/mutantbob/rust-avr-code-generation-bug
Activity
jamesmunns commentedon Jun 16, 2022
Since I looked it up, here's the expected/actual in hex, which makes the bug more clear:
input:
expected:
actual:
Patryk27 commentedon Jun 16, 2022
Hi,
Could you please check with
opt-level = 1
? It feels like a bug nonetheless, but I'm curious about the result 🙂Patryk27 commentedon Jun 16, 2022
Also:
@rustbot claim
Patryk27 commentedon Jun 20, 2022
I'm analyzing the codegen bug - simplest reproduction so far:
Patryk27 commentedon Jun 25, 2022
Got it!
The simplest reproduction:
This prints
14649
(0x3939
in hex) instead of12345
(0x3039
in hex), because the generated binary accidentally overwrites the high-order byte with the low-order one:After calling
next()
(call 0xa6
on the screenshot), the returned number -12345
(0x3039
in hex) - gets stored into r23:r24 (you can see that r23=0x39, r24=0x30, and AVR being little-endian means the number is reconstructed from r24 first, r23 second).print()
expects itsn: u16
parameter to be stored inr24:r25
, so right before callingprint()
, the codegen has a simple job: mover23:r24
intor24:r25
; and that's where the bug comes into play.What the code generator says is:
... which always sets
r24
andr25
to the same value (r23
) - what it should generate instead is:Overall, that's an omission on the LLVM's side (when moving register pairs, it should consider whether the registers are overlapping or not) - I'll try to prepare a patch 🙂
Edit: https://reviews.llvm.org/D128588.
[AVR] Fix expanding MOVW for overlapping registers
[AVR] Fix expanding MOVW for overlapping registers
Auto merge of rust-lang#98567 - Patryk27:update-llvm, r=nikic
mutantbob commentedon Jul 8, 2022
I just updated to nightly-2022-07-08 and some weirdness I was seeing with
ufmt
seems to have been fixed.Under nightly-2022-05-10 I would see this in the serial log:
start FIFO length = 00e@�@�⸮
Under nightly-2022-07-08 I get
start FIFO length = 307208 ; %16=8
It also fixed the problems I was having with the st7789 crate that led me to file this bug.
[AVR] Fix expanding MOVW for overlapping registers