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 Clang
.setVirtualFileSystem(&*VFS
);
76 #if CLANG_VERSION_MAJOR < 4
77 IntrusiveRefCntPtr
<CompilerInvocation
> CI
=
78 createCompilerInvocation(std::move(CFlags
), Path
,
79 Clang
.getDiagnostics());
80 Clang
.setInvocation(&*CI
);
82 std::shared_ptr
<CompilerInvocation
> CI(
83 createCompilerInvocation(std::move(CFlags
), Path
,
84 Clang
.getDiagnostics()));
85 Clang
.setInvocation(CI
);
88 std::unique_ptr
<CodeGenAction
> Act(new EmitLLVMOnlyAction(&*LLVMCtx
));
89 if (!Clang
.ExecuteAction(*Act
))
90 return std::unique_ptr
<llvm::Module
>(nullptr);
92 return Act
->takeModule();
95 std::unique_ptr
<llvm::Module
>
96 getModuleFromSource(llvm::opt::ArgStringList CFlags
,
97 StringRef Name
, StringRef Content
)
101 llvm::IntrusiveRefCntPtr
<OverlayFileSystem
> OverlayFS(
102 new OverlayFileSystem(getRealFileSystem()));
103 llvm::IntrusiveRefCntPtr
<InMemoryFileSystem
> MemFS(
104 new InMemoryFileSystem(true));
107 * pushOverlay helps setting working dir for MemFS. Must call
110 OverlayFS
->pushOverlay(MemFS
);
111 MemFS
->addFile(Twine(Name
), 0, llvm::MemoryBuffer::getMemBuffer(Content
));
113 return getModuleFromSource(std::move(CFlags
), Name
, OverlayFS
);
116 std::unique_ptr
<llvm::Module
>
117 getModuleFromSource(llvm::opt::ArgStringList CFlags
, StringRef Path
)
119 IntrusiveRefCntPtr
<vfs::FileSystem
> VFS(vfs::getRealFileSystem());
120 return getModuleFromSource(std::move(CFlags
), Path
, VFS
);
123 std::unique_ptr
<llvm::SmallVectorImpl
<char>>
124 getBPFObjectFromModule(llvm::Module
*Module
)
126 using namespace llvm
;
128 std::string
TargetTriple("bpf-pc-linux");
130 const Target
* Target
= TargetRegistry::lookupTarget(TargetTriple
, Error
);
132 llvm::errs() << Error
;
133 return std::unique_ptr
<llvm::SmallVectorImpl
<char>>(nullptr);
136 llvm::TargetOptions Opt
;
137 TargetMachine
*TargetMachine
=
138 Target
->createTargetMachine(TargetTriple
,
142 Module
->setDataLayout(TargetMachine
->createDataLayout());
143 Module
->setTargetTriple(TargetTriple
);
145 std::unique_ptr
<SmallVectorImpl
<char>> Buffer(new SmallVector
<char, 0>());
146 raw_svector_ostream
ostream(*Buffer
);
148 legacy::PassManager PM
;
150 #if CLANG_VERSION_MAJOR < 7
151 NotAdded
= TargetMachine
->addPassesToEmitFile(PM
, ostream
,
152 TargetMachine::CGFT_ObjectFile
);
154 NotAdded
= TargetMachine
->addPassesToEmitFile(PM
, ostream
, nullptr,
155 TargetMachine::CGFT_ObjectFile
);
158 llvm::errs() << "TargetMachine can't emit a file of this type\n";
159 return std::unique_ptr
<llvm::SmallVectorImpl
<char>>(nullptr);;
163 return std::move(Buffer
);
169 void perf_clang__init(void)
171 perf::LLVMCtx
.reset(new llvm::LLVMContext());
172 LLVMInitializeBPFTargetInfo();
173 LLVMInitializeBPFTarget();
174 LLVMInitializeBPFTargetMC();
175 LLVMInitializeBPFAsmPrinter();
178 void perf_clang__cleanup(void)
180 perf::LLVMCtx
.reset(nullptr);
181 llvm::llvm_shutdown();
184 int perf_clang__compile_bpf(const char *filename
,
186 size_t *p_obj_buf_sz
)
188 using namespace perf
;
190 if (!p_obj_buf
|| !p_obj_buf_sz
)
193 llvm::opt::ArgStringList CFlags
;
194 auto M
= getModuleFromSource(std::move(CFlags
), filename
);
197 auto O
= getBPFObjectFromModule(&*M
);
201 size_t size
= O
->size_in_bytes();
204 buffer
= malloc(size
);
207 memcpy(buffer
, O
->data(), size
);
209 *p_obj_buf_sz
= size
;