1633ea072SLang Hames //===--------------- LLJITWithCustomObjectLinkingLayer.cpp ----------------===//
2633ea072SLang Hames //
3633ea072SLang Hames // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4633ea072SLang Hames // See https://llvm.org/LICENSE.txt for license information.
5633ea072SLang Hames // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6633ea072SLang Hames //
7633ea072SLang Hames //===----------------------------------------------------------------------===//
8633ea072SLang Hames //
9633ea072SLang Hames // This file shows how to switch LLJIT to use a custom object linking layer (we
10633ea072SLang Hames // use ObjectLinkingLayer, which is backed by JITLink, as an example).
11633ea072SLang Hames //
12633ea072SLang Hames //===----------------------------------------------------------------------===//
13633ea072SLang Hames
14633ea072SLang Hames #include "llvm/ADT/StringMap.h"
15633ea072SLang Hames #include "llvm/ExecutionEngine/JITLink/JITLink.h"
16633ea072SLang Hames #include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
174e30b20bSLang Hames #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
18633ea072SLang Hames #include "llvm/ExecutionEngine/Orc/LLJIT.h"
19633ea072SLang Hames #include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
20633ea072SLang Hames #include "llvm/Support/InitLLVM.h"
21633ea072SLang Hames #include "llvm/Support/TargetSelect.h"
22633ea072SLang Hames #include "llvm/Support/raw_ostream.h"
23633ea072SLang Hames
24633ea072SLang Hames #include "../ExampleModules.h"
25633ea072SLang Hames
26633ea072SLang Hames using namespace llvm;
27633ea072SLang Hames using namespace llvm::orc;
28633ea072SLang Hames
29633ea072SLang Hames ExitOnError ExitOnErr;
30633ea072SLang Hames
31633ea072SLang Hames const llvm::StringRef TestMod =
32633ea072SLang Hames R"(
33633ea072SLang Hames define i32 @callee() {
34633ea072SLang Hames entry:
35633ea072SLang Hames ret i32 7
36633ea072SLang Hames }
37633ea072SLang Hames
38633ea072SLang Hames define i32 @entry() {
39633ea072SLang Hames entry:
40633ea072SLang Hames %0 = call i32 @callee()
41633ea072SLang Hames ret i32 %0
42633ea072SLang Hames }
43633ea072SLang Hames )";
44633ea072SLang Hames
45633ea072SLang Hames class MyPlugin : public ObjectLinkingLayer::Plugin {
46633ea072SLang Hames public:
47633ea072SLang Hames // The modifyPassConfig callback gives us a chance to inspect the
48633ea072SLang Hames // MaterializationResponsibility and target triple for the object being
49633ea072SLang Hames // linked, then add any JITLink passes that we would like to run on the
50633ea072SLang Hames // link graph. A pass is just a function object that is callable as
51633ea072SLang Hames // Error(jitlink::LinkGraph&). In this case we will add two passes
52633ea072SLang Hames // defined as lambdas that call the printLinkerGraph method on our
53633ea072SLang Hames // plugin: One to run before the linker applies fixups and another to
54633ea072SLang Hames // run afterwards.
modifyPassConfig(MaterializationResponsibility & MR,jitlink::LinkGraph & LG,jitlink::PassConfiguration & Config)554e30b20bSLang Hames void modifyPassConfig(MaterializationResponsibility &MR,
564e30b20bSLang Hames jitlink::LinkGraph &LG,
57633ea072SLang Hames jitlink::PassConfiguration &Config) override {
584e30b20bSLang Hames
594e30b20bSLang Hames outs() << "MyPlugin -- Modifying pass config for " << LG.getName() << " ("
604e30b20bSLang Hames << LG.getTargetTriple().str() << "):\n";
614e30b20bSLang Hames
624e30b20bSLang Hames // Print sections, symbol names and addresses, and any edges for the
6370c92126SLang Hames // associated blocks at the 'PostPrune' phase of JITLink (after
6470c92126SLang Hames // dead-stripping, but before addresses are allocated in the target
6570c92126SLang Hames // address space. See llvm/docs/JITLink.rst).
664e30b20bSLang Hames //
6770c92126SLang Hames // Experiment with adding the 'printGraph' pass at other points in the
6870c92126SLang Hames // pipeline. E.g. PrePrunePasses, PostAllocationPasses, and
6970c92126SLang Hames // PostFixupPasses.
7070c92126SLang Hames Config.PostPrunePasses.push_back(printGraph);
71633ea072SLang Hames }
72633ea072SLang Hames
notifyLoaded(MaterializationResponsibility & MR)73633ea072SLang Hames void notifyLoaded(MaterializationResponsibility &MR) override {
744e30b20bSLang Hames outs() << "Loading object defining " << MR.getSymbols() << "\n";
75633ea072SLang Hames }
76633ea072SLang Hames
notifyEmitted(MaterializationResponsibility & MR)77633ea072SLang Hames Error notifyEmitted(MaterializationResponsibility &MR) override {
784e30b20bSLang Hames outs() << "Emitted object defining " << MR.getSymbols() << "\n";
79633ea072SLang Hames return Error::success();
80633ea072SLang Hames }
81633ea072SLang Hames
notifyFailed(MaterializationResponsibility & MR)820aec49c8SLang Hames Error notifyFailed(MaterializationResponsibility &MR) override {
830aec49c8SLang Hames return Error::success();
840aec49c8SLang Hames }
850aec49c8SLang Hames
notifyRemovingResources(JITDylib & JD,ResourceKey K)86*bf6d7ca9SLang Hames Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override {
870aec49c8SLang Hames return Error::success();
880aec49c8SLang Hames }
890aec49c8SLang Hames
notifyTransferringResources(JITDylib & JD,ResourceKey DstKey,ResourceKey SrcKey)90*bf6d7ca9SLang Hames void notifyTransferringResources(JITDylib &JD, ResourceKey DstKey,
910aec49c8SLang Hames ResourceKey SrcKey) override {}
920aec49c8SLang Hames
93633ea072SLang Hames private:
printBlockContent(jitlink::Block & B)944e30b20bSLang Hames static void printBlockContent(jitlink::Block &B) {
95633ea072SLang Hames constexpr JITTargetAddress LineWidth = 16;
96633ea072SLang Hames
974e30b20bSLang Hames if (B.isZeroFill()) {
984e30b20bSLang Hames outs() << " " << formatv("{0:x16}", B.getAddress()) << ": "
994e30b20bSLang Hames << B.getSize() << " bytes of zero-fill.\n";
1004e30b20bSLang Hames return;
1014e30b20bSLang Hames }
102633ea072SLang Hames
103118e953bSLang Hames ExecutorAddr InitAddr(B.getAddress().getValue() & ~(LineWidth - 1));
104118e953bSLang Hames ExecutorAddr StartAddr = B.getAddress();
105118e953bSLang Hames ExecutorAddr EndAddr = B.getAddress() + B.getSize();
1064e30b20bSLang Hames auto *Data = reinterpret_cast<const uint8_t *>(B.getContent().data());
107633ea072SLang Hames
108118e953bSLang Hames for (ExecutorAddr CurAddr = InitAddr; CurAddr != EndAddr; ++CurAddr) {
109633ea072SLang Hames if (CurAddr % LineWidth == 0)
110118e953bSLang Hames outs() << " " << formatv("{0:x16}", CurAddr.getValue())
111118e953bSLang Hames << ": ";
112633ea072SLang Hames if (CurAddr < StartAddr)
1134e30b20bSLang Hames outs() << " ";
114633ea072SLang Hames else
1154e30b20bSLang Hames outs() << formatv("{0:x-2}", Data[CurAddr - StartAddr]) << " ";
116633ea072SLang Hames if (CurAddr % LineWidth == LineWidth - 1)
1174e30b20bSLang Hames outs() << "\n";
118633ea072SLang Hames }
119633ea072SLang Hames if (EndAddr % LineWidth != 0)
1204e30b20bSLang Hames outs() << "\n";
1214e30b20bSLang Hames }
1224e30b20bSLang Hames
printGraph(jitlink::LinkGraph & G)1234e30b20bSLang Hames static Error printGraph(jitlink::LinkGraph &G) {
1244e30b20bSLang Hames
1254e30b20bSLang Hames DenseSet<jitlink::Block *> BlocksAlreadyVisited;
1264e30b20bSLang Hames
1274e30b20bSLang Hames outs() << "Graph \"" << G.getName() << "\"\n";
1284e30b20bSLang Hames // Loop over all sections...
1294e30b20bSLang Hames for (auto &S : G.sections()) {
1304e30b20bSLang Hames outs() << " Section " << S.getName() << ":\n";
1314e30b20bSLang Hames
1324e30b20bSLang Hames // Loop over all symbols in the current section...
1334e30b20bSLang Hames for (auto *Sym : S.symbols()) {
1344e30b20bSLang Hames
1354e30b20bSLang Hames // Print the symbol's address.
1364e30b20bSLang Hames outs() << " " << formatv("{0:x16}", Sym->getAddress()) << ": ";
1374e30b20bSLang Hames
1384e30b20bSLang Hames // Print the symbol's name, or "<anonymous symbol>" if it doesn't have
1394e30b20bSLang Hames // one.
1404e30b20bSLang Hames if (Sym->hasName())
1414e30b20bSLang Hames outs() << Sym->getName() << "\n";
1424e30b20bSLang Hames else
1434e30b20bSLang Hames outs() << "<anonymous symbol>\n";
1444e30b20bSLang Hames
1454e30b20bSLang Hames // Get the content block for this symbol.
1464e30b20bSLang Hames auto &B = Sym->getBlock();
1474e30b20bSLang Hames
1484e30b20bSLang Hames if (BlocksAlreadyVisited.count(&B)) {
1494e30b20bSLang Hames outs() << " Block " << formatv("{0:x16}", B.getAddress())
1504e30b20bSLang Hames << " already printed.\n";
1514e30b20bSLang Hames continue;
1524e30b20bSLang Hames } else
1534e30b20bSLang Hames outs() << " Block " << formatv("{0:x16}", B.getAddress())
1544e30b20bSLang Hames << ":\n";
1554e30b20bSLang Hames
1564e30b20bSLang Hames outs() << " Content:\n";
1574e30b20bSLang Hames printBlockContent(B);
1584e30b20bSLang Hames BlocksAlreadyVisited.insert(&B);
1594e30b20bSLang Hames
160158ad803SLang Hames if (!B.edges().empty()) {
1614e30b20bSLang Hames outs() << " Edges:\n";
1624e30b20bSLang Hames for (auto &E : B.edges()) {
1634e30b20bSLang Hames outs() << " "
1644e30b20bSLang Hames << formatv("{0:x16}", B.getAddress() + E.getOffset())
1654e30b20bSLang Hames << ": kind = " << formatv("{0:d}", E.getKind())
1664e30b20bSLang Hames << ", addend = " << formatv("{0:x}", E.getAddend())
1674e30b20bSLang Hames << ", target = ";
1684e30b20bSLang Hames jitlink::Symbol &TargetSym = E.getTarget();
1694e30b20bSLang Hames if (TargetSym.hasName())
1704e30b20bSLang Hames outs() << TargetSym.getName() << "\n";
1714e30b20bSLang Hames else
1724e30b20bSLang Hames outs() << "<anonymous target>\n";
173633ea072SLang Hames }
174633ea072SLang Hames }
1754e30b20bSLang Hames outs() << "\n";
1764e30b20bSLang Hames }
1774e30b20bSLang Hames }
1784e30b20bSLang Hames return Error::success();
179633ea072SLang Hames }
180633ea072SLang Hames };
181633ea072SLang Hames
1824e30b20bSLang Hames static cl::opt<std::string>
1834e30b20bSLang Hames EntryPointName("entry", cl::desc("Symbol to call as main entry point"),
1844e30b20bSLang Hames cl::init("entry"));
1854e30b20bSLang Hames
18636c7d79dSFangrui Song static cl::list<std::string> InputObjects(cl::Positional,
1874e30b20bSLang Hames cl::desc("input objects"));
1884e30b20bSLang Hames
main(int argc,char * argv[])189633ea072SLang Hames int main(int argc, char *argv[]) {
190633ea072SLang Hames // Initialize LLVM.
191633ea072SLang Hames InitLLVM X(argc, argv);
192633ea072SLang Hames
193633ea072SLang Hames InitializeNativeTarget();
194633ea072SLang Hames InitializeNativeTargetAsmPrinter();
195633ea072SLang Hames
196633ea072SLang Hames cl::ParseCommandLineOptions(argc, argv, "LLJITWithObjectLinkingLayerPlugin");
197633ea072SLang Hames ExitOnErr.setBanner(std::string(argv[0]) + ": ");
198633ea072SLang Hames
199633ea072SLang Hames // Detect the host and set code model to small.
200633ea072SLang Hames auto JTMB = ExitOnErr(JITTargetMachineBuilder::detectHost());
201633ea072SLang Hames JTMB.setCodeModel(CodeModel::Small);
202633ea072SLang Hames
203633ea072SLang Hames // Create an LLJIT instance with an ObjectLinkingLayer as the base layer.
204633ea072SLang Hames // We attach our plugin in to the newly created ObjectLinkingLayer before
205633ea072SLang Hames // returning it.
206633ea072SLang Hames auto J = ExitOnErr(
207633ea072SLang Hames LLJITBuilder()
208633ea072SLang Hames .setJITTargetMachineBuilder(std::move(JTMB))
209633ea072SLang Hames .setObjectLinkingLayerCreator(
210633ea072SLang Hames [&](ExecutionSession &ES, const Triple &TT) {
211633ea072SLang Hames // Create ObjectLinkingLayer.
212633ea072SLang Hames auto ObjLinkingLayer = std::make_unique<ObjectLinkingLayer>(
213962a2479SLang Hames ES, ExitOnErr(jitlink::InProcessMemoryManager::Create()));
214633ea072SLang Hames // Add an instance of our plugin.
215633ea072SLang Hames ObjLinkingLayer->addPlugin(std::make_unique<MyPlugin>());
2166edc3fe5SStefan Gränitz return ObjLinkingLayer;
217633ea072SLang Hames })
218633ea072SLang Hames .create());
219633ea072SLang Hames
2204e30b20bSLang Hames if (!InputObjects.empty()) {
2214e30b20bSLang Hames // Load the input objects.
2224e30b20bSLang Hames for (auto InputObject : InputObjects) {
2234e30b20bSLang Hames auto ObjBuffer =
2244e30b20bSLang Hames ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(InputObject)));
2254e30b20bSLang Hames ExitOnErr(J->addObjectFile(std::move(ObjBuffer)));
2264e30b20bSLang Hames }
2274e30b20bSLang Hames } else {
2284e30b20bSLang Hames auto M = ExitOnErr(parseExampleModule(TestMod, "test-module"));
2294e30b20bSLang Hames M.withModuleDo([](Module &MP) {
2304e30b20bSLang Hames outs() << "No input objects specified. Using demo module:\n"
2314e30b20bSLang Hames << MP << "\n";
2324e30b20bSLang Hames });
233633ea072SLang Hames ExitOnErr(J->addIRModule(std::move(M)));
2344e30b20bSLang Hames }
235633ea072SLang Hames
236633ea072SLang Hames // Look up the JIT'd function, cast it to a function pointer, then call it.
23716dcbb53SLang Hames auto EntryAddr = ExitOnErr(J->lookup(EntryPointName));
23816dcbb53SLang Hames auto *Entry = EntryAddr.toPtr<int()>();
239633ea072SLang Hames
240633ea072SLang Hames int Result = Entry();
241633ea072SLang Hames outs() << "---Result---\n"
2424e30b20bSLang Hames << EntryPointName << "() = " << Result << "\n";
243633ea072SLang Hames
244633ea072SLang Hames return 0;
245633ea072SLang Hames }
246