12. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
LLVM で Hello, World
Hello, World を出力する例を Ruby-LLVM で再実装する。
http://llvm.org/docs/LangRef.html#module-structure
1111
; Declare the string constant as a global constant.
@.str = private unnamed_addr constant [13 x i8] c"hello world¥0A¥00"
; External declaration of the puts function
declare i32 @puts(i8* nocapture) nounwind
; Definition of main function
define i32 @main() { ; i32()*
; Convert [13 x i8]* to i8 *...
%cast210 = getelementptr [13 x i8]* @.str, i64 0, i64 0
; Call puts function to write out the string to stdout.
call i32 @puts(i8* %cast210)
ret i32 0
}
; Named metadata
!1 = metadata !{i32 42}
!foo = !{!1, null}
; Declare the string constant as a global constant.
@.str = private unnamed_addr constant [13 x i8] c"hello world¥0A¥00"
; External declaration of the puts function
declare i32 @puts(i8* nocapture) nounwind
; Definition of main function
define i32 @main() { ; i32()*
; Convert [13 x i8]* to i8 *...
%cast210 = getelementptr [13 x i8]* @.str, i64 0, i64 0
; Call puts function to write out the string to stdout.
call i32 @puts(i8* %cast210)
ret i32 0
}
; Named metadata
!1 = metadata !{i32 42}
!foo = !{!1, null}
13. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
Ruby-LLVM での Hello, World 実装例 1212
gem 'ruby-llvm'
require 'llvm/core'
require 'llvm/execution_engine'
m = LLVM::Module.new('hello')
str = "Hello, World!"
llvm_str_type = LLVM.Array(LLVM::Int8, str.size + 1)
llvm_g_str = m.globals.add(llvm_str_type, ".str")
llvm_g_str.initializer = LLVM::ConstantArray.string(str)
arg_types = [LLVM.Pointer(LLVM::Int8)]
cputs = m.functions.add('puts', arg_types, LLVM::Int32)
# Definition of main function
main = m.functions.add('main', [], LLVM::Int32) do |function|
entryBB = function.basic_blocks.append
entryBB.build do |builder|
zero = LLVM.Int(0)
# GetElementPointer(gep)
cast210 = builder.gep llvm_g_str, [zero, zero], 'cast210'
builder.call cputs, cast210
builder.ret zero
end
end
m.verify
m.dump
puts "-----------------------------------------------------"
LLVM.init_x86
LLVM::JITCompiler.new(m).run_function(m.functions["main"], *[])
gem 'ruby-llvm'
require 'llvm/core'
require 'llvm/execution_engine'
m = LLVM::Module.new('hello')
str = "Hello, World!"
llvm_str_type = LLVM.Array(LLVM::Int8, str.size + 1)
llvm_g_str = m.globals.add(llvm_str_type, ".str")
llvm_g_str.initializer = LLVM::ConstantArray.string(str)
arg_types = [LLVM.Pointer(LLVM::Int8)]
cputs = m.functions.add('puts', arg_types, LLVM::Int32)
# Definition of main function
main = m.functions.add('main', [], LLVM::Int32) do |function|
entryBB = function.basic_blocks.append
entryBB.build do |builder|
zero = LLVM.Int(0)
# GetElementPointer(gep)
cast210 = builder.gep llvm_g_str, [zero, zero], 'cast210'
builder.call cputs, cast210
builder.ret zero
end
end
m.verify
m.dump
puts "-----------------------------------------------------"
LLVM.init_x86
LLVM::JITCompiler.new(m).run_function(m.functions["main"], *[])
17. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
Ruby-LLVM での Hello, World (3)
外部で定義された関数 puts を宣言する。
m.functions
LLVM::Module::FunctionCollection オブジェクト
FunctionCollection#add メソッド
第1引数: 外部関数の名前
第2引数: LLVM 上の引数の型を表す配列
第3引数: LLVM 上の返り値の型
返り値: LLVM::Function オブジェクト
1616
# External Declaration of the `puts` function
arg_types = [LLVM.Pointer(LLVM::Int8)]
cputs = m.functions.add('puts', arg_types, LLVM::Int32)
# External Declaration of the `puts` function
arg_types = [LLVM.Pointer(LLVM::Int8)]
cputs = m.functions.add('puts', arg_types, LLVM::Int32)
extern int puts(const char *s);extern int puts(const char *s);
18. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
Ruby-LLVM での Hello, World (4)
メイン関数の定義
function.basic_blocks :
LLVM::Function::BasicBlockCollection オブジェクト
BasicBlock#build :
LLVM::Builder オブジェクトを生成してブロックに渡す
builder.gep:
複雑なデータ構造での格納場所(アドレス)を取得する。
1717
# Definition of main function
main = m.functions.add('main', [], LLVM::Int32) do |function|
entryBB = function.basic_blocks.append
entryBB.build do |builder|
zero = LLVM.Int(0)
# GetElementPointer(gep)
cast210 = builder.gep llvm_g_str, [zero, zero], 'cast210'
builder.call cputs, cast210
builder.ret zero
end
end
# Definition of main function
main = m.functions.add('main', [], LLVM::Int32) do |function|
entryBB = function.basic_blocks.append
entryBB.build do |builder|
zero = LLVM.Int(0)
# GetElementPointer(gep)
cast210 = builder.gep llvm_g_str, [zero, zero], 'cast210'
builder.call cputs, cast210
builder.ret zero
end
end
32. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
電卓の Transformer の実装
LLVM 関係の処理は、LLVMBuilder クラスに外出し
rule を記述するだけで比較的簡単に実装可能
3131
class LLVMTransformer <Parslet::Transform
b = @@builder = LLVMBuilder.new
rule(:int => simple(:n)){
b.int n
}
rule(:left => simple(:l),
:right => simple(:r),
:op => simple(:op)){
b.calc op, l, r
}
rule(:expr => simple(x)){
b.ret x
}
def do(tree)
apply(tree)
@@builder.dump
end
end
class LLVMTransformer <Parslet::Transform
b = @@builder = LLVMBuilder.new
rule(:int => simple(:n)){
b.int n
}
rule(:left => simple(:l),
:right => simple(:r),
:op => simple(:op)){
b.calc op, l, r
}
rule(:expr => simple(x)){
b.ret x
}
def do(tree)
apply(tree)
@@builder.dump
end
end
パーサによって生成された
"int" 要素を処理。
LLVM の整数に変換。
パーサによって生成された
"int" 要素を処理。
LLVM の整数に変換。
パーサによって生成された
"left"、 "right"、 "op" の
要素を持つサブツリーに対して
処理を行う。
LLVM の命令に変換する。
パーサによって生成された
"left"、 "right"、 "op" の
要素を持つサブツリーに対して
処理を行う。
LLVM の命令に変換する。
33. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
電卓の LLVMBuilder の実装
LLVMBuilder では LLVM の命令を生成する。
building_loop で rule からの指示を待つ
3232
class LLVMBuilder
def initialize
@fiber = Fiber.new do
@module = LLVM::Module.new("calculator")
@module.functions.add("add", [], LLVM::Int) do |f,|
bb = f.basic_blocks.append("entry")
bb.build do |builder|
building_loop builder
end
end
end
@fiber.resume
end
...
end
class LLVMBuilder
def initialize
@fiber = Fiber.new do
@module = LLVM::Module.new("calculator")
@module.functions.add("add", [], LLVM::Int) do |f,|
bb = f.basic_blocks.append("entry")
bb.build do |builder|
building_loop builder
end
end
end
@fiber.resume
end
...
end
新しい Fiber を生成している。新しい Fiber を生成している。
1. Module の生成
2. Function の追加
3. Basic Block の追加
4. Builder を使い、LLVM 命令を追加
1. Module の生成
2. Function の追加
3. Basic Block の追加
4. Builder を使い、LLVM 命令を追加
building_loop まで Fiber を実行させておく。building_loop まで Fiber を実行させておく。
34. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
電卓の LLVMBuilder の実装 3333
def building_loop builder
llvm_ir = nil
loop do
op, l, r = Fiber.yield llvm_ir
case op
when "+"
llvm_ir = builder.add l, r
when "-"
llvm_ir = builder.sub l, r
when '*'
llvm_ir = builder.mul l, r
when "/"
llvm_ir = builder.sdiv l, r
when :exit
llvm_ir = builder.ret l
break
end
end
end
def building_loop builder
llvm_ir = nil
loop do
op, l, r = Fiber.yield llvm_ir
case op
when "+"
llvm_ir = builder.add l, r
when "-"
llvm_ir = builder.sub l, r
when '*'
llvm_ir = builder.mul l, r
when "/"
llvm_ir = builder.sdiv l, r
when :exit
llvm_ir = builder.ret l
break
end
end
end
Fiber から呼出し元への返り値みたいなもの。
Fiber なので、Fiber.yield の引数で処理結果を
渡して呼出し元に処理を戻す。
Fiber から呼出し元への返り値みたいなもの。
Fiber なので、Fiber.yield の引数で処理結果を
渡して呼出し元に処理を戻す。
呼出し元から来る引数みたいなもの。
Fiber なので、Fiber.yield の返り値で
呼出しから受け取る。
呼出し元から来る引数みたいなもの。
Fiber なので、Fiber.yield の返り値で
呼出しから受け取る。
四則演算の LLVM
の命令を生成
四則演算の LLVM
の命令を生成
計算結果を return する
LLVM の命令を生成
計算結果を return する
LLVM の命令を生成
35. 2013/7/6 第2回 きつねさんでもわかる LLVM 読書会用資料
電卓の LLVMBuilder の実装
Fiber の処理を calc 、ret というメソッドで隠ぺい
calc +、-、×、÷ の四則演算を LLVM の処理に変換する
ret 処理結果を LLVM の関数の返り値にする。
3434
def int n
LLVM.Int n.to_i
end
def calc op, l, r
@fiber.resume op, l, r
end
def ret x
@fiber.resume :exit, x
end
def dump
@module.dump
end
def int n
LLVM.Int n.to_i
end
def calc op, l, r
@fiber.resume op, l, r
end
def ret x
@fiber.resume :exit, x
end
def dump
@module.dump
end
Fiber の外から、演算を受け取って、
Fiber に渡す。
さらに Fiber での処理結果を返す。
Fiber の外から、演算を受け取って、
Fiber に渡す。
さらに Fiber での処理結果を返す。
Fiber がまだ動いていたら、
:exit を送って、終了させる。
BasicBlock#build のブロックの処理が
終わらないと、正常に動作しない。
Fiber がまだ動いていたら、
:exit を送って、終了させる。
BasicBlock#build のブロックの処理が
終わらないと、正常に動作しない。
Ruby の整数を LLVM の整数に
変換する。
Ruby の整数を LLVM の整数に
変換する。
処理を終了し、返り値として返す処理を終了し、返り値として返す