å æ¥LLVMã®å ¥éè¨äºãæ¸ãã¾ããã clangãåãLLVM IR (Intermediate representation, ä¸é表ç¾) ãé ¼ãã«ãBrainf**kã®ã³ã³ãã¤ã©ãæ¸ãã¦ã¿ã¾ããã itchyny.hatenablog.com ãã®è¨äºã§æ¸ããã³ã¼ãã§ã¯ãç´æ¥printfã§LLVM IRã®å½ä»¤ãåºåãã¦ãã¾ããã ãã®ã¹ããããè¸ããã¨ã§ãLLVM IRã®å½ä»¤ãã©ã調ã¹ãã°ãããã«ã¤ãã¦èº«ã«ã¤ããã¨æãã¾ãã
ãããããã®ãã³ã³ãã¤ã©ãã¯æ¬¡ã®ãããªåé¡ãããã¾ããã
- bf2llvmã³ãã³ããåºåããã®ãLLVM IRã®ããã«ãlliãllcã¨ãã£ãLLVM IRã®ã©ã³ã¿ã¤ã ãã³ã³ãã¤ã©ãå¿ è¦ã¨ãªã
- æé©åãè¡ãã«ã¯ããã«optã³ãã³ããå¿ è¦ã«ãªãããã¯ãLLVMãã¼ã«ãã§ã¤ã³ãã¤ã³ã¹ãã¼ã«ãã¦ããç°å¢ã§ãã使ããªã
- ã½ã¼ã¹ã³ã¼ãããå®è¡ãã¡ã¤ã«ã¾ã§ã®ãã¹ã®ä¸ã§ãLLVM IRã®æååã«å¤æãã¦ãã¼ã¹ãã¦ããç®æããã
- ä¸æ£ãªIRã³ã¼ããåãå¯è½æ§ãé«ã
- ä¸æå¤æ°ã®ã¤ã³ããã¯ã¹ãèªåã§ç®¡çãã¦ãããã¡ã³ããã³ã¹æ§ãæªã
LLVMãã¼ã«ãã§ã¤ã³ã«ã¯ãLLVM IRã®ã³ã¼ããããã°ã©ã ããåãããã®APIãå«ã¾ãã¦ãã¾ãã ä»åã¯LLVM APIã使ã£ã¦ã³ã³ãã¤ã©ãä½ãç·´ç¿ããã¦ã¿ã¾ãã
LLVM API, Hello world!
ã¾ãã¯ã¨ã¦ãçãã³ã¼ããçæããã¨ããããå§ãã¾ãããã Kaleidoscopeã®ãã¥ã¼ããªã¢ã«ã横ç®ã«ããªããã³ã¼ããæ¸ãã¦ããã¾ãã
次ã®ãããªã³ã¼ããèãã¦ã¿ã¾ãã
test.c
int main() { return 42; }
ãã®ã³ã¼ãã«å¯¾å¿ããLLVM IRã®ã³ã¼ãã¯æ¬¡ã®ããã«ãªãã¾ã (clang -S -emit-llvm -O2 test.c
ã®åºåãåã£ããã®ã§ã)ã
test.ll
define i32 @main() { ret i32 42 }
ãã®IRãåãã³ã¼ããæ¸ãã¦ã¿ã¾ãã
test.cpp
#include "llvm/IR/IRBuilder.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" static llvm::LLVMContext TheContext; static llvm::IRBuilder<> Builder(TheContext); static std::unique_ptr<llvm::Module> TheModule; int main() { TheModule = llvm::make_unique<llvm::Module>("top", TheContext); llvm::Function* mainFunc = llvm::Function::Create( llvm::FunctionType::get(llvm::Type::getInt32Ty(TheContext), false), llvm::Function::ExternalLinkage, "main", TheModule.get()); Builder.SetInsertPoint(llvm::BasicBlock::Create(TheContext, "", mainFunc)); Builder.CreateRet(Builder.getInt32(42)); TheModule->dump(); }
ã³ã³ãã¤ã«ãã¦å®è¡ãã¦ã¿ã¾ãã
$ g++ `llvm-config --cxxflags --ldflags --libs --system-libs` test.cpp -o test $ ./test ; ModuleID = 'top' source_filename = "top" define i32 @main() { ret i32 42 } $ ./test 2>&1 | lli $ echo $? 42
TheModule->dump()
ãæ¨æºã¨ã©ã¼åºåã«IRãåºåããã®ã§2>&1
ãã¦ãã¾ãã
lliã使ãã¨LLVM IRå½ä»¤åãç´æ¥å®è¡ãã¦ãåä½ã確èªãããã¨ãã§ãã¾ãã
ç°¡åãªã¨ãããã説æãã¦ããã¾ãããã
次ã®è¡ã¯ret i32 42
ãçæãã¦ããã³ã¼ãã§ãã
Builder.CreateRet(Builder.getInt32(42));
IRBuilderã®å種ã¡ã½ããã調ã¹ãæã¯ãdoxygenã§çæããããã«ããå½¹ã«ç«ã¡ã¾ãã
CreateRet
ã®å¼æ°ã¯1ã¤ã§ãValue*
ãåãã¾ãã
getInt32
ã¯IRBuilderBaseã§å®ç¾©ããã¦ãã¾ãã
次ã®ã³ã¼ãã¯define i32 @main()
ã«å¯¾å¿ãã¾ãã
llvm::Function* mainFunc = llvm::Function::Create( llvm::FunctionType::get(llvm::Type::getInt32Ty(TheContext), false), llvm::Function::ExternalLinkage, "main", TheModule.get());
llvm::FunctionType::get
ã§ã°ã°ã£ã¦åºã¦æ¥ãFunctionTypeãè¦ãã¨ãllvm::FunctionType::get
ã®å¼æ°ã¯2ã¤ã¾ãã¯3ã¤ã§ãå¼æ°ã®åã¯çç¥ã§ãããã¨ããããã¾ãã
é¢æ°ãä½ã£ãå¾ã«ä¸èº«ãçæããã«ã¯ãSetInsertPoint
ã使ã£ã¦ã³ã¼ããåãå ´æãæå®ãã¾ãã
Builder.SetInsertPoint(llvm::BasicBlock::Create(TheContext, "", mainFunc));
llvm::BasicBlock::Create
ã¯ã³ã¼ããããã¯ãä½ããã©ãã«ãçæãã¾ãã
Builder.SetInsertPoint(llvm::BasicBlock::Create(TheContext, "entry", mainFunc));
ãã®ããã«ã©ãã«åãæå®ããã¨ã次ã®ããã«ãªãã¾ãã
define i32 @main() { entry: ret i32 42 }
ã©ãã«ã使ã£ã¦éãã§ã¿ã¾ãããã
br
(branch) å½ä»¤ã使ã£ã¦ä½ã£ãã©ãã«ã«ã¸ã£ã³ããã¦ã¿ã¾ãã
test.c
Builder.SetInsertPoint(llvm::BasicBlock::Create(TheContext, "entry", mainFunc)); llvm::BasicBlock* newBlock = llvm::BasicBlock::Create(TheContext, "new_label", mainFunc); Builder.CreateBr(newBlock); Builder.CreateRet(Builder.getInt32(42)); Builder.SetInsertPoint(newBlock); Builder.CreateRet(Builder.getInt32(36));
åä½ã確èªãã¾ãã
$ g++ `llvm-config --cxxflags --ldflags --libs --system-libs` test.cpp -o test $ ./test ; ModuleID = 'top' source_filename = "top" define i32 @main() { entry: br label %new_label ret i32 42 new_label: ; preds = %entry ret i32 36 } $ ./test 2>&1 | lli $ echo $? 36
llvm::BasicBlock::Create
âBuilder.CreateBr
ã®é ã«å¼ãã§ãã¾ããããã¡ãã¨ä¸ã®ã©ãã«ã¸ã®br
å½ä»¤ãä½ããã¨ãã§ãã¦ãã¾ãã
llvm::BasicBlock::Create
ã¯ãæå®ãããé¢æ°ã®ä¸çªæå¾ã«ãããã¯ãçæããã¨ããé¢æ°ãªã®ã§ã (第åå¼æ°ãæå®ãã¦çæå ´æãå¶å¾¡ãããã¨ãã§ãã¾ã)ã
次ã«ãç°¡åãªæ¼ç®ãè¡ãã³ã¼ããçæãã¦ã¿ã¾ãã
int main() { int a, b; a = 32; b = a + 8; return a + b; }
72ã§çµäºããã³ã¼ãã§ãã ããã«å¯¾å¿ããLLVM IRã³ã¼ããæã§æ¸ãã¦ã¿ã¾ãã IRã«æ £ãã¦ãã¦ããããããã®ãµã¤ãºãªãæã§æ¸ããããã«ãªã£ã¦ãã¾ããã
test.ll
define i32 @main() { %a = alloca i32 %b = alloca i32 store i32 32, i32* %a %1 = load i32, i32* %a %2 = add i32 %1, 8 store i32 %2, i32* %b %3 = load i32, i32* %a %4 = load i32, i32* %b %5 = add i32 %3, %4 ret i32 %5 }
å®éãã®ã³ã¼ãã¯å®è¡ã§ãã¾ãã
$ lli test.ll $ echo $? 72
ãã®LLVM IRãåºåããã³ã¼ããIRBuilderã使ã£ã¦æ¸ãã¦ã¿ã¾ãã
test.cpp
#include "llvm/IR/IRBuilder.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" static llvm::LLVMContext TheContext; static llvm::IRBuilder<> Builder(TheContext); static std::unique_ptr<llvm::Module> TheModule; int main() { TheModule = llvm::make_unique<llvm::Module>("top", TheContext); llvm::Function* mainFunc = llvm::Function::Create( llvm::FunctionType::get(llvm::Type::getInt32Ty(TheContext), false), llvm::Function::ExternalLinkage, "main", TheModule.get()); Builder.SetInsertPoint(llvm::BasicBlock::Create(TheContext, "", mainFunc)); llvm::Value* a = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr, "a"); llvm::Value* b = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr, "b"); Builder.CreateStore(Builder.getInt32(32), a); Builder.CreateStore(Builder.CreateAdd(Builder.CreateLoad(a), Builder.getInt32(8)), b); Builder.CreateRet(Builder.CreateAdd(Builder.CreateLoad(a), Builder.CreateLoad(b))); TheModule->dump(); }
åä½ã確èªãã¾ãã
$ g++ `llvm-config --cxxflags --ldflags --libs --system-libs` test.cpp -o test $ ./test ; ModuleID = 'top' source_filename = "top" define i32 @main() { %a = alloca i32 %b = alloca i32 store i32 32, i32* %a %1 = load i32, i32* %a %2 = add i32 %1, 8 store i32 %2, i32* %b %3 = load i32, i32* %a %4 = load i32, i32* %b %5 = add i32 %3, %4 ret i32 %5 } $ ./test 2>&1 | lli 72
å°ããã¤IRBuilderã®è§¦ãæ¹ãåãã£ã¦ãã¾ããã LLVM IRã®ã©ã®å½ä»¤ã使ãã°ãããããã£ã¦ããã°ãIRBuilderã®ããã¥ã¡ã³ãã®ä¸ã§ãã¼ã¸å æ¤ç´¢ãã¦ã³ã¼ããæ¸ãã¦ãããã¨ãã§ãã¾ãã
Builder.CreateStore(Builder.CreateAdd(Builder.CreateLoad(a), Builder.getInt32(8)), b);
ãã®ä¸è¡ã次ã®ä¸è¡ã«å¯¾å¿ãããã¨ããããã¾ãã
%1 = load i32, i32* %a %2 = add i32 %1, 8 store i32 %2, i32* %b
ãããªãããããä¸æå¤æ°ã®ããã®ã¤ã³ããã¯ã¹ãèªåã§ç®¡çããªãã¦ãè¯ãã§ããã ã ãã¶ã³ã¼ããæ¸ãã®ã楽ã«ãªãã¾ããã

ãã¤ãããã§ããããLLVM ~ã³ã³ãã¤ã©ãèªä½ããããã®ã¬ã¤ãããã¯~
- ä½è :ææ¨ é¤ å,風è¬
- çºå£²æ¥: 2013/06/21
- ã¡ãã£ã¢: åè¡æ¬ï¼ã½ããã«ãã¼ï¼
Brainf**kã®ã³ã³ãã¤ã©ãæ¸ãã¦ã¿ããï¼
ååã®è¨äºã§ãBrainf**kã®ã³ã³ãã¤ã©ãæ¸ãã¾ããããç´æ¥LLVM IRãåãã³ã¼ãã ã£ãããã«ä¸æå¤æ°ã管çããªãã¨ãããªããä¸æ£ãªã³ã¼ããåºåãã¦ãã¾ãããã«ãªããã½ã¼ã¹ã³ã¼ããããµãã¨è¨ã£ãåé¡ãããã¾ããã ä»åã¯LLVM APIã使ã£ã¦æ¸ãç´ãã¦ã¿ã¾ãããã
ã¾ãã¯Brainf**kã®ãã¼ã¿é åã®ç¢ºä¿ã¨éæ¾ãè¡ãé¨åãæ¸ãã¦ã¿ã¾ãã Cã§æ¸ãã¨
#include <stdlib.h> int main() { char* data = (char*)calloc(30000, sizeof(char)); char* ptr = data; free(data); }
ã«å¯¾å¿ããã³ã¼ãã§ãã
bf2llvm.cpp
#include "llvm/IR/IRBuilder.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" static llvm::LLVMContext TheContext; static llvm::IRBuilder<> Builder(TheContext); static std::unique_ptr<llvm::Module> TheModule; int main() { TheModule = llvm::make_unique<llvm::Module>("top", TheContext); llvm::Function* mainFunc = llvm::Function::Create( llvm::FunctionType::get(llvm::Type::getInt32Ty(TheContext), false), llvm::Function::ExternalLinkage, "main", TheModule.get()); Builder.SetInsertPoint(llvm::BasicBlock::Create(TheContext, "", mainFunc)); llvm::Value* data = Builder.CreateAlloca(Builder.getInt8PtrTy(), nullptr, "data"); llvm::Value* ptr = Builder.CreateAlloca(Builder.getInt8PtrTy(), nullptr, "ptr"); llvm::Function* funcCalloc = llvm::cast<llvm::Function>( TheModule->getOrInsertFunction("calloc", Builder.getInt8PtrTy(), Builder.getInt64Ty(), Builder.getInt64Ty(), nullptr)); llvm::Value* data_ptr = Builder.CreateCall(funcCalloc, {Builder.getInt64(30000), Builder.getInt64(1)}); Builder.CreateStore(data_ptr, data); Builder.CreateStore(data_ptr, ptr); llvm::Function* funcFree = llvm::cast<llvm::Function>( TheModule->getOrInsertFunction("free", Builder.getVoidTy(), Builder.getInt8PtrTy(), nullptr)); Builder.CreateCall(funcFree, {Builder.CreateLoad(data)}); Builder.CreateRet(Builder.getInt32(0)); TheModule->dump(); }
å®è¡ãã¦åä½ã確ããã¾ãã
$ g++ `llvm-config --cxxflags --ldflags --libs --system-libs` bf2llvm.cpp -o bf2llvm $ ./bf2llvm ; ModuleID = 'top' source_filename = "top" define i32 @main() { %data = alloca i8* %ptr = alloca i8* %1 = call i8* @calloc(i64 30000, i64 1) store i8* %1, i8** %data store i8* %1, i8** %ptr %2 = load i8*, i8** %data call void @free(i8* %2) ret i32 0 } declare i8* @calloc(i64, i64) declare void @free(i8*)
é¢æ°calloc
ã¨free
ãå¼ãã§ãã¾ããã
llvm::Function* funcCalloc = llvm::cast<llvm::Function>( TheModule->getOrInsertFunction("calloc", Builder.getInt8PtrTy(), Builder.getInt64Ty(), Builder.getInt64Ty(), nullptr)); llvm::Value* data_ptr = Builder.CreateCall(funcCalloc, {Builder.getInt64(30000), Builder.getInt64(1)});
getOrInsertFunction
ã§ã¯é¢æ°åãè¿ãå¤ã®åã¨å¼æ°ã®åãåãã¾ãã
ã¢ã¸ã¥ã¼ã«ã®ä¸ããé¢æ°ãæ¢ãã¦è¿ãã¾ããããããªããã°declare
ã«ãã£ã¦å®£è¨ãã¾ã (ãªã³ã¯æã«è§£æ±ºããã¾ã)ã
ããã¦CreateCall
ã«ãã£ã¦call
å½ä»¤ãä½ã£ã¦ãã¾ãã
次ã¯ãã¤ã³ã¿ã®ç§»åãæ¸ãã¦ã¿ã¾ãã
Brainf**kã§ã¯>
, <
å½ä»¤ã«ç¸å½ããã³ã¼ãã§ãã
Cã ã¨++ptr
, --ptr
ã«å¯¾å¿ãã¾ãã
ååã®ã¨ã³ããªã¼ã§ã¯æ¬¡ã®ããã«æ¸ãã¦ãã¾ããã
int idx = 1; void emit_move_ptr(int diff) { printf(" %%%d = load i8*, i8** %%ptr, align 8\n", idx); printf(" %%%d = getelementptr inbounds i8, i8* %%%d, i32 %d\n", idx + 1, idx, diff); printf(" store i8* %%%d, i8** %%ptr, align 8\n", idx + 1); idx += 2; }
ããã¯æ¬¡ã®ããã«ãªãã¾ãã
void emit_move_ptr(llvm::Value* ptr, int diff) { Builder.CreateStore( Builder.CreateInBoundsGEP( Builder.getInt8Ty(), Builder.CreateLoad(ptr), Builder.getInt32(diff)), ptr); }
CreateInBoundsGEP
ãgetelementptr
å½ä»¤ãçæãã¾ãã
次ã«ãã¡ã¢ãªã¼ã«è¶³ãããå¼ããããã+
, -
å½ä»¤ã«ç¸å½ããã³ã¼ããæ¸ãã¦ã¿ã¾ãã
Cã§æ¸ãã¨++*ptr
ã--*ptr
ã¨ãªãã³ã¼ãã§ããã
void emit_add(llvm::Value* ptr, int diff) { llvm::Value* tmp = Builder.CreateLoad(ptr); Builder.CreateStore( Builder.CreateAdd( Builder.CreateLoad(tmp), Builder.getInt8(diff)), tmp); }
ããã¦ãBrainf**kã®.
, Cã§ã¯putchar(*ptr)
ã«ç¸å½ããã³ã¼ããæ¸ãã¦ã¿ã¾ãã
ååè¦ãããã«ãsext
å½ä»¤ã§i8
ããi32
ã«å¤æããå¿
è¦ãããã¾ãã
void emit_put(llvm::Value* ptr) { llvm::Function* funcPutChar = llvm::cast<llvm::Function>( TheModule->getOrInsertFunction("putchar", Builder.getInt32Ty(), Builder.getInt32Ty(), nullptr)); Builder.CreateCall( funcPutChar, Builder.CreateSExt( Builder.CreateLoad(Builder.CreateLoad(ptr)), Builder.getInt32Ty())); }
ããã§>
, <
, +
, -
, .
ã®5å½ä»¤ã®æºåãã§ããã®ã§ãæ¨æºå
¥åããBrainf**kãåãåã£ã¦LLVM IRãåãããã«ãã¦ã¿ã¾ãããã
bf2llvm.cpp
// ... llvm::Value* data_ptr = Builder.CreateCall(funcCalloc, {Builder.getInt64(30000), Builder.getInt64(1)}); Builder.CreateStore(data_ptr, data); Builder.CreateStore(data_ptr, ptr); char c; while (std::cin.get(c)) { switch (c) { case '>': emit_move_ptr(ptr, 1); break; case '<': emit_move_ptr(ptr, -1); break; case '+': emit_add(ptr, 1); break; case '-': emit_add(ptr, -1); break; case '.': emit_put(ptr); break; } } llvm::Function* funcFree = llvm::cast<llvm::Function>( // ...
åããã¦ã¿ã¾ãããã
$ g++ `llvm-config --cxxflags --ldflags --libs --system-libs` bf2llvm.cpp -o bf2llvm $ echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.+++++++++++++++++++++++++++++.+++++++..+++.>++++++++++++++++++++++++++++++++.<++++++++.--------.+++.------.--------.>+." | ./bf2llvm 2>&1 | lli Hello world!
æ£ããIRãåãã¦ãããã§ããã
ã«ã¼ãã«å¯¾å¿ããã
Brainf**kã®[
ã¯Cè¨èªã®while (*ptr) {
ã«ã]
ã¯}
ã«å¯¾å¿ãã¾ãã
LLVM IRã§ã¯ãããã
br label %while_cond while_cond: %5 = load i8*, i8** %%ptr %6 = load i8, i8* %5 %7 = icmp ne i8 %6, 0 br i1 %7, label %while_body, label %while_end while_body:
ã¨
br label %while_cond while_end:
ã«å¯¾å¿ãã¾ãã
Brainf**kã®ã³ã¼ãã§ã¯ã«ã¼ãã¯ä½åº¦ãåºã¦ããã®ã§ãwhile_cond6 .. while_body6 .. while_end6
ã®ããã«æ°åãã¤ãã¦ã©ãã«ãåºå¥ããå¿
è¦ãããã¾ãã
ååã¯ãã®çªå·ãemit_while_start
ã«æ¸¡ãã¦IRãç´æ¥åºåãã¦ãã¾ããããä»åã¯ã¸ã£ã³ãããæã«llvm::BasicBlock*
ã使ãå¿
è¦ããããããæ§é ä½ãä½ã£ã¦ç®¡çãã¦ã¿ã¾ããã
struct WhileBlock { llvm::BasicBlock* cond_block; llvm::BasicBlock* body_block; llvm::BasicBlock* end_block; }; void emit_while_start(llvm::Function* func, llvm::Value* ptr, WhileBlock* while_block, int while_index) { while_block->cond_block = llvm::BasicBlock::Create( TheContext, std::string("while_cond") + std::to_string(while_index), func); while_block->body_block = llvm::BasicBlock::Create( TheContext, std::string("while_body") + std::to_string(while_index), func); while_block->end_block = llvm::BasicBlock::Create( TheContext, std::string("while_end") + std::to_string(while_index), func); Builder.CreateBr(while_block->cond_block); Builder.SetInsertPoint(while_block->cond_block); Builder.CreateCondBr( Builder.CreateICmpNE( Builder.CreateLoad(Builder.CreateLoad(ptr)), Builder.getInt8(0)), while_block->body_block, while_block->end_block); Builder.SetInsertPoint(while_block->body_block); } void emit_while_end(WhileBlock* while_block) { Builder.CreateBr(while_block->cond_block); Builder.SetInsertPoint(while_block->end_block); } // ... int while_index = 0; WhileBlock while_blocks[1000]; WhileBlock* while_block_ptr = while_blocks; // ... case '[': emit_while_start(mainFunc, ptr, while_block_ptr++, while_index++); break; case ']': emit_while_end(--while_block_ptr); break;
llvm::BasicBlock::Create
ãé¢æ°ã®æå¾ã«ãããã¯ãã¤ããã¨ããã®ã¨ãBuilder.SetInsertPoint
ã§IRãåããããã¯ã移åããã¨ãããã¨ãç解ãã¦ããã°ããããªã«é£ãããªãã¨æãã¾ãã
$ g++ `llvm-config --cxxflags --ldflags --libs --system-libs` bf2llvm.cpp -o bf2llvm $ echo "+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.------------.<++++++++.--------.+++.------.--------.>+." | ./bf2llvm 2>&1 | lli Hello, world! $ cat prime.bf ++++++++++[->+++++>++++>+++<<<]>>++++>++>>+>+++<<<<<.>>>>>[<[-]++<[-]+>>>+[<[->> +<<]<[->>>>+>+<<<<<]>>>>[-<<<<+>>>>]<[->+>-[>+>>]>[+[-<+>]>+>>]<<<<<<]>[-<<<+>>> ]>[-]>[-<<+>+>]<<[->>+<<]+>[<[-]>[-]]<[<<<<<[-]>>>>>[-]]>[-]>[-]>[-]<<<<<<-<[->> >+>+<<<<]>>>>[-<<<<+>>>>]<<<[->>>>+>+<<<<<]>>>>>[-<<<<<+>>>>>]<<<+[->>[->+>+<<]> >[-<<+>>]+<[>-<<->[-]]>[<<<+<[-]>>>>[-]]<[-]<<<]>>[-]<[<<->>[-]]<[-]<<+<[->>>+>+ <<<<]>>>>[-<<<<+>>>>]>+++++++++++++<<[->>[->+>+<<]>>[-<<+>>]+<[>-<<->[-]]>[<<<+< [-]>>>>[-]]<[-]<<<]>>[-]<[<<[-[-]]>>[-]]<[-]<<<+>>]<<<[<.>>>>>++++++++++<<[->+>- [>+>>]>[+[-<+>]>+>>]<<<<<<]>[-<+>]>[-]>>>++++++++++<[->-[>+>>]>[+[-<+>]>+>>]<<<< <]>[-]>>[>++++++[-<++++++++>]<.<<+>+>[-]]<[<[->-<]++++++[->++++++++<]>.[-]]>[-]< <<++++++[-<++++++++>]<.[-]<<<<<[-]]>>+] $ cat prime.bf | ./bf2llvm 2>&1 | /usr/local/opt/llvm/bin/lli 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251
大ä¸å¤«ããã§ããã
ãªãã¸ã§ã¯ããã¡ã¤ã«ãçæããã
ããã¾ã§TheModule->dump()
ã使ã£ã¦LLVM IRãåºåãã¦ãã¾ããã
ããããããã§ã¯å®è¡ããããã«lliãå¿
è¦ã¨ãªããçµå±LLVMãã¼ã«ãã§ã¤ã³ã®å
¥ã£ã¦ãªãç°å¢ã§ã¯åãããã¨ãã§ãã¾ããã
Kaleidoscopeã®Chapter 8ãåèã«ããªãã (ã¾ã£ããåãã§ããã¾ã ç解ã追ãã¤ãã¦ãªãã¦â¦)ããªãã¸ã§ã¯ããã¡ã¤ã«ãçæãã¦ã¿ã¾ãããã
TargetTriple
ã¨ã¯ãã¢ã¼ããã¯ãã£, ãã³ãã¼, ãã©ãããã©ã¼ã ã®ãã¨ã§ãã
clang --version | grep Target
ã§ç¢ºèªãããã¨ãã§ãã¾ãã
ç§ã®æå
ã§ã¯x86_64-apple-darwin15.6.0
ã§ããã
std::string TargetTriple = llvm::sys::getDefaultTargetTriple();
ãã®TargetTriple
ã«å¯¾å¿ããtargetãå¼ãã¾ãã
std::string err; const llvm::Target* Target = llvm::TargetRegistry::lookupTarget(TargetTriple, err); if (!Target) { std::cerr << "Failed to lookup target " + TargetTriple + ": " + err; return 1; }
CPUãæå®ãã¾ãã ãã®æå®ã«ãã£ã¦ã¡ã¢ãªã¼alignmentãendiannessãªã©ãå¤ããããã§ãã
llvm::TargetOptions opt; llvm::TargetMachine* TheTargetMachine = Target->createTargetMachine( TargetTriple, "generic", "", opt, llvm::Optional<llvm::Reloc::Model>());
ããã§å¼ããtargetã¨target machineããç§ãã¡ã®ã¢ã¸ã¥ã¼ã«ã«ã»ãããã¾ãã
TheModule->setTargetTriple(TargetTriple); TheModule->setDataLayout(TheTargetMachine->createDataLayout());
ãªãã¸ã§ã¯ããã¡ã¤ã«ãéãã¦
std::string Filename = "output.o"; std::error_code err_code; llvm::raw_fd_ostream dest(Filename, err_code, llvm::sys::fs::F_None); if (err_code) { std::cerr << "Could not open file: " << err_code.message(); return 1; }
æ¸ãè¾¼ãã§çµããã§ãã
llvm::legacy::PassManager pass; if (TheTargetMachine->addPassesToEmitFile(pass, dest, llvm::TargetMachine::CGFT_ObjectFile)) { std::cerr << "TheTargetMachine can't emit a file of this type\n"; return 1; } pass.run(*TheModule); dest.flush();
åããã¦ã¿ã¾ãããã
$ g++ `llvm-config --cxxflags --ldflags --libs --system-libs` bf2llvm.cpp -o bf2llvm $ echo "+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.------------.<++++++++.--------.+++.------.--------.>+." | ./bf2llvm Wrote output.o $ gcc output.o $ ./a.out Hello, world! $ cat fibonacci.bf >>+++++++++++[-<<++++>+++>>+<]>>+<++<<->>[>>>++++++++++<<[->+>-[>+>>]>[+[-<+>]>+ >>]<<<<<<]>>[-]>>>++++++++++<[->-[>+>>]>[+[-<+>]>+>>]<<<<<]>[-]>>[>++++++[-<++++ ++++>]<.<<+>+>[-]]<[<[->-<]++++++[->++++++++<]>.[-]]<<++++++[-<++++++++>]<.[-]<< [-<+>]<<[->>+>+<<<]>>>[-<<<+>>>]<-[<<<<<.>.>>>>[-]]<[->+>+<<]>>[-<<+>>]<<<<[->>+ <<]>>>[-<<<+>>>]<<-] $ cat fibonacci.bf | ./bf2llvm Wrote output.o $ gcc output.o $ ./a.out 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233
ãã¡ãã¨åãã¦ããã§ããã
ã¾ã¨ã
Brainf**kã®ã½ã¼ã¹ã³ã¼ããåãåãããªãã¸ã§ã¯ããã¡ã¤ã«ãçæããã³ã³ãã¤ã©ãä½ããã¨ãã§ãã¾ããã LLVM APIã使ããã¨ã§ã以ä¸ã®ãããªæ©æµãããã¾ããã
- ä¸æå¤æ°ã®ã¤ã³ããã¯ã¹ããã¡ãã¡ç®¡çããªãã¦ãã
- ãªãã¸ã§ã¯ããã¡ã¤ã«ãçæãããã¨ãã§ãã
- IRã®å½ä»¤ãæ½è±¡åããã¦ãããããããªIRãåã確çãæ¸ã
- IRãç´æ¥æ¸ãã¦ããã¨ãå¤é¨ã®é¢æ°ã®
declare
ãæ¸ããªããã°ãããªãã£ãããgetOrInsertFunction
ã使ãã¨æ°ã«ããå¿ è¦ããªããªã
APIã使ã£ã¦ã³ã¼ããæ¸ããã¨ã¯æå¤ã¨éª¨ãæããäºã§ãçµå±doxygenã®çæããã¥ã¡ã³ããè¡ã£ããæ¥ããããªãã試è¡é¯èª¤ããå¿ è¦ãããã¾ããã æå¤ã ã£ããã¨ã¯ãIRã®ã·ã³ã¿ãã¯ã¹çã«ããããªã³ã¼ããåããã¨ãããã¨ãããã¨ã§ãã ä¾ãã°
llvm::Value* a = Builder.CreateAlloca(Builder.getInt32Ty(), nullptr, "a"); Builder.CreateAdd(Builder.CreateLoad(a), Builder.getInt8(8));
ãã®ã³ã¼ãã¯æ¬¡ã®ãããªã³ã¼ããåãã¾ãã
%1 = load i32, i32* %a %2 = add i32 %1, i8 8
å®è¡ãã¦ã¿ãã¨ã¨ã©ã¼ã«ãªãã¾ãã
lli: <stdin>:7:20: error: expected value token %2 = add i32 %1, i8 8 ^
åãéããã§ã¯ãªãã¦ã·ã³ã¿ãã¯ã¹ã¨ã©ã¼ã«ãªããã¨ããããã§ããã å°æ¥æåãå¤ããããããã¾ãããã
ååã®è¨äºã§ãããç¨åº¦LLVM IRã«æ £ã親ããã§ããããã«ãIRBuilderãããªã楽ã«è§¦ããã¨ãã§ãã¾ããã éã«ãIRã®å½ä»¤ããªã«ãç¥ããã«IRBuilderã§ã³ã¼ãçæããã®ã¯ããªãé£ããã¨æãã¾ãã 対å¿ããCã®ã³ã¼ãããã£ã¦ã対å¿ããLLVM IRã³ã¼ãããã£ã¦ããããåºåãããããªIRBuilderã®é¢æ°ãæ¢ãã¾ãã Brainf**kã®ã³ã³ãã¤ã©ä½ããéãã¦ããªãLLVMã®ä¸çã®é°å²æ°ãããã£ã¦ãã¾ããã ããããKaleidoscopeãåèã«ããªãããå®ç¨çãªã·ã³ã¿ãã¯ã¹ãæã¤è¨èªã®ã³ã³ãã¤ã©ãä½ã£ã¦ã¿ããã¨æãã¾ãã
ã½ã¼ã¹ã³ã¼ã
bf2llvm.cpp
/* * Brainf**k compiler based on LLVM API * $ g++ `llvm-config --cxxflags --ldflags --libs --system-libs` bf2llvm.cpp -o bf2llvm * $ echo "+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.\ >-.------------.<++++++++.--------.+++.------.--------.>+." | \ ./bf2llvm * $ gcc output.o * $ ./a.out */ #include <iostream> #include <string> #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" static llvm::LLVMContext TheContext; static llvm::IRBuilder<> Builder(TheContext); static std::unique_ptr<llvm::Module> TheModule; void emit_move_ptr(llvm::Value* ptr, int diff) { Builder.CreateStore( Builder.CreateInBoundsGEP( Builder.getInt8Ty(), Builder.CreateLoad(ptr), Builder.getInt32(diff)), ptr); } void emit_add(llvm::Value* ptr, int diff) { llvm::Value* tmp = Builder.CreateLoad(ptr); Builder.CreateStore( Builder.CreateAdd( Builder.CreateLoad(tmp), Builder.getInt8(diff)), tmp); } void emit_put(llvm::Value* ptr) { llvm::Function* funcPutChar = llvm::cast<llvm::Function>( TheModule->getOrInsertFunction("putchar", Builder.getInt32Ty(), Builder.getInt32Ty(), nullptr)); Builder.CreateCall( funcPutChar, Builder.CreateSExt( Builder.CreateLoad(Builder.CreateLoad(ptr)), Builder.getInt32Ty())); } void emit_get(llvm::Value* ptr) { llvm::Function* funcGetChar = llvm::cast<llvm::Function>( TheModule->getOrInsertFunction("getchar", Builder.getInt32Ty(), nullptr)); Builder.CreateStore( Builder.CreateTrunc( Builder.CreateCall(funcGetChar), Builder.getInt8Ty()), Builder.CreateLoad(ptr)); } struct WhileBlock { llvm::BasicBlock* cond_block; llvm::BasicBlock* body_block; llvm::BasicBlock* end_block; }; void emit_while_start(llvm::Function* func, llvm::Value* ptr, WhileBlock* while_block, int while_index) { while_block->cond_block = llvm::BasicBlock::Create( TheContext, std::string("while_cond") + std::to_string(while_index), func); while_block->body_block = llvm::BasicBlock::Create( TheContext, std::string("while_body") + std::to_string(while_index), func); while_block->end_block = llvm::BasicBlock::Create( TheContext, std::string("while_end") + std::to_string(while_index), func); Builder.CreateBr(while_block->cond_block); Builder.SetInsertPoint(while_block->cond_block); Builder.CreateCondBr( Builder.CreateICmpNE( Builder.CreateLoad(Builder.CreateLoad(ptr)), Builder.getInt8(0)), while_block->body_block, while_block->end_block); Builder.SetInsertPoint(while_block->body_block); } void emit_while_end(WhileBlock* while_block) { Builder.CreateBr(while_block->cond_block); Builder.SetInsertPoint(while_block->end_block); } int main() { TheModule = llvm::make_unique<llvm::Module>("top", TheContext); llvm::Function* mainFunc = llvm::Function::Create( llvm::FunctionType::get(llvm::Type::getInt32Ty(TheContext), false), llvm::Function::ExternalLinkage, "main", TheModule.get()); Builder.SetInsertPoint(llvm::BasicBlock::Create(TheContext, "", mainFunc)); llvm::Value* data = Builder.CreateAlloca(Builder.getInt8PtrTy(), nullptr, "data"); llvm::Value* ptr = Builder.CreateAlloca(Builder.getInt8PtrTy(), nullptr, "ptr"); llvm::Function* funcCalloc = llvm::cast<llvm::Function>( TheModule->getOrInsertFunction("calloc", Builder.getInt8PtrTy(), Builder.getInt64Ty(), Builder.getInt64Ty(), nullptr)); llvm::Value* data_ptr = Builder.CreateCall(funcCalloc, {Builder.getInt64(30000), Builder.getInt64(1)}); Builder.CreateStore(data_ptr, data); Builder.CreateStore(data_ptr, ptr); int while_index = 0; WhileBlock while_blocks[1000]; WhileBlock* while_block_ptr = while_blocks; char c; while (std::cin.get(c)) { switch (c) { case '>': emit_move_ptr(ptr, 1); break; case '<': emit_move_ptr(ptr, -1); break; case '+': emit_add(ptr, 1); break; case '-': emit_add(ptr, -1); break; case '[': emit_while_start(mainFunc, ptr, while_block_ptr++, while_index++); break; case ']': if (--while_block_ptr < while_blocks) { std::cerr << "unmatching ]\n"; return 1; } emit_while_end(while_block_ptr); break; case '.': emit_put(ptr); break; case ',': emit_get(ptr); break; } } llvm::Function* funcFree = llvm::cast<llvm::Function>( TheModule->getOrInsertFunction("free", Builder.getVoidTy(), Builder.getInt8PtrTy(), nullptr)); Builder.CreateCall(funcFree, {Builder.CreateLoad(data)}); Builder.CreateRet(Builder.getInt32(0)); llvm::InitializeAllTargetInfos(); llvm::InitializeAllTargets(); llvm::InitializeAllTargetMCs(); llvm::InitializeAllAsmParsers(); llvm::InitializeAllAsmPrinters(); std::string TargetTriple = llvm::sys::getDefaultTargetTriple(); std::string err; const llvm::Target* Target = llvm::TargetRegistry::lookupTarget(TargetTriple, err); if (!Target) { std::cerr << "Failed to lookup target " + TargetTriple + ": " + err; return 1; } llvm::TargetOptions opt; llvm::TargetMachine* TheTargetMachine = Target->createTargetMachine( TargetTriple, "generic", "", opt, llvm::Optional<llvm::Reloc::Model>()); TheModule->setTargetTriple(TargetTriple); TheModule->setDataLayout(TheTargetMachine->createDataLayout()); std::string Filename = "output.o"; std::error_code err_code; llvm::raw_fd_ostream dest(Filename, err_code, llvm::sys::fs::F_None); if (err_code) { std::cerr << "Could not open file: " << err_code.message(); return 1; } llvm::legacy::PassManager pass; if (TheTargetMachine->addPassesToEmitFile(pass, dest, llvm::TargetMachine::CGFT_ObjectFile)) { std::cerr << "TheTargetMachine can't emit a file of this type\n"; return 1; } pass.run(*TheModule); dest.flush(); std::cout << "Wrote " << Filename << "\n"; return 0; }