本章對應官方教程第4章,本章介紹如何爲中間代碼(LLVM IR)添加優化以及添加JIT編譯器支持。html
教程以下:git
教你使用swift寫編譯器玩具(0)github
教你使用swift寫編譯器玩具(1)express
教你使用swift寫編譯器玩具(2)swift
咱們都知道在編譯的過程當中有着中間代碼優化這一步。咱們想要中間代碼可以去除無用以及重複計算的內容,因此這個時候咱們須要使用中間代碼優化器。
舉一個例子,在沒有優化以前,咱們輸入def test(x) (1+2+x)*(x+(1+2));
得到的結果以下所示。
def test(x) (1+2+x)*(x+(1+2));
Read function definition:
define i64 @test(i64 %x) {
entry:
%add = add i64 3, %x
%add1 = add i64 %x, 3
%mul = mul i64 %add, %add1
ret i64 %mul
}
複製代碼
咱們能夠看出來其實add
和add1
是相同的值,徹底沒有必要算兩次。因此通過優化以後長下面這樣
def test(x) (1+2+x)*(x+(1+2));
Read function definition:
define i64 @test(i64 %x) {
entry:
%add = add i64 3, %x
%mul = mul i64 %add, %add
ret i64 %mul
}
複製代碼
咱們能夠看出出來以前的兩個add被優化成了一個。
那麼咱們該如何添加優化呢?LLVM爲咱們提供了PassManager
。可是有趣的是在LLVMSwift
中,PassManager
被deprecated
了。因此咱們只須要使用更簡單的PassPipeliner
便可。
有多簡單呢?簡單到只須要添加兩行代碼。
let passPipeliner = PassPipeliner(module: theModule)
func lexerWithDefinition(_ lexer: Lexer) {
if let p = parseDefinition() {
if let f = p.codeGen() {
//在這裏調用execute()方法
passPipeliner.execute()
print("Read function definition:")
f.dump()
}
} else {
lexer.nextToken()
}
}
複製代碼
使用LLVMSwift
中的JIT也十分簡單。咱們只須要在合適的地方調用便可。
首先咱們定義全局變量JIT,並在main中初始化它。
var theJIT: JIT!
let targetMachine = try! TargetMachine()
theJIT = JIT(machine: targetMachine)
複製代碼
接着咱們在lexerWithDefinition
中把Module
中的IR添加到JIT中。
...
f.dump()
_ = try! theJIT.addLazilyCompiledIR(theModule) { (_) -> JIT.TargetAddress in
return JIT.TargetAddress()
}
複製代碼
在lexerWithTopLevelExpression
中把繼續把Module
中的IR添加到JIT中。
...
let handle = try theJIT.addEagerlyCompiledIR(theModule) { (name) -> JIT.TargetAddress in
return JIT.TargetAddress()
}
let addr = try theJIT.address(of: "__anon_expr")
typealias FnPr = @convention(c) () -> Int
let fn = unsafeBitCast(addr, to: FnPr.self)
print("Evaluated to \(fn()).")
try theJIT.removeModule(handle)
initModule()
複製代碼
還記得以前parseTopLevelExpr
中添加的默認函數名"__anon_expr"
嗎?在lexerWithTopLevelExpression
新增代碼的意思就是把頂級表達式包在一個名爲"__anon_expr"
且返回值爲空的函數中進行調用。
可是目前咱們還只能調用一次函數,調用第二次函數時咱們就找不到這個函數了。因此這個時候咱們須要有一個全局的表用來記錄。
var functionProtos: [String: PrototypeAST] = [:]
func getFunction(named name: String) -> Function? {
if let f = theModule.function(named: name) {
return f
} else {
let fi = functionProtos[name]
guard fi != nil else {
return nil
}
return fi?.codeGen()
}
}
複製代碼
接着咱們須要爲CallExprAST
和FunctionAST
替換獲取函數名的方式。
//CallExprAST
let calleeF = getFunction(named: callee!)
//FunctionAST
functionProtos[proto!.name!] = proto
let theFunction = getFunction(named: proto!.name!)
guard theFunction != nil else {
return nil
}
複製代碼
這樣咱們老是能夠在當前Module
中得到先前定義過的函數進行調用。
最後咱們還須要更新一下lexerWithDefinition
方法和lexerWithExtern
方法。
//lexerWithDefinition
...
f.dump()
_ = try! theJIT.addLazilyCompiledIR(theModule) { (_) -> JIT.TargetAddress in
return JIT.TargetAddress()
}
initModule()
//lexerWithExtern
...
f.dump()
functionProtos[p.name!] = p
func initModule() {
theModule = Module(name: "main")
theModule.dataLayout = targetMachine.dataLayout
}
複製代碼
直接輸入表達式。
1+20;//輸入
Read top-level expression:
define i64 @__anon_expr() {
entry:
ret i64 21
}
Evaluated to 21.//輸出
複製代碼
函數調用。
def testfunc(x y) x + y*2;//輸入
Read function definition:
define i64 @testfunc(i64 %x, i64 %y) {
entry:
%mul = mul i64 %y, 2
%add = add i64 %x, %mul
ret i64 %add
}
testfunc(1, 2);//輸入
Read top-level expression:
define i64 @__anon_expr() {
entry:
%call = call i64 @testfunc(i64 1, i64 2)
ret i64 %call
}
Evaluated to 5.//輸出
testfunc(1, 3);//輸入
Read top-level expression:
define i64 @__anon_expr() {
entry:
%call = call i64 @testfunc(i64 1, i64 3)
ret i64 %call
}
Evaluated to 7.//輸出
複製代碼