CS:APP archlab 記錄

Introduction

在本lab中,您將學習流水線Y86處理器的設計和實現,同時優化它和基準程序以最大化性能。 允許您保留對基準程序的任何語義轉換,或者對流水線處理器進行增強,或者兩者。 完成lab後,您將對影響程序性能的代碼和硬件之間的交互的理解有很大的提升。 lab分爲三個part,每個part都有自己的練習。 在part A中,您將編寫一些簡單的Y86程序並熟悉Y86工具。 在part B中,您將使用兩個新指令擴展SEQ仿真器。 這兩part將爲您準備Part C(lab的核心)做好準備,在part C中,您將優化Y86基準程序和處理器設計。

 

Handout Instructions

1.首先將文件archlab-handout.tar複製到計劃在其中進行工作的(受保護)目錄。

2.然後輸入命令:tar xvf archlab-handout.tar。 這將導致以下文件解壓縮到目錄中:README,Makefile,sim.tar,archlab.ps,archlab.pdf和simguide.pdf。

3.接下來,輸入命令tar xvf sim.tar。 這將創建目錄sim,其中包含您的Y86工具的個人副本。 您將在此目錄中進行所有工作。

4.最後,轉到sim目錄並構建Y86工具:

 

Part A

在這個part,您將在目錄sim / misc中工作。

您的任務是編寫和模擬以下三個Y86程序。 這些程序的所需行爲由examples.c中的示例C函數定義。 確保在每個程序的開頭都將您的姓名和ID放在註釋中。 您可以通過以下方式測試您的程序:首先使用程序YAS對其進行彙編,然後使用指令集模擬器YIS運行它們。

在所有Y86函數中,您都應遵循IA32約定,以瞭解棧幀的結構和寄存器使用說明,包括保存和恢復您使用的任何被調用這保護寄存器。

sum.ys: Iteratively sum linked list elements

編寫一個Y86程序sum.ys,以迭代方式求和鏈表的元素。 您的程序應包含一些代碼,這些代碼可以設置棧結構,調用一個函數然後停止。 在這種情況下,該函數應該是功能上與圖1中的e C sum list等效的函數(sum_list)的Y86代碼。請使用以下三元素列表來測試程序:

代碼如下:

    .pos 0
    # 初始化esp和ebp指針
    irmovl stack, %esp
    irmovl stack, %ebp
    call main
    halt

# 聲明鏈表
    .align 4
ele1:
    .long 0x00a
    .long ele2
ele2:
    .long 0x0b0
    .long ele3
ele3:
    .long 0xc00
    .long 0

main:
    # 調用的棧幀操作
    pushl %ebp
    rrmovl %esp, %ebp

    irmovl ele1, %ebx  # 參數ls = ele1
    pushl %ebx         # 參數壓棧
    call sum_list
    popl %ebx

    # 返回的棧幀操作
    rrmovl %ebp, %esp
    popl %ebp
    ret

sum_list:
    # 調用的棧幀操作                                                                                              
    pushl %ebp
    rrmovl %esp, %ebp
    xorl %eax, %eax      # val = 0
    irmovl $4, %ebx      # 設置常數4
    mrmovl 8(%ebp), %ecx # 獲取參數
    jmp test:

loop:
    mrmovl (%ecx), %edx  # ls->val
    addl %edx, %eax      # val += ls->val
    addl %ebx, %ecx      # &(ls->next)
    mrmovl (%ecx), %ecx   # ls = ls->next

test:
    andl %ecx, %ecx      # 設置條件碼爲參數ls
    jne loop

    # 返回的棧幀操作
    rrmovl %ebp, %esp
    popl %ebp
    ret

    .pos 0x200
stack:

運行結果如下。

 

rsum.ys: Recursively sum linked list elements

編寫一個Y86程序rsum.ys,該程序遞歸求和鏈表的元素。 此代碼應與sum.ys中的代碼相似,不同之處在於它應使用函數rsum list遞歸求和一個數字列表,如圖1中的C函數rsum list所示。 使用同樣的三元素列表測試代碼。

    .pos 0
    # 初始化esp和ebp指針
    irmovl stack, %esp
    irmovl stack, %ebp
    call main
    halt

# 聲明鏈表
    .align 4
ele1:
    .long 0x00a
    .long ele2
ele2:
    .long 0x0b0
    .long ele3
ele3:
    .long 0xc00
    .long 0

main:
    # 調用的棧幀操作
    pushl %ebp
    rrmovl %esp, %ebp

    irmovl ele1, %edi  # 參數ls = ele1
    pushl %edi         # 參數壓棧
    call rsum_list
    popl %edi

    # 返回的棧幀操作
    rrmovl %ebp, %esp
    popl %ebp
    ret

rsum_list:
    # 調用的棧幀操作
    pushl %ebp
    rrmovl %esp, %ebp

    mrmovl 8(%ebp), %ebx  # 獲取參數
    andl %ebx, %ebx       # 設置條件碼爲參數ls
    jne recursive         # !ls則遞歸
    irmovl $0, %eax

    # 返回的棧幀操作
    rrmovl %ebp, %esp
    popl %ebp
    ret

recursive:
    mrmovl (%ebx), %ecx # val = ls->val
    irmovl $4, %edx     # 常數4
    addl %edx, %ebx     # &(ls->next)
    mrmovl (%ebx), %ebx # ls->next

    pushl %ecx          # 保存val
    pushl %ebx          # 參數壓棧
    call rsum_list
    popl %ebx
    popl %ecx           # 恢復val的值
    addl %ecx, %eax     # return val + rest

    # 返回的棧幀操作
    rrmovl %ebp, %esp
    popl %ebp
    ret

    .pos 0x200
stack:

運行結果如下。

 

 

copy.ys: Copy a source block to a destination block

編寫一個程序(copy.ys),將一個word塊從內存的一個部分複製到內存的另一個(不重疊區域)區域,計算所有複製word的校驗和(Xor)。

您的程序應由設置棧幀,調用功能複製塊然後停止的代碼組成。 該函數在功能上應等效於圖1所示的C函數複製塊。使用以下三元素的源塊和目標塊測試程序:

代碼如下:

    .pos 0
    # 初始化esp和ebp指針
    irmovl stack, %esp
    irmovl stack, %ebp
    call main
    halt

#聲明測試數組
.align 4
src:
    .long 0x00a
    .long 0x0b0
    .long 0xc00

dest:
    .long 0x111
    .long 0x222
    .long 0x333

main:
    # 調用的棧幀操作
    pushl %ebp
    rrmovl %esp, %ebp

    irmovl src, %ebx  # 參數src
    irmovl dest, %ecx # 參數dest
    irmovl $3, %edx   # 參數len
    pushl %edx
    pushl %ecx                                                                                        
    pushl %ebx        # 參數壓棧
    call copy_block
    popl %ebx
    popl %ecx
    popl %edx

    # 返回的棧幀操作
    rrmovl %ebp, %esp
    popl %ebp
    ret

copy_block:
    # 調用的棧幀操作
    pushl %ebp
    rrmovl %esp, %ebp

    xorl %eax, %eax       # result = 0
    mrmovl 8(%ebp), %ebx  # src參數
    mrmovl 12(%ebp), %ecx # dest參數
    mrmovl 16(%ebp), %edx # len參數
    jmp test:

loop:
    mrmovl (%ebx), %edi   # val = *src
    irmovl $4, %esi       # 常數4
    addl %esi, %ebx       # src++
    rmmovl %edi, (%ecx)   # *dest = val
    addl %esi, %ecx       # dest++
    xorl %edi, %eax       # result ^= val
    irmovl $1, %esi       # 常數1
    subl %esi, %edx       # len--

test:
    andl %edx, %edx       # 設置條件碼爲len
    jne loop

    # 返回的棧幀操作
    rrmovl %ebp, %esp
    popl %ebp
    ret

    .pos 0x200
stack:

結果如下:

 

Part B

在這個part,您將在目錄sim / seq中進行工作。

在part B,您的任務是擴展SEQ處理器以支持兩個新指令:iaddl(在作業問題4.47和4.49中描述)和leave(在作業問題4.48和4.50中描述)。 爲了添加這些說明,您將修改文件seq-full.hcl,該文件實現CS:APP2e教科書中描述的SEQ版本。 此外,它包含解決方案所需的一些常量的聲明。

您的HCL文件必須以包含以下信息的標題註釋開頭:

•您的name和id。

•iaddl指令所需的計算的描述。 使用CS:APP2e文本中圖4.18中對irmovl和OP1的描述作爲參考。

•leave指令所需的計算的描述。 使用CS:APP2e文本中圖4.20中popl的描述作爲參考。

先是iaddl和leave的描述註釋:

#     id:victorika
#     name:wwq
#iaddl:
#   fetch:      icode:ifunc <-- M1[PC]
#               rA:rB <-- M1[PC + 1]
#               valC <-- M4[PC + 2]
#               valP <-- PC + 6
#   decode:     valB <-- R[rB]
#   execute:    valE <-- valC + valB
#               Set CC
#   memory: 
#   write back: R[rB] <- valE
#   PC update:  PC <-- valP
#
#leave:
#   fetch:      icode:ifunc <-- M1[PC]
#               valP <-- PC + 1
#   decode:     valA <-- R[%ebp]
#   execute:    valE <-- 4 + valA
#   memory:     valM <-- M4[valA]
#   write back: R[%esp] <-- valE
#               R[%ebp] <-- valM
#   PC update:  PC <-- valP

然後添加指令,修改的代碼如下:

bool instr_valid = icode in  
    { INOP, IHALT, IRRMOVL, IIRMOVL, IRMMOVL, IMRMOVL,
           IOPL, IJXX, ICALL, IRET, IPUSHL, IPOPL, IIADDL, ILEAVE};

bool need_regids =
    icode in { IRRMOVL, IOPL, IPUSHL, IPOPL, 
             IIRMOVL, IRMMOVL, IMRMOVL, IIADDL};

bool need_valC =
    icode in { IIRMOVL, IRMMOVL, IMRMOVL, IJXX, ICALL, IIADDL };

int srcA = [
    icode in { IRRMOVL, IRMMOVL, IOPL, IPUSHL  } : rA;
    icode in { IPOPL, IRET } : RESP;
    icode in { ILEAVE } : REBP;
    1 : RNONE; # Don't need register
];

int srcB = [
    icode in { IOPL, IRMMOVL, IMRMOVL, IIADDL  } : rB;
    icode in { IPUSHL, IPOPL, ICALL, IRET } : RESP;
    1 : RNONE;  # Don't need register
];

int dstE = [
    icode in { IRRMOVL } && Cnd : rB;
    icode in { IIRMOVL, IOPL, IIADDL} : rB;
    icode in { IPUSHL, IPOPL, ICALL, IRET, ILEAVE } : RESP;
    1 : RNONE;  # Don't write any register
];

int dstM = [
    icode in { IMRMOVL, IPOPL } : rA;
    icode in { ILEAVE } : REBP;
    1 : RNONE;  # Don't write any register
];

int aluA = [
    icode in { IRRMOVL, IOPL } : valA;
    icode in { IIRMOVL, IRMMOVL, IMRMOVL, IIADDL } : valC;
    icode in { ICALL, IPUSHL } : -4;
    icode in { IRET, IPOPL, ILEAVE } : 4;
    # Other instructions don't need ALU
];

int aluB = [
    icode in { IRMMOVL, IMRMOVL, IOPL, ICALL,
              IPUSHL, IRET, IPOPL, IIADDL } : valB;
    icode in { ILEAVE } : valA;
    icode in { IRRMOVL, IIRMOVL } : 0;
    # Other instructions don't need ALU
];

bool set_cc = icode in { IOPL, IIADDL };

bool mem_read = icode in { IMRMOVL, IPOPL, IRET, ILEAVE };

int mem_addr = [
    icode in { IRMMOVL, IPUSHL, ICALL, IMRMOVL } : valE;
    icode in { IPOPL, IRET, ILEAVE } : valA;
    # Other instructions don't need address
];

這一步基本是不會錯的。。所以要確保你的流程是對的,然後再寫代碼。。

構建和測試您的解決方案

修改完seq-full.hcl文件後,您將需要基於此HCL文件構建SEQ模擬器(ssim)的新實例,然後對其進行測試:

•構建新的模擬器。 您可以使用make來構建新的SEQ模擬器:

這將構建一個ssim版本,該版本使用您在seq-full.hcl中指定的控制邏輯。 要保存輸入,可以在Makefile中分配VERSION = full。

•在一個簡單的Y86程序上測試您的解決方案。 對於您的初始測試,我們建議在TTY模式下運行簡單的程序,例如asumi.yo(測試iaddl)和asuml.yo(測試leave),並將結果與ISA仿真進行比較:

如果ISA測試失敗,則應在GUI模式下通過單步執行模擬器來調試實現。

•使用基準程序重新測試您的解決方案。 一旦模擬器能夠正確執行小程序,您就可以在../y86代碼中的Y86基準程序上對其進行自動測試:

這將在基準程序上運行ssim,並通過將結果處理器狀態與高級ISA仿真中的狀態進行比較來檢查正確性。 請注意,這些程序均未測試添加的指令。 您只需確保您的解決方案沒有爲原始說明注入錯誤。 有關更多詳細信息,請參見文件../y86-code/README文件。

•執行迴歸測試。 一旦可以正確執行基準測試程序,則應在../ptest中運行大量的迴歸測試。 要測試除iaddl之外的所有內容並離開:

要測試iaddl的實現,請執行以下操作:

有關SEQ模擬器的更多信息,請參閱《 Y86處理器模擬器的CS:APP2e指南》(simguide.pdf)。

測試結果如下:

 

Part C

這個part您將在sim/pipe目錄中工作。

圖2中的ncopy函數將len個元素的整數數組src複製到一個不重疊的dst中,返回src中包含的正整數的個數。 圖3顯示了ncopy的基準Y86版本。 文件pipe-full.hcl包含PIPE的HCL代碼的副本,以及常量值IIADDL的聲明。

 

partC中的任務是修改ncopy.ys和pipe-full.hcl,以使ncopy.ys儘可能快地運行。

您將提交兩個文件:pipe-full.hcl和ncopy.ys。 每個文件應以帶有以下信息的標題註釋開頭:

•您的name和id。
•您的代碼的高級描述。 在每種情況下,請描述修改代碼的方式和原因。

 

Coding Rules

您可以自由地進行任何修改,但有以下限制:

•您的ncopy.ys函數必須適用於任意數組大小。 您可能會想通過簡單地編寫64個複製指令來爲64個元素的數組硬連接解決方案,但這將是一個壞主意,因爲我們將根據其在任意數組上的性能來對您的解決方案進行評分。

•您的ncopy.ys函數必須與YIS一起正確運行。 正確地說,我們的意思是它必須正確複製src塊並返回(以%eax表示)正確數量的正整數。

•ncopy文件的彙編版本不得超過1000個字節。 您可以使用提供的腳本check-len.pl檢查帶有ncopy函數嵌入的任何程序的長度:

•您的pipe-full.hclimplementation必須通過../y86代碼和../ptest中的迴歸測試(不需要用於測試iaddl並離開的-il標誌)。

除此之外,如果您認爲有幫助,可以自由實施iaddl指令。 您可以進行保留到ncopy.ys函數的任何語義轉換,例如重新排序指令,用單個指令替換指令組,刪除某些指令以及添加其他指令。 您可能會發現在CS:APP2e的5.8節中閱讀有關循環展開的信息很有用。

 

Building and Running Your Solution

爲了測試您的解決方案,您將需要構建一個調用ncopy函數的驅動程序。 我們爲您提供了gen-driver.pl程序,該程序爲任意大小的輸入數組生成驅動程序。 例如,輸入:

將構造以下兩個有用的驅動程序:

•sdriver.yo:一個小型驅動程序,用於在具有4個元素的小型陣列上測試ncopy函數。 如果您的解決方案是正確的,則在複製src數組後,該程序將在寄存器%eax中以2的值停止運行。

•ldriver.yo:大型驅動程序,用於在具有63個元素的較大陣列上測試ncopy函數。 如果您的解決方案是正確的,則該程序將在複製src數組後以%eax寄存器中的值31(0x1f)暫停。

每次修改ncopy.ys程序時,都可以通過鍵入以下內容來重建驅動程序:

每次修改pipe-full.hcl文件時,您都可以通過鍵入以下內容來重建模擬器:

如果要重建模擬器和驅動程序,請鍵入:

要在小型4元素陣列上以GUI模式測試您的解決方案,請鍵入:

要在更大的63元素數組上測試解決方案,請輸入:

一旦模擬器在這兩個塊長度上正確運行了您的ncopy.ys版本,您將需要執行以下附加測試:

•在ISA模擬器上測試驅動程序文件。 確保您的ncopy.ys函數可與YIS一起正常使用:

•使用ISA模擬器在一系列塊長度上測試代碼。 Perl腳本correctness.pl生成驅動程序文件,其塊長度從0到某個限制(默認爲65),再加上一些更大的大小。 它模擬它們(默認情況下爲YIS),並檢查結果。 它生成一個報告,顯示每個塊長度的狀態:

該腳本生成測試程序,其結果計數從一次運行到另一次運行隨機變化,因此它提供了比標準驅動程序更嚴格的測試。

如果對於某個長度K得出的結果不正確,則可以爲該長度的驅動程序文件生成一個包含檢查代碼的驅動程序文件,並且結果隨機變化:

該程序將以具有以下值的寄存器%eax結尾:

•在基準程序上測試管道模擬器。 一旦模擬器能夠正確執行sdriver.ys和ldriver.ys,則應使用../y86代碼的Y86基準程序對其進行測試:

這將在基準程序上運行psim並將結果與YIS進行比較。

•使用廣泛的迴歸測試來測試管道模擬器。 一旦可以正確執行基準測試程序,則應使用../ptest中的迴歸測試對其進行檢查。 例如,如果您的解決方案實現了iaddl指令,則

•使用管道模擬器在一系列塊長度上測試代碼。 最後,您可以在管道模擬器上運行與之前使用ISA模擬器進行的代碼測試相同的代碼

優化點:

1.首先用上一個part的iaddl指令加到這個part裏,然後把其中的算數運算操作替換成iaddl,同時因爲iaddl有set CC這個步驟,所以對應的andl指令也是不需要的。

2.循環展開,選擇循環展開的因子是4,剛好給我卡了過去。

下面直接給彙編代碼:

    # Loop header
    xorl %eax,%eax      # count = 0;
    iaddl $-3, %edx     # len- = 3
    jle next_start      # if so, goto Done:
Loop1:  
    mrmovl (%ebx), %esi # read val from src...
    mrmovl 4(%ebx), %edi # read val from src + 1
    rmmovl %esi, (%ecx) # ...and store it to dst
    rmmovl %edi, 4(%ecx) # store it to dst + 1
test1:
    andl %esi, %esi     # *src <= 0 ?
    jle test2
    iaddl $1, %eax      # count++
test2:
    andl %edi, %edi     # *(src + 1) <= 0 ?
    jle test3
    iaddl $1, %eax      # count++
test3:
    mrmovl 8(%ebx), %esi # read val from src + 2
    mrmovl 12(%ebx), %edi # read val from src + 3
    rmmovl %esi, 8(%ecx) # store it to dst + 2
    rmmovl %edi, 12(%ecx) # store it to dst + 3
    andl %esi, %esi     # *(src + 2) <= 0?
    jle test4
    iaddl $1, %eax      # count++
test4:
    andl %edi, %edi     # *(src + 3) <= 0?
    jle Npos
    iaddl $1, %eax      # count++
Npos:
    iaddl $16, %ebx     # src += 4
    iaddl $16, %ecx     # dst += 4
    iaddl $-4, %edx     # len -= 4
    andl %edx,%edx      # len > 0?
    jg Loop1            # if so, goto Loop:
next_start:
    iaddl $3, %edx      # len += 3
    jle Done
loop2:
    mrmovl (%ebx), %esi # read val from src
    rmmovl %esi, (%ecx) # store it to dst
    andl %esi, %esi     # *src <= 0 ?
    jle test5
    iaddl $1, %eax      # count++
test5:
    iaddl $4, %ebx      # src += 1
    iaddl $4, %ecx      # dst += 1
    iaddl $-1, %edx     # len -= 1
    jg loop2

長度檢查。

答案檢查。

性能檢查。

本來我因子用了2,結果是10多一點,就試着擴大一下因子,就過去了。

 

Hints

•根據設計,驅動程序和驅動程序都足夠小,可以在GUI模式下進行調試。 我們發現最容易在GUI模式下進行調試,建議您使用它。

•如果您在Unix服務器上以GUI模式運行,請確保已初始化DISPLAY環境變量:

•對於某些X服務器,當您在GUI模式下運行psim或ssim時,“程序代碼”窗口以關閉的圖標形式開始運行。 只需單擊圖標即可展開窗口。

•對於某些基於Microsoft Windows的X服務器,“內存內容”窗口不會自動調整其大小。 您需要手動調整窗口大小。

•如果您要求psim和ssim模擬器執行不是有效的Y86目標文件的文件,則會因段錯誤而終止。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章