暫存器重新命名

暫存器重新命名是計算機 CPU 的微體系結構（Microarchitecture）中的一種技術，避免了機器指令或者微操作不必要的順序化執行，從而提高了處理器的指令級並行的能力。

問題定義

計算機的 CPU 往往用暫存器來儲存指令的運算元與結果。x86 指令集體系結構有 8 個整數暫存器，x86-64 指令級體系結構有 16 個整數暫存器，許多 RISC 體系結構有 32 個整數暫存器，IA-64 有 128 個整數暫存器。在小型處理器，這些指令集體系結構暫存器直接對應於暫存器堆中的物理暫存器。

不同的指令可以有不同的執行時間，特別是 CISC 指令集體系結構上更為明顯。例如，一條讀記憶體的指令的執行時間，足夠執行幾百條其它指令。因此，在允許多條指令並行執行的情況下，那些指令地址順序靠後的指令，比讀取記憶體指令更早完成，這就形成了指令執行順序不同於其在程式中的順序。這種亂序執行是高效能 CPU 提高運算速度的關鍵辦法之一。

考慮下述程式碼片段在亂序執行 CPU 上的執行：

第 4、第 5、第 6 條指令在功能上是不依賴於第 1、第 2、第 3 條指令的。但是處理器卻不能在第 3 條指令完成前去完成第 4 條指令（在指令流水線上，不能在第 3 條指令完成前，就提交第 4 條指令的結果），因為這可能會導致第 3 條指令把錯誤的資料寫入記憶體。

透過改變一些暫存器的名字，可以使上例中指令並行執行所受的限制：

現在，4、5、6 號指令可以與 1、2、3 號指令並行執行，二者沒有依賴問題。這使得程式可以用更短時間完成。

編譯器會盡力檢測出類似這樣的問題，並把不同的暫存器分配給不同的指令使用。但是，受指令集體系結構的限制，彙編程式可以使用的暫存器名字的數量是有限的。很多高效能 CPU 的實現 — 微體系結構 — 有很多物理暫存器，可以在處理器指令流水線執行時把這些指令集體系結構暫存器對映為不同的物理暫存器，從而在硬體級提供了額外的並行能力。

資料衝突

如果多條指令使用了同一個儲存位置，這些指令如果不按程式地址順序執行可能會導致 3 種資料衝突（data hazard）:

先寫後讀（Read-after-write，RAW）: 從暫存器或者記憶體中讀取的資料，必然是之前的指令存入此處的。直接資料相關（true data dependency）
先寫後寫（Write-after-write，WAW）：連續寫入特定的暫存器或記憶體，那麼該儲存位置最終只包含第二次寫的資料。這可以取消或者廢除第一次寫入操作。WAW 相關也被說成是 “輸出相關”（output dependencies）。
先讀後寫（Write-after-read，WAR）：讀操作獲得的資料是此前寫入的，而不是此後寫操作的結果。因此並行和亂序時無法改善的資源衝突（antidependency）。WAW 和 WAR 可以透過暫存器重新命名解決（register renaming），不必等待所有讀操作完成後再執行寫操作，可以保持這個儲存位置的兩份副本：老值與新值。讀老值的操作可以繼續進行，無需考慮那些寫新值甚至寫新值之後的讀新值的操作。產生了額外的亂序執行機會。當所有讀老值操作被滿足後，老值所使用的暫存器既可以釋放。這是暫存器重新命名的實質。

任何被讀或寫的儲存都是可以被重名。最常考慮的是通用整數暫存器與浮點暫存器。標誌暫存器、狀態暫存器甚至單個狀態位也是常見的重新命名的物件。

記憶體位置也可以被重新命名，雖然這麼做不太常見。全美達的 Crusoe 處理器的 gated store buffer 是一種記憶體重新命名。

如果程式沒有立即重用暫存器，它就不需要暫存器重新命名這種機制。例如 IA-64 指令集體系結構提供了 128 個通用暫存器，就是出於此考慮。但這種努力也遇到了困難：

編譯器很難完全避免在不導致程式尺寸大增的同時避免重用暫存器。程式的迴圈的連續迭代執行就需要複製迴圈體的程式碼以使用不同的暫存器，這種技術叫做迴圈展開。
大量的暫存器，需要在指令的運算元中用很多位去指出，導致程式尺寸變大。
很多指令集在歷史上就使用了很少的暫存器，出於相容原因現在也很難改變。

程式的程式碼尺寸增加，會導致指令快取記憶體的未命中（cache miss）增加，處理器執行停頓等待從低階儲存中讀入程式碼。這對運算效能的影響是致命的。

體系結構暫存器與物理暫存器

編譯器或者彙編器生成的機器語言程式讀寫有限數量的指令集體系結構（ISA）暫存器。例如，Alpha ISA 使用 32 個 64 位寬整數暫存器，32 個 64 位寬浮點暫存器。這些體系結構暫存器，是程式可以直接訪問的邏輯上的暫存器。如果程式設計師在偵錯程式中把這個程式暫停，可以觀察到這 64 個暫存器與一些狀態暫存器當前儲存的值。

一款特定的處理器，實現了這種處理器體系結構。例如 Alpha 21264 有 80 個整數暫存器、72 個浮點暫存器，作為處理器內物理實現的暫存器。也就是說，Alpha 21264 處理器有 80 個物理存在的位置儲存整數運算的結果，72 個位置存放浮點運算的結果。實際上，該款處理器有更多的物理存在的儲存位置，但與暫存器重名關係不大。

下面介紹兩種暫存器重新命名方法，區別於為執行單元準備資料的電路。處理器把指令流使用的體系結構暫存器改為用若干位表示的 tags 所索引的物理暫存器。

tag 索引的暫存器堆（tag-indexed register file）是一個很大的暫存器堆，當一條指令發射（issue）給執行單元，源運算元的暫存器 tags 將傳送給物理暫存器堆，其中該 tags 所對應的物理暫存器的內容被髮送給該執行單元。

保留站（reservation station）方法，存在多個小型相關的暫存器堆，通常是每個執行單元的輸入口都有一個物理暫存器堆。發射佇列中的每條指令的每個運算元對應著這個物理暫存器堆的一個儲存位置。當一條指令發射給某個執行單元，執行單元對應的暫存器堆的相應條目被讀出傳送給執行單元。

體系結構暫存器堆（Architectural Register File）或者引退暫存器堆（Retirement Register File，RRF）：儲存了被提交的體系暫存器的狀態。透過邏輯暫存器的號來查詢這個暫存器堆。重排序緩衝區（reorder buffer）中的引退（retired）或者說提交（committed）指令，把結果寫入這個暫存器堆。
遠期暫存器堆（Future File）：處理器對分支做投機執行的暫存器的狀態保存於此。使用邏輯暫存器號來索引訪問。在 Intel P6 微體系結構，稱之為 Active Register File。
歷史緩衝區（History Buffer）：用於儲存分支時的邏輯暫存器狀態。如果分支預測失敗，將使用歷史緩衝區的資料來恢復執行狀態。
重排緩衝區（Reorder Buffer，ROB）：為了實現指令的順序提交，處理器內部使用了一個 Buffer。如果在該緩衝區中排在一條指令之前的所有都已經提交，沒有處於未提交狀態的（稱作 in flight），則該指令也被提交（即確認執行完畢）。因此重排緩衝區是在遠期暫存器堆之後，體系結構暫存器堆之前。提交的指令的結果寫入體系暫存器堆。

重排緩衝區分為 data-less 與 data-ful 兩種。

Willamette's ROB，其條目指向物理暫存器堆（PRF）中的暫存器。此外還包括一些簙記資料。這是第一種亂序執行設計，由 Andy Glew 在 Illinois 用 HaRRM 完成。

Intel P6 的 ROB，條目包含了資料。沒有單獨的物理暫存器堆。來自 ROB 的資料在指令提交後將複製到引退暫存器堆（RRF）。

細節：tag 索引暫存器堆

這種重新命名模式用於 MIPS R10000、Alpha 21264，以及 AMD Athlon 的浮點部分。

在重新命名階段，每個被引用的體系結構暫存器（不論是讀還是寫）按其體系結構索引號到重新命名檔案（remap file）中查詢，取出一個 tag 與一個 ready 位。如果一個排隊在前的寫操作要把資料寫入該暫存器，但該寫操作尚未執行完，則這個 tag 是未就緒的（non-ready）。

對於暫存器讀，這個 tag 替換了體系結構暫存器，即這種先寫後讀的暫存器資料相關必須恪守，該讀操作只有在該 ready 位是就緒之後才可以被分派執行。
對於暫存器寫，從一個空閒 tag 先進先出佇列（free tag FIFO）取出一個新的 tag，且這項新的對映條目寫入重新命名檔案，未來的讀取該體系結構暫存器的指令將指向這個新的 tag，即 “寫後寫” 是一種暫存器資料偽相關，用這種重新命名就可去去掉偽相關。這個 tag 被標記為未就緒，因為寫操作尚未執行。以前為該體系結構暫存器分配的物理暫存器被儲存在指令的重排緩衝區（reorder buffer）中；即：以前為該體系結構暫存器分配的物理暫存器可以從重排緩衝區中查到。重排緩衝區是一個先進先出佇列，依照指令解碼的順序（即指令在程式中的先後順序）安排指令引退的順序。

運算元暫存器被重新命名後的指令將被放入不同的發射佇列（issue queues）。這些指令等待所需的各種資源（如源運算元對應的物理暫存器）就緒。

當指令執行完，其結果的 tags 將被公告，發射佇列中用這些公告的 tags 匹配那些未就緒的 tags。一個匹配就意味著該運算元就緒了。重新命名檔案也去匹配這些公告的 tags，從而標記哪些對應的物理暫存器是就緒的。

當發射佇列中的某條指令的所有運算元是就緒的，這條指令就是發射就緒。在每個週期，發射佇列撿出一些就緒指令，傳送到功能單元。未就緒指令仍然留在發射佇列中。這種從發射佇列中無序刪除指令，使得發射佇列的電路實現佔用面積大、功耗高。

被髮射的指令讀取源運算元的 tag 索引在物理暫存器堆中對應的物理暫存器（忽略掉剛剛公告過的運算元），然後開始執行指令。

指令執行結果寫入目的運算元的 tag 索引在物理暫存器堆對應的物理暫存器，同時公告給每個功能單元輸入端的旁路網路（bypass network，即把執行結果 “直通” 給流水線各個步驟的中間緩衝）。

寫暫存器的指令在引退時，把被寫的目的運算元暫存器使用過的上一個 tag 放入 “空閒 tag 佇列” 中，使得它可以被其它被解碼的指令重用。而該指令的目的運算元暫存器當前對應的 tag 仍然被佔用，因為後面可能還有指令需要讀取當前 tag 對應的物理暫存器的內容。

一個異常（將導致中斷）或者分支預測失敗導致了重新命名檔案退回到最後一條有效的指令的重新命名狀態，透過組合狀態的快照（在歷史緩衝區）與重排緩衝區中等待順序引退的指令的以前用過的 tags。這種機制可以實現恢復任意時刻的重新命名狀態。

細節：保留站

這種重新命名模式用於 AMD K7 與 K8 的整數暫存器設計。

在重新命名階段，作為源運算元的體系結構暫存器在遠期暫存器堆與重新命名檔案中查詢對應的物理暫存器。如果沒有寫指令還沒有完成寫入該物理暫存器，則說明這個源運算元已經就緒。當這條指令被放入發射佇列，從遠期暫存器堆相應的物理暫存器讀出內容放入保留站中對應的條目。指令對目的暫存器的寫入，在重新命名檔案中的產生了一個新的、未就緒的 tag。tag 數通常是按照指令的順序分配，因此不需要空閒 tag 先進先出佇列。

如同 tag 索引模式，發射佇列中的未就緒運算元等待匹配的 tag 公告。但不同於 tag 索引模式，tag 的匹配導致對應的內容資料寫入發射佇列對應的保留站的條目。

被髮射的指令從保留站讀取它的運算元，忽略掉那些剛剛公告過的運算元，然後開始執行。保留站暫存器堆通常很小，可能只有 8 個條目。

指令執行結果寫入重排緩衝區，以及保留站（如果發射佇列有匹配的 tags），以及遠期暫存器堆。

指令引退時，複製重排序緩衝區中的值到體系結構暫存器堆。體系結構暫存器堆用於從異常或者分支預測失敗時恢復。

在指令引退時可以識別出異常與分支預測失敗，引起體系結構暫存器堆覆蓋掉遠期暫存器堆的內容，並標記重新命名檔案中所有暫存器都是就緒。通常，沒有辦法為一條處於解碼與引退之間的指令恢復遠期暫存器堆，因此通常沒有辦法在更早期為分支預測失敗做恢復工作。

比較兩種模式

在兩種模式下，指令被順序送入發射佇列，但從發射佇列移出是亂序的。

保留站具有更好的延遲（latency）效能。因為重新命名階段直接獲得暫存器內容，而不是獲得物理暫存器號，再用這個號去獲得內容值。

保留站具有更好的從指令發射到執行的延遲效能。因為每個本地暫存器堆遠小於那種大型的用 tag 索引的中央暫存器堆。tag 產生與異常處理也更為簡單。

與 tag 索引的簡單的暫存器堆相比，保留站的物理暫存器堆的總規模更大，功耗更大，更為複雜。更糟糕的是，每個保留站的每個條目可以被每條結果匯流排寫入。例如，每個功能單元具有 8 條發射佇列條目的處理器，相比於 tag 索引的模式有 9 倍的旁路網路，結果直通（forwarding）需要更大的功耗與面積。

保留站模式在 4 個位置（遠期暫存器堆，保留站，重排序區、系統結構暫存器堆）儲存結果值。而 tag 索引模式只需要在物理暫存器堆儲存結果值。由於結果值來自功能單元，保留站模式必須公告結果到許多儲存位置，用掉了非常多的功耗、面積、時間。如果處理器具有非常精確的分支預測、非常關注執行延遲，保留站也是個很好的選擇。

歷史

IBM System 360 Model 91 是早期支援亂序執行的計算機。它使用 Tomasulo 演算法用到了暫存器重新命名。

1990，POWER1 是第一種使用了暫存器重新命名與亂序執行的微處理。

最初的 R10000 設計既沒有發射佇列壓偏（collapsing），也沒有可變優先權編碼，因此遇到了暫存器資源餓死（starvation）— 最老的指令在佇列中一直沒有被髮射，直到解碼指令因為缺乏可重新命名的物理暫存器而完全停頓，而所有就緒指令都已經發射了。後來修改設計的 R12000 使用了部分可變優先順序編碼來克服此問題。

早期亂序執行處理器並不區分重新命名與重排序緩衝區 / 物理暫存器堆的功能。因此，某些最早期的產品，如 Sohi 的 RUU，Metaflow DCAF，組合了排程、重新命名，儲存在一個結構中。

大多數現代處理器的重新命名使用了一個對映表，用邏輯暫存器號去索引。例如 Intel P6 微體系結構。遠期暫存器堆也是如此，並在此存放資料。

但是，更早的處理器使用內容定址儲存技術實現重新命名，例如 HPSM RAT，以及儲存器別名表（Register Alias Table）。

某種程度上，亂序執行的微體系結構的故事就是這些內容定址儲存如何逐步被清除。

Intel P6 微體系結構是 Intel 處理器中第一種亂序執行、暫存器重新命名。P6 發展出了 Pentium Pro、Pentium II、Pentium III、Pentium M、Core、Core 2 等處理器系列。