代码生成器是编译器中的最后一个核心模块,它主要有三个任务:指令选择,寄存器分配和指派,以及指令排序,最终将中间代码生成为目标文件。
lcc的代码生成器主要有gen.c和*.md组成,之所以由两个部分组成是因为lcc是一个被设计为可变目标编译器以支持多个架构的cpu。
同时,代码生成器虽然大部分与硬件相关,但是仍然可以提取出来一部分脱离具体硬件的逻辑。
当然,最大程度的归一化这部分逻辑得益于函数指针的合理运用。
这部分逻辑就在gen.c文件中,其他无法与cpu架构剥离的部分就由iburg通过*.md生成。
本文先分析gen.c中的内存,后续以常用的x86平台来补全对整个代码生成器的理解。
相关代码如下:
//代码生成器模块
#include "c.h"
static char rcsid[] = "$Id: gen.c,v 1.1 2002/08/28 23:12:43 drh Exp $";
#define readsreg(p) \
(generic((p)->op)==INDIR && (p)->kids[0]->op==VREG+P)
#define setsrc(d) ((d) && (d)->x.regnode && \
(d)->x.regnode->set == src->x.regnode->set && \
(d)->x.regnode->mask&src->x.regnode->mask)
#define relink(a, b) ((b)->x.prev = (a), (a)->x.next = (b))
static Symbol askfixedreg(Symbol);
static Symbol askreg(Symbol, unsigned*);
static void blkunroll(int, int, int, int, int, int, int[]);
static void docall(Node);
static void dumpcover(Node, int, int);
static void dumpregs(char *, char *, char *);
static void dumprule(int);
static void dumptree(Node);
static void genreload(Node, Symbol, int);
static void genspill(Symbol, Node, Symbol);
static Symbol getreg(Symbol, unsigned*, Node);
static int getrule(Node, int);
static void linearize(Node, Node);
static int moveself(Node);
static void prelabel(Node);
static Node* prune(Node, Node*);
static void putreg(Symbol);
static void ralloc(Node);
static void reduce(Node, int);
static int reprune(Node*, int, int, Node);
static int requate(Node);
static Node reuse(Node, int);
static void rewrite(Node);
static Symbol spillee(Symbol, unsigned mask[], Node);
static void spillr(Symbol, Node);
static int uses(Node, Regnode);
int offset;//最后一个自动变量偏移量的绝对值
int maxoffset;
int framesize;
int argoffset;
int maxargoffset;
int dalign, salign;//源块和目标块的对齐字节数
int bflag = 0; /* omit */
int dflag = 0;
int swap;
unsigned (*emitter)(Node, int) = emitasm;
static char NeedsReg[] = {
0, /* unused */
1, /* CNST */
0, 0, /* ARG ASGN */
1, /* INDIR */
0, 0, 1, 1, /* - - CVF CVI */
1, 0, 1, 1, /* CVP - CVU NEG */
1, /* CALL */
1, /* LOAD */
0, /* RET */
1, 1, 1, /* ADDRG ADDRF ADDRL */
1, 1, 1, 1, 1, /* ADD SUB LSH MOD RSH */
1, 1, 1, 1, /* BAND BCOM BOR BXOR */
1, 1, /* DIV MUL */
0, 0, 0, 0, 0, 0, /* EQ GE GT LE LT NE */
0, 0 /* JUMP LABEL */
};
Node head;
unsigned freemask[2];
unsigned usedmask[2];
unsigned tmask[2];
unsigned vmask[2];
Symbol mkreg(char *fmt, int n, int mask, int set) {
Symbol p;
NEW0(p, PERM);
p->name = p->x.name = stringf(fmt, n);
NEW0(p->x.regnode, PERM);
p->x.regnode->number = n;
p->x.regnode->mask = mask<<n;
p->x.regnode->set = set;
return p;
}
Symbol mkwildcard(Symbol *syms) {
Symbol p;
NEW0(p, PERM);
p->name = p->x.name = "wildcard";
p->x.wildcard = syms;
return p;
}
//对齐栈空间,以便安排下一个栈帧
void mkauto(Symbol p) {
assert(p->sclass == AUTO);
offset = roundup(offset + p->type->size, p->type->align);
p->x.offset = -offset;
p->x.name = stringd(-offset);
}
//保存当前栈的偏移量以及每一个寄存器的分配状态
void blockbeg(Env *e) {
e->offset = offset;
e->freemask[IREG] = freemask[IREG];
e->freemask[FREG] = freemask[FREG];
}
//恢复偏移量以及寄存器状态
void blockend(Env *e) {
if (offset > maxoffset)
maxoffset = offset;
offset = e->offset;
freemask[IREG] = e->freemask[IREG];
freemask[FREG] = e->freemask[FREG];
}
//对齐并更新argooffset(参数构建区偏移量)
int mkactual(int align, int size) {
int n = roundup(argoffset, align);
argoffset = n + size;
return n;
}
//处理参数列表结束的CALL节点
//为下一个参数集合清除argoffset值,
//计算出最大的输出参数块的大小并存于maxargoffset中
static void docall(Node p) {
p->syms[1] = p->syms[0];
//记录当前调用参数块的大小,必要的时候可由调用者弹出
p->syms[0] = intconst(argoffset);
if (argoffset > maxargoffset)
maxargoffset = argoffset;
argoffset = 0;
}
//块复制代码生成器入口,产生代码,完成在主存中赋值size个字节的功能
//源地址通过寄存器sreg和偏移量soff相加得到
//目的地址通过dreg和doff相加得到
//tmp
void blkcopy(int dreg, int doff, int sreg, int soff, int size, int tmp[]) {
assert(size >= 0);
if (size == 0)
return;
else if (size <= 2)
blkunroll(size, dreg, doff, sreg, soff, size, tmp);
else if (size == 3) {
blkunroll(2, dreg, doff, sreg, soff, 2, tmp);
blkunroll(1, dreg, doff+2, sreg, soff+2, 1, tmp);
}
else if (size <= 16) {//如果4到16个字节,将size向下取整为4的倍数
blkunroll(4, dreg, doff, sreg, soff, size&~3, tmp);//每次复制4个字节
blkcopy(dreg, doff+(size&~3),
sreg, soff+(size&~3), size&3, tmp);
}
else//大于16字节,直接调用blkloop
(*IR->x.blkloop)(dreg, doff, sreg, soff, size, tmp);
}
static void blkunroll(int k, int dreg, int doff, int sreg, int soff, int size, int tmp[]) {
int i;
assert(IR-&