LCC编译器的源程序分析(55)最终代码的生成

上次已经说明了怎么样选择合适的指令,现在就来介绍生成最终的代码,如下:
#010 mov dword [ebp + -12], 1
其实生成上面的代码是通过后面的语句来构造出来的,它的过程如下:
ASGNI4(ADDRLP4(nTest1), CNSTI4(1))
stmt: ASGNI4(addr,rc) / mov dword %0, %1
addr: base / [%0]
base: ADDRLP4 / ebp + %a
rc: con / %0
con: CNSTI4 / %a
在中间表示里,通过指令模式匹配到语句(stmt: ASGNI4(addr,rc) / mov dword %0, %1),然后通过树的两子节点来选择addr和rc的生成。addr又通过模式匹配选择到addr: base / [%0],接着再进一下就选择base: ADDRLP4 / ebp + %a,这样就可以生成[ebp + -12]代码了。右节点选择rc: con / %0,接着选择con: CNSTI4 / %a,这样就可以生成下面的代码:
mov dword [ebp + %a], %a
最后通过格式化把这个变量分配的栈位置-12输出,再把常量的值1输出,就生成最终的代码:
mov dword [ebp + -12], 1
这样就完成了语句(int nTest1 = 1;)编译过程。

在函数emitcode里调用函数emitasm,代码如下:
#001 unsigned emitasm(Node p, int nt) {
#002 int rulenum;
#003 short *nts;
#004 char *fmt;
#005 Node kids[10];
#006
#007 p = reuse(p, nt);
#008 rulenum = getrule(p, nt);
#009 nts = IR->x._nts[rulenum];
#010 fmt = IR->x._templates[rulenum];
#011 assert(fmt);
#012 if (IR->x._isinstruction[rulenum] && p->x.emitted)
#013 print("%s", p->syms[RX]->x.name);
#014 else if (*fmt == '#')
#015 (*IR->x.emit2)(p);
#016 else {
#017 if (*fmt == '?') {
#018 fmt++;
#019 assert(p->kids[0]);
#020 if (p->syms[RX] == p->x.kids[0]->syms[RX])
#021 while (*fmt++ != '\n')
#022 ;
#023 }
#024 for ((*IR->x._kids)(p, rulenum, kids); *fmt; fmt++)
#025 if (*fmt != '%')
#026 (void)putchar(*fmt);
#027 else if (*++fmt == 'F')
#028 print("%d", framesize);
#029 else if (*fmt >= '0' && *fmt <= '9')
#030 emitasm(kids[*fmt - '0'], nts[*fmt - '0']);
#031 else if (*fmt >= 'a' && *fmt < 'a' + NELEMS(p->syms))
#032 fputs(p->syms[*fmt - 'a']->x.name, stdout);
#033 else
#034 (void)putchar(*fmt);
#035 }
#036 return 0;
#037 }
第7行处理可重用的节点。
第8行就是从模式匹配里的两个值来找到相应的语句编号rulenum。
第9行和第10行获取语句的模板。
第24行到第35行就是按照mov dword %0, %1来格式化输出代码到文件里。

这样就完成了代码的生成工作。

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License