Y86-64 指令集架構 (Y86-64 ISA)

Overview Table

主題 核心內容 書頁
ISA 的定義 狀態 (state) + 指令與編碼 (encodings) + 程式慣例 (conventions) + 例外處理 (exceptions) p.391
Programmer-visible state 15 個 registers、3 個 condition codes (ZF/SF/OF)、PC、memory、Stat p.391-392
Y86-64 指令集 x86-64 的子集:movq 拆成 4 種、OPq 只作用於 register、7 種 jXX、6 種 cmovXX p.392-394
指令編碼 每指令 1-10 bytes;首 byte = code:function;register ID 0-0xE,0xF = 無 register p.394-396
唯一解讀性 byte 編碼有唯一解讀 (unique interpretation);首 byte 決定長度與意義 p.395-396
RISC vs CISC Y86-64 兼有兩者特徵:CISC 的 CC 與變長編碼 + RISC 的 load/store 架構 p.397-399
例外 (Stat) AOK=1 / HLT=2 / ADR=3 / INS=4;非 AOK 即停機 p.399-400
Y86-64 程式 sum 範例、assembler 指示詞 (.pos/.align/.quad)、yas 組譯器與 yis 模擬器 p.400-405
指令細節 pushq %rsp push 原值;popq %rsp 將記憶體讀出的值寫入 %rsp p.406-408

4.1.1 Programmer-Visible State(p.391-392)

Programmer-visible state 指每條 Y86-64 指令可以讀取與修改的處理器狀態;此處的「programmer」是撰寫組合語言的人或產生機器碼的 compiler。實作處理器時不必照 ISA 的形式組織狀態,只要機器層級程式「看起來」能存取這些狀態即可——這是後續 SEQ/PIPE 實作的基礎假設。

+---------------------------------------------------------------+
| RF: Program registers (15 個, 各 64-bit)                       |
|  %rax %rcx %rdx %rbx | %rsp %rbp %rsi %rdi | %r8 ... %r14      |
+---------------------------------------------------------------+
| CC: Condition codes    | Stat: Program status                  |
|   ZF  SF  OF           |  (AOK / HLT / ADR / INS)              |
+------------------------+--------------------------------------+
| PC: Program counter    | DMEM: Memory (概念上為一大 byte 陣列)  |
+---------------------------------------------------------------+

4.1.2 Y86-64 指令(p.392-394)

Y86-64 指令集大致是 x86-64 的子集:只有 8-byte 整數運算(因此可無歧義地稱為「word」)、較少的 addressing modes、較少的運算種類。組合語法類似 x86-64 的 ATT format

指令 說明 關鍵限制
irmovq, rrmovq, mrmovq, rmmovq x86-64 的 movq 拆成 4 種,第 1 字母 = 來源 (i/r/m)、第 2 字母 = 目的 (r/m) 記憶體參照只有 base + displacement(無 index register、無 scaling);不允許 memory-to-memory 與 immediate-to-memory
OPq(addq, subq, andq, xorq) 4 種整數運算,設定 ZF/SF/OF 只能作用於 register data(x86-64 可作用於 memory)
jXX(jmp, jle, jl, je, jne, jge, jg) 7 種 jump,依 condition codes 決定是否跳 branch conditions 與 x86-64 相同(見 03-Machine-Level-Programs/03-Control-Flow)
cmovXX(cmovle, cmovl, cmove, cmovne, cmovge, cmovg) 6 種 conditional move,格式同 rrmovq 僅當 CC 滿足條件才更新目的 register;只能 register–register
call / ret call 將 return address 推入 stack 後跳到目的位址;ret 返回
pushq / popq 與 x86-64 相同的 push/pop
halt 停止執行,Stat 設為 HLT x86-64 對應的 hlt 是應用程式不可用的特權指令
nop 不做事
Important

movq 拆成 4 種是為了讓每種資料傳輸型式在硬體實作時能被明確辨識——這在第 4.3 節設計 SEQ 各階段時會直接派上用場。

4.1.3 Instruction Encoding(p.394-396)

每條指令需要 1 到 10 bytes,由所需欄位決定。第一個 byte 一律是 instruction specifier,分成高 4 bits 的 code 與低 4 bits 的 function:

指令總長 = 1 (specifier byte)
         + [0 或 1] (register specifier byte: rA:rB)
         + [0 或 8] (constant word: V / D / Dest)

  byte 0        byte 1        後續 8 bytes
+---------+  +-----------+  +--------------------------+
| code:fn |  |  rA : rB  |  |  8-byte constant (LE)    |
+---------+  +-----------+  +--------------------------+

各指令編碼(hex):

指令 編碼 長度
halt 00 1
nop 10 1
rrmovq rA, rB / cmovXX rA, rB 2fn rA:rB 2
irmovq V, rB 30 F:rB V 10
rmmovq rA, D(rB) 40 rA:rB D 10
mrmovq D(rB), rA 50 rA:rB D 10
OPq rA, rB 6fn rA:rB 2
jXX Dest 7fn Dest 9
call Dest 80 Dest 9
ret 90 1
pushq rA A0 rA:F 2
popq rA B0 rA:F 2

Function codes(fn;code 相同的一組指令以 fn 區分):

fn OPq (6fn) jXX (7fn) cmovXX (2fn)
0 addq jmp rrmovq
1 subq jle cmovle
2 andq jl cmovl
3 xorq je cmove
4 jne cmovne
5 jge cmovge
6 jg cmovg

Register identifiers(register file 是 CPU 內的小型 RAM,ID 即位址):

ID Register ID Register
0 %rax 8 %r8
1 %rcx 9 %r9
2 %rdx A %r10
3 %rbx B %r11
4 %rsp C %r12
5 %rbp D %r13
6 %rsi E %r14
7 %rdi F No register

編碼範例(p.395):rmmovq %rsp, 0x123456789abcd(%rdx)

  1. rmmovq 首 byte = 40
  2. rA = %rsp = 4、rB = %rdx = 2 → register byte = 42
  3. displacement 補零成 8 bytes:00 01 23 45 67 89 ab cd → byte-reversed:cd ab 89 67 45 23 01 00
  4. 完整編碼:4042cdab896745230100
Important

唯一解讀性 (unique interpretation):任意 byte 序列要嘛編碼唯一的指令序列、要嘛不合法。Y86-64 成立,因為每指令首 byte 的 code:fn 組合唯一,且由首 byte 即可決定後續 bytes 的長度與意義。前提是必須從序列的第一個 byte 開始;若不知道起始位置,就無法可靠切分指令——這正是 disassemblers 從 object code 抽取程式時的困難所在。

Aside:x86-64 vs Y86-64 編碼比較(p.396)

Y86-64 編碼較簡單但較不緊湊:register 欄位固定位置(x86-64 塞在各種位置);常數一律 8 bytes(x86-64 可用 1/2/4/8 bytes)。

RISC vs CISC(Aside, p.397-399)

CISC(complex instruction set computer)由早期電腦演化而來,1980 年代初指令集已相當龐大;RISC(reduced instruction set computer)則是 1980 年代初 IBM(John Cocke)提出的替代路線,後由 Berkeley 的 Patterson 與 Stanford 的 Hennessy 發展並命名——更簡單的指令集可用更少硬體實作,並能組織成高效的 pipeline 結構。

面向 CISC 早期 RISC
指令數量 很多(Intel 文件超過 1,200 頁) 通常少於 100
執行時間 有長執行時間的指令(整塊記憶體複製等) 無長執行時間指令(有些甚至無整數乘法)
編碼 變長(x86-64:1-15 bytes) 定長(通常 4 bytes)
Operand 格式 多樣(displacement + base + index + scale) 簡單,通常僅 base + displacement
算術/邏輯運算 可作用於 memory 與 register 僅 register;memory 只透過 load/store → load/store architecture
實作細節 對機器層級程式隱藏 暴露給程式(如 delayed jumps),由 compiler 最佳化
條件判斷 Condition codes(指令副作用) 無 CC;明確 test 指令把結果存進一般 registers
Procedure 連結 Stack-intensive(參數與返回位址放 stack) Register-intensive(常有多達 32 個 registers)

4.1.4 Y86-64 Exceptions(p.399-400)

Value Name 意義
1 AOK 正常執行
2 HLT 執行了 halt 指令
3 ADR 存取非法記憶體位址(fetch 指令或讀寫資料時);超過實作規定的最大位址即觸發
4 INS 遇到非法 instruction code
             AOK (正常執行)
              |
   +----------+-------------+
   |          |             |
 halt      非法位址       非法指令
   v          v             v
  HLT        ADR           INS
   \__________|____________/
              v
        處理器停止執行 (本設計)

4.1.5 Y86-64 Programs(p.400-405)

以 C 函式 sum(long *start, long count)(迴圈累加陣列)比較 x86-64(gcc 產生)與 Y86-64(手寫)程式碼,差異有三:

完整程式檔的結構(Figure 4.7):以 . 開頭的字是 assembler directives:

Directive 作用
.pos 0 從位址 0 開始產生程式碼(所有 Y86-64 程式的起始位址)
.align 8 對齊 8-byte 邊界
.quad V 插入一個 8-byte 資料字
0x000  irmovq stack,%rsp   ; 設定 stack pointer
       call main           ; 執行主程式
       halt                ; 終止
0x018  array: 4 個 .quad   ; 資料 (.align 8)
0x038  main:  設參數 → call sum → ret
0x056  sum:   迴圈累加
0x200  stack:              ; stack 起點, 向低位址成長
        (須確保 stack 不會長到覆蓋 code/data)

4.1.6 Some Y86-64 Instruction Details(p.406-408)

兩個特殊的指令組合需要明確定義,Y86-64 採用與 x86-64 一致的慣例(以實驗驗證,Practice Problems 4.7、4.8):

指令 歧義 Y86-64/x86-64 慣例
pushq %rsp push 原值還是遞減後的值? push 指令執行前的原值(實驗中 pushtest 回傳 0)
popq %rsp %rsp 設為讀出的值還是遞增後的 stack pointer? 設為從記憶體讀出的值(實驗中 poptest 回傳 0xabcd);行為等同 mrmovq (%rsp), %rsp
pushq %rsp:                      popq %rsp:
  R[%rsp] <- R[%rsp] - 8           R[%rsp] <- M[R[%rsp]]
  M[R[%rsp]] <- (原 %rsp 值)        (讀出值直接覆蓋, 遞增結果被丟棄)
Aside:x86 各型號的不一致(p.408)

Intel 文件記載:Intel 286 之後的 IA-32 處理器 PUSH ESP push 原值,但 8086 的 PUSH SP push 遞減後的值。這種不一致 (1) 降低程式可攜性、(2) 使文件複雜化——教訓是:事先定清楚細節、力求完全一致,長期能省下大量麻煩。(pop 到 stack pointer 則沒有這種跨型號歧義。)

Exam/Test Patterns

情境 / 關鍵字 答案
Y86-64 有幾個 program registers?少了哪個? 15 個;省略 %r15 以簡化編碼
給組語求 byte 編碼(如 Practice 4.1) 首 byte 查 code:fn → register byte rA:rB(單 register 補 F)→ 常數補足 8 bytes 後 byte-reversed(little-endian)
給 byte 序列反解指令(如 Practice 4.2) 首 byte 決定指令與長度;遇到不存在的 code:fn(如 f0)即 invalid → 對應 Stat = INS
irmovq V, rB 的 register byte F:rB(來源欄設 0xF = no register)
register 欄位值 0xF 的意義 不存取任何 register
jXX / call 的 Dest 定址方式 絕對位址(非 x86-64 的 PC-relative)
為什麼 byte 編碼可唯一解讀? 每指令首 byte 的 code:fn 唯一,且首 byte 決定總長度;但必須知道起始 byte 位置
哪些指令設定 condition codes? 只有 OPq(addq/subq/andq/xorq)
Y86-64 的 OPq 能用 immediate 或 memory operand 嗎? 不能,只能 register–register;immediate 須先 irmovq 入 register
Stat 四種值 AOK=1(正常)、HLT=2(halt)、ADR=3(非法位址)、INS=4(非法指令)
pushq %rsp push 什麼值? 原始(未遞減)的 %rsp 值
popq %rsp 等價於哪條指令? mrmovq (%rsp), %rsp
Y86-64 哪些特徵屬 CISC、哪些屬 RISC? CISC:condition codes、變長編碼、return address 放 stack;RISC:load/store 架構、規則編碼、register 傳參
所有 Y86-64 程式的起始位址 位址 0(.pos 0)
最長 / 最短的指令 10 bytes(irmovq/rmmovq/mrmovq);1 byte(halt/nop/ret)