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