中介表示法(intermediate representation,IR)
/ IR = 'eye-arr' /
把一本書從法文翻成日文時,你不會逐字母處理原文;你會先抓住意義,再用新語言表達。編譯器做的事類似。它不是直接從原始文字走到機器碼,而是先把你的程式轉成它自己的內部形式——既不是原始語言、也不是目標機器。那個內部形式就是中介表示法(IR)。
IR 是一種資料結構,通常不是你手寫的文字,設計成讓編譯器能輕鬆分析與改寫程式。它通常看起來像一種簡化、規則的語言:操作被拆成小而一致的步驟,流程控制以基本區塊與跳躍明確表達,許多原始語言的便利(迴圈、複雜運算式、多載)都降階成少數幾個基本操作。關鍵是 IR 與語言、目標都無關。一個 C 程式和一個 Rust 程式都能降階成相同形狀的 IR,而那一份 IR 之後又能轉成 x86 或 ARM 程式碼。大多數真實編譯器依序使用好幾種 IR,從高階(接近原始碼)開始,逐步降階到低階(接近機器)。
它重要在於:IR 是編譯器重用的秘訣:寫 N 個語言前端與 M 個機器後端,藉由共用中間的一份 IR 與一個最佳化器,你就得到 N 乘 M 個編譯器,而不必為每一對組合各寫一個翻譯器。它也是最佳化器真正操作的對象——這個領域裡每一項最佳化都是對 IR 的改寫。一個提醒:在單一編譯器內,「IR」是個移動的目標;「那個 IR」通常指好幾種形式,而隨著程式從高階降階到低階,你能表達或證明的東西也會改變。
// C 原始碼: // 偏高階的 IR(三位址形式): int y = (a + b) * 2; t1 = a + b t2 = t1 * 2 y = t2
一個巢狀運算式變成一連串扁平的小操作——讓最佳化器容易推理。
通常不只有一種 IR:編譯器會經過好幾層,從高階到低階,所以某個分析能看見什麼(型別、迴圈、原始位址)取決於你看的是哪一層 IR。