Skip to content

Commit e4e1650

Browse files
committed
修正 MIPS 教程中示例的错误
1 parent 12fb20f commit e4e1650

File tree

3 files changed

+19
-8
lines changed

3 files changed

+19
-8
lines changed

docs/tutorials/08-target-code-generation.md

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# 目标代码生成
22

3-
中间代码生成后,我们就来到了编译器设计的最后阶段了。目标代码生成通常以编译器此前生成的**中间代码****符号表**及其他相关信息作为输入,输出与源程序**语义等价**的目标程序代码。 代码生成模块需要面向某一个特定的目标体系结构生成目标代码,这种目标体系结构可以是 X86、MIPS、ARM 等,而且由于我们采用三端设计,因此我们可以把目标代码生成理解成是**对中间代码的翻译**。对于不同体系结构,中间代码翻译成目标代码的处理方式也不同。下面我们给出一个中间代码为四元式,目标代码为MIPS指令结构的指导方案
3+
中间代码生成后,我们就来到了编译器设计的最后阶段了。目标代码生成通常以编译器此前生成的**中间代码****符号表**及其他相关信息作为输入,输出与源程序**语义等价**的目标程序代码。 代码生成模块需要面向某一个特定的目标体系结构生成目标代码,这种目标体系结构可以是 X86、MIPS、ARM 等,而且由于我们采用三端设计,因此我们可以把目标代码生成理解成是**对中间代码的翻译**。对于不同体系结构,中间代码翻译成目标代码的处理方式也不同。下面我们给出一个中间代码为四元式,目标代码为 MIPS 指令结构的指导方案
44

55
## 一、MIPS 快速回顾
66

@@ -20,7 +20,7 @@
2020

2121
- `$zero`:始终为 0,可以用来减少常数 0 的使用。
2222
- `$at`(assembly temporary):汇编器保留寄存器,由汇编器在特定场景(通常是加载大常数)自动生成。
23-
- `$v0 - $v1`:作为函数返回值,一般返回值只使用 `$v0`,当返回值超过 32 位时会同时使用 `$v1`
23+
- `$v0 - $v1`:作为函数返回值,一般返回值只使用 `$v0`,当返回值超过 32 位时需要同时使用 `$v1`
2424
- `$a0 - $a3`:函数调用参数,前 4 个参数通常保存在这几寄存器中,更多的参数则是压栈处理。
2525
- `$t0 - $t7, $t8 - $t9`:临时寄存器,用于基本块内的变量,发生函数调用时不必保存。
2626
- `$s0 - $s7`(saved):全局寄存器,这些寄存器用于跨基本块的变量,往往需要在发生函数调用时进行保存。
@@ -64,6 +64,8 @@ int main() {
6464
```
6565

6666
> MIPS `.data` 段的数据是依次存储的,因此细心的你可能会觉得这里有潜在的字节不对齐的问题。但是不用担心,在 `.data` 段汇编器是会自动完成字节对齐的操作的,不过之后在 `.text` 段使用 1 字节存储 `char` 类型变量时就需要注意变量的分配的位置了。
67+
>
68+
> 同时,由于 `printf` 中的格式化字符串并没有具体对应变量,因此需要单独为它们分配全局变量名称。
6769
6870
通过生成全局数据段,我们可以在程序运行之前为所有的全局变量分配内存空间,并在必要时将它们初始化为指定的值。这样,在程序的执行过程中,全局变量的值就可以被保存在内存中,并随时被读取和修改。
6971

@@ -75,13 +77,15 @@ int main() {
7577

7678
当我们调用函数时,都需要为其在栈上分配一段内存,称为栈帧。其中包含了函数中定义的局部变量,溢出的变量,保存的寄存器以及传出的参数。在 MIPS 中,栈帧的基本结构如下。
7779

78-
![stack-frame](imgs/chapter07-3/stack-frame.png)
80+
![stack-frame](imgs/chapter07-3/stack-frame.svg)
7981

8082
> 尽管 MIPS 中使用 `$fp``$sp` 共同维护栈帧,但实际上只使用 `$sp` 也是足够的。关于栈帧更详细的内容可以参考 [MIPS Calling Convention](https://courses.cs.washington.edu/courses/cse410/09sp/examples/MIPSCallingConventionsSummary.pdf)
8183
8284
在栈帧管理中,比较关键的是参数的处理。不同函数间彼此不可见,因此为了使得被调用的函数能够正确获取参数,调用者需要将参数保存在紧挨着被调用者栈帧的位置,即当前的栈顶。这里需要注意,尽管我们可以将前 4 个参数通过 `$a0 - $a3` 传递,我们仍需要在栈中为其预留位置(不用保存值)。
8385

84-
我们的实验中,存在 `int``char` 两种类型,其中 `int` 占 4 字节,而 `char` 只占 1 字节。因此这里就涉及到了字节对齐的问题。MIPS 要求了 `lw/sw` 指令的地址必须四字节对齐,因此在为 `int` 分配空间时有可能需要添加 padding 来保证 4 字节对齐。此外,在 MIPS 中还要求栈帧 8 字节对齐,因此必要时可以在 local variable 之后添加 4 字节的 padding。
86+
> 这样的参数传递是 C 语言的调用规范,由于大家实验中的代码不涉及与 C 语言标准库的链接,因此自行设计参数传递方式也是可以的。
87+
88+
我们的实验中,存在 `int``char` 两种类型,其中 `int` 占 4 字节,而 `char` 只占 1 字节。因此这里就涉及到了字节对齐的问题。MIPS 要求了 `lw/sw` 指令的地址必须四字节对齐,因此在为 `int` 分配空间时有可能需要添加 padding 来保证 4 字节对齐。此外,规范的 MIPS 还要求栈帧 8 字节对齐,因此必要时可以在 local variable 之后添加 4 字节的 padding,不过在实验中可以不用处理栈帧的字节对齐问题。
8589

8690
### (2)寄存器分配
8791

@@ -158,11 +162,11 @@ sw $t3, ($t1)
158162
- 函数序言:申请所需的栈空间,即更新 `$sp`(和 `$fp`) 寄存器,保存使用到的全局寄存器(如果需要),以及自己的 `$ra` 寄存器。
159163
- 函数尾声:释放栈空间,恢复全局寄存器(如果需要)以及 `$ra` 寄存器。
160164

161-
> 由于任意全局寄存器都可能被调用者使用,因此为了不破坏调用现场,被调用者需要保存其使用到的全局寄存器,并在结束时进行恢复
165+
> 由于任意全局寄存器都可能被调用者使用,因此为了不破坏调用现场,被调用者需要保存其使用到的全局寄存器,并在结束时恢复其原本的值
162166
163167
对于函数调用者,主要有以下几个步骤。
164168

165-
1. 参数传递。在调用函数前,调用者需要将函数参数压入栈中。对于 MIPS,可以将前四个参数通过 `$a0 - $a3` 四个寄存器传递,但仍需要为其在栈中预留位置。参数的位置通常由参数编号和当前的 `$sp` 决定,从而被调用者可以在不知道调用者栈帧的情况下获取参数。
169+
1. 参数传递。在调用函数前,调用者需要将函数参数压入栈中。对于 MIPS,可以将前四个参数通过 `$a0 - $a3` 四个寄存器传递,但仍需要为其在栈中预留位置。参数的位置由参数编号和当前的 `$sp` 决定,从而被调用者可以在不知道调用者栈帧的情况下获取参数。
166170
2. 保存现场(可选),也可以将这一任务交给被调用者的函数序言。
167171
3. 函数跳转。通过 `jal``jalr` 指令跳转到被调用的函数,函数返回值被保存在 `$v0` 寄存器中。
168172
4. 恢复现场(可选),也可以将这一任务交给被调用者的函数尾声。
@@ -195,7 +199,10 @@ f:
195199
196200
sw $a0, 8($sp) # 如需要,保存参数至预留的栈空间
197201
198-
lw $t0, 8($sp) # 返回值
202+
lw $t0, 8($sp) # b = a
203+
sw $t0, 4($sp)
204+
205+
lw $t0, 4($sp) # 准备返回值
199206
move $v0, $t0
200207
201208
lw $ra, 0($sp) # 恢复自己的 $ra
@@ -212,7 +219,7 @@ main:
212219
jal f # 调用函数
213220
move $t0, $v0 # 获取返回值(此处未使用该返回值)
214221
215-
lw $ra, 4($sp) # 恢复自己的 $ra
222+
lw $ra, 4($sp) # 恢复自己的 $ra
216223
addu $sp, $sp, 8 # 恢复栈帧
217224
218225
li $v0, 10 # 10 号系统调用,结束程序
-79.6 KB
Binary file not shown.

docs/tutorials/imgs/chapter07-3/stack-frame.svg

Lines changed: 4 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)