实验内容
实验提供了一个Y86-64处理器,要求我们编写汇编、添加处理器指令、优化汇编程序。
注意在make时如果出现 tk.h: No such file or directory 这样的报错,需要进Makefile根据指示注释掉几行代码。
PartA
第一部分要求我们根据提供的C程序,编写对应汇编程序。在misc目录下make得到的yas可以把汇编程序转换为相应的机器码,yis可以模拟机器码在Y86-64处理器上的执行。
/* linked list element */
typedef struct ELE {
long val;
struct ELE *next;
} *list_ptr;
Y86-64上的汇编指令比较少,在书上都有提供,编写下来比较简单,别忘了整个程序前后的声明即可。
/* sum_list - Sum the elements of a linked list */
long sum_list(list_ptr ls)
{
long val = 0;
while (ls) {
val += ls->val;
ls = ls->next;
}
return val;
}
# Execution begins at address 0
.pos 0
irmovq stack, %rsp
call main
halt
.align 8
# Source block
src:
.quad 0x00a
.quad 0x0b0
.quad 0xc00
# Destination block
dest:
.quad 0x111
.quad 0x222
.quad 0x333
main:
irmovq $3, %rdx
irmovq dest, %rsi
irmovq src, %rdi
call copy_block
ret
copy_block:
irmovq $8, %r8
irmovq $0xffffffffffffffff, %r9
xorq %rax, %rax
loop:
andq %rdx, %rdx
je end
mrmovq (%rdi), %r10
rmmovq %r10, (%rsi)
xorq %r10, %rax
addq %r8, %rdi
addq %r8, %rsi
addq %r9, %rdx
jmp loop
end:
ret
# Stack starts here
.pos 0x200
stack:
/* rsum_list - Recursive version of sum_list */
long rsum_list(list_ptr ls)
{
if (!ls)
return 0;
else {
long val = ls->val;
long rest = rsum_list(ls->next);
return val + rest;
}
}
main:
irmovq ele1, %rdi
call rsum_list
ret
rsum_list:
irmovq $8, %r8
xorq %rax, %rax
andq %rdi, %rdi
je end
mrmovq (%rdi), %r9
addq %r8, %rdi
mrmovq (%rdi), %rdi
pushq %r9
call rsum_list
popq %r9
addq %r9, %rax
end:
ret
/* copy_block - Copy src to dest and return xor checksum of src */
long copy_block(long *src, long *dest, long len)
{
long result = 0;
while (len > 0) {
long val = *src++;
*dest++ = val;
result ^= val;
len--;
}
return result;
}
main:
irmovq ele1, %rdi
call sum_list
ret
sum_list:
irmovq $8, %r8
xorq %rax, %rax
jmp test
loop:
mrmovq (%rdi), %r9
addq %r9, %rax
addq %r8, %rdi #指针移动到next的位置
mrmovq (%rdi), %rdi
jmp test
test:
andq %rdi, %rdi #测试%rdi是否为0
jne loop
ret
运行程序后将寄存器的值与文档里的值进行对比即可。
PartB
这一部分需要修改seq里的硬件描述文件seq-full.c,添加一条iaddq指令,实现立即数和整数的加法运算。需要添加的部分有:
#有效指令
bool instr_valid = icode in
{ INOP, IHALT, IRRMOVQ, IIRMOVQ, IRMMOVQ, IMRMOVQ,
IOPQ, IJXX, ICALL, IRET, IPUSHQ, IPOPQ, IIADDQ };
#需要寄存器
bool need_regids =
icode in { IRRMOVQ, IOPQ, IPUSHQ, IPOPQ,
IIRMOVQ, IRMMOVQ, IMRMOVQ, IIADDQ };
#有立即数valC
bool need_valC =
icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IJXX, ICALL, IIADDQ };
#srcB是寄存器值rB
word srcB = [
icode in { IOPQ, IRMMOVQ, IMRMOVQ, IIADDQ } : rB;
icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP;
1 : RNONE; # Don't need register
];
#寄存器的写入位置为rB
word dstE = [
icode in { IRRMOVQ } && Cnd : rB;
icode in { IIRMOVQ, IOPQ, IIADDQ} : rB;
icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP;
1 : RNONE; # Don't write any register
];
#ALU的计算符号位Add
word aluA = [
icode in { IRRMOVQ, IOPQ } : valA;
icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IIADDQ } : valC;
icode in { ICALL, IPUSHQ } : -8;
icode in { IRET, IPOPQ } : 8;
# Other instructions don't need ALU
];
#ALU的B值位valB,即为寄存器rB
word aluB = [
icode in { IRMMOVQ, IMRMOVQ, IOPQ, ICALL,
IPUSHQ, IRET, IPOPQ, IIADDQ } : valB;
icode in { IRRMOVQ, IIRMOVQ } : 0;
# Other instructions don't need ALU
];
#计算后需要设置条件码
bool set_cc = icode in { IOPQ, IIADDQ };
随后根据文档里的要求进行程序测试即可。
PartC
这一部分给定了一个ncopy.ys的汇编代码,运行在一个流水线处理器上。要求我们修改ncopy.ys,使其性能尽可能的好,CPE越低,分数越高。
首先需要为这个处理器添加iaddq指令,大致内容与PartB相同,后面需要用到iaddq设置条件码来进行判断。
然后就是对ncopy.ys进行修改,未修改的ncopy.ys内容如下:
xorq %rax,%rax # count = 0;
andq %rdx,%rdx # len <= 0?
jle Done # if so, goto Done:
Loop:
mrmovq (%rdi), %r10 # read val from src...
rmmovq %r10, (%rsi) # ...and store it to dst
andq %r10, %r10 # val <= 0?
jle Npos # if so, goto Npos:
irmovq $1, %r10
addq %r10, %rax # count++
Npos:
irmovq $1, %r10
subq %r10, %rdx # len--
irmovq $8, %r10
addq %r10, %rdi # src++
addq %r10, %rsi # dst++
andq %rdx,%rdx # len > 0?
jg Loop # if so, goto Loop:
我做的时候用了两种优化方法:
第一种为错开一部分指令,避免因为load/use才产生的气泡。
第二种为循环展开,由于ncopy最多只能用1000字节,所以循环展开的路数不能太大。这里我的展开方式为12路——4路——2路——1路。即先每12个元素并为一个循环,如果元素个数不满12,则每4个元素并为一个循环......
##################################################################
# You can modify this portion
#对%rbx进行iaddq测试是否大于等于12
rrmovq %rdx, %rbx
L12:
iaddq $-12, %rbx
jl Start4
#12路展开,错开部分指令避免Load/Use
l12_1:
mrmovq (%rdi), %r8
mrmovq $0x8(%rdi), %r9
rmmovq %r8, (%rsi)
andq %r8, %r8
jle l12_2
iaddq $0x1, %rax
l12_2:
mrmovq $0x10(%rdi), %r10
rmmovq %r9, $0x8(%rsi)
andq %r9, %r9
jle l12_3
iaddq $0x1, %rax
l12_3:
mrmovq $0x18(%rdi), %r8
andq %r10, %r10
rmmovq %r10, $0x10(%rsi)
jle l12_4
iaddq $0x1, %rax
l12_4:
mrmovq $0x20(%rdi), %r9
andq %r8, %r8
rmmovq %r8, $0x18(%rsi)
jle l12_5
iaddq $0x1, %rax
l12_5:
mrmovq $0x28(%rdi), %r10
andq %r9, %r9
rmmovq %r9, $0x20(%rsi)
jle l12_6
iaddq $0x1, %rax
l12_6:
mrmovq $0x30(%rdi), %r8
andq %r10, %r10
rmmovq %r10, $0x28(%rsi)
jle l12_7
iaddq $0x1, %rax
l12_7:
mrmovq $0x38(%rdi), %r9
andq %r8, %r8
rmmovq %r8, $0x30(%rsi)
jle l12_8
iaddq $0x1, %rax
l12_8:
mrmovq $0x40(%rdi), %r10
andq %r9, %r9
rmmovq %r9, $0x38(%rsi)
jle l12_9
iaddq $0x1, %rax
l12_9:
mrmovq $0x48(%rdi), %r8
andq %r10, %r10
rmmovq %r10, $0x40(%rsi)
jle l12_10
iaddq $0x1, %rax
l12_10:
mrmovq $0x50(%rdi), %r9
andq %r8, %r8
rmmovq %r8, $0x48(%rsi)
jle l12_11
iaddq $0x1, %rax
l12_11:
mrmovq $0x58(%rdi), %r10
andq %r9, %r9
rmmovq %r9, $0x50(%rsi)
jle l12_12
iaddq $0x1, %rax
l12_12:
andq %r10, %r10
rmmovq %r10, $0x58(%rsi)
jle l12_13
iaddq $0x1, %rax
l12_13:
iaddq $-12, %rdx
iaddq $0x60, %rdi
iaddq $0x60, %rsi
jmp L12
Start4:
rrmovq %rdx, %rbx
L4:
iaddq $-4, %rbx
jl Start2
l4_1:
mrmovq (%rdi), %r8
mrmovq $0x8(%rdi), %r9
andq %r8, %r8
rmmovq %r8, (%rsi)
jle l4_2
iaddq $0x1, %rax
l4_2:
mrmovq $0x10(%rdi), %r10
andq %r9, %r9
rmmovq %r9, $0x8(%rsi)
jle l4_3
iaddq $0x1, %rax
l4_3:
mrmovq $0x18(%rdi), %r8
andq %r10, %r10
rmmovq %r10, $0x10(%rsi)
jle l4_4
iaddq $0x1, %rax
l4_4:
andq %r8, %r8
rmmovq %r8, $0x18(%rsi)
jle l4_5
iaddq $0x1, %rax
l4_5:
iaddq $-4, %rdx
iaddq $0x20, %rdi
iaddq $0x20, %rsi
jmp L4
Start2:
rrmovq %rdx, %rbx
L2:
iaddq $-2, %rbx
jl Start1
l2_1:
mrmovq (%rdi), %r8
mrmovq $0x8(%rdi), %r9
andq %r8, %r8
rmmovq %r8, (%rsi)
jle l2_2
iaddq $0x1, %rax
l2_2:
andq %r9, %r9
rmmovq %r9, $0x8(%rsi)
jle l2_3
iaddq $0x1, %rax
l2_3:
iaddq $-2, %rdx
iaddq $0x10, %rdi
iaddq $0x10, %rsi
jmp L2
#从2路转到1路说明最多只剩一个元素待copy了
Start1:
mrmovq (%rdi), %r8
andq %rdx, %rdx
jle Done
rmmovq %r8, (%rsi)
andq %r8, %r8
jle Done
iaddq $0x1, %rax
##################################################################
上面有些指令旁路即可解决相关,但是还是被我错开了(在写完了才发现,不想改回来了),不过不影响性能。
./correctness.pl测试正确性:
./benchmark.pl测试性能:
后面琢磨了一下,想要优化得更好,可能还需要改变一下循环展开的路数和层级。