HaskellでLLVMを触る
Optimization levelはNoneにする
samples
星にゃーん氏の
example
コレのとおりに作ったら動いた、感動したmrsekut.icon*2
brewでllvmをinstallした場合は、binの直打ちでコマンドを打つ必要があるらしい ref $ /usr/local/opt/llvm/bin/lli
構成としては
parser→AST→LLVMのAST
この記事では自作言語の拡張子を.scとしてる
code:経過
自作言語.sc
↓ parser ┐
独自AST | Main.hs(自作しているところ)
↓ compiler |
LLVM AST |
↓ compiler | ←ここはほぼライブラリの力
LLVM IR.ll ┘
↓ llc
アセンブリ.s
↓ gcc
bin
使っているライブラリ
IRをきれいに出力してくれるもの?
int32とか関数覚えていかないといけないの大変だなmrsekut.icon
ノリとしては、「llvm-hsのやり方がわかっている」というよりは、「llvm irの読み方がわかっている」と書ける感じなんだろうか
例えば最小のものを動かして見てみる
差をわかりやすくするためにわざと変な名前にしている
多分moduleの名前は'main'とかじゃないといけないので動かない
code:hs
compile :: AST.Exp -> Text
compile expr = ppllvm $ buildModule "moduleName" $ do
function "funcName" [] i32 $ \[] -> do
ret (int32 0)
出力
code: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をそれぞれタプルにしてやる必要がある
code:c
int 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の話ではなく一般的なコード生成の話だなmrsekut.icon
タイガーブックとかに書いてるんかな、後で読も..
関数定義
これとか参考になる?
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)になるなmrsekut.icon
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文のように分岐先が複数ある場合
code:ll
define 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文が来たときだけ関数をわけるか
code: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
}
せいかい
code: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
%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 code:c
int branch(int cond) {
if (cond > 1) {
return branch(cond -1) * cond;
}else{
return 1;
}
}
int main() {
printf("%d\n", branch(5));
return 0;
}