1 // SPDX-License-Identifier: GPL-2.0
3 * llvm C frontend for perf. Support dynamically compile C file
5 * Inspired by clang example code:
6 * http://llvm.org/svn/llvm-project/cfe/trunk/examples/clang-interpreter/main.cpp
8 * Copyright (C) 2016 Wang Nan <wangnan0@huawei.com>
9 * Copyright (C) 2016 Huawei Inc.
12 #include "clang/Basic/Version.h"
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"
18 #include "llvm/IR/LegacyPassManager.h"
19 #include "llvm/IR/Module.h"
20 #include "llvm/Option/Option.h"
21 #include "llvm/Support/FileSystem.h"
22 #include "llvm/Support/ManagedStatic.h"
23 #include "llvm/Support/TargetRegistry.h"
24 #include "llvm/Support/TargetSelect.h"
25 #include "llvm/Target/TargetMachine.h"
26 #include "llvm/Target/TargetOptions.h"
34 static std::unique_ptr
<llvm::LLVMContext
> LLVMCtx
;
36 using namespace clang
;
38 static CompilerInvocation
*
39 createCompilerInvocation(llvm::opt::ArgStringList CFlags
, StringRef
& Path
,
40 DiagnosticsEngine
& Diags
)
42 llvm::opt::ArgStringList CCArgs
{
44 "-triple", "bpf-pc-linux",
46 "-ferror-limit", "19",
47 "-fmessage-length", "127",
57 CCArgs
.append(CFlags
.begin(), CFlags
.end());
58 CompilerInvocation
*CI
= tooling::newInvocation(&Diags
, CCArgs
);
60 FrontendOptions
& Opts
= CI
->getFrontendOpts();
62 Opts
.Inputs
.emplace_back(Path
,
63 FrontendOptions::getInputKindForExtension("c"));
67 static std::unique_ptr
<llvm::Module
>
68 getModuleFromSource(llvm::opt::ArgStringList CFlags
,
69 StringRef Path
, IntrusiveRefCntPtr
<vfs::FileSystem
> VFS
)
71 CompilerInstance Clang
;
72 Clang
.createDiagnostics();
74 #if CLANG_VERSION_MAJOR < 9
75 Clang
.setVirtualFileSystem(&*VFS
);
77 Clang
.createFileManager(&*VFS
);
80 #if CLANG_VERSION_MAJOR < 4
81 IntrusiveRefCntPtr
<CompilerInvocation
> CI
=
82 createCompilerInvocation(std::move(CFlags
), Path
,
83 Clang
.getDiagnostics());
84 Clang
.setInvocation(&*CI
);
86 std::shared_ptr
<CompilerInvocation
> CI(
87 createCompilerInvocation(std::move(CFlags
), Path
,
88 Clang
.getDiagnostics()));
89 Clang
.setInvocation(CI
);
92 std::unique_ptr
<CodeGenAction
> Act(new EmitLLVMOnlyAction(&*LLVMCtx
));
93 if (!Clang
.ExecuteAction(*Act
))
94 return std::unique_ptr
<llvm::Module
>(nullptr);
96 return Act
->takeModule();
99 std::unique_ptr
<llvm::Module
>
100 getModuleFromSource(llvm::opt::ArgStringList CFlags
,
101 StringRef Name
, StringRef Content
)
105 llvm::IntrusiveRefCntPtr
<OverlayFileSystem
> OverlayFS(
106 new OverlayFileSystem(getRealFileSystem()));
107 llvm::IntrusiveRefCntPtr
<InMemoryFileSystem
> MemFS(
108 new InMemoryFileSystem(true));
111 * pushOverlay helps setting working dir for MemFS. Must call
114 OverlayFS
->pushOverlay(MemFS
);
115 MemFS
->addFile(Twine(Name
), 0, llvm::MemoryBuffer::getMemBuffer(Content
));
117 return getModuleFromSource(std::move(CFlags
), Name
, OverlayFS
);
120 std::unique_ptr
<llvm::Module
>
121 getModuleFromSource(llvm::opt::ArgStringList CFlags
, StringRef Path
)
123 IntrusiveRefCntPtr
<vfs::FileSystem
> VFS(vfs::getRealFileSystem());
124 return getModuleFromSource(std::move(CFlags
), Path
, VFS
);
127 std::unique_ptr
<llvm::SmallVectorImpl
<char>>
128 getBPFObjectFromModule(llvm::Module
*Module
)
130 using namespace llvm
;
132 std::string
TargetTriple("bpf-pc-linux");
134 const Target
* Target
= TargetRegistry::lookupTarget(TargetTriple
, Error
);
136 llvm::errs() << Error
;
137 return std::unique_ptr
<llvm::SmallVectorImpl
<char>>(nullptr);
140 llvm::TargetOptions Opt
;
141 TargetMachine
*TargetMachine
=
142 Target
->createTargetMachine(TargetTriple
,
146 Module
->setDataLayout(TargetMachine
->createDataLayout());
147 Module
->setTargetTriple(TargetTriple
);
149 std::unique_ptr
<SmallVectorImpl
<char>> Buffer(new SmallVector
<char, 0>());
150 raw_svector_ostream
ostream(*Buffer
);
152 legacy::PassManager PM
;
154 #if CLANG_VERSION_MAJOR < 7
155 NotAdded
= TargetMachine
->addPassesToEmitFile(PM
, ostream
,
156 TargetMachine::CGFT_ObjectFile
);
158 NotAdded
= TargetMachine
->addPassesToEmitFile(PM
, ostream
, nullptr,
159 TargetMachine::CGFT_ObjectFile
);
162 llvm::errs() << "TargetMachine can't emit a file of this type\n";
163 return std::unique_ptr
<llvm::SmallVectorImpl
<char>>(nullptr);
173 void perf_clang__init(void)
175 perf::LLVMCtx
.reset(new llvm::LLVMContext());
176 LLVMInitializeBPFTargetInfo();
177 LLVMInitializeBPFTarget();
178 LLVMInitializeBPFTargetMC();
179 LLVMInitializeBPFAsmPrinter();
182 void perf_clang__cleanup(void)
184 perf::LLVMCtx
.reset(nullptr);
185 llvm::llvm_shutdown();
188 int perf_clang__compile_bpf(const char *filename
,
190 size_t *p_obj_buf_sz
)
192 using namespace perf
;
194 if (!p_obj_buf
|| !p_obj_buf_sz
)
197 llvm::opt::ArgStringList CFlags
;
198 auto M
= getModuleFromSource(std::move(CFlags
), filename
);
201 auto O
= getBPFObjectFromModule(&*M
);
205 size_t size
= O
->size_in_bytes();
208 buffer
= malloc(size
);
211 memcpy(buffer
, O
->data(), size
);
213 *p_obj_buf_sz
= size
;