CPU處理無條件轉移指令時,為何實際上要清除流水線?
(若我的認知有誤,請務必指出教我.正確的回答對我來說簡直是恩賜.)
以X86或X64來說.只考慮段內轉移.書上說執行JMP與CALL會清除指令流水線,對嗎?1.JMP指令剛讀取完之後,無條件轉移到目的地址,只考慮這一點,這等同於目的地址就在下一條無異.對於流水線來說,只需要接下來PC指向目的地址就可以不用清除流水線了.2.
要實現PC自動指向JMP的目的地址,在我的認知下,好像只是要做一個加法,或者遇到絕對地址就直接覆蓋.是否如我認為的這樣簡單?是否有邏輯錯誤?為何聯繫到實際的實現CPU卻沒有這樣做?3.在我認知下,CPU中的BTB,在首次遇到JMP時,對此還是不能有幫助的吧?4.假設,假設JMP可以實現不中斷流水線,那麼CALL是否可以在CPU拆解為PUSH+JMP,從而實現不清除流水線?
Ultraman大神和不懂CPU的同學的回答已經很完善,我僅說題干中疏漏之處:
書上說執行JMP與CALL會清除指令流水線?
這個說法並不精準,分支指令的確清除流水線,但是這件事發生在 「分支指令被識別出來,並預測它會跳轉」 這個時刻。如果是取指時發現,就會僅清除取指流水線,如果是解碼時發現,就會僅清除解碼流水線。如果指令被諸如uop cache,或loop buffer等硬體記憶過,那麼它很有可能順利執行且不清除任何流水線。
要實現PC自動指向JMP的目的地址,在我的認知下,好像只是要做一個加法,或者遇到絕對地址就直接覆蓋.是否如我認為的這樣簡單?
想法很美好,但是實現你說的想法一般需要4-5個時鐘周期,因此等CPU做完你說的加法,其實又需要清除這4-5個周期的流水線 orz 且這當中又有其他困難之處: 「這個加法實現起來一點都不簡單」
拿x86舉例子,無條件跳轉可以分成 1.絕對跳轉 2.相對跳轉 3.特殊情況如任務切換
題干提到的加減偏移量僅適用於「絕對跳轉」的情況,後面兩種情況則需要其他的硬體去實現。
比如相對轉移: JMP ptr [EAX + 8*ECX +0x8], 這樣的目標地址,是無法在解碼之前就確定下來的。
那麼CALL是否可以在CPU拆解為PUSH+JMP?
Call和Jmp的區別不僅僅是Call會將現場壓棧。Call是功能性很強的指令,它可以完成代碼優先順序控制功能。Previlige保障了用戶態的代碼不會干擾系統態的代碼,而Call可以提供不同優先順序的代碼間引用。
拋開這一點,題乾沒問題,在pipeline flush的情況上,Call和Jmp是大同小異的。
不是完全懂CPU,只說我知道的部分吧。
第一個問題:
取指和解碼是流水線里的兩步,只有經過解碼才能知道這條指令是不是JMP,而這時下一條指令已經在流水線取指這一步上了。所以流水線是必須要清掉的,否則的話就要在取指的時候判斷指令是否是JMP,這樣會讓流水線變得複雜,不如讓BTB去做好了。而且實際CPU上,取指到解碼中間還有沒有別的動作就不清楚了,所以最可靠的方法還是清空流水線。如果不清空流水線,那麼就說明BTB已經準確預測了。第二個問題:
你把取指和解碼當成一步了,其實是兩步甚至更多,所以參見上面的問題,你這樣設計會破壞流水線的結構。另外,題主沒有說是哪種JMP,如果是帶判斷的、目標地址是寄存器的JMP,那麼流水線里的東西會很多。
第三個問題:
不清楚,我的印象里BTB不能做到100%預測,所以首次JMP時可能會失敗。第四個問題:你這個是假設JMP不中斷流水線的情況下,但因為JMP必然中斷流水線,所以不成立;假設JMP不中斷流水線,那麼你的假設可能是成立的,這一點我不了解具體的微結構設計,不完全確定。推薦閱讀:
※為什麼Python有那麼多方便的庫而C++很少?
※如何不使用loop循環,創建一個長度為100的數組,並且每個元素的值等於它的下標?
※菜鳥想學計算機要從哪方面開始入手?
※足球經理《Football Manager》的比賽模擬原理是什麼?
※如何玩轉github?