Files
SchoolWork-LaTeX/计算机系统结构/平时作业/第二章作业.tex
423A35C7 5906ac1efc 重构目录层次
0-课程笔记
1-平时作业
2-实验报告
3-期末大作业
2024-09-02 18:32:58 +08:00

249 lines
10 KiB
TeX
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

\documentclass[全部作业]{subfiles}
\input{mysubpreamble}
\setcounter{chapter}{1}
\begin{document}
\chapter{指令:计算机的语言}
\begin{enumerate}
\questionandanswer[2.3]{
[ 5 ]<2.2,2.3>对于以下C语句,请编写相应的RISC-V汇编代码。假设变量f、g、h、i和j分别分配给寄存器x5、x6、x7、x28和x29。假设数组A和B的基地址分别在寄存器x10和x11中。
\mint{C}|B[8] = A[i-j];|
}{}
{\kaishu
% 这个minted环境放到自定义的命令里好像一直会报错
这里假设数组A和B中的元素都是64位的,即“双字”,即8个字节。
\begin{minted}{asm}
sub x30, x28, x29 // i-j,从A开始的双字偏移
slli x30, x30, 3 // (i-j)*8, 从A开始的字节偏移
add x30, x30, x10 // A[i-j]的地址
ld x31, 0(x30) // 加载A[i-j]
sd x31, 64(x11) // 放到B[8],8是双字偏移,转化成字节偏移就是64
\end{minted}
% 由于minted用了verbatim,而verbatim的文档中有这样一句话:
% You cannot use the verbatim environment inside user defined commands;
% 可以看到在自己定义的环境里无法使用minted了。
\begin{verification}
使用命令 \mintinline{shell}|riscv64-unknown-linux-gnu-gcc -S main.c -o main.s| 编译,左边是C代码,右边是汇编代码。
\begin{minipage}{0.5\linewidth}
\begin{minted}{C}
...
long long *A, *B, i,j;
B[8] = A[i - j];
...
\end{minted}
\end{minipage}
\begin{minipage}{0.5\linewidth}
\begin{minted}{asm}
...
ld a4,-24(s0) // 加载i
ld a5,-32(s0) // 加载j
sub a5,a4,a5
slli a5,a5,3
ld a4,-40(s0) // 加载A
add a4,a4,a5
ld a5,-48(s0) // 加载B
addi a5,a5,64
ld a4,0(a4)
sd a4,0(a5)
...
\end{minted}
\end{minipage}
比较后可以发现基本差不多,但是最后一步赋值时这里先用了addi偏移64位,再存储,为什么不是直接64(a5)一步存储到从a5偏移64个字节的位置呢?
\end{verification}
}
\questionandanswer[2.4]{
[ 10 ]<2.2,2.3>对于以下 RISC-V汇编指令,相应的C语句是什么?假设变量f、g、h、i和j分别分配给寄存器x5、x6、x7、x28和x29。假设数组A和B的基地址分别在寄存器x10和x11中。}{}
\begin{minted}[fontfamily=tt]{asm}
slli x30, x5, 3 // x30 = f*8
add x30, x10, x30 // x30 = &A[f]
slli x31, x6, 3 // x31 = g*8
add x31, x11, x31 // x31 = &B[g]
ld x5, 0(x30) // f = A[f]
addi x12, x30, 8 // x12 = (&f + 1) = (原始的f)&A[f + 1]
ld x30, 0(x12) // x30 = A[f + 1]
add x30, x30, x5 // x30 = f + A[f + 1] = (原始的f)A[f] + A[f + 1]
sd x30, 0(x31) // B[g] = A[f] + A[f + 1]
\end{minted}
{\kaishu
注释已写在上方,因此此汇编相应的C语句如下:
\mint{C}|B[g] = A[f] + A[f + 1];|
}
\questionandanswer[2.10]{
假设寄存器x5和x6分别保存值0x8000000000000000和0xD000000000000000。
}{}
\questionandanswer[2.10.1]{
[ 5 ]<2.4>以下汇编代码中x30的值是多少?
\mint{asm}|add x30, x5, x6|
}{
由于后面全是0,只需要考虑最高4位二进制。x5为$8_{16}$即$1000_{2}$,x6为$\mathrm{D}_{16}$即$1101_{2}$,要注意最高位为1代表负数,由于负数已按照补码方式表示,所以可以直接相加。这里两个数都是负数,相加后为$1\ 0101_{2}$,进位忽略,即$0101_{2}=5_{16}$,此时x30的值为
$$
0 \mathrm{x} 5000000000000000
$$
}
\questionandanswer[2.10.2]{
[ 5 ]<2.4> x30中的结果是否为预期结果,或者是否溢出?
}{
两个负数相加,结果的最高位为0,表示正数,不是预期结果,即产生了溢出。
}
\questionandanswer[2.10.3]{
[ 5 ]<2.4>对于上面指定的寄存器x5和x6的内容,以下汇编代码中x30的值是多少?
\mint{asm}|sub x30, x5, x6|
}{
还是只考虑最高4位二进制,最高位为1代表负数,直接相减不方便,可以先按照补码方式转换为相反数再相加,按照补码方式$1101_{2}$的相反数为$0011$,因此$1000_{2}-1101_{2}=1000_{2}+0011_{2}=1011_{2}$,即$\mathrm{B}_{16}$。因此x30的值为
$$
0 \mathrm{x} \mathrm{B}000000000000000
$$
}
\questionandanswer[2.10.4]{
[ 5 ]<2.4> x30中的结果是否为预期结果,或者是否溢出?
}{
两个负数相减,结果仍为负数,最高位是1,为预期结果,无溢出。
}
\questionandanswer[2.10.5]{
[ 5 ]<2.4>对于上面指定的寄存器x5和x6的内容,以下汇编代码中x30的值是多少?
\mint{asm}|add x30, x5, x6|
\vspace{-1.5em}
\mint{asm}|add x30, x30, x5|
}{
第一行就是2.10.1的指令,之后执行第二行,按照最高位来看就是$0101_{2}+1000_{2}=1101_{2}$,即$\mathrm{D}_{16}$,所以x30的值为
$$
0 \mathrm{xD} 000000000000000
$$
}
\questionandanswer[2.10.6]{
[ 5 ]<2.4> x30中的结果是否为预期结果,或者是否溢出?
}{
结果的最高位为1,虽然仍是负数但显然不是$\mathrm{x}5+\mathrm{x}6+\mathrm{x}5$的结果,所以不是预期结果,产生了溢出。
}
\questionandanswer[2.27]{
[ 5 ]<2.7>将以下循环转换为C代码。假设C语言级的整数i保存在寄存器x5中, x6保存名为result的C语言级的整数,x10保存整数MemArray的基址。
}{}
\begin{minted}{asm}
addi x6, x0, 0 // result = 0
addi x29, x0, 100 // x29 = 100
LOOP: ld x7, 0(x10) // x7 = *MemArray
add x5, x5, x7 // i = i + *MemArray
addi x10, x10, 8 // MemArray++
addi x6, x6, 1 // result++
blt x6, x29, LOOP // (result < 100)
\end{minted}
{\kaishu
注释已写在上方。根据题意和命名来看, \mintinline{C}{MemArray} 应该是整数数组而不是整数吧,不然总不能 \mintinline{C}{(&MemArray)++} 吧。这里假设整数都是64位的,所以 \mintinline{C}{MemArray++} 对应的汇编代码是增加8个字节,即 \mintinline{asm}{addi x10, x10, 8 }。
根据注释分析出的结果,可以得到C代码:
\begin{minted}{C}
for (int result = 0; result < 100; result++) {
i += *(MemArray++);
}
\end{minted}
为什么这里是 \mintinline{C}{result} 作为循环变量而 \mintinline{C}{i} 作为结果啊???但按照题意分析出来就是这样。
}
\questionandanswer[2.31]{
[ 20 ]<2.8>将函数f转换为RISC-V汇编语言。假设g的函数声明是 \mintinline{C}{int g(int a,int b)}。函数f的代码如下:
}{}
\begin{minted}{C}
int f(int a, int b, int c, int d) {
return g(g(a,b), c+d);
}
\end{minted}
{\kaishu
注意:
% \setlist[2]{label=\alph{enumii}.,listparindent=\parindent}
\begin{enumerate}[label=(\arabic{enumii})]
\item 调用函数时参数应该是从右往左计算的,但是需要保存的局部变量是从左到右保存的;
\item 栈指针应该是16字节(四字)对齐的;
\item 返回地址应该是调用者保存的。
\end{enumerate}
\begin{minted}{asm}
f: // 保存自己要用到的保存寄存器,好像没有
// 此时a,b,c,d 分别保存在 x10, x11, x12, x13 中
addw x5, x12, x13 // 计算c+d,4字节整数的要加w
addi sp, sp, -16 // 移动栈指针,栈指针应该是16字节对齐的?
sd x1, 8(sp) // 保存x1,返回地址
sd x5, 0(sp) // 保存x5,这里int类型4个字节,由于对齐产生了空位
// a和b在调用g(a,b)后不会用到,所以不用保存
// ***开始计算g(a,b)
// a和b已经在x10和x11中所以不需要移动
jal x1, g // 跳转到g
// g的返回值在x10中,正好是下一次调用的第一个参数
ld x11, 0(sp) // 恢复x5,直接恢复到x11上避免再移动
// x1不需要着急恢复,马上就是下一次调用了
// 只恢复了x5,栈指针要16字节对齐不能移动
// ***开始计算g(g(a,b), c+d)
// 第二个参数已从x5恢复
// 第一个参数已经在x10中
jal x1, g // 跳转到g
// g的返回值在x10中,不需要移动
ld x1, 0(sp) // 恢复x1
addi sp, sp, 16 // 恢复栈指针
// 恢复保存的保存寄存器,好像没有
jalr x0, x1 // 返回
\end{minted}
}
\vspace{2em}
\begin{verification}
使用命令 \mintinline{shell}|riscv64-unknown-linux-gnu-gcc -S main.c -o main.s| 编译。
\baselineskip=1.4em
\begin{minted}{asm}
f:
.LFB0:
.cfi_startproc
addi sp,sp,-32
.cfi_def_cfa_offset 32
sd ra,24(sp)
sd s0,16(sp)
.cfi_offset 1, -8
.cfi_offset 8, -16
addi s0,sp,32
.cfi_def_cfa 8, 0
mv a5,a0
mv a4,a3
sw a5,-20(s0)
mv a5,a1
sw a5,-24(s0)
mv a5,a2
sw a5,-28(s0)
mv a5,a4
sw a5,-32(s0)
lw a4,-24(s0)
lw a5,-20(s0)
mv a1,a4
mv a0,a5
call g
mv a5,a0
mv a3,a5
lw a5,-28(s0)
mv a4,a5
lw a5,-32(s0)
addw a5,a4,a5
sext.w a5,a5
mv a1,a5
mv a0,a3
call g
mv a5,a0
mv a0,a5
ld ra,24(sp)
.cfi_restore 1
ld s0,16(sp)
.cfi_restore 8
.cfi_def_cfa 2, 32
addi sp,sp,32
.cfi_def_cfa_offset 0
jr ra
.cfi_endproc
\end{minted}
\end{verification}
\end{enumerate}
\end{document}