1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
|
From 87f6adc2acf635a0a4c294217fb54c55eee3a06c Mon Sep 17 00:00:00 2001
From: Jinyang He <hejinyang@loongson.cn>
Date: Wed, 24 Jan 2024 09:17:49 +0800
Subject: [PATCH 08/14] [LoongArch] Insert nops and emit align reloc when
handle alignment directive (#72962)
Refer to RISCV, we will fix up the alignment if linker relaxation
changes code size and breaks alignment. Insert enough Nops and emit
R_LARCH_ALIGN relocation type so that linker could satisfy the alignment
by removing Nops.
It does so only in sections with the SHF_EXECINSTR flag.
In LoongArch psABI v2.30, R_LARCH_ALIGN requires symbol index. The
lowest 8 bits of addend represent alignment and the other bits of addend
represent the maximum number of bytes to emit.
(cherry picked from commit c51ab483e6c2d991a01179584705b83fbea1940d)
Change-Id: Iba30702c9dda378acfae0b1f1134926fa838a368
---
llvm/lib/MC/MCExpr.cpp | 2 +-
.../MCTargetDesc/LoongArchAsmBackend.cpp | 67 ++++++++++++++++
.../MCTargetDesc/LoongArchAsmBackend.h | 15 ++++
.../MCTargetDesc/LoongArchFixupKinds.h | 4 +-
.../Relocations/align-non-executable.s | 27 +++++++
.../MC/LoongArch/Relocations/relax-addsub.s | 15 +++-
.../MC/LoongArch/Relocations/relax-align.s | 79 +++++++++++++++++++
7 files changed, 205 insertions(+), 4 deletions(-)
create mode 100644 llvm/test/MC/LoongArch/Relocations/align-non-executable.s
create mode 100644 llvm/test/MC/LoongArch/Relocations/relax-align.s
diff --git a/llvm/lib/MC/MCExpr.cpp b/llvm/lib/MC/MCExpr.cpp
index a561fed11179..79808a58d81c 100644
--- a/llvm/lib/MC/MCExpr.cpp
+++ b/llvm/lib/MC/MCExpr.cpp
@@ -711,7 +711,7 @@ static void AttemptToFoldSymbolOffsetDifference(
if (DF) {
Displacement += DF->getContents().size();
} else if (auto *AF = dyn_cast<MCAlignFragment>(FI);
- AF && Layout &&
+ AF && Layout && AF->hasEmitNops() &&
!Asm->getBackend().shouldInsertExtraNopBytesForCodeAlign(
*AF, Count)) {
Displacement += Asm->computeFragmentSize(*Layout, *AF);
diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp
index 8d82327b2e2b..8c482356402f 100644
--- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp
+++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp
@@ -17,10 +17,13 @@
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCELFObjectWriter.h"
+#include "llvm/MC/MCExpr.h"
+#include "llvm/MC/MCSection.h"
#include "llvm/MC/MCValue.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/EndianStream.h"
#include "llvm/Support/LEB128.h"
+#include "llvm/Support/MathExtras.h"
#define DEBUG_TYPE "loongarch-asmbackend"
@@ -177,6 +180,70 @@ void LoongArchAsmBackend::applyFixup(const MCAssembler &Asm,
}
}
+// Linker relaxation may change code size. We have to insert Nops
+// for .align directive when linker relaxation enabled. So then Linker
+// could satisfy alignment by removing Nops.
+// The function returns the total Nops Size we need to insert.
+bool LoongArchAsmBackend::shouldInsertExtraNopBytesForCodeAlign(
+ const MCAlignFragment &AF, unsigned &Size) {
+ // Calculate Nops Size only when linker relaxation enabled.
+ if (!AF.getSubtargetInfo()->hasFeature(LoongArch::FeatureRelax))
+ return false;
+
+ // Ignore alignment if MaxBytesToEmit is less than the minimum Nop size.
+ const unsigned MinNopLen = 4;
+ if (AF.getMaxBytesToEmit() < MinNopLen)
+ return false;
+ Size = AF.getAlignment().value() - MinNopLen;
+ return AF.getAlignment() > MinNopLen;
+}
+
+// We need to insert R_LARCH_ALIGN relocation type to indicate the
+// position of Nops and the total bytes of the Nops have been inserted
+// when linker relaxation enabled.
+// The function inserts fixup_loongarch_align fixup which eventually will
+// transfer to R_LARCH_ALIGN relocation type.
+// The improved R_LARCH_ALIGN requires symbol index. The lowest 8 bits of
+// addend represent alignment and the other bits of addend represent the
+// maximum number of bytes to emit. The maximum number of bytes is zero
+// means ignore the emit limit.
+bool LoongArchAsmBackend::shouldInsertFixupForCodeAlign(
+ MCAssembler &Asm, const MCAsmLayout &Layout, MCAlignFragment &AF) {
+ // Insert the fixup only when linker relaxation enabled.
+ if (!AF.getSubtargetInfo()->hasFeature(LoongArch::FeatureRelax))
+ return false;
+
+ // Calculate total Nops we need to insert. If there are none to insert
+ // then simply return.
+ unsigned Count;
+ if (!shouldInsertExtraNopBytesForCodeAlign(AF, Count))
+ return false;
+
+ MCSection *Sec = AF.getParent();
+ MCContext &Ctx = Asm.getContext();
+ const MCExpr *Dummy = MCConstantExpr::create(0, Ctx);
+ // Create fixup_loongarch_align fixup.
+ MCFixup Fixup =
+ MCFixup::create(0, Dummy, MCFixupKind(LoongArch::fixup_loongarch_align));
+ const MCSymbolRefExpr *MCSym = getSecToAlignSym()[Sec];
+ if (MCSym == nullptr) {
+ // Create a symbol and make the value of symbol is zero.
+ MCSymbol *Sym = Ctx.createNamedTempSymbol("la-relax-align");
+ Sym->setFragment(&*Sec->getBeginSymbol()->getFragment());
+ Asm.registerSymbol(*Sym);
+ MCSym = MCSymbolRefExpr::create(Sym, Ctx);
+ getSecToAlignSym()[Sec] = MCSym;
+ }
+
+ uint64_t FixedValue = 0;
+ unsigned Lo = Log2_64(Count) + 1;
+ unsigned Hi = AF.getMaxBytesToEmit() >= Count ? 0 : AF.getMaxBytesToEmit();
+ MCValue Value = MCValue::get(MCSym, nullptr, Hi << 8 | Lo);
+ Asm.getWriter().recordRelocation(Asm, Layout, &AF, Fixup, Value, FixedValue);
+
+ return true;
+}
+
bool LoongArchAsmBackend::shouldForceRelocation(const MCAssembler &Asm,
const MCFixup &Fixup,
const MCValue &Target) {
diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h
index 657f5ca5e731..71bbd003888a 100644
--- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h
+++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h
@@ -17,7 +17,9 @@
#include "MCTargetDesc/LoongArchFixupKinds.h"
#include "MCTargetDesc/LoongArchMCTargetDesc.h"
#include "llvm/MC/MCAsmBackend.h"
+#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCFixupKindInfo.h"
+#include "llvm/MC/MCSection.h"
#include "llvm/MC/MCSubtargetInfo.h"
namespace llvm {
@@ -27,6 +29,7 @@ class LoongArchAsmBackend : public MCAsmBackend {
uint8_t OSABI;
bool Is64Bit;
const MCTargetOptions &TargetOptions;
+ DenseMap<MCSection *, const MCSymbolRefExpr *> SecToAlignSym;
public:
LoongArchAsmBackend(const MCSubtargetInfo &STI, uint8_t OSABI, bool Is64Bit,
@@ -45,6 +48,15 @@ public:
uint64_t Value, bool IsResolved,
const MCSubtargetInfo *STI) const override;
+ // Return Size with extra Nop Bytes for alignment directive in code section.
+ bool shouldInsertExtraNopBytesForCodeAlign(const MCAlignFragment &AF,
+ unsigned &Size) override;
+
+ // Insert target specific fixup type for alignment directive in code section.
+ bool shouldInsertFixupForCodeAlign(MCAssembler &Asm,
+ const MCAsmLayout &Layout,
+ MCAlignFragment &AF) override;
+
bool shouldForceRelocation(const MCAssembler &Asm, const MCFixup &Fixup,
const MCValue &Target) override;
@@ -79,6 +91,9 @@ public:
std::unique_ptr<MCObjectTargetWriter>
createObjectTargetWriter() const override;
const MCTargetOptions &getTargetOptions() const { return TargetOptions; }
+ DenseMap<MCSection *, const MCSymbolRefExpr *> &getSecToAlignSym() {
+ return SecToAlignSym;
+ }
};
} // end namespace llvm
diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchFixupKinds.h b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchFixupKinds.h
index 178fa6e5262b..78414408f21f 100644
--- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchFixupKinds.h
+++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchFixupKinds.h
@@ -108,7 +108,9 @@ enum Fixups {
// 20-bit fixup corresponding to %gd_hi20(foo) for instruction lu12i.w.
fixup_loongarch_tls_gd_hi20,
// Generate an R_LARCH_RELAX which indicates the linker may relax here.
- fixup_loongarch_relax = FirstLiteralRelocationKind + ELF::R_LARCH_RELAX
+ fixup_loongarch_relax = FirstLiteralRelocationKind + ELF::R_LARCH_RELAX,
+ // Generate an R_LARCH_ALIGN which indicates the linker may fixup align here.
+ fixup_loongarch_align = FirstLiteralRelocationKind + ELF::R_LARCH_ALIGN,
};
} // end namespace LoongArch
} // end namespace llvm
diff --git a/llvm/test/MC/LoongArch/Relocations/align-non-executable.s b/llvm/test/MC/LoongArch/Relocations/align-non-executable.s
new file mode 100644
index 000000000000..47834acd9521
--- /dev/null
+++ b/llvm/test/MC/LoongArch/Relocations/align-non-executable.s
@@ -0,0 +1,27 @@
+## A label difference separated by an alignment directive, when the
+## referenced symbols are in a non-executable section with instructions,
+## should generate ADD/SUB relocations.
+## https://github.com/llvm/llvm-project/pull/76552
+
+# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+relax %s \
+# RUN: | llvm-readobj -r - | FileCheck --check-prefixes=CHECK,RELAX %s
+# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=-relax %s \
+# RUN: | llvm-readobj -r - | FileCheck %s
+
+.section ".dummy", "a"
+.L1:
+ la.pcrel $t0, sym
+.p2align 3
+.L2:
+.dword .L2 - .L1
+
+# CHECK: Relocations [
+# CHECK-NEXT: Section ({{.*}}) .rela.dummy {
+# CHECK-NEXT: 0x0 R_LARCH_PCALA_HI20 sym 0x0
+# RELAX-NEXT: 0x0 R_LARCH_RELAX - 0x0
+# CHECK-NEXT: 0x4 R_LARCH_PCALA_LO12 sym 0x0
+# RELAX-NEXT: 0x4 R_LARCH_RELAX - 0x0
+# RELAX-NEXT: 0x8 R_LARCH_ADD64 .L2 0x0
+# RELAX-NEXT: 0x8 R_LARCH_SUB64 .L1 0x0
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
diff --git a/llvm/test/MC/LoongArch/Relocations/relax-addsub.s b/llvm/test/MC/LoongArch/Relocations/relax-addsub.s
index cd01332afd0b..18e0ede5e293 100644
--- a/llvm/test/MC/LoongArch/Relocations/relax-addsub.s
+++ b/llvm/test/MC/LoongArch/Relocations/relax-addsub.s
@@ -28,12 +28,23 @@
# RELAX: Relocations [
# RELAX-NEXT: Section ({{.*}}) .rela.text {
+# RELAX-NEXT: 0x4 R_LARCH_ALIGN {{.*}} 0x4
# RELAX-NEXT: 0x10 R_LARCH_PCALA_HI20 .L1 0x0
# RELAX-NEXT: 0x10 R_LARCH_RELAX - 0x0
# RELAX-NEXT: 0x14 R_LARCH_PCALA_LO12 .L1 0x0
# RELAX-NEXT: 0x14 R_LARCH_RELAX - 0x0
# RELAX-NEXT: }
# RELAX-NEXT: Section ({{.*}}) .rela.data {
+# RELAX-NEXT: 0x10 R_LARCH_ADD8 .L3 0x0
+# RELAX-NEXT: 0x10 R_LARCH_SUB8 .L2 0x0
+# RELAX-NEXT: 0x11 R_LARCH_ADD16 .L3 0x0
+# RELAX-NEXT: 0x11 R_LARCH_SUB16 .L2 0x0
+# RELAX-NEXT: 0x13 R_LARCH_ADD32 .L3 0x0
+# RELAX-NEXT: 0x13 R_LARCH_SUB32 .L2 0x0
+# RELAX-NEXT: 0x17 R_LARCH_ADD64 .L3 0x0
+# RELAX-NEXT: 0x17 R_LARCH_SUB64 .L2 0x0
+# RELAX-NEXT: 0x1F R_LARCH_ADD_ULEB128 .L3 0x0
+# RELAX-NEXT: 0x1F R_LARCH_SUB_ULEB128 .L2 0x0
# RELAX-NEXT: 0x20 R_LARCH_ADD8 .L4 0x0
# RELAX-NEXT: 0x20 R_LARCH_SUB8 .L3 0x0
# RELAX-NEXT: 0x21 R_LARCH_ADD16 .L4 0x0
@@ -57,7 +68,7 @@
# RELAX: Hex dump of section '.data':
# RELAX-NEXT: 0x00000000 04040004 00000004 00000000 00000004
-# RELAX-NEXT: 0x00000010 0c0c000c 0000000c 00000000 0000000c
+# RELAX-NEXT: 0x00000010 00000000 00000000 00000000 00000000
# RELAX-NEXT: 0x00000020 00000000 00000000 00000000 00000000
# RELAX-NEXT: 0x00000030 00000000 00000000 00000000 000000
@@ -78,7 +89,7 @@
.word .L2 - .L1
.dword .L2 - .L1
.uleb128 .L2 - .L1
-## TODO Handle alignment directive.
+## With relaxation, emit relocs because the .align makes the diff variable.
.byte .L3 - .L2
.short .L3 - .L2
.word .L3 - .L2
diff --git a/llvm/test/MC/LoongArch/Relocations/relax-align.s b/llvm/test/MC/LoongArch/Relocations/relax-align.s
new file mode 100644
index 000000000000..294fd9fb916c
--- /dev/null
+++ b/llvm/test/MC/LoongArch/Relocations/relax-align.s
@@ -0,0 +1,79 @@
+## The file testing Nop insertion with R_LARCH_ALIGN for relaxation.
+
+# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=-relax %s -o %t
+# RUN: llvm-objdump -d %t | FileCheck %s --check-prefix=INSTR
+# RUN: llvm-readobj -r %t | FileCheck %s --check-prefix=RELOC
+# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+relax %s -o %t.r
+# RUN: llvm-objdump -d %t.r | FileCheck %s --check-prefixes=INSTR,RELAX-INSTR
+# RUN: llvm-readobj -r %t.r | FileCheck %s --check-prefixes=RELOC,RELAX-RELOC
+
+.text
+break 0
+# INSTR: break 0
+
+## Not emit R_LARCH_ALIGN if alignment directive is less than or equal to
+## minimum code alignment(a.k.a 4).
+.p2align 2
+.p2align 1
+.p2align 0
+
+## Not emit instructions if max emit bytes less than min nop size.
+.p2align 4, , 2
+
+## Not emit R_LARCH_ALIGN if alignment directive with specific padding value.
+## The behavior is the same as GNU assembler.
+break 1
+.p2align 4, 1
+# INSTR-NEXT: break 1
+# INSTR-COUNT-2: 01 01 01 01
+
+break 2
+.p2align 4, 1, 12
+# INSTR-NEXT: break 2
+# INSTR-COUNT-3: 01 01 01 01
+
+break 3
+.p2align 4
+# INSTR-NEXT: break 3
+# INSTR-COUNT-3: nop
+
+break 4
+.p2align 5
+.p2align 4
+# INSTR-NEXT: break 4
+# INSTR-COUNT-3: nop
+# RELAX-INSTR-COUNT-7: nop
+
+break 5
+.p2align 4, , 11
+# INSTR-NEXT: break 5
+# RELAX-INSTR-COUNT-3: nop
+
+break 6
+## Not emit the third parameter.
+.p2align 4, , 12
+# INSTR-NEXT: break 6
+# INSTR-NEXT: nop
+# INSTR-NEXT: nop
+# RELAX-INSTR-NEXT: nop
+
+ret
+# INSNR-NEXT: ret
+
+## Test the symbol index is different from .text.
+.section .text2, "ax"
+.p2align 4
+break 7
+
+# RELOC: Relocations [
+# RELAX-RELOC-NEXT: Section ({{.*}}) .rela.text {
+# RELAX-RELOC-NEXT: 0x24 R_LARCH_ALIGN .Lla-relax-align0 0x4
+# RELAX-RELOC-NEXT: 0x34 R_LARCH_ALIGN .Lla-relax-align0 0x5
+# RELAX-RELOC-NEXT: 0x50 R_LARCH_ALIGN .Lla-relax-align0 0x4
+# RELAX-RELOC-NEXT: 0x60 R_LARCH_ALIGN .Lla-relax-align0 0xB04
+# RELAX-RELOC-NEXT: 0x70 R_LARCH_ALIGN .Lla-relax-align0 0x4
+# RELAX-RELOC-NEXT: }
+# RELAX-RELOC-NEXT: Section ({{.*}}) .rela.text2 {
+# RELAX-RELOC-NEXT: 0x0 R_LARCH_ALIGN .Lla-relax-align1 0x4
+# RELAX-RELOC-NEXT: }
+# RELOC-NEXT: ]
--
2.20.1
|