HaskellでLLVMを触る
Optimization levelは None
にする
samples
星にゃーん氏の
example
コレのとおりに作ったら動いた、感動した


brewでllvmをinstallした場合は、binの直打ちでコマンドを打つ必要があるらしい
ref$ /usr/local/opt/llvm/bin/lli
構成としては
parser→AST→LLVMのAST
この記事では自作言語の拡張子を .sc
としてる
経過自作言語.sc
↓ parser ┐
独自AST | Main.hs(自作しているところ)
↓ compiler |
LLVM AST |
↓ compiler | ←ここはほぼライブラリの力
LLVM IR.ll ┘
↓ llc
アセンブリ.s
↓ gcc
bin
使っているライブラリ
IRをきれいに出力してくれるもの?
int32とか関数覚えていかないといけないの大変だな

ノリとしては、「llvm-hsのやり方がわかっている」というよりは、「llvm irの読み方がわかっている」と書ける感じなんだろうか
例えば最小のものを動かして見てみる
差をわかりやすくするためにわざと変な名前にしている
多分moduleの名前は 'main'
とかじゃないといけないので動かない
hscompile :: AST.Exp -> Text
compile expr = ppllvm $ buildModule "moduleName" $ do
function "funcName" [] i32 $ \[] -> do
ret (int32 0)
出力
ll; ModuleID = 'moduleName'
define external ccc i32 @funcName() {
ret i32 0
}
この関数funcNameは、 i32
型の値を返すだけで何も出力しないので出力するための関数printfを作ってみる
call
を使うことでOperandを実行することができる
call <Operand関数> [(Operand, Ags),..]
第2引数には (Operand, [Args])
のリストを取る
例えば以下のような printf
を実行するためには、 "%d\n"
と r
をそれぞれタプルにしてやる必要がある
cint r = 1 + 1;
printf("%d\n", r);
イメージとしてはこんな感じになるはず
call printf [(文字列を表すOperand,[]), (r, [])]
文字列を作るのが globalStringPtr
これを使って、 "%d\n"
を表すOperandを作成する
externVarArgs
ptr i8
とは
Operand
型とは
namedってなに
変数定義
alloca, store, loadなどを使う?
これをするとメモリスタックにallocateすることになる
こっちをしたとしても結局は「どこのアドレスに保存したか」の情報をHaskell側が持っている必要がある
#??もしくは定義のところはHaskellのtableで管理するのか
Stateモナドで管理している
両者の長短を知りたい
Haskellの話ではなく一般的なコード生成の話だな

タイガーブックとかに書いてるんかな、後で読も..
関数定義
これとか参考になる?
f = x => x + 1;
という式があったときに
Program [Assign "f" (Lambda "x" (Add (Var "x") (Nat 1)))]
x => x + 1
の部分、つまり Lambda String Exp
をtoOperandするときに、 fun1
という名前の関数をLLVM上に作成する
そしてAssignするときに f
に対して fun
を紐付けてState上で管理するとか
そうすると
(String, String)
になるな

Var
のAssingのときは (String, Exp)
なのでずれる
Program [Assign "a" (Nat 3)]
無理やり合わすとしたら
LambdaのときのStringをAST型にする
VarのときのExpをStringにする
VarのときのExpを先にOperandにしちゃってその変数名 %1
を保存する
できるのか?
if文の作り方
どういう粒度でLLVMで関数に切り出すのか
関数を関数に切り出すのはわかるが、それ以外はあるのか
freshName :: MonadIRBuilder m => ShortByteString -> m Name
alloca
block :: MonadIRBuilder m => m Name
前のblockを終わらせて、新しいblockを始める
emitBlockStart :: MonadIRBuilder m => Name -> m ()
named :: MonadIRBuilder m => m r -> ShortByteString -> m r
似たような命令がたくさんある
br
condBr
select
switch
Swtich文のように分岐先が複数ある場合
lldefine i32 @branch(i32 %cond) {
entry:
%cond.addr = alloca i32, align 4
%i = alloca i32, align 4
store i32 %cond, i32* %cond.addr, align 4
store i32 -1, i32* %i, align 4
%0 = load i32, i32* %cond.addr, align 4
%cmp = icmp eq i32 %0, 0
br i1 %cmp, label %if.then, label %if.else
if.then:
store i32 0, i32* %i, align 4
br label %if.end
if.else:
store i32 1, i32* %i, align 4
br label %if.end
if.end:
%1 = load i32, i32* %i, align 4
ret i32 %1
}
br <比較式>, <thenの飛び先label>, <elseの飛び先label>
こういうのを目指せばいい
とりあえず、printfを実行するmainと、それ以外を作る関数を分けよう
もしくは、if文が来たときだけ関数をわけるか
ll; ModuleID = 'main'
@putNumForm = unnamed_addr constant [4 x i8] c"%d\0a\00"
declare external ccc i32 @printf(i8*, ...)
define i32 @branch() {
br i1 1, label %then_0, label %else_0
then_0:
ret i32 1
else_0:
ret i32 0
}
define external ccc i32 @main() {
%1 = call i32 @branch()
%2 = call ccc i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @putNumForm, i32 0, i32 0), i32 %1)
ret i32 0
}
せいかい
ll; ModuleID = '/tmp/webcompile/_16911_0.bc'
@.str = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
; Function Attrs: noinline nounwind optnone
define i32 @branch(i32) #0 {
%2 = alloca i32, align 4
%3 = alloca i32, align 4
store i32 %0, i32* %3, align 4
%4 = load i32, i32* %3, align 4
%5 = icmp sgt i32 %4, 1
br i1 %5, label %6, label %12
; <label>:6: ; preds = %1
%7 = load i32, i32* %3, align 4
%8 = sub nsw i32 %7, 1
%9 = call i32 @branch(i32 %8)
%10 = load i32, i32* %3, align 4
%11 = mul nsw i32 %9, %10
store i32 %11, i32* %2, align 4
br label %13
; <label>:12: ; preds = %1
store i32 1, i32* %2, align 4
br label %13
; <label>:13: ; preds = %12, %6
%14 = load i32, i32* %2, align 4
ret i32 %14
}
; Function Attrs: noinline nounwind optnone
define i32 @main() #0 {
%1 = alloca i32, align 4
store i32 0, i32* %1, align 4
%2 = call i32 @branch(i32 5)
%3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i32 %2)
ret i32 0
}
declare i32 @printf(i8*, ...) #1
c#include <stdio.h>
int branch(int cond) {
if (cond > 1) {
return branch(cond -1) * cond;
}else{
return 1;
}
}
int main() {
printf("%d\n", branch(5));
return 0;
}