ã³ã³ãã¤ã©ãä½ã£ã¦ã¿ããã¨æã£ã¦ãã¦ããã¢ã»ã³ããªè¨èªã¯ããããããªãã ãã¼ãµã¼ã¿ãããªã³ã¼ãã¯æ¸ãããã¨ãããããã©ãã³ã¼ãçæã®å¦çã¯ãã£ã±ãã ã å®è¡ãã¡ã¤ã«ããã¤ããªã¨ãã£ã¿ã¼ã§è¦ãã¨ããªã«ããæãã
ãããªç§ãªã®ã§ãããLLVMã«èå³ãæã¡å§ãã¦ãã¾ãã SwiftãRustããããã¯Emscriptenãªã©ãè¿å¹´æ³¨ç®ããã¦ããè¨èªãã³ã³ãã¤ã©æè¡ã®ä¸æ¢ã«ã¯LLVMãããã¾ãã ã¢ã»ã³ããªã¯ããåãããªãã¦ãLLVMã使ãããªããããã«ãªãã°ããã«ããã©ãããã©ã¼ã ã§å®è¡ãã¡ã¤ã«ãçæã§ããè¨èªå¦çç³»ãä½ãã®ã§ã¯ãªããã ã³ã³ãã¤ã©ä½ã£ã¦ã¿ãããªãLLVMã使ã£ã¦ã¿ããããªã¨æã£ã¦ããä»æ¥ãã®ããã§ãã
ã¨ããããããLLVMãåå¼·ãããã¨æã£ã¦ãã©ãããå§ããã°ãããããåããã¾ããã§ããã ããã¥ã¢ã«ã¯å·¨å¤§ã§èªãæ°ãèµ·ããªããããªãã¡ã¬ã³ã¹ãè¦ã¦ããã£ã±ãã§ãã é°å²æ°ã¯ããããã§ããã¤ã³ã¹ãã©ã¯ã·ã§ã³ã£ã¦ããã®ã¯ããªããã£ããªå½ä»¤ã®ãã¨ã§ãããããã®ãªãã¡ã¬ã³ã¹ã¯ä¾¿å©ãªãã§ãããã ã§ãç´ äººã«ã¯ã©ãããæãä»ãã¦ããã®ãããããªãã LLVMãç¨ããå¦çç³»ã®å ¥éã¨ãã¦åé«ãKaleidoscopeããã£ã¦ã¿ããããªã¨æã£ã¦å§ãã¦ããLexerãä½ã£ã¦Parserãä½ã£ã¦ãããããC++ãªãã¦ä¹ ã ã«æ¸ãããªãããªãã¦è¨ã£ã¦ãããã¡ã«LLVM IRã«ãã©ãçããã«é£½ãã¦ãã¾ãã C++ãèã«åããªããã ã¨æã£ã¦Goè¨èªã®LLVM bindingã使ã£ã¦ã³ã¼ããæ¸ããã¨ãã¦ããGo bindingã®ã½ã¼ã¹ã³ã¼ãã¨LLVMæ¬ä½ã®ã³ã¼ãããã£ã¡ãã£ã¡é£ãã§åã£ã¦ç²å¼ãã¦ãã¾ãã ä»ã®äººãä½ã£ãå¦çç³»ã®ã³ã¼ããèªãã§ã¿ããã¨æã£ã¦cloneãã¦ã¿ã¦ãããã¤ã®éã«ãèªãæããªãå¿ãã¦ãã¾ãã
ãããªãã¡ãã¡ãªç§ã¯ãã£ããã©ãããã°ããã®ãã¨æã£ã¦èª¿ã¹ã¦ããã®ã§ãããããæ¥æ¬¡ã®ãããªããã°è¨äºãè¦ã¤ãã¾ããã
ããã ããããªç°¡åãªãã¨ãå¿ãã¦ããã
LLVMã使ã£ã¦ããã¨ã¦ã身è¿ãªã³ã³ãã¤ã©ããããããªããï¼
ç§ã¯æ
ã¦ã¦ãã®ããã°ã®äººã¨åããã¨ããã¦ã¿ã¾ãã (æ¬è¨äºã¯LLVMãã¤ã³ã¹ãã¼ã«ããã¦ãããLLVMãã¼ã«ã®ãã¹ãéã£ã¦ãããã¨ãåæã¨ãã¾ããç§ã®æå
ã§ã¯/usr/local/opt/llvm/bin/
ã§ãã)ã
$ cat > test.c <<EOF int main() { return 42; } EOF $ clang -S -emit-llvm -O2 test.c $ lli test.ll $ echo $? 42
ãããªãç§ã«ãã§ããï¼ é«ã¶ãæ°æã¡ãæãã¦å®è¡ãã¡ã¤ã«çæã¾ã§è©¦ãã¦ã¿ã¾ãã
$ llc test.ll $ gcc test.s -o test $ ./test $ echo $? 42
å®è¡ãã¡ã¤ã«ãçæããå®è¡ãããã¨ãã§ãã¾ããã
clang -S -emit-llvm
ãçæãããã¡ã¤ã«ãtest.ll
ãããããããè¦ãã¦ã¿ã¾ãã
test.ll
; ModuleID = 'test.c' source_filename = "test.c" target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-apple-macosx10.12.0" ; Function Attrs: norecurse nounwind readnone ssp uwtable define i32 @main() #0 { ret i32 42 } attributes #0 = { norecurse nounwind readnone ssp uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+fxsr,+mmx,+sse,+sse2,+sse3,+sse4.1,+ssse3" "unsafe-fp-math"="false" "use-soft-float"="false" } !llvm.module.flags = !{!0} !llvm.ident = !{!1} !0 = !{i32 1, !"PIC Level", i32 2} !1 = !{!"Apple LLVM version 8.0.0 (clang-800.0.42.1)"}
ãã¼ããããã¯ãã£ã±ãã§ãã ã¨ããããã³ã¡ã³ããªã©ãããªããããªç©ãåã£ã¦ã¿ã¦ãã©ãã ãæ¶ãã¦ãåãã試ãã¦ã¿ã¾ãã
test.ll
source_filename = "test.c" define i32 @main() #0 { ret i32 42 } attributes #0 = { norecurse nounwind readnone ssp uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+fxsr,+mmx,+sse,+sse2,+sse3,+sse4.1,+ssse3" "unsafe-fp-math"="false" "use-soft-float"="false" }
$ lli test.ll; echo $? 42
åãã¾ãã
#0
ã¨æ¸ãã¦ããã®ãããåãããªãã§ããæ¶ãã¦ã¿ã¾ãã
test.ll
define i32 @main() { ret i32 42 }
$ lli test.ll; echo $? 42
åãã¾ããããªãã ãsource_filename
ããªãã¦ãåããã§ããâ¦
æ¹ãã¦æãå°ãããªã£ãLLVM IR (Intermediate representation, ä¸é表ç¾) ã®ã³ã¼ããçºãã¦ã¿ã¾ãã
test.ll
define i32 @main() { ret i32 42 }
define
ã§é¢æ°ãå®ç¾©ãã¦ããã®ããªãret
ã¯return
ã ãããªãi32
ã¯32bit intãªãã ããªã¨ã容æã«æ³åãä»ãã¾ãã
ãããªå°ããã³ã¼ããªããæã§æ¸ããã¨ãã§ãããã§ãã
ããã§ãããã§ã«define
, ret
, i32
ã¨ãã£ããã¼ã¯ã¼ããç解ãå§ãã¦ããã®ã§ãã
ããã¾ã§ãã£ããã¨ã¯ã¨ã¦ãç°¡åã§ããããããã«è©¦ãããã¨ã§ãã
ã¨ã¦ãå°ããªCã®ã³ã¼ããæ¸ããclang -S -emit-llvm
ã§LLVM IRãã¡ã¤ã«ãçæããlli
ãllc
ã¨ãã£ãã³ãã³ãã§å®è¡ã§ãããã¨ã確ãããªããããã¡ã¤ã«ãåã£ã¦æ¬å½ã«å¿
è¦ãªé¨åãæ¢ããã ãã§ãã
LLVMã®ãªãã¡ã¬ã³ã¹ã¯å
¨ãè¦ãªãã¦ããCã®ã©ãããã³ã¼ããã©ãããå½ä»¤åã«ãªããã¯clang
ãæãã¦ãããã®ã§ãã
ãã®æ¹æ³ãªããé·ããªãã¡ã¬ã³ã¹ãå ¥éãã¥ã¼ããªã¢ã«ãèªã¾ãªãã¦ããä¸æ©ãã¤çå®ã«LLVM IRãå¦ãã§ãããã®ã§ãã
ãã¤ãããã§ããããLLVM ~ã³ã³ãã¤ã©ãèªä½ããããã®ã¬ã¤ãããã¯~
- ä½è :ææ¨ é¤ å,風è¬
- çºå£²æ¥: 2013/06/21
- ã¡ãã£ã¢: åè¡æ¬ï¼ã½ããã«ãã¼ï¼
åºæ¬çãªå½ä»¤ãå¦ã¼ã
int main()
ã¨æ¸ãã¨define i32 @main()
ã¨ãªããã¨ããreturn 42
ãret i32 42
ã¨ãªããã¨ã¯ãããã¾ããã
ãããããã ãã§ã¯ä½ãã§ããªãã®ã§ãããã¤ãåºæ¬çãªæãå¤æãã¦ã¿ã¦LLVM IRãã©ããªããã調ã¹ã¦ã¿ã¾ãããã
ã¾ããå¤æ°ã®å®£è¨ã調ã¹ã¦ã¿ã¾ãã 次ã®ãããªã³ã¼ããæ¸ãã¦ã¿ã¾ãã
test.c
int main() { char c; int i; long l; }
ããã®LLVM IRãè¦ã¦ã¿ã¾ãã-O0
ã§æé©åãããªãããã«æ°ãã¤ãã¾ãã
$ clang -S -emit-llvm -O0 test.c $ cat test.ll
ã³ã¡ã³ããattributesãªã©ãåé¤ããã次ã®ããã«ãªãã¾ãã
test.ll
define i32 @main() { %1 = alloca i8, align 1 %2 = alloca i32, align 4 %3 = alloca i64, align 8 ret i32 0 }
å®éãã®ãã¡ã¤ã«ã¯å®è¡ã§ãã¾ãã
$ lli test.ll
ä¸è¡ãã¤è¦æ¯ã¹ãã¨ãchar
ãi8
, int
ãi32
, long
ãi64
ã«å¯¾å¿ãã¦ãããã¨ããããã¾ãã
align
ã¯ã¢ãã¬ã¹ã®ã¢ã©ã¤ã³ã¡ã³ãã表ãã¦ãããå¤æ°ã®ã¢ãã¬ã¹ããã®æ°åã®åæ°ã§ãããã¨ãä¿è¨¼ããã¾ãã
次ã«ä»£å ¥ã調ã¹ã¾ãã å ç¨ã®ã³ã¼ãã«ä»£å ¥ã追å ãã¦èª¿ã¹ã¦ã¿ã¾ãã
test.c
int main() { char c; int i; long l; c = 'a'; i = 72; l = 123456789012345; }
ãã®ã³ã¼ãã®LLVM IRã¯æ¬¡ã®ããã«ãªãã¾ãã
test.ll
define i32 @main() { %1 = alloca i8, align 1 %2 = alloca i32, align 4 %3 = alloca i64, align 8 store i8 97, i8* %1, align 1 store i32 72, i32* %2, align 4 store i64 123456789012345, i64* %3, align 8 ret i32 0 }
store
å½ä»¤ã¨ãªãã¾ããã
第ä¸å¼æ°ã代å
¥ãããå¤ã§ã第äºå¼æ°ã代å
¥å
ã®ã¢ãã¬ã¹ã¨ãªãããã§ãã
å°ãã³ã¼ããå¤ãã¦ã¿ã¾ãã
test.c
int main() { char a, b; a = 32; b = a; }
ãã®ã³ã¼ãã®LLVM IRã¯æ¬¡ã®ããã«ãªãã¾ãã
test.ll
define i32 @main() { %1 = alloca i8, align 1 %2 = alloca i8, align 1 store i8 32, i8* %1, align 1 %3 = load i8, i8* %1, align 1 store i8 %3, i8* %2, align 1 ret i32 0 }
%1
ã«32
ãä¿åãã%1
ã®å¤ãload
å½ä»¤ã§åãåºãã¦%3
ã¨ããããã%2
ã«store
ãã¦ãã¾ãã
足ãç®ãå¼ãç®ããã¨ã©ããªãã§ãããããint
å士ã®è¶³ãå¼ããè¦ã¦ã¿ã¾ãã
test.c
int main() { int a, b, c; a = 32; b = a + 24; c = b - a; }
test.ll
define i32 @main() { ; int a, b, c %1 = alloca i32, align 4 %2 = alloca i32, align 4 %3 = alloca i32, align 4 ; a = 32 store i32 32, i32* %1, align 4 ; b = a + 24 %4 = load i32, i32* %1, align 4 %5 = add nsw i32 %4, 24 store i32 %5, i32* %2, align 4 ; c = b - a %6 = load i32, i32* %2, align 4 %7 = load i32, i32* %1, align 4 %8 = sub nsw i32 %6, %7 store i32 %8, i32* %3, align 4 ret i32 0 }
ãã¯ã足ãç®ã¯add
å½ä»¤, å¼ãç®ã¯sub
å½ä»¤ã¨ãªãã¾ããã
nsw
ã¯ç解ãå¾åããã¦è¯ãã§ãããã
å¾ã¯æ¨æºåºåãæ°ã«ãªãã¾ããHello worldã®LLVM IRãè¦ã¦ã¿ã¾ãã
test.c
#include <stdio.h> int main() { printf("Hello, world!\n"); }
test.ll
@.str = private unnamed_addr constant [15 x i8] c"Hello, world!\0A\00", align 1 define i32 @main() { %1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @.str, i32 0, i32 0)) ret i32 0 } declare i32 @printf(i8*, ...)
declare
å½ä»¤ã§printf
ã使ããã¨ã宣è¨ããcall
å½ä»¤ã§å¼ã³åºããè¡ã£ã¦ãã¾ãã
printf
ã§ã¯ãªãã¦putchar
ã使ã£ã¦ã¿ã¾ãã
test.c
#include <stdio.h> int main() { char c; c = 'a'; putchar(c); }
test.ll
define i32 @main() { %1 = alloca i8, align 1 store i8 97, i8* %1, align 1 %2 = load i8, i8* %1, align 1 %3 = sext i8 %2 to i32 %4 = call i32 @putchar(i32 %3) ret i32 0 } declare i32 @putchar(i32)
ããè¦ãã¨ãload
ãã¦putchar
ããã ãã§ã¯ãªãã¦ãsext
å½ä»¤ãå
¥ã£ã¦ãã¾ãã
ããããã°putchar
ã®å¼æ°ã¯char
ã§ã¯ãªãã¦int
ã§ãããã
sext
ã¯signed extendã®ç¥ã§ãåã®å¤æãè¡ãå½ä»¤ã§ãã
Brainf**kã®ã³ã³ãã¤ã©ãä½ã£ã¦ã¿ãã
宣è¨ã»ä»£å ¥ã»æ¼ç®ããã¦é¢æ°ã¨ãã¨ã¦ãåºæ¬çãªã³ã¼ããLLVM IRã§ã©ã®ããã«ãªããã観å¯ãã¦ãã¾ããã ããã ãã§ã¯å®ç¨çãªããã°ã©ãã³ã°è¨èªã®ã³ã³ãã¤ã©ãä½ãã«ã¯ç¥èã足ããªãã®ã§ãããããªããã£ããªè¨èªã®å¦çç³»ãªãä½ããæ°ããã¦ãã¾ããã
ããªããã£ããªè¨èªâ¦ã¨ããã°Brainf**kã§ãããã ç§ã¯ãã®è¨èªã大好ãã§ãã è¨èªå¦çç³»ã®Hello worldã¨è¨ã£ã¦ãããã®ã§ã¯ãªãã§ããããã å ã»ã©ç´¹ä»ããè¨äºã§ããBrainf**kã®ã³ã³ãã¤ã©ãæ¸ãã¦ãã¾ãã ç®çãåãã§ããããã£ããçä¼¼ã«ãªããªãããã«ã¡ãè¦ç¨åº¦ã«ãã¦ãèªåã®æã§æ¸ãã¦ã¿ããã¨æãã¾ãã
ã¾ãã¯Brainf**kã®ãã¼ã¿é åã®ç¨æããã¾ãã
test.c
#include <stdlib.h> int main() { char* data = (char*)calloc(30000, sizeof(char)); char* ptr = data; free(data); }
ãã®LLVM IRãè¦ã¦ã¿ã¾ãããã
test.ll
define i32 @main() { %1 = alloca i8*, align 8 %2 = alloca i8*, align 8 %3 = call i8* @calloc(i64 30000, i64 1) store i8* %3, i8** %1, align 8 %4 = load i8*, i8** %1, align 8 store i8* %4, i8** %2, align 8 %5 = load i8*, i8** %1, align 8 call void @free(i8* %5) ret i32 0 } declare i8* @calloc(i64, i64) declare void @free(i8*)
i8*
åã®å¤æ°data
ã¨ptr
ãä½ããcalloc
ã®çµæã代å
¥ãã¦ãã¾ãã
次ã«ããã¤ã³ã¿ã¼ã®ç§»å>
ã¨ã¤ã³ã¯ãªã¡ã³ã+
ã«å¯¾å¿ããCã®ã³ã¼ãããLLVM IRã§è¦ã¦ã¿ã¾ãã
test.ll
; > ++ptr %5 = load i8*, i8** %2, align 8 %6 = getelementptr inbounds i8, i8* %5, i32 1 store i8* %6, i8** %2, align 8 ; + ++*ptr %7 = load i8*, i8** %2, align 8 %8 = load i8, i8* %7, align 1 %9 = add i8 %8, 1 store i8 %9, i8* %7, align 1
getelementptr
ã¨ããæ°ããå½ä»¤ãåºã¦ãã¾ãããã
æå³ã¯å¾ã§èª¿ã¹ãã¨ãã¦ãä»ã¯ãããããã®ã ã¨æã£ã¦ããã¾ãããã
++*ptr
ã¯ãptr
ã®å¤ã%7
ã«å
¥ãã¦ãpointer dereferenceãã¦%8
ã«å
¥ãã¦ã1足ãã¦%9
ã«å
¥ãã¦ãããã%7
ã«ä»£å
¥ãããã¨ä¸è¡ãã¤æå³ãèªã¿åããããã§ããã
ãã¨Brainf**kã®.
å½ä»¤ã«å¯¾å¿ããputchar(*ptr);
ã®ã³ã¼ããè¦ã¦ããã¾ãã
test.ll
; . putchar(*ptr) %5 = load i8*, i8** %2, align 8 %6 = load i8, i8* %5, align 1 %7 = sext i8 %6 to i32 %8 = call i32 @putchar(i32 %7)
ããã§ãããsext
(signed extend) å½ä»¤ã§i32
ã«å¤æããå¿
è¦ãããã¾ãããã
Brainf**kã®>
, +
, .
ã«å¯¾å¿ããLLVM IRã³ã¼ããåããã¾ããã<
, -
ã®ã³ã¼ãã容æã«æ³åãã¤ãã¾ãã
ããããéå
·ãæã£ãã®ã§ãBrainf**kã®LLVM IRã³ã³ãã¤ã©ãæ¸ãã¦ã¿ã¾ãã
bf2llvm.c
#include <stdio.h> #include <stdlib.h> void emit_header() { printf("define i32 @main() {\n"); printf(" %%data = alloca i8*, align 8\n"); printf(" %%ptr = alloca i8*, align 8\n"); printf(" %%data_ptr = call i8* @calloc(i64 30000, i64 1)\n"); printf(" store i8* %%data_ptr, i8** %%data, align 8\n"); printf(" store i8* %%data_ptr, i8** %%ptr, align 8\n"); } 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_add(int diff) { printf(" %%%d = load i8*, i8** %%ptr, align 8\n", idx); printf(" %%%d = load i8, i8* %%%d, align 1\n", idx + 1, idx); printf(" %%%d = add i8 %%%d, %d\n", idx + 2, idx + 1, diff); printf(" store i8 %%%d, i8* %%%d, align 1\n", idx + 2, idx); idx += 3; } void emit_put() { printf(" %%%d = load i8*, i8** %%ptr, align 8\n", idx); printf(" %%%d = load i8, i8* %%%d, align 1\n", idx + 1, idx); printf(" %%%d = sext i8 %%%d to i32\n", idx + 2, idx + 1); printf(" %%%d = call i32 @putchar(i32 %%%d)\n", idx + 3, idx + 2); idx += 4; } void emit_footer() { printf(" %%%d = load i8*, i8** %%data, align 8\n", idx); printf(" call void @free(i8* %%%d)\n", idx); printf(" ret i32 0\n"); printf("}\n\n"); printf("declare i8* @calloc(i64, i64)\n\n"); printf("declare void @free(i8*)\n\n"); printf("declare i32 @putchar(i32)\n"); } int main() { char c; emit_header(); while ((c = getchar()) != EOF) { switch (c) { case '>': emit_move_ptr(1); break; case '<': emit_move_ptr(-1); break; case '+': emit_add(1); break; case '-': emit_add(-1); break; case '.': emit_put(); break; } } emit_footer(); return 0; }
åããã¦ã¿ã¾ãã
$ gcc bf2llvm.c -o bf2llvm $ echo "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++." | ./bf2llvm define i32 @main() { %data = alloca i8*, align 8 %ptr = alloca i8*, align 8 %data_ptr = call i8* @calloc(i64 30000, i64 1) store i8* %data_ptr, i8** %data, align 8 store i8* %data_ptr, i8** %ptr, align 8 %1 = load i8*, i8** %ptr, align 8 %2 = load i8, i8* %1, align 1 %3 = add i8 %2, 1 store i8 %3, i8* %1, align 1 ; ç¥ store i8 %195, i8* %193, align 1 %196 = load i8*, i8** %ptr, align 8 %197 = load i8, i8* %196, align 1 %198 = sext i8 %197 to i32 %199 = call i32 @putchar(i32 %198) %200 = load i8*, i8** %data, align 8 call void @free(i8* %200) ret i32 0 } declare i8* @calloc(i64, i64) declare void @free(i8*) declare i32 @putchar(i32) $ echo "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++." | ./bf2llvm | lli A
ãã£ãï¼
ããã§ã¯+
ã¨.
ãã試ãã¦ãªãã®ã§ä»ã®å½ä»¤ã使ã£ã¦ã¿ã¾ãã
$ echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.+++++++++++++++++++++++++++++.+++++++..+++.>++++++++++++++++++++++++++++++++.<++++++++.--------.+++.------.--------.>+." | ./bf2llvm | lli Hello world!
åãã¦ããã§ããï¼ æåã®å³ ã¯è¶ ããããã§ãï¼
ã«ã¼ãã«å¯¾å¿ããã
Brainf**kã®[
, ]
ã«å¯¾å¿ãã¾ãããã
ã¾ãã¯Cã§æ¸ãã¦clang
ã§LLVM IRã調ã¹ã¦ã¿ã¾ãã
[
ã¯while (*ptr) {
ã«ã]
ã¯}
ã«å¯¾å¿ãã¾ãã
以ä¸ã®ããã°ã©ã ã¯ãBrainf**kã®++[>++<-]>.
ã«å¯¾å¿ããã³ã¼ãã§ãå®è¡ããã"\x04"
ãåºåãã¾ãã
test.c
#include <stdio.h> #include <stdlib.h> int main() { char* data = (char*)calloc(30000, sizeof(char));; char* ptr = data; ++*ptr; ++*ptr; while (*ptr) { ++ptr; ++*ptr; ++*ptr; --ptr; --*ptr; } ++ptr; putchar(*ptr); free(data); }
LLVM IRã¯æ¬¡ã®ããã«ãªãã¾ããã
test.ll
define i32 @main() { %1 = alloca i32, align 4 %2 = alloca i8*, align 8 %3 = alloca i8*, align 8 store i32 0, i32* %1, align 4 %4 = call i8* @calloc(i64 30000, i64 1) ; ç¥ store i8 %11, i8* %9, align 1 br label %12 ; <label>:12 ; preds = %16, %0 %13 = load i8*, i8** %3, align 8 %14 = load i8, i8* %13, align 1 %15 = icmp ne i8 %14, 0 br i1 %15, label %16, label %30 ; <label>:16 ; preds = %12 %17 = load i8*, i8** %3, align 8 %18 = getelementptr inbounds i8, i8* %17, i32 1 store i8* %18, i8** %3, align 8 ; ç¥ store i8 %29, i8* %27, align 1 br label %12 ; <label>:30 ; preds = %12 %31 = load i8*, i8** %3, align 8 ; ç¥ ret i32 %38 } declare i8* @calloc(i64, i64) declare i32 @putchar(i32) declare void @free(i8*)
whileæã®ã¸ã£ã³ãã«br
(branch) å½ä»¤ããæ¡ä»¶æã«å¯¾å¿ããå ´æã¯icmp
(integer compare) ã使ããã¦ãããã¨ããããã¾ãã
; <label>:12
ã¯ãè¦ã¦ã®éãã©ãã«ã§ãbr label %12
ã§ããã«ã¸ã£ã³ããã¦ããã®ã ã¨æ³åãã¤ãã¾ãã
次ã®ããã«ã©ãã«ãèªã¿ãããæ¸ããã¨ãã§ãã¾ãã
br label %while_cond0 while_cond0: ; ç¥ br i1 %14, label %while_body0, label %while_end0 while_body0: ; ç¥ br label %while_cond0 while_end0:
whileæã®åºåã®ä»æ¹ãåãã£ãã®ã§ãã³ã³ãã¤ã©ã®ç¶ããæ¸ãã¦ã¿ã¾ãã
bf2llvm.c
void emit_while_start(int while_index) { printf(" br label %%while_cond%d\n", while_index); printf("while_cond%d:\n", while_index); printf(" %%%d = load i8*, i8** %%ptr, align 8\n", idx); printf(" %%%d = load i8, i8* %%%d, align 1\n", idx + 1, idx); printf(" %%%d = icmp ne i8 %%%d, 0\n", idx + 2, idx + 1); printf(" br i1 %%%d, label %%while_body%d, label %%while_end%d\n", idx + 2, while_index, while_index); printf("while_body%d:\n", while_index); idx += 3; } void emit_while_end(int while_index) { printf(" br label %%while_cond%d\n", while_index); printf("while_end%d:\n", while_index); } /* ç¥ */ int main() { char c; int while_index = 0; int while_indices[1000]; int* while_index_ptr = while_indices; emit_header(); while ((c = getchar()) != EOF) { switch (c) { /* ç¥ */ case '[': emit_while_start(*while_index_ptr++ = while_index++); break; case ']': emit_while_end(*--while_index_ptr); break; } } emit_footer(); return 0; }
試ãã¦ã¿ã¾ãããã
$ gcc bf2llvm.c -o bf2llvm $ echo "+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.------------.<++++++++.--------.+++.------.--------.>+." | ./bf2llvm | lli Hello, world!
åãã¦ããã§ããï¼ ããå°ããããããã³ã¼ããåããã¦ã¿ã¾ãã
$ cat fibonacci.bf >>+++++++++++[-<<++++>+++>>+<]>>+<++<<->>[>>>++++++++++<<[->+>-[>+>>]>[+[-<+>]>+ >>]<<<<<<]>>[-]>>>++++++++++<[->-[>+>>]>[+[-<+>]>+>>]<<<<<]>[-]>>[>++++++[-<++++ ++++>]<.<<+>+>[-]]<[<[->-<]++++++[->++++++++<]>.[-]]<<++++++[-<++++++++>]<.[-]<< [-<+>]<<[->>+>+<<<]>>>[-<<<+>>>]<-[<<<<<.>.>>>>[-]]<[->+>+<<]>>[-<<+>>]<<<<[->>+ <<]>>>[-<<<+>>>]<<-] $ cat fibonacci.bf | ./bf2llvm | lli 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233 $ cat prime.bf ++++++++++[->+++++>++++>+++<<<]>>++++>++>>+>+++<<<<<.>>>>>[<[-]++<[-]+>>>+[<[->> +<<]<[->>>>+>+<<<<<]>>>>[-<<<<+>>>>]<[->+>-[>+>>]>[+[-<+>]>+>>]<<<<<<]>[-<<<+>>> ]>[-]>[-<<+>+>]<<[->>+<<]+>[<[-]>[-]]<[<<<<<[-]>>>>>[-]]>[-]>[-]>[-]<<<<<<-<[->> >+>+<<<<]>>>>[-<<<<+>>>>]<<<[->>>>+>+<<<<<]>>>>>[-<<<<<+>>>>>]<<<+[->>[->+>+<<]> >[-<<+>>]+<[>-<<->[-]]>[<<<+<[-]>>>>[-]]<[-]<<<]>>[-]<[<<->>[-]]<[-]<<+<[->>>+>+ <<<<]>>>>[-<<<<+>>>>]>+++++++++++++<<[->>[->+>+<<]>>[-<<+>>]+<[>-<<->[-]]>[<<<+< [-]>>>>[-]]<[-]<<<]>>[-]<[<<[-[-]]>>[-]]<[-]<<<+>>]<<<[<.>>>>>++++++++++<<[->+>- [>+>>]>[+[-<+>]>+>>]<<<<<<]>[-<+>]>[-]>>>++++++++++<[->-[>+>>]>[+[-<+>]>+>>]<<<< <]>[-]>>[>++++++[-<++++++++>]<.<<+>+>[-]]<[<[->-<]++++++[->++++++++<]>.[-]]>[-]< <<++++++[-<++++++++>]<.[-]<<<<<[-]]>>+] $ cat prime.bf | ./bf2llvm | 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
Erik Bosmanããã¨ããæ¹ãæ¸ããããã³ãã«ããéåã®ããã°ã©ã ãåãããã¨ãã§ãã¾ãã åºåã¯èªè ã確èªãã¦ãã ããã
åºæ¬æ§æã«å¯¾å¿ã§ãã¦ãããã使ã£ãå°ããªã³ã¼ããåãã¦ãã¾ãã°ãããããããã°ã©ã ãåã (ã¨ã¯è¨ãã¡ã¢ãªã¼ã®éçã¯ããã¾ãã) ã¨ããã®ã¯ã³ã³ãã¤ã©ã®åéã§ã³ã¼ãæ¸ãã¦ãã¦æ¥½ããæã§ãã
æé©å
LLVMã«ã¯LLVM IRã¬ãã«ã®å½ä»¤åãæé©åããæ©è½ãããã¾ãã LLVMãããã¯ã¨ã³ãã«æã¤ã³ã³ãã¤ã«è¨èªã¯ãLLVMã®æé©åã®æ©æµãåãããã¾ãã
LLVM IRå½ä»¤åãã©ã®ããã«æé©åããããã調ã¹ãã«ã¯ãoptã³ãã³ãã使ãã¨ããã§ãããã
$ cat hello.bf +++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.------------.<++++++++.--------.+++.------.--------.>+. $ cat hello.bf | ./bf2llvm | opt -S -O3 ; ModuleID = '<stdin>' source_filename = "<stdin>" ; Function Attrs: nounwind define i32 @main() local_unnamed_addr #0 { while_end0: %0 = tail call i32 @putchar(i32 72) %1 = tail call i32 @putchar(i32 101) %2 = tail call i32 @putchar(i32 108) %3 = tail call i32 @putchar(i32 108) %4 = tail call i32 @putchar(i32 111) %5 = tail call i32 @putchar(i32 44) %6 = tail call i32 @putchar(i32 32) %7 = tail call i32 @putchar(i32 119) %8 = tail call i32 @putchar(i32 111) %9 = tail call i32 @putchar(i32 114) %10 = tail call i32 @putchar(i32 108) %11 = tail call i32 @putchar(i32 100) %12 = tail call i32 @putchar(i32 33) ret i32 0 } ; Function Attrs: nounwind declare i32 @putchar(i32) local_unnamed_addr #0 attributes #0 = { nounwind }
ãªãã¨ãIO以å¤ã¯å ¨ã¦è¨ç®ããã¦ãã¾ãã¾ããï¼ ãã¨ãã¨é åã確ä¿ãã¦ãã®ä¸ã§è¨ç®ãã¦ããã¯ãããæé©åã«ãã£ã¦ãããæ¶ãã¦ãã¾ãã¾ããã
ããå°ãè¤éãªããã³ãã«ããéåã®ã¹ã¯ãªããã§å®è¡æéãæ¯è¼ãã¦ã¿ã¾ãããã
$ time sh -c 'cat mandelbrot.b | ./bf2llvm | lli > /dev/null' 6.34 real 6.27 user 0.04 sys $ time sh -c 'cat mandelbrot.b | ./bf2llvm | opt -S -O3 | lli > /dev/null' 3.55 real 3.50 user 0.05 sys
LLVMã®æé©åã«ãã£ã¦ç´40%ãé«éåãã¾ãã (Hello worldã»ã©åç´ã§ã¯ãªãã®ã§ãé åãã¨æ¶ããã¨ãããã¨ã¯ããã¾ãã)ã ã³ã³ãã¤ã«ãã¦å®è¡ãã¡ã¤ã«ãä½ã£ã¦ã¿ã¾ãã
$ cat mandelbrot.b | ./bf2llvm | opt -S -O3 > mandelbrot.ll $ llc mandelbrot.ll $ gcc mandelbrot.s -o mandelbrot $ time ./mandelbrot > /dev/null 0.98 real 0.97 user 0.00 sys
ã¯ãããããéãã§ããã æã§Brainf**kã®ã¤ã³ã¿ã¼ããªã¿ãæ¸ãã¦æé©åãããã¨ãã¦ããå°æå ã®æ¹åã®çµã¿åããã§LLVMã¬ãã«ã®æé©åããã®ã¯é£ããã§ãããã ã¾ãã¯ç´ æ´ã«å½ä»¤åãåãã¦ãæé©åããã¾ãããã¦ãã¾ãã°é度ãåºãã¨ã¦ãããããã§ããã ç§ã®ããã«ã¢ã»ã³ããªã®åãããªã人ã§ããLLVM IRãæ£ããåããã¨ããã§ããã°å®è¡ãã¡ã¤ã«çæã¾ã§é¢åè¦ã¦ãããã®ã¯LLVMã®ç´ æ´ãããã¨ããã§ããã
ã¾ã¨ã
LLVMã®æåã®ä¸æ©ã¯LLVM IRã®å½ä»¤ã¨è¦ªããã¨ããã«ããã¾ãã ãããå¤ãã®ãã¥ã¼ããªã¢ã«ã§ã¯ãé£ããè¨èªãC++ã§ãã¼ã¹ãã¦æ§ææ¨ãä½ã£ã¦IRãåãã¦ã¨ãã説æããå§ã¾ãã¾ãã ã§ãã人ã«ã¯ã§ããã®ã§ãããããç§ã«ã¯ç解ã追ãã¤ãã¾ããã§ããã LLVM IRãåãã¦è§¦ã£ã人ãããããªãIRBuilderããã³ã¼ãçæã§ããã®ã§ããããã LLVMç´ äººããããªãIRBuilderã®ã¡ã½ããä¸è¦§ãçºãã¦ãããã使ãã°ãããªã¿ããã«ããã«åãããã®ãªã®ã§ããããã
ãããªäººã«ã¨ã£ã¦ãclang
ã¯è¯ãå
çã ã¨æãã¾ãã
Cã®ã³ã¼ããã©ã®ãããªLLVM IRã®å½ä»¤åã«ãªããæãã¦ããã¾ãã
Cã¨LLVM IRãè¦æ¯ã¹ãªãããèªåã§ãå½ä»¤åãåãã³ã¼ããæ¸ãã¦ã¿ã¦ãå¾ã
ã«åãã£ã¦ãããã®ã ã¨æãã¾ãã
ããã¯IRBuilderã使ã£ãã³ã¼ãã«æ¯ã¹ããæ³¥èãã³ã¼ãããããã¾ããããå
¥éã®å£ãè¶
ãã大äºãªä¸æ©ã ã¨æãã¾ãã
ãã®è¨äºã¯ãå®éã«ç§èªèº«ãä¸æ©ãã¤ç解ãæ·±ããªãããåæ並è¡ãã¦æ¸ãé²ãã¦ãã¾ããã ç§ã¯LLVMã«ã¤ãã¦ã¯ãç´ äººã§ãKaleidoscopeã¬ãã«ã«ããå°éãã¦ãã¾ããã
ãããªç§ã ããããããã®è¨äºãæ¸ããã¨æã£ãæã«ã¯ä»ã®ç§ã«æ¸ãããKaleidoscopeã®æåã®LLVMå ¥éè¨äºã«ããããã¨ããæããããã¾ããã LLVMã触ã£ã¦ã¿ãããã©ãæåã®å£ãè¶ ããããªãããããã£ã人ã«å±ãã°ãããªã¨æãã¾ãã
ããã¤ãã®å½ä»¤ã«ã¤ãã¦ãã¾ã説æããã«éãéããã¨ãããããã¾ããã å½ä»¤ã®æ£ç¢ºãªæå³ãå¼æ°ãªã©ã®ç解ãæ·±ããããã«ã¯ãè¨èªããã¥ã¢ã«ãåç §ãã¦ãã ããã ãªããç¨èªãè¨è¿°ã«ã¯æ°ãã¤ãã¦æ¸ãã¦ãã¾ããã誤ããããã°ãææãã ããã
LLVMã®ãã¼ã¸ãéããæãå½ä»¤ã®èª¬ææ¸ãã¼ã¼ã£ã¨çºãã¦ããæããã®ãã¼ã«ã¯ç§ã«ä½¿ãããã®ãªã®ã ãããã¨ããä¸å®ãããã¾ããã ä»ã®æ°æã¡ã¯å ¨ãéãã¾ãã å®éã«è§¦ã£ã¦è©¦è¡é¯èª¤ããããã³ããåãã£ã¦ãã¾ããã ããããLLVMã®ä¸çã®å ¥ãå£ã«ç«ã£ãæ°æã¡ã§ãã IRã®å½ä»¤ã®é°å²æ°ãæ´ãã¦ããã®ã§ã次ã¯IRBuilderã使ã£ãã³ã¼ãçæããå®ç¨çãªè¨èªã®ã³ã³ãã¤ã©ä½æã«ææ¦ãã¦ã¿ããã¨æãã¾ãã ä»å¾ãç解ãæ·±ããã³ã³ãã¤ã©æè¡ã®åå¼·ãç¶ãã¦ãããã¨æãã¾ãã
ç¶ãã§ãã itchyny.hatenablog.com
ã½ã¼ã¹ã³ã¼ã
bf2llvm.c
/* * Brainf**k -> LLVM IR Compiler * $ gcc bf2llvm.c -o bf2llvm * $ echo "+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.\ >-.------------.<++++++++.--------.+++.------.--------.>+." | \ ./bf2llvm | opt -S -O3 | lli */ #include <stdio.h> #include <stdlib.h> void emit_header() { printf("define i32 @main() {\n"); printf(" %%data = alloca i8*, align 8\n"); printf(" %%ptr = alloca i8*, align 8\n"); printf(" %%data_ptr = call i8* @calloc(i64 30000, i64 1)\n"); printf(" store i8* %%data_ptr, i8** %%data, align 8\n"); printf(" store i8* %%data_ptr, i8** %%ptr, align 8\n"); } 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_add(int diff) { printf(" %%%d = load i8*, i8** %%ptr, align 8\n", idx); printf(" %%%d = load i8, i8* %%%d, align 1\n", idx + 1, idx); printf(" %%%d = add i8 %%%d, %d\n", idx + 2, idx + 1, diff); printf(" store i8 %%%d, i8* %%%d, align 1\n", idx + 2, idx); idx += 3; } void emit_put() { printf(" %%%d = load i8*, i8** %%ptr, align 8\n", idx); printf(" %%%d = load i8, i8* %%%d, align 1\n", idx + 1, idx); printf(" %%%d = sext i8 %%%d to i32\n", idx + 2, idx + 1); printf(" %%%d = call i32 @putchar(i32 %%%d)\n", idx + 3, idx + 2); idx += 4; } void emit_get() { printf(" %%%d = call i32 @getchar()\n", idx); printf(" %%%d = trunc i32 %%%d to i8\n", idx + 1, idx); printf(" %%%d = load i8*, i8** %%ptr, align 8\n", idx + 2); printf(" store i8 %%%d, i8* %%%d, align 1\n", idx + 1, idx + 2); idx += 3; } void emit_while_start(int while_index) { printf(" br label %%while_cond%d\n", while_index); printf("while_cond%d:\n", while_index); printf(" %%%d = load i8*, i8** %%ptr, align 8\n", idx); printf(" %%%d = load i8, i8* %%%d, align 1\n", idx + 1, idx); printf(" %%%d = icmp ne i8 %%%d, 0\n", idx + 2, idx + 1); printf(" br i1 %%%d, label %%while_body%d, label %%while_end%d\n", idx + 2, while_index, while_index); printf("while_body%d:\n", while_index); idx += 3; } void emit_while_end(int while_index) { printf(" br label %%while_cond%d\n", while_index); printf("while_end%d:\n", while_index); } void emit_footer() { printf(" %%%d = load i8*, i8** %%data, align 8\n", idx); printf(" call void @free(i8* %%%d)\n", idx); printf(" ret i32 0\n"); printf("}\n\n"); printf("declare i8* @calloc(i64, i64)\n\n"); printf("declare void @free(i8*)\n\n"); printf("declare i32 @putchar(i32)\n\n"); printf("declare i32 @getchar()\n"); } int main() { char c; int while_index = 0; int while_indices[1000]; int* while_index_ptr = while_indices; emit_header(); while ((c = getchar()) != EOF) { switch (c) { case '>': emit_move_ptr(1); break; case '<': emit_move_ptr(-1); break; case '+': emit_add(1); break; case '-': emit_add(-1); break; case '[': emit_while_start(*while_index_ptr++ = while_index++); break; case ']': emit_while_end(*--while_index_ptr); break; case '.': emit_put(); break; case ',': emit_get(); break; } } emit_footer(); return 0; }