[llvm]在llvm的codegen程式碼中呼叫printf函式
阿新 • • 發佈:2018-11-27
為了除錯方便,有時我們需要在codegen程式碼中新增printf函式,列印除錯資訊
PrintTest.cpp
//clang++ PrintTest.cpp `llvm-config --cxxflags --ldflags --system-libs --libs core mcjit native` -o p #include <iostream> #include "llvm/ADT/APFloat.h" #include "llvm/ADT/STLExtras.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" #include "llvm/IR/Type.h" #include "llvm/IR/Verifier.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/raw_os_ostream.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Transforms/Scalar.h" #include "llvm/Transforms/Scalar/GVN.h" #include "KaleidoscopeJIT.h" typedef void (*print_int32_ptr_t)(int32_t); llvm::Function *get_printf(llvm::Module *mod) { const char *fun_name = "printf"; llvm::Function *func = mod->getFunction(fun_name); if (func == nullptr) { llvm::FunctionType *func_type = llvm::FunctionType::get( llvm::Type::getInt32Ty(mod->getContext()), {llvm::Type::getInt8PtrTy(mod->getContext())}, true); func = llvm::Function::Create(func_type, llvm::GlobalValue::ExternalLinkage, fun_name, mod); } return func; } int main() { llvm::LLVMContext context; llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetAsmPrinter(); llvm::InitializeNativeTargetAsmParser(); auto native_jit = llvm::make_unique<llvm::orc::KaleidoscopeJIT>(); auto module = llvm::make_unique<llvm::Module>("print_module", context); module->setDataLayout(native_jit->getTargetMachine().createDataLayout()); const char *fun_name = "print_test"; llvm::FunctionType *func_type = llvm::FunctionType::get( llvm::Type::getVoidTy(context), {llvm::Type::getInt32Ty(context)}, false); auto func_ptr = llvm::Function::Create(func_type, llvm::GlobalValue::ExternalLinkage, fun_name, module.get()); auto func_arg_iter = func_ptr->arg_begin(); func_arg_iter->setName("val_"); llvm::Value *arg_val = &(*func_arg_iter); llvm::IRBuilder<> builder(context); auto bb_entry = llvm::BasicBlock::Create(context, "entry", func_ptr); builder.SetInsertPoint(bb_entry); auto printf_ptr = get_printf(module.get()); builder.CreateCall(printf_ptr, {builder.CreateGlobalStringPtr("in llvm fun, value = %d\n"), arg_val}); builder.CreateRetVoid(); llvm::raw_os_ostream os(std::cout); module->print(os,nullptr); auto jit_mod = native_jit->addModule(std::move(module)); auto print_sym = native_jit->findSymbol(fun_name); if(!print_sym){ std::cout << "cant't find symbol : " << fun_name << std::endl; return -1; } auto print_ptr = (print_int32_ptr_t)llvm::cantFail(print_sym.getAddress()); std::cout << "====================" << std::endl; (*print_ptr)(12); std::cout << "=====================" << std::endl; native_jit->removeModule(jit_mod); return 0; }
KaleidoscopeJIT.h
//===- KaleidoscopeJIT.h - A simple JIT for Kaleidoscope --------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Contains a simple JIT definition for use in the kaleidoscope tutorials. // //===----------------------------------------------------------------------===// #ifndef LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H #define LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H #include "llvm/ADT/iterator_range.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ExecutionEngine/ExecutionEngine.h" #include "llvm/ExecutionEngine/JITSymbol.h" #include "llvm/ExecutionEngine/RTDyldMemoryManager.h" #include "llvm/ExecutionEngine/SectionMemoryManager.h" #include "llvm/ExecutionEngine/Orc/CompileUtils.h" #include "llvm/ExecutionEngine/Orc/IRCompileLayer.h" #include "llvm/ExecutionEngine/Orc/LambdaResolver.h" #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Mangler.h" #include "llvm/Support/DynamicLibrary.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetMachine.h" #include <algorithm> #include <memory> #include <string> #include <vector> namespace llvm { namespace orc { class KaleidoscopeJIT { public: using ObjLayerT = RTDyldObjectLinkingLayer; using CompileLayerT = IRCompileLayer<ObjLayerT, SimpleCompiler>; using ModuleHandleT = CompileLayerT::ModuleHandleT; KaleidoscopeJIT() : TM(EngineBuilder().selectTarget()), DL(TM->createDataLayout()), ObjectLayer([]() { return std::make_shared<SectionMemoryManager>(); }), CompileLayer(ObjectLayer, SimpleCompiler(*TM)) { llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr); } TargetMachine &getTargetMachine() { return *TM; } ModuleHandleT addModule(std::unique_ptr<Module> M) { // We need a memory manager to allocate memory and resolve symbols for this // new module. Create one that resolves symbols by looking back into the // JIT. auto Resolver = createLambdaResolver( [&](const std::string &Name) { if (auto Sym = findMangledSymbol(Name)) return Sym; return JITSymbol(nullptr); }, [](const std::string &S) { return nullptr; }); auto H = cantFail(CompileLayer.addModule(std::move(M), std::move(Resolver))); ModuleHandles.push_back(H); return H; } void removeModule(ModuleHandleT H) { ModuleHandles.erase(find(ModuleHandles, H)); cantFail(CompileLayer.removeModule(H)); } JITSymbol findSymbol(const std::string Name) { return findMangledSymbol(mangle(Name)); } private: std::string mangle(const std::string &Name) { std::string MangledName; { raw_string_ostream MangledNameStream(MangledName); Mangler::getNameWithPrefix(MangledNameStream, Name, DL); } return MangledName; } JITSymbol findMangledSymbol(const std::string &Name) { #ifdef LLVM_ON_WIN32 // The symbol lookup of ObjectLinkingLayer uses the SymbolRef::SF_Exported // flag to decide whether a symbol will be visible or not, when we call // IRCompileLayer::findSymbolIn with ExportedSymbolsOnly set to true. // // But for Windows COFF objects, this flag is currently never set. // For a potential solution see: https://reviews.llvm.org/rL258665 // For now, we allow non-exported symbols on Windows as a workaround. const bool ExportedSymbolsOnly = false; #else const bool ExportedSymbolsOnly = true; #endif // Search modules in reverse order: from last added to first added. // This is the opposite of the usual search order for dlsym, but makes more // sense in a REPL where we want to bind to the newest available definition. for (auto H : make_range(ModuleHandles.rbegin(), ModuleHandles.rend())) if (auto Sym = CompileLayer.findSymbolIn(H, Name, ExportedSymbolsOnly)) return Sym; // If we can't find the symbol in the JIT, try looking in the host process. if (auto SymAddr = RTDyldMemoryManager::getSymbolAddressInProcess(Name)) return JITSymbol(SymAddr, JITSymbolFlags::Exported); #ifdef LLVM_ON_WIN32 // For Windows retry without "_" at beginning, as RTDyldMemoryManager uses // GetProcAddress and standard libraries like msvcrt.dll use names // with and without "_" (for example "_itoa" but "sin"). if (Name.length() > 2 && Name[0] == '_') if (auto SymAddr = RTDyldMemoryManager::getSymbolAddressInProcess(Name.substr(1))) return JITSymbol(SymAddr, JITSymbolFlags::Exported); #endif return nullptr; } std::unique_ptr<TargetMachine> TM; const DataLayout DL; ObjLayerT ObjectLayer; CompileLayerT CompileLayer; std::vector<ModuleHandleT> ModuleHandles; }; } // end namespace orc } // end namespace llvm #endif // LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H
使用如下命令編譯:
clang++ PrintTest.cpp `llvm-config --cxxflags --ldflags --system-libs --libs core mcjit native` -o p
輸出結果:
; ModuleID = 'print_module' source_filename = "print_module" target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" @0 = private unnamed_addr constant [25 x i8] c"in llvm fun, value = %d\0A\00" define void @print_test(i32 %val_) { entry: %0 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([25 x i8], [25 x i8]* @0, i32 0, i32 0), i32 %val_) ret void } declare i32 @printf(i8*, ...) ==================== in llvm fun, value = 12 =====================