ucc编译器(汇编生成)

语言: CN / TW / HK

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】

有了中间代码,后面生成汇编就好办了。需要支持什么cpu,就将中间代码生成对应的汇编代码就好了。这部分的工作不复杂,但是比较琐碎。

1、函数入口

void EmitTranslationUnit(AstTranslationUnit transUnit)

2、涉及到的文件

emit.c,主要是告诉我们怎么生成一个gcc认识的汇编文件

https://github.com/sheisc/ucc162.3/blob/a92719fff0ab7eb5b0c45768acedabb3cd70ca05/ucc/ucl/emit.c

x86.c,真正用来生成汇编指令

https://github.com/sheisc/ucc162.3/blob/a92719fff0ab7eb5b0c45768acedabb3cd70ca05/ucc/ucl/x86.c

3、emit.c分析

3.1 EmitTranslationUnit会调用各个接口,创建一个合适的s文件

/**
 * Emit  assembly code for the translation unit
 */
void EmitTranslationUnit(AstTranslationUnit transUnit)
{
	if(ASMFileName){
		ASMFile = fopen(ASMFileName, "w");
		ASMFileName = NULL;
	}else{
		ASMFile = CreateOutput(Input.filename, ExtName);
	}
	SwitchTableNum = 1;
	// "# Code auto-generated by UCC\n\n"
	BeginProgram();
	// ".data\n\n"
	Segment(DATA);
	/**
		.str0:	.string	"%d \012"
		.str1:	.string	"a + b + c + d = %d.\012"
	 */
	EmitStrings();

	EmitFloatConstants();

	EmitGlobals();
	// ".text\n\n"
	Segment(CODE);

	ImportFunctions();
	/**
		The key function is 
			void EmitFunction(FunctionSymbol fsym)
		in x86.c
	 */
	EmitFunctions(transUnit);

	EndProgram();

	fclose(ASMFile);
}

这个函数里面,Segement、Strings、FloatConstants、Globals、ImportFuncions这些都是为了创建汇编文件的格式基础,为后续的EmitFunctions做铺垫。在这些函数中调用的子函数,部分内容需要具体的cpu来实现,比如Segment这种函数。

3.2 emit.c和x86.c最重要的连接EmitFunction

/**
 * Emit all the functions
 */
static void EmitFunctions(AstTranslationUnit transUnit)
{
	AstNode p;
	FunctionSymbol fsym;

	p = transUnit->extDecls;
	while (p != NULL)
	{
		if (p->kind == NK_Function)
		{
			fsym = ((AstFunction)p)->fsym;
			if (fsym->sclass != TK_STATIC || fsym->ref > 0)
			{
				EmitFunction(fsym);
			}
		}
		p = p->next;
	}
}

4、x86.c分析

4.1 EmitFunction->EmitBBlock->EmitIRInst,这是最重要的翻译流程

static void EmitIRInst(IRInst inst)
{
	struct irinst instc = *inst;

	(* Emitter[inst->opcode])(&instc);
	return;
}

4.2 opcode.h一对一翻译

/**
	OPCODE(IJMP,    "ijmp",                 IndirectJump)
	OPCODE(INC,     "++",                   Inc)
	OPCODE(DEC,     "--",                   Dec)
	OPCODE(ADDR,    "&",                    Address)
	OPCODE(DEREF,   "*",                    Deref)
	OPCODE(EXTI1,   "(int)(char)",          Cast)
	OPCODE(EXTU1,   "(int)(unsigned char)", Cast)
 */
static void (* Emitter[])(IRInst inst) = 
{
#define OPCODE(code, name, func) Emit##func, 
#include "opcode.h"
#undef OPCODE
};

4.3 最简单的翻译,Jump跳转

/**
	(1)	the target of Jump is a BBlock, not Variable.
		So no DST->ref -- here.
	(2)  X87 top is saved in EmitBlock(),
		because Jump must be the last IL in a basic 
		block.
 */
static void EmitJump(IRInst inst)
{
	BBlock p = (BBlock)DST;

	DST = p->sym;
	assert(DST->kind == SK_Label);
	ClearRegs();
	PutASMCode(X86_JMP, inst->opds); 
}

4.4 另外一个最简单的翻译,获取地址

static void EmitAddress(IRInst inst)
{
	assert(DST->kind == SK_Temp && SRC1->kind != SK_Temp);
	AllocateReg(inst, 0);
	PutASMCode(X86_ADDR, inst->opds);
	ModifyVar(DST);
}

4.5 中间语言和汇编语言之间的映射

enum ASMCode
{
#define TEMPLATE(code, str) code,
#include "x86win32.tpl"
#undef TEMPLATE
};

static const char * asmCodeName[] = {
#define TEMPLATE(code, str) #code,
#include "x86win32.tpl"
#undef TEMPLATE
};

4.6 汇编语言的打印

目前主要是PutASMCode实现,

这个函数在x86linux.c & x86win32.c都进行了实现

这就看自己需要实现哪一种os下的汇编代码了

4.7 函数翻译过程中涉及到其他问题

Export,函数输出

LayoutFrame & EmitPrologue,堆栈处理

EmitEpilogue,堆栈复原

4.8 寄存器的处理

reg.c

https://github.com/sheisc/ucc162.3/blob/a92719fff0ab7eb5b0c45768acedabb3cd70ca05/ucc/ucl/reg.c

4.9 总结

相对而言,后端移植涉及到很多文件,比如emit.c、x86.c、reg.c、x86win32.c、x86linux.c,还涉及到opinfo.h、x86linux.tpl、x86win32.tpl,作为阅读代码同学,要了解每一个文件的作用,至少需要知道里面的处理流程。评价自己是否真正理解了这个理解,就看自己是不是可以把ucc移植到另外一个cpu架构上了,这是最好的判别方法。