-
Notifications
You must be signed in to change notification settings - Fork 13.6k
Description
The following code snippet should light the pin 13 LED when compiled for the atmega328 and run on an arduino. It does not. This is possibly related to #74743, and potentially also avr-rust#47. I initially found this while trying to implement a RawWakerVTable
, so it is not restricted to compiler-generated vtables - any function pointers which are stored in the data segment are affected.
Attempting to simplify further (such as using a single function pointer) tends to end up with pointers which are passed entirely in code and never loaded from the data segment. These appear to be compiled correctly.
#![no_std]
#![no_main]
#![feature(llvm_asm, test)]
#[panic_handler]
fn panic(_panic: &core::panic::PanicInfo) -> ! {
loop {}
}
trait Foo {
unsafe fn foo(&self);
}
impl Foo for () {
unsafe fn foo(&self) {
llvm_asm!(
"SBI 0x04, 5
SBI 0x05, 5"
);
}
}
#[inline(never)]
unsafe fn invoke(foo: &dyn Foo) {
foo.foo()
}
#[no_mangle]
pub unsafe extern "C" fn main() {
invoke(core::hint::black_box(&()));
loop {}
}
Built + Flashed with:
% cargo +nightly build -Z build-std=core --target=avr-unknown-gnu-atmega328 --release
% avrdude -p atmega328p -c arduino -P /dev/tty.usbmodem142401 -Uflash:w:./target/avr-unknown-gnu-atmega328/release/avr-test.elf
Stepping through the execution in simavr/gdb makes the failure obvious - the vtable contains the byte-address of the implementation function, but the icall
instruction (and indeed the instruction pointer of the AVR in general) uses word-addresses.
(gdb) disas
Dump of assembler code for function _ZN8avr_test6invoke17h7f830b8dbe21df91E:
0x000000ae <+0>: movw r30, r22
0x000000b0 <+2>: ldd r18, Z+6 ; 0x06
0x000000b2 <+4>: ldd r19, Z+7 ; 0x07
0x000000b4 <+6>: movw r30, r18
=> 0x000000b6 <+8>: icall
0x000000b8 <+10>: ret
End of assembler dump.
(gdb) info r
<snip>
r30 0xa8 168
r31 0x0 0
SREG 0x0 0
SP 0x8f5 0x8008f5
PC2 0xb6 182
pc 0x5b 0xb6 <avr_test::invoke+8>
(gdb) si
0x00000150 in ?? ()
As can be seen from the GDB output, after loading the VTable entry the Z register (the combination of r30 and r31) contains "0x00A8", and execution jumps to "0x0150". The vtable should instead include "0x0054", which when treated as a word-address will jump to the expected "0x00A8".