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 陣列) |
+---------------------------------------------------------------+
- 15 個 program registers:%rax–%rdi 加上 %r8–%r14,各儲存一個 64-bit word。省略 x86-64 的 %r15 以簡化指令編碼。
- %rsp 被 push、pop、call、return 指令當作 stack pointer;其餘 registers 沒有固定意義。
- 3 個 single-bit condition codes:ZF(zero)、SF(sign)、OF(overflow),記錄最近一次算術/邏輯指令的效果。
- PC 保存目前執行指令的位址。
- Memory 概念上是一大塊 byte 陣列,程式使用 virtual addresses;硬體與 OS 合作轉譯為 physical addresses(詳見 09-Virtual-Memory/01-Address-Spaces-and-VM-Caching)。
- Stat(status code)表示程式執行的整體狀態:正常或發生某類例外。
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 |
不做事 |
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 |
rrmovq與 conditional moves 共用 code 2,可視為「無條件 move」,如同jmp是無條件 jump,兩者 function code 都是 0。
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 |
- 只需一個 register operand 的指令(
irmovq,pushq,popq)把另一個 register 欄位設為 0xF(無 register)——此慣例在處理器實作中很有用。 - 8-byte constant word 的三種用途:
irmovq的 immediate、rmmovq/mrmovq的 displacement、branch 與 call 的 destination。 - branch/call 目的位址採絕對定址 (absolute addressing),而非 x86-64 的 PC-relative;PC-relative 的優點是編碼較短、程式碼可搬移而不必更新 branch targets,Y86-64 為求簡化而不採用。
- 與 x86-64 相同,所有整數採 little-endian 編碼,反組譯時 bytes 呈反序。
編碼範例(p.395):rmmovq %rsp, 0x123456789abcd(%rdx)
rmmovq首 byte =40- rA = %rsp = 4、rB = %rdx = 2 → register byte =
42 - displacement 補零成 8 bytes:
00 01 23 45 67 89 ab cd→ byte-reversed:cd ab 89 67 45 23 01 00 - 完整編碼:
4042cdab896745230100
唯一解讀性 (unique interpretation):任意 byte 序列要嘛編碼唯一的指令序列、要嘛不合法。Y86-64 成立,因為每指令首 byte 的 code:fn 組合唯一,且由首 byte 即可決定後續 bytes 的長度與意義。前提是必須從序列的第一個 byte 開始;若不知道起始位置,就無法可靠切分指令——這正是 disassemblers 從 object code 抽取程式時的困難所在。
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) |
- Y86-64 兼有兩者特徵:CISC 面——有 condition codes、變長指令、以 stack 存返回位址;RISC 面——load/store 架構、規則化編碼、以 registers 傳遞參數。可視為「把 CISC(x86)用 RISC 原則簡化」。
- 1990 年代初論戰平息:純 RISC 或純 CISC 都不如兩者優點的融合。現代 CISC 機器把指令動態轉譯成 RISC-like 的簡單運算(如 register-memory add 拆成 read → add → write 三個 operations),詳見 05-Program-Optimization/02-Modern-Processor-Operation。x86 靠向後相容性主宰桌機/伺服器市場;RISC(如 ARM)則稱霸嵌入式市場。
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
處理器停止執行 (本設計)
- 本章設計中,遇到任一例外處理器就直接停止。完整的設計會呼叫 exception handler(可設定為中止程式或呼叫使用者定義的 signal handler),詳見 08-Exceptional-Control-Flow/01-Exceptions。
4.1.5 Y86-64 Programs(p.400-405)
以 C 函式 sum(long *start, long count)(迴圈累加陣列)比較 x86-64(gcc 產生)與 Y86-64(手寫)程式碼,差異有三:
- Y86-64 須先用
irmovq把常數載入 registers(如常數 8、1),因為算術指令不能用 immediate data。 - 從記憶體讀值再加到 register 需要兩條指令(
mrmovq+addq);x86-64 一條addq (%rdi),%rax即可。 - 利用
subq也會設定 CC 的特性省去testq;但進迴圈前必須先用andq %rsi,%rsi把 CC 設好,jne的第一次判斷才正確。
完整程式檔的結構(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)
- 因為工具只有 assembler,程式設計者必須自行處理平常交給 compiler、linker、run-time system 的工作:stack 擺放、資料初始化、程式初始化與終止。
- yas(Y86-64 assembler):輸出 ASCII 格式的 object code,每行含位址與 1-10 bytes 的機器碼(Figure 4.8)。
- yis(instruction set simulator):模擬 Y86-64 機器碼程式的執行,不模擬任何特定處理器實作;可在硬體完成前除錯程式,也可用來核對硬體模擬或實機執行的結果。輸出僅列出有變動的 registers 與 memory words(左為原值、右為終值)。範例輸出:
Stopped in 34 steps at PC = 0x13. Status 'HLT', CC Z=1 S=0 O=0,%rax = 0xabcdabcdabcdabcd 即 4 元素陣列之和;stack 從 0x200 向下用到 0x1f0-0x1f8,程式碼最大位址僅 0x090,故未被覆蓋。
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 值) (讀出值直接覆蓋, 遞增結果被丟棄)
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) |
Related Notes
- 04-Processor-Architecture/02-Logic-Design-and-HCL — 下一節:實作此 ISA 所需的硬體元件與 HCL
- 04-Processor-Architecture/03-Sequential-SEQ-Implementation — 以 SEQ 實作 Y86-64 指令的各階段
- 04-Processor-Architecture/05-Pipelined-PIPE-Implementation-and-Hazards — RISC 式規則編碼對 pipeline 的意義
- 03-Machine-Level-Programs/01-Program-Encodings-and-Data-Formats — x86-64 編碼與資料格式(對照母集)
- 03-Machine-Level-Programs/03-Control-Flow — condition codes 與 jXX/cmovXX 條件的來源(Figure 3.15)
- 08-Exceptional-Control-Flow/01-Exceptions — 完整設計中 exception handler 的角色