phi 節點(phi node)
/ FYE node (phi = the Greek letter, written φ) /
想像兩條路匯成一條。抵達交會點的旅人帶著一個包裹,但依他走哪條路,包裹來自不同的店。要知道交會點上包裹裡是什麼,你得問他從哪條路進來。phi 節點就是編譯器對流程控制匯流處的某個值問出這個問題的方式。
phi 節點之所以存在,是因為 SSA 的那條規則:每個名字恰好定義一次。這條規則在匯流點會破功。假設一個 if 在 then 路徑把 x 設為 1、在 else 路徑把 x 設為 2;分支重新匯合後,使用 x 的程式碼無法指向單一定義。SSA 在匯流區塊的開頭引入一個 phi 節點:x3 = phi(來自 then 區塊的 x1, 來自 else 區塊的 x2)。它的意思是「若從 then 區塊來,x3 等於 x1,否則等於 x2」。它不是一個真正計算什麼的機器指令——它是一種記號,依每條傳入邊記錄哪個較早版本流入。每個前驅區塊都有一個 phi 引數。
它重要在於:phi 節點讓 SSA 能跨越分支與迴圈守住它「單一定義」的承諾(迴圈變數會在迴圈標頭得到一個 phi,在它的初始值與更新值間選擇)。沒有 phi 節點,資料流分析就得在每個匯流點往回散佈穿越流程控制。一個提醒:phi 是 IR 的虛構。在程式碼產生前,編譯器藉由在傳入邊上插入普通的複製指令來「銷毀」phi,所以沒有任何 phi 會存活到真正的機器碼。
if (cond) x1 = 1; else x2 = 2; // 匯流區塊: x3 = phi(x1 [來自 then], x2 [來自 else]) // 依實際走過的路徑挑選 use(x3)
phi 節點把 x3 命名為「到達此處的那個版本」,讓 SSA 每個名字維持恰好一個定義。
phi 不是真正的指令,在執行期不計算任何東西;它在程式碼產生前藉由在傳入邊插入普通複製指令而被移除,所以你在反組譯中永遠看不到 phi。