汇编:为 copy 增加翻译规则

本节阅读量:

第一章的汇编生成器已经会做这些事:

1
2
3
4
给每条 IR 操作的 dst 分配栈槽
在函数开头一次性留出栈空间
把 add 翻译成 movq、addq、movq
把最终结果放进 %rax

第二章继续使用这套流程。汇编生成器只需要认识上一节新增的 copy 操作。

copy 怎样生成汇编

代码在:

1
code/02_let/src/compile/assembly.cpp

IR:

1
x0 = 40

表示把操作数 40 保存到内部名字 x0。它只需要两条指令:

1
2
movq $40, %rax
movq %rax, -8(%rbp)

对应的新增分支是:

1
2
3
4
case OpKind::copy:
    out_ += "    movq " + operand_text(op.lhs) + ", %rax\n";
    out_ += "    movq %rax, " + stack_slot(op.dst) + "\n";
    return;

这里仍然使用第一章已有的两个辅助函数:

1
2
operand_text(op.lhs)  把整数或 IR 名字写成汇编操作数
stack_slot(op.dst)    找到结果对应的栈槽

因此,如果 lhs 是另一个 IR 名字,copy 也不需要特殊处理。例如:

1
x1 = x0

可以生成:

1
2
movq -8(%rbp), %rax
movq %rax, -16(%rbp)

完整例子

源码:

1
(let x 40 (+ x 2))

IR:

1
2
3
x0 = 40
t.0 = x0 + 2
return t.0

其中只有第一条使用新的 copy 分支;第二条 add 和最后的返回都沿用第一章:

1
2
3
4
5
6
movq $40, %rax
movq %rax, -8(%rbp)
movq -8(%rbp), %rax
addq $2, %rax
movq %rax, -16(%rbp)
movq -16(%rbp), %rax

加入第一章已有的函数外壳后,就是完整输出。

Shadowing 也不需要新的汇编规则。lowerer 已经把两个同名变量变成 x0x1,原有的栈槽分配会自然地给它们不同的位置:

1
2
x0 -> -8(%rbp)
x1 -> -16(%rbp)

到这里,第二章新增的编译路径就接通了:

1
let 绑定 -> copy IR -> copy 汇编分支

手动验证

1
2
3
4
5
6
cd code/02_let
make
./mini compile examples/let.lang -o out.s
cc out.s -o out
./out
echo $?

退出码应该是:

1
42

2.5 IR:把用户名字变成内部名字

上一节

2.7 本章总结

下一节