上次已经说明了怎么样选择合适的指令,现在就来介绍生成最终的代码,如下:
#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
来格式化输出代码到文件里。
这样就完成了代码的生成工作。