計算機指令


理清幾個點:

  1. 指令中存放的二進制碼

    例如R型指令:

    opcode:操作碼 Rm:第二源操作數 Shamt:位移量 Rn:第一源操作數 Rd:目的寄存器
    11bit 5bit 6bit 5bit 5bit

    例如,這里的Rm位置下的二進制碼表示的是二進制的地址(指的是第幾個寄存器),這里用的是寄存器尋址,就是說要取的值還在寄存器里,不會把它放入我們的指令中來。

  2. word 大小依賴平台(cpu一次能處理的位數)
    字節 byte 8bit
    bit 1bit

    1KB = 1024Bit、1MB = 1024KB 、1GB = 1024MB

2.1引言

計算機中的基本單詞成為指令(如ADD,SUB,CMP

指令集為計算機全部指令——也包括偽指令,不過經過匯編器會被編為基本指令。偽指令就是基本指令的變種。

指令集的兩種形式:

  • 便於人們書寫的(匯編語言)
  • 計算機所識別的(二進制碼)

2.2計算機硬件的操作

例如:

ADD a b c

這匯編語句對應一條指令,這條指令有且只有三個操作數。

這符合我們設計原則1:簡單源於規整。

2.3計算機硬件的操作數

LEGv8算數運算指令的操作數必須來自寄存器。

LEGv8的寄存器為64bit。

LEGv8體系下的字為32bit。

雙字為64bit。

LEGv8體系下的寄存器被限制為32個。為什么?

設計原則2:越少越快

大量的寄存器會使時鍾周期變成,因為電信號要傳的更遠。
另一個原因是指令的現在,LEGv8指令格式只給操作數分配5個bit,如果寄存器超出32個,那么指令也會隨之變長——因為需要更多的位來表示操作數。

2.3.1存儲器操作數

STUR Rd [基址寄存器,#偏移量]

寄存器溢出:就是指要存的變量的值多於寄存器的個數,所以我們將不常用的變量的值放在存儲器中。

2.4有符號數和無符號數

最低有效位:最右邊的一位

最高有效位:最左邊的一位

原碼表示法:符號和幅值表示法(舍棄不用)

補碼:

  • 前置位為0:正數
  • 前置位都為1:負數

補碼負值計算公式:

符號擴展——指從存儲器中載入寄存器的數值沒有64位,那么就要用數字去填充空缺位

  • 符號擴展:就是用符號位去填充空缺
  • 零擴展:用0去填充空缺位置。

2.5計算機中指令的表示

設計原則3:優秀的設計需要好的權衡和折中

因為有些指令不要三個寄存器,而需要更大位數來表示立即數和跳轉地址,但指令長度又被限制於32位,所以好把表示寄存器的位數用於表示立即數。例如無條件跳轉指令B,opcode只有6bit剩下的全部用於地址位,還有以下列舉的指令類型。

D型指令(load,store):

opcode address op2:操作碼的邏輯擴展 Rn:基址寄存器 Rt
11bit 12bit 2bit 5bit 5bit

I型指令(ADDI,SUBI)

opcode immediate Rn Rd
10bit 12bit 5bit 5bit

重點

計算機構建原則:

  • 指令用數的形式表示
  • 程序和數據一樣,存儲在存儲器上進行讀寫。
這兩個准則引發了存儲程序原理的誕生,使得計算機發揮了巨大的潛力。存儲器可以存放各種東西。

2.6邏輯操作

  • 左移:LSL
  • 右移:LSR
  • 與:AND——操作位相同,結果位1
  • 或:OR——操作位不同,結果位1
  • 取反:NOT——0變1,1變0
  • 異或:EOR——操作位不同時未1

2.7決策指令

CBZ register Lable 如果register為0跳轉到Lable
CBNZ register Lable 如果register不為0跳轉到Lable

B.cond 指令 ,cond表示條件

cond用於有符號數:

  • EQ:equal
  • NE:not equal
  • LT:less than
  • LE:less equal
  • GT:greater than
  • GE:greater equal

cond用於無符號數:

  • LO:low
  • LS:low same
  • HI:high
  • HS:high same

LEGv8還有一下四個二進制位來表示指令執行狀態:

  • 負數標志位(N)
  • 零標志位(Z)
  • 溢出標志位(V)
  • 僅為標志位(C):若從最高位借位或者進位就設置條件嗎。

到頭設置標志位的方法:

  • 用指令比較兩個寄存器大小,然后根據結果跳轉。
  • 比較兩個寄存器的值,然后用第三個寄存器記錄比較是否成功。

只有ADD,ADDI,AND,ADNDI,SUB,SUBI能設置條件碼,想要設置條件碼就在指令尾加S(ADDS)

2.7.2邊界檢查

SUBS XZR, X20, X11 --X11=length
B.HS IndexOutOfBounds

檢查索引是否越界。

如果一個有符號數(非負數) 減去一個無符號數,如果有符號數>無符號數,運算中產生進位或者借位時,C置為1,

如果有符號數是一個(負數)那么大於無符號數,運算中中產生進位或者借位時,C置為1.

跳轉指令就可以憑借進位標志位(C)是否為1懸着跳轉。

2.8對函數的支持

執行函數的步驟:

  1. 將參數放到函數可以訪問到的地方
  2. 將執行控制權交給函數
  3. 獲得函數執行所需的存儲資源
  4. 執行所需的任務
  5. 將結果值放在調用程序可訪問的地方
  6. 將控制點放回調用點,因為一個函數在多個地方可能會被調用

LEGv8為過程(函數)分配寄存器的約定:

  • X0~X7:作為參數寄存器用於傳遞參數和放回結果
  • LR(X30):作為放回地址寄存器,用於放回原始調用點。asdas

分支和連接指令(Branch and Link instruction)

BL procedureAddress
用於跳轉到某個地址的同時將下一條指令的地址保存在寄存器中。

寄存器跳轉指令(Branch register)

BR LR
跳轉會原始調用點。

PC:保存當前真正執行的指令地址

  • 程序計數器
  • 指令地址寄存器

兩個都是PC的別名。

我疑惑:棧為什么要從高地址開始?

	早期的系統需要考慮有限內存下的內存布局問題。具體來說,內存的一端放置了靜態代碼和靜態數據之后,剩余的區域,既需要動態數據,又需要可增長的棧,那么合理的方案就是各放一端向中間生長。現在的問題就是兩個選項:靜態內存放在哪端;棧是在靜態內存的同端還是對端。

2.8.1使用更多的寄存器

問題背景當函數需要的寄存器數量超過8個時,編譯器就要使用臨時的寄存器。

因為臨時的寄存器的寄存器可能放一些值被其他的指令所使用,所以編譯器使用臨時的寄存器不能留下痕跡(就是要回復原始數據)

這里保存和恢復舊數據使用stack,為了避免保存和恢復一個未使用過的寄存器。

LEGv8將寄存器分為兩組:

  • X9~X17:不需要保留(調用者負責)
  • X19~X28:需要保留(被調用者負責)

2.8.2過程(函數)嵌套

葉過程:不調用其他過程(函數)

如果存在嵌套過程,即A調用B,B調用C。

此時:

  • X0X7與X9X17中所使用的寄存器由調用者負責保存
  • X19~X25和LR由被調用者負責保存。

2.8.3在Stack中為新數據分配內存

這個討論的是,Stack中不僅要保存寄存器的值(寄存器的大小是固定的)還要保存局部變量的值(大小是不固定的,INT32bit,Double64bit)。

過程幀(活動記錄):Stack中包含過程所保存的的寄存器和局部變量的片段。

幀指針:指向給定過程中的寄存器和局部變量的值。

  • 一種的幀指針指向過程的第一雙字。
  • 另一種是在一個過程中為本地存儲器引用提供一個固定的基址。

2.8.4在堆中為新數據分配內存

堆:類似鏈表這樣的數據結構通常會在生命期內增長或者縮短,這類數據結構對應的段。

LEGv8語言中寄存器的使用約定:

2.9人機交互

  • LDURB:將字節從內存中讀到寄存器的最右邊
  • STURB:將寄存器最右邊八位寫入內存中

表示一個字符串的三種方法:

  1. 保留字符串的第一個位子用於給出字符串的長度(java)
  2. 附加一個指明字符串長度的變量
  3. 字符串最后一個位置用一個字符來標識結尾(C語言)

2.10寬立即數和地址尋址。

2.10.1寬立即數

寬立即數用於更多位的常量或地址(因為LEGv8的指令長度限制了常熟或者地址的大小)

設置寄存器中的16位:

  • MOVZ:將寄存器剩下的位置置0
  • MOVK:將寄存器剩余的位置不變

LSL實現要加載的16位字段。LSL后面的數(加載完16位字段后剩余的位)取決於需要64位雙字的那個象限。例如加載到第四象限,那么后面剩下48位。

例如:

被載入的寄存器 被載入的數字 第幾位開始載入
MOVZ X9 255 LSL 16

2.10.2分支尋址

B指令的指令格式:

  • 分支指令
  • 分支和鏈接指令
6bit 26bit

CBZ等條件分支指令和依賴條件碼分支指令的格式

8bit 19bit 5bit

$$
程序計數器=寄存器內容+分支地址偏移量
$$

這個計算式表明:19位來表示一個地址可能不夠用了,因為一個程序不可能小於2^19。

因此我們要指定一個寄存器將它與分支地址偏移量相加,得到跳轉的地址。

這里我們要采用PC寄存器,因為條件分支轉移地址傾向於轉移到附近的指令。SPEC測試中顯示轉移的范圍來16個字以內(16條指令以內,因為一條指令的長度就是一個字)。運用這個公式我們可以轉移到2^18個字內,(只要用邏輯操作將分支偏移量*4 因為LEGv8的地址是按字節編址的)。

條件分支和跳轉指令(B)采用PC相對尋址。

分支和鏈接指令采用其他尋址方式。

2.11並行與指令:同步

同步:一個線程若需要其他線程產生的結果那么它需要停下來等待,就是一個個做。

線程同步:即當有一個線程在對內存進行操作時,其他線程都不可以對這個內存地址進行操作,直到該線程完成操作, 其他線程才能對該內存地址進行操作,而其他線程又處於等待狀態,實現線程同步的方法有很多,臨界區對象就是其中一種。

例程:某個系統對外提供的功能接口或者服務集合。

lock和unlock可以直接建立互斥區,允許當個處理器操作。

處理器對鎖單元加鎖的方法使用一個寄存器的1與該鎖(鎖在臨界區里)地址里的值交換,若寄存器為1,表面臨界區在被使用,若為0則可以被占用。

同步交換(atomic)由當個不可中斷的指令對實現。

指令對:

  • LDXR:取出鎖值
  • STXR:對臨界區進行操作。

STXR由兩個功能:

STXR X23, X9, [X20] -- Memory[X20] = X23 如果成功x9 = 0否則x9 = 1
  1. 將存儲地址寫入X20
  2. 將X9置為1或者0.

2.12翻譯並啟動程序

編譯器:將高級語言編譯為匯編語言

匯編器:可以識別並轉換為功能等價的機器語言

目標文件(.o,.obj)包含的內容:

  • 目標托文件
  • 代碼的
  • 靜態數據段
  • 重定位信息
  • 符號表 ——程序中出現的(符號和地址)對。
  • 調試信息

連接器:將改變代碼不需要重新編譯所有的,只需要編譯和匯編某一行在用連接器縫合。

工作的步驟:

  • 將代碼和數據塊象征性的放入內存。
  • 決定數據和指令標簽的地址
  • 修補內部和外部引用。

連接器可以生成可運行的文件,內容和目標文件差不多,但不包括未解決的引用。

加載器

  • 讀取可執行文件頭來確定代碼段的地址空間
  • 為代碼和數據創建一個足夠大的地址空間
  • 將可執行文件的指令和數據復制到內存中
  • 把主程序的參數復制到棧中
  • 初始化處理器中的相關寄存器,將棧指針指向第一個空位置
  • 條狀到啟動例程,該例程將參數復制到參數寄存器並且調用程序的main函數。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM