Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
00b86691 WN |
2 | /* |
3 | * llvm C frontend for perf. Support dynamically compile C file | |
4 | * | |
5 | * Inspired by clang example code: | |
6 | * http://llvm.org/svn/llvm-project/cfe/trunk/examples/clang-interpreter/main.cpp | |
7 | * | |
8 | * Copyright (C) 2016 Wang Nan <wangnan0@huawei.com> | |
9 | * Copyright (C) 2016 Huawei Inc. | |
10 | */ | |
11 | ||
7854e499 | 12 | #include "clang/Basic/Version.h" |
00b86691 WN |
13 | #include "clang/CodeGen/CodeGenAction.h" |
14 | #include "clang/Frontend/CompilerInvocation.h" | |
15 | #include "clang/Frontend/CompilerInstance.h" | |
16 | #include "clang/Frontend/TextDiagnosticPrinter.h" | |
17 | #include "clang/Tooling/Tooling.h" | |
5e08a765 | 18 | #include "llvm/IR/LegacyPassManager.h" |
00b86691 WN |
19 | #include "llvm/IR/Module.h" |
20 | #include "llvm/Option/Option.h" | |
77dfa84a | 21 | #include "llvm/Support/FileSystem.h" |
00b86691 | 22 | #include "llvm/Support/ManagedStatic.h" |
5e08a765 WN |
23 | #include "llvm/Support/TargetRegistry.h" |
24 | #include "llvm/Support/TargetSelect.h" | |
25 | #include "llvm/Target/TargetMachine.h" | |
26 | #include "llvm/Target/TargetOptions.h" | |
00b86691 WN |
27 | #include <memory> |
28 | ||
29 | #include "clang.h" | |
30 | #include "clang-c.h" | |
31 | ||
32 | namespace perf { | |
33 | ||
34 | static std::unique_ptr<llvm::LLVMContext> LLVMCtx; | |
35 | ||
36 | using namespace clang; | |
37 | ||
00b86691 | 38 | static CompilerInvocation * |
a9495fe9 WN |
39 | createCompilerInvocation(llvm::opt::ArgStringList CFlags, StringRef& Path, |
40 | DiagnosticsEngine& Diags) | |
00b86691 WN |
41 | { |
42 | llvm::opt::ArgStringList CCArgs { | |
43 | "-cc1", | |
44 | "-triple", "bpf-pc-linux", | |
45 | "-fsyntax-only", | |
46 | "-ferror-limit", "19", | |
47 | "-fmessage-length", "127", | |
48 | "-O2", | |
49 | "-nostdsysteminc", | |
50 | "-nobuiltininc", | |
51 | "-vectorize-loops", | |
52 | "-vectorize-slp", | |
53 | "-Wno-unused-value", | |
54 | "-Wno-pointer-sign", | |
55 | "-x", "c"}; | |
a9495fe9 WN |
56 | |
57 | CCArgs.append(CFlags.begin(), CFlags.end()); | |
00b86691 WN |
58 | CompilerInvocation *CI = tooling::newInvocation(&Diags, CCArgs); |
59 | ||
60 | FrontendOptions& Opts = CI->getFrontendOpts(); | |
61 | Opts.Inputs.clear(); | |
7854e499 SD |
62 | Opts.Inputs.emplace_back(Path, |
63 | FrontendOptions::getInputKindForExtension("c")); | |
00b86691 WN |
64 | return CI; |
65 | } | |
66 | ||
77dfa84a | 67 | static std::unique_ptr<llvm::Module> |
a9495fe9 WN |
68 | getModuleFromSource(llvm::opt::ArgStringList CFlags, |
69 | StringRef Path, IntrusiveRefCntPtr<vfs::FileSystem> VFS) | |
00b86691 WN |
70 | { |
71 | CompilerInstance Clang; | |
72 | Clang.createDiagnostics(); | |
73 | ||
00b86691 WN |
74 | Clang.setVirtualFileSystem(&*VFS); |
75 | ||
7854e499 | 76 | #if CLANG_VERSION_MAJOR < 4 |
00b86691 | 77 | IntrusiveRefCntPtr<CompilerInvocation> CI = |
a9495fe9 WN |
78 | createCompilerInvocation(std::move(CFlags), Path, |
79 | Clang.getDiagnostics()); | |
00b86691 | 80 | Clang.setInvocation(&*CI); |
7854e499 SD |
81 | #else |
82 | std::shared_ptr<CompilerInvocation> CI( | |
83 | createCompilerInvocation(std::move(CFlags), Path, | |
84 | Clang.getDiagnostics())); | |
85 | Clang.setInvocation(CI); | |
86 | #endif | |
00b86691 WN |
87 | |
88 | std::unique_ptr<CodeGenAction> Act(new EmitLLVMOnlyAction(&*LLVMCtx)); | |
89 | if (!Clang.ExecuteAction(*Act)) | |
90 | return std::unique_ptr<llvm::Module>(nullptr); | |
91 | ||
92 | return Act->takeModule(); | |
93 | } | |
94 | ||
77dfa84a | 95 | std::unique_ptr<llvm::Module> |
a9495fe9 WN |
96 | getModuleFromSource(llvm::opt::ArgStringList CFlags, |
97 | StringRef Name, StringRef Content) | |
77dfa84a WN |
98 | { |
99 | using namespace vfs; | |
100 | ||
101 | llvm::IntrusiveRefCntPtr<OverlayFileSystem> OverlayFS( | |
102 | new OverlayFileSystem(getRealFileSystem())); | |
103 | llvm::IntrusiveRefCntPtr<InMemoryFileSystem> MemFS( | |
104 | new InMemoryFileSystem(true)); | |
105 | ||
106 | /* | |
107 | * pushOverlay helps setting working dir for MemFS. Must call | |
108 | * before addFile. | |
109 | */ | |
110 | OverlayFS->pushOverlay(MemFS); | |
111 | MemFS->addFile(Twine(Name), 0, llvm::MemoryBuffer::getMemBuffer(Content)); | |
112 | ||
a9495fe9 | 113 | return getModuleFromSource(std::move(CFlags), Name, OverlayFS); |
77dfa84a WN |
114 | } |
115 | ||
116 | std::unique_ptr<llvm::Module> | |
a9495fe9 | 117 | getModuleFromSource(llvm::opt::ArgStringList CFlags, StringRef Path) |
77dfa84a WN |
118 | { |
119 | IntrusiveRefCntPtr<vfs::FileSystem> VFS(vfs::getRealFileSystem()); | |
a9495fe9 | 120 | return getModuleFromSource(std::move(CFlags), Path, VFS); |
77dfa84a WN |
121 | } |
122 | ||
5e08a765 WN |
123 | std::unique_ptr<llvm::SmallVectorImpl<char>> |
124 | getBPFObjectFromModule(llvm::Module *Module) | |
125 | { | |
126 | using namespace llvm; | |
127 | ||
128 | std::string TargetTriple("bpf-pc-linux"); | |
129 | std::string Error; | |
130 | const Target* Target = TargetRegistry::lookupTarget(TargetTriple, Error); | |
131 | if (!Target) { | |
132 | llvm::errs() << Error; | |
133 | return std::unique_ptr<llvm::SmallVectorImpl<char>>(nullptr); | |
134 | } | |
135 | ||
136 | llvm::TargetOptions Opt; | |
137 | TargetMachine *TargetMachine = | |
138 | Target->createTargetMachine(TargetTriple, | |
139 | "generic", "", | |
140 | Opt, Reloc::Static); | |
141 | ||
142 | Module->setDataLayout(TargetMachine->createDataLayout()); | |
143 | Module->setTargetTriple(TargetTriple); | |
144 | ||
145 | std::unique_ptr<SmallVectorImpl<char>> Buffer(new SmallVector<char, 0>()); | |
146 | raw_svector_ostream ostream(*Buffer); | |
147 | ||
148 | legacy::PassManager PM; | |
c6555c14 YS |
149 | bool NotAdded; |
150 | #if CLANG_VERSION_MAJOR < 7 | |
151 | NotAdded = TargetMachine->addPassesToEmitFile(PM, ostream, | |
152 | TargetMachine::CGFT_ObjectFile); | |
153 | #else | |
154 | NotAdded = TargetMachine->addPassesToEmitFile(PM, ostream, nullptr, | |
155 | TargetMachine::CGFT_ObjectFile); | |
156 | #endif | |
157 | if (NotAdded) { | |
5e08a765 WN |
158 | llvm::errs() << "TargetMachine can't emit a file of this type\n"; |
159 | return std::unique_ptr<llvm::SmallVectorImpl<char>>(nullptr);; | |
160 | } | |
161 | PM.run(*Module); | |
162 | ||
163 | return std::move(Buffer); | |
164 | } | |
165 | ||
00b86691 WN |
166 | } |
167 | ||
168 | extern "C" { | |
169 | void perf_clang__init(void) | |
170 | { | |
171 | perf::LLVMCtx.reset(new llvm::LLVMContext()); | |
5e08a765 WN |
172 | LLVMInitializeBPFTargetInfo(); |
173 | LLVMInitializeBPFTarget(); | |
174 | LLVMInitializeBPFTargetMC(); | |
175 | LLVMInitializeBPFAsmPrinter(); | |
00b86691 WN |
176 | } |
177 | ||
178 | void perf_clang__cleanup(void) | |
179 | { | |
180 | perf::LLVMCtx.reset(nullptr); | |
181 | llvm::llvm_shutdown(); | |
182 | } | |
edd695b0 WN |
183 | |
184 | int perf_clang__compile_bpf(const char *filename, | |
185 | void **p_obj_buf, | |
186 | size_t *p_obj_buf_sz) | |
187 | { | |
188 | using namespace perf; | |
189 | ||
190 | if (!p_obj_buf || !p_obj_buf_sz) | |
191 | return -EINVAL; | |
192 | ||
193 | llvm::opt::ArgStringList CFlags; | |
194 | auto M = getModuleFromSource(std::move(CFlags), filename); | |
195 | if (!M) | |
196 | return -EINVAL; | |
197 | auto O = getBPFObjectFromModule(&*M); | |
198 | if (!O) | |
199 | return -EINVAL; | |
200 | ||
201 | size_t size = O->size_in_bytes(); | |
202 | void *buffer; | |
203 | ||
204 | buffer = malloc(size); | |
205 | if (!buffer) | |
206 | return -ENOMEM; | |
207 | memcpy(buffer, O->data(), size); | |
208 | *p_obj_buf = buffer; | |
209 | *p_obj_buf_sz = size; | |
210 | return 0; | |
211 | } | |
00b86691 | 212 | } |