CSAPP的Lab学习——Archlab(Architecture Lab)


前言

一个本硕双非的小菜鸡,备战24年秋招。刚刚看完CSAPP,真是一本神书啊!遂尝试将它的Lab实现,并记录期间心酸历程。 代码下载

官方网站:CSAPP官方网站 这道题下载完了记得不是完事了,还有一句话需要执行

cpp 复制代码
unix> cd sim
unix> make clean; make

如果执行make clean; make出现了报错,如: 可参考这位大佬的解决方案:林夕丶

以下是官方文档翻译: 在这个实验室中,您将学习流水线Y86-64处理器的设计和实现,优化它和基准程序以最大化性能。允许您对基准测试程序进行任何语义保留转换,或者对流水线处理器进行增强,或者两者都进行。当您完成了实验室的工作后,您将会对影响程序性能的代码和硬件之间的交互产生强烈的欣赏。 这个实验室被分为三个部分,每个部分都有自己的把手。 在A部分中,您将编写一些简单的Y86-64程序,并熟悉Y86-64工具。 在B部分中,您将使用一个新的指令来扩展SEQ模拟器。 这两个部分将为您为实验室的核心C部分做准备,在那里您将优化Y86-64基准程序和处理器设计。


一、A部分

您将在本部分的目录sim/misc中工作。 您的任务是编写和模拟以下三个Y86-64程序。这些程序的所需行为由示例中的examples.c函数定义。一定要在每个程序的开头把你的名字和ID放在注释中。您可以测试您的程序,首先用程序YAS组装它们,然后用指令集模拟器YIS运行它们。 在所有的Y86-64函数中,您应该遵循x86-64约定来传递函数参数、使用寄存器和使用堆栈。这包括保存和恢复您使用的任何被调用的保存寄存器。

人话:把sim/misc/examples.c里面的仨函数用Y86-64程序写出来 参考书中图4.8 ps:我感觉用处不大啊,就是手写Y86-64的汇编程序呗,难怪我看做的人也不是很多。(并不打算多费时间在这上,就参考其他大佬的了) 具体详见知乎大佬的解析:早睡晚起

sum .ys:迭代求和链表元素写一个Y86-64的程序和。

即对链表中的元素进行迭代求和。您的程序应该由一些设置堆栈结构、调用一个函数、然后停止的代码组成。在这种情况下,函数应该是Y86-64代码(sum list),在功能上等同于C sum list和图1中的列表函数。使用以下三元素列表测试程序:

cpp 复制代码
# Sample linked list
.align 8
ele1:
.quad 0x00a
.quad ele2
ele2:
.quad 0x0b0
.quad ele3
ele3:
.quad 0xc00
.quad 0

1 /* linked list element */
2 typedef struct ELE {
3 long val;
4 struct ELE *next;
5 } *list_ptr;
6
7 /* sum_list - Sum the elements of a linked list */
8 long sum_list(list_ptr ls)
9 {
10 long val = 0;
11 while (ls) {
12 val += ls->val;
13 ls = ls->next;
14 }
15 return val;
16 }
17
18 /* rsum_list - Recursive version of sum_list */
19 long rsum_list(list_ptr ls)
20 {
21 if (!ls)
22 return 0;
23 else {
24 long val = ls->val;
25 long rest = rsum_list(ls->next);
26 return val + rest;
27 }
28 }
29
30 /* copy_block - Copy src to dest and return xor checksum of src */
31 long copy_block(long *src, long *dest, long len)
32 {
33 long result = 0;
34 while (len > 0) {
35 long val = *src++;
36 *dest++ = val;
37 result ˆ= val;
38 len--;
39 }
40 return result;
41 }
/*Y86-64解决方案函数的C个版本。参见sim/misc/examples.c*/

代码:

cpp 复制代码
# sum_list - Sum the elements of a linked list
# author: Deconx

# Execution begins at address 0
        .pos 0
        irmovq stack, %rsp      # Set up stack pointer
        call main               # Execute main program
        halt                    # Terminate program

# Sample linked list
        .align 8
ele1:
        .quad 0x00a
        .quad ele2
ele2:
        .quad 0x0b0
        .quad ele3
ele3:
        .quad 0xc00
        .quad 0

main:
        irmovq ele1,%rdi
        call sum_list
        ret

# long sum_list(list_ptr ls)
# start in %rdi
sum_list:
        irmovq $0, %rax
        jmp test

loop:
        mrmovq (%rdi), %rsi
        addq %rsi, %rax
        mrmovq 8(%rdi), %rdi

test:
        andq %rdi, %rdi
        jne loop
        ret

# Stack starts here and grows to lower addresses
        .pos 0x200
stack:

注意,应在stack下方空一行,否则汇编器会报错。 然后按操作手册来,新建一个sum.ys文件,并把代码放入。 执行 ./yas sum.ys ./yis sum.yo

看到%rax是cba就是成功了

rsum .递归求和链表元素

编写一个Y86-64程序 rsum.ys,递归地和链表的元素。这个代码应该类似于sum.ys的代码,除了它应该使用一个函数rsum list递归地求和一个数字列表的列表,如C函数rsum list所示 在图1中列出。使用用于测试列表相同的三元素列表测试程序list.ys.

代码:

cpp 复制代码
# /* rsum_list - Recursive version of sum_list */
# author: Deconx

# Execution begins at address 0
        .pos 0
        irmovq stack, %rsp      # Set up stack pointer
        call main               # Execute main program
        halt                    # Terminate program

# Sample linked list
        .align 8
ele1:
        .quad 0x00a
        .quad ele2
ele2:
        .quad 0x0b0
        .quad ele3
ele3:
        .quad 0xc00
        .quad 0

main:
        irmovq ele1,%rdi
        call rsum_list
        ret

# long sum_list(list_ptr ls)
# start in %rdi
rsum_list:
        andq %rdi, %rdi
        je return               # if(!ls)
        mrmovq (%rdi), %rbx     # val = ls->val
        mrmovq 8(%rdi), %rdi    # ls = ls->next
        pushq %rbx
        call rsum_list          # rsum_list(ls->next)
        popq %rbx
        addq %rbx, %rax         # val + rest
        ret
return:
        irmovq $0, %rax
        ret


# Stack starts here and grows to lower addresses
        .pos 0x200
stack:

注意,应在stack下方空一行,否则汇编器会报错。 然后按操作手册来,新建一个rsum.ys文件,并把代码放入。 执行 ./yas rsum.ys ./yis rsum.yo

看到%rax是cba就是成功了

复制将源块复制到目标块

编写一个程序(copy.ys),将单词块从内存的一个部分复制到另一个内存(非重叠区域),计算所有复制单词的校验和(Xor)。 您的程序应该由设置堆栈框架、调用函数副本的代码组成阻止,然后停止。该函数应该在功能上等同于C函数copy block,如图1所示。使用以下三个元素的源块和目标块测试程序:

cpp 复制代码
.align 8
# Source block
src:
.quad 0x00a
.quad 0x0b0
.quad 0xc00
# Destination block
dest:
.quad 0x111
.quad 0x222
.quad 0x333

代码:

cpp 复制代码
/* copy_block - Copy src to dest and return xor checksum of src */
# author: Deconx

# Execution begins at address 0
        .pos 0
        irmovq stack, %rsp      # Set up stack pointer
        call main               # Execute main program
        halt                    # Terminate program

# Sample
        .align 8
# Source block
src:
        .quad 0x00a
        .quad 0x0b0
        .quad 0xc00

# Destination block
dest:
        .quad 0x111
        .quad 0x222
        .quad 0x333

main:
        irmovq src, %rdi        # src
        irmovq dest, %rsi       # dest
        irmovq $3, %rdx         # len
        call copy_block
        ret

# long copy_block(long *src, long *dest, long len)
# src in %rdi
# dest in %rsi
# len in %rdx
copy_block:
        irmovq $8, %r8
        irmovq $1, %r9
        irmovq $0, %rax
        andq %rdx, %rdx
        jmp test
loop:
        mrmovq (%rdi), %r10     # val = *src1
        addq %r8, %rdi          # src++
        rmmovq %r10, (%rsi)     # *dest = val
        addq %r8, %rsi          # dest++
        xorq %r10, %rax         # result ^= val
        subq %r9, %rdx          # len--.  Set CC
test:
        jne loop                # Stop when 0
        ret

# Stack starts here and grows to lower addresses
        .pos 0x200
stack:

这道题几乎与4-7完全相同

注意,应在stack下方空一行,否则汇编器会报错。 然后按操作手册来,新建一个copy_block.ys文件,并把代码放入。 执行 ./yas copy_block.ys ./yis copy_block.yo

看到%rax是cba就是成功了

二、B部分

您将在这部分的目录sim/seq中工作。 B部分中的任务是扩展SEQ处理器以支持iaddq,在家庭作业问题4.51和4.52中描述。要添加此说明, 您将修改文件的seq-full.hcl,它实现了CS: APP3e教科书中描述的SEQ版本。此外,它还包含了您 的解决方案所需要的一些常量的声明。 HCL文件必须以包含以下信息的标题注释开头:

  1. 你的名字和身份证。
  2. 对iaddq指令所需的计算方法的描述。使用CS: APP3e文本中图4.18中的irmovq和OPq的描述作为指南。 构建和测试您的解决方案 一旦你完成了修改序列的全部。hcl文件,然后你将需要基于这个hcl文件建立一个新的SEQ模拟器(ssim)的实例,然后测试它: 建立一个新的模拟器。你可以使用make来构建一个新的SEQ模拟器: unix> make VERSION=full 这构建了一个使用您在seq-full中指定的控制逻辑的ssim版本。要保存键入操作,您可以在Makefile中给与VERSION=full。 在一个简单的Y86-64程序上测试你的解决方案。对于您的初始测试,我们建议在TTY模式下运行简单的程序,如asumi.yo(测试iaddq),并将结果与ISA模拟进行比较: unix> ./ssim -t ../y86-code/asumi.yo 如果ISA测试失败,那么您应该通过以GUI模式单步进模拟器来调试实现: unix> ./ssim -g ../y86-code/asumi.yo 使用基准测试程序重新测试您的解决方案。一旦你的模拟器能够正确地执行小程序,那么你就可以自动在Y86-64基准测试程序上测试它。../y86-code: unix> (cd ../y86-code; make testssim) 这将在基准测试程序上运行ssim,并通过将生成的处理器状态与来自高级ISA模拟的状态进行比较来检查其正确性。请注意,这些程序都没有测试所添加的指令。您只是在确保您的解决方案没有为原始指令注入错误。查看文件../y86-code/README文件了解更多细节。 执行回归测试。一旦您能够正确地执行基准测试程序,那么您就应该在../ptest中运行广泛的回归测试集。要测试除iaddq之外的所有内容并leave: unix> (cd ../ptest; make SIM=../seq/ssim) 要测试iaddq的实现: unix> (cd ../ptest; make SIM=../seq/ssim TFLAGS=-i) 有关SEQ模拟器的更多信息,请参阅关于Y86-64处理器模拟器(simguide.pdf)的分发CS: APP3e指南。
cpp 复制代码
1 /*
2 * ncopy - copy src to dst, returning number of positive ints
3 * contained in src array.
4 */
5 word_t ncopy(word_t *src, word_t *dst, word_t len)
6 {
7 word_t count = 0;
8 word_t val;
9
10 while (len > 0) {
11 val = *src++;
12 *dst++ = val;
13 if (val > 0)
14 count++;
15 len--;
16 }
17 return count;
18 }
/*图2:ncopy函数的C版本。请参阅sim/pipe/ncopy.c*/

这块就不越俎代庖了,大家看大佬的就好:大佬答案 测试方法

cpp 复制代码
make VERSION=full
cd seq/
./ssim -t ../y86-code/asumi.yoY86-64 Processor: seq-full.hcl
cd ../ptest; make SIM=../seq/ssim TFLAGS=-i

三、C部分

您将在本部分的目录sim/pipe中工作。 图2中的ncopy函数将一个len-元素整数数组src复制到一个不重叠的dst中,返回src中包含的正整数的计数。图3显示了ncopy的基线Y86-64版本。文件pipe-full.hcl包含一个针对PIPE的HCL代码的副本,以及一个常量值IIADDQ的声明。 您在C部分中的任务是修改ncopy.ys和pipe-full.hcl的目标是制作ncopy.ys。跑得越快越好。 你将提交两个文件:pipe-full.hcl和ncopy.ys。每个文件都应该以一个包含以下信息的标题注释开头: 你的名字和身份证。 对您的代码的高级描述。在每种情况下,描述您如何以及为什么修改代码。

您可以自由地做任何您想要的修改,并有以下限制:

  1. 你的ncopy.ys函数必须适用于任意大小的数组。您可能会试图通过简单地编码64个复制指令来硬连接64个元素数组的解决方案,但这将是一个坏主意,因为我们将根据其在任意数组上的性能对您的解决方案进行分级。
  2. 你的ncopy.ys函数必须与YIS一起正确运行。通过正确地复制,我们的意思是它必须正确地复制src块并返回(以%rax表示)正确的正整数数。
  3. ncopy文件的组装版本的长度不能超过1000字节。您可以使用提供的脚本check-len.pl检查嵌入了ncopy函数的任何程序的长度:unix> ./check-len.pl < ncopy.yo
  4. 你的pipe-full.hcl实现必须通过中的回归测试:../y86-code和 ../ptest。(没有测试iaddq的-i标志)。

除此之外,如果你认为iaddq指令会有帮助,你就可以自由地执行iaddq指令。您可以对ncopy进行任何保留语义的转换。它的功能,如重新排序指令,用单个指令替换指令组,删除一些指令,以及添加其他指令。您可能会发现在CS: APP3e的第5.8节中阅读关于循环展开的信息很有用。

cpp 复制代码
1 ##################################################################
2 # ncopy.ys - Copy a src block of len words to dst.
3 # Return the number of positive words (>0) contained in src.
4 #
5 # Include your name and ID here.
6 #
7 # Describe how and why you modified the baseline code.
8 #
9 ##################################################################
10 # Do not modify this portion
11 # Function prologue.
12 # %rdi = src, %rsi = dst, %rdx = len
13 ncopy:
14
15 ##################################################################
16 # You can modify this portion
17 # Loop header
18 xorq %rax,%rax # count = 0;
19 andq %rdx,%rdx # len <= 0?
20 jle Done # if so, goto Done:
21
22 Loop: mrmovq (%rdi), %r10 # read val from src...
23 rmmovq %r10, (%rsi) # ...and store it to dst
24 andq %r10, %r10 # val <= 0?
25 jle Npos # if so, goto Npos:
26 irmovq $1, %r10
27 addq %r10, %rax # count++
28 Npos: irmovq $1, %r10
29 subq %r10, %rdx # len--
30 irmovq $8, %r10
31 addq %r10, %rdi # src++
32 addq %r10, %rsi # dst++
33 andq %rdx,%rdx # len > 0?
34 jg Loop # if so, goto Loop:
35 ##################################################################
36 # Do not modify the following section of code
37 # Function epilogue.
38 Done:
39 ret
40 ##################################################################
41 # Keep the following label at the end of your function
42 End:
/*ncopy函数的基线Y86-64版本。请参阅sim/pipe/ncopy.ys*/

解答:

实现iaddq指令

pipe-full.hcl改动部分,可参考PartB

cpp 复制代码
# Is instruction valid?
bool instr_valid = f_icode in 
	{ INOP, IHALT, IRRMOVQ, IIRMOVQ, IRMMOVQ, IMRMOVQ,
	  IOPQ, IJXX, ICALL, IRET, IPUSHQ, IPOPQ, IIADDQ};
cpp 复制代码
# Does fetched instruction require a regid byte?
bool need_regids =
	f_icode in { IRRMOVQ, IOPQ, IPUSHQ, IPOPQ, 
		     IIRMOVQ, IRMMOVQ, IMRMOVQ, IIADDQ};

# Does fetched instruction require a constant word?
bool need_valC =
	f_icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IJXX, ICALL, IIADDQ};
cpp 复制代码
## What register should be used as the B source?
word d_srcB = [
	D_icode in { IOPQ, IRMMOVQ, IMRMOVQ , IIADDQ} : D_rB;
	D_icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP;
	1 : RNONE;  # Don't need register
];

## What register should be used as the E destination?
word d_dstE = [
	D_icode in { IRRMOVQ, IIRMOVQ, IOPQ, IIADDQ} : D_rB;
	D_icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP;
	1 : RNONE;  # Don't write any register
cpp 复制代码
## Select input A to ALU
word aluA = [
	E_icode in { IRRMOVQ, IOPQ } : E_valA;
	E_icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IIADDQ} : E_valC;
	E_icode in { ICALL, IPUSHQ } : -8;
	E_icode in { IRET, IPOPQ } : 8;
	# Other instructions don't need ALU
];

## Select input B to ALU
word aluB = [
	E_icode in { IRMMOVQ, IMRMOVQ, IOPQ, ICALL, 
		     IPUSHQ, IRET, IPOPQ, IIADDQ} : E_valB;
	E_icode in { IRRMOVQ, IIRMOVQ } : 0;
	# Other instructions don't need ALU
];
cpp 复制代码
## Should the condition codes be updated?
bool set_cc = E_icode == (E_icode == IOPQ || E_icode == IIADDQ)&&
	# State changes only during normal operation
	!m_stat in { SADR, SINS, SHLT } && !W_stat in { SADR, SINS, SHLT };

在sim/pipe输入以下命令测试

cpp 复制代码
./psim -t ../y86-code/asumi.yo
(cd ../ptest;make SIM=../pipe/psim TFLAGS=-i)
(cd ../ptest;make SIM=../pipe/psim)

测试成功 通过这两个命令测试CPE

cpp 复制代码
./correctness.pl
./benchmark.pl

得: 出了点问题,改完iaddrq指令后,CPE全都变成小于1了。。。不知道为啥? 网上也没找到原因,重新替换也不好使。


总结

这一章比较乱,我的重心也没放在这里。最近事太多心理上也十分的烦躁、焦虑。希望以后可以恢复过来,然后我再次跟这道题抗争到底!我能行的!

相关推荐
phoenix098144 分钟前
Linux入门DAY29
linux·运维
入秋1 小时前
Linux服务器安装部署 Nginx、Redis、PostgreSQL、Docker
linux·前端
程序员鱼皮1 小时前
这套 Java 监控系统太香了!我连夜给项目加上了
java·前端·ai·程序员·开发·软件开发
Mr. Cao code2 小时前
使用Tomcat Clustering和Redis Session Manager实现Session共享
java·linux·运维·redis·缓存·tomcat
zcz16071278212 小时前
Linux 网络命令大全
linux·运维·网络
the sun342 小时前
Reactor设计模式及其在epoll中的应用
linux·运维·服务器·c++
喜欢你,还有大家2 小时前
Linux笔记7——shell编程基础-1
linux·运维·笔记
运维成长记2 小时前
Top 100 Linux Interview Questions and Answers
linux·运维·服务器
人工智能训练师2 小时前
openEuler系统中如何将docker安装在指定目录
linux·运维·服务器·人工智能·ubuntu
百里晴鸢3 小时前
别再混淆!Linux硬链接与软链接的5大关键区别
linux·操作系统