2 6 窺探 ISA 的精妙設計 - ianchen0119/AwesomeCS GitHub Wiki

學習 IEEE-754 的浮點數設計讓我們知道工程師對於每一個 bit 都是斤斤計較的。這樣的觀念同樣可以套用在 ISA 上面,即使現今的 CPU 有 32 位元以上的定址能力,也不代表我們所使用的組語指令可以一次處理 32 bits 的資料。 本篇文章會跟大家分享筆者在 ARM 與 RISC-V 上的發現,一起來學習兩個不同的 ISA 分別碰到哪些問題以及它們特有的處理方法。

ARM

在學習過 RV32I 的 Format 後,要去看別家 ISA 的 Spec 絕非難事。相信各位讀者都能看懂下圖的:

ARM instruction format

以其中的 MOV 指令為例:

MOV r0, #0x1000

該指令會讓 R0 暫存器儲存整數 4096 。 不過在 MOV 指令的格式表中, Oprand2 僅被分配到 12 個位元,這意味著我們只能存放 0 - 8191 的數值,若我們今天想要儲存遠大於 8191 的整數時該怎麼做呢? 在 ARM 中,他利用了 Rotate 達到該目的。

Rotate

ARM 不像其他 ISA ,沒有直接使用 immediate 指令,所以像 MOV 指令會透過 bit 25 來得知 operand2 為 register 還是 immediate , ARM 設計將 Oprand2 的 12 個 bits 分配成兩個部分:

  • 4 bits 用來表示 16 種位移方式,因此我們可以選擇 rotate 2, 4, 8, ..., 32 個 bits 。
  • 8 bits 用來表示 256 種數字組合,如果沒有進行 Rotate ,剛好能表示範圍為 0 - 255 的數字。

Rotate 四個 bits

圖片來源

上圖為 rotate 4 個 bits 的示意圖,也就是將 LSB 的 4 個 bits 挪到 MSB 的位置,其指令如下:

MOV r0, r0, ROR #4

16 種位移模式的資料分布狀況也都可以從下圖得知:

如此一來,就能利用 12 個 bit 完整的操作 32 bit 的空間。

RISC-V

RISC-V 採用了 immediate 的方式,直接存取立即數。 關於 Instruction set 的格式,下圖都有紀錄:

RV32I

根據上圖,因為還需要保留 register, funct, opcode 等資訊,不難發現在任何指令集的 immediate 都不可能分配到 32 bits 的空間。 不過, RISC-V 明顯是個狠腳色,如果一個指令做不完,那我就多做幾步即可,以 Hello, world! 程式的彙編檔案為例:

    .text
    .align 2
    .globl main
main:
    addi sp, sp, -16
    sw   ra, 12(sp)
    lui  a0, %hi(string1)
    addi a0, a0, %lo(string1)
    lui  a0, %hi(string2)
    addi a0, a0, %lo(string2)
    call printf
    lw   ra, 12(sp)
    addi sp, sp, 16
    li   a0, 0
    ret
    .secton rodata
    .balign 4
string1:
    .string "Hello, %s!\n"
string2:
    .string "world"

重點放在第 7 - 8 行, lui 以及 addi 都是屬於 RV32I 指令,用途分別為:

  • ADDI

常數部分為 sign-extended 12-bit ,會將 12-bit 做 sign-extension 成 32-bit 後,再與 rs1 暫存器做加法運算,將結果寫入 rd 暫存器,addi rd, rs1, 0 可被使用來當做 mov 指令。

  • LUI

將 unsigned 20-bit 放到 rd 暫存器的最高 20-bit,並將剩餘的 12-bit 補 0 ,此指令可與 ADDI 搭配,一起組合出完整 32-bit 的數值。

31(MSB) 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0(LSB)
19(imm) 18(imm) 17(imm) 16(imm) 15(imm) 14(imm) 13(imm) 12(imm) 11(imm) 10(imm) 9(imm) 8(imm) 7(imm) 6(imm) 5(imm) 4(imm) 3(imm) 2(imm) 1(imm) 0(imm) 4(rd) 3(rd) 2(rd) 1(rd) 0(rd) 6(opcode) 5(opcode) 4(opcode) 3(opcode) 2(opcode) 1(opcode) 0(opcode)

%hi()%lo() 又能分別為我們取高位的 20 bits 以及低位的 12 bits ,透過這個技巧,我們就能在 RISC-V 架構中完整的利用 32 bits 的空間囉!

Reference