Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * llvm C frontend for perf. Support dynamically compile C file
0004  *
0005  * Inspired by clang example code:
0006  * http://llvm.org/svn/llvm-project/cfe/trunk/examples/clang-interpreter/main.cpp
0007  *
0008  * Copyright (C) 2016 Wang Nan <wangnan0@huawei.com>
0009  * Copyright (C) 2016 Huawei Inc.
0010  */
0011 
0012 #include "clang/Basic/Version.h"
0013 #include "clang/CodeGen/CodeGenAction.h"
0014 #include "clang/Frontend/CompilerInvocation.h"
0015 #include "clang/Frontend/CompilerInstance.h"
0016 #include "clang/Frontend/TextDiagnosticPrinter.h"
0017 #include "clang/Tooling/Tooling.h"
0018 #include "llvm/IR/LegacyPassManager.h"
0019 #include "llvm/IR/Module.h"
0020 #include "llvm/Option/Option.h"
0021 #include "llvm/Support/FileSystem.h"
0022 #include "llvm/Support/ManagedStatic.h"
0023 #if CLANG_VERSION_MAJOR >= 14
0024 #include "llvm/MC/TargetRegistry.h"
0025 #else
0026 #include "llvm/Support/TargetRegistry.h"
0027 #endif
0028 #include "llvm/Support/TargetSelect.h"
0029 #include "llvm/Target/TargetMachine.h"
0030 #include "llvm/Target/TargetOptions.h"
0031 #include <memory>
0032 
0033 #include "clang.h"
0034 #include "clang-c.h"
0035 
0036 namespace perf {
0037 
0038 static std::unique_ptr<llvm::LLVMContext> LLVMCtx;
0039 
0040 using namespace clang;
0041 
0042 static CompilerInvocation *
0043 createCompilerInvocation(llvm::opt::ArgStringList CFlags, StringRef& Path,
0044              DiagnosticsEngine& Diags)
0045 {
0046     llvm::opt::ArgStringList CCArgs {
0047         "-cc1",
0048         "-triple", "bpf-pc-linux",
0049         "-fsyntax-only",
0050         "-O2",
0051         "-nostdsysteminc",
0052         "-nobuiltininc",
0053         "-vectorize-loops",
0054         "-vectorize-slp",
0055         "-Wno-unused-value",
0056         "-Wno-pointer-sign",
0057         "-x", "c"};
0058 
0059     CCArgs.append(CFlags.begin(), CFlags.end());
0060     CompilerInvocation *CI = tooling::newInvocation(&Diags, CCArgs
0061 #if CLANG_VERSION_MAJOR >= 11
0062                                                         ,/*BinaryName=*/nullptr
0063 #endif
0064                                                         );
0065 
0066     FrontendOptions& Opts = CI->getFrontendOpts();
0067     Opts.Inputs.clear();
0068     Opts.Inputs.emplace_back(Path,
0069             FrontendOptions::getInputKindForExtension("c"));
0070     return CI;
0071 }
0072 
0073 static std::unique_ptr<llvm::Module>
0074 getModuleFromSource(llvm::opt::ArgStringList CFlags,
0075             StringRef Path, IntrusiveRefCntPtr<vfs::FileSystem> VFS)
0076 {
0077     CompilerInstance Clang;
0078     Clang.createDiagnostics();
0079 
0080 #if CLANG_VERSION_MAJOR < 9
0081     Clang.setVirtualFileSystem(&*VFS);
0082 #else
0083     Clang.createFileManager(&*VFS);
0084 #endif
0085 
0086 #if CLANG_VERSION_MAJOR < 4
0087     IntrusiveRefCntPtr<CompilerInvocation> CI =
0088         createCompilerInvocation(std::move(CFlags), Path,
0089                      Clang.getDiagnostics());
0090     Clang.setInvocation(&*CI);
0091 #else
0092     std::shared_ptr<CompilerInvocation> CI(
0093         createCompilerInvocation(std::move(CFlags), Path,
0094                      Clang.getDiagnostics()));
0095     Clang.setInvocation(CI);
0096 #endif
0097 
0098     std::unique_ptr<CodeGenAction> Act(new EmitLLVMOnlyAction(&*LLVMCtx));
0099     if (!Clang.ExecuteAction(*Act))
0100         return std::unique_ptr<llvm::Module>(nullptr);
0101 
0102     return Act->takeModule();
0103 }
0104 
0105 std::unique_ptr<llvm::Module>
0106 getModuleFromSource(llvm::opt::ArgStringList CFlags,
0107             StringRef Name, StringRef Content)
0108 {
0109     using namespace vfs;
0110 
0111     llvm::IntrusiveRefCntPtr<OverlayFileSystem> OverlayFS(
0112             new OverlayFileSystem(getRealFileSystem()));
0113     llvm::IntrusiveRefCntPtr<InMemoryFileSystem> MemFS(
0114             new InMemoryFileSystem(true));
0115 
0116     /*
0117      * pushOverlay helps setting working dir for MemFS. Must call
0118      * before addFile.
0119      */
0120     OverlayFS->pushOverlay(MemFS);
0121     MemFS->addFile(Twine(Name), 0, llvm::MemoryBuffer::getMemBuffer(Content));
0122 
0123     return getModuleFromSource(std::move(CFlags), Name, OverlayFS);
0124 }
0125 
0126 std::unique_ptr<llvm::Module>
0127 getModuleFromSource(llvm::opt::ArgStringList CFlags, StringRef Path)
0128 {
0129     IntrusiveRefCntPtr<vfs::FileSystem> VFS(vfs::getRealFileSystem());
0130     return getModuleFromSource(std::move(CFlags), Path, VFS);
0131 }
0132 
0133 std::unique_ptr<llvm::SmallVectorImpl<char>>
0134 getBPFObjectFromModule(llvm::Module *Module)
0135 {
0136     using namespace llvm;
0137 
0138     std::string TargetTriple("bpf-pc-linux");
0139     std::string Error;
0140     const Target* Target = TargetRegistry::lookupTarget(TargetTriple, Error);
0141     if (!Target) {
0142         llvm::errs() << Error;
0143         return std::unique_ptr<llvm::SmallVectorImpl<char>>(nullptr);
0144     }
0145 
0146     llvm::TargetOptions Opt;
0147     TargetMachine *TargetMachine =
0148         Target->createTargetMachine(TargetTriple,
0149                         "generic", "",
0150                         Opt, Reloc::Static);
0151 
0152     Module->setDataLayout(TargetMachine->createDataLayout());
0153     Module->setTargetTriple(TargetTriple);
0154 
0155     std::unique_ptr<SmallVectorImpl<char>> Buffer(new SmallVector<char, 0>());
0156     raw_svector_ostream ostream(*Buffer);
0157 
0158     legacy::PassManager PM;
0159     bool NotAdded;
0160     NotAdded = TargetMachine->addPassesToEmitFile(PM, ostream
0161 #if CLANG_VERSION_MAJOR >= 7
0162                                                       , /*DwoOut=*/nullptr
0163 #endif
0164 #if CLANG_VERSION_MAJOR < 10
0165                                                       , TargetMachine::CGFT_ObjectFile
0166 #else
0167                                                       , llvm::CGFT_ObjectFile
0168 #endif
0169                                                       );
0170     if (NotAdded) {
0171         llvm::errs() << "TargetMachine can't emit a file of this type\n";
0172         return std::unique_ptr<llvm::SmallVectorImpl<char>>(nullptr);
0173     }
0174     PM.run(*Module);
0175 
0176     return Buffer;
0177 }
0178 
0179 }
0180 
0181 extern "C" {
0182 void perf_clang__init(void)
0183 {
0184     perf::LLVMCtx.reset(new llvm::LLVMContext());
0185     LLVMInitializeBPFTargetInfo();
0186     LLVMInitializeBPFTarget();
0187     LLVMInitializeBPFTargetMC();
0188     LLVMInitializeBPFAsmPrinter();
0189 }
0190 
0191 void perf_clang__cleanup(void)
0192 {
0193     perf::LLVMCtx.reset(nullptr);
0194     llvm::llvm_shutdown();
0195 }
0196 
0197 int perf_clang__compile_bpf(const char *filename,
0198                 void **p_obj_buf,
0199                 size_t *p_obj_buf_sz)
0200 {
0201     using namespace perf;
0202 
0203     if (!p_obj_buf || !p_obj_buf_sz)
0204         return -EINVAL;
0205 
0206     llvm::opt::ArgStringList CFlags;
0207     auto M = getModuleFromSource(std::move(CFlags), filename);
0208     if (!M)
0209         return  -EINVAL;
0210     auto O = getBPFObjectFromModule(&*M);
0211     if (!O)
0212         return -EINVAL;
0213 
0214     size_t size = O->size_in_bytes();
0215     void *buffer;
0216 
0217     buffer = malloc(size);
0218     if (!buffer)
0219         return -ENOMEM;
0220     memcpy(buffer, O->data(), size);
0221     *p_obj_buf = buffer;
0222     *p_obj_buf_sz = size;
0223     return 0;
0224 }
0225 }