⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 c--minusminus语言编译器的设计文档.txt

📁 unix环境下实现的cmm语言编译器
💻 TXT
📖 第 1 页 / 共 3 页
字号:
9		boffset = offset;	/*保存当前栈的偏移*/10		emit_code(p);		/*释放临时栈是为防止计算函数的参数时		 *因寄存器溢出而破坏原来压栈的参数区*/11		free_temp_stack(boffset);12		if (NeedsReg[index(p->op)] && 0 == p->count)13			free_node_reg(p);	/*释放根结点的寄存器*/14	}15	DEBUG(fprint(2, "gen end:\n"));16 }7		调用prelabel对dag进行寄存器标注。8		调用mark_instruction对需要生成代码的结点进行标记。10		为单棵的dag调用emit_code生成代码。11		free_temp_stack释放在emit_code中由于寄存器溢出而分配的临时空间。12-13	释放根结点的已不再使用的寄存器。--------------------------------------------------------------------------------1 static void emit_code(Node forest)2 {3	Node p = forest;	/*对于溢出结点的重新取值是在get_code_template中进行的*/4	if (NULL == p || p->x.emitted || p->x.spills)5		return ;6	DEBUG(fprint(2, "emit_code begin:%d\n",p->op));7	if (CALL == generic(p->op)) { 8		Symbol rs = get_reg(p->syms[RX]);9		putreg(rs);10	}	11	emit_code(p->kids[0]);12	if (ARG == generic(p->op))13		emit_asm(p);14	emit_code(p->kids[1]);	/*NEG不能改变原寄存器的值,所以不能释放左结点的寄存器。	 *当右结点发生寄存器溢出时,为了防止为它生成重新取值代码而	 *占用左结点的寄存器而不释放左结点的寄存器,但如果父结点的	 *寄存器与左结点的寄存器相同,可以释放在左结点的寄存器,因为	 *在释放左结点的寄存器之后,在为父结点分配寄存器时,会分配该寄存器。	 */ 15	if (generic(p->op) != NEG16	&& (!p->kids[1] || !p->kids[1]->x.spills17	|| equal_reg(p,p->kids[0])))18		free_node_reg(p->kids[0]);19	reg_alloc(p);20	if (p->x.isinstruction && generic(p->op) != ARG)21		emit_asm(p);22	p->x.emitted = 1;23	free_node_reg(p->kids[1]);24	if (DIV == generic(p->op))	/*DIV结点中的edx可以提前释放,以节约寄存器*/25		put_edx();26	if (MOD == generic(p->op))	/*MOD结点中的eax可以提前释放*/27		put_eax();28	DEBUG(fprint(2, "emit_code end:\n"));29 }7-10	由于代码生成是按照后序遍历的顺序,先生成子结点的代码后才会为结点		申请寄存器。函数调用时会先在ARG结点生成参数压栈指令。		这时如果eax不是空闲的,则会产生溢出eax寄存器的代码,而破坏了接下来为		函数调用设置好了的参数区。这里提前在为ARG子结点生成压栈指令前,生成		溢出eax寄存器的代码来防止上述情况的发生。11		为左子结点生成代码。12-13	对于ARG结点需要提前在为右结点生成压栈指令前为本结点发送压栈指令,		这样才能产生参数按右至左的压栈顺序。函数emit_asm发送汇编代码。15-18	有选择的释放左子结点的寄存器。19		为结点申请寄存器。20		为需要产生指令的结点发送汇编代码。其中ARG结点已经在之前发送了指令。22		为结点做上己发送指令的标记。23		free_node_reg函数释放右子结点的寄存器。24-27	对除运算由于商在寄存器eax中因此可以提前释放edx。同理对于求余运算可以		释放寄存器eax。-------------------------------------------------------------------------------/*emit_asm:为p产生汇编代码*/1 static void emit_asm(Node p)2 {3	DEBUG(fprint(2, "emit_asm begin:\n"));4	char *fmt;5	if (p->x.isinstruction && p->x.emitted) {6		outs(p->syms[RX]->x.name);	7		return ;8	}9	fmt = get_code_templates(p);10	assert(fmt);11	if (*fmt == '#')12		(*IR->x.emit2)(p);13	else {14		if (*fmt == '?') {15			fmt++;16			assert(p->kids[0]);17			if (equal_reg(p, p->kids[0]))18				while (*fmt++ != '\n')19					;20		}21		for (; *fmt; fmt++)22			if (*fmt != '%')23				*bp++ = *fmt;24			else if (*++fmt >= '0' && *fmt <= '2') 25				emit_asm(p->kids[*fmt - '0']);26			else if (*fmt >= 'a' && *fmt < 'a' + NELEMS(p->syms)){27					outs(p->syms[*fmt - 'a']->x.name);28			}else29				*bp++ = *fmt;30	}31	if (ASGN+A == p->op)32		putreg(p->syms[RX]);33	DEBUG(fprint(2, "emit_asm end:\n"));34 }5-8		对生成了指令的结点只输出它的寄存器的名字。9		get_code_templates选择代码模板。11-12	为需要特殊处理的结点发送代码。14-20	对于需要生成双指令的结点,例如"?mov %c, %0\nadd %c, %1\n",在左子结点		的寄存器与本结点的寄存器相同时,可以省略寄存器复制指令。31-32	对于局部数组初始化,在生成传送指令后,需要特别的为它释放计数器ecx寄存器-------------------------------------------------------------------------------/*get_code_templates:为p选择汇编代码模板*/1 static char *get_code_templates(Node p)2 {3	assert(p);4	DEBUG(fprint(2, "get_code_templates begin:%d\n", p->op));5	if (p->kids[0] && p->kids[0]->x.spills)6		gen_re_load(p, 0);7	if (p->kids[1] && p->kids[1]->x.spills)8		gen_re_load(p, 1);9	switch (generic(p->op)){	10	case CVI: case CVP: 11	case CVU: 12		if (CVIU == p->op || CVUI == p->op13		|| CVUP == p->op || CVPU == p->op)14			if (!p->x.isinstruction)15				return "%0";16		break;17	case ASGN:18		if (A == optype(p->op)) /*为初始局部数组选择指令*/19			get_reg(p->syms[RX]);	/*设置寄存器ecx*/	20		if (VREG == generic(p->kids[0]->op))21			return "mov %0, %1\n";22		break;23	case INDIR:24		if (VREG == generic(p->kids[0]->op))25			return "#read register\n";26		break;27	case ADDRL:28		if (p->x.isinstruction)	/*计算局部变量的地址*/29			return "mov %c, ebp\nadd %c, %a\n";30		break;31	}32	DEBUG(fprint(2, "get_code_templates end:%d\n", p->op));33	return templates[p->op];	34 }5-8	为寄存器溢出的子结点重新生成取值的代码。12-15	CVUI等转换指令可以不生成寄存器传送指令,而直接使用子结点的寄存器。17-19	为局部数组的初始化分配传送字节的计数器ecx。20-21	为寄存器变量赋值。33		templates是一个以结点的操作符为索引,值为指示生成代码的字符串。-------------------------------------------------------------------------------/*get_node_reg: 为p分配寄存器*/1 static void get_node_reg(Node p, int t)2 {3	Symbol rs = NULL;4	if (!p->syms[RX]->x.wildcard) { /*需要固定的寄存器*/5		if ((DIV == generic(p->op) || MOD == generic(p->op)) && t) {6			rs = p->syms[RX];	/*保存原来的寄存器*/7			set_eax(p);			/*设置为eax寄存器*/8			get_node_reg(p, 0);9			set_edx(p);			/*设置为edx寄存器*/10			get_node_reg(p, 0);11			p->syms[RX] = rs;	/*恢复原来的寄存器*/12			rs->x.lastuse = p;13			return ;14		}15		rs = askfixedreg(p->syms[RX]);16		if (!rs && p->syms[RX]->x.lastuse->kids[0] == p) 		/*如果是它的父结点占用了寄存器则不必溢出寄存器		 *这主要是为了处理在子结点出现寄存器溢出时,为它生成		 *重新取值代码,而分配寄存器时可能会溢出它的父结点寄存器的情况		 */17			return ; 		18	}19	if (NULL == rs)20		rs = get_reg(p->syms[RX]); 21	rs->x.lastuse = p;/*lastuse指向使用它的结点*/22	p->syms[RX] = rs;23 }5-14	对于除和求余需要为它们分配eax和edx寄存器,其中t在get_node_reg内部		调用时0,在其它结点调用为1。15		申请固定的寄存器。19-20	对于需要通用寄存器或需要溢出寄存器的结点调用get_reg函数进行处理。21		将申请到的寄存器指向引用它的结点,这是为了在溢出寄存器时,能够为结点		生成重新取值的代码。--------------------------------------------------------------------------------/*get_reg:为结点获得寄存器rs*/1 static Symbol get_reg(Symbol rs)2 {3	assert(rs);4	Symbol r = ask_reg(rs);5	if (NULL == r) {	/*需要溢出寄存器*/6		r = sel_over_reg(rs);7		overflow(r);8		r = ask_reg(r);9	}10	return r;11 }get_reg首先调用ask_reg申请寄存器。若失败则调用sel_over_reg选择需要溢出的寄存器。然后调用overflow产生溢出代码。最后再次调用ask_reg申请寄存器,这次申请会一定成功--------------------------------------------------------------------------------/*sel_over_reg选择溢出的寄存器*/1 static Symbol sel_over_reg(Symbol rs)2 {3	int i, min;4	Node p;5	if (!rs->x.wildcard)6		return rs;7	for (i = 7, min = INT_MAX; i >= 0; i--) {8		Symbol ri = rs->x.wildcard[i];9		if (ri != NULL && ri->x.lastuse) {10			Node q = ri->x.lastuse;11			if (q->count < min 			/*查找引用次数最小的结点*/12			&& (!ri->x.regnode->vbl)	/*不溢出寄存器变量*/13			&& (ri->x.regnode->mask & ~non_spill_mask)){14				min = q->count;15				p = q;16			}17		}18	}19	return p->syms[RX];20 }5-6	对于固定的寄存器,直接返回该寄存器进行溢出。7-18	sel_over_reg总是选择引用次数最小、且不是寄存器变量、同时不受保护的		寄存器,做为溢出寄存器。--------------------------------------------------------------------------------/*overflow:保存溢出寄存器rs中的值,并将重新产生为引用rs的值的结点*/1 static void overflow(Symbol rs)2 {3	int ty;4	assert(rs);5	Node p = rs->x.lastuse;6	assert(p);7	DEBUG(fprint(2, "overflow register:%s,%d\n", rs->x.name, p->op));8	Symbol temp = newtemp(AUTO,optype(p->op));9	if (offset > maxoffset)  {	/*为临时变量在栈中分配存储空间*/10		print("sub esp, %d\n", offset - maxoffset);11		maxoffset = offset;12	}13	ty = optype(p->op);	/*将寄存器中的值保存到临时变量中*/14	if (C == ty) 15		print("mov byte [ebp + %s], %s\n", temp->x.name, p->syms[RX]->x.name);16	else 17		print("mov dword [ebp + %s], %s\n", temp->x.name, p->syms[RX]->x.name);	/*重新产生引用寄存器的结点*/18	p->kids[0] = newnode(ADDRL+P, NULL, NULL, temp);19	p->op = INDIR+optype(p->op);20	p->syms[RX] = NULL;21	p->kids[1] = NULL;22	p->x.isinstruction = 0;	23	p->x.emitted = 0;		/*需要重新生成取值代码*/24	p->x.spills = 1;		25	p->x.marked = 0;26	putreg(rs);27 }8-12	调用newtemp生成保存寄存器的临时变量,并且在栈中分配存储空间。		maxoffset记录了栈的最大的偏移。14-17	直接生成溢出指令。18-25	重新产生引用栈中己溢出寄存器的值的结点。26		释放寄存器。--------------------------------------------------------------------------------以上各函数位于gen.c中

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -