3

所以我編譯C的一個子集,以一個簡單的堆棧虛擬機用於學習目的,我想知道如何以最佳方式編譯一個switch語句,例如一個簡單的VM編譯開關語句

switch (e) { 
    case 0: { ... } 
    case 1: { ... } 
    ... 
    case k: { ... } 
} 

這本書我經歷提供了一個簡單的方式與索引跳躍而書中描述的簡單方案僅適用於連續的,上升的情況下值的範圍進行編譯。

現在,我使用的符號標籤第一遍和第二遍時我要解決的標籤,以實際跳躍的目標,因爲有標籤簡化了初始編譯堆棧說明了不少。我現在的想法是用下面的方案以任何順序將本書中的內容概括爲任何順序的案例值。呼叫的情況下值c1, c2, ..., cn和相應的標籤j1, j2, ..., jn然後生成的假設的e值以下指令序列是在堆棧的頂部:

dup, loadc c1, eq, jumpnz j1, 
dup, loadc c2, eq, jumpnz j2, 
... 
dup, loadc cn, eq, jumpnz jn, 
pop, jump :default 
j1: ... 
j2: ... 
... 
jn: ... 
default: ... 

的符號是希望清楚,但如果不是:dup =重複的值上堆棧頂部,loadc c =在堆棧頂部推一個常量ceq =比較堆棧中的前兩個值,並根據比較結果推0或1,jumpnz j =跳轉到標籤j如果堆棧頂層值不是0 ,label: =在第二次編譯過程中將解析爲實際地址的內容。

所以我的問題是,那麼什麼是編譯開關語句的一些其他的方式?我的做法比連續範圍值的索引跳轉表要緊湊得多,但是如果存在很大的差距,則工作得很好。

+0

並索引跳躍類似:將在列表和循環,其次是索引跳躍的值。這將處理差距很大的值。 – cup

+2

使用分支的二叉樹來最小化分支的平均數量。所有連續或接近連續的序列應該用跳轉表完成。 –

+0

通常情況下應該使用跳轉表,除非值之間的差距太大 - 但是您必須找到一個便宜的函數,將源值集合映射到更緊湊的東西或將其轉換爲一個序列或ifs樹。 –

回答

3

首先對所有情況進行排序。然後確定所有(足夠大以至於值得的)連續或接近連續的序列,並將它們視爲一個單獨的單位,並用跳轉表處理。然後,不要使用比較和跳轉的線性序列,而要使用分支的平衡二叉樹來最小化平均跳轉次數。您可以通過遞歸比較案例或案例塊的中位數來做到這一點。