本节将以一个最简单的函数来分析gcc 的执行过程,这个函数没有函数体,也没有传入的参数。
例子如下:
void main( )
{
}
执行之后产生的汇编应该是这个样子
.file "test.c"
gcc_compiled.:
.text .align 2
.globl _main
_main:
pushl %ebp
movl %esp,%ebp
L1:
leave
ret
这是用1.40 gcc 的cc1 程序直接产生,后续版本可能会增加其他一些内容,但可以看出这个
版本产生的代码是最简洁的,它只产生了一个栈帧。
首先作词法分析:
我们这里没有按照gcc源代码的做法,用手工做词法分析,而是采用词法分析工具flex,来完成词法分析,这样做的结果,代码看起来很简洁直观。涉及到的词法分析规则如下:
void {
yylval.ttype = ridpointers[RID_VOID];
return TYPESPEC;
}
[ \t] ;
[a-zA-Z0-9]+ {
yylval.ttype = get_identifier (yytext);
lastiddecl = lookup_name (yylval.ttype);
if (lastiddecl != 0 && TREE_CODE (lastiddecl) == TYPE_DECL)
{
return TYPENAME;
}
return IDENTIFIER;
}
\n {
lineno++;
}
. {
return yytext[0];
}
设想一下,当词法分析器遇到void这个字串时,就将该字串归结为一个单词,随后执行下列语句:
yylval.ttype = ridpointers[RID_VOID]
ridpointers是一个tree 类型的数组,当程序启动时,会对该数组作初始化动作:
ridpointers[(int) RID_VOID] = get_identifier ("void");
get_identifier实质是返回一个tree_identifier类型的tree_node节点。这个节点的
相关属性为
length: id 长度,
pointer: 保存为id值,字符串类型比如int 就为"int",void 就为"void".
get_identifier首先去hash表中查找名字为"void" 的tree_identifier类型的tree_node节点,没找到就创建一个,然后把这个tree_node节点返回;
这样当前词法分析的当前值就为名字为"void" 的tree_identifier类型的节点,最后返回TYPESPEC,表明是一个保留字。
接下来的:
[ \t] ;
表明遇到空格和字表符不做任何处理;
[a-zA-Z0-9]+ {....}
表明现在识别到以字母开头的字串,在本样例中表明词法分析器识别到了main字串,后续处理调用get_identifier函数,查找当前hash表是否有该tree_identifier类型的tree_node节点,没有则创建,并返回该节点,
调用lookup_name,看看该tree_identifier是否在全局或局部变量域已经有定义,如果没有则认为该节点是identifier节点,如果有则需要进一步确认是否表示数据类型的节点。
\n { lineno++; }
遇到换行符号,行数加1;
. {
return yytext[0];
}
最后的其他字串将直接返给语法分析器。
词法分析部分就这样简单。
接下来我们将做语法分析...