//===------- JITLinkTestCommon.cpp - Common code for JITLink tests --------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "JITLinkTestCommon.h" #include "llvm/MC/MCCodeEmitter.h" #include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCParser/MCTargetAsmParser.h" #include "llvm/Support/TargetSelect.h" using namespace llvm::jitlink; namespace llvm { Expected> JITLinkTestCommon::TestResources::Create(StringRef AsmSrc, StringRef TripleStr, bool PIC, bool LargeCodeModel, MCTargetOptions Options) { Error Err = Error::success(); auto R = std::unique_ptr(new TestResources( AsmSrc, TripleStr, PIC, LargeCodeModel, std::move(Options), Err)); if (Err) return std::move(Err); return std::move(R); } MemoryBufferRef JITLinkTestCommon::TestResources::getTestObjectBufferRef() const { return MemoryBufferRef(StringRef(ObjBuffer.data(), ObjBuffer.size()), "Test object"); } JITLinkTestCommon::TestResources::TestResources(StringRef AsmSrc, StringRef TripleStr, bool PIC, bool LargeCodeModel, MCTargetOptions Options, Error &Err) : ObjStream(ObjBuffer), Options(std::move(Options)) { ErrorAsOutParameter _(&Err); Triple TT(Triple::normalize(TripleStr)); if (auto Err2 = initializeTripleSpecifics(TT)) { Err = std::move(Err2); return; } initializeTestSpecifics(AsmSrc, TT, PIC, LargeCodeModel); } Error JITLinkTestCommon::TestResources::initializeTripleSpecifics(Triple &TT) { std::string ErrorMsg; TheTarget = TargetRegistry::lookupTarget("", TT, ErrorMsg); if (!TheTarget) return make_error(ErrorMsg, inconvertibleErrorCode()); MRI.reset(TheTarget->createMCRegInfo(TT.getTriple())); if (!MRI) report_fatal_error("Could not build MCRegisterInfo for triple"); MAI.reset(TheTarget->createMCAsmInfo(*MRI, TT.getTriple())); if (!MAI) report_fatal_error("Could not build MCAsmInfo for triple"); MCII.reset(TheTarget->createMCInstrInfo()); if (!MCII) report_fatal_error("Could not build MCInstrInfo for triple"); STI.reset(TheTarget->createMCSubtargetInfo(TT.getTriple(), "", "")); if (!STI) report_fatal_error("Could not build MCSubtargetInfo for triple"); DisCtx = std::make_unique(MAI.get(), MRI.get(), nullptr); Dis.reset(TheTarget->createMCDisassembler(*STI, *DisCtx)); if (!Dis) report_fatal_error("Could not build MCDisassembler"); return Error::success(); } void JITLinkTestCommon::TestResources::initializeTestSpecifics( StringRef AsmSrc, const Triple &TT, bool PIC, bool LargeCodeModel) { SrcMgr.AddNewSourceBuffer(MemoryBuffer::getMemBuffer(AsmSrc), SMLoc()); AsCtx = std::make_unique(MAI.get(), MRI.get(), &MOFI, &SrcMgr); MOFI.InitMCObjectFileInfo(TT, PIC, *AsCtx, LargeCodeModel); std::unique_ptr CE( TheTarget->createMCCodeEmitter(*MCII, *MRI, *AsCtx)); if (!CE) report_fatal_error("Could not build MCCodeEmitter"); std::unique_ptr MAB( TheTarget->createMCAsmBackend(*STI, *MRI, Options)); if (!MAB) report_fatal_error("Could not build MCAsmBackend for test"); std::unique_ptr MOW(MAB->createObjectWriter(ObjStream)); MOS.reset(TheTarget->createMCObjectStreamer( TT, *AsCtx, std::move(MAB), std::move(MOW), std::move(CE), *STI, Options.MCRelaxAll, Options.MCIncrementalLinkerCompatible, false)); std::unique_ptr MAP( createMCAsmParser(SrcMgr, *AsCtx, *MOS, *MAI)); std::unique_ptr TAP( TheTarget->createMCAsmParser(*STI, *MAP, *MCII, Options)); if (!TAP) report_fatal_error("Could not build MCTargetAsmParser for test"); MAP->setTargetParser(*TAP); if (MAP->Run(false)) report_fatal_error("Failed to parse test case"); } JITLinkTestCommon::TestJITLinkContext::TestJITLinkContext( TestResources &TR, TestCaseFunction TestCase) : TR(TR), TestCase(std::move(TestCase)) {} JITLinkTestCommon::TestJITLinkContext & JITLinkTestCommon::TestJITLinkContext::setMemoryManager( std::unique_ptr MM) { assert(!MemMgr && "Memory manager already set"); MemMgr = std::move(MM); return *this; } JITLinkMemoryManager & JITLinkTestCommon::TestJITLinkContext::getMemoryManager() { if (!MemMgr) MemMgr = std::make_unique(); return *MemMgr; } MemoryBufferRef JITLinkTestCommon::TestJITLinkContext::getObjectBuffer() const { return TR.getTestObjectBufferRef(); } void JITLinkTestCommon::TestJITLinkContext::notifyFailed(Error Err) { ADD_FAILURE() << "Unexpected failure: " << toString(std::move(Err)); } void JITLinkTestCommon::TestJITLinkContext::lookup( const DenseSet &Symbols, std::unique_ptr LC) { jitlink::AsyncLookupResult LookupResult; DenseSet MissingSymbols; for (const auto &Symbol : Symbols) { auto I = Externals.find(Symbol); if (I != Externals.end()) LookupResult[Symbol] = I->second; else MissingSymbols.insert(Symbol); } if (MissingSymbols.empty()) LC->run(std::move(LookupResult)); else { std::string ErrMsg; { raw_string_ostream ErrMsgStream(ErrMsg); ErrMsgStream << "Failed to resolve external symbols: ["; for (auto &Sym : MissingSymbols) ErrMsgStream << " " << Sym; ErrMsgStream << " ]\n"; } LC->run( make_error(std::move(ErrMsg), inconvertibleErrorCode())); } } void JITLinkTestCommon::TestJITLinkContext::notifyResolved(LinkGraph &G) { if (NotifyResolved) NotifyResolved(G); } void JITLinkTestCommon::TestJITLinkContext::notifyFinalized( std::unique_ptr A) { if (NotifyFinalized) NotifyFinalized(std::move(A)); } Error JITLinkTestCommon::TestJITLinkContext::modifyPassConfig( const Triple &TT, PassConfiguration &Config) { if (TestCase) Config.PostFixupPasses.push_back([&](LinkGraph &G) -> Error { TestCase(G); return Error::success(); }); return Error::success(); } JITLinkTestCommon::JITLinkTestCommon() { initializeLLVMTargets(); } Expected> JITLinkTestCommon::disassemble(const MCDisassembler &Dis, jitlink::Block &B, size_t Offset) { ArrayRef InstBuffer( reinterpret_cast(B.getContent().data()) + Offset, B.getContent().size() - Offset); MCInst Inst; uint64_t InstSize; auto Status = Dis.getInstruction(Inst, InstSize, InstBuffer, 0, nulls(), nulls()); if (Status != MCDisassembler::Success) return make_error("Could not disassemble instruction", inconvertibleErrorCode()); return std::make_pair(Inst, InstSize); } Expected JITLinkTestCommon::decodeImmediateOperand( const MCDisassembler &Dis, jitlink::Block &B, size_t OpIdx, size_t Offset) { auto InstAndSize = disassemble(Dis, B, Offset); if (!InstAndSize) return InstAndSize.takeError(); if (OpIdx >= InstAndSize->first.getNumOperands()) return make_error("Invalid operand index", inconvertibleErrorCode()); auto &Op = InstAndSize->first.getOperand(OpIdx); if (!Op.isImm()) return make_error("Operand at index is not immediate", inconvertibleErrorCode()); return Op.getImm(); } bool JITLinkTestCommon::AreTargetsInitialized = false; void JITLinkTestCommon::initializeLLVMTargets() { if (!AreTargetsInitialized) { InitializeAllTargets(); InitializeAllTargetMCs(); InitializeAllAsmParsers(); InitializeAllAsmPrinters(); InitializeAllDisassemblers(); AreTargetsInitialized = true; } } } // end namespace llvm