CodeGeneratork类
用这个类来翻译中间代码,首先需要一个入口函数generate,其调用顺序如下:
生成汇编代码需要提前制定一些规则:
- 为了方便,所有变量或者临时变量都保存在内存中,这里可以优化.
- 对内存的操作限定使用mov指令
- 限定每个寄存器的使用范围:1234567ax 累加器bx GOT的地址cx 临时存储dx div指令和idiv指令专用的临时存储sp 栈指针bp 帧指针si,di 未使用
大部分计算都使用ax和cx两个寄存器,遇到寄存器不够用时,可以通过压栈的方式先保存寄存器的值然后空出来使用.
代码段包括.data保存已经初始化的全局静态变量和局部静态变量.
.rotata保存只读变量,字符串字面量和const常量
.bss段保存未初始化的局部静态变量和全局变量.
.text段保存着代码文本.
Int节点为整数常量,需要直接将值加载到寄存器中:movl $num,%eax
遇到字符串常量要加载到寄存器中,加载的是地址,地址在生成汇编代码之前处理,就是给常量分配内存地址.
先从简单的开始转换,考虑转换一元运算表达式的转换过程:
- 转换节点的表达式.
- 对不同操作符添加不同的汇编代码,比如操作符为~时,代码为:123Type src=node.expr().type();compile(node.expr());//编译表达式后的结果保存在ax寄存器中.as.not(ax(src));//对ax寄存器按位取反.
栈帧的分配过程
|
|
栈指针尽可能少做修改,需要修改的情况如下:
- 函数被调用后,需要下拉栈顶
- 将其他函数的参数压入栈
- 从其他函数返回
- 调用allocate函数,手动分配
由于在编译函数体时,如果不进行实际编译就无法得知callee-save寄存器的个数,也就不知道局部变量的内存引用偏移量.
所以编译函数体的步骤: