at v4.14 5.1 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/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 31namespace perf { 32 33static std::unique_ptr<llvm::LLVMContext> LLVMCtx; 34 35using namespace clang; 36 37static CompilerInvocation * 38createCompilerInvocation(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 65static std::unique_ptr<llvm::Module> 66getModuleFromSource(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 86std::unique_ptr<llvm::Module> 87getModuleFromSource(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 107std::unique_ptr<llvm::Module> 108getModuleFromSource(llvm::opt::ArgStringList CFlags, StringRef Path) 109{ 110 IntrusiveRefCntPtr<vfs::FileSystem> VFS(vfs::getRealFileSystem()); 111 return getModuleFromSource(std::move(CFlags), Path, VFS); 112} 113 114std::unique_ptr<llvm::SmallVectorImpl<char>> 115getBPFObjectFromModule(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 152extern "C" { 153void perf_clang__init(void) 154{ 155 perf::LLVMCtx.reset(new llvm::LLVMContext()); 156 LLVMInitializeBPFTargetInfo(); 157 LLVMInitializeBPFTarget(); 158 LLVMInitializeBPFTargetMC(); 159 LLVMInitializeBPFAsmPrinter(); 160} 161 162void perf_clang__cleanup(void) 163{ 164 perf::LLVMCtx.reset(nullptr); 165 llvm::llvm_shutdown(); 166} 167 168int 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}