1 | //===-- examples/clang-interpreter/main.cpp - Clang C Interpreter Example -===// |
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 | #include "clang/Basic/DiagnosticOptions.h" |
10 | #include "clang/CodeGen/CodeGenAction.h" |
11 | #include "clang/Driver/Compilation.h" |
12 | #include "clang/Driver/Driver.h" |
13 | #include "clang/Driver/Tool.h" |
14 | #include "clang/Frontend/CompilerInstance.h" |
15 | #include "clang/Frontend/CompilerInvocation.h" |
16 | #include "clang/Frontend/FrontendDiagnostic.h" |
17 | #include "clang/Frontend/TextDiagnosticPrinter.h" |
18 | #include "llvm/ADT/SmallString.h" |
19 | #include "llvm/ExecutionEngine/ExecutionEngine.h" |
20 | #include "llvm/ExecutionEngine/Orc/CompileUtils.h" |
21 | #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" |
22 | #include "llvm/ExecutionEngine/Orc/IRCompileLayer.h" |
23 | #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" |
24 | #include "llvm/ExecutionEngine/SectionMemoryManager.h" |
25 | #include "llvm/IR/DataLayout.h" |
26 | #include "llvm/IR/Mangler.h" |
27 | #include "llvm/IR/Module.h" |
28 | #include "llvm/Support/FileSystem.h" |
29 | #include "llvm/Support/Host.h" |
30 | #include "llvm/Support/ManagedStatic.h" |
31 | #include "llvm/Support/Path.h" |
32 | #include "llvm/Support/TargetSelect.h" |
33 | #include "llvm/Support/raw_ostream.h" |
34 | #include "llvm/Target/TargetMachine.h" |
35 | |
36 | using namespace clang; |
37 | using namespace clang::driver; |
38 | |
39 | // This function isn't referenced outside its translation unit, but it |
40 | // can't use the "static" keyword because its address is used for |
41 | // GetMainExecutable (since some platforms don't support taking the |
42 | // address of main, and some platforms can't implement GetMainExecutable |
43 | // without being given the address of a function in the main executable). |
44 | std::string GetExecutablePath(const char *Argv0, void *MainAddr) { |
45 | return llvm::sys::fs::getMainExecutable(Argv0, MainAddr); |
46 | } |
47 | |
48 | namespace llvm { |
49 | namespace orc { |
50 | |
51 | class SimpleJIT { |
52 | private: |
53 | ExecutionSession ES; |
54 | std::unique_ptr<TargetMachine> TM; |
55 | const DataLayout DL; |
56 | MangleAndInterner Mangle{ES, DL}; |
57 | JITDylib &MainJD{ES.createBareJITDylib("<main>" )}; |
58 | RTDyldObjectLinkingLayer ObjectLayer{ES, createMemMgr}; |
59 | IRCompileLayer CompileLayer{ES, ObjectLayer, |
60 | std::make_unique<SimpleCompiler>(*TM)}; |
61 | |
62 | static std::unique_ptr<SectionMemoryManager> createMemMgr() { |
63 | return std::make_unique<SectionMemoryManager>(); |
64 | } |
65 | |
66 | SimpleJIT( |
67 | std::unique_ptr<TargetMachine> TM, DataLayout DL, |
68 | std::unique_ptr<DynamicLibrarySearchGenerator> ProcessSymbolsGenerator) |
69 | : TM(std::move(TM)), DL(std::move(DL)) { |
70 | llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr); |
71 | MainJD.addGenerator(std::move(ProcessSymbolsGenerator)); |
72 | } |
73 | |
74 | public: |
75 | ~SimpleJIT() { |
76 | if (auto Err = ES.endSession()) |
77 | ES.reportError(std::move(Err)); |
78 | } |
79 | |
80 | static Expected<std::unique_ptr<SimpleJIT>> Create() { |
81 | auto JTMB = JITTargetMachineBuilder::detectHost(); |
82 | if (!JTMB) |
83 | return JTMB.takeError(); |
84 | |
85 | auto TM = JTMB->createTargetMachine(); |
86 | if (!TM) |
87 | return TM.takeError(); |
88 | |
89 | auto DL = (*TM)->createDataLayout(); |
90 | |
91 | auto ProcessSymbolsGenerator = |
92 | DynamicLibrarySearchGenerator::GetForCurrentProcess( |
93 | DL.getGlobalPrefix()); |
94 | |
95 | if (!ProcessSymbolsGenerator) |
96 | return ProcessSymbolsGenerator.takeError(); |
97 | |
98 | return std::unique_ptr<SimpleJIT>(new SimpleJIT( |
99 | std::move(*TM), std::move(DL), std::move(*ProcessSymbolsGenerator))); |
100 | } |
101 | |
102 | const TargetMachine &getTargetMachine() const { return *TM; } |
103 | |
104 | Error addModule(ThreadSafeModule M) { |
105 | return CompileLayer.add(MainJD, std::move(M)); |
106 | } |
107 | |
108 | Expected<JITEvaluatedSymbol> findSymbol(const StringRef &Name) { |
109 | return ES.lookup({&MainJD}, Mangle(Name)); |
110 | } |
111 | |
112 | Expected<JITTargetAddress> getSymbolAddress(const StringRef &Name) { |
113 | auto Sym = findSymbol(Name); |
114 | if (!Sym) |
115 | return Sym.takeError(); |
116 | return Sym->getAddress(); |
117 | } |
118 | }; |
119 | |
120 | } // end namespace orc |
121 | } // end namespace llvm |
122 | |
123 | llvm::ExitOnError ExitOnErr; |
124 | |
125 | int main(int argc, const char **argv) { |
126 | // This just needs to be some symbol in the binary; C++ doesn't |
127 | // allow taking the address of ::main however. |
128 | void *MainAddr = (void*) (intptr_t) GetExecutablePath; |
129 | std::string Path = GetExecutablePath(argv[0], MainAddr); |
130 | IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); |
131 | TextDiagnosticPrinter *DiagClient = |
132 | new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts); |
133 | |
134 | IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); |
135 | DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagClient); |
136 | |
137 | const std::string TripleStr = llvm::sys::getProcessTriple(); |
138 | llvm::Triple T(TripleStr); |
139 | |
140 | // Use ELF on Windows-32 and MingW for now. |
141 | #ifndef CLANG_INTERPRETER_COFF_FORMAT |
142 | if (T.isOSBinFormatCOFF()) |
143 | T.setObjectFormat(llvm::Triple::ELF); |
144 | #endif |
145 | |
146 | ExitOnErr.setBanner("clang interpreter" ); |
147 | |
148 | Driver TheDriver(Path, T.str(), Diags); |
149 | TheDriver.setTitle("clang interpreter" ); |
150 | TheDriver.setCheckInputsExist(false); |
151 | |
152 | // FIXME: This is a hack to try to force the driver to do something we can |
153 | // recognize. We need to extend the driver library to support this use model |
154 | // (basically, exactly one input, and the operation mode is hard wired). |
155 | SmallVector<const char *, 16> Args(argv, argv + argc); |
156 | Args.push_back("-fsyntax-only" ); |
157 | std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(Args)); |
158 | if (!C) |
159 | return 0; |
160 | |
161 | // FIXME: This is copied from ASTUnit.cpp; simplify and eliminate. |
162 | |
163 | // We expect to get back exactly one command job, if we didn't something |
164 | // failed. Extract that job from the compilation. |
165 | const driver::JobList &Jobs = C->getJobs(); |
166 | if (Jobs.size() != 1 || !isa<driver::Command>(*Jobs.begin())) { |
167 | SmallString<256> Msg; |
168 | llvm::raw_svector_ostream OS(Msg); |
169 | Jobs.Print(OS, "; " , true); |
170 | Diags.Report(diag::err_fe_expected_compiler_job) << OS.str(); |
171 | return 1; |
172 | } |
173 | |
174 | const driver::Command &Cmd = cast<driver::Command>(*Jobs.begin()); |
175 | if (llvm::StringRef(Cmd.getCreator().getName()) != "clang" ) { |
176 | Diags.Report(diag::err_fe_expected_clang_command); |
177 | return 1; |
178 | } |
179 | |
180 | // Initialize a compiler invocation object from the clang (-cc1) arguments. |
181 | const llvm::opt::ArgStringList &CCArgs = Cmd.getArguments(); |
182 | std::unique_ptr<CompilerInvocation> CI(new CompilerInvocation); |
183 | CompilerInvocation::CreateFromArgs(*CI, CCArgs, Diags); |
184 | |
185 | // Show the invocation, with -v. |
186 | if (CI->getHeaderSearchOpts().Verbose) { |
187 | llvm::errs() << "clang invocation:\n" ; |
188 | Jobs.Print(llvm::errs(), "\n" , true); |
189 | llvm::errs() << "\n" ; |
190 | } |
191 | |
192 | // FIXME: This is copied from cc1_main.cpp; simplify and eliminate. |
193 | |
194 | // Create a compiler instance to handle the actual work. |
195 | CompilerInstance Clang; |
196 | Clang.setInvocation(std::move(CI)); |
197 | |
198 | // Create the compilers actual diagnostics engine. |
199 | Clang.createDiagnostics(); |
200 | if (!Clang.hasDiagnostics()) |
201 | return 1; |
202 | |
203 | // Infer the builtin include path if unspecified. |
204 | if (Clang.getHeaderSearchOpts().UseBuiltinIncludes && |
205 | Clang.getHeaderSearchOpts().ResourceDir.empty()) |
206 | Clang.getHeaderSearchOpts().ResourceDir = |
207 | CompilerInvocation::GetResourcesPath(argv[0], MainAddr); |
208 | |
209 | // Create and execute the frontend to generate an LLVM bitcode module. |
210 | std::unique_ptr<CodeGenAction> Act(new EmitLLVMOnlyAction()); |
211 | if (!Clang.ExecuteAction(*Act)) |
212 | return 1; |
213 | |
214 | llvm::InitializeNativeTarget(); |
215 | llvm::InitializeNativeTargetAsmPrinter(); |
216 | |
217 | int Res = 255; |
218 | std::unique_ptr<llvm::LLVMContext> Ctx(Act->takeLLVMContext()); |
219 | std::unique_ptr<llvm::Module> Module = Act->takeModule(); |
220 | |
221 | if (Module) { |
222 | auto J = ExitOnErr(llvm::orc::SimpleJIT::Create()); |
223 | |
224 | ExitOnErr(J->addModule( |
225 | llvm::orc::ThreadSafeModule(std::move(Module), std::move(Ctx)))); |
226 | auto Main = (int (*)(...))ExitOnErr(J->getSymbolAddress("main" )); |
227 | Res = Main(); |
228 | } |
229 | |
230 | // Shutdown. |
231 | llvm::llvm_shutdown(); |
232 | |
233 | return Res; |
234 | } |
235 | |