License cleanup: add SPDX GPL-2.0 license identifier to files with no license
[sfrench/cifs-2.6.git] / tools / perf / util / c++ / clang.cpp
1 // SPDX-License-Identifier: GPL-2.0
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"
17 #include "llvm/IR/LegacyPassManager.h"
18 #include "llvm/IR/Module.h"
19 #include "llvm/Option/Option.h"
20 #include "llvm/Support/FileSystem.h"
21 #include "llvm/Support/ManagedStatic.h"
22 #include "llvm/Support/TargetRegistry.h"
23 #include "llvm/Support/TargetSelect.h"
24 #include "llvm/Target/TargetMachine.h"
25 #include "llvm/Target/TargetOptions.h"
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
37 static CompilerInvocation *
38 createCompilerInvocation(llvm::opt::ArgStringList CFlags, StringRef& Path,
39                          DiagnosticsEngine& Diags)
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"};
55
56         CCArgs.append(CFlags.begin(), CFlags.end());
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
65 static std::unique_ptr<llvm::Module>
66 getModuleFromSource(llvm::opt::ArgStringList CFlags,
67                     StringRef Path, IntrusiveRefCntPtr<vfs::FileSystem> VFS)
68 {
69         CompilerInstance Clang;
70         Clang.createDiagnostics();
71
72         Clang.setVirtualFileSystem(&*VFS);
73
74         IntrusiveRefCntPtr<CompilerInvocation> CI =
75                 createCompilerInvocation(std::move(CFlags), Path,
76                                          Clang.getDiagnostics());
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
86 std::unique_ptr<llvm::Module>
87 getModuleFromSource(llvm::opt::ArgStringList CFlags,
88                     StringRef Name, StringRef Content)
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
104         return getModuleFromSource(std::move(CFlags), Name, OverlayFS);
105 }
106
107 std::unique_ptr<llvm::Module>
108 getModuleFromSource(llvm::opt::ArgStringList CFlags, StringRef Path)
109 {
110         IntrusiveRefCntPtr<vfs::FileSystem> VFS(vfs::getRealFileSystem());
111         return getModuleFromSource(std::move(CFlags), Path, VFS);
112 }
113
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
150 }
151
152 extern "C" {
153 void perf_clang__init(void)
154 {
155         perf::LLVMCtx.reset(new llvm::LLVMContext());
156         LLVMInitializeBPFTargetInfo();
157         LLVMInitializeBPFTarget();
158         LLVMInitializeBPFTargetMC();
159         LLVMInitializeBPFAsmPrinter();
160 }
161
162 void perf_clang__cleanup(void)
163 {
164         perf::LLVMCtx.reset(nullptr);
165         llvm::llvm_shutdown();
166 }
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 }
196 }