SchoolWork-LaTeX/计算机系统结构/平时作业/第二章作业.tex

249 lines
10 KiB
TeX
Raw Permalink Normal View History

2024-09-02 17:47:53 +08:00
\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+d4字节整数的要加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}