1. 程式人生 > >指令集基本原理

指令集基本原理

展現 模型 style 虛擬存儲器 縮小 索引 動態 包括 晶體管

指令集體系結構----程序員或編譯器編寫人員能夠看到的計算機部分。 指令集體系結構包括: 1.對各種指令集進行了分類,並對各種方法的優勢和劣勢進行某種量化評估。 2.對一些指令集測量數據進行分析。 3.語言與編譯器問題以及他們對指令集體系結構的影響。 桌面式計算機強調設計整數、浮點類型的程序的性能,很少考慮程序的規模。今天的服務器主要用於數據庫、文件服務器和Web應用程序,再加上一些正對許多用戶的時分應用程序。因此浮點性能的重要性要遠低於整數與字符字符串的重要性,但是所有服務器處理器都仍包含浮點指令。 個人移動設備和嵌入式應用看重成本和能耗,所以代碼規模非常重要,因為減少存儲器可以降低價格和能耗,為了降低芯片成本,某些類型的指令稱為可選選項。
由於與PC軟件保持二進制兼容的重要性,再加上摩爾定律提供了充足的晶體管,使Intel在內部使用RISC指令集,而在外部支持80x86指令集。 後來的80x86微處理器使用硬件將80x86指令轉換為類RISC的指令,然後在芯片內部執行經過轉換的操作。它們仍然向程序員展現80x86體系結構,同時允許計算機設計人員實現RISC類型的處理器,以提高性能。
指令集體系結構的分類 處理器中的內部存儲類型是最基本的區別,主要包括棧、累加器或寄存器組。操作數可以顯式命名,也可以隱式命名。 在棧體系結構中,操作數隱式位於棧的頂部,而在累加器體系結構中,操作數隱式為累加器。 通用寄存器體系結構只有顯式操作數,或者為寄存器,或者為存儲器位置。
四類指令集中C=A+B的代碼序列如下表:
累加器 寄存器(寄存器--存儲器) 寄存器(載入--存儲)
Push A Load A Load R1,A Load R1,A
Push B Add B Add R3,R1,B Load R2,B
Add Store C Store R3,C Add R3,R1,R2
Pop C Store R3,c
對於棧和累加器體系結構,Add指令擁有隱式操作數,對於寄存器體系結構擁有顯示操作數。如上表所示,實際上有兩種類型的寄存器計算機。一類可以用任意指令來訪問存儲器,稱為寄存器--存儲器體系結構,另一類則只能用載入和存儲指令訪問存儲器,稱為載入--存儲體系結構。第三類將所有操作數都保存在存儲器中,稱為存儲器--存儲器體系結構。一些指令集體系結構的寄存器要多於單個累加器,但對這些特殊寄存器的使用設置一些限制,稱為拓展累加器或專用寄存器計算機。
盡管大多數早期計算機使用棧或累加器類型的體系結構,但是新體系結構都使用了載入--存儲寄存器體系結構。通用寄存器計算機之所以出現,主要原因是1.寄存器快於存儲器 2.對編譯器來說,使用寄存器要比使用其他內部存儲形式的效率更高。 例如在寄存器計算機中,在對表達式(AxB)-(BxC)-(AxD)求值時,可以按任意順序執行乘法計算,這種做法的效率更高,可能是操作數位置的原因,也可能是流水線因素的原因。 在棧計算機上,硬件只能按唯一的順序對表達式進行求值,因為操作數隱藏在棧中的,它必須多次載入操作數。 寄存器可用於保存變量,當變量被分配到寄存器中時,可以降低存儲器通信流量、加快程序速度,提高代碼密度(由於存儲器的名稱位數少於存儲器位置的名稱位數)。 編譯器編寫人員希望所有人員希望所有寄存器都是等價的。較早的計算機將一些寄存器專門用於一些特殊應用,顯著降低了通用寄存器的數量。編譯器將所有未確認用途的寄存器保留給表達式求值使用。 大多數編譯器會為表達式求值保留一些寄存器,為參數傳遞使用一些,其余寄存器可用於保存變量。現代編譯器技術能夠有效地使用大量寄存器,從而增加最新體系結構的寄存器數目。 有兩個重要指令集特性可以區分GPR(通用寄存器)體系結構,這兩個特性都關註一個典型算術或邏輯指令操作的本質,第一個特性關註一個ALU指令是有兩個還是三個操作數。 在三操作數格式中,指令包含一個結果操作數和兩個源操作數。在兩操作數格式中,操作數之一即是結果操作數也是源操作數。 GPR體系結構的第二個區別是考慮ALU指令中可能有多少個操作數是存儲器地址。一個典型ALU指令支持的存儲器操作數數量可能是0-3個。
存儲器地址的數目 所允許的最大操作數個數 體系結構的類型 示例
0 3 載入-存儲 Alpha、ARM、MIPS、PowerPC、SPARC
1 2 寄存器--存儲器 IBM 360/370 Intel80x86
2 2 存儲器--存儲器 VAX
3 3 存儲器--存儲器 VAX
類型 優勢 劣勢
寄存器--寄存器 (0,3) 簡單、固定長度指令編碼。簡單代碼生成模型。 指令執行所需要的時鐘數相同。 指令數目高於指令中有存儲器引用的體系結構。 指令多、指令密度低,增大了程序的規模。
寄存器--存儲器 (1,2) 無需獨立的載入指令就可以訪問數據。 指令格式易於編碼,可以得到很好的指令密度。 由於在二元運算中,源操作數會被銷毀,所以操作數是不等價的。 在每條指令中對寄存器數目和存儲器地址進行編碼可能會限制寄存器的個數。 每條指令的時鐘數會隨操作數的位置變化。
存儲器--存儲器 (2,2)或(3,3) 最緊湊,沒有為臨時值浪費寄存器 指令規模變化很大,特別是對於三操作數指令。存儲器訪問會造成存儲器瓶頸。

存儲器尋址 一個體系結構,無論是載入--存儲式,還是允許任何操作數都是存儲器引用,都必須定義如何解釋存儲器地址以及如何指定這些地址。 1.解釋存儲器地址 大部分指令集都是字節尋址的,提供對字節、半字和字的訪問方式。大部分計算機還提供雙字的訪問。小端字節順序將地址為"x..x000"的字節放在雙字的最低有效位置(小端)。 大端字節順序將地址為"x...x000"的字節放在雙字的最高有效位置(大端)。 在對比字符串時,小端排序不能與字的正常排序方式相匹配,字符串在寄存器中是反向表示的。 在許多計算機中,對大於一字節的對象進行尋址時都必須是對齊的。大小為s字節的對象,字節地址為A,如果A mod s = 0 ,則對該對象的尋址是對齊的。 由於存儲器通常與一個字或雙字的倍數邊界對齊,所以非對齊尋址會增加硬件復雜性。一個非對齊存儲器尋址可能需要多個對齊的存儲器引用,因此即使在允許非對齊尋址的計算機中,采用對齊尋址的程序也可以運行更快。 即使數據是對齊的,要支持字節、半字和字尋址也需要一個對齊網絡來對齊64位寄存器中的字節、半字和字。 2.尋址方式 除了存儲器中的位置之外,尋址方式還指定常量和寄存器。在使用存儲器位置時,由尋址方式指定的實際存儲器地址稱為有效地址。
尋址方式 指令舉例 含義 使用場景
寄存器尋址 Add R4,R3 Regs[R4] <-- Regs[R4] + Regs[R3] 當一個值在寄存器中
立即數尋址 Add R4,#3 Regs[R4] <-- Regs[R4] + 3 對於常量
位移量尋址 Add R4,100(R1) Regs[R4] <-- Regs[R4] + Mem[100 + Regs[R1]] 訪問本地變量(+模擬寄存器間接、直接尋址方式)
寄存器間接尋址 Add R4,(R1) Regs[R4] <-- Regs[R4] + Mem[Regs[R1]] 使用指針或計算得出的地址尋址
索引尋址 Add R3,(R1 + R2) Regs[R3] <-- Regs[R3] + Mem[Regs[R1]+[R2]] 有時用於數組尋址:R1為數組基址,R2為索引值
直接或間接尋址 Add R1,(1001) Regs[R1] <-- Regs[R1] + Mem[1001] 有時用於訪問靜態數據,地址常量可能很大
存儲器間接尋址 Add R1,@(R3) Regs[R1] <-- Regs[R1]+Mem[Mem[Regs[R3]]] 如果R3為指針p的地址,則此方法生成*p
自動遞增尋址 Add R1,(R2)+ Regs[R1] <-- Regs[R1] + Mem[Regs[R2]] Regs[R2] <-- Regs[R2] + d 用於在循環內部逐步遍歷數組。R2為元素的開始位置,每次引用都會將R2增加一個元素d的值
自動遞減尋址 Add R1,-(R2) Regs[R2] <-- Regs[R2] -d Regs[R1] <-- Regs[R1] + Mem[Regs[R2]] 與自動遞增的用途相同,自動遞增或遞減也可以用作push/pop,以實現棧
比例尋址 Add R1,100(R2)[R3] Regs[R1] <-- Regs[R1] + Mem[100 + Regs[R2] + Regs[R3]*d] 用於索引數組,在某些計算機中,可用於任何索引尋址方式
尋址模式能夠大幅減少指令數目,他們也會增加構建計算機的復雜度,對於實施尋址模式的計算機,可能會增加每條指令的平均時鐘周期數目(CPI)。在這些尋址方式中,位移量尋址和立即數尋址成為主導尋址方式。 位移量尋址方式 在使用位移量類型的尋址方式時,一個主要問題就是所用位移量的範圍。由於位移量字段的大小直接影響到指令的長度,所以其選擇非常重要。 位移量的分布非常廣泛,既存在大量小數值,又有相當數量的大數值。位移值的廣泛分布是由於變量有多個存儲區域,而且訪問他們的位移量不同,並且編譯器使用的總尋址機制也各有不同。 大多數位移值是正數,但最大的胃一直為負數(14位以上)。位移量的位數--整數平均值在0-1之間,浮點平均值為13左右。 立即數或直接操作數尋址方式 在進行算術運算、比較(主要用於分支)和移動時,如果希望將常量放在寄存器中,可以使用立即數。 直接操作數尋址方式可用於寫在代碼中的常量和地址常量。對於立即數的使用,重點是要知道是需要對所有運算都支持立即數還是僅對一部分運算支持立即數。 技術分享圖片 大約有四分之一的數據傳送和ALU運算擁有立即操作數。整數程序在大約五分之一的指令中使用立即數,而浮點程序在大約六分之一的指令中使用立即數。 對於載入操作,載入立即數指令將16位載入一個32位寄存器的任一半中,載入立即數並不是嚴格意義上的載入,因為他們並不訪問存儲器。 與位移值相似,立即數取值的大小也影響到指令長度,小立即數的應用最多,有時也會使用大型立即數,更多的是用在地址計算中。 當預測一種新的體系結構時至少支持以下尋址方式:位移量尋址、立即數尋址和寄存器間接尋址。位移量尋址方式中的地址大小至少為12~16位,立即數字段的大小至少為8~16位。 操作數的類型與大小 常見的操作數類型包括字符(8位)、半字(16位)、字(32位)、單精度浮點(1個字)和雙精度浮點(2個字)。整數都是用二進制補碼數字表示的,所有計算機都遵循相同的浮點標準---IEEE標準754. 一些體系結構提供了對字符串的操作,但是這些操作十分優先,將字符串中的每個字符都看作單個字符,支持對字符串執行的典型操作包括比較和移動。 一些體系結構支持二進制格式,通常稱為壓縮十進制會二進制編碼十進制,通常提供在被稱為壓縮和解壓縮的操作之間來回轉換。 對於基準測試程序,所訪問數據的大小分布,如下圖: 技術分享圖片 雙字數據類型用於表示浮點程序中的雙精度浮點值,還用於表示地址,這是因為該計算機使用64位地址。在采用32位地址的計算機上,64位地址將被32位地址替代,所以整數程序中的幾乎所有雙字訪問都會變成單字訪問。 在一些體系結構中,寄存器中的對象可以作為字節或半字進行訪問,這種訪問非常少見。
指令集中的操作 下表給出10種簡單指令,對於一組在8086上運行的整數程序,這10種簡單指令占到所執行指令的96%。也就是說,執行最多的指令是一個指令集中的簡單操作。 技術分享圖片 指令操作符的分類與示例,所有計算機通常都提供所有前三類運算
操作符類型 實例
算術與邏輯 整數算術與邏輯運算:加減乘除與或
數據傳送 載入--存儲(在采用存儲器尋址的計算機上為move指令)
控制 分支、跳轉、過程調用與返回、陷阱
系統 操作系統調用、虛擬存儲器管理指令
浮點 浮點運算:加、乘、除、比較
十進制 十進制加、十進制乘、二進制到字符的轉換
字符串 字符串移動、字符串比較、字符串搜索
圖形 像素與頂點操作、壓縮/解壓縮操作

控制流指令 當控制中的改變是無條件時,使用跳轉jump。當改變是有條件時,使用分支branch。 通常可區分4種不同類型的控制流變化:條件分支、跳轉、過程調用、過程返回。 控制流指令在一個載入--存儲計算機上的出現頻率 技術分享圖片 將控制流指令分為三類:調用或返回、跳轉和條件分支。條件分支占大多數。 控制流指令的尋址方式: 控制流指令中的目標地址在任何情況下都必須指定。在大多數情況下,這個目標是在指令中明確指定的,但過程返回是一個例外,這是因為在編譯時無法知道要返回的目標位置。 指定目標的最常見方法是提供一個將被加到程序計數器PC的位移量。這類控制流指令被稱為PC相對指令。由於目標位置通常與當前指令的距離較近,而且在指定相對於當前PC的位置時,需要的位數較少,所以PC相對分支或跳轉具有優勢。 采用PC相對尋址可以使代碼的雲訊不受裝載位置的影響,這一特性被稱為位置無關,可以在鏈接程序時減少一些工作,而且對於在執行期間進行動態鏈接的程序也有參考價值。 如果在編譯時不知道目標位置,為了實現返回和間接跳轉,需要一種不同於PC相對尋址的方法,即必須有一種動態指定目標的方法,使目標能夠在運行時發生變化,這種動態尋址只需要給出包含目標地址的寄存器名稱即可,跳轉可能允許使用任意尋址方式來提供目標地址。 寄存器間接跳轉的其他用途:1.case或switch語句 2.虛擬函數或虛擬方法 3.高階函數或函數指針 4.動態共享庫 在以上的四種情況下,目標地址在編譯時都是未知的,通常是在寄存器間接跳轉之前從存儲器加載到寄存器中。 由於分支通常使用PC相對尋址來指定其目標,一個重要的問題是關註分支目標距離分支有多遠 指令中PC相對分支的位移量分布如下: 技術分享圖片 分支距離(以目標與分支指令之間的指令數來表示)。整數程序中最常見的分支是轉向可以用4~8位編碼的目標地址。也就是說,短位移量字段對於分支指令是足夠的,有了較小分支位移量的較短指令,設計者可以提高編碼密度。 對於同一程序,如果體系結構需要的指令較少,那分支距離就較短。但如果計算機的指令長度是變化的,可以與任意字節連接對齊,則表示該位移量所需要的位數可能會增加。 條件分支選項 由於大部分控制流改變都是分支,如何指定分支條件很重要。 對分支條件進行求值的主要方法:
名稱 示例 如何測試條件 優點 缺點
條件碼(CC) 8086、ARM、PowerPC、SPARC、SuperH 測試由ALU運算設定的特殊位,可能受程序的控制 有時條件設置比較自由 CC是一種額外狀態。由於條件碼是將來自一條指令的信息傳給一個分支,所以限制了指令的順序
條件寄存器 Alpha、MIPS 用比較結果測試任意寄存器 簡單 占用一個寄存器
比較與分支 PA_RISC、VAX 比較是分支的一部分。比較範圍通常限於子集 分支需要一條指令,而不是兩條 對流水線執行來說,每個指令要完成的工作過多
條件分支中不同比較類型的使用頻率。編譯器與體系結構的這種組合中,小於分支占主導地位。 技術分享圖片 過程調用選項 過程調用和返回包括控制轉移,還可能涉及一些狀態保存過程;至少必須將返回地址保存在某個地方,有時保存在特殊的鏈接寄存器中,有時只是保存在GPR中。 較早的體系結構提供了一種用於保存許多寄存器的機制,而較新的體系結構需要編譯器為所存儲和恢復的每個寄存器生成存儲和載入操作。 在保存寄存器時,要麽保存在調用位置,要麽保存在被調用的過程內部。調用者保存是指發出調用的過程必須保存它希望在調用之後進行訪問的寄存器。被調用者保存必須保存它希望使用的寄存器,而調用者不受限制。 因為不同的過程編譯是獨立的,所以兩個不同過程中對全局可見變量的訪問存在復雜的關系。編譯器對這樣的過程會采用調用者保存。即由調用者將所有可能在調用期間訪問的變量都保存起來。現在大多數實際系統都采用兩種機制的組合方式,這一約定在應用程序二進制接口ABI中指定,它確定了一些基本規則,指出哪些寄存器應由調用者保存,哪些由被調用者保存。 以上從編譯器的層次,完成了對指令體系結構的設計。 即采用位移量、立即數和寄存器間接尋址方式的載入--存儲體系結構。介紹的數據為8位、16位、32位和64位整數,還有32位和64位的浮點數。 指令包括簡單操作、PC相對條件分支、用於過程調用的跳轉和鏈接指令,還有用於過程返回的寄存器間接跳轉。
指令集編碼 在對指令進行編碼時,由於寄存器字段和尋址方式字段可能在一條指令中出現許多次,所以寄存器數目和尋址方式的數目都對指令大小有顯著影響。對大多數指令而言,對尋址方式字段和寄存器字段進行編碼時所占的位數,要遠多於指定操作碼所占的位數。 常見的指令集編碼選擇: 1.變長編碼:它幾乎允許所有操作使用所有尋址方式。當存在許多尋址方式和操作時,是最佳選擇。8086,VAX。這種方式的代碼表示長度通常是最短的。 結構:操作與操作數數目+地址標識符1+地址字段1+......+地址標識符n+地址字段n 2.定長編碼:它將操作和尋址方式合並到操作碼中,當尋址方式與操作數較少時,其效果最好。 Alpha、ARM、MIPS。定長格式中的操作數個數是相同的,尋址方式作為操作碼的一部分進行指定,生成的代碼規模通常是最大的。 結構:操作+地址字段1+地址字段2+地址字段3 3.混合編碼
編譯器 幾乎所有的臺式機和服務器應用程序都是用高級語言編寫的。意味著:由於所執行的大多數指令都是編譯器的輸出,所以指令集體系結構基本上就是編譯器目標。 編譯器的構成:
結構 相關性 功能
語言分析 與語言有關、與機器無關 將語言轉換為公共中間形式
高級優化 與語言有關、與機器無關 循環轉換和過程內聯化
全局優化 與語言相關較低,與機器稍微有關 全局和本地優化器+寄存器分配
代碼生成器 高度機器相關,語言無關 詳盡的指令選擇和機器相關優化,可能包含匯編器
編譯器的目標是:編譯結果的正確性,編譯後代碼速度,快速編譯、支持調試、語言之間的互操作性。正常情況下,編譯器中的各次掃描將更抽象的高級表示轉換為逐漸低層級的表示方法,最後到達指令集級別。 編譯器假定最後的幾個步驟有能力處理特殊的問題。例如,在知道被調用過程的確切大小之前,編譯器通常就必須選擇對哪些進程調用進行內聯展開。 全局公共子表達式消去法:找出一個表達式計算相同取值的兩個實例,並將第一次計算的結果值保存在臨時存儲位置。然後利用這個臨時值,清除這一公共表達式的第二次計算。必須將臨時值分配到寄存器中。如果沒有將臨時值保存到寄存器中,這一優化會減緩代碼的運行速度。寄存器分配通常是全局優化掃描即將結束、馬上要生成代碼時進行的。因此,執行這一優化的優化程序必須假定寄存器分配器會將這一臨時值分配到寄存器中。 現代編譯器執行的優化分類: 高級優化--一般對源代碼執行,並將輸出結果傳送給之後的優化掃描。 本地優化--僅對直行代碼段內的代碼進行優化 全局優化--將本地優化拓展到分支範圍之外,並引入一組專為優化循環的轉換 寄存器分配--將寄存器與操作數聯系起來 與處理器相關的優化--嘗試充分利用特定的體系結構知識 寄存器分配 寄存器分配是最重要的優化,寄存器分配算法以圖形著色的技術為基礎。圖形著色技術的基本思想:構造一幅圖,用來表示可能執行的寄存器分配方案,然後利用這個圖來分配寄存器。 問題在於如何使用有限種顏色,使相關圖中兩個相鄰結點的顏色都不相同。這種方法的重點是將活躍變量全部分配到寄存器中。當至少有16個通用寄存器可用於為整數變量進行全局分配,而且有其他寄存器分配為浮點變量時,圖形著色方法效果最好。
優化名稱 解釋 優化轉換總數的百分比
高級優化: 過程整合 在源代碼級別或接近該級別,與處理器無關 用過程主體代替過程調用
本地優化: 公共子表達式消除法 常量傳播 降低棧高度 在直行代碼範圍內 用單一副本代替同一計算的兩個示例 對於一個被賦值為常量的變量,用該常量代替其所有實例 重新排列表達式樹,用以最大限度地減少表達式求值所需的資源 18% 22%
全局優化: 全局公共制表達式消除法 副本傳播 代碼移動 消去歸納變量 跨越分支 與本地優化相同,但跨越了分支範圍 對於一個已經被賦值為X的變量A(A=X),用X代替變量A的所有實例 如果在循環的每次叠代中,其中一些代碼總是計算相同值 簡化/消去循環內的數組尋址計算 13% 11% 16% 2%
與處理器相關優化: 降低強度 流水線調度 分支偏移優化 依賴於處理器知識 許多示例,比如用加法和移位來代替與常量的乘法 重新排列指令順序,以提高流水線性能 選擇能夠到達目標的最短分支位移
高級語言用來保存數據的三個獨立區域: 1.棧用於分配本地變量。棧會在進程調用與返回時相應增大或縮小。棧內的對象是相對於棧指針進行尋址的,這些對象主要是標量,而不是數組。 棧用於活動記錄,而不是用於表達式求值。因此,幾乎不會在棧中壓入或彈出數值。 2.全局數據區用於靜態分配所聲明的對象,比如全局變量和常量。這些對象中有很大一部分都是數組或其他聚合數據結構 3.堆用於分配不符合棧規則的動態對象。棧中的對象用指針訪問,通常不是標量。 對於分配到棧中的對象,寄存器分配的處理效率要遠高於對全局變量的處理效率,而寄存器分配對於分配到堆中的對象基本上不可能實現,因為他們都用指針訪問的。 全局變量和一些棧變量可不可能分配,因為他們具有別名,即有多種方法可以引用變量的地址,從而不能合法地將其放到寄存器中。

指令集基本原理