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