lcc源代码解析之gen.c

代码生成器是编译器中的最后一个核心模块,它主要有三个任务:指令选择,寄存器分配和指派,以及指令排序,最终将中间代码生成为目标文件。

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-&
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值