step2 实验指导
我们按照上一节划分的编译器阶段,分阶段给出 step2 实验指导。
词法语法分析
如果你使用工具完成词法语法分析,修改你的规范以满足要求,剩下的交给工具即可。
语法规范已经给出,词法规范的变化也很简单,新增三个 token:-
、~
和 !
。
你的规范和我们的要求等价、能通过测试即可,不用完全一样。
语义检查无需修改。
如果你是手写分析,TODO
IR 生成
显然,我们要引入一类 IR 表示一元操作。 一元操作 IR 的含义是:弹出栈顶,对弹出的值做某个一元操作,再把操作的结果值压入栈顶。 换言之,就是直接对栈顶做某个操作。
指令 | 参数 | 含义 | IR 栈大小变化 |
---|---|---|---|
neg |
无参数 | 栈顶取负 | 不变 |
not |
同上 | 栈顶按位取反 | 不变 |
lnot |
同上 | 栈顶取逻辑非 | 不变 |
和 step1 一样,这一节所讲的领悟意思即可。 你不用照着实现。 例如你可以把三条指令变成一条
Unary(op)
,其中op
是"-"
、"~"
或"!"
。 你甚至也不必显式转成 IR。
和 step1 一样,采用 Visitor 模式遍历 AST 来生成 IR。除了 step1 的要求,step2 还要求你遍历 AST 时,
- 遇到一元表达式的时候,先生成子表达式的 IR,然后再根据操作类型生成一个
neg
或not
或lnot
所以,~!--3
会翻译成 push 3 ; neg ; neg ; lnot ; not
五条 IR 指令。
汇编生成
很简单,如下表。
IR | 汇编 |
---|---|
neg |
lw t1, 0(sp) ; neg t1, t1 ; sw t1, 0(sp) |
not |
…… |
lnot |
…… |
要知道每个操作生成什么样的汇编,可以参考 gcc 的输出。
例如我们想知道取负的汇编,那我们用 gcc 编译 int foo(int x) { return -x; }
,
结果如下(记得加 -O3
),我们就知道取负是 neg 目标寄存器, 操作数寄存器
。
foo:
neg a0,a0
ret
仿照上面,自己确定 not
和 lnot
的汇编。
任务
- 改进你的编译器,支持本节引入的新特性,通过相关测试。
- 实验报告中回答思考题。
总结
本节内容不多,也很简单。