249 lines
10 KiB
TeX
249 lines
10 KiB
TeX
|
\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}
|