| /* | 
 |  * llvm C frontend for perf. Support dynamically compile C file | 
 |  * | 
 |  * Inspired by clang example code: | 
 |  * http://llvm.org/svn/llvm-project/cfe/trunk/examples/clang-interpreter/main.cpp | 
 |  * | 
 |  * Copyright (C) 2016 Wang Nan <wangnan0@huawei.com> | 
 |  * Copyright (C) 2016 Huawei Inc. | 
 |  */ | 
 |  | 
 | #include "clang/CodeGen/CodeGenAction.h" | 
 | #include "clang/Frontend/CompilerInvocation.h" | 
 | #include "clang/Frontend/CompilerInstance.h" | 
 | #include "clang/Frontend/TextDiagnosticPrinter.h" | 
 | #include "clang/Tooling/Tooling.h" | 
 | #include "llvm/IR/LegacyPassManager.h" | 
 | #include "llvm/IR/Module.h" | 
 | #include "llvm/Option/Option.h" | 
 | #include "llvm/Support/FileSystem.h" | 
 | #include "llvm/Support/ManagedStatic.h" | 
 | #include "llvm/Support/TargetRegistry.h" | 
 | #include "llvm/Support/TargetSelect.h" | 
 | #include "llvm/Target/TargetMachine.h" | 
 | #include "llvm/Target/TargetOptions.h" | 
 | #include <memory> | 
 |  | 
 | #include "clang.h" | 
 | #include "clang-c.h" | 
 |  | 
 | namespace perf { | 
 |  | 
 | static std::unique_ptr<llvm::LLVMContext> LLVMCtx; | 
 |  | 
 | using namespace clang; | 
 |  | 
 | static CompilerInvocation * | 
 | createCompilerInvocation(llvm::opt::ArgStringList CFlags, StringRef& Path, | 
 | 			 DiagnosticsEngine& Diags) | 
 | { | 
 | 	llvm::opt::ArgStringList CCArgs { | 
 | 		"-cc1", | 
 | 		"-triple", "bpf-pc-linux", | 
 | 		"-fsyntax-only", | 
 | 		"-ferror-limit", "19", | 
 | 		"-fmessage-length", "127", | 
 | 		"-O2", | 
 | 		"-nostdsysteminc", | 
 | 		"-nobuiltininc", | 
 | 		"-vectorize-loops", | 
 | 		"-vectorize-slp", | 
 | 		"-Wno-unused-value", | 
 | 		"-Wno-pointer-sign", | 
 | 		"-x", "c"}; | 
 |  | 
 | 	CCArgs.append(CFlags.begin(), CFlags.end()); | 
 | 	CompilerInvocation *CI = tooling::newInvocation(&Diags, CCArgs); | 
 |  | 
 | 	FrontendOptions& Opts = CI->getFrontendOpts(); | 
 | 	Opts.Inputs.clear(); | 
 | 	Opts.Inputs.emplace_back(Path, IK_C); | 
 | 	return CI; | 
 | } | 
 |  | 
 | static std::unique_ptr<llvm::Module> | 
 | getModuleFromSource(llvm::opt::ArgStringList CFlags, | 
 | 		    StringRef Path, IntrusiveRefCntPtr<vfs::FileSystem> VFS) | 
 | { | 
 | 	CompilerInstance Clang; | 
 | 	Clang.createDiagnostics(); | 
 |  | 
 | 	Clang.setVirtualFileSystem(&*VFS); | 
 |  | 
 | 	IntrusiveRefCntPtr<CompilerInvocation> CI = | 
 | 		createCompilerInvocation(std::move(CFlags), Path, | 
 | 					 Clang.getDiagnostics()); | 
 | 	Clang.setInvocation(&*CI); | 
 |  | 
 | 	std::unique_ptr<CodeGenAction> Act(new EmitLLVMOnlyAction(&*LLVMCtx)); | 
 | 	if (!Clang.ExecuteAction(*Act)) | 
 | 		return std::unique_ptr<llvm::Module>(nullptr); | 
 |  | 
 | 	return Act->takeModule(); | 
 | } | 
 |  | 
 | std::unique_ptr<llvm::Module> | 
 | getModuleFromSource(llvm::opt::ArgStringList CFlags, | 
 | 		    StringRef Name, StringRef Content) | 
 | { | 
 | 	using namespace vfs; | 
 |  | 
 | 	llvm::IntrusiveRefCntPtr<OverlayFileSystem> OverlayFS( | 
 | 			new OverlayFileSystem(getRealFileSystem())); | 
 | 	llvm::IntrusiveRefCntPtr<InMemoryFileSystem> MemFS( | 
 | 			new InMemoryFileSystem(true)); | 
 |  | 
 | 	/* | 
 | 	 * pushOverlay helps setting working dir for MemFS. Must call | 
 | 	 * before addFile. | 
 | 	 */ | 
 | 	OverlayFS->pushOverlay(MemFS); | 
 | 	MemFS->addFile(Twine(Name), 0, llvm::MemoryBuffer::getMemBuffer(Content)); | 
 |  | 
 | 	return getModuleFromSource(std::move(CFlags), Name, OverlayFS); | 
 | } | 
 |  | 
 | std::unique_ptr<llvm::Module> | 
 | getModuleFromSource(llvm::opt::ArgStringList CFlags, StringRef Path) | 
 | { | 
 | 	IntrusiveRefCntPtr<vfs::FileSystem> VFS(vfs::getRealFileSystem()); | 
 | 	return getModuleFromSource(std::move(CFlags), Path, VFS); | 
 | } | 
 |  | 
 | std::unique_ptr<llvm::SmallVectorImpl<char>> | 
 | getBPFObjectFromModule(llvm::Module *Module) | 
 | { | 
 | 	using namespace llvm; | 
 |  | 
 | 	std::string TargetTriple("bpf-pc-linux"); | 
 | 	std::string Error; | 
 | 	const Target* Target = TargetRegistry::lookupTarget(TargetTriple, Error); | 
 | 	if (!Target) { | 
 | 		llvm::errs() << Error; | 
 | 		return std::unique_ptr<llvm::SmallVectorImpl<char>>(nullptr); | 
 | 	} | 
 |  | 
 | 	llvm::TargetOptions Opt; | 
 | 	TargetMachine *TargetMachine = | 
 | 		Target->createTargetMachine(TargetTriple, | 
 | 					    "generic", "", | 
 | 					    Opt, Reloc::Static); | 
 |  | 
 | 	Module->setDataLayout(TargetMachine->createDataLayout()); | 
 | 	Module->setTargetTriple(TargetTriple); | 
 |  | 
 | 	std::unique_ptr<SmallVectorImpl<char>> Buffer(new SmallVector<char, 0>()); | 
 | 	raw_svector_ostream ostream(*Buffer); | 
 |  | 
 | 	legacy::PassManager PM; | 
 | 	if (TargetMachine->addPassesToEmitFile(PM, ostream, | 
 | 					       TargetMachine::CGFT_ObjectFile)) { | 
 | 		llvm::errs() << "TargetMachine can't emit a file of this type\n"; | 
 | 		return std::unique_ptr<llvm::SmallVectorImpl<char>>(nullptr);; | 
 | 	} | 
 | 	PM.run(*Module); | 
 |  | 
 | 	return std::move(Buffer); | 
 | } | 
 |  | 
 | } | 
 |  | 
 | extern "C" { | 
 | void perf_clang__init(void) | 
 | { | 
 | 	perf::LLVMCtx.reset(new llvm::LLVMContext()); | 
 | 	LLVMInitializeBPFTargetInfo(); | 
 | 	LLVMInitializeBPFTarget(); | 
 | 	LLVMInitializeBPFTargetMC(); | 
 | 	LLVMInitializeBPFAsmPrinter(); | 
 | } | 
 |  | 
 | void perf_clang__cleanup(void) | 
 | { | 
 | 	perf::LLVMCtx.reset(nullptr); | 
 | 	llvm::llvm_shutdown(); | 
 | } | 
 |  | 
 | int perf_clang__compile_bpf(const char *filename, | 
 | 			    void **p_obj_buf, | 
 | 			    size_t *p_obj_buf_sz) | 
 | { | 
 | 	using namespace perf; | 
 |  | 
 | 	if (!p_obj_buf || !p_obj_buf_sz) | 
 | 		return -EINVAL; | 
 |  | 
 | 	llvm::opt::ArgStringList CFlags; | 
 | 	auto M = getModuleFromSource(std::move(CFlags), filename); | 
 | 	if (!M) | 
 | 		return  -EINVAL; | 
 | 	auto O = getBPFObjectFromModule(&*M); | 
 | 	if (!O) | 
 | 		return -EINVAL; | 
 |  | 
 | 	size_t size = O->size_in_bytes(); | 
 | 	void *buffer; | 
 |  | 
 | 	buffer = malloc(size); | 
 | 	if (!buffer) | 
 | 		return -ENOMEM; | 
 | 	memcpy(buffer, O->data(), size); | 
 | 	*p_obj_buf = buffer; | 
 | 	*p_obj_buf_sz = size; | 
 | 	return 0; | 
 | } | 
 | } |