我開始在使用LLVM作爲後端的語言中添加閉包(lambdas)。我已經實現了它們,它們可以總是內聯的簡單情況,即不需要生成閉包定義本身的代碼,因爲它是在內聯的地方使用的。如何在LLVM IR中有效實施閉包?
但是,如果閉包不總是內聯(例如,它傳遞給另一個未內聯的函數),如何生成閉包的代碼。優選地,呼叫站點不應該關心他們是否通過常規功能或關閉,並將其稱爲正常功能。
我可以用綜合名稱生成一個函數,但它必須將引用環境作爲額外參數,並且該函數不能僅僅傳遞給不知道所需額外參數的另一個函數。
我想到了一種可能的解決方案,它使用LLVM的蹦牀內在函數,它從函數中「消除」單個參數,並返回一個指向蹦牀函數的指針,該函數只需要少一個參數。在這種情況下,如果爲閉包生成的函數將引用環境作爲第一個參數,我可以刪除它並返回一個函數,該函數的精確度與實際聲明的參數完全一致。這聽起來可行嗎?高效?有沒有更好的解決方案?
代碼示例:
def applyFunctionTo(value: Int, f: (Int) -> Int) = f(value)
def main() = {
val m := 4;
val n := 5;
val lambda := { (x: Int) => x + m + n };
applyFunctionTo(3, lambda)
}
現在,讓我們來想象,這將不會被內聯了def main() = 3 + 4 + 5
,並applyFunctionTo
將可能被單獨編譯,我們不能改變調用點存在。隨着蹦牀,我想生成的代碼會是這樣的(僞代碼表示,*表示指針):
def main$lambda$1(env: {m: Int, n: Int}*, x: Int) = x + env.m + env.n
def main() = {
m = 4
n = 5
env* = allocate-space-for {Int, Int}
env = {m, n}
tramp* = create-trampoline-for(main$lambda$1*, env*)
return applyFunctionTo(3, tramp*)
// release memory for env and trampoline if the lambda didn't escape
}
這個問題似乎對不對?
使用虛擬方法實現閉包和實現對象沒有區別。 – 2012-01-03 11:50:50
可能你是對的,但是,語言不會有虛擬方法(還)。至少它會在這之前關閉和其他許多東西。我可能會以愚蠢的順序添加一些功能,因爲我主要是爲了學習目的而做的。我只希望最終有用的東西來自它。 – 2012-01-03 13:18:58
我的意思是沒有理由發明任何新的閉包:你可以做同樣的事情,比如說C++編譯器已經在做。有可能它已經是最有效率的事情了。 – 2012-01-03 13:27:34