at v4.10 5.0 kB view raw
1/* 2 * llvm C frontend for perf. Support dynamically compile C file 3 * 4 * Inspired by clang example code: 5 * http://llvm.org/svn/llvm-project/cfe/trunk/examples/clang-interpreter/main.cpp 6 * 7 * Copyright (C) 2016 Wang Nan <wangnan0@huawei.com> 8 * Copyright (C) 2016 Huawei Inc. 9 */ 10 11#include "clang/CodeGen/CodeGenAction.h" 12#include "clang/Frontend/CompilerInvocation.h" 13#include "clang/Frontend/CompilerInstance.h" 14#include "clang/Frontend/TextDiagnosticPrinter.h" 15#include "clang/Tooling/Tooling.h" 16#include "llvm/IR/LegacyPassManager.h" 17#include "llvm/IR/Module.h" 18#include "llvm/Option/Option.h" 19#include "llvm/Support/FileSystem.h" 20#include "llvm/Support/ManagedStatic.h" 21#include "llvm/Support/TargetRegistry.h" 22#include "llvm/Support/TargetSelect.h" 23#include "llvm/Target/TargetMachine.h" 24#include "llvm/Target/TargetOptions.h" 25#include <memory> 26 27#include "clang.h" 28#include "clang-c.h" 29 30namespace perf { 31 32static std::unique_ptr<llvm::LLVMContext> LLVMCtx; 33 34using namespace clang; 35 36static CompilerInvocation * 37createCompilerInvocation(llvm::opt::ArgStringList CFlags, StringRef& Path, 38 DiagnosticsEngine& Diags) 39{ 40 llvm::opt::ArgStringList CCArgs { 41 "-cc1", 42 "-triple", "bpf-pc-linux", 43 "-fsyntax-only", 44 "-ferror-limit", "19", 45 "-fmessage-length", "127", 46 "-O2", 47 "-nostdsysteminc", 48 "-nobuiltininc", 49 "-vectorize-loops", 50 "-vectorize-slp", 51 "-Wno-unused-value", 52 "-Wno-pointer-sign", 53 "-x", "c"}; 54 55 CCArgs.append(CFlags.begin(), CFlags.end()); 56 CompilerInvocation *CI = tooling::newInvocation(&Diags, CCArgs); 57 58 FrontendOptions& Opts = CI->getFrontendOpts(); 59 Opts.Inputs.clear(); 60 Opts.Inputs.emplace_back(Path, IK_C); 61 return CI; 62} 63 64static std::unique_ptr<llvm::Module> 65getModuleFromSource(llvm::opt::ArgStringList CFlags, 66 StringRef Path, IntrusiveRefCntPtr<vfs::FileSystem> VFS) 67{ 68 CompilerInstance Clang; 69 Clang.createDiagnostics(); 70 71 Clang.setVirtualFileSystem(&*VFS); 72 73 IntrusiveRefCntPtr<CompilerInvocation> CI = 74 createCompilerInvocation(std::move(CFlags), Path, 75 Clang.getDiagnostics()); 76 Clang.setInvocation(&*CI); 77 78 std::unique_ptr<CodeGenAction> Act(new EmitLLVMOnlyAction(&*LLVMCtx)); 79 if (!Clang.ExecuteAction(*Act)) 80 return std::unique_ptr<llvm::Module>(nullptr); 81 82 return Act->takeModule(); 83} 84 85std::unique_ptr<llvm::Module> 86getModuleFromSource(llvm::opt::ArgStringList CFlags, 87 StringRef Name, StringRef Content) 88{ 89 using namespace vfs; 90 91 llvm::IntrusiveRefCntPtr<OverlayFileSystem> OverlayFS( 92 new OverlayFileSystem(getRealFileSystem())); 93 llvm::IntrusiveRefCntPtr<InMemoryFileSystem> MemFS( 94 new InMemoryFileSystem(true)); 95 96 /* 97 * pushOverlay helps setting working dir for MemFS. Must call 98 * before addFile. 99 */ 100 OverlayFS->pushOverlay(MemFS); 101 MemFS->addFile(Twine(Name), 0, llvm::MemoryBuffer::getMemBuffer(Content)); 102 103 return getModuleFromSource(std::move(CFlags), Name, OverlayFS); 104} 105 106std::unique_ptr<llvm::Module> 107getModuleFromSource(llvm::opt::ArgStringList CFlags, StringRef Path) 108{ 109 IntrusiveRefCntPtr<vfs::FileSystem> VFS(vfs::getRealFileSystem()); 110 return getModuleFromSource(std::move(CFlags), Path, VFS); 111} 112 113std::unique_ptr<llvm::SmallVectorImpl<char>> 114getBPFObjectFromModule(llvm::Module *Module) 115{ 116 using namespace llvm; 117 118 std::string TargetTriple("bpf-pc-linux"); 119 std::string Error; 120 const Target* Target = TargetRegistry::lookupTarget(TargetTriple, Error); 121 if (!Target) { 122 llvm::errs() << Error; 123 return std::unique_ptr<llvm::SmallVectorImpl<char>>(nullptr); 124 } 125 126 llvm::TargetOptions Opt; 127 TargetMachine *TargetMachine = 128 Target->createTargetMachine(TargetTriple, 129 "generic", "", 130 Opt, Reloc::Static); 131 132 Module->setDataLayout(TargetMachine->createDataLayout()); 133 Module->setTargetTriple(TargetTriple); 134 135 std::unique_ptr<SmallVectorImpl<char>> Buffer(new SmallVector<char, 0>()); 136 raw_svector_ostream ostream(*Buffer); 137 138 legacy::PassManager PM; 139 if (TargetMachine->addPassesToEmitFile(PM, ostream, 140 TargetMachine::CGFT_ObjectFile)) { 141 llvm::errs() << "TargetMachine can't emit a file of this type\n"; 142 return std::unique_ptr<llvm::SmallVectorImpl<char>>(nullptr);; 143 } 144 PM.run(*Module); 145 146 return std::move(Buffer); 147} 148 149} 150 151extern "C" { 152void perf_clang__init(void) 153{ 154 perf::LLVMCtx.reset(new llvm::LLVMContext()); 155 LLVMInitializeBPFTargetInfo(); 156 LLVMInitializeBPFTarget(); 157 LLVMInitializeBPFTargetMC(); 158 LLVMInitializeBPFAsmPrinter(); 159} 160 161void perf_clang__cleanup(void) 162{ 163 perf::LLVMCtx.reset(nullptr); 164 llvm::llvm_shutdown(); 165} 166 167int perf_clang__compile_bpf(const char *filename, 168 void **p_obj_buf, 169 size_t *p_obj_buf_sz) 170{ 171 using namespace perf; 172 173 if (!p_obj_buf || !p_obj_buf_sz) 174 return -EINVAL; 175 176 llvm::opt::ArgStringList CFlags; 177 auto M = getModuleFromSource(std::move(CFlags), filename); 178 if (!M) 179 return -EINVAL; 180 auto O = getBPFObjectFromModule(&*M); 181 if (!O) 182 return -EINVAL; 183 184 size_t size = O->size_in_bytes(); 185 void *buffer; 186 187 buffer = malloc(size); 188 if (!buffer) 189 return -ENOMEM; 190 memcpy(buffer, O->data(), size); 191 *p_obj_buf = buffer; 192 *p_obj_buf_sz = size; 193 return 0; 194} 195}