diff --git a/lld/ELF/Arch/AVR.cpp b/lld/ELF/Arch/AVR.cpp --- a/lld/ELF/Arch/AVR.cpp +++ b/lld/ELF/Arch/AVR.cpp @@ -26,6 +26,7 @@ //===----------------------------------------------------------------------===// #include "InputFiles.h" +#include "OutputSections.h" #include "Symbols.h" #include "Target.h" #include "lld/Common/ErrorHandler.h" @@ -47,6 +48,9 @@ const uint8_t *loc) const override; void relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const override; + void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override; + bool tryRelaxLongJumpCall(uint8_t *loc, uint64_t callAddr, + uint64_t destAddr) const; }; } // namespace @@ -234,6 +238,57 @@ return cast>(file)->getObj().getHeader().e_flags; } +// Try to relax +// jmp _foo ; 4-byte instruction +// to +// rjmp _foo ; 2-byte instruction +// nop ; 2-byte instruction +bool AVR::tryRelaxLongJumpCall(uint8_t *loc, uint64_t callAddr, + uint64_t destAddr) const { + // The offset must be in range [-4094, 4096]. + const int64_t Off = destAddr - callAddr; + if (Off < -4094 || Off > 4096) + return false; + + // Set the first instruction to RCALL/RJMP. + const uint16_t OffCode = static_cast(Off - 2) >> 1; + const uint16_t OpCode = read16le(loc) == 0x940c ? 0xc000 : 0xd000; + write16le(loc, OpCode | (OffCode & 0xfff)); + + // Set the second instruction to NOP. + write16le(loc + 2, 0); + + return true; +} + +void AVR::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const { + uint64_t secAddr = sec.getOutputSection()->addr; + if (auto *s = dyn_cast(&sec)) + secAddr += s->outSecOff; + for (const Relocation &rel : sec.relocs()) { + uint8_t *loc = buf + rel.offset; + const uint64_t val = SignExtend64( + sec.getRelocTargetVA(sec.file, rel.type, rel.addend, + secAddr + rel.offset, *rel.sym, rel.expr), + 32); + + switch (rel.type) { + // Try to relax a long jump/call (a 4-byte instruction) to a pair of + // short jump/call (a 2-byte instruction) and nop (a 2-byte instruction). + case R_AVR_CALL: + if (config->relax && + (getEFlags(ctx.objectFiles[0]) & EF_AVR_LINKRELAX_PREPARED) != 0) + if (tryRelaxLongJumpCall(loc, secAddr + rel.offset, val)) + continue; + [[fallthrough]]; + + default: + relocate(loc, rel, val); + break; + } + } +} + uint32_t AVR::calcEFlags() const { assert(!ctx.objectFiles.empty()); diff --git a/lld/test/ELF/avr-relax.s b/lld/test/ELF/avr-relax.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/avr-relax.s @@ -0,0 +1,39 @@ +# REQUIRES: avr +# RUN: llvm-mc -filetype=obj -triple=avr -mcpu=atmega328p %s -o %t.o +# RUN: ld.lld %t.o -o %t0.exe -Ttext=0 --no-relax +# RUN: llvm-objdump -d %t0.exe --mcpu=atmega328 | FileCheck %s --check-prefix=NORELAX +# RUN: ld.lld %t.o -o %t1.exe -Ttext=0 +# RUN: llvm-objdump -d %t1.exe --mcpu=atmega328 | FileCheck %s --check-prefix=RELAX + +main: + call foo + rcall foo +foo: + jmp foo + rjmp foo + +la0: + jmp la1 + .zero 4096 +la1: + jmp la0 + +# NORELAX:
: +# NORELAX-NEXT: 0: 0e 94 03 00 call 0x6 +# NORELAX-NEXT: 4: 00 d0 rcall .+0 +# NORELAX: : +# NORELAX-NEXT: 6: 0c 94 03 00 jmp 0x6 +# NORELAX-NEXT: a: fd cf rjmp .-6 + +# RELAX:
: +# RELAX-NEXT: 0: 02 d0 rcall .+4 +# RELAX-NEXT: 2: 00 00 nop +# RELAX-NEXT: 4: 00 d0 rcall .+0 +# RELAX: : +# RELAX-NEXT: 6: ff cf rjmp .-2 +# RELAX-NEXT: 8: 00 00 nop +# RELAX-NEXT: a: fd cf rjmp .-6 +# RELAX: : +# RELAX-NEXT: c: 0c 94 08 08 jmp 0x1010 +# RELAX: : +# RELAX-NEXT: 1010: 0c 94 06 00 jmp 0xc diff --git a/lld/test/ELF/basic-avr.s b/lld/test/ELF/basic-avr.s --- a/lld/test/ELF/basic-avr.s +++ b/lld/test/ELF/basic-avr.s @@ -1,7 +1,6 @@ -# REQUIRES: avr -# RUN: llvm-mc -filetype=obj -triple=avr-unknown-linux -mcpu=atmega328p %s -o %t.o -# RUN: ld.lld %t.o -o %t.exe -Ttext=0 -# RUN: llvm-objdump -d %t.exe | FileCheck %s +# RUN: llvm-mc -filetype=obj -triple=avr -mcpu=atmega328p %s -o %t.o +# RUN: ld.lld %t.o -o %t.exe -Ttext=0 --no-relax +# RUN: llvm-objdump -d %t.exe --mcpu=atmega328 | FileCheck %s main: call foo @@ -9,6 +8,6 @@ jmp foo # CHECK:
: -# CHECK-NEXT: 0: 0e 94 02 00 +# CHECK-NEXT: 0: 0e 94 02 00 call 0x4 # CHECK: : -# CHECK-NEXT: 4: 0c 94 02 00 +# CHECK-NEXT: 4: 0c 94 02 00 jmp 0x4