视频加载失败

课程

1234 字
约 4 分钟

GitHub Copilot

计算机系统基础exercises/Homework/RainClass/chapter03·更新于 2026-09-15
short do_loop(short x, short y, short k){

    do{

        x *= (y % k) ;

        k--;

    } while ( (k > 0) && (y > k) );

    return x;

}
1   movw        8(%ebp), %bx

2   movw        12(%ebp), %si

3   movw        16(%ebp), %cx

4 .L1 :

5   movw        %si, %dx

6   movw        %dx, %ax

7   sarw        $15, %dx

8   idiv        %cx

9   imulw       %dx, %bx

10  decw        %cx

11  testw       %cx, %cx

12  jle         .L2

13  cmpw        %cx, %si

14  jg          .L1

15 .L2 :

16  movswl      %bx, %eax
1   movw        8(%ebp), %bx    //将ebp+8处的值(x)移动到寄存器bx中

2   movw        12(%ebp), %si   //将ebp+12处的值(y)移动到寄存器si中

3   movw        16(%ebp), %cx   //将ebp+16处的值(k)移动到寄存器cx中

4 .L1 :

5   movw        %si, %dx            //将寄存器si中的值(y)移动到寄存器dx中

6   movw        %dx, %ax            //将寄存器dx中的值(y)移动到寄存器ax中

7   sarw        $15, %dx            //将寄存器dx中的值算术右移15位,扩展符号位,(dx的位应该为符号位的拷贝或者设置为全0)

8   idiv        %cx                         //用ax中的值(y)除以cx中的值(k),商存入ax,余数存入dx
                                        (CSAPP P.134 rdx的位应该为符号位的拷贝(有符号运算)或者设置为全0(无符号))
9   imulw       %dx, %bx            //将寄存器bx中的值(x)乘以寄存器dx中的值(y % k),结果存入bx

10  decw        %cx                     //将寄存器cx中的值(k)减1

11  testw       %cx, %cx            //测试寄存器cx中的值是否为0

12  jle         .L2                     //如果cx中的值小于或等于0,则跳转到.L2

13  cmpw        %cx, %si        //比较寄存器cx中的值(k)和寄存器si中的值(y)

14  jg          .L1                     //如果寄存器si中的值(y)大于寄存器cx中的值(k),则跳转到.L1

15 .L2 :

16  movswl      %bx, %eax       //将寄存器bx中的值(x)扩展为32位并移动到寄存器eax中

(1) 给每条汇编指令添加注释,并说明每条指令执行后目的寄存器中存放的是什么信息:


1   movw        8(%ebp), %bx        // 将栈中第8个偏移量(即x的值)加载到 %bx 寄存器中。%bx = x
2   movw        12(%ebp), %si       // 将栈中第12个偏移量(即y的值)加载到 %si 寄存器中。%si = y
3   movw        16(%ebp), %cx       // 将栈中第16个偏移量(即k的值)加载到 %cx 寄存器中。%cx = k
4   .L1 :
5   movw        %si, %dx            // 将 %si (y的值) 移动到 %dx 寄存器中。%dx = y
6   movw        %dx, %ax            // 将 %dx 的值(即y)移动到 %ax 寄存器中。%ax = y
7   sarw        $15, %dx            // 对 %dx 执行算术右移 15 位,符号扩展。%dx 存储的是 y 的符号位(高位),此操作将其移到最低位。
8   idiv        %cx                 // 执行带符号除法,将 %ax(y的值)除以 %cx(k的值)。结果存储在 %ax(商)和 %dx(余数)中。
9   imulw       %dx, %bx            // 将 %dx(余数)与 %bx(x的值)相乘,结果存储在 %bx 中。%bx = x * (y % k)
10  decw        %cx                 // 将 %cx(k的值)减 1。%cx = k - 1
11  testw       %cx, %cx            // 测试 %cx 是否为零,检查循环是否需要继续。此指令将更新标志寄存器(ZF 标志)。
12  jle         .L2                 // 如果 %cx <= 0,跳转到标签 .L2,结束循环。
13  cmpw        %cx, %si            // 比较 %cx(k的值)与 %si(y的值)。用于判断 k 是否大于 y。
14  jg          .L1                 // 如果 %cx > %si,跳转到标签 .L1,继续循环。
15  .L2 :
16  movswl      %bx, %eax           // 将 %bx(x * (y % k))扩展为 32 位并存储到 %eax。返回值存储在 %eax 中。

(2) 被调用者保存寄存器与调用者保存寄存器:

  • 调用者保存寄存器:在这个过程中没有明确提到需要保存的调用者保存寄存器。因为函数本身的参数(x、y、k)通过栈传递,且没有复杂的寄存器重用情况。

  • 被调用者保存寄存器:由于函数使用了 %bx 和 %si 等寄存器,这些寄存器在函数内被修改,若函数的调用者(比如主函数)对这些寄存器有依赖,调用者就需要在函数调用前将它们保存到栈中,并在调用结束后恢复。这些寄存器是函数调用前需要保存的:

    • %bx(保存 x)
    • %si(保存 y)
    • %cx(保存 k)

    由于函数的实现没有明确进行寄存器保存和恢复操作,这可能是因为函数栈帧在调用过程中没有做显式的保存工作。通常,如果需要使用这些寄存器,调用者需要显式保存它们。

(3) 为什么第 7 行中的 DX 寄存器需要算术右移 15 位?

  • 第 7 行的

    sarw $15, %dx

    指令执行的是算术右移 15 位。这个操作的目的是将 y的符号位(即高位的符号位)移到低位,以便后续除法运算。具体来说:

    • idiv 指令用于带符号除法,它使用 AX 寄存器作为被除数,CX 作为除数,DX 存储余数。
    • 为了正确处理带符号数, DX 必须在除法前包含 AX 的符号扩展。算术右移 15 位将 DX 的高位符号扩展到低位,以确保除法时 DX 包含 y % k 的符号信息。

    这一步是为了保证 yk 为负数时,余数的符号处理是正确的,符合带符号整数的规则。

GitHub Copilot

(1) 给每条汇编指令添加注释,并说明每条指令执行后,目的寄存器中存放的是什么信息:

1   movw        8(%ebp), %bx   ; 将参数x的值(8(%ebp))移动到寄存器BX中

2   movw        12(%ebp), %si  ; 将参数y的值(12(%ebp))移动到寄存器SI中

3   movw        16(%ebp), %cx  ; 将参数k的值(16(%ebp))移动到寄存器CX中

4 .L1 :                         ; 循环开始标签

5   movw        %si, %dx       ; 将寄存器SI的值(y)移动到寄存器DX中

6   movw        %dx, %ax       ; 将寄存器DX的值(y)移动到寄存器AX中

7   sarw        $15, %dx       ; 将寄存器DX的值算术右移15位,扩展符号位

8   idiv        %cx            ; 用AX中的值(y)除以CX中的值(k),商存入AX,余数存入DX

9   imulw       %dx, %bx       ; 将寄存器BX中的值(x)乘以寄存器DX中的值(y % k),结果存入BX

10  decw        %cx            ; 将寄存器CX中的值(k)减1

11  testw       %cx, %cx       ; 测试寄存器CX中的值是否为0

12  jle         .L2            ; 如果CX中的值小于或等于0,则跳转到.L2

13  cmpw        %cx, %si       ; 比较寄存器CX中的值(k)和寄存器SI中的值(y)

14  jg          .L1            ; 如果寄存器SI中的值(y)大于寄存器CX中的值(k),则跳转到.L1

15 .L2 :                        ; 循环结束标签

16  movswl      %bx, %eax      ; 将寄存器BX中的值(x)扩展为32位并移动到寄存器EAX中

(2) 上述函数过程体中用到了哪些被调用者保存寄存器和哪些调用者保存寄存器?在该函数过程体前面的准备阶段哪些寄存器必须保存到栈中?

  • 被调用者保存寄存器(Callee-saved registers):BX, SI, DI, BP
  • 调用者保存寄存器(Caller-saved registers):AX, CX, DX

在该函数过程体前面的准备阶段,必须保存到栈中的寄存器是被调用者保存寄存器(BX, SI, DI, BP),因为这些寄存器的值在函数调用前后需要保持不变。

(3) 为什么第7行中的DX寄存器需要算术右移15位?

第7行中的 sarw $15, %dx 是为了对寄存器DX中的值进行符号扩展。算术右移15位将最高位(符号位)扩展到整个寄存器中,这样在进行有符号除法操作时,能够正确处理负数的情况。具体来说,这是为了确保在执行 idiv 指令时,DX:AX 组成的32位被除数能够正确表示原始的16位有符号数。

Profile Image of the Author
Sonder
好想要技术
这是公告标题
这只是一个公告
分类
标签
站点信息
构建平台
GitHub Actions
博客版本
Firefly v6.16.7
文章许可
CC BY-NC-SA 4.0
文章目录