1. 程式人生 > 其它 >CSAPP(四)——處理器體系架構

CSAPP(四)——處理器體系架構

指令集在CPU和程式設計師(編譯器)之間提供了一個抽象層,看起來,CPU在一條接著一條的順序執行編譯後的指令,但出於效能考慮實際情況卻遠比這個“看起來”要複雜。現代CPU使用一種稱作“流水線”的技術來執行每一條指令。

本章基於一種具有簡單指令集“Y86-64”的CPU架構進行研究,旨在對處理器的整個體系架構的瞭解更進一步。

Y86-64指令集體系結構

程式設計師可見的狀態

Y86-64指令

和x86-64不同的幾點:

  1. mov類命令,它的運算元型別必須由特定的字首指定,比如rrmoveq代表該mov指令的第一個運算元是一個暫存器,第二個也是一個暫存器。這使得我們對指令進行二進位制編碼時更加方便。mov類指令不允許mm
    im/mi操作,即記憶體到記憶體,記憶體和暫存器的直接移動。
  2. OPq類命令,即計算類命令,只有addqsubqandqxorq,它們只對暫存器數進行操作,x86-64則允許對記憶體資料進行操作。這四個指令會設定條件碼
  3. halt指令導致處理器停止,並將狀態碼設定成HLT

指令由一個位元組進行編碼,高四位代表指令型別,低四位代表指令型別下細緻的操作型別,比如jXX這種跳轉指令,它有jmpjlejne等等。一些需要指定暫存器的指令需要一個位元組來指定使用哪個暫存器。同樣,高四位代表暫存器rA(第一個運算元),低四位代表暫存器rB(第二個運算元)。有些指令只需要一個暫存器,比如irmovq

,所以它的rB可以傳入,rA固定為Fdest代表64位的目的地址,因為有的命令需要讀取記憶體地址。

如上是一些同類操作指令的細緻分類。

如上是Y86-64中包含的暫存器識別符號,它們被編碼為0~E,F表示沒有暫存器運算元。

練習題4.1

A: 
  irmovq $15, %rbx
  30f3f000000000000000
B:
  rrmovq %rbx, %rcx
  2031
C:
  rmmovq %rcx, -3(%rbx)
  4013fdffffffffffffff
D:
  addq %rbx, %rcx
  6031
E:
  jmp loop
  700c01000000000000

練習題4.2

A: 
  irmovq $-4, %rbx
  rmmovq %rsi, $0x800(%rbx)

B:
    pushq %rsi
    call 0x20C    #假設過程叫proc
  proc:
    irmoveq $10, rbx
    ret

C:
  mrmovq 0x7(%rbp), %rsp
  nop
  f0               # 非法的一個指令
  popq %rcx

D:
  loop:
    subq %rcx, %rbx
    je loop             # je 0x400
    nop

E:
  xorq %rsi, %rds
  pushq f0            # 這裡錯了, pushq 要求一個暫存器和,所以應該是[0~e]f

Y86-64異常

對於Y86-64,當遇到錯誤時直接停止執行指令,在更完善的設計中,處理器會呼叫一個異常處理程式來處理某種型別的異常。

Y86-64程式

上圖給出了同樣一段程式使用x86-64和Y86-64的對比。

  1. 由於Y86-64的運算指令只允許操作暫存器,不能操作立即數,所以,先使用irmovq將用於更新long指標和long資料的立即數$8$1儲存到暫存器%r8%r9中。同樣。第8~9行中由於運算指令只能操作暫存器,所以先將記憶體資料移到了暫存器中。
  2. 由於沒有testq指令,這裡使用了andq指令。Y86-64中andq也會設定條件碼,同樣的subq也會,所有運算操作都會。所以我們無需在test塊中引入判斷命令。

下圖是完整的程式檔案,包含初始化,程式結束等操作。

習題4.4

如下是x86-64版本,閱讀該程式碼。

rsum:
        testq   %rsi, %rsi
        jle     .L3
        pushq   %rbx
        movq    (%rdi), %rbx
        subq    $1, %rsi
        addq    $8, %rdi
        call    rsum
        addq    %rbx, %rax
        popq    %rbx
        ret
.L3:
        movl    $0, %eax
        ret

我們可以斷定%rsi儲存的是count,如果count為0了,就跳到.L3,返回0。
然後,就是常規操作,儲存%rbx到棧幀
%rdi我們可以斷定是start,那麼(%rdi)就是*start,=
把start中起始元素存到%rbx
然後增加start,減少count
再次呼叫rsum
然後將結果與%rbx中之前的資料相加,放到返回值中
彈出棧資料,返回
翻譯成Y86-64

rsum:
        xor %rax, %rax
        andq   %rsi, %rsi
        jle     .L3
        pushq   %rbx
        mrmovq  (%rdi), %rbx
        irmovq $8, %r8
        irmovq $1, %r9
        subq    %r9, %rsi
        addq    %r8, %rdi
        call    rsum
        addq    %rbx, %rax
        popq    %rbx
        ret
.L3:
        ret

習題4.4/4.5

對我來說有點難啊

原始程式碼

sum:
  irmovq $8, %r8
  irmovq $1, %r9
  xorq  %rax, %rax
  andq  %rsi, %rsi
  jmp   test
loop:
  mrmovq (%rdi), %r10        # 這裡%r10就是當前要加的陣列元素
  addq %r10, %rax
  addq %r8, %rdi
  subq %r9, %rsi
test:
  jne   loop
  ret
4.4 使用條件跳轉實現absSum

absSum:
  irmovq $8, %r8
  irmovq $1, %r9
  xorq  %rax, %rax
  andq  %rsi, %rsi
  jmp   test
loop:
  mrmovq (%rdi), %r10
  xorq   %r11, %r11    # 將暫存器%r11置0
  subq   %r10, %r11    # 將%r10減去%r11放到%r11中
  jle    add           # 如果是正數,也就是-x<=0 x>0,就直接相加
  rrmovq %r11, %r10    # 如果是負數,將它的相反數儲存到%r10
add:
  addq %r10, %rax
  addq %r8, %rdi
  subq %r9, %rsi
test:
  jne   loop
  ret
4.5 使用條件控制實現absSum

absSum:
  irmovq $8, %r8
  irmovq $1, %r9
  xorq  %rax, %rax
  andq  %rsi, %rsi
  jmp   test
loop:
  mrmovq (%rdi), %r10 
  xorq   %r11, %r11    # 將暫存器%r11置0
  subq   %r10, %r11    # 將%r10減去%r11放到%r11中
  cmovg  %r11, %r10    # 如果 -x > 0,x = -x
  addq %r10, %rax
  addq %r8, %rdi
  subq %r9, %rsi
test:
  jne   loop
  ret

不管是哪種方式,思路就是subq取相反數,然後適當時候替換%r10。

未完...