To work around a Rust compiler bug in the AVR backend, I have marked a lot of my functions as #[inline(always)]
, by just adding the annotations until enough case-of-case optimizations etc. would fire that I no longer run into the issue.
However, with these annotations, linking now fails with lots of relocation truncated to fit
messages:
target/avr-atmega328p/release/deps/chip8_avr-41c427b8d446a439.o: In function `LBB5_18':
chip8_avr.cgu-0.rs:(.text.main+0x432): relocation truncated to fit: R_AVR_7_PCREL against `no symbol'
target/avr-atmega328p/release/deps/chip8_avr-41c427b8d446a439.o: In function `LBB5_23':
chip8_avr.cgu-0.rs:(.text.main+0x45c): relocation truncated to fit: R_AVR_7_PCREL against `no symbol'
target/avr-atmega328p/release/deps/chip8_avr-41c427b8d446a439.o: In function `LBB5_31':
chip8_avr.cgu-0.rs:(.text.main+0x4ae): relocation truncated to fit: R_AVR_7_PCREL against `no symbol'
target/avr-atmega328p/release/deps/chip8_avr-41c427b8d446a439.o: In function `LBB5_34':
chip8_avr.cgu-0.rs:(.text.main+0x4d2): relocation truncated to fit: R_AVR_7_PCREL against `no symbol'
target/avr-atmega328p/release/deps/chip8_avr-41c427b8d446a439.o: In function `LBB5_146':
chip8_avr.cgu-0.rs:(.text.main+0x58a): relocation truncated to fit: R_AVR_7_PCREL against `no symbol'
chip8_avr.cgu-0.rs:(.text.main+0x58e): relocation truncated to fit: R_AVR_7_PCREL against `no symbol'
chip8_avr.cgu-0.rs:(.text.main+0x592): relocation truncated to fit: R_AVR_7_PCREL against `no symbol'
target/avr-atmega328p/release/deps/chip8_avr-41c427b8d446a439.o: In function `LBB5_153':
chip8_avr.cgu-0.rs:(.text.main+0x59a): relocation truncated to fit: R_AVR_7_PCREL against `no symbol'
chip8_avr.cgu-0.rs:(.text.main+0x59e): relocation truncated to fit: R_AVR_7_PCREL against `no symbol'
chip8_avr.cgu-0.rs:(.text.main+0x5a6): relocation truncated to fit: R_AVR_7_PCREL against `no symbol'
chip8_avr.cgu-0.rs:(.text.main+0x5aa): additional relocation overflows omitted from the output
collect2: error: ld returned 1 exit status
This SO answer implies that large intra-function jumps are something that the compiler needs to be prepared for. What is the equivalent setting on Rust?
After looking at disassemblies in enough detail, it turns out that these relocation targets are all in offsets of branch instructions, not (short) jumps; e.g. at 0x08:
00000000 <_ZN12chip8_engine7opcodes6decode17haab3c6c935229a6aE>:
0: e8 2f mov r30, r24
2: f9 2f mov r31, r25
4: 80 e0 ldi r24, 0x00 ; 0
6: 61 30 cpi r22, 0x01 ; 1
8: 01 f4 brne .+0 ; 0xa <_ZN12chip8_engine7opcodes6decode17haab3c6c935229a6aE+0xa>
a: 81 83 std Z+1, r24 ; 0x01
c: 82 83 std Z+2, r24 ; 0x02
e: 81 e0 ldi r24, 0x01 ; 1
00000010 <LBB0_2>:
10: 80 83 st Z, r24
12: 08 95 ret
The AVR fork of the Rust compiler currently generates these branches with empty (.+0
) offsets, then tries to use the linker to fill them in. For large enough functions, these intra-function offsets can become larger than what fits into a single branch instruction.
I've reported this as a compiler bug in the AVR fork of Rust. One potential solution that came up there was to get the linker to generate a two-step branch (a branch to a jump) for cases where the offset doesn't fit; the other is to not use the linker at all: since the branches in question are intra-function, the relative addresses should be known at compile time, allowing for generating a two-step branch when needed.