at v5.6 5.6 kB view raw
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/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" 27#include <memory> 28 29#include "clang.h" 30#include "clang-c.h" 31 32namespace perf { 33 34static std::unique_ptr<llvm::LLVMContext> LLVMCtx; 35 36using namespace clang; 37 38static CompilerInvocation * 39createCompilerInvocation(llvm::opt::ArgStringList CFlags, StringRef& Path, 40 DiagnosticsEngine& Diags) 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"}; 56 57 CCArgs.append(CFlags.begin(), CFlags.end()); 58 CompilerInvocation *CI = tooling::newInvocation(&Diags, CCArgs); 59 60 FrontendOptions& Opts = CI->getFrontendOpts(); 61 Opts.Inputs.clear(); 62 Opts.Inputs.emplace_back(Path, 63 FrontendOptions::getInputKindForExtension("c")); 64 return CI; 65} 66 67static std::unique_ptr<llvm::Module> 68getModuleFromSource(llvm::opt::ArgStringList CFlags, 69 StringRef Path, IntrusiveRefCntPtr<vfs::FileSystem> VFS) 70{ 71 CompilerInstance Clang; 72 Clang.createDiagnostics(); 73 74#if CLANG_VERSION_MAJOR < 9 75 Clang.setVirtualFileSystem(&*VFS); 76#else 77 Clang.createFileManager(&*VFS); 78#endif 79 80#if CLANG_VERSION_MAJOR < 4 81 IntrusiveRefCntPtr<CompilerInvocation> CI = 82 createCompilerInvocation(std::move(CFlags), Path, 83 Clang.getDiagnostics()); 84 Clang.setInvocation(&*CI); 85#else 86 std::shared_ptr<CompilerInvocation> CI( 87 createCompilerInvocation(std::move(CFlags), Path, 88 Clang.getDiagnostics())); 89 Clang.setInvocation(CI); 90#endif 91 92 std::unique_ptr<CodeGenAction> Act(new EmitLLVMOnlyAction(&*LLVMCtx)); 93 if (!Clang.ExecuteAction(*Act)) 94 return std::unique_ptr<llvm::Module>(nullptr); 95 96 return Act->takeModule(); 97} 98 99std::unique_ptr<llvm::Module> 100getModuleFromSource(llvm::opt::ArgStringList CFlags, 101 StringRef Name, StringRef Content) 102{ 103 using namespace vfs; 104 105 llvm::IntrusiveRefCntPtr<OverlayFileSystem> OverlayFS( 106 new OverlayFileSystem(getRealFileSystem())); 107 llvm::IntrusiveRefCntPtr<InMemoryFileSystem> MemFS( 108 new InMemoryFileSystem(true)); 109 110 /* 111 * pushOverlay helps setting working dir for MemFS. Must call 112 * before addFile. 113 */ 114 OverlayFS->pushOverlay(MemFS); 115 MemFS->addFile(Twine(Name), 0, llvm::MemoryBuffer::getMemBuffer(Content)); 116 117 return getModuleFromSource(std::move(CFlags), Name, OverlayFS); 118} 119 120std::unique_ptr<llvm::Module> 121getModuleFromSource(llvm::opt::ArgStringList CFlags, StringRef Path) 122{ 123 IntrusiveRefCntPtr<vfs::FileSystem> VFS(vfs::getRealFileSystem()); 124 return getModuleFromSource(std::move(CFlags), Path, VFS); 125} 126 127std::unique_ptr<llvm::SmallVectorImpl<char>> 128getBPFObjectFromModule(llvm::Module *Module) 129{ 130 using namespace llvm; 131 132 std::string TargetTriple("bpf-pc-linux"); 133 std::string Error; 134 const Target* Target = TargetRegistry::lookupTarget(TargetTriple, Error); 135 if (!Target) { 136 llvm::errs() << Error; 137 return std::unique_ptr<llvm::SmallVectorImpl<char>>(nullptr); 138 } 139 140 llvm::TargetOptions Opt; 141 TargetMachine *TargetMachine = 142 Target->createTargetMachine(TargetTriple, 143 "generic", "", 144 Opt, Reloc::Static); 145 146 Module->setDataLayout(TargetMachine->createDataLayout()); 147 Module->setTargetTriple(TargetTriple); 148 149 std::unique_ptr<SmallVectorImpl<char>> Buffer(new SmallVector<char, 0>()); 150 raw_svector_ostream ostream(*Buffer); 151 152 legacy::PassManager PM; 153 bool NotAdded; 154#if CLANG_VERSION_MAJOR < 7 155 NotAdded = TargetMachine->addPassesToEmitFile(PM, ostream, 156 TargetMachine::CGFT_ObjectFile); 157#else 158 NotAdded = TargetMachine->addPassesToEmitFile(PM, ostream, nullptr, 159 TargetMachine::CGFT_ObjectFile); 160#endif 161 if (NotAdded) { 162 llvm::errs() << "TargetMachine can't emit a file of this type\n"; 163 return std::unique_ptr<llvm::SmallVectorImpl<char>>(nullptr); 164 } 165 PM.run(*Module); 166 167 return Buffer; 168} 169 170} 171 172extern "C" { 173void perf_clang__init(void) 174{ 175 perf::LLVMCtx.reset(new llvm::LLVMContext()); 176 LLVMInitializeBPFTargetInfo(); 177 LLVMInitializeBPFTarget(); 178 LLVMInitializeBPFTargetMC(); 179 LLVMInitializeBPFAsmPrinter(); 180} 181 182void perf_clang__cleanup(void) 183{ 184 perf::LLVMCtx.reset(nullptr); 185 llvm::llvm_shutdown(); 186} 187 188int perf_clang__compile_bpf(const char *filename, 189 void **p_obj_buf, 190 size_t *p_obj_buf_sz) 191{ 192 using namespace perf; 193 194 if (!p_obj_buf || !p_obj_buf_sz) 195 return -EINVAL; 196 197 llvm::opt::ArgStringList CFlags; 198 auto M = getModuleFromSource(std::move(CFlags), filename); 199 if (!M) 200 return -EINVAL; 201 auto O = getBPFObjectFromModule(&*M); 202 if (!O) 203 return -EINVAL; 204 205 size_t size = O->size_in_bytes(); 206 void *buffer; 207 208 buffer = malloc(size); 209 if (!buffer) 210 return -ENOMEM; 211 memcpy(buffer, O->data(), size); 212 *p_obj_buf = buffer; 213 *p_obj_buf_sz = size; 214 return 0; 215} 216}