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".