教你使用swift寫編譯器玩具(4)

前言

本章對應官方教程第4章,本章介紹如何爲中間代碼(LLVM IR)添加優化以及添加JIT編譯器支持。html

教程以下:git

教你使用swift寫編譯器玩具(0)github

教你使用swift寫編譯器玩具(1)express

教你使用swift寫編譯器玩具(2)swift

教你使用swift寫編譯器玩具(3)bash

教你使用swift寫編譯器玩具(4)函數

教你使用swift寫編譯器玩具(5)post

教你使用swift寫編譯器玩具(6)測試

教你使用swift寫編譯器玩具(7)優化

教你使用swift寫編譯器玩具(8)

倉庫在這

開始

中間代碼優化

咱們都知道在編譯的過程當中有着中間代碼優化這一步。咱們想要中間代碼可以去除無用以及重複計算的內容,因此這個時候咱們須要使用中間代碼優化器。

舉一個例子,在沒有優化以前,咱們輸入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
}
複製代碼

咱們能夠看出來其實addadd1是相同的值,徹底沒有必要算兩次。因此通過優化以後長下面這樣

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中,PassManagerdeprecated了。因此咱們只須要使用更簡單的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()
        }
    }
複製代碼

添加JIT支持

使用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()
    }
}
複製代碼

接着咱們須要爲CallExprASTFunctionAST替換獲取函數名的方式。

//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.//輸出
複製代碼
相關文章
相關標籤/搜索