常數傳播(constant propagation)
如果你知道 x 永遠是 5,那麼之後任何用到 x 的地方,你大可直接寫 5——而一旦這麼做,像 x + 3 這樣的運算式現在就能算成 8,完全不需要執行期算術。這個兩段式的想法——把變數換成它已知的常數值、再求值得到的常數運算式——就是常數傳播,連同它的搭檔常數摺疊。
兩者攜手合作。常數摺疊(constant folding)是較簡單的一半:在編譯期,對所有運算元都是常數的運算式求值——3 * 4 變成 12、1 << 4 變成 16。常數傳播(constant propagation)是資料流的一半:追蹤哪些變數持有已知常數值,並在它們的使用處代入。所以若編譯器證明 n = 10,它就把之後使用 n 的地方換成 10,這常常為摺疊製造出新的全常數運算式。兩者接連發生:傳播、摺疊,新的常數又可能進一步傳播。在 SSA 形式中這特別乾淨,因為每個值只有一個定義可追溯,而一個精煉版本(稀疏條件常數傳播)甚至能摺掉條件現在已知的分支。
它重要在於:這是最普遍套用的最佳化之一——它清理抽象與內聯留下的冗餘、縮小程式碼,並餵養死碼消除(一個條件摺疊為 false 的分支會使一側變死)。一個值得直說的提醒:編譯器只能在結果可證明與實際執行程式相同時才摺疊,所以它對浮點捨入、以及結果取決於執行期值的操作很小心;而且它不會發明一個無法證明的常數,所以一個只是「碰巧」在你測試執行中是常數、但其實讀自輸入的值,不會被傳播。
int n = 10; int area = n * n + 0; // 常數傳播把 n 換成 10,常數摺疊算出 10*10+0: int area = 100; // 沒有乘法或加法存活到執行期
傳播 n=10 使運算式全為常數,接著摺疊在編譯期算它一次。
最佳化器只傳播它能「證明」在每條路徑上都是常數的值,而非在某次測試中看似常數的值;在摺疊可能改變結果處(例如某些浮點捨入)它會保持保守。