Last active
March 10, 2019 08:03
-
-
Save MaxXSoft/fd3bcb1b3901861268564da90023ab56 to your computer and use it in GitHub Desktop.
Generate a simple program to object file using LLVM.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
generate a simple program to object file using LLVM | |
int main(int argc, const char *argv[]) { | |
if (argc == 2) { | |
puts(argv[1]); | |
} | |
return 0; | |
} | |
*/ | |
#include <memory> | |
#include <system_error> | |
#include "llvm/IR/LLVMContext.h" | |
#include "llvm/IR/IRBuilder.h" | |
#include "llvm/IR/Module.h" | |
#include "llvm/IR/Type.h" | |
#include "llvm/IR/Function.h" | |
#include "llvm/IR/BasicBlock.h" | |
#include "llvm/IR/Constants.h" | |
#include "llvm/IR/InlineAsm.h" | |
#include "llvm/Support/TargetSelect.h" | |
#include "llvm/Support/TargetRegistry.h" | |
#include "llvm/Target/TargetMachine.h" | |
#include "llvm/Target/TargetOptions.h" | |
#include "llvm/Support/FileSystem.h" | |
#include "llvm/IR/LegacyPassManager.h" | |
#include "llvm/Transforms/Utils.h" | |
#include "llvm/Transforms/InstCombine/InstCombine.h" | |
#include "llvm/Transforms/Scalar.h" | |
#include "llvm/Transforms/Scalar/GVN.h" | |
using namespace llvm; | |
void InitTarget() { | |
// initialize target registry | |
InitializeAllTargetInfos(); | |
InitializeAllTargets(); | |
InitializeAllTargetMCs(); | |
InitializeAllAsmParsers(); | |
InitializeAllAsmPrinters(); | |
} | |
bool Compile(Module *module, const char *filename) { | |
// initialize target triple | |
std::string target_error; | |
auto target_tri = sys::getDefaultTargetTriple(); | |
module->setTargetTriple(target_tri); | |
// lookup target in target registry | |
auto target = TargetRegistry::lookupTarget(target_tri, target_error); | |
if (!target) { | |
errs() << target_error << '\n'; | |
return false; | |
} | |
// initialize target machine | |
TargetOptions opt; | |
auto rm = Optional<Reloc::Model>(); | |
auto machine = target->createTargetMachine(target_tri, "generic", "", opt, rm); | |
module->setDataLayout(machine->createDataLayout()); | |
// open object file | |
std::error_code ec; | |
raw_fd_ostream dest(filename, ec, sys::fs::F_None); | |
if (ec) { | |
errs() << "could not open file '" << filename << "': " << ec.message() << "\n"; | |
return false; | |
} | |
// compile to object file | |
legacy::PassManager pass; | |
auto file_type = TargetMachine::CGFT_ObjectFile; | |
if (machine->addPassesToEmitFile(pass, dest, nullptr, file_type)) { | |
errs() << "target machine cannot emit file of this type\n"; | |
return false; | |
} | |
pass.run(*module); | |
dest.flush(); | |
return true; | |
} | |
auto InitFPM(Module *module) { | |
auto fpm = std::make_unique<legacy::FunctionPassManager>(module); | |
// allocas to registers | |
fpm->add(createPromoteMemoryToRegisterPass()); | |
// peephole optimizations | |
fpm->add(createInstructionCombiningPass()); | |
// reassociate expressions | |
fpm->add(createReassociatePass()); | |
// eliminate common sub-expressions | |
fpm->add(createGVNPass()); | |
// simplify the control flow graph | |
fpm->add(createCFGSimplificationPass()); | |
fpm->doInitialization(); | |
return fpm; | |
} | |
int main(int argc, const char *argv[]) { | |
// create context, builder, top module and pass manager | |
LLVMContext context; | |
IRBuilder<> builder(context); | |
auto module = std::make_unique<Module>("test module", context); | |
auto fpm = InitFPM(module.get()); | |
// declare 'puts' function | |
Type *puts_arg_ty[] = {builder.getInt8PtrTy()}; | |
auto puts_type = FunctionType::get(builder.getInt32Ty(), puts_arg_ty, false); | |
auto puts_func = module->getOrInsertFunction("puts", puts_type); | |
// create main function declaration | |
// int main(int argc, char **argv) | |
Type *main_arg_ty[] = {builder.getInt32Ty(), builder.getInt8PtrTy()->getPointerTo()}; | |
auto main_type = FunctionType::get(builder.getInt32Ty(), main_arg_ty, false); | |
auto main_func = Function::Create(main_type, Function::ExternalLinkage, "main", module.get()); | |
// create entry block for function | |
auto entry = BasicBlock::Create(context, "entry", main_func); | |
builder.SetInsertPoint(entry); | |
// generate contidion | |
// argc == 2 | |
auto cond = builder.CreateICmpEQ(main_func->arg_begin(), builder.getInt32(2)); | |
// generate blocks of 'if' | |
// if (cond) { then_block } | |
auto then_block = BasicBlock::Create(context, "then", main_func); | |
auto merge_block = BasicBlock::Create(context, "merge"); | |
builder.CreateCondBr(cond, then_block, merge_block); | |
// generate code in 'then' block | |
// puts(argv[1]) | |
builder.SetInsertPoint(then_block); | |
auto str_ptr = builder.CreateGEP(main_func->arg_begin() + 1, builder.getInt32(1)); | |
auto str = builder.CreateLoad(str_ptr); | |
builder.CreateCall(puts_func, str); | |
builder.CreateBr(merge_block); | |
// generate 'merge' block | |
main_func->getBasicBlockList().push_back(merge_block); | |
builder.SetInsertPoint(merge_block); | |
// generate return | |
// return 0 | |
builder.CreateRet(builder.getInt32(0)); | |
// optimize function | |
fpm->run(*main_func); | |
// dump generated IR | |
module->print(errs(), nullptr); | |
// compile to object | |
if (argc == 2) { | |
InitTarget(); | |
Compile(module.get(), argv[1]); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment