if-then-else、loop控制語句在SIMD指令下的後端指令生成實現--筆記
阿新 • • 發佈:2020-12-10
作者:Yaong
出處:https://www.cnblogs.com/yaongtime/p/14111134.html
版權:本文版權歸作者和部落格園共有
轉載:歡迎轉載,但未經作者同意,必須保留此段宣告;必須在文章中給出原文連線;否則必究法律責任
if-then-else、loop控制語句的後端實現
本文是通過程式碼而來,主要記錄了在SIMD指令集上,編譯器後端對控制語句(if-then-else、loop)的指令生成方法。
引言:
"A unique feature of most GPU’s is that they are designed to run many different instances of the same program in lock step in order to reduce the size of the scheduling hardware by sharing it between many different “cores.” When control flow diverges, meaning that when two different instances (fragments, vertices, etc.) branch in different directions, then the GPU will take both sides of the branch. For example, if both thread 1 and thread 2 are currently in block A, and thread 1 wants to branch to block B while thread 2 wants to branch to block C, then the GPU will first branch to block B with thread 1 enabled and thread 2 disabled, and then when execution reaches a predefined “merge block,” the GPU will jump back to block C while flipping the enabled threads and run until the merge block is reached, at which point the control flow has converged and both thread 1 and thread 2 can be enabled. "
if-then-else語句
進入到if-then-else語句塊,轉換為branch指令時,因為使用的是SIMD指令,所以各個channel的跳轉控制流程可能會出現分歧。
在此我們先假設condition等於true,執行if-then語句塊;condition等於false,執行else語句。
當各個channel的condition相同時,各個channel不會出現分歧,各個channel的跳轉地址(程式碼塊)也是相同的,這種情況下,當condition等於true時,所有channel執行if-then語句塊,反之,condition等於false,所以channel執行else語句。
當各個channel的condition條件不一樣,那麼各個channel就會出現分歧,if、else兩個語句塊都需要被按條件被執行一次。對於condition等於1的channel,需要執行if-then語句塊,而不能執行else語句;對於condition等於false的channel,要執行else語句,而不能執行if-then語句塊。
最後,進入到匯合點後,所有的channel又可以並行的執行相同的指令了。
如何處理上述的兩種情況呢?
首先這需要機器指令級別上的支援,不同的處理器有不同的實現。
來看看VC4提供的指令方案:
1.每個channel有獨立的標誌位,比如,N、Z、C
2.運算指令的執行結果能設定每個channel的標誌位
3.指令支援條件執行
4.branch指令支援跳轉條件,且支援各個channel標誌位間的邏輯運算
具體來看.
虛擬碼:
sf: set flag
zs: zero set
zc: zero clear
01: mov execute, 0
02: OR.sf tmp0, condition, 0
03: mov.zs execute, @else_block
04: mov.sf null, execute
05: Branch.all_zero_clear @else_block
06: then_block:
07: …
08: mov.sf null, execute
09: mov.zs execute, @after_block
10: sub.sf tmp1, execute, @after_block
11: branch.all_zero_set @after_block
12: end_then:
13: else_block:
14: sub.sf tmp2, execute, @else_block
15: mov.zs execute, 0
16: …
17: end_else:
18: after_block:
虛擬碼中condition表示if的條件“if(condition)”,而execute實時記錄了各個channel的執行條件。
當各個channel的condition相等時,這種情況相對簡單些,各個channel執行的流程是相同的,要麼執行if-then語句塊,那麼執行else語句。
當各個channels的condition值不相等時,“if-then”“else”兩個語句塊,均要被執行一次,但是,不是每個channel都要執行,只有在進入到語句塊中execute等於零的channel才能執行其中的指令,而非零值的channel不能執行語句塊中的指令(非零值是等於else語句塊的index號,指向執行完當前語句塊後,接下來要進入執行的語句塊)。具體來看,執行流程首先(PC指標)進入到“if”基本塊中,對於execute值等於0的channel會執行基本塊中的指令,非0的要跳過基本塊中的所有指令。在if-then語句塊執行後,執行過“if”語句的channel,就不能再執行else語句的指令,所以在if-then語句塊結束後需要對execute更新,在執行if-then語句塊時execute等於0的channel,需要把execute更新為after block index,即執行完else基本塊後的基本塊的index號,非0的channel的execute需要更新為0,表明在接下來的else基本塊中需要執行指令,所以在執行流程(PC指標)進入else基本塊中後,同樣的execute等於零的channel才會執行指令,非零不執行.
進入某個語句塊前,需要對execute進行測試,進入某個語句塊前execute儲存了各個channel的將要執行的語句塊的地址。
對滿足if語句塊的channel的退出if語句塊時,需要將execute更新為下一個執行的語句塊地址,即@after_block。
回到虛擬碼:
當我們開始處理if-then-else語句時,我們先判斷執行條件,以取得下一句指令的入口,這時可能出現3種情況:
1.所有channel的condition等於0,則跳轉到else_block
2.所有channel的condition不等於0,則執行if_block,並且在if_block執行完畢後,要跳過else_block。
3.各個channel的condition不全為零,按標誌位,先執行if_block,再執行else_block。
1.將execute初始化為0,execute儲存了各個channel的執行條件,在進入if-then語句塊後,對execute等於0的channel,要執行if-then語句塊中的指令,在進入else語句後,這些channel就不能再執行其中的else語句中的指令了。對不執行if-then語句塊的channel,對應的execute值等於@else_block。
2.將condition與0做比較,並且運算結果會設定各個channel的標誌位,如果condition等於0,會將對應channel的 Z標誌位設定為0,否則設定為1。
3.語句3中,mov會利用語句2中對Z標誌位的設定結果,更新各個channel的execute值。如果Z標誌位等於0,則將@else_block的地址跟新到execute中。
4.語句4,根據execute的值,更新Z標誌位
5.語句5根據以上4句的運算結果,如果全部channel的execute都不等於0,則跳轉到@else_block地址。
具體的說,如果全部channel的condition等於0,那麼所有channel的execute都等於@else_block,所以語句4的執行後,所有channel的Z標誌位都不為零,語句5滿足跳轉條件,跳轉到@else_block;
如果全部channel的condition都等於1,那麼所有channel的execute等於0,所以語句4的執行後,所有channel的Z標誌位都為零,語句5的跳轉條件不滿足,進入@then_block;
如果各個channel的condition不全相等,那麼,對於condition等於0的channel的execute值就等於@else_block;而對於1的channel,execute的值等於0,所以語句4的執行後,所有channel的Z標誌位不全為零,語句5的跳轉條件不滿足,進入@then_block。
6.從語句6開始是if-then語句塊。一旦能進入if-then語句塊中,就需要依據各個channel的execute值,來判斷指令能否被執行。所以每條指令執行前都要用execute更新一次Z標誌位(暫不考慮優化),且每條指令都要加上條件執行碼,例如:
Mov.sf null, execute Add.zs tmp4, tmp3, tmp2 Mov.sf null, execute Sub.zs tmp5, tmp3, tmp1
如前文所述,對於execute等於0的channel,執行“Mov.sf null, execute”後,Z標誌位置位,接下來的“Add.zs tmp4, tmp3, tmp2”就能被執行。
7.在if-then語句塊執行完畢後,離開if-then語句塊前,需要判斷執行流程的下一個入口。如5中所述,能進入到if-then語句塊中有兩種情況,分別對應不同的出口。- 如果是全部channel的condition等於1,這種情況下我們接下來不需要執行else語句了。因為各個channel的execute均等於0,所以語句8執行後會把每個channel的Z標誌位置位,緊接著語句9,也就把每個channel的execute值都更新為@after_block,在語句10測試跳轉條件,與@after_block相減,結果為0,所以各個channel的Z標誌位被置位,語句11的跳轉條件是全部Z標誌位置位,此時條件滿足,即跳轉到after_block,也就結束了if-then-else語句的處理。
-
如果不是全部channel的condition都等於1,接下來我們需要進入到else語句,執行非零的channel。語句8會把execute等於0的channel的Z標誌位置位,語句9把Z標誌位置位的channel的execute更新為@after_blcok,其餘儲存不變,語句10做條件測試的結果,不能滿足語句11的跳轉條件。接下進入else語句,並且這時各個channel的execute的值等於@else_blcok或@after_block。