Skip to content

Demonstration of a possible LLVM bug when compiling for an AVR target

Notifications You must be signed in to change notification settings

tronje/llvm-avr-compiler-bug

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Possible LLVM compiler bug for AVR targets

The example in main.rs is as minimal as we could make it. The log! statements in State::run are "load bearing", so they need to be there to trigger the bug in this example. Basically some kind of repeated access to multiple struct members appears to trigger the miscompilation.

The 49 bytes of padding appear to be necessary as well; we've only observed this bug with a total struct size of more than 64 bytes.

The miscompilation

So we're not sure why this bug happens, but we've looked at the assembly of broken versions (as in main.rs), and working versions (e.g. main.rs but with a smaller State struct or fewer prints). We spotted one difference that stands out, in the code generated for the State::run method.

Broken code:

ldd	r16, Z+61	; 0x3d
ldd	r17, Z+62	; 0x3e
ldd	r14, Z+63	; 0x3f
ld	r15, Y

This works fine:

ldd	r16, Z+60	; 0x3c
ldd	r17, Z+61	; 0x3d
ldd	r14, Z+62	; 0x3e
ldd	r15, Z+63	; 0x3f

The special Y register is used in the broken version, apparently as an optimization, but not in the working version.

gcc.gnu.org says:

In order to access stack locations, avr-gcc will set up a 16-bit frame pointer in R29:R28 (Y) because the stack pointer (SP) cannot be used to access stack slots.

And LLVM says:

// We tenatively reserve the frame pointer register r29:r28 because the
// function may require one, but we cannot tell until register allocation
// is complete, which can be too late.
//
// Instead we just unconditionally reserve the Y register.
//
// TODO: Write a pass to enumerate functions which reserved the Y register
//       but didn't end up needing a frame pointer. In these, we can
//       convert one or two of the spills inside to use the Y register.
Reserved.set(AVR::R28);
Reserved.set(AVR::R29);
Reserved.set(AVR::R29R28);

But the Y register is used, anyway.

Rust Toolchain

We've reproduced the bug with Rust 1.81.0-nightly.

Target

We're using an atmega164pa.

About

Demonstration of a possible LLVM bug when compiling for an AVR target

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published