This repository has been archived by the owner on Jun 12, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add brainf**k compiler by LLVM IR and unique_resource
- Loading branch information
Showing
4 changed files
with
445 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
TARGET = brainfk_llvm_compiler_generator_example.cpp | ||
CXX = /usr/local/Cellar/llvm/4.0.1/bin/clang++ # Any clang | ||
FLAGS = -std=c++1z | ||
INCLUDE_PATH = -I$(HOME)/Documents/src/cpp/IncludeLibraries/ -I /usr/local/Cellar/boost/1.64.0_1/include/ # Using boost | ||
LIB_PATH = `llvm-config --cxxflags --ldflags --libs --system-libs` | ||
|
||
target: $(TARGET) | ||
$(CXX) $(LIB_PATH) $(FLAGS) $(INCLUDE_PATH) $(TARGET) | ||
run: $(TARGET) | ||
$(CXX) $(LIB_PATH) $(FLAGS) $(INCLUDE_PATH) $(TARGET) | ||
./a.out # dump | ||
./a.out 2>&1 | lli # pipe lli | ||
$(CXX) output.o -o fizzbuzz # assembly object file | ||
./fizzbuzz # execute binary | ||
clean: a.out | ||
$(RM) output.o fizzbuzz a.out | ||
|
28 changes: 28 additions & 0 deletions
28
example/brainfk/llvm/brainfk_llvm_compiler_generator_example.cpp
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#include <srook/brainfk/llvm/brainfk_compiler.hpp> | ||
|
||
int main() | ||
{ | ||
using namespace std::string_literals; | ||
namespace br_keywords = srook::brainfk::label; | ||
|
||
const auto bk_keywords = srook::brainfk::make_keywords(( | ||
br_keywords::_INCREMENT_THE_POINTER = ">"s, | ||
br_keywords::_DECREMENT_THE_POINTER = "<"s, | ||
br_keywords::_INCREMENT_THE_BYTE_AT_THE_POINTER = "+"s, | ||
br_keywords::_DECREMENT_THE_BYTE_AT_THE_POINTER = "-"s, | ||
br_keywords::_OUTPUT = "."s, | ||
br_keywords::_INPUT = ","s, | ||
br_keywords::_JUMP_FORWARD = "["s, | ||
br_keywords::_JUMP_BACKWARD = "]"s)); | ||
|
||
srook::brainfk::brainfk_syntax_llvm_compiler<std::string> bkcomp(bk_keywords); | ||
if(!bkcomp.file_open("../sample_bf/fizzbuzz.bf")){ | ||
return EXIT_FAILURE; | ||
} | ||
if(!bkcomp.analyze()){ | ||
return EXIT_FAILURE; | ||
} | ||
bkcomp.exec(); | ||
bkcomp.dump_IR(); | ||
bkcomp.output_object("output.o"); | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,256 @@ | ||
// Copyright (C) 2017 roki | ||
#ifndef INCLUDED_SROOK_BRAINFXXK_LLVM_COMPILER_HPP | ||
#define INCLUDED_SROOK_BRAINFXXK_LLVM_COMPILER_HPP | ||
|
||
#include <functional> | ||
#include <srook/brainfk/brainfk.hpp> | ||
#include <srook/scope/unique_resource.hpp> | ||
|
||
#if __has_include(<llvm/ADT/Optional.h>) and\ | ||
__has_include(<llvm/IR/BasicBlock.h>) and\ | ||
__has_include(<llvm/IR/IRBuilder.h>) and\ | ||
__has_include(<llvm/IR/LegacyPassManager.h>) and\ | ||
__has_include(<llvm/IR/Module.h>) and\ | ||
__has_include(<llvm/Support/FileSystem.h>) and\ | ||
__has_include(<llvm/Support/TargetRegistry.h>) and\ | ||
__has_include(<llvm/Support/TargetSelect.h>) and\ | ||
__has_include(<llvm/Support/raw_ostream.h>) and\ | ||
__has_include(<llvm/Target/TargetMachine.h>) and\ | ||
__has_include(<llvm/Target/TargetOptions.h>) | ||
#include <llvm/ADT/Optional.h> | ||
#include <llvm/IR/BasicBlock.h> | ||
#include <llvm/IR/IRBuilder.h> | ||
#include <llvm/IR/LegacyPassManager.h> | ||
#include <llvm/IR/Module.h> | ||
#include <llvm/Support/FileSystem.h> | ||
#include <llvm/Support/TargetRegistry.h> | ||
#include <llvm/Support/TargetSelect.h> | ||
#include <llvm/Support/raw_ostream.h> | ||
#include <llvm/Target/TargetMachine.h> | ||
#include <llvm/Target/TargetOptions.h> | ||
#else | ||
#error "This feature needs llvm libraries" | ||
#endif | ||
|
||
namespace srook { | ||
namespace brainfk{ | ||
|
||
template <class String, class FileStream, class BYTE, std::size_t BUFFER_SIZE = 300000> | ||
struct brainfk_syntax_llvm_compiler_core : brainfk_syntax_core<String, FileStream, BYTE, BUFFER_SIZE> { | ||
|
||
using base_type = brainfk_syntax_core<String, FileStream, BYTE, BUFFER_SIZE>; | ||
|
||
brainfk_syntax_llvm_compiler_core(const std::array<String, 8>& tk) | ||
: base_type::brainfk_syntax_core(tk),llvm_wrap(nullptr) | ||
{} | ||
|
||
const OPTIONAL<String>& execer(decltype(std::cout) & outer) override | ||
{ | ||
llvm_wrap = std::make_unique<llvm_brainfk_wrapper>(); | ||
|
||
llvm_wrap->allocate(BUFFER_SIZE); | ||
{ | ||
auto scoped_llvm = srook::make_unique_resource(llvm_wrap.get(),[this](llvm_brainfk_wrapper*& ll){ll->destroy();}); | ||
|
||
std::array<typename llvm_brainfk_wrapper::LoopBlock,BUFFER_SIZE> buff{}; | ||
typename decltype(buff)::iterator iter = std::begin(buff); | ||
std::size_t buff_index = 0; | ||
|
||
if (!this -> fl) { | ||
return this -> fl; | ||
} | ||
for (std::size_t i = 0; i < this->tokenizer.size(); ++i) { | ||
switch (this->tokenizer[i]) { | ||
case base_type::Syn::LEFT_ARROW: | ||
llvm_wrap->move_ptr(1); | ||
break; | ||
case base_type::Syn::RIGHT_ARROW: | ||
llvm_wrap->move_ptr(-1); | ||
break; | ||
case base_type::Syn::PLUS: | ||
llvm_wrap->incdecr(1); | ||
break; | ||
case base_type::Syn::MINUS: | ||
llvm_wrap->incdecr(-1); | ||
break; | ||
case base_type::Syn::DOT: | ||
llvm_wrap->put(); | ||
break; | ||
case base_type::Syn::CAMMA: | ||
llvm_wrap->get(); | ||
break; | ||
case base_type::Syn::LEFT_BRACE: | ||
llvm_wrap->loop_start(iter++,buff_index++); | ||
break; | ||
case base_type::Syn::RIGHT_BRACE: | ||
if(--iter < std::begin(buff))return (this -> fl = NULLOPT); | ||
llvm_wrap->loop_end(iter); | ||
break; | ||
} | ||
} | ||
} | ||
analyzed = true; | ||
|
||
return this -> fl; | ||
} | ||
|
||
void dump_IR() | ||
{ | ||
if(analyzed){ | ||
llvm_wrap->get_module()->dump(); | ||
} | ||
} | ||
|
||
void output_object(std::string output_object_name) | ||
{ | ||
if(!analyzed)return; | ||
|
||
using namespace llvm; | ||
|
||
InitializeAllTargetInfos(); | ||
InitializeAllTargets(); | ||
InitializeAllTargetMCs(); | ||
InitializeAllAsmParsers(); | ||
InitializeAllAsmPrinters(); | ||
|
||
const std::string target_triple = sys::getDefaultTargetTriple(); | ||
std::string err; | ||
const Target* target = TargetRegistry::lookupTarget(target_triple,err); | ||
if(!target){ | ||
std::cerr << "Failed to lookup target: " << err << std::endl; | ||
return; | ||
} | ||
TargetOptions opt; | ||
TargetMachine* target_m = target->createTargetMachine(target_triple,"generic","",opt,Optional<Reloc::Model>()); | ||
llvm_wrap->get_module()->setTargetTriple(target_triple); | ||
llvm_wrap->get_module()->setDataLayout(target_m->createDataLayout()); | ||
|
||
std::error_code err_code; | ||
raw_fd_ostream dest(std::move(output_object_name),err_code,sys::fs::F_None); | ||
if(err_code){ | ||
std::cerr << "Failed to open file: " << err_code.message() << "\n"; | ||
return; | ||
} | ||
legacy::PassManager pass; | ||
if(target_m->addPassesToEmitFile(pass,dest,TargetMachine::CGFT_ObjectFile)){ | ||
std::cerr << "Failed to emit a file of this type\n"; | ||
} | ||
pass.run(*llvm_wrap->get_module()); | ||
dest.flush(); | ||
} | ||
private: | ||
struct llvm_brainfk_wrapper{ | ||
struct LoopBlock{ | ||
llvm::BasicBlock* cond; | ||
llvm::BasicBlock* body; | ||
llvm::BasicBlock* end; | ||
}; | ||
|
||
explicit llvm_brainfk_wrapper() | ||
: context_{}, | ||
builder_{context_}, | ||
module_{std::make_unique<llvm::Module>("top",context_)}, | ||
main_function_{llvm::Function::Create(llvm::FunctionType::get(llvm::Type::getInt32Ty(context_),false),llvm::Function::ExternalLinkage,"main",module_.get())}, | ||
data_{nullptr},ptr_{nullptr} | ||
{ | ||
using namespace llvm; | ||
builder_.SetInsertPoint(BasicBlock::Create(context_,"",main_function_)); | ||
} | ||
|
||
llvm::Value* allocate(const std::size_t size) | ||
{ | ||
using namespace llvm; | ||
data_ = builder_.CreateAlloca(builder_.getInt8PtrTy(),nullptr,"data_"); | ||
ptr_ = builder_.CreateAlloca(builder_.getInt8PtrTy(),nullptr,"ptr_"); | ||
Function* calloc = cast<Function>(module_->getOrInsertFunction("calloc",builder_.getInt8PtrTy(),builder_.getInt64Ty(),builder_.getInt64Ty(),nullptr)); | ||
Value* data_ptr = builder_.CreateCall(calloc,{builder_.getInt64(size),builder_.getInt64(1)}); | ||
builder_.CreateStore(data_ptr,data_); | ||
builder_.CreateStore(data_ptr,ptr_); | ||
|
||
return data_; | ||
} | ||
|
||
void loop_start(typename std::array<LoopBlock,BUFFER_SIZE>::iterator iter,std::size_t index) | ||
{ | ||
using namespace llvm; | ||
using namespace std::string_literals; | ||
|
||
iter->cond = BasicBlock::Create(context_,"loop_cond"s + std::to_string(index),main_function_); | ||
iter->body = BasicBlock::Create(context_,"loop_body"s + std::to_string(index),main_function_); | ||
iter->end = BasicBlock::Create(context_,"loop_end"s + std::to_string(index),main_function_); | ||
builder_.CreateBr(iter->cond); | ||
builder_.SetInsertPoint(iter->cond); | ||
builder_.CreateCondBr(builder_.CreateICmpNE(builder_.CreateLoad(builder_.CreateLoad(ptr_)),builder_.getInt8(0)),iter->body,iter->end); | ||
builder_.SetInsertPoint(iter->body); | ||
} | ||
|
||
void loop_end(typename std::array<LoopBlock,BUFFER_SIZE>::iterator iter) | ||
{ | ||
builder_.CreateBr(iter->cond); | ||
builder_.SetInsertPoint(iter->end); | ||
} | ||
|
||
void move_ptr(int diff) | ||
{ | ||
builder_.CreateStore(builder_.CreateInBoundsGEP(builder_.getInt8Ty(),builder_.CreateLoad(ptr_),builder_.getInt32(diff)),ptr_); | ||
} | ||
|
||
void incdecr(int diff) | ||
{ | ||
using llvm::Value; | ||
Value* tmp = builder_.CreateLoad(ptr_); | ||
builder_.CreateStore(builder_.CreateAdd(builder_.CreateLoad(tmp),builder_.getInt8(diff)),tmp); | ||
} | ||
|
||
void put() | ||
{ | ||
using namespace llvm; | ||
Function* putchar = cast<Function>(module_->getOrInsertFunction("putchar",builder_.getInt32Ty(),builder_.getInt32Ty(),nullptr)); | ||
builder_.CreateCall(putchar,builder_.CreateSExt(builder_.CreateLoad(builder_.CreateLoad(ptr_)),builder_.getInt32Ty())); | ||
} | ||
|
||
void get() | ||
{ | ||
using namespace llvm; | ||
Function* getchar = cast<Function>(module_->getOrInsertFunction("getchar",builder_.getInt32Ty(),builder_.getInt32Ty(),nullptr)); | ||
builder_.CreateStore(builder_.CreateTrunc(builder_.CreateCall(getchar),builder_.getInt8Ty()),builder_.CreateLoad(ptr_)); | ||
} | ||
|
||
void destroy() | ||
{ | ||
using namespace llvm; | ||
Function* free = cast<Function>(module_->getOrInsertFunction("free",builder_.getVoidTy(),builder_.getInt8PtrTy(),nullptr)); | ||
builder_.CreateCall(free,{builder_.CreateLoad(data_)}); | ||
builder_.CreateRet(builder_.getInt32(EXIT_SUCCESS)); | ||
} | ||
|
||
std::unique_ptr<llvm::Module>& get_module() | ||
{ | ||
return module_; | ||
} | ||
private: | ||
llvm::LLVMContext context_; | ||
llvm::IRBuilder<> builder_; | ||
std::unique_ptr<llvm::Module> module_; | ||
llvm::Function* main_function_; | ||
llvm::Value* data_,*ptr_; | ||
}; | ||
private: | ||
std::unique_ptr<llvm_brainfk_wrapper> llvm_wrap; | ||
bool analyzed = false; | ||
}; | ||
|
||
template <class, class = unsigned char, std::size_t = 300000> | ||
struct brainfk_syntax_llvm_compiler; | ||
template <class BYTE, std::size_t size> | ||
struct brainfk_syntax_llvm_compiler<std::string, BYTE, size> final : brainfk_syntax_llvm_compiler_core<std::string, std::ifstream, BYTE, size> { | ||
using brainfk_syntax_llvm_compiler_core<std::string, std::ifstream, BYTE, size>::brainfk_syntax_llvm_compiler_core; | ||
}; | ||
template <class BYTE, std::size_t size> | ||
struct brainfk_syntax_llvm_compiler<std::wstring, BYTE, size> final : brainfk_syntax_llvm_compiler_core<std::wstring, std::wifstream, BYTE, size> { | ||
using brainfk_syntax_llvm_compiler_core<std::wstring, std::wifstream, BYTE, size>::brainfk_syntax_llvm_compiler_core; | ||
}; | ||
|
||
} // namespace brainfk | ||
} // namespace srook | ||
#endif |
Oops, something went wrong.