1*fe6060f1SDimitry Andric //===------- ELF_riscv.cpp -JIT linker implementation for ELF/riscv -------===// 2*fe6060f1SDimitry Andric // 3*fe6060f1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*fe6060f1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*fe6060f1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*fe6060f1SDimitry Andric // 7*fe6060f1SDimitry Andric //===----------------------------------------------------------------------===// 8*fe6060f1SDimitry Andric // 9*fe6060f1SDimitry Andric // ELF/riscv jit-link implementation. 10*fe6060f1SDimitry Andric // 11*fe6060f1SDimitry Andric //===----------------------------------------------------------------------===// 12*fe6060f1SDimitry Andric 13*fe6060f1SDimitry Andric #include "llvm/ExecutionEngine/JITLink/ELF_riscv.h" 14*fe6060f1SDimitry Andric #include "llvm/ExecutionEngine/JITLink/JITLink.h" 15*fe6060f1SDimitry Andric #include "llvm/ExecutionEngine/JITLink/riscv.h" 16*fe6060f1SDimitry Andric #include "llvm/Object/ELF.h" 17*fe6060f1SDimitry Andric #include "llvm/Object/ELFObjectFile.h" 18*fe6060f1SDimitry Andric 19*fe6060f1SDimitry Andric #include "ELFLinkGraphBuilder.h" 20*fe6060f1SDimitry Andric #include "JITLinkGeneric.h" 21*fe6060f1SDimitry Andric 22*fe6060f1SDimitry Andric #define DEBUG_TYPE "jitlink" 23*fe6060f1SDimitry Andric using namespace llvm; 24*fe6060f1SDimitry Andric 25*fe6060f1SDimitry Andric namespace llvm { 26*fe6060f1SDimitry Andric namespace jitlink { 27*fe6060f1SDimitry Andric 28*fe6060f1SDimitry Andric static Expected<const Edge &> getRISCVPCRelHi20(const Edge &E) { 29*fe6060f1SDimitry Andric using namespace riscv; 30*fe6060f1SDimitry Andric assert((E.getKind() == R_RISCV_PCREL_LO12_I || 31*fe6060f1SDimitry Andric E.getKind() == R_RISCV_PCREL_LO12_S) && 32*fe6060f1SDimitry Andric "Can only have high relocation for R_RISCV_PCREL_LO12_I or " 33*fe6060f1SDimitry Andric "R_RISCV_PCREL_LO12_S"); 34*fe6060f1SDimitry Andric 35*fe6060f1SDimitry Andric const Symbol &Sym = E.getTarget(); 36*fe6060f1SDimitry Andric const Block &B = Sym.getBlock(); 37*fe6060f1SDimitry Andric JITTargetAddress Offset = Sym.getOffset(); 38*fe6060f1SDimitry Andric 39*fe6060f1SDimitry Andric struct Comp { 40*fe6060f1SDimitry Andric bool operator()(const Edge &Lhs, JITTargetAddress Offset) { 41*fe6060f1SDimitry Andric return Lhs.getOffset() < Offset; 42*fe6060f1SDimitry Andric } 43*fe6060f1SDimitry Andric bool operator()(JITTargetAddress Offset, const Edge &Rhs) { 44*fe6060f1SDimitry Andric return Offset < Rhs.getOffset(); 45*fe6060f1SDimitry Andric } 46*fe6060f1SDimitry Andric }; 47*fe6060f1SDimitry Andric 48*fe6060f1SDimitry Andric auto Bound = 49*fe6060f1SDimitry Andric std::equal_range(B.edges().begin(), B.edges().end(), Offset, Comp{}); 50*fe6060f1SDimitry Andric 51*fe6060f1SDimitry Andric for (auto It = Bound.first; It != Bound.second; ++It) { 52*fe6060f1SDimitry Andric if (It->getKind() == R_RISCV_PCREL_HI20) 53*fe6060f1SDimitry Andric return *It; 54*fe6060f1SDimitry Andric } 55*fe6060f1SDimitry Andric 56*fe6060f1SDimitry Andric return make_error<JITLinkError>( 57*fe6060f1SDimitry Andric "No HI20 PCREL relocation type be found for LO12 PCREL relocation type"); 58*fe6060f1SDimitry Andric } 59*fe6060f1SDimitry Andric 60*fe6060f1SDimitry Andric static uint32_t extractBits(uint64_t Num, unsigned High, unsigned Low) { 61*fe6060f1SDimitry Andric return (Num & ((1ULL << (High + 1)) - 1)) >> Low; 62*fe6060f1SDimitry Andric } 63*fe6060f1SDimitry Andric 64*fe6060f1SDimitry Andric class ELFJITLinker_riscv : public JITLinker<ELFJITLinker_riscv> { 65*fe6060f1SDimitry Andric friend class JITLinker<ELFJITLinker_riscv>; 66*fe6060f1SDimitry Andric 67*fe6060f1SDimitry Andric public: 68*fe6060f1SDimitry Andric ELFJITLinker_riscv(std::unique_ptr<JITLinkContext> Ctx, 69*fe6060f1SDimitry Andric std::unique_ptr<LinkGraph> G, PassConfiguration PassConfig) 70*fe6060f1SDimitry Andric : JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {} 71*fe6060f1SDimitry Andric 72*fe6060f1SDimitry Andric private: 73*fe6060f1SDimitry Andric Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const { 74*fe6060f1SDimitry Andric using namespace riscv; 75*fe6060f1SDimitry Andric using namespace llvm::support; 76*fe6060f1SDimitry Andric 77*fe6060f1SDimitry Andric char *BlockWorkingMem = B.getAlreadyMutableContent().data(); 78*fe6060f1SDimitry Andric char *FixupPtr = BlockWorkingMem + E.getOffset(); 79*fe6060f1SDimitry Andric JITTargetAddress FixupAddress = B.getAddress() + E.getOffset(); 80*fe6060f1SDimitry Andric switch (E.getKind()) { 81*fe6060f1SDimitry Andric case R_RISCV_HI20: { 82*fe6060f1SDimitry Andric int64_t Value = E.getTarget().getAddress() + E.getAddend(); 83*fe6060f1SDimitry Andric int32_t Hi = (Value + 0x800) & 0xFFFFF000; 84*fe6060f1SDimitry Andric uint32_t RawInstr = *(little32_t *)FixupPtr; 85*fe6060f1SDimitry Andric *(little32_t *)FixupPtr = (RawInstr & 0xFFF) | static_cast<uint32_t>(Hi); 86*fe6060f1SDimitry Andric break; 87*fe6060f1SDimitry Andric } 88*fe6060f1SDimitry Andric case R_RISCV_LO12_I: { 89*fe6060f1SDimitry Andric int64_t Value = E.getTarget().getAddress() + E.getAddend(); 90*fe6060f1SDimitry Andric int32_t Lo = Value & 0xFFF; 91*fe6060f1SDimitry Andric uint32_t RawInstr = *(little32_t *)FixupPtr; 92*fe6060f1SDimitry Andric *(little32_t *)FixupPtr = 93*fe6060f1SDimitry Andric (RawInstr & 0xFFFFF) | (static_cast<uint32_t>(Lo & 0xFFF) << 20); 94*fe6060f1SDimitry Andric break; 95*fe6060f1SDimitry Andric } 96*fe6060f1SDimitry Andric case R_RISCV_CALL: { 97*fe6060f1SDimitry Andric int64_t Value = E.getTarget().getAddress() + E.getAddend() - FixupAddress; 98*fe6060f1SDimitry Andric int32_t Hi = (Value + 0x800) & 0xFFFFF000; 99*fe6060f1SDimitry Andric int32_t Lo = Value & 0xFFF; 100*fe6060f1SDimitry Andric uint32_t RawInstrAuipc = *(little32_t *)FixupPtr; 101*fe6060f1SDimitry Andric uint32_t RawInstrJalr = *(little32_t *)(FixupPtr + 4); 102*fe6060f1SDimitry Andric *(little32_t *)FixupPtr = RawInstrAuipc | static_cast<uint32_t>(Hi); 103*fe6060f1SDimitry Andric *(little32_t *)(FixupPtr + 4) = 104*fe6060f1SDimitry Andric RawInstrJalr | (static_cast<uint32_t>(Lo) << 20); 105*fe6060f1SDimitry Andric break; 106*fe6060f1SDimitry Andric } 107*fe6060f1SDimitry Andric case R_RISCV_PCREL_HI20: { 108*fe6060f1SDimitry Andric int64_t Value = E.getTarget().getAddress() + E.getAddend() - FixupAddress; 109*fe6060f1SDimitry Andric int32_t Hi = (Value + 0x800) & 0xFFFFF000; 110*fe6060f1SDimitry Andric uint32_t RawInstr = *(little32_t *)FixupPtr; 111*fe6060f1SDimitry Andric *(little32_t *)FixupPtr = (RawInstr & 0xFFF) | static_cast<uint32_t>(Hi); 112*fe6060f1SDimitry Andric break; 113*fe6060f1SDimitry Andric } 114*fe6060f1SDimitry Andric case R_RISCV_PCREL_LO12_I: { 115*fe6060f1SDimitry Andric auto RelHI20 = getRISCVPCRelHi20(E); 116*fe6060f1SDimitry Andric if (!RelHI20) 117*fe6060f1SDimitry Andric return RelHI20.takeError(); 118*fe6060f1SDimitry Andric int64_t Value = RelHI20->getTarget().getAddress() + 119*fe6060f1SDimitry Andric RelHI20->getAddend() - E.getTarget().getAddress(); 120*fe6060f1SDimitry Andric int64_t Lo = Value & 0xFFF; 121*fe6060f1SDimitry Andric uint32_t RawInstr = *(little32_t *)FixupPtr; 122*fe6060f1SDimitry Andric *(little32_t *)FixupPtr = 123*fe6060f1SDimitry Andric (RawInstr & 0xFFFFF) | (static_cast<uint32_t>(Lo & 0xFFF) << 20); 124*fe6060f1SDimitry Andric break; 125*fe6060f1SDimitry Andric } 126*fe6060f1SDimitry Andric case R_RISCV_PCREL_LO12_S: { 127*fe6060f1SDimitry Andric auto RelHI20 = getRISCVPCRelHi20(E); 128*fe6060f1SDimitry Andric int64_t Value = RelHI20->getTarget().getAddress() + 129*fe6060f1SDimitry Andric RelHI20->getAddend() - E.getTarget().getAddress(); 130*fe6060f1SDimitry Andric int64_t Lo = Value & 0xFFF; 131*fe6060f1SDimitry Andric uint32_t Imm31_25 = extractBits(Lo, 11, 5) << 25; 132*fe6060f1SDimitry Andric uint32_t Imm11_7 = extractBits(Lo, 4, 0) << 7; 133*fe6060f1SDimitry Andric uint32_t RawInstr = *(little32_t *)FixupPtr; 134*fe6060f1SDimitry Andric 135*fe6060f1SDimitry Andric *(little32_t *)FixupPtr = (RawInstr & 0x1FFF07F) | Imm31_25 | Imm11_7; 136*fe6060f1SDimitry Andric break; 137*fe6060f1SDimitry Andric } 138*fe6060f1SDimitry Andric } 139*fe6060f1SDimitry Andric return Error::success(); 140*fe6060f1SDimitry Andric } 141*fe6060f1SDimitry Andric }; 142*fe6060f1SDimitry Andric 143*fe6060f1SDimitry Andric template <typename ELFT> 144*fe6060f1SDimitry Andric class ELFLinkGraphBuilder_riscv : public ELFLinkGraphBuilder<ELFT> { 145*fe6060f1SDimitry Andric private: 146*fe6060f1SDimitry Andric static Expected<riscv::EdgeKind_riscv> 147*fe6060f1SDimitry Andric getRelocationKind(const uint32_t Type) { 148*fe6060f1SDimitry Andric using namespace riscv; 149*fe6060f1SDimitry Andric switch (Type) { 150*fe6060f1SDimitry Andric case ELF::R_RISCV_32: 151*fe6060f1SDimitry Andric return EdgeKind_riscv::R_RISCV_32; 152*fe6060f1SDimitry Andric case ELF::R_RISCV_64: 153*fe6060f1SDimitry Andric return EdgeKind_riscv::R_RISCV_64; 154*fe6060f1SDimitry Andric case ELF::R_RISCV_HI20: 155*fe6060f1SDimitry Andric return EdgeKind_riscv::R_RISCV_HI20; 156*fe6060f1SDimitry Andric case ELF::R_RISCV_LO12_I: 157*fe6060f1SDimitry Andric return EdgeKind_riscv::R_RISCV_LO12_I; 158*fe6060f1SDimitry Andric case ELF::R_RISCV_CALL: 159*fe6060f1SDimitry Andric return EdgeKind_riscv::R_RISCV_CALL; 160*fe6060f1SDimitry Andric case ELF::R_RISCV_PCREL_HI20: 161*fe6060f1SDimitry Andric return EdgeKind_riscv::R_RISCV_PCREL_HI20; 162*fe6060f1SDimitry Andric case ELF::R_RISCV_PCREL_LO12_I: 163*fe6060f1SDimitry Andric return EdgeKind_riscv::R_RISCV_PCREL_LO12_I; 164*fe6060f1SDimitry Andric case ELF::R_RISCV_PCREL_LO12_S: 165*fe6060f1SDimitry Andric return EdgeKind_riscv::R_RISCV_PCREL_LO12_S; 166*fe6060f1SDimitry Andric } 167*fe6060f1SDimitry Andric 168*fe6060f1SDimitry Andric return make_error<JITLinkError>("Unsupported riscv relocation:" + 169*fe6060f1SDimitry Andric formatv("{0:d}", Type)); 170*fe6060f1SDimitry Andric } 171*fe6060f1SDimitry Andric 172*fe6060f1SDimitry Andric Error addRelocations() override { 173*fe6060f1SDimitry Andric using Base = ELFLinkGraphBuilder<ELFT>; 174*fe6060f1SDimitry Andric LLVM_DEBUG(dbgs() << "Adding relocations\n"); 175*fe6060f1SDimitry Andric 176*fe6060f1SDimitry Andric // TODO a partern is forming of iterate some sections but only give me 177*fe6060f1SDimitry Andric // ones I am interested, I should abstract that concept some where 178*fe6060f1SDimitry Andric for (auto &SecRef : Base::Sections) { 179*fe6060f1SDimitry Andric if (SecRef.sh_type != ELF::SHT_RELA && SecRef.sh_type != ELF::SHT_REL) 180*fe6060f1SDimitry Andric continue; 181*fe6060f1SDimitry Andric auto RelSectName = Base::Obj.getSectionName(SecRef); 182*fe6060f1SDimitry Andric if (!RelSectName) 183*fe6060f1SDimitry Andric return RelSectName.takeError(); 184*fe6060f1SDimitry Andric 185*fe6060f1SDimitry Andric LLVM_DEBUG({ 186*fe6060f1SDimitry Andric dbgs() << "Adding relocations from section " << *RelSectName << "\n"; 187*fe6060f1SDimitry Andric }); 188*fe6060f1SDimitry Andric 189*fe6060f1SDimitry Andric auto UpdateSection = Base::Obj.getSection(SecRef.sh_info); 190*fe6060f1SDimitry Andric if (!UpdateSection) 191*fe6060f1SDimitry Andric return UpdateSection.takeError(); 192*fe6060f1SDimitry Andric 193*fe6060f1SDimitry Andric auto UpdateSectionName = Base::Obj.getSectionName(**UpdateSection); 194*fe6060f1SDimitry Andric if (!UpdateSectionName) 195*fe6060f1SDimitry Andric return UpdateSectionName.takeError(); 196*fe6060f1SDimitry Andric // Don't process relocations for debug sections. 197*fe6060f1SDimitry Andric if (Base::isDwarfSection(*UpdateSectionName)) { 198*fe6060f1SDimitry Andric LLVM_DEBUG({ 199*fe6060f1SDimitry Andric dbgs() << " Target is dwarf section " << *UpdateSectionName 200*fe6060f1SDimitry Andric << ". Skipping.\n"; 201*fe6060f1SDimitry Andric }); 202*fe6060f1SDimitry Andric continue; 203*fe6060f1SDimitry Andric } else 204*fe6060f1SDimitry Andric LLVM_DEBUG({ 205*fe6060f1SDimitry Andric dbgs() << " For target section " << *UpdateSectionName << "\n"; 206*fe6060f1SDimitry Andric }); 207*fe6060f1SDimitry Andric 208*fe6060f1SDimitry Andric auto *JITSection = Base::G->findSectionByName(*UpdateSectionName); 209*fe6060f1SDimitry Andric if (!JITSection) 210*fe6060f1SDimitry Andric return make_error<llvm::StringError>( 211*fe6060f1SDimitry Andric "Refencing a section that wasn't added to graph" + 212*fe6060f1SDimitry Andric *UpdateSectionName, 213*fe6060f1SDimitry Andric llvm::inconvertibleErrorCode()); 214*fe6060f1SDimitry Andric 215*fe6060f1SDimitry Andric auto Relocations = Base::Obj.relas(SecRef); 216*fe6060f1SDimitry Andric if (!Relocations) 217*fe6060f1SDimitry Andric return Relocations.takeError(); 218*fe6060f1SDimitry Andric 219*fe6060f1SDimitry Andric for (const auto &Rela : *Relocations) { 220*fe6060f1SDimitry Andric auto Type = Rela.getType(false); 221*fe6060f1SDimitry Andric 222*fe6060f1SDimitry Andric LLVM_DEBUG({ 223*fe6060f1SDimitry Andric dbgs() << "Relocation Type: " << Type << "\n" 224*fe6060f1SDimitry Andric << "Name: " << Base::Obj.getRelocationTypeName(Type) << "\n"; 225*fe6060f1SDimitry Andric }); 226*fe6060f1SDimitry Andric 227*fe6060f1SDimitry Andric auto SymbolIndex = Rela.getSymbol(false); 228*fe6060f1SDimitry Andric auto Symbol = Base::Obj.getRelocationSymbol(Rela, Base::SymTabSec); 229*fe6060f1SDimitry Andric if (!Symbol) 230*fe6060f1SDimitry Andric return Symbol.takeError(); 231*fe6060f1SDimitry Andric 232*fe6060f1SDimitry Andric auto BlockToFix = *(JITSection->blocks().begin()); 233*fe6060f1SDimitry Andric auto *TargetSymbol = Base::getGraphSymbol(SymbolIndex); 234*fe6060f1SDimitry Andric 235*fe6060f1SDimitry Andric if (!TargetSymbol) { 236*fe6060f1SDimitry Andric return make_error<llvm::StringError>( 237*fe6060f1SDimitry Andric "Could not find symbol at given index, did you add it to " 238*fe6060f1SDimitry Andric "JITSymbolTable? index: " + 239*fe6060f1SDimitry Andric std::to_string(SymbolIndex) + ", shndx: " + 240*fe6060f1SDimitry Andric std::to_string((*Symbol)->st_shndx) + " Size of table: " + 241*fe6060f1SDimitry Andric std::to_string(Base::GraphSymbols.size()), 242*fe6060f1SDimitry Andric llvm::inconvertibleErrorCode()); 243*fe6060f1SDimitry Andric } 244*fe6060f1SDimitry Andric int64_t Addend = Rela.r_addend; 245*fe6060f1SDimitry Andric JITTargetAddress FixupAddress = 246*fe6060f1SDimitry Andric (*UpdateSection)->sh_addr + Rela.r_offset; 247*fe6060f1SDimitry Andric 248*fe6060f1SDimitry Andric LLVM_DEBUG({ 249*fe6060f1SDimitry Andric dbgs() << "Processing relocation at " 250*fe6060f1SDimitry Andric << format("0x%016" PRIx64, FixupAddress) << "\n"; 251*fe6060f1SDimitry Andric }); 252*fe6060f1SDimitry Andric auto Kind = getRelocationKind(Type); 253*fe6060f1SDimitry Andric if (!Kind) 254*fe6060f1SDimitry Andric return Kind.takeError(); 255*fe6060f1SDimitry Andric 256*fe6060f1SDimitry Andric BlockToFix->addEdge(*Kind, FixupAddress - BlockToFix->getAddress(), 257*fe6060f1SDimitry Andric *TargetSymbol, Addend); 258*fe6060f1SDimitry Andric } 259*fe6060f1SDimitry Andric } 260*fe6060f1SDimitry Andric return Error::success(); 261*fe6060f1SDimitry Andric } 262*fe6060f1SDimitry Andric 263*fe6060f1SDimitry Andric public: 264*fe6060f1SDimitry Andric ELFLinkGraphBuilder_riscv(StringRef FileName, 265*fe6060f1SDimitry Andric const object::ELFFile<ELFT> &Obj, const Triple T) 266*fe6060f1SDimitry Andric : ELFLinkGraphBuilder<ELFT>(Obj, std::move(T), FileName, 267*fe6060f1SDimitry Andric riscv::getEdgeKindName) {} 268*fe6060f1SDimitry Andric }; 269*fe6060f1SDimitry Andric 270*fe6060f1SDimitry Andric Expected<std::unique_ptr<LinkGraph>> 271*fe6060f1SDimitry Andric createLinkGraphFromELFObject_riscv(MemoryBufferRef ObjectBuffer) { 272*fe6060f1SDimitry Andric LLVM_DEBUG({ 273*fe6060f1SDimitry Andric dbgs() << "Building jitlink graph for new input " 274*fe6060f1SDimitry Andric << ObjectBuffer.getBufferIdentifier() << "...\n"; 275*fe6060f1SDimitry Andric }); 276*fe6060f1SDimitry Andric 277*fe6060f1SDimitry Andric auto ELFObj = object::ObjectFile::createELFObjectFile(ObjectBuffer); 278*fe6060f1SDimitry Andric if (!ELFObj) 279*fe6060f1SDimitry Andric return ELFObj.takeError(); 280*fe6060f1SDimitry Andric 281*fe6060f1SDimitry Andric if ((*ELFObj)->getArch() == Triple::riscv64) { 282*fe6060f1SDimitry Andric auto &ELFObjFile = cast<object::ELFObjectFile<object::ELF64LE>>(**ELFObj); 283*fe6060f1SDimitry Andric return ELFLinkGraphBuilder_riscv<object::ELF64LE>( 284*fe6060f1SDimitry Andric (*ELFObj)->getFileName(), ELFObjFile.getELFFile(), 285*fe6060f1SDimitry Andric (*ELFObj)->makeTriple()) 286*fe6060f1SDimitry Andric .buildGraph(); 287*fe6060f1SDimitry Andric } else { 288*fe6060f1SDimitry Andric assert((*ELFObj)->getArch() == Triple::riscv32 && 289*fe6060f1SDimitry Andric "Invalid triple for RISCV ELF object file"); 290*fe6060f1SDimitry Andric auto &ELFObjFile = cast<object::ELFObjectFile<object::ELF32LE>>(**ELFObj); 291*fe6060f1SDimitry Andric return ELFLinkGraphBuilder_riscv<object::ELF32LE>( 292*fe6060f1SDimitry Andric (*ELFObj)->getFileName(), ELFObjFile.getELFFile(), 293*fe6060f1SDimitry Andric (*ELFObj)->makeTriple()) 294*fe6060f1SDimitry Andric .buildGraph(); 295*fe6060f1SDimitry Andric } 296*fe6060f1SDimitry Andric } 297*fe6060f1SDimitry Andric 298*fe6060f1SDimitry Andric void link_ELF_riscv(std::unique_ptr<LinkGraph> G, 299*fe6060f1SDimitry Andric std::unique_ptr<JITLinkContext> Ctx) { 300*fe6060f1SDimitry Andric PassConfiguration Config; 301*fe6060f1SDimitry Andric const Triple &TT = G->getTargetTriple(); 302*fe6060f1SDimitry Andric if (Ctx->shouldAddDefaultTargetPasses(TT)) { 303*fe6060f1SDimitry Andric if (auto MarkLive = Ctx->getMarkLivePass(TT)) 304*fe6060f1SDimitry Andric Config.PrePrunePasses.push_back(std::move(MarkLive)); 305*fe6060f1SDimitry Andric else 306*fe6060f1SDimitry Andric Config.PrePrunePasses.push_back(markAllSymbolsLive); 307*fe6060f1SDimitry Andric } 308*fe6060f1SDimitry Andric if (auto Err = Ctx->modifyPassConfig(*G, Config)) 309*fe6060f1SDimitry Andric return Ctx->notifyFailed(std::move(Err)); 310*fe6060f1SDimitry Andric 311*fe6060f1SDimitry Andric ELFJITLinker_riscv::link(std::move(Ctx), std::move(G), std::move(Config)); 312*fe6060f1SDimitry Andric } 313*fe6060f1SDimitry Andric 314*fe6060f1SDimitry Andric } // namespace jitlink 315*fe6060f1SDimitry Andric } // namespace llvm 316