Skip to content

Commit d519636

Browse files
committed
[AVR] Force relocations for jumps and calls
1 parent 69d66fa commit d519636

26 files changed

+247
-265
lines changed

llvm/lib/Target/AVR/AVRInstrInfo.cpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -557,17 +557,23 @@ void AVRInstrInfo::insertIndirectBranch(MachineBasicBlock &MBB,
557557
MachineBasicBlock &RestoreBB,
558558
const DebugLoc &DL, int64_t BrOffset,
559559
RegScavenger *RS) const {
560-
// This method inserts a *direct* branch (JMP), despite its name.
561-
// LLVM calls this method to fixup unconditional branches; it never calls
562-
// insertBranch or some hypothetical "insertDirectBranch".
563-
// See lib/CodeGen/RegisterRelaxation.cpp for details.
564-
// We end up here when a jump is too long for a RJMP instruction.
560+
// When an instruction's jump target is too far away¹, the branch relaxation
561+
// pass might split an existing, large basic block into two pieces - if that
562+
// happens, this function gets called to create a new jump between the blocks.
563+
//
564+
// Since this new jump might be too large for an `rjmp` anyway, let's
565+
// conservatively emit `jmp` and back off to `rjmp` only if the target arch
566+
// doesn't support `jmp`.
567+
//
568+
// TODO maybe we could use `BrOffset` to determine whether it's safe to emit
569+
// `rjmp`? (it takes one byte less to encode, so it'd be a small win)
570+
//
571+
// ¹ "too far" in the sense of "the instruction encoding doesn't allow for an
572+
// offset that large"
573+
565574
if (STI.hasJMPCALL())
566575
BuildMI(&MBB, DL, get(AVR::JMPk)).addMBB(&NewDestBB);
567576
else
568-
// The RJMP may jump to a far place beyond its legal range. We let the
569-
// linker to report 'out of range' rather than crash, or silently emit
570-
// incorrect assembly code.
571577
BuildMI(&MBB, DL, get(AVR::RJMPk)).addMBB(&NewDestBB);
572578
}
573579

llvm/lib/Target/AVR/MCTargetDesc/AVRAsmBackend.cpp

Lines changed: 26 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -27,129 +27,32 @@
2727
#include "llvm/Support/MathExtras.h"
2828
#include "llvm/Support/raw_ostream.h"
2929

30-
// FIXME: we should be doing checks to make sure asm operands
31-
// are not out of bounds.
32-
3330
namespace adjust {
3431

3532
using namespace llvm;
3633

37-
static void signed_width(unsigned Width, uint64_t Value,
38-
std::string Description, const MCFixup &Fixup,
39-
MCContext *Ctx = nullptr) {
40-
if (!isIntN(Width, Value)) {
41-
std::string Diagnostic = "out of range " + Description;
42-
43-
int64_t Min = minIntN(Width);
44-
int64_t Max = maxIntN(Width);
45-
46-
Diagnostic += " (expected an integer in the range " + std::to_string(Min) +
47-
" to " + std::to_string(Max) + ")";
48-
49-
if (Ctx) {
50-
Ctx->reportError(Fixup.getLoc(), Diagnostic);
51-
} else {
52-
llvm_unreachable(Diagnostic.c_str());
53-
}
54-
}
55-
}
56-
57-
static void unsigned_width(unsigned Width, uint64_t Value,
58-
std::string Description, const MCFixup &Fixup,
59-
MCContext *Ctx = nullptr) {
34+
static void ensureUnsignedWidth(unsigned Width, uint64_t Value,
35+
std::string Description, const MCFixup &Fixup,
36+
MCContext *Ctx) {
6037
if (!isUIntN(Width, Value)) {
6138
std::string Diagnostic = "out of range " + Description;
6239

6340
int64_t Max = maxUIntN(Width);
6441

65-
Diagnostic +=
66-
" (expected an integer in the range 0 to " + std::to_string(Max) + ")";
42+
Diagnostic += " (expected an unsigned integer in the range 0 to " +
43+
std::to_string(Max) + ", got " + std::to_string(Value) + ")";
6744

68-
if (Ctx) {
69-
Ctx->reportError(Fixup.getLoc(), Diagnostic);
70-
} else {
71-
llvm_unreachable(Diagnostic.c_str());
72-
}
45+
Ctx->reportError(Fixup.getLoc(), Diagnostic);
7346
}
7447
}
7548

76-
/// Adjusts the value of a branch target before fixup application.
77-
static void adjustBranch(unsigned Size, const MCFixup &Fixup, uint64_t &Value,
78-
MCContext *Ctx = nullptr) {
79-
// We have one extra bit of precision because the value is rightshifted by
80-
// one.
81-
unsigned_width(Size + 1, Value, std::string("branch target"), Fixup, Ctx);
82-
83-
// Rightshifts the value by one.
84-
AVR::fixups::adjustBranchTarget(Value);
85-
}
86-
87-
/// Adjusts the value of a relative branch target before fixup application.
88-
static void adjustRelativeBranch(unsigned Size, const MCFixup &Fixup,
89-
uint64_t &Value, MCContext *Ctx = nullptr) {
90-
// Jumps are relative to the current instruction.
91-
Value -= 2;
92-
93-
// We have one extra bit of precision because the value is rightshifted by
94-
// one.
95-
signed_width(Size + 1, Value, std::string("branch target"), Fixup, Ctx);
96-
97-
// Rightshifts the value by one.
98-
AVR::fixups::adjustBranchTarget(Value);
99-
}
100-
101-
/// 22-bit absolute fixup.
102-
///
103-
/// Resolves to:
104-
/// 1001 kkkk 010k kkkk kkkk kkkk 111k kkkk
105-
///
106-
/// Offset of 0 (so the result is left shifted by 3 bits before application).
107-
static void fixup_call(unsigned Size, const MCFixup &Fixup, uint64_t &Value,
108-
MCContext *Ctx = nullptr) {
109-
adjustBranch(Size, Fixup, Value, Ctx);
110-
111-
auto top = Value & (0xf00000 << 6); // the top four bits
112-
auto middle = Value & (0x1ffff << 5); // the middle 13 bits
113-
auto bottom = Value & 0x1f; // end bottom 5 bits
114-
115-
Value = (top << 6) | (middle << 3) | (bottom << 0);
116-
}
117-
118-
/// 7-bit PC-relative fixup.
119-
///
120-
/// Resolves to:
121-
/// 0000 00kk kkkk k000
122-
/// Offset of 0 (so the result is left shifted by 3 bits before application).
123-
static void fixup_7_pcrel(unsigned Size, const MCFixup &Fixup, uint64_t &Value,
124-
MCContext *Ctx = nullptr) {
125-
adjustRelativeBranch(Size, Fixup, Value, Ctx);
126-
127-
// Because the value may be negative, we must mask out the sign bits
128-
Value &= 0x7f;
129-
}
130-
131-
/// 12-bit PC-relative fixup.
132-
/// Yes, the fixup is 12 bits even though the name says otherwise.
133-
///
134-
/// Resolves to:
135-
/// 0000 kkkk kkkk kkkk
136-
/// Offset of 0 (so the result isn't left-shifted before application).
137-
static void fixup_13_pcrel(unsigned Size, const MCFixup &Fixup, uint64_t &Value,
138-
MCContext *Ctx = nullptr) {
139-
adjustRelativeBranch(Size, Fixup, Value, Ctx);
140-
141-
// Because the value may be negative, we must mask out the sign bits
142-
Value &= 0xfff;
143-
}
144-
14549
/// 6-bit fixup for the immediate operand of the STD/LDD family of
14650
/// instructions.
14751
///
14852
/// Resolves to:
14953
/// 10q0 qq10 0000 1qqq
150-
static void fixup_6(const MCFixup &Fixup, uint64_t &Value,
151-
MCContext *Ctx = nullptr) {
152-
unsigned_width(6, Value, std::string("immediate"), Fixup, Ctx);
54+
static void fixup_6(const MCFixup &Fixup, uint64_t &Value, MCContext *Ctx) {
55+
ensureUnsignedWidth(6, Value, std::string("immediate"), Fixup, Ctx);
15356

15457
Value = ((Value & 0x20) << 8) | ((Value & 0x18) << 7) | (Value & 0x07);
15558
}
@@ -160,8 +63,8 @@ static void fixup_6(const MCFixup &Fixup, uint64_t &Value,
16063
/// Resolves to:
16164
/// 0000 0000 kk00 kkkk
16265
static void fixup_6_adiw(const MCFixup &Fixup, uint64_t &Value,
163-
MCContext *Ctx = nullptr) {
164-
unsigned_width(6, Value, std::string("immediate"), Fixup, Ctx);
66+
MCContext *Ctx) {
67+
ensureUnsignedWidth(6, Value, std::string("immediate"), Fixup, Ctx);
16568

16669
Value = ((Value & 0x30) << 2) | (Value & 0x0f);
16770
}
@@ -170,9 +73,8 @@ static void fixup_6_adiw(const MCFixup &Fixup, uint64_t &Value,
17073
///
17174
/// Resolves to:
17275
/// 0000 0000 AAAA A000
173-
static void fixup_port5(const MCFixup &Fixup, uint64_t &Value,
174-
MCContext *Ctx = nullptr) {
175-
unsigned_width(5, Value, std::string("port number"), Fixup, Ctx);
76+
static void fixup_port5(const MCFixup &Fixup, uint64_t &Value, MCContext *Ctx) {
77+
ensureUnsignedWidth(5, Value, std::string("port number"), Fixup, Ctx);
17678

17779
Value &= 0x1f;
17880

@@ -183,9 +85,8 @@ static void fixup_port5(const MCFixup &Fixup, uint64_t &Value,
18385
///
18486
/// Resolves to:
18587
/// 1011 0AAd dddd AAAA
186-
static void fixup_port6(const MCFixup &Fixup, uint64_t &Value,
187-
MCContext *Ctx = nullptr) {
188-
unsigned_width(6, Value, std::string("port number"), Fixup, Ctx);
88+
static void fixup_port6(const MCFixup &Fixup, uint64_t &Value, MCContext *Ctx) {
89+
ensureUnsignedWidth(6, Value, std::string("port number"), Fixup, Ctx);
18990

19091
Value = ((Value & 0x30) << 5) | (Value & 0x0f);
19192
}
@@ -195,8 +96,8 @@ static void fixup_port6(const MCFixup &Fixup, uint64_t &Value,
19596
/// Resolves to:
19697
/// 1010 ikkk dddd kkkk
19798
static void fixup_lds_sts_16(const MCFixup &Fixup, uint64_t &Value,
198-
MCContext *Ctx = nullptr) {
199-
unsigned_width(7, Value, std::string("immediate"), Fixup, Ctx);
99+
MCContext *Ctx) {
100+
ensureUnsignedWidth(7, Value, std::string("immediate"), Fixup, Ctx);
200101
Value = ((Value & 0x70) << 8) | (Value & 0x0f);
201102
}
202103

@@ -213,7 +114,7 @@ namespace ldi {
213114
/// 0000 KKKK 0000 KKKK
214115
/// Offset of 0 (so the result isn't left-shifted before application).
215116
static void fixup(unsigned Size, const MCFixup &Fixup, uint64_t &Value,
216-
MCContext *Ctx = nullptr) {
117+
MCContext *Ctx) {
217118
uint64_t upper = Value & 0xf0;
218119
uint64_t lower = Value & 0x0f;
219120

@@ -223,25 +124,25 @@ static void fixup(unsigned Size, const MCFixup &Fixup, uint64_t &Value,
223124
static void neg(uint64_t &Value) { Value *= -1; }
224125

225126
static void lo8(unsigned Size, const MCFixup &Fixup, uint64_t &Value,
226-
MCContext *Ctx = nullptr) {
127+
MCContext *Ctx) {
227128
Value &= 0xff;
228129
ldi::fixup(Size, Fixup, Value, Ctx);
229130
}
230131

231132
static void hi8(unsigned Size, const MCFixup &Fixup, uint64_t &Value,
232-
MCContext *Ctx = nullptr) {
133+
MCContext *Ctx) {
233134
Value = (Value & 0xff00) >> 8;
234135
ldi::fixup(Size, Fixup, Value, Ctx);
235136
}
236137

237138
static void hh8(unsigned Size, const MCFixup &Fixup, uint64_t &Value,
238-
MCContext *Ctx = nullptr) {
139+
MCContext *Ctx) {
239140
Value = (Value & 0xff0000) >> 16;
240141
ldi::fixup(Size, Fixup, Value, Ctx);
241142
}
242143

243144
static void ms8(unsigned Size, const MCFixup &Fixup, uint64_t &Value,
244-
MCContext *Ctx = nullptr) {
145+
MCContext *Ctx) {
245146
Value = (Value & 0xff000000) >> 24;
246147
ldi::fixup(Size, Fixup, Value, Ctx);
247148
}
@@ -263,13 +164,9 @@ void AVRAsmBackend::adjustFixupValue(const MCFixup &Fixup,
263164
default:
264165
llvm_unreachable("unhandled fixup");
265166
case AVR::fixup_7_pcrel:
266-
adjust::fixup_7_pcrel(Size, Fixup, Value, Ctx);
267-
break;
268167
case AVR::fixup_13_pcrel:
269-
adjust::fixup_13_pcrel(Size, Fixup, Value, Ctx);
270-
break;
271168
case AVR::fixup_call:
272-
adjust::fixup_call(Size, Fixup, Value, Ctx);
169+
// Handled by linker, see `shouldForceRelocation()`
273170
break;
274171
case AVR::fixup_ldi:
275172
adjust::ldi::fixup(Size, Fixup, Value, Ctx);
@@ -330,13 +227,15 @@ void AVRAsmBackend::adjustFixupValue(const MCFixup &Fixup,
330227
adjust::ldi::ms8(Size, Fixup, Value, Ctx);
331228
break;
332229
case AVR::fixup_16:
333-
adjust::unsigned_width(16, Value, std::string("port number"), Fixup, Ctx);
230+
adjust::ensureUnsignedWidth(16, Value, std::string("port number"), Fixup,
231+
Ctx);
334232

335233
Value &= 0xffff;
336234
break;
337235
case AVR::fixup_16_pm:
338236
Value >>= 1; // Flash addresses are always shifted.
339-
adjust::unsigned_width(16, Value, std::string("port number"), Fixup, Ctx);
237+
adjust::ensureUnsignedWidth(16, Value, std::string("port number"), Fixup,
238+
Ctx);
340239

341240
Value &= 0xffff;
342241
break;
@@ -517,8 +416,6 @@ bool AVRAsmBackend::shouldForceRelocation(const MCAssembler &Asm,
517416
return Fixup.getKind() >= FirstLiteralRelocationKind;
518417
case AVR::fixup_7_pcrel:
519418
case AVR::fixup_13_pcrel:
520-
// Always resolve relocations for PC-relative branches
521-
return false;
522419
case AVR::fixup_call:
523420
return true;
524421
}

llvm/test/CodeGen/AVR/branch-relaxation-long.ll

Lines changed: 38 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
1-
; RUN: llc < %s -march=avr -mattr=avr3 | FileCheck %s
2-
; RUN: llc < %s -march=avr -mattr=avr2 | FileCheck --check-prefix=AVR2 %s
1+
; RUN: llc < %s -march=avr -mcpu=avr25 -filetype=obj -o - | llvm-objdump --mcpu=avr25 -dr --no-show-raw-insn --no-leading-addr - | FileCheck --check-prefix=AVR25 %s
2+
; RUN: llc < %s -march=avr -mcpu=avr3 -filetype=obj -o - | llvm-objdump --mcpu=avr3 -dr --no-show-raw-insn --no-leading-addr - | FileCheck --check-prefix=AVR3 %s
33

4-
; CHECK-LABEL: relax_to_jmp:
5-
; CHECK: cpi r{{[0-9]+}}, 0
6-
; CHECK: brne [[BB1:.LBB[0-9]+_[0-9]+]]
7-
; CHECK: jmp [[BB2:.LBB[0-9]+_[0-9]+]]
8-
; CHECK: [[BB1]]:
9-
; CHECK: nop
10-
; CHECK: [[BB2]]:
4+
; AVR25: <relax_to_jmp>:
5+
; AVR25-NEXT: andi r24, 0x1
6+
; AVR25-NEXT: cpi r24, 0x0
7+
; AVR25-NEXT: brne .+0
8+
; AVR25-NEXT: R_AVR_7_PCREL .text+0x8
9+
; AVR25-NEXT: rjmp .+0
10+
; AVR25-NEXT: R_AVR_13_PCREL .text+0x100c
11+
; AVR25: ldi r24, 0x3
12+
; AVR25-NEXT: ret
1113

12-
;; A `RJMP` is generated instead of expected `JMP` for AVR2,
13-
;; and it is up to the linker to report 'out of range' or
14-
;; 'exceed flash maximum size'.
15-
; AVR2-LABEL: relax_to_jmp:
16-
; AVR2: cpi r{{[0-9]+}}, 0
17-
; AVR2: brne [[BB1:.LBB[0-9]+_[0-9]+]]
18-
; AVR2: rjmp [[BB2:.LBB[0-9]+_[0-9]+]]
19-
; AVR2: [[BB1]]:
20-
; AVR2: nop
21-
; AVR2: [[BB2]]:
14+
; AVR3: <relax_to_jmp>:
15+
; AVR3-NEXT: andi r24, 0x1
16+
; AVR3-NEXT: cpi r24, 0x0
17+
; AVR3-NEXT: brne .+0
18+
; AVR3-NEXT: R_AVR_7_PCREL .text+0xa
19+
; AVR3-NEXT: jmp 0x0
20+
; AVR3-NEXT: R_AVR_CALL .text+0x100e
21+
; AVR3: ldi r24, 0x3
22+
; AVR3-NEXT: ret
2223

2324
define i8 @relax_to_jmp(i1 %a) {
2425
entry-block:
@@ -2081,24 +2082,25 @@ finished:
20812082
ret i8 3
20822083
}
20832084

2084-
; CHECK-LABEL: relax_to_jmp_backwards:
2085-
; CHECK: [[BB1:.LBB[0-9]+_[0-9]+]]
2086-
; CHECK: nop
2087-
; CHECK: cpi r{{[0-9]+}}, 0
2088-
; CHECK: breq [[BB2:.LBB[0-9]+_[0-9]+]]
2089-
; CHECK: jmp [[BB1]]
2090-
; CHECK: [[BB2]]:
2085+
; AVR25: <relax_to_jmp_backwards>:
2086+
; AVR25-NEXT: andi r24, 0x1
2087+
; AVR25: cpi r24, 0x0
2088+
; AVR25-NEXT: breq .+0
2089+
; AVR25-NEXT: R_AVR_7_PCREL .text+0x201c
2090+
; AVR25-NEXT: rjmp .+0
2091+
; AVR25-NEXT: R_AVR_13_PCREL .text+0x1012
2092+
; AVR25: ldi r24, 0x3
2093+
; AVR25-NEXT: ret
20912094

2092-
;; A `RJMP` is generated instead of expected `JMP` for AVR2,
2093-
;; and it is up to the linker to report 'out of range' or
2094-
;; 'exceed flash maximum size'.
2095-
; AVR2-LABEL: relax_to_jmp_backwards:
2096-
; AVR2: [[BB1:.LBB[0-9]+_[0-9]+]]
2097-
; AVR2: nop
2098-
; AVR2: cpi r{{[0-9]+}}, 0
2099-
; AVR2: breq [[BB2:.LBB[0-9]+_[0-9]+]]
2100-
; AVR2: rjmp [[BB1]]
2101-
; AVR2: [[BB2]]:
2095+
; AVR3: <relax_to_jmp_backwards>:
2096+
; AVR3-NEXT: andi r24, 0x1
2097+
; AVR3: cpi r24, 0x0
2098+
; AVR3-NEXT: breq .+0
2099+
; AVR3-NEXT: R_AVR_7_PCREL .text+0x2020
2100+
; AVR3-NEXT: jmp 0x0
2101+
; AVR3-NEXT: R_AVR_CALL .text+0x1014
2102+
; AVR3: ldi r24, 0x3
2103+
; AVR3-NEXT: ret
21022104

21032105
define i8 @relax_to_jmp_backwards(i1 %a) {
21042106
entry-block:

llvm/test/CodeGen/AVR/jmp.ll

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@ bb2:
1515

1616
declare i8 @bar(i8);
1717

18-
; CHECK: rcall .-2
18+
; CHECK: rcall .+0
1919
; CHECK-NEXT: 00000000: R_AVR_13_PCREL bar
2020
; CHECK-NEXT: cpi r24, 0x7b
21-
; CHECK-NEXT: brne .+4
21+
; CHECK-NEXT: brne .+0
22+
; CHECK-NEXT: 00000004: R_AVR_7_PCREL .text+0xa
2223
; CHECK-NEXT: ldi r24, 0x64
2324
; CHECK-NEXT: ret
2425
; CHECK-NEXT: ldi r24, 0xc8

0 commit comments

Comments
 (0)