Skip to content

Commit 1c327ec

Browse files
committed
[RISCV] Track Linker Relaxable through Assembly Relaxation (llvm#152602)
Span-dependent instructions on RISC-V interact in a complex manner with linker relaxation. The span-dependent assembler algorithm implemented in LLVM has to start with the smallest version of an instruction and then only make it larger, so we compress instructions before emitting them to the streamer. When the instruction is streamed, the information that the instruction (or rather, the fixup on the instruction) is linker relaxable must be accurate, even though the assembler relaxation process may transform a not-linker-relaxable instruction/fixup into one that that is linker relaxable, for instance `c.jal` becoming `qc.e.jal`, or `bne` getting turned into `beq; jal` (the `jal` is linker relaxable). In order for this to work, the following things have to happen: - Any instruction/fixup which might be relaxed to a linker-relaxable instruction/fixup, gets marked as `RelaxCandidate = true` in RISCVMCCodeEmitter. - In RISCVAsmBackend, when emitting the `R_RISCV_RELAX` relocation, we have to check that the relocation/fixup kind is one that may need a relax relocation, as well as that it is marked as linker relaxable (the latter will not be set if relaxation is disabled). - Linker Relaxable instructions streamed to a Relaxable fragment need to mark the fragment and its section as linker relaxable. I also added more debug output for Sections/Fixups which are marked Linker Relaxable. This results in more relocations, when these PC-relative fixups cross an instruction with a fixup that is resolved as not linker-relaxable but caused the fragment to be marked linker relaxable at streaming time (i.e. `c.j`). (cherry picked from commit 9e8f7ac) Fixes: llvm#150071
1 parent 6096d35 commit 1c327ec

File tree

8 files changed

+161
-15
lines changed

8 files changed

+161
-15
lines changed

llvm/lib/MC/MCExpr.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,10 @@ static void attemptToFoldSymbolOffsetDifference(const MCAssembler *Asm,
361361
if (BBeforeRelax && AAfterRelax)
362362
return;
363363
}
364+
const auto *RF = dyn_cast<MCRelaxableFragment>(F);
365+
if (RF && RF->isLinkerRelaxable()) {
366+
return;
367+
}
364368
if (&*F == FA) {
365369
// If FA and FB belong to the same subsection, the loop will find FA and
366370
// we can resolve the difference.

llvm/lib/MC/MCFragment.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ LLVM_DUMP_METHOD void MCFragment::dump() const {
7070
OS << "\n Fixup @" << F.getOffset() << " Value:";
7171
F.getValue()->print(OS, nullptr);
7272
OS << " Kind:" << F.getKind();
73+
if (F.isLinkerRelaxable())
74+
OS << " LinkerRelaxable";
7375
}
7476
};
7577

@@ -113,6 +115,8 @@ LLVM_DUMP_METHOD void MCFragment::dump() const {
113115
}
114116
case MCFragment::FT_Relaxable: {
115117
const auto *F = cast<MCRelaxableFragment>(this);
118+
if (F->isLinkerRelaxable())
119+
OS << " LinkerRelaxable";
116120
OS << " Size:" << F->getContents().size() << ' ';
117121
F->getInst().dump_pretty(OS);
118122
printFixups(F->getFixups());

llvm/lib/MC/MCObjectStreamer.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,13 @@ void MCObjectStreamer::emitInstToFragment(const MCInst &Inst,
408408
Inst, IF->getContentsForAppending(), Fixups, STI);
409409
IF->doneAppending();
410410
IF->appendFixups(Fixups);
411+
412+
for (auto &Fixup : Fixups) {
413+
if (Fixup.isLinkerRelaxable()) {
414+
IF->setLinkerRelaxable();
415+
getCurrentSectionOnly()->setLinkerRelaxable();
416+
}
417+
}
411418
}
412419

413420
#ifndef NDEBUG

llvm/lib/MC/MCSection.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ LLVM_DUMP_METHOD void MCSection::dump(
6363
raw_ostream &OS = errs();
6464

6565
OS << "MCSection Name:" << getName();
66+
if (isLinkerRelaxable())
67+
OS << " LinkerRelaxable";
6668
for (auto &F : *this) {
6769
OS << '\n';
6870
F.dump();

llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,23 @@ void RISCVAsmBackend::maybeAddVendorReloc(const MCFragment &F,
792792
Asm->getWriter().recordRelocation(F, VendorFixup, VendorTarget, VendorValue);
793793
}
794794

795+
static bool relaxableFixupNeedsRelocation(const MCFixupKind Kind) {
796+
// Some Fixups are marked as LinkerRelaxable by
797+
// `RISCVMCCodeEmitter::getImmOpValue` only because they may be
798+
// (assembly-)relaxed into a linker-relaxable instruction. This function
799+
// should return `false` for those fixups so they do not get a `R_RISCV_RELAX`
800+
// relocation emitted in addition to the relocation.
801+
switch (Kind) {
802+
default:
803+
break;
804+
case RISCV::fixup_riscv_rvc_jump:
805+
case RISCV::fixup_riscv_rvc_branch:
806+
case RISCV::fixup_riscv_jal:
807+
return false;
808+
}
809+
return true;
810+
}
811+
795812
bool RISCVAsmBackend::addReloc(const MCFragment &F, const MCFixup &Fixup,
796813
const MCValue &Target, uint64_t &FixedValue,
797814
bool IsResolved) {
@@ -834,25 +851,32 @@ bool RISCVAsmBackend::addReloc(const MCFragment &F, const MCFixup &Fixup,
834851
return false;
835852
}
836853

837-
// If linker relaxation is enabled and supported by the current relocation,
838-
// generate a relocation and then append a RELAX.
839-
if (Fixup.isLinkerRelaxable())
854+
// If linker relaxation is enabled and supported by the current fixup, then we
855+
// always want to generate a relocation.
856+
bool NeedsRelax = Fixup.isLinkerRelaxable() &&
857+
relaxableFixupNeedsRelocation(Fixup.getKind());
858+
if (NeedsRelax)
840859
IsResolved = false;
860+
841861
if (IsResolved && Fixup.isPCRel())
842862
IsResolved = isPCRelFixupResolved(Target.getAddSym(), F);
843863

844864
if (!IsResolved) {
845-
// Some Fixups require a vendor relocation, record it (directly) before we
865+
// Some Fixups require a VENDOR relocation, record it (directly) before we
846866
// add the relocation.
847867
maybeAddVendorReloc(F, Fixup);
848868

849869
Asm->getWriter().recordRelocation(F, Fixup, Target, FixedValue);
850-
}
851870

852-
if (Fixup.isLinkerRelaxable()) {
853-
auto FA = MCFixup::create(Fixup.getOffset(), nullptr, ELF::R_RISCV_RELAX);
854-
Asm->getWriter().recordRelocation(F, FA, MCValue::get(nullptr),
855-
FixedValueA);
871+
if (NeedsRelax) {
872+
// Some Fixups get a RELAX relocation, record it (directly) after we add
873+
// the relocation.
874+
MCFixup RelaxFixup =
875+
MCFixup::create(Fixup.getOffset(), nullptr, ELF::R_RISCV_RELAX);
876+
MCValue RelaxTarget = MCValue::get(nullptr);
877+
uint64_t RelaxValue;
878+
Asm->getWriter().recordRelocation(F, RelaxFixup, RelaxTarget, RelaxValue);
879+
}
856880
}
857881

858882
return false;

llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -576,8 +576,21 @@ uint64_t RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo,
576576
"getImmOpValue expects only expressions or immediates");
577577
const MCExpr *Expr = MO.getExpr();
578578
MCExpr::ExprKind Kind = Expr->getKind();
579-
unsigned FixupKind = RISCV::fixup_riscv_invalid;
579+
580+
// `RelaxCandidate` must be set to `true` in two cases:
581+
// - The fixup's relocation gets a R_RISCV_RELAX relocation
582+
// - The underlying instruction may be relaxed to an instruction that gets a
583+
// `R_RISCV_RELAX` relocation.
584+
//
585+
// The actual emission of `R_RISCV_RELAX` will be handled in
586+
// `RISCVAsmBackend::applyFixup`.
580587
bool RelaxCandidate = false;
588+
auto AsmRelaxToLinkerRelaxableWithFeature = [&](unsigned Feature) -> void {
589+
if (!STI.hasFeature(RISCV::FeatureExactAssembly) && STI.hasFeature(Feature))
590+
RelaxCandidate = true;
591+
};
592+
593+
unsigned FixupKind = RISCV::fixup_riscv_invalid;
581594
if (Kind == MCExpr::Specifier) {
582595
const auto *RVExpr = cast<MCSpecifierExpr>(Expr);
583596
FixupKind = RVExpr->getSpecifier();
@@ -644,18 +657,26 @@ uint64_t RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo,
644657
// FIXME: Sub kind binary exprs have chance of underflow.
645658
if (MIFrm == RISCVII::InstFormatJ) {
646659
FixupKind = RISCV::fixup_riscv_jal;
660+
AsmRelaxToLinkerRelaxableWithFeature(RISCV::FeatureVendorXqcilb);
647661
} else if (MIFrm == RISCVII::InstFormatB) {
648662
FixupKind = RISCV::fixup_riscv_branch;
663+
// This might be assembler relaxed to `b<cc>; jal` but we cannot relax
664+
// the `jal` again in the assembler.
649665
} else if (MIFrm == RISCVII::InstFormatCJ) {
650666
FixupKind = RISCV::fixup_riscv_rvc_jump;
667+
AsmRelaxToLinkerRelaxableWithFeature(RISCV::FeatureVendorXqcilb);
651668
} else if (MIFrm == RISCVII::InstFormatCB) {
652669
FixupKind = RISCV::fixup_riscv_rvc_branch;
670+
// This might be assembler relaxed to `b<cc>; jal` but we cannot relax
671+
// the `jal` again in the assembler.
653672
} else if (MIFrm == RISCVII::InstFormatCI) {
654673
FixupKind = RISCV::fixup_riscv_rvc_imm;
655674
} else if (MIFrm == RISCVII::InstFormatI) {
656675
FixupKind = RISCV::fixup_riscv_12_i;
657676
} else if (MIFrm == RISCVII::InstFormatQC_EB) {
658677
FixupKind = RISCV::fixup_riscv_qc_e_branch;
678+
// This might be assembler relaxed to `qc.e.b<cc>; jal` but we cannot
679+
// relax the `jal` again in the assembler.
659680
} else if (MIFrm == RISCVII::InstFormatQC_EAI) {
660681
FixupKind = RISCV::fixup_riscv_qc_e_32;
661682
RelaxCandidate = true;
@@ -670,9 +691,9 @@ uint64_t RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo,
670691
assert(FixupKind != RISCV::fixup_riscv_invalid && "Unhandled expression!");
671692

672693
addFixup(Fixups, 0, Expr, FixupKind);
673-
// If linker relaxation is enabled and supported by this relocation, set
674-
// a bit so that if fixup is unresolved, a R_RISCV_RELAX relocation will be
675-
// appended.
694+
// If linker relaxation is enabled and supported by this relocation, set a bit
695+
// so that the assembler knows the size of the instruction is not fixed/known,
696+
// and the relocation will need a R_RISCV_RELAX relocation.
676697
if (EnableRelax && RelaxCandidate)
677698
Fixups.back().setLinkerRelaxable();
678699
++MCNumFixups;

llvm/test/MC/RISCV/Relocations/mc-dump.s

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
# RUN: llvm-mc -filetype=obj --triple=riscv64 --mattr=+relax %s -debug-only=mc-dump -o /dev/null 2>&1 | FileCheck %s
33

44
# CHECK:Sections:[
5-
# CHECK-NEXT:MCSection Name:.text
5+
# CHECK-NEXT:MCSection Name:.text LinkerRelaxable
66
# CHECK-NEXT:0 Data Size:0 []
77
# CHECK-NEXT: Symbol @0 .text
88
# CHECK-NEXT:0 Align Align:4 Fill:0 FillLen:1 MaxBytesToEmit:4 Nops
99
# CHECK-NEXT:0 Data LinkerRelaxable Size:8 [97,00,00,00,e7,80,00,00]
10-
# CHECK-NEXT: Fixup @0 Value:specifier(19,ext) Kind:4023
10+
# CHECK-NEXT: Fixup @0 Value:specifier(19,ext) Kind:4023 LinkerRelaxable
1111
# CHECK-NEXT: Symbol @0 $x
1212
# CHECK-NEXT:8 Align Align:8 Fill:0 FillLen:1 MaxBytesToEmit:8 Nops
1313
# CHECK-NEXT:12 Data Size:4 [13,05,30,00]
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# RUN: llvm-mc --triple=riscv32 -mattr=+relax,+experimental-xqcilb,+experimental-xqcibi \
2+
# RUN: %s -filetype=obj -o - -riscv-add-build-attributes \
3+
# RUN: | llvm-objdump -dr -M no-aliases - \
4+
# RUN: | FileCheck %s
5+
6+
## This tests that we correctly emit relocations for linker relaxation when
7+
## relaxing `JAL` to `QC.E.JAL`.
8+
9+
## PR150071
10+
11+
.global foo
12+
13+
# CHECK-LABEL: <branch_over_relaxable>:
14+
branch_over_relaxable:
15+
jal x1, foo
16+
# CHECK: qc.e.jal 0x0 <branch_over_relaxable>
17+
# CHECK-NEXT: R_RISCV_VENDOR QUALCOMM
18+
# CHECK-NEXT: R_RISCV_CUSTOM195 foo
19+
# CHECK-NEXT: R_RISCV_RELAX *ABS*
20+
bne a0, a1, branch_over_relaxable
21+
# CHECK-NEXT: bne a0, a1, 0x6 <branch_over_relaxable+0x6>
22+
# CHECK-NEXT: R_RISCV_BRANCH branch_over_relaxable
23+
# CHECK-NOT: R_RISCV_RELAX
24+
qc.e.bnei a0, 0x21, branch_over_relaxable
25+
# CHECK-NEXT: qc.e.bnei a0, 0x21, 0xa <branch_over_relaxable+0xa>
26+
# CHECK-NEXT: R_RISCV_VENDOR QUALCOMM
27+
# CHECK-NEXT: R_RISCV_CUSTOM193 branch_over_relaxable
28+
# CHECK-NOT: R_RISCV_RELAX
29+
ret
30+
# CHECK-NEXT: c.jr ra
31+
32+
# CHECK-LABEL: <short_jump_over_fixed>:
33+
short_jump_over_fixed:
34+
nop
35+
# CHECK: c.nop
36+
j short_jump_over_fixed
37+
# CHECK-NEXT: c.j 0x14 <short_jump_over_fixed+0x2>
38+
# CHECK-NEXT: R_RISCV_RVC_JUMP short_jump_over_fixed
39+
# CHECK-NOT: R_RISCV_RELAX
40+
ret
41+
# CHECK-NEXT: c.jr ra
42+
43+
# CHECK-LABEL: <short_jump_over_relaxable>:
44+
short_jump_over_relaxable:
45+
call foo
46+
# CHECK: auipc ra, 0x0
47+
# CHECK-NEXT: R_RISCV_CALL_PLT foo
48+
# CHECK-NEXT: R_RISCV_RELAX *ABS*
49+
# CHECK-NEXT: jalr ra, 0x0(ra) <short_jump_over_relaxable>
50+
j short_jump_over_relaxable
51+
# CHECK-NEXT: c.j 0x20 <short_jump_over_relaxable+0x8>
52+
# CHECK-NEXT: R_RISCV_RVC_JUMP short_jump_over_relaxable
53+
# CHECK-NOT: R_RISCV_RELAX
54+
ret
55+
# CHECK-NEXT: c.jr ra
56+
57+
# CHECK-LABEL: <mid_jump_over_fixed>:
58+
mid_jump_over_fixed:
59+
nop
60+
# CHECK: c.nop
61+
.space 0x1000
62+
# CHECK-NEXT: ...
63+
j mid_jump_over_fixed
64+
# CHECK-NEXT: jal zero, 0x1026 <mid_jump_over_fixed+0x1002>
65+
# CHECK-NEXT: R_RISCV_JAL mid_jump_over_fixed
66+
# CHECK-NOT: R_RISCV_RELAX
67+
ret
68+
# CHECK-NEXT: c.jr ra
69+
70+
# CHECK-LABEL: <mid_jump_over_relaxable>:
71+
mid_jump_over_relaxable:
72+
call foo
73+
# CHECK: auipc ra, 0x0
74+
# CHECK-NEXT: R_RISCV_CALL_PLT foo
75+
# CHECK-NEXT: R_RISCV_RELAX *ABS*
76+
# CHECK-NEXT: jalr ra, 0x0(ra) <mid_jump_over_relaxable>
77+
.space 0x1000
78+
# CHECK-NEXT: ...
79+
j mid_jump_over_relaxable
80+
# CHECK-NEXT: jal zero, 0x2034 <mid_jump_over_relaxable+0x1008>
81+
# CHECK-NEXT: R_RISCV_JAL mid_jump_over_relaxable
82+
# CHECK-NOT: R_RISCV_RELAX
83+
ret
84+
# CHECK-NEXT: c.jr ra

0 commit comments

Comments
 (0)