Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit f873029

Browse files
committedJun 16, 2023
[BOLT] Add minimal RISC-V 64-bit support
Just enough features are implemented to process a simple "hello world" executable and produce something that still runs (including libc calls). This was mainly a matter of implementing support for various relocations. Currently, the following are handled: - R_RISCV_JAL - R_RISCV_CALL - R_RISCV_CALL_PLT - R_RISCV_BRANCH - R_RISCV_RVC_BRANCH - R_RISCV_RVC_JUMP - R_RISCV_GOT_HI20 - R_RISCV_PCREL_HI20 - R_RISCV_PCREL_LO12_I - R_RISCV_RELAX - R_RISCV_NONE Executables linked with linker relaxation will probably fail to be processed. BOLT relocates .text to a high address while leaving .plt at its original (low) address. This causes PC-relative PLT calls that were relaxed to a JAL to not fit their offset in an I-immediate anymore. This is something that will be addressed in a later patch. Changes to the BOLT core are relatively minor. Two things were tricky to implement and needed slightly larger changes. I'll explain those below. The R_RISCV_CALL(_PLT) relocation is put on the first instruction of a AUIPC/JALR pair, the second does not get any relocation (unlike other PCREL pairs). This causes issues with the combinations of the way BOLT processes binaries and the RISC-V MC-layer handles relocations: - BOLT reassembles instructions one by one and since the JALR doesn't have a relocation, it simply gets copied without modification; - Even though the MC-layer handles R_RISCV_CALL properly (adjusts both the AUIPC and the JALR), it assumes the immediates of both instructions are 0 (to be able to or-in a new value). This will most likely not be the case for the JALR that got copied over. To handle this difficulty without resorting to RISC-V-specific hacks in the BOLT core, a new binary pass was added that searches for AUIPC/JALR pairs and zeroes-out the immediate of the JALR. A second difficulty was supporting ABS symbols. As far as I can tell, ABS symbols were not handled at all, causing __global_pointer$ to break. RewriteInstance::analyzeRelocation was updated to handle these generically. Tests are provided for all supported relocations. Note that in order to test the correct handling of PLT entries, an ELF file produced by GCC had to be used. While I tried to strip the YAML representation, it's still quite large. Any suggestions on how to improve this would be appreciated. Reviewed By: rafauler Differential Revision: https://reviews.llvm.org/D145687
1 parent f9f92f1 commit f873029

26 files changed

+1740
-11
lines changed
 

‎bolt/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ list(INSERT CMAKE_MODULE_PATH 0 "${BOLT_SOURCE_DIR}/cmake/modules")
99

1010
# Determine default set of targets to build -- the intersection of
1111
# those BOLT supports and those LLVM is targeting.
12-
set(BOLT_TARGETS_TO_BUILD_all "AArch64;X86")
12+
set(BOLT_TARGETS_TO_BUILD_all "AArch64;X86;RISCV")
1313
set(BOLT_TARGETS_TO_BUILD_default)
1414
foreach (tgt ${BOLT_TARGETS_TO_BUILD_all})
1515
if (tgt IN_LIST LLVM_TARGETS_TO_BUILD)

‎bolt/CODE_OWNERS.TXT

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,7 @@ D: DWARF support
2020
N: Vladislav Khmelevsky
2121
E: och95@yandex.ru
2222
D: AArch64 backend
23+
24+
N: Job Noorman
25+
E: jnoorman@igalia.com
26+
D: RISC-V backend

‎bolt/include/bolt/Core/BinaryContext.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,8 @@ class BinaryContext {
722722
TheTriple->getArch() == llvm::Triple::x86_64;
723723
}
724724

725+
bool isRISCV() const { return TheTriple->getArch() == llvm::Triple::riscv64; }
726+
725727
// AArch64-specific functions to check if symbol is used to delimit
726728
// code/data in .text. Code is marked by $x, data by $d.
727729
MarkerSymType getMarkerType(const SymbolRef &Symbol) const;

‎bolt/include/bolt/Core/MCPlusBuilder.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,12 @@ class MCPlusBuilder {
633633
return false;
634634
}
635635

636+
/// Returns true if First/Second is a AUIPC/JALR call pair.
637+
virtual bool isRISCVCall(const MCInst &First, const MCInst &Second) const {
638+
llvm_unreachable("not implemented");
639+
return false;
640+
}
641+
636642
/// If non-zero, this is used to fill the executable space with instructions
637643
/// that will trap. Defaults to 0.
638644
virtual unsigned getTrapFillValue() const { return 0; }
@@ -2032,6 +2038,10 @@ MCPlusBuilder *createAArch64MCPlusBuilder(const MCInstrAnalysis *,
20322038
const MCInstrInfo *,
20332039
const MCRegisterInfo *);
20342040

2041+
MCPlusBuilder *createRISCVMCPlusBuilder(const MCInstrAnalysis *,
2042+
const MCInstrInfo *,
2043+
const MCRegisterInfo *);
2044+
20352045
} // namespace bolt
20362046
} // namespace llvm
20372047

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//===- bolt/Passes/FixRISCVCallsPass.h --------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file declares the FixRISCVCallsPass class, which sets the JALR immediate
10+
// to 0 for AUIPC/JALR pairs with a R_RISCV_CALL(_PLT) relocation. This is
11+
// necessary since MC expects it to be zero in order to or-in fixups.
12+
//===----------------------------------------------------------------------===//
13+
14+
#ifndef BOLT_PASSES_FIXRISCVCALLSPASS_H
15+
#define BOLT_PASSES_FIXRISCVCALLSPASS_H
16+
17+
#include "bolt/Passes/BinaryPasses.h"
18+
19+
namespace llvm {
20+
namespace bolt {
21+
22+
class FixRISCVCallsPass : public BinaryFunctionPass {
23+
void runOnFunction(BinaryFunction &Function);
24+
25+
public:
26+
explicit FixRISCVCallsPass(const cl::opt<bool> &PrintPass)
27+
: BinaryFunctionPass(PrintPass) {}
28+
29+
const char *getName() const override { return "fix-riscv-calls"; }
30+
31+
/// Pass entry point
32+
void runOnFunctions(BinaryContext &BC) override;
33+
};
34+
35+
} // namespace bolt
36+
} // namespace llvm
37+
38+
#endif

‎bolt/include/bolt/Rewrite/RewriteInstance.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,9 @@ class RewriteInstance {
279279
/// is the expected .plt \p Section entry function size.
280280
void disassemblePLTSectionX86(BinarySection &Section, uint64_t EntrySize);
281281

282+
/// Disassemble riscv-specific .plt \p Section auxiliary function
283+
void disassemblePLTSectionRISCV(BinarySection &Section);
284+
282285
/// ELF-specific part. TODO: refactor into new class.
283286
#define ELF_FUNCTION(TYPE, FUNC) \
284287
template <typename ELFT> TYPE FUNC(object::ELFObjectFile<ELFT> *Obj); \
@@ -536,6 +539,9 @@ class RewriteInstance {
536539
const PLTSectionInfo AArch64_PLTSections[3] = {
537540
{".plt"}, {".iplt"}, {nullptr}};
538541

542+
/// RISCV PLT sections.
543+
const PLTSectionInfo RISCV_PLTSections[3] = {{".plt"}, {nullptr}};
544+
539545
/// Return PLT information for a section with \p SectionName or nullptr
540546
/// if the section is not PLT.
541547
const PLTSectionInfo *getPLTSectionInfo(StringRef SectionName) {
@@ -549,6 +555,9 @@ class RewriteInstance {
549555
case Triple::aarch64:
550556
PLTSI = AArch64_PLTSections;
551557
break;
558+
case Triple::riscv64:
559+
PLTSI = RISCV_PLTSections;
560+
break;
552561
}
553562
for (; PLTSI && PLTSI->Name; ++PLTSI)
554563
if (SectionName == PLTSI->Name)

‎bolt/lib/Core/BinaryContext.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ BinaryContext::createBinaryContext(const ObjectFile *File, bool IsPIC,
128128
ArchName = "aarch64";
129129
FeaturesStr = "+all";
130130
break;
131+
case llvm::Triple::riscv64:
132+
ArchName = "riscv64";
133+
// RV64GC
134+
FeaturesStr = "+m,+a,+f,+d,+zicsr,+zifencei,+c";
135+
break;
131136
default:
132137
return createStringError(std::errc::not_supported,
133138
"BOLT-ERROR: Unrecognized machine in ELF file");

‎bolt/lib/Core/BinaryFunction.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,7 +1323,7 @@ bool BinaryFunction::disassemble() {
13231323
if (BC.isAArch64())
13241324
handleAArch64IndirectCall(Instruction, Offset);
13251325
}
1326-
} else if (BC.isAArch64()) {
1326+
} else if (BC.isAArch64() || BC.isRISCV()) {
13271327
// Check if there's a relocation associated with this instruction.
13281328
bool UsedReloc = false;
13291329
for (auto Itr = Relocations.lower_bound(Offset),
@@ -1343,7 +1343,7 @@ bool BinaryFunction::disassemble() {
13431343
UsedReloc = true;
13441344
}
13451345

1346-
if (MIB->hasPCRelOperand(Instruction) && !UsedReloc)
1346+
if (!BC.isRISCV() && MIB->hasPCRelOperand(Instruction) && !UsedReloc)
13471347
handlePCRelOperand(Instruction, AbsoluteInstrAddr, Size);
13481348
}
13491349

‎bolt/lib/Core/Relocation.cpp

Lines changed: 181 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,24 @@ static bool isSupportedAArch64(uint64_t Type) {
8989
}
9090
}
9191

92+
static bool isSupportedRISCV(uint64_t Type) {
93+
switch (Type) {
94+
default:
95+
return false;
96+
case ELF::R_RISCV_JAL:
97+
case ELF::R_RISCV_CALL:
98+
case ELF::R_RISCV_CALL_PLT:
99+
case ELF::R_RISCV_BRANCH:
100+
case ELF::R_RISCV_RELAX:
101+
case ELF::R_RISCV_GOT_HI20:
102+
case ELF::R_RISCV_PCREL_HI20:
103+
case ELF::R_RISCV_PCREL_LO12_I:
104+
case ELF::R_RISCV_RVC_JUMP:
105+
case ELF::R_RISCV_RVC_BRANCH:
106+
return true;
107+
}
108+
}
109+
92110
static size_t getSizeForTypeX86(uint64_t Type) {
93111
switch (Type) {
94112
default:
@@ -163,6 +181,28 @@ static size_t getSizeForTypeAArch64(uint64_t Type) {
163181
}
164182
}
165183

184+
static size_t getSizeForTypeRISCV(uint64_t Type) {
185+
switch (Type) {
186+
default:
187+
errs() << object::getELFRelocationTypeName(ELF::EM_RISCV, Type) << '\n';
188+
llvm_unreachable("unsupported relocation type");
189+
case ELF::R_RISCV_RVC_JUMP:
190+
case ELF::R_RISCV_RVC_BRANCH:
191+
return 2;
192+
case ELF::R_RISCV_JAL:
193+
case ELF::R_RISCV_BRANCH:
194+
case ELF::R_RISCV_PCREL_HI20:
195+
case ELF::R_RISCV_PCREL_LO12_I:
196+
case ELF::R_RISCV_32_PCREL:
197+
case ELF::R_RISCV_CALL:
198+
case ELF::R_RISCV_CALL_PLT:
199+
return 4;
200+
case ELF::R_RISCV_GOT_HI20:
201+
// See extractValueRISCV for why this is necessary.
202+
return 8;
203+
}
204+
}
205+
166206
static bool skipRelocationTypeX86(uint64_t Type) {
167207
return Type == ELF::R_X86_64_NONE;
168208
}
@@ -171,6 +211,16 @@ static bool skipRelocationTypeAArch64(uint64_t Type) {
171211
return Type == ELF::R_AARCH64_NONE || Type == ELF::R_AARCH64_LD_PREL_LO19;
172212
}
173213

214+
static bool skipRelocationTypeRISCV(uint64_t Type) {
215+
switch (Type) {
216+
default:
217+
return false;
218+
case ELF::R_RISCV_NONE:
219+
case ELF::R_RISCV_RELAX:
220+
return true;
221+
}
222+
}
223+
174224
static bool skipRelocationProcessX86(uint64_t &Type, uint64_t Contents) {
175225
return false;
176226
}
@@ -262,6 +312,10 @@ static bool skipRelocationProcessAArch64(uint64_t &Type, uint64_t Contents) {
262312
return false;
263313
}
264314

315+
static bool skipRelocationProcessRISCV(uint64_t &Type, uint64_t Contents) {
316+
return false;
317+
}
318+
265319
static uint64_t encodeValueX86(uint64_t Type, uint64_t Value, uint64_t PC) {
266320
switch (Type) {
267321
default:
@@ -407,6 +461,57 @@ static uint64_t extractValueAArch64(uint64_t Type, uint64_t Contents,
407461
}
408462
}
409463

464+
static uint64_t extractUImmRISCV(uint32_t Contents) {
465+
return SignExtend64<32>(Contents & 0xfffff000);
466+
}
467+
468+
static uint64_t extractIImmRISCV(uint32_t Contents) {
469+
return SignExtend64<12>(Contents >> 20);
470+
}
471+
472+
static uint64_t extractJImmRISCV(uint32_t Contents) {
473+
return SignExtend64<21>(
474+
(((Contents >> 21) & 0x3ff) << 1) | (((Contents >> 20) & 0x1) << 11) |
475+
(((Contents >> 12) & 0xff) << 12) | (((Contents >> 31) & 0x1) << 20));
476+
}
477+
478+
static uint64_t extractBImmRISCV(uint32_t Contents) {
479+
return SignExtend64<13>(
480+
(((Contents >> 8) & 0xf) << 1) | (((Contents >> 25) & 0x3f) << 5) |
481+
(((Contents >> 7) & 0x1) << 11) | (((Contents >> 31) & 0x1) << 12));
482+
}
483+
484+
static uint64_t extractValueRISCV(uint64_t Type, uint64_t Contents,
485+
uint64_t PC) {
486+
switch (Type) {
487+
default:
488+
errs() << object::getELFRelocationTypeName(ELF::EM_RISCV, Type) << '\n';
489+
llvm_unreachable("unsupported relocation type");
490+
case ELF::R_RISCV_JAL:
491+
return extractJImmRISCV(Contents);
492+
case ELF::R_RISCV_CALL:
493+
case ELF::R_RISCV_CALL_PLT:
494+
return extractUImmRISCV(Contents);
495+
case ELF::R_RISCV_BRANCH:
496+
return extractBImmRISCV(Contents);
497+
case ELF::R_RISCV_GOT_HI20:
498+
// We need to know the exact address of the GOT entry so we extract the
499+
// value from both the AUIPC and L[D|W]. We cannot rely on the symbol in the
500+
// relocation for this since it simply refers to the object that is stored
501+
// in the GOT entry, not to the entry itself.
502+
return extractUImmRISCV(Contents & 0xffffffff) +
503+
extractIImmRISCV(Contents >> 32);
504+
case ELF::R_RISCV_PCREL_HI20:
505+
return extractUImmRISCV(Contents);
506+
case ELF::R_RISCV_PCREL_LO12_I:
507+
return extractIImmRISCV(Contents);
508+
case ELF::R_RISCV_RVC_JUMP:
509+
return SignExtend64<11>(Contents >> 2);
510+
case ELF::R_RISCV_RVC_BRANCH:
511+
return SignExtend64<8>(((Contents >> 2) & 0x1f) | ((Contents >> 5) & 0xe0));
512+
}
513+
}
514+
410515
static bool isGOTX86(uint64_t Type) {
411516
switch (Type) {
412517
default:
@@ -444,6 +549,15 @@ static bool isGOTAArch64(uint64_t Type) {
444549
}
445550
}
446551

552+
static bool isGOTRISCV(uint64_t Type) {
553+
switch (Type) {
554+
default:
555+
return false;
556+
case ELF::R_RISCV_GOT_HI20:
557+
return true;
558+
}
559+
}
560+
447561
static bool isTLSX86(uint64_t Type) {
448562
switch (Type) {
449563
default:
@@ -472,6 +586,13 @@ static bool isTLSAArch64(uint64_t Type) {
472586
}
473587
}
474588

589+
static bool isTLSRISCV(uint64_t Type) {
590+
switch (Type) {
591+
default:
592+
return false;
593+
}
594+
}
595+
475596
static bool isPCRelativeX86(uint64_t Type) {
476597
switch (Type) {
477598
default:
@@ -543,46 +664,78 @@ static bool isPCRelativeAArch64(uint64_t Type) {
543664
}
544665
}
545666

667+
static bool isPCRelativeRISCV(uint64_t Type) {
668+
switch (Type) {
669+
default:
670+
llvm_unreachable("Unknown relocation type");
671+
case ELF::R_RISCV_JAL:
672+
case ELF::R_RISCV_CALL:
673+
case ELF::R_RISCV_CALL_PLT:
674+
case ELF::R_RISCV_BRANCH:
675+
case ELF::R_RISCV_GOT_HI20:
676+
case ELF::R_RISCV_PCREL_HI20:
677+
case ELF::R_RISCV_PCREL_LO12_I:
678+
case ELF::R_RISCV_RVC_JUMP:
679+
case ELF::R_RISCV_RVC_BRANCH:
680+
case ELF::R_RISCV_32_PCREL:
681+
return true;
682+
}
683+
}
684+
546685
bool Relocation::isSupported(uint64_t Type) {
547686
if (Arch == Triple::aarch64)
548687
return isSupportedAArch64(Type);
688+
if (Arch == Triple::riscv64)
689+
return isSupportedRISCV(Type);
549690
return isSupportedX86(Type);
550691
}
551692

552693
size_t Relocation::getSizeForType(uint64_t Type) {
553694
if (Arch == Triple::aarch64)
554695
return getSizeForTypeAArch64(Type);
696+
if (Arch == Triple::riscv64)
697+
return getSizeForTypeRISCV(Type);
555698
return getSizeForTypeX86(Type);
556699
}
557700

558701
bool Relocation::skipRelocationType(uint64_t Type) {
559702
if (Arch == Triple::aarch64)
560703
return skipRelocationTypeAArch64(Type);
704+
if (Arch == Triple::riscv64)
705+
return skipRelocationTypeRISCV(Type);
561706
return skipRelocationTypeX86(Type);
562707
}
563708

564709
bool Relocation::skipRelocationProcess(uint64_t &Type, uint64_t Contents) {
565710
if (Arch == Triple::aarch64)
566711
return skipRelocationProcessAArch64(Type, Contents);
712+
if (Arch == Triple::riscv64)
713+
skipRelocationProcessRISCV(Type, Contents);
567714
return skipRelocationProcessX86(Type, Contents);
568715
}
569716

570717
uint64_t Relocation::encodeValue(uint64_t Type, uint64_t Value, uint64_t PC) {
571718
if (Arch == Triple::aarch64)
572719
return encodeValueAArch64(Type, Value, PC);
720+
if (Arch == Triple::riscv64)
721+
llvm_unreachable("not implemented");
573722
return encodeValueX86(Type, Value, PC);
574723
}
575724

576725
uint64_t Relocation::extractValue(uint64_t Type, uint64_t Contents,
577726
uint64_t PC) {
578727
if (Arch == Triple::aarch64)
579728
return extractValueAArch64(Type, Contents, PC);
729+
if (Arch == Triple::riscv64)
730+
return extractValueRISCV(Type, Contents, PC);
580731
return extractValueX86(Type, Contents, PC);
581732
}
582733

583734
bool Relocation::isGOT(uint64_t Type) {
584735
if (Arch == Triple::aarch64)
585736
return isGOTAArch64(Type);
737+
if (Arch == Triple::riscv64)
738+
return isGOTRISCV(Type);
586739
return isGOTX86(Type);
587740
}
588741

@@ -597,42 +750,56 @@ bool Relocation::isNone(uint64_t Type) { return Type == getNone(); }
597750
bool Relocation::isRelative(uint64_t Type) {
598751
if (Arch == Triple::aarch64)
599752
return Type == ELF::R_AARCH64_RELATIVE;
753+
if (Arch == Triple::riscv64)
754+
return Type == ELF::R_RISCV_RELATIVE;
600755
return Type == ELF::R_X86_64_RELATIVE;
601756
}
602757

603758
bool Relocation::isIRelative(uint64_t Type) {
604759
if (Arch == Triple::aarch64)
605760
return Type == ELF::R_AARCH64_IRELATIVE;
761+
if (Arch == Triple::riscv64)
762+
llvm_unreachable("not implemented");
606763
return Type == ELF::R_X86_64_IRELATIVE;
607764
}
608765

609766
bool Relocation::isTLS(uint64_t Type) {
610767
if (Arch == Triple::aarch64)
611768
return isTLSAArch64(Type);
769+
if (Arch == Triple::riscv64)
770+
return isTLSRISCV(Type);
612771
return isTLSX86(Type);
613772
}
614773

615774
uint64_t Relocation::getNone() {
616775
if (Arch == Triple::aarch64)
617776
return ELF::R_AARCH64_NONE;
777+
if (Arch == Triple::riscv64)
778+
return ELF::R_RISCV_NONE;
618779
return ELF::R_X86_64_NONE;
619780
}
620781

621782
uint64_t Relocation::getPC32() {
622783
if (Arch == Triple::aarch64)
623784
return ELF::R_AARCH64_PREL32;
785+
if (Arch == Triple::riscv64)
786+
return ELF::R_RISCV_32_PCREL;
624787
return ELF::R_X86_64_PC32;
625788
}
626789

627790
uint64_t Relocation::getPC64() {
628791
if (Arch == Triple::aarch64)
629792
return ELF::R_AARCH64_PREL64;
793+
if (Arch == Triple::riscv64)
794+
llvm_unreachable("not implemented");
630795
return ELF::R_X86_64_PC64;
631796
}
632797

633798
bool Relocation::isPCRelative(uint64_t Type) {
634799
if (Arch == Triple::aarch64)
635800
return isPCRelativeAArch64(Type);
801+
if (Arch == Triple::riscv64)
802+
return isPCRelativeRISCV(Type);
636803
return isPCRelativeX86(Type);
637804
}
638805

@@ -696,7 +863,20 @@ void Relocation::print(raw_ostream &OS) const {
696863
};
697864
if (Arch == Triple::aarch64)
698865
OS << AArch64RelocNames[Type];
699-
else
866+
else if (Arch == Triple::riscv64) {
867+
// RISC-V relocations are not sequentially numbered so we cannot use an
868+
// array
869+
switch (Type) {
870+
default:
871+
llvm_unreachable("illegal RISC-V relocation");
872+
#undef ELF_RELOC
873+
#define ELF_RELOC(name, value) \
874+
case value: \
875+
OS << #name; \
876+
break;
877+
#include "llvm/BinaryFormat/ELFRelocs/RISCV.def"
878+
}
879+
} else
700880
OS << X86RelocNames[Type];
701881
OS << ", 0x" << Twine::utohexstr(Offset);
702882
if (Symbol) {

‎bolt/lib/Passes/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ add_llvm_library(LLVMBOLTPasses
1414
FrameAnalysis.cpp
1515
FrameOptimizer.cpp
1616
FixRelaxationPass.cpp
17+
FixRISCVCallsPass.cpp
1718
HFSort.cpp
1819
HFSortPlus.cpp
1920
Hugify.cpp

‎bolt/lib/Passes/FixRISCVCallsPass.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#include "bolt/Passes/FixRISCVCallsPass.h"
2+
#include "bolt/Core/ParallelUtilities.h"
3+
4+
#include <iterator>
5+
6+
using namespace llvm;
7+
8+
namespace llvm {
9+
namespace bolt {
10+
11+
void FixRISCVCallsPass::runOnFunction(BinaryFunction &BF) {
12+
auto &BC = BF.getBinaryContext();
13+
14+
for (auto &BB : BF) {
15+
for (auto II = BB.begin(), IE = BB.end(); II != IE; ++II) {
16+
auto NextII = std::next(II);
17+
18+
if (NextII == IE)
19+
break;
20+
21+
if (!BC.MIB->isRISCVCall(*II, *NextII))
22+
continue;
23+
24+
auto L = BC.scopeLock();
25+
26+
// The MC layer handles R_RISCV_CALL_PLT but assumes that the immediate
27+
// in the JALR is zero (fixups are or'ed into instructions). Note that
28+
// NextII is guaranteed to point to a JALR by isRISCVCall.
29+
NextII->getOperand(2).setImm(0);
30+
}
31+
}
32+
}
33+
34+
void FixRISCVCallsPass::runOnFunctions(BinaryContext &BC) {
35+
if (!BC.isRISCV() || !BC.HasRelocations)
36+
return;
37+
38+
ParallelUtilities::WorkFuncTy WorkFun = [&](BinaryFunction &BF) {
39+
runOnFunction(BF);
40+
};
41+
42+
ParallelUtilities::runOnEachFunction(
43+
BC, ParallelUtilities::SchedulingPolicy::SP_INST_LINEAR, WorkFun, nullptr,
44+
"FixRISCVCalls");
45+
}
46+
47+
} // namespace bolt
48+
} // namespace llvm

‎bolt/lib/Rewrite/BinaryPassManager.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "bolt/Passes/AllocCombiner.h"
1313
#include "bolt/Passes/AsmDump.h"
1414
#include "bolt/Passes/CMOVConversion.h"
15+
#include "bolt/Passes/FixRISCVCallsPass.h"
1516
#include "bolt/Passes/FixRelaxationPass.h"
1617
#include "bolt/Passes/FrameOptimizer.h"
1718
#include "bolt/Passes/Hugify.h"
@@ -185,6 +186,11 @@ static cl::opt<bool>
185186
cl::desc("print functions after fix relaxations pass"),
186187
cl::cat(BoltOptCategory));
187188

189+
static cl::opt<bool>
190+
PrintFixRISCVCalls("print-fix-riscv-calls",
191+
cl::desc("print functions after fix RISCV calls pass"),
192+
cl::cat(BoltOptCategory));
193+
188194
static cl::opt<bool> PrintVeneerElimination(
189195
"print-veneer-elimination",
190196
cl::desc("print functions after veneer elimination pass"),
@@ -328,6 +334,11 @@ void BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
328334
std::make_unique<VeneerElimination>(PrintVeneerElimination));
329335
}
330336

337+
if (BC.isRISCV()) {
338+
Manager.registerPass(
339+
std::make_unique<FixRISCVCallsPass>(PrintFixRISCVCalls));
340+
}
341+
331342
// Here we manage dependencies/order manually, since passes are run in the
332343
// order they're registered.
333344

‎bolt/lib/Rewrite/RewriteInstance.cpp

Lines changed: 72 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,11 @@ MCPlusBuilder *createMCPlusBuilder(const Triple::ArchType Arch,
300300
return createAArch64MCPlusBuilder(Analysis, Info, RegInfo);
301301
#endif
302302

303+
#ifdef RISCV_AVAILABLE
304+
if (Arch == Triple::riscv64)
305+
return createRISCVMCPlusBuilder(Analysis, Info, RegInfo);
306+
#endif
307+
303308
llvm_unreachable("architecture unsupported by MCPlusBuilder");
304309
}
305310

@@ -1042,7 +1047,9 @@ void RewriteInstance::discoverFileObjects() {
10421047
section_iterator Section =
10431048
cantFail(Symbol.getSection(), "cannot get symbol section");
10441049
if (Section == InputFile->section_end()) {
1045-
// Could be an absolute symbol. Could record for pretty printing.
1050+
// Could be an absolute symbol. Used on RISC-V for __global_pointer$ so we
1051+
// need to record it to handle relocations against it. For other instances
1052+
// of absolute symbols, we record for pretty printing.
10461053
LLVM_DEBUG(if (opts::Verbosity > 1) {
10471054
dbgs() << "BOLT-INFO: absolute sym " << UniqueName << "\n";
10481055
});
@@ -1464,6 +1471,50 @@ void RewriteInstance::disassemblePLTSectionAArch64(BinarySection &Section) {
14641471
}
14651472
}
14661473

1474+
void RewriteInstance::disassemblePLTSectionRISCV(BinarySection &Section) {
1475+
const uint64_t SectionAddress = Section.getAddress();
1476+
const uint64_t SectionSize = Section.getSize();
1477+
StringRef PLTContents = Section.getContents();
1478+
ArrayRef<uint8_t> PLTData(
1479+
reinterpret_cast<const uint8_t *>(PLTContents.data()), SectionSize);
1480+
1481+
auto disassembleInstruction = [&](uint64_t InstrOffset, MCInst &Instruction,
1482+
uint64_t &InstrSize) {
1483+
const uint64_t InstrAddr = SectionAddress + InstrOffset;
1484+
if (!BC->DisAsm->getInstruction(Instruction, InstrSize,
1485+
PLTData.slice(InstrOffset), InstrAddr,
1486+
nulls())) {
1487+
errs() << "BOLT-ERROR: unable to disassemble instruction in PLT section "
1488+
<< Section.getName() << " at offset 0x"
1489+
<< Twine::utohexstr(InstrOffset) << '\n';
1490+
exit(1);
1491+
}
1492+
};
1493+
1494+
// Skip the first special entry since no relocation points to it.
1495+
uint64_t InstrOffset = 32;
1496+
1497+
while (InstrOffset < SectionSize) {
1498+
InstructionListType Instructions;
1499+
MCInst Instruction;
1500+
const uint64_t EntryOffset = InstrOffset;
1501+
const uint64_t EntrySize = 16;
1502+
uint64_t InstrSize;
1503+
1504+
while (InstrOffset < EntryOffset + EntrySize) {
1505+
disassembleInstruction(InstrOffset, Instruction, InstrSize);
1506+
Instructions.emplace_back(Instruction);
1507+
InstrOffset += InstrSize;
1508+
}
1509+
1510+
const uint64_t EntryAddress = SectionAddress + EntryOffset;
1511+
const uint64_t TargetAddress = BC->MIB->analyzePLTEntry(
1512+
Instruction, Instructions.begin(), Instructions.end(), EntryAddress);
1513+
1514+
createPLTBinaryFunction(TargetAddress, EntryAddress, EntrySize);
1515+
}
1516+
}
1517+
14671518
void RewriteInstance::disassemblePLTSectionX86(BinarySection &Section,
14681519
uint64_t EntrySize) {
14691520
const uint64_t SectionAddress = Section.getAddress();
@@ -1523,6 +1574,8 @@ void RewriteInstance::disassemblePLT() {
15231574
auto analyzeOnePLTSection = [&](BinarySection &Section, uint64_t EntrySize) {
15241575
if (BC->isAArch64())
15251576
return disassemblePLTSectionAArch64(Section);
1577+
if (BC->isRISCV())
1578+
return disassemblePLTSectionRISCV(Section);
15261579
return disassemblePLTSectionX86(Section, EntrySize);
15271580
};
15281581

@@ -1990,7 +2043,7 @@ bool RewriteInstance::analyzeRelocation(
19902043
// Section symbols are marked as ST_Debug.
19912044
IsSectionRelocation = (cantFail(Symbol.getType()) == SymbolRef::ST_Debug);
19922045
// Check for PLT entry registered with symbol name
1993-
if (!SymbolAddress && IsAArch64) {
2046+
if (!SymbolAddress && (IsAArch64 || BC->isRISCV())) {
19942047
const BinaryData *BD = BC->getPLTBinaryDataByName(SymbolName);
19952048
SymbolAddress = BD ? BD->getAddress() : 0;
19962049
}
@@ -2053,7 +2106,7 @@ bool RewriteInstance::analyzeRelocation(
20532106
if (SkipVerification)
20542107
return true;
20552108

2056-
if (IsAArch64)
2109+
if (IsAArch64 || BC->isRISCV())
20572110
return true;
20582111

20592112
if (SymbolName == "__hot_start" || SymbolName == "__hot_end")
@@ -2107,7 +2160,11 @@ void RewriteInstance::processDynamicRelocations() {
21072160
if (!DynamicRelSectionOrErr)
21082161
report_error("unable to find section corresponding to DT_RELA",
21092162
DynamicRelSectionOrErr.getError());
2110-
if (DynamicRelSectionOrErr->getSize() != DynamicRelocationsSize)
2163+
auto DynamicRelSectionSize = DynamicRelSectionOrErr->getSize();
2164+
// On RISC-V DT_RELASZ seems to include both .rela.dyn and .rela.plt
2165+
if (DynamicRelocationsSize == DynamicRelSectionSize + PLTRelocationsSize)
2166+
DynamicRelocationsSize = DynamicRelSectionSize;
2167+
if (DynamicRelSectionSize != DynamicRelocationsSize)
21112168
report_error("section size mismatch for DT_RELASZ",
21122169
errc::executable_format_error);
21132170
readDynamicRelocations(DynamicRelSectionOrErr->getSectionRef(),
@@ -2598,6 +2655,14 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
25982655
Expected<StringRef> SectionName = Section->getName();
25992656
if (SectionName && !SectionName->empty())
26002657
ReferencedSection = BC->getUniqueSectionByName(*SectionName);
2658+
} else if (ReferencedSymbol &&
2659+
(cantFail(Symbol.getFlags()) & SymbolRef::SF_Absolute)) {
2660+
// This might be a relocation for an ABS symbols like __global_pointer$ on
2661+
// RISC-V
2662+
ContainingBF->addRelocation(Rel.getOffset(), ReferencedSymbol,
2663+
Rel.getType(), 0,
2664+
cantFail(Symbol.getValue()));
2665+
return;
26012666
}
26022667
}
26032668

@@ -2607,7 +2672,7 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
26072672
const bool IsToCode = ReferencedSection && ReferencedSection->isText();
26082673

26092674
// Special handling of PC-relative relocations.
2610-
if (!IsAArch64 && Relocation::isPCRelative(RType)) {
2675+
if (!IsAArch64 && !BC->isRISCV() && Relocation::isPCRelative(RType)) {
26112676
if (!IsFromCode && IsToCode) {
26122677
// PC-relative relocations from data to code are tricky since the
26132678
// original information is typically lost after linking, even with
@@ -2640,7 +2705,7 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
26402705
}
26412706

26422707
bool ForceRelocation = BC->forceSymbolRelocations(SymbolName);
2643-
if (BC->isAArch64() && Relocation::isGOT(RType))
2708+
if ((BC->isAArch64() || BC->isRISCV()) && Relocation::isGOT(RType))
26442709
ForceRelocation = true;
26452710

26462711
if (!ReferencedSection && !ForceRelocation) {
@@ -2788,7 +2853,7 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection,
27882853
// These are mostly local data symbols but undefined symbols
27892854
// in relocation sections can get through here too, from .plt.
27902855
assert(
2791-
(IsAArch64 || IsSectionRelocation ||
2856+
(IsAArch64 || BC->isRISCV() || IsSectionRelocation ||
27922857
BC->getSectionNameForAddress(SymbolAddress)->startswith(".plt")) &&
27932858
"known symbols should not resolve to anonymous locals");
27942859

‎bolt/lib/Target/RISCV/CMakeLists.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
set(LLVM_LINK_COMPONENTS
2+
MC
3+
Support
4+
RISCVDesc
5+
)
6+
7+
add_llvm_library(LLVMBOLTTargetRISCV
8+
RISCVMCPlusBuilder.cpp
9+
10+
DISABLE_LLVM_LINK_LLVM_DYLIB
11+
12+
DEPENDS
13+
RISCVCommonTableGen
14+
)
15+
16+
target_link_libraries(LLVMBOLTTargetRISCV PRIVATE LLVMBOLTCore)
17+
18+
include_directories(
19+
${LLVM_MAIN_SRC_DIR}/lib/Target/RISCV
20+
${LLVM_BINARY_DIR}/lib/Target/RISCV
21+
)
Lines changed: 328 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,328 @@
1+
//===- bolt/Target/RISCV/RISCVMCPlusBuilder.cpp -----------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file provides RISCV-specific MCPlus builder.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "MCTargetDesc/RISCVMCExpr.h"
14+
#include "MCTargetDesc/RISCVMCTargetDesc.h"
15+
#include "bolt/Core/MCPlusBuilder.h"
16+
#include "llvm/BinaryFormat/ELF.h"
17+
#include "llvm/MC/MCInst.h"
18+
#include "llvm/Support/Debug.h"
19+
#include "llvm/Support/ErrorHandling.h"
20+
#include "llvm/Support/Format.h"
21+
#include "llvm/Support/raw_ostream.h"
22+
23+
#define DEBUG_TYPE "mcplus"
24+
25+
using namespace llvm;
26+
using namespace bolt;
27+
28+
namespace {
29+
30+
class RISCVMCPlusBuilder : public MCPlusBuilder {
31+
public:
32+
using MCPlusBuilder::MCPlusBuilder;
33+
34+
bool shouldRecordCodeRelocation(uint64_t RelType) const override {
35+
switch (RelType) {
36+
case ELF::R_RISCV_JAL:
37+
case ELF::R_RISCV_CALL:
38+
case ELF::R_RISCV_CALL_PLT:
39+
case ELF::R_RISCV_BRANCH:
40+
case ELF::R_RISCV_RVC_BRANCH:
41+
case ELF::R_RISCV_RVC_JUMP:
42+
case ELF::R_RISCV_GOT_HI20:
43+
case ELF::R_RISCV_PCREL_HI20:
44+
case ELF::R_RISCV_PCREL_LO12_I:
45+
return true;
46+
default:
47+
llvm_unreachable("Unexpected RISCV relocation type in code");
48+
}
49+
}
50+
51+
bool isNop(const MCInst &Inst) const {
52+
return Inst.getOpcode() == RISCV::ADDI &&
53+
Inst.getOperand(0).getReg() == RISCV::X0 &&
54+
Inst.getOperand(1).getReg() == RISCV::X0 &&
55+
Inst.getOperand(2).getImm() == 0;
56+
}
57+
58+
bool isCNop(const MCInst &Inst) const {
59+
return Inst.getOpcode() == RISCV::C_NOP;
60+
}
61+
62+
bool isNoop(const MCInst &Inst) const override {
63+
return isNop(Inst) || isCNop(Inst);
64+
}
65+
66+
bool hasPCRelOperand(const MCInst &Inst) const override {
67+
switch (Inst.getOpcode()) {
68+
default:
69+
return false;
70+
case RISCV::JAL:
71+
case RISCV::AUIPC:
72+
return true;
73+
}
74+
}
75+
76+
bool replaceBranchTarget(MCInst &Inst, const MCSymbol *TBB,
77+
MCContext *Ctx) const override {
78+
assert((isCall(Inst) || isBranch(Inst)) && !isIndirectBranch(Inst) &&
79+
"Invalid instruction");
80+
81+
unsigned SymOpIndex;
82+
83+
switch (Inst.getOpcode()) {
84+
default:
85+
llvm_unreachable("not implemented");
86+
case RISCV::C_J:
87+
SymOpIndex = 0;
88+
break;
89+
case RISCV::JAL:
90+
case RISCV::C_BEQZ:
91+
case RISCV::C_BNEZ:
92+
SymOpIndex = 1;
93+
break;
94+
case RISCV::BEQ:
95+
case RISCV::BGE:
96+
SymOpIndex = 2;
97+
break;
98+
}
99+
100+
Inst.getOperand(SymOpIndex) = MCOperand::createExpr(
101+
MCSymbolRefExpr::create(TBB, MCSymbolRefExpr::VK_None, *Ctx));
102+
return true;
103+
}
104+
105+
IndirectBranchType analyzeIndirectBranch(
106+
MCInst &Instruction, InstructionIterator Begin, InstructionIterator End,
107+
const unsigned PtrSize, MCInst *&MemLocInstr, unsigned &BaseRegNum,
108+
unsigned &IndexRegNum, int64_t &DispValue, const MCExpr *&DispExpr,
109+
MCInst *&PCRelBaseOut) const override {
110+
MemLocInstr = nullptr;
111+
BaseRegNum = 0;
112+
IndexRegNum = 0;
113+
DispValue = 0;
114+
DispExpr = nullptr;
115+
PCRelBaseOut = nullptr;
116+
return IndirectBranchType::UNKNOWN;
117+
}
118+
119+
bool convertJmpToTailCall(MCInst &Inst) override {
120+
if (isTailCall(Inst))
121+
return false;
122+
123+
switch (Inst.getOpcode()) {
124+
default:
125+
llvm_unreachable("unsupported tail call opcode");
126+
case RISCV::JAL:
127+
case RISCV::JALR:
128+
case RISCV::C_J:
129+
case RISCV::C_JR:
130+
break;
131+
}
132+
133+
setTailCall(Inst);
134+
return true;
135+
}
136+
137+
bool analyzeBranch(InstructionIterator Begin, InstructionIterator End,
138+
const MCSymbol *&TBB, const MCSymbol *&FBB,
139+
MCInst *&CondBranch,
140+
MCInst *&UncondBranch) const override {
141+
auto I = End;
142+
143+
while (I != Begin) {
144+
--I;
145+
146+
// Ignore nops and CFIs
147+
if (isPseudo(*I) || isNoop(*I))
148+
continue;
149+
150+
// Stop when we find the first non-terminator
151+
if (!isTerminator(*I) || isTailCall(*I) || !isBranch(*I))
152+
break;
153+
154+
// Handle unconditional branches.
155+
if (isUnconditionalBranch(*I)) {
156+
// If any code was seen after this unconditional branch, we've seen
157+
// unreachable code. Ignore them.
158+
CondBranch = nullptr;
159+
UncondBranch = &*I;
160+
const MCSymbol *Sym = getTargetSymbol(*I);
161+
assert(Sym != nullptr &&
162+
"Couldn't extract BB symbol from jump operand");
163+
TBB = Sym;
164+
continue;
165+
}
166+
167+
// Handle conditional branches and ignore indirect branches
168+
if (isIndirectBranch(*I))
169+
return false;
170+
171+
if (CondBranch == nullptr) {
172+
const MCSymbol *TargetBB = getTargetSymbol(*I);
173+
if (TargetBB == nullptr) {
174+
// Unrecognized branch target
175+
return false;
176+
}
177+
FBB = TBB;
178+
TBB = TargetBB;
179+
CondBranch = &*I;
180+
continue;
181+
}
182+
183+
llvm_unreachable("multiple conditional branches in one BB");
184+
}
185+
186+
return true;
187+
}
188+
189+
const MCSymbol *getTargetSymbol(const MCInst &Inst,
190+
unsigned OpNum = 0) const override {
191+
const MCOperand &Op = Inst.getOperand(OpNum);
192+
if (!Op.isExpr())
193+
return nullptr;
194+
195+
return MCPlusBuilder::getTargetSymbol(Op.getExpr());
196+
}
197+
198+
bool lowerTailCall(MCInst &Inst) override {
199+
removeAnnotation(Inst, MCPlus::MCAnnotation::kTailCall);
200+
if (getConditionalTailCall(Inst))
201+
unsetConditionalTailCall(Inst);
202+
return true;
203+
}
204+
205+
uint64_t analyzePLTEntry(MCInst &Instruction, InstructionIterator Begin,
206+
InstructionIterator End,
207+
uint64_t BeginPC) const override {
208+
auto I = Begin;
209+
210+
assert(I != End);
211+
auto &AUIPC = *I++;
212+
assert(AUIPC.getOpcode() == RISCV::AUIPC);
213+
assert(AUIPC.getOperand(0).getReg() == RISCV::X28);
214+
215+
assert(I != End);
216+
auto &LD = *I++;
217+
assert(LD.getOpcode() == RISCV::LD);
218+
assert(LD.getOperand(0).getReg() == RISCV::X28);
219+
assert(LD.getOperand(1).getReg() == RISCV::X28);
220+
221+
assert(I != End);
222+
auto &JALR = *I++;
223+
(void)JALR;
224+
assert(JALR.getOpcode() == RISCV::JALR);
225+
assert(JALR.getOperand(0).getReg() == RISCV::X6);
226+
assert(JALR.getOperand(1).getReg() == RISCV::X28);
227+
228+
assert(I != End);
229+
auto &NOP = *I++;
230+
(void)NOP;
231+
assert(isNoop(NOP));
232+
233+
assert(I == End);
234+
235+
auto AUIPCOffset = AUIPC.getOperand(1).getImm() << 12;
236+
auto LDOffset = LD.getOperand(2).getImm();
237+
return BeginPC + AUIPCOffset + LDOffset;
238+
}
239+
240+
bool replaceImmWithSymbolRef(MCInst &Inst, const MCSymbol *Symbol,
241+
int64_t Addend, MCContext *Ctx, int64_t &Value,
242+
uint64_t RelType) const override {
243+
unsigned ImmOpNo = -1U;
244+
245+
for (unsigned Index = 0; Index < MCPlus::getNumPrimeOperands(Inst);
246+
++Index) {
247+
if (Inst.getOperand(Index).isImm()) {
248+
ImmOpNo = Index;
249+
break;
250+
}
251+
}
252+
253+
if (ImmOpNo == -1U)
254+
return false;
255+
256+
Value = Inst.getOperand(ImmOpNo).getImm();
257+
setOperandToSymbolRef(Inst, ImmOpNo, Symbol, Addend, Ctx, RelType);
258+
return true;
259+
}
260+
261+
const MCExpr *getTargetExprFor(MCInst &Inst, const MCExpr *Expr,
262+
MCContext &Ctx,
263+
uint64_t RelType) const override {
264+
switch (RelType) {
265+
default:
266+
return Expr;
267+
case ELF::R_RISCV_GOT_HI20:
268+
// The GOT is reused so no need to create GOT relocations
269+
case ELF::R_RISCV_PCREL_HI20:
270+
return RISCVMCExpr::create(Expr, RISCVMCExpr::VK_RISCV_PCREL_HI, Ctx);
271+
case ELF::R_RISCV_PCREL_LO12_I:
272+
return RISCVMCExpr::create(Expr, RISCVMCExpr::VK_RISCV_PCREL_LO, Ctx);
273+
case ELF::R_RISCV_CALL:
274+
return RISCVMCExpr::create(Expr, RISCVMCExpr::VK_RISCV_CALL, Ctx);
275+
case ELF::R_RISCV_CALL_PLT:
276+
return RISCVMCExpr::create(Expr, RISCVMCExpr::VK_RISCV_CALL_PLT, Ctx);
277+
}
278+
}
279+
280+
bool evaluateMemOperandTarget(const MCInst &Inst, uint64_t &Target,
281+
uint64_t Address,
282+
uint64_t Size) const override {
283+
return false;
284+
}
285+
286+
bool isCallAuipc(const MCInst &Inst) const {
287+
if (Inst.getOpcode() != RISCV::AUIPC)
288+
return false;
289+
290+
const auto &ImmOp = Inst.getOperand(1);
291+
if (!ImmOp.isExpr())
292+
return false;
293+
294+
const auto *ImmExpr = ImmOp.getExpr();
295+
if (!isa<RISCVMCExpr>(ImmExpr))
296+
return false;
297+
298+
switch (cast<RISCVMCExpr>(ImmExpr)->getKind()) {
299+
default:
300+
return false;
301+
case RISCVMCExpr::VK_RISCV_CALL:
302+
case RISCVMCExpr::VK_RISCV_CALL_PLT:
303+
return true;
304+
}
305+
}
306+
307+
bool isRISCVCall(const MCInst &First, const MCInst &Second) const override {
308+
if (!isCallAuipc(First))
309+
return false;
310+
311+
assert(Second.getOpcode() == RISCV::JALR);
312+
return true;
313+
}
314+
};
315+
316+
} // end anonymous namespace
317+
318+
namespace llvm {
319+
namespace bolt {
320+
321+
MCPlusBuilder *createRISCVMCPlusBuilder(const MCInstrAnalysis *Analysis,
322+
const MCInstrInfo *Info,
323+
const MCRegisterInfo *RegInfo) {
324+
return new RISCVMCPlusBuilder(Analysis, Info, RegInfo);
325+
}
326+
327+
} // namespace bolt
328+
} // namespace llvm

‎bolt/test/RISCV/Inputs/plt-gnu-ld.yaml

Lines changed: 830 additions & 0 deletions
Large diffs are not rendered by default.

‎bolt/test/RISCV/lit.local.cfg

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
if 'RISCV' not in config.root.targets:
2+
config.unsupported = True
3+
4+
flags = '--target=riscv64 -nostdlib -ffreestanding -Wl,--no-relax,--emit-relocs'
5+
6+
config.substitutions.insert(0, ('%cflags', f'%cflags {flags}'))
7+
config.substitutions.insert(0, ('%cxxflags', f'%cxxflags {flags}'))

‎bolt/test/RISCV/plt-gnu-ld.test

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// This test checks that the PLT symbols are properly recognized
2+
// by the BOLT tool.
3+
4+
// RUN: yaml2obj %p/Inputs/plt-gnu-ld.yaml &> %t.exe
5+
// RUN: llvm-bolt %t.exe -o %t.bolt.exe --print-cfg --print-only=main \
6+
// RUN: | FileCheck %s
7+
8+
// CHECK: Binary Function "main" after building cfg {
9+
// CHECK: auipc ra, puts@PLT

‎bolt/test/RISCV/reloc-abs.s

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: %clang %cflags -Wl,--defsym='__global_pointer$'=0x2800 -o %t %s
2+
// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \
3+
// RUN: | FileCheck %s
4+
5+
.data
6+
.globl d
7+
.p2align 3
8+
d:
9+
.dword 0
10+
11+
.text
12+
.globl _start
13+
.p2align 1
14+
// CHECK: Binary Function "_start" after building cfg {
15+
_start:
16+
nop
17+
.option push
18+
.option norelax
19+
1:
20+
// CHECK: .Ltmp0
21+
// CHECK: auipc gp, %pcrel_hi(__global_pointer$)
22+
// CHECK-NEXT: addi gp, gp, %pcrel_lo(.Ltmp0)
23+
auipc gp, %pcrel_hi(__global_pointer$)
24+
addi gp, gp, %pcrel_lo(1b)
25+
.option pop
26+
.size _start, .-_start

‎bolt/test/RISCV/reloc-branch.s

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %clang %cflags -o %t %s
2+
// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \
3+
// RUN: | FileCheck %s
4+
5+
.text
6+
.globl _start
7+
.p2align 1
8+
// CHECK: Binary Function "_start" after building cfg {
9+
_start:
10+
// CHECK: beq zero, zero, .Ltmp0
11+
beq zero, zero, 1f
12+
nop
13+
// CHECK: .Ltmp0
14+
1:
15+
ret
16+
.size _start, .-_start

‎bolt/test/RISCV/reloc-call.s

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %clang %cflags -o %t %s
2+
// RUN: llvm-bolt --print-fix-riscv-calls --print-only=_start -o /dev/null %t \
3+
// RUN: | FileCheck %s
4+
5+
.text
6+
7+
.global f
8+
.p2align 1
9+
f:
10+
ret
11+
.size f, .-f
12+
13+
// CHECK: Binary Function "_start" after fix-riscv-calls {
14+
.globl _start
15+
.p2align 1
16+
_start:
17+
// CHECK: auipc ra, f
18+
// CHECK-NEXT: jalr ra
19+
call f
20+
ret
21+
.size _start, .-_start

‎bolt/test/RISCV/reloc-got.s

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %clang %cflags -o %t %s
2+
// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \
3+
// RUN: | FileCheck %s
4+
5+
.data
6+
.globl d
7+
.p2align 3
8+
d:
9+
.dword 0
10+
11+
.text
12+
.globl _start
13+
.p2align 1
14+
// CHECK: Binary Function "_start" after building cfg {
15+
_start:
16+
nop // Here to not make the _start and .Ltmp0 symbols coincide
17+
// CHECK: .Ltmp0
18+
// CHECK: auipc t0, %pcrel_hi(__BOLT_got_zero+{{[0-9]+}})
19+
// CHECK-NEXT: ld t0, %pcrel_lo(.Ltmp0)(t0)
20+
1:
21+
auipc t0, %got_pcrel_hi(d)
22+
ld t0, %pcrel_lo(1b)(t0)
23+
ret
24+
.size _start, .-_start

‎bolt/test/RISCV/reloc-jal.s

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// RUN: %clang %cflags -o %t %s
2+
// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \
3+
// RUN: | FileCheck %s
4+
5+
.text
6+
7+
.global f
8+
.p2align 1
9+
f:
10+
ret
11+
.size f, .-f
12+
13+
// CHECK: Binary Function "_start" after building cfg {
14+
.globl _start
15+
.p2align 1
16+
_start:
17+
// CHECK: jal ra, f
18+
jal ra, f
19+
ret
20+
.size _start, .-_start

‎bolt/test/RISCV/reloc-pcrel.s

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %clang %cflags -o %t %s
2+
// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \
3+
// RUN: | FileCheck %s
4+
5+
.data
6+
.globl d
7+
.p2align 3
8+
d:
9+
.dword 0
10+
11+
.text
12+
.globl _start
13+
.p2align 1
14+
// CHECK: Binary Function "_start" after building cfg {
15+
_start:
16+
nop // Here to not make the _start and .Ltmp0 symbols coincide
17+
// CHECK: .Ltmp0
18+
// CHECK: auipc t0, %pcrel_hi(d)
19+
// CHECK-NEXT: ld t0, %pcrel_lo(.Ltmp0)(t0)
20+
ld t0, d
21+
ret
22+
.size _start, .-_start

‎bolt/test/RISCV/reloc-rvc-branch.s

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %clang %cflags -o %t %s
2+
// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \
3+
// RUN: | FileCheck %s
4+
5+
.text
6+
.globl _start
7+
.p2align 1
8+
// CHECK: Binary Function "_start" after building cfg {
9+
_start:
10+
// CHECK: beqz a0, .Ltmp0
11+
c.beqz a0, 1f
12+
nop
13+
// CHECK: .Ltmp0
14+
1:
15+
ret
16+
.size _start, .-_start

‎bolt/test/RISCV/reloc-rvc-jump.s

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %clang %cflags -o %t %s
2+
// RUN: llvm-bolt --print-cfg --print-only=_start -o /dev/null %t \
3+
// RUN: | FileCheck %s
4+
5+
.text
6+
.globl _start
7+
.p2align 1
8+
// CHECK: Binary Function "_start" after building cfg {
9+
_start:
10+
// CHECK: j .Ltmp0
11+
c.j 1f
12+
nop
13+
// CHECK: .Ltmp0
14+
1:
15+
ret
16+
.size _start, .-_start

0 commit comments

Comments
 (0)
Please sign in to comment.