家庭作业

第二章作业

家庭作业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;
}

家庭作业_第1张图片

运行结果:
家庭作业_第2张图片

溢出:

家庭作业_第3张图片

家庭作业_第4张图片

第三章作业

家庭作业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。

解答:

家庭作业_第5张图片

家庭作业6.36

对于一个总大小为128数据字节的高速缓存,重复习题6.35。

分析:

缓存能完全容得下两个数组,所以只会出现冷不命中。

解答:

家庭作业_第6张图片

你可能感兴趣的:(家庭作业)