[关闭]
@Vany 2015-06-23T01:52:14.000000Z 字数 2303 阅读 1088

编译系统设计 CodeGen


汇编的结构

  1. data segment
  2. 声明数据用
  3. 全局变量存在这里(在PROGRAMVAR中的)
  4. 常数(const)也可以存在这里
  5. 需要了解如何定义数据
  6. code segment
  7. 代码,一条条指令
  8. 需要了解那些指令
  9. stack segment
  10. 栈,用于动态分配内存,即所有的函数内声明的变量应该都是在这里动态的使用
  11. 需要了解如何在Code Seg中操纵栈(push,pop bp
  12. 需要了解如何定义栈(大小等)

除此之外

  1. 还需要了解寄存器有哪些,以及作用
  2. 在MASM中,用于运算的寄存器非常少,真正能用于运算的非常少,基本只能满足一次运算。了解实际运算如何对应到汇编中--具体见《汇编知识要点.doc》P16
  3. 需要了解MASM的指令,对应程序语句的循环、跳转等
    最好先在写对应代码前,试验一下,编译汇编,看看这种写法是正确的

我们的基本思路就是遍历整个语法树,然后递归生成每个segment的字符串。

几种表

以下几种表用于在code generation过程中储存信息,
常量表,类型表,变量表,label表,函数表(用于存储函数名和入口地址,用么?还是直接用一个统一的标签,到时候直接call好了)

每个表有单独的结构体储存以及处理相应信息,例如添加新符号,拿到该符号对应的地址

由于routine的嵌套,这个表是根据作用域不同是在变的。那么每个作用域定义一个存储这些表的结构体(就是类似于你那个symtab_function_blocks),除此之外,还要包括指向它父作用域的指针(在处理的过程中把这些指针连起来)。

每个表提供接口,例如变量表,提供询问该id的地址为多少,应该能够计算出来,返回给它。为了能够计算地址,这个结构体应该储存作用域对应的栈空间开始的指针地址(真的要么?),然后根据这个进行偏移计算。

再如label表,直接返回label对应的地址。

这里面关键要解决的一个问题是,表是从当前作用域开始寻找,如果找到就立即返回,如果没找到向父作用于一层层的找。这样可以实现子作用域新声明的同名变量覆盖(使隐藏)父作用域的变量(或符号等……)。

类型的处理

类型无外乎以下几种:

  1. 系统类型(int, char, real, boolean)
  2. 数组
  3. 结构体
  4. 枚举类型

我们应该实现多个表达这几种基本类型的结构体,然后其他的用到类型的地方存的都是这些,例如在类型表中储存的就是(id,指向这些类型的结构体的指针)这个pair;别的地方用到该类型时,直接指向该表示该类型结构体的指针。

对于系统类型,没有什么要说的,标识好就行

对于数组,标识好类型、大小

对于结构体,用一个vector存里面的信息,名字,大小

对于枚举类型,我想可以用一个map,将枚举类型转化为0~n-1
另外,除了可以type a = (mon, tue, sun)这样定义外,
还有一个是type a = tue..sun(枚举子界类型),
在处理这个时我想可以设置一个数字的上下界,例如定义时上下界是0~n-1,在这里就是1~n-1

其实应该还有挺多东西,例如枚举类型的赋值、运算、求前后继,类型检查,数组的偏移量计算,结构体的偏移量计算等等,不知应不应该这里留一些接口?

常量的定义

常量储存在data segment中(这个需要讨论下,如果是简单的数字就直接写在codeseg里好了),我们需要维护一张常量表。
如果存在data segment其结构包括<名字,{地址,类型}>;这个地方还需要讨论一下,或者常量表里面直接储存类型和值就好了,用到时直接拿出来。

label的处理

对于label其实在实际处理时很简单,因为就是goto用得到,而汇编里直接Jmp就可以了吧
但这里有个问题是,子作用域中的label和父作用域中的label如果重名,应该以子的优先,因此我们需要一个label表,用来查询当前该label对应的别名。
比如在pascal的程序中有label 8888,调用goto 8888应该就可以实现功能。但是我们在实现中可以将8888前加上该作用域的名字,构成别名,然后放到汇编里,这样的话我们还是可以遇到goto就生成jmp,只是名字不同了;当遇到label时,自动将其转换成别名。

因此,这里面我们还是实现label的那个part吧,对于我们实现来说更方便一些,明天我改改。

变量的处理

变量表中储存:(变量名, 类型,返回地址?)
变量其实难点在于如何计算地址(相对地址),这个要根据函数的调用的处理进行计算。

函数的处理

函数调用的处理

函数调用涉及栈的操作,利用Push pop什么的维护栈
堆栈结构可以详见我室友的那个ppt里面,然后根据结构我们编写相应的代码

e.g. 调用时:
push bp什么的指针,,,
push 变量
声明标签,call xxx
push 实参

太困了,不细化了……

函数代码的生成,函数套函数

函数代码生成就直接在当前的code segment后生成就好,但是如果遇到了函数里面还定义了一个函数的情况,我们想可以将子函数的代码放在父函数最后一条语句后面。

运算的过程

表达式计算就直接按照语法树计算生成代码即可(每次运算前要做类型检查),如果遇到较长复杂表达式,我们不能用那么多寄存器,
这个需要仔细考虑下,可以在data segment里面开辟一段专门用作计算的数据区,把数据暂时存在那里

其他循环、条件分支等语句就正常按照语义来就好了

内存的分配

不同变量类型的内存的分配要规定清楚,以便于计算偏移量
我感觉这部分最好也让类型的那个struct来做

输入输出等系统函数

这个有空再讨论吧,据说可以把c语言的库的代码拿过来
不过最好先实现read\write功能

感觉很多地方没有说细,不懂得在讨论吧,面谈比较方便,打字不知哪里是重点

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注