第二章作业
家庭作业2.78:写出函数mul5div8的代码,对于整数参数x,计算5 * x/8,但是要遵循位级整数编码规则。你的代码计算5 * x也会产生溢出。
分析:
这相当于计算((x<<2) + x) >> 3,根据题目要求,无需考虑5 * x时产生溢出,但要考虑 x 为负数时的舍入。
假设 x 的位模式为[b(w-1), b(w-2), ... , b(0)],那么我们需要计算:
[b(w-1),b(w-2),b(w-3), ... ,b(0), 0, 0]+ [b(w-1),b(w-2),...,b(2), b(1), b(0)],相加后的结果整体右移3位。
右移三位时,b(1)+0和b(0)+0对结果无影响,b(0)+b(2)如果产生进位,则结果再加一。
int mul5div8(int x)
{
int b0 = x&1, b2 = (x>>2)&1;
int ans = ((x<<2) + x) >> 3;
ans += (b0&b2);
return ans;
}
运行结果:
溢出:
第三章作业
家庭作业3.56
考虑下面的汇编代码:
movl 8(%ebp),%esi
movl 12(%ebp),%ebx
movl $1431655765,%edi
movl $-2147483648,%edx
.L2:
movl %edx,%eax
andl %esi,%eax
xorl %eax,%edi
movl %ebx,%ecx
shrl %cl,%edx
testl %edx,%edx
jne .L2
movl %edi,%eax
以上代码是以下整体形式的C代码编译产生的:
int loop(int x,int n)
{
int result = ;
int mask;
for(mask= ;mask ;mask=)
{
result^ = ;
}
return result;
}
这个函数的结果是在寄存器%eax中返回的。检查循环之前、之中、之后的汇编代码,形成一个寄存器和程序变量之间一致的映射。
A.哪个寄存器保存着程序值x、n、result和mask?
B.result和mask的初始值是什么?
C.mask的测试条件是什么?
D.mask是如何被修改的?
E.result是如何被修改的?
F.填写这段代码中所有缺失部分
分析:
-
x at %ebp+8,n at %ebp+12,返回值为result,且存在%eax中,由此得出
寄存器 变量 %esi x %ebx n %edi result %edx mask
以及result和mask的初始值result = 1431655765,mask = -2147483648;
-
检查循环之中的代码可得出,每次循环的运算结果为result^ = mask & x;
-
由跳转语句jne .L2得知循环的判断条件为是否等于0,结合测试 条件testl %edx,%edx,edx中存放的变量为mask,所以循环条件为mask!= 0
-
movl %ebx,%ecx将n放入%ecx寄存器中,而shrl %cl,%edx语句取%ecx的低八位。说明n是一个不大的数字,每次循环之后将mask左移n位。
解答:
int loop(int x,int n)
{
int result = 1431655765;
int mask;
for(mask = -2147483648;mask!= 0 ;mask = mask>>n)
{
result^ = mask & x;
}
return result;
}
第四章作业
家庭作业4.43
IA32pushl指令被描述成要减少栈指针,然后将寄存器存储在栈指针的位置。因此,如果我们有一条指令形如对于某个寄存器REG,pushl REG,它等价于下面的代码序列:
subl $4,%esp
movl REG,(%esp)
A.这段代码序列正确的描述了指令pushl %esp的行为吗?请解释。
B.你该如何改写这段代码序列,使得它能够像对REG是其他寄存器时一样,正确地描述REG是%esp的情况?
分析:
A.%esp和其余寄存器的不同之处就在于,%esp是栈指针,始终指向栈顶,pushl %esp 是将 esp 当前的内容入栈。
B.如果想要描述%esp的情况, REG 是 esp,那么代码是先减去了 esp,然后将减了 4 以后的 REG 移入了 esp。
解答:
A.没有正确执行 pushl %esp,pushl %esp 是将 esp 当前的内容入栈。
B.修改:
movl REG, %eax
subl $4, %esp
movl %eax, (%esp)
第六章作业
家庭作业6.35
考虑下面的矩阵转置函数:
typedef int array[4][4];
void transpose2(array dst,array src)
{
int i,j;
for(i=0;i<4;i++)
{
for(j=0;j<4;j++)
{
dst[i][j]=src[j][i];
}
}
}
假设这段代码运行在一台具有如下属性的机器上:
- sizeof(int)==4。
- 数组src从地址0开始,而数组dst从地址64开始(十进制)。
- 只有一个L1数据高速缓存,它是直接映射、直写、写分配的,块大小为16字节。
- 这个高速缓存总共有32个数据字节,初始为空。
- 对src和dst数组的访问分别是读和写不命中的唯一来源。
对于每个row和col,指明对src[row][col]和dst[row][col]的访问是命中(h)还是不命中(m)。
分析:
对于写分配的高速缓存,每次写不命中时,需要读取数据到高速缓存中。
该高速缓存只有 2 个组,src[0] src[2] 对应组 0,src[1] src[3] 对应组 1。
同理,dst[0] dst[2] 对应组 0,dst[1] dst[3] 对应组 1。
解答:
家庭作业6.36
对于一个总大小为128数据字节的高速缓存,重复习题6.35。
分析:
缓存能完全容得下两个数组,所以只会出现冷不命中。