generated at
HaskellでLLVMを触る
Online Compilerなどで目指すものを見てから実装してみるとイメージしやすい
Optimization levelは None にする

samples
星にゃーん氏の


example


Kaleidoscopeを作る


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


ノリとしては、「llvm-hsのやり方がわかっている」というよりは、「llvm irの読み方がわかっている」と書ける感じなんだろうか

例えば最小のものを動かして見てみる
差をわかりやすくするためにわざと変な名前にしている
多分moduleの名前は 'main' とかじゃないといけないので動かない
hs
compile :: 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 をそれぞれタプルにしてやる必要がある
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で管理するのか
Hytlではこっちにしている
Stateモナドで管理している
両者の長短を知りたい
Haskellの話ではなく一般的なコード生成の話だなmrsekut
タイガーブックとかに書いてるんかな、後で読も..

関数定義
これとか参考になる?
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
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文のように分岐先が複数ある場合
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文が来たときだけ関数をわけるか
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; }