Skip to content

Commit c51ab48

Browse files
authored
[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.
1 parent c41472d commit c51ab48

File tree

6 files changed

+203
-2
lines changed

6 files changed

+203
-2
lines changed

llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@
1717
#include "llvm/MC/MCAssembler.h"
1818
#include "llvm/MC/MCContext.h"
1919
#include "llvm/MC/MCELFObjectWriter.h"
20+
#include "llvm/MC/MCExpr.h"
21+
#include "llvm/MC/MCSection.h"
2022
#include "llvm/MC/MCValue.h"
2123
#include "llvm/Support/EndianStream.h"
2224
#include "llvm/Support/LEB128.h"
25+
#include "llvm/Support/MathExtras.h"
2326

2427
#define DEBUG_TYPE "loongarch-asmbackend"
2528

@@ -176,6 +179,70 @@ void LoongArchAsmBackend::applyFixup(const MCAssembler &Asm,
176179
}
177180
}
178181

182+
// Linker relaxation may change code size. We have to insert Nops
183+
// for .align directive when linker relaxation enabled. So then Linker
184+
// could satisfy alignment by removing Nops.
185+
// The function returns the total Nops Size we need to insert.
186+
bool LoongArchAsmBackend::shouldInsertExtraNopBytesForCodeAlign(
187+
const MCAlignFragment &AF, unsigned &Size) {
188+
// Calculate Nops Size only when linker relaxation enabled.
189+
if (!AF.getSubtargetInfo()->hasFeature(LoongArch::FeatureRelax))
190+
return false;
191+
192+
// Ignore alignment if MaxBytesToEmit is less than the minimum Nop size.
193+
const unsigned MinNopLen = 4;
194+
if (AF.getMaxBytesToEmit() < MinNopLen)
195+
return false;
196+
Size = AF.getAlignment().value() - MinNopLen;
197+
return AF.getAlignment() > MinNopLen;
198+
}
199+
200+
// We need to insert R_LARCH_ALIGN relocation type to indicate the
201+
// position of Nops and the total bytes of the Nops have been inserted
202+
// when linker relaxation enabled.
203+
// The function inserts fixup_loongarch_align fixup which eventually will
204+
// transfer to R_LARCH_ALIGN relocation type.
205+
// The improved R_LARCH_ALIGN requires symbol index. The lowest 8 bits of
206+
// addend represent alignment and the other bits of addend represent the
207+
// maximum number of bytes to emit. The maximum number of bytes is zero
208+
// means ignore the emit limit.
209+
bool LoongArchAsmBackend::shouldInsertFixupForCodeAlign(
210+
MCAssembler &Asm, const MCAsmLayout &Layout, MCAlignFragment &AF) {
211+
// Insert the fixup only when linker relaxation enabled.
212+
if (!AF.getSubtargetInfo()->hasFeature(LoongArch::FeatureRelax))
213+
return false;
214+
215+
// Calculate total Nops we need to insert. If there are none to insert
216+
// then simply return.
217+
unsigned Count;
218+
if (!shouldInsertExtraNopBytesForCodeAlign(AF, Count))
219+
return false;
220+
221+
MCSection *Sec = AF.getParent();
222+
MCContext &Ctx = Asm.getContext();
223+
const MCExpr *Dummy = MCConstantExpr::create(0, Ctx);
224+
// Create fixup_loongarch_align fixup.
225+
MCFixup Fixup =
226+
MCFixup::create(0, Dummy, MCFixupKind(LoongArch::fixup_loongarch_align));
227+
const MCSymbolRefExpr *MCSym = getSecToAlignSym()[Sec];
228+
if (MCSym == nullptr) {
229+
// Create a symbol and make the value of symbol is zero.
230+
MCSymbol *Sym = Ctx.createNamedTempSymbol("la-relax-align");
231+
Sym->setFragment(&*Sec->getBeginSymbol()->getFragment());
232+
Asm.registerSymbol(*Sym);
233+
MCSym = MCSymbolRefExpr::create(Sym, Ctx);
234+
getSecToAlignSym()[Sec] = MCSym;
235+
}
236+
237+
uint64_t FixedValue = 0;
238+
unsigned Lo = Log2_64(Count) + 1;
239+
unsigned Hi = AF.getMaxBytesToEmit() >= Count ? 0 : AF.getMaxBytesToEmit();
240+
MCValue Value = MCValue::get(MCSym, nullptr, Hi << 8 | Lo);
241+
Asm.getWriter().recordRelocation(Asm, Layout, &AF, Fixup, Value, FixedValue);
242+
243+
return true;
244+
}
245+
179246
bool LoongArchAsmBackend::shouldForceRelocation(const MCAssembler &Asm,
180247
const MCFixup &Fixup,
181248
const MCValue &Target,

llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
#include "MCTargetDesc/LoongArchFixupKinds.h"
1818
#include "MCTargetDesc/LoongArchMCTargetDesc.h"
1919
#include "llvm/MC/MCAsmBackend.h"
20+
#include "llvm/MC/MCExpr.h"
2021
#include "llvm/MC/MCFixupKindInfo.h"
22+
#include "llvm/MC/MCSection.h"
2123
#include "llvm/MC/MCSubtargetInfo.h"
2224

2325
namespace llvm {
@@ -27,6 +29,7 @@ class LoongArchAsmBackend : public MCAsmBackend {
2729
uint8_t OSABI;
2830
bool Is64Bit;
2931
const MCTargetOptions &TargetOptions;
32+
DenseMap<MCSection *, const MCSymbolRefExpr *> SecToAlignSym;
3033

3134
public:
3235
LoongArchAsmBackend(const MCSubtargetInfo &STI, uint8_t OSABI, bool Is64Bit,
@@ -45,6 +48,15 @@ class LoongArchAsmBackend : public MCAsmBackend {
4548
uint64_t Value, bool IsResolved,
4649
const MCSubtargetInfo *STI) const override;
4750

51+
// Return Size with extra Nop Bytes for alignment directive in code section.
52+
bool shouldInsertExtraNopBytesForCodeAlign(const MCAlignFragment &AF,
53+
unsigned &Size) override;
54+
55+
// Insert target specific fixup type for alignment directive in code section.
56+
bool shouldInsertFixupForCodeAlign(MCAssembler &Asm,
57+
const MCAsmLayout &Layout,
58+
MCAlignFragment &AF) override;
59+
4860
bool shouldForceRelocation(const MCAssembler &Asm, const MCFixup &Fixup,
4961
const MCValue &Target,
5062
const MCSubtargetInfo *STI) override;
@@ -80,6 +92,9 @@ class LoongArchAsmBackend : public MCAsmBackend {
8092
std::unique_ptr<MCObjectTargetWriter>
8193
createObjectTargetWriter() const override;
8294
const MCTargetOptions &getTargetOptions() const { return TargetOptions; }
95+
DenseMap<MCSection *, const MCSymbolRefExpr *> &getSecToAlignSym() {
96+
return SecToAlignSym;
97+
}
8398
};
8499
} // end namespace llvm
85100

llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchFixupKinds.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ enum Fixups {
109109
fixup_loongarch_tls_gd_hi20,
110110
// Generate an R_LARCH_RELAX which indicates the linker may relax here.
111111
fixup_loongarch_relax = FirstLiteralRelocationKind + ELF::R_LARCH_RELAX,
112+
// Generate an R_LARCH_ALIGN which indicates the linker may fixup align here.
113+
fixup_loongarch_align = FirstLiteralRelocationKind + ELF::R_LARCH_ALIGN,
112114
// 36-bit fixup corresponding to %call36(foo) for a pair instructions:
113115
// pcaddu18i+jirl.
114116
fixup_loongarch_call36 = FirstLiteralRelocationKind + ELF::R_LARCH_CALL36,
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
## A label difference separated by an alignment directive, when the
2+
## referenced symbols are in a non-executable section with instructions,
3+
## should generate ADD/SUB relocations.
4+
## https://github.com/llvm/llvm-project/pull/76552
5+
6+
# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+relax %s \
7+
# RUN: | llvm-readobj -r - | FileCheck --check-prefixes=CHECK,RELAX %s
8+
# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=-relax %s \
9+
# RUN: | llvm-readobj -r - | FileCheck %s
10+
11+
.section ".dummy", "a"
12+
.L1:
13+
la.pcrel $t0, sym
14+
.p2align 3
15+
.L2:
16+
.dword .L2 - .L1
17+
18+
# CHECK: Relocations [
19+
# CHECK-NEXT: Section ({{.*}}) .rela.dummy {
20+
# CHECK-NEXT: 0x0 R_LARCH_PCALA_HI20 sym 0x0
21+
# RELAX-NEXT: 0x0 R_LARCH_RELAX - 0x0
22+
# CHECK-NEXT: 0x4 R_LARCH_PCALA_LO12 sym 0x0
23+
# RELAX-NEXT: 0x4 R_LARCH_RELAX - 0x0
24+
# RELAX-NEXT: 0x8 R_LARCH_ADD64 .L2 0x0
25+
# RELAX-NEXT: 0x8 R_LARCH_SUB64 .L1 0x0
26+
# CHECK-NEXT: }
27+
# CHECK-NEXT: ]

llvm/test/MC/LoongArch/Relocations/relax-addsub.s

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,23 @@
2828

2929
# RELAX: Relocations [
3030
# RELAX-NEXT: Section ({{.*}}) .rela.text {
31+
# RELAX-NEXT: 0x4 R_LARCH_ALIGN {{.*}} 0x4
3132
# RELAX-NEXT: 0x10 R_LARCH_PCALA_HI20 .L1 0x0
3233
# RELAX-NEXT: 0x10 R_LARCH_RELAX - 0x0
3334
# RELAX-NEXT: 0x14 R_LARCH_PCALA_LO12 .L1 0x0
3435
# RELAX-NEXT: 0x14 R_LARCH_RELAX - 0x0
3536
# RELAX-NEXT: }
3637
# RELAX-NEXT: Section ({{.*}}) .rela.data {
38+
# RELAX-NEXT: 0x10 R_LARCH_ADD8 .L3 0x0
39+
# RELAX-NEXT: 0x10 R_LARCH_SUB8 .L2 0x0
40+
# RELAX-NEXT: 0x11 R_LARCH_ADD16 .L3 0x0
41+
# RELAX-NEXT: 0x11 R_LARCH_SUB16 .L2 0x0
42+
# RELAX-NEXT: 0x13 R_LARCH_ADD32 .L3 0x0
43+
# RELAX-NEXT: 0x13 R_LARCH_SUB32 .L2 0x0
44+
# RELAX-NEXT: 0x17 R_LARCH_ADD64 .L3 0x0
45+
# RELAX-NEXT: 0x17 R_LARCH_SUB64 .L2 0x0
46+
# RELAX-NEXT: 0x1F R_LARCH_ADD_ULEB128 .L3 0x0
47+
# RELAX-NEXT: 0x1F R_LARCH_SUB_ULEB128 .L2 0x0
3748
# RELAX-NEXT: 0x20 R_LARCH_ADD8 .L4 0x0
3849
# RELAX-NEXT: 0x20 R_LARCH_SUB8 .L3 0x0
3950
# RELAX-NEXT: 0x21 R_LARCH_ADD16 .L4 0x0
@@ -57,7 +68,7 @@
5768

5869
# RELAX: Hex dump of section '.data':
5970
# RELAX-NEXT: 0x00000000 04040004 00000004 00000000 00000004
60-
# RELAX-NEXT: 0x00000010 0c0c000c 0000000c 00000000 0000000c
71+
# RELAX-NEXT: 0x00000010 00000000 00000000 00000000 00000000
6172
# RELAX-NEXT: 0x00000020 00000000 00000000 00000000 00000000
6273
# RELAX-NEXT: 0x00000030 00000000 00000000 00000000 000000
6374

@@ -78,7 +89,7 @@
7889
.word .L2 - .L1
7990
.dword .L2 - .L1
8091
.uleb128 .L2 - .L1
81-
## TODO Handle alignment directive.
92+
## With relaxation, emit relocs because the .align makes the diff variable.
8293
.byte .L3 - .L2
8394
.short .L3 - .L2
8495
.word .L3 - .L2
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
## The file testing Nop insertion with R_LARCH_ALIGN for relaxation.
2+
3+
# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=-relax %s -o %t
4+
# RUN: llvm-objdump -d %t | FileCheck %s --check-prefix=INSTR
5+
# RUN: llvm-readobj -r %t | FileCheck %s --check-prefix=RELOC
6+
# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+relax %s -o %t.r
7+
# RUN: llvm-objdump -d %t.r | FileCheck %s --check-prefixes=INSTR,RELAX-INSTR
8+
# RUN: llvm-readobj -r %t.r | FileCheck %s --check-prefixes=RELOC,RELAX-RELOC
9+
10+
.text
11+
break 0
12+
# INSTR: break 0
13+
14+
## Not emit R_LARCH_ALIGN if alignment directive is less than or equal to
15+
## minimum code alignment(a.k.a 4).
16+
.p2align 2
17+
.p2align 1
18+
.p2align 0
19+
20+
## Not emit instructions if max emit bytes less than min nop size.
21+
.p2align 4, , 2
22+
23+
## Not emit R_LARCH_ALIGN if alignment directive with specific padding value.
24+
## The behavior is the same as GNU assembler.
25+
break 1
26+
.p2align 4, 1
27+
# INSTR-NEXT: break 1
28+
# INSTR-COUNT-2: 01 01 01 01
29+
30+
break 2
31+
.p2align 4, 1, 12
32+
# INSTR-NEXT: break 2
33+
# INSTR-COUNT-3: 01 01 01 01
34+
35+
break 3
36+
.p2align 4
37+
# INSTR-NEXT: break 3
38+
# INSTR-COUNT-3: nop
39+
40+
break 4
41+
.p2align 5
42+
.p2align 4
43+
# INSTR-NEXT: break 4
44+
# INSTR-COUNT-3: nop
45+
# RELAX-INSTR-COUNT-7: nop
46+
47+
break 5
48+
.p2align 4, , 11
49+
# INSTR-NEXT: break 5
50+
# RELAX-INSTR-COUNT-3: nop
51+
52+
break 6
53+
## Not emit the third parameter.
54+
.p2align 4, , 12
55+
# INSTR-NEXT: break 6
56+
# INSTR-NEXT: nop
57+
# INSTR-NEXT: nop
58+
# RELAX-INSTR-NEXT: nop
59+
60+
ret
61+
# INSNR-NEXT: ret
62+
63+
## Test the symbol index is different from .text.
64+
.section .text2, "ax"
65+
.p2align 4
66+
break 7
67+
68+
# RELOC: Relocations [
69+
# RELAX-RELOC-NEXT: Section ({{.*}}) .rela.text {
70+
# RELAX-RELOC-NEXT: 0x24 R_LARCH_ALIGN .Lla-relax-align0 0x4
71+
# RELAX-RELOC-NEXT: 0x34 R_LARCH_ALIGN .Lla-relax-align0 0x5
72+
# RELAX-RELOC-NEXT: 0x50 R_LARCH_ALIGN .Lla-relax-align0 0x4
73+
# RELAX-RELOC-NEXT: 0x60 R_LARCH_ALIGN .Lla-relax-align0 0xB04
74+
# RELAX-RELOC-NEXT: 0x70 R_LARCH_ALIGN .Lla-relax-align0 0x4
75+
# RELAX-RELOC-NEXT: }
76+
# RELAX-RELOC-NEXT: Section ({{.*}}) .rela.text2 {
77+
# RELAX-RELOC-NEXT: 0x0 R_LARCH_ALIGN .Lla-relax-align1 0x4
78+
# RELAX-RELOC-NEXT: }
79+
# RELOC-NEXT: ]

0 commit comments

Comments
 (0)