rust/tests/run-make/avr-rjmp-offset/rmake.rs
Julian Frimmel a35ed2f9eb
Use rust-lld instead of avr-gcc as the linker
This fixes the [build error] caused by the `avr-gcc` (used as linker)
not being available in the Rust CI. This is a viable solution, which
shows the wrong/right behavior and, since no functions from `libgcc` are
called, does not produce errors. This was discussed [here]. Another
small problem is, that `lld` doesn't link the correct startup-code by
default. This is not a problem for this test (since it does not actually
use anything the startup code is needed for (no variables, no stack, no
interrupts)), but this causes the `main`-function to be removed by the
default flag `--gc-sections`. Therefore the `rmake`-driver also adds the
linker flag `--entry=main` to mark the `main`-function as the entry
point and thus preventing it from getting removed. The code would work
on a real AVR device.

[build error]: https://github.com/rust-lang/rust/pull/131755#issuecomment-2415127952
[here]: https://github.com/rust-lang/rust/pull/131755#issuecomment-2416469675
2024-10-17 09:00:08 +02:00

61 lines
2.5 KiB
Rust

//@ needs-llvm-components: avr
//@ needs-rust-lld
//! Regression test for #129301/llvm-project#106722 within `rustc`.
//!
//! Some LLVM-versions had wrong offsets in the local labels, causing the first
//! loop instruction to be missed. This test therefore contains a simple loop
//! with trivial instructions in it, to see, where the label is placed.
//!
//! This must be a `rmake`-test and cannot be a `tests/assembly`-test, since the
//! wrong output is only produced with direct assembly generation, but not when
//! "emit-asm" is used, as described in the issue description of #129301:
//! https://github.com/rust-lang/rust/issues/129301#issue-2475070770
use run_make_support::{llvm_objdump, rustc};
fn main() {
rustc()
.input("avr-rjmp-offsets.rs")
.opt_level("s")
.panic("abort")
.target("avr-unknown-gnu-atmega328")
// normally one links with `avr-gcc`, but this is not available in CI,
// hence this test diverges from the default behavior to enable linking
// at all, which is necessary for the test (to resolve the labels). To
// not depend on a special linker script, the main-function is marked as
// the entry function, causing the linker to not remove it.
.linker("rust-lld")
.link_arg("--entry=main")
.output("compiled")
.run();
let disassembly = llvm_objdump().disassemble().input("compiled").run().stdout_utf8();
// search for the following instruction sequence:
// ```disassembly
// 00000080 <main>:
// 80: 81 e0 ldi r24, 0x1
// 82: 92 e0 ldi r25, 0x2
// 84: 85 b9 out 0x5, r24
// 86: 95 b9 out 0x5, r25
// 88: fd cf rjmp .-6
// ```
// This matches on all instructions, since the size of the instructions be-
// fore the relative jump has an impact on the label offset. Old versions
// of the Rust compiler did produce a label `rjmp .-4` (misses the first
// instruction in the loop).
assert!(disassembly.contains("<main>"), "no main function in output");
disassembly
.trim()
.lines()
.skip_while(|&line| !line.contains("<main>"))
.inspect(|line| println!("{line}"))
.skip(1)
.zip(["ldi\t", "ldi\t", "out\t", "out\t", "rjmp\t.-6"])
.for_each(|(line, expected_instruction)| {
assert!(
line.contains(expected_instruction),
"expected instruction `{expected_instruction}`, got `{line}`"
);
});
}