diff --git a/llvm/lib/Target/AVR/MCTargetDesc/AVRAsmBackend.cpp b/llvm/lib/Target/AVR/MCTargetDesc/AVRAsmBackend.cpp --- a/llvm/lib/Target/AVR/MCTargetDesc/AVRAsmBackend.cpp +++ b/llvm/lib/Target/AVR/MCTargetDesc/AVRAsmBackend.cpp @@ -514,6 +514,21 @@ // Fixups which should always be recorded as relocations. case AVR::fixup_7_pcrel: case AVR::fixup_13_pcrel: + // Fix inaccurate PC relative offsets. These errors are introduced by + // instructions such as "rjmp .+off" or "breq .+off", which mean branch + // to address_of_current_instruction+off+2 in avr-gcc. + // TODO: Find a better way to fix, maybe by inheriting the virtual function + // 'bool MCAsmBackend::evaluateTargetFixup(...)'. + if (const auto *SymA = Target.getSymA()) + if (SymA->getSymbol().getName().size() == 0) { + // Adjust the offset to offset+2, while other fields are not changed. + const auto *SymB = Target.getSymB(); + const int64_t C = Target.getConstant() + 2; + const uint32_t RefKind = Target.getRefKind(); + MCValue &MCV = const_cast(Target); + MCV = MCValue::get(SymA, SymB, C, RefKind); + } + [[fallthrough]]; case AVR::fixup_call: return true; } diff --git a/llvm/test/MC/AVR/relocations.s b/llvm/test/MC/AVR/relocations.s --- a/llvm/test/MC/AVR/relocations.s +++ b/llvm/test/MC/AVR/relocations.s @@ -2,6 +2,24 @@ ; CHECK: RELOCATION RECORDS FOR +; CHECK: R_AVR_13_PCREL .text+0x4 +rjmp .+2 + +; CHECK-NEXT: R_AVR_13_PCREL .text+0x4 +rjmp . + +; CHECK-NEXT: R_AVR_13_PCREL .text+0x4 +rjmp .-2 + +; CHECK-NEXT: R_AVR_7_PCREL .text+0xa +breq .+2 + +; CHECK-NEXT: R_AVR_7_PCREL .text+0xa +breq . + +; CHECK-NEXT: R_AVR_7_PCREL .text+0xa +breq .-2 + .global bar bar: jmp bar