您的位置:首页 > 其它

LCC编译器的源程序分析(18)(19)

2007-06-02 10:21 369 查看
/***********************************
*作者:蔡军生
*出处:http://blog.csdn.net/caimouse/
************************************/

LCC编译器的源程序分析(18)函数定义

激动人心的时刻就要开始了,从这节开始,就进入处理实际的代码了。由于C语言是函数式的语言,也就是每个程序都是有一个一个的函数组成的,一个C源程序至少包含一个函数(main函数),也可以包含一个main函数和若干个其它函数。因此,函数是C程序的基本单位。仔细地查看一下第一节里的例子代码,它是如下:
#001 #include <stdio.h>
#002
#003 int main(void)
#004 {
#005 int nTest1 = 1;
#006 int nTest2 = 2;
#007 int nTest3;
#008 int i;
#009
#010 nTest3 = nTest1 + nTest2;
#011 printf("nTest3 = %d/r/n",nTest3);
#012
#013 for (i = 0; i < 5; i++)
#014 {
#015 printf("%d/r/n",nTest3+i);
#016 }
#017
#018 printf(__TIME__" "__DATE__"/r/nhello world/n");
#019 return 0;
#020 }
#021
当预处理变成下面的代码:
int main(void)
{
int nTest1 = 1;
int nTest2 = 2;
int nTest3;
int i;

nTest3 = nTest1 + nTest2;
printf("nTest3 = %d/r/n",nTest3);

for (i = 0; i < 5; i++)
{
printf("%d/r/n",nTest3+i);
}

printf("00:30:28"" ""Apr 07 2007""/r/nhello world/n");
return 0;
}

现在就来分析LCC处理这个函数定义的代码。首先跟其它函数声明一样,把函数声明int main(void)调用以前介绍的函数声明处理方法来分析后,就会运行到函数decl里,然后判断是否有函数的定义,如果有的话就去处理定义的代码,并生成最终的代码。如下:
#022 //判断是否函数定义开始。
#023 if (params && id && isfunc(ty1) &&
#024 (t == '{' || istypename(t, tsym) ||
#025 (kind[t] == STATIC && t != TYPEDEF)))
#026 {
#027 if (sclass == TYPEDEF)
#028 {
#029 error("invalid use of `typedef'/n");
#030 sclass = EXTERN;
#031 }
#032
#033 if (ty1->u.f.oldstyle)
#034 {
#035 exitscope();
#036 }
#037
#038 //函数定义,开始生成代码。
#039 funcdefn(sclass, id, ty1, params, pos);
#040
#041 return;
第23行判断是否函数的声明,如果是函数的声明,就再进一步判断是否函数定义的复合语句。如果有函数定义,在第39行开始处理函数定义,并生成汇编代码。

现在就开始去分析函数定义的处理函数funcdefn,调用的参数如下:
sclass 是函数返回存储类型。
id 是函数声明的名称。
ty1是返回类型。
params是函数的参数列表。
pos是函数定义的位置。

funcdefn处理函数定义是非常多代码的,要准备好艰苦的心理啊。从上面的函数main里,就可以看到函数定义要处理的几部分肯定有:
1. 局部变量的声明。
2. 局部变量的赋值。
3. 调用其它函数。
4. 循环语句。
5. 函数返回值。
当然函数的定义不仅仅限于上面的这些,还有很多语句,比如if语句等等。不管它的代码有多么复杂,相信我们一定有能力去分析它的实现的。

下面先来粗略地分析funcdefn函数代码实现:
#001 //函数定义分析函数。
#002 static void funcdefn(int sclass, char *id, Type ty,
#003 Symbol params[], Coordinate pt)
#004 {
#005 int i, n;
#006 Symbol *callee, *caller, p;
#007
#008 Type rty = freturn(ty);
#009
第8行是处理函数返回的类型。

#010 if (isstruct(rty) && rty->size == 0)
#011 error("illegal use of incomplete type `%t'/n", rty);
#012
第10行是处理返回类型出错的情况。

#013 //设置参数结束。
#014 for (n = 0; params
; n++)
#015 ;
#016 if (n > 0 && params[n-1]->name == NULL)
#017 params[--n] = NULL;
#018
#019 if (Aflag >= 2 && n > 31)
#020 warning("more than 31 parameters in function `%s'/n", id);
#021
第14行到第17行计算参数个数,然后设置参数列表结束位置。
第19行提示参数的个数过多,比如超过31个,一般的函数都不可能有那么多参数的,除非是程序自动生成的函数。

#022 //旧风格的函数定义。
#023 if (ty->u.f.oldstyle)
#024 {
#025 if (Aflag >= 1)
#026 warning("old-style function definition for `%s'/n", id);
#027
#028 caller = params;
#029 callee = newarray(n + 1, sizeof *callee, FUNC);
#030 memcpy(callee, caller, (n+1)*sizeof *callee);
#031 enterscope();
#032 assert(level == PARAM);
#033
#034 //
#035 while (kind[t] == STATIC || istypename(t, tsym))
#036 decl(dclparam);
#037
#038 foreach(identifiers, PARAM, oldparam, callee);
#039
#040 for (i = 0; (p = callee[i]) != NULL; i++)
#041 {
#042 if (!p->defined)
#043 callee[i] = dclparam(0, p->name, inttype, &p->src);
#044
#045 *caller[i] = *p;
#046 caller[i]->sclass = AUTO;
#047 caller[i]->type = promote(p->type);
#048 }
#049
#050 p = lookup(id, identifiers);
#051 if (p && p->scope == GLOBAL && isfunc(p->type)
#052 && p->type->u.f.proto)
#053 {
#054 Type *proto = p->type->u.f.proto;
#055 for (i = 0; caller[i] && proto[i]; i++)
#056 {
#057 Type ty = unqual(proto[i]);
#058 if (eqtype(isenum(ty) ? ty->type : ty,
#059 unqual(caller[i]->type), 1) == 0)
#060 break;
#061 else if (isenum(ty) && !isenum(unqual(caller[i]->type)))
#062 warning("compatibility of `%t' and `%t' is compiler dependent/n",
#063 proto[i], caller[i]->type);
#064 }
#065
#066 if (proto[i] || caller[i])
#067 error("conflicting argument declarations for function `%s'/n", id);
#068
#069 }
#070 else
#071 {
#072 Type *proto = newarray(n + 1, sizeof *proto, PERM);
#073 if (Aflag >= 1)
#074 warning("missing prototype for `%s'/n", id);
#075
#076 for (i = 0; i < n; i++)
#077 proto[i] = caller[i]->type;
#078
#079 proto[i] = NULL;
#080 ty = func(rty, proto, 1);
#081 }
#082 }
#083 else
#084 {
#085 //新风格的函数。
#086 callee = params;
#087 caller = newarray(n + 1, sizeof *caller, FUNC);
#088
#089 for (i = 0; (p = callee[i]) != NULL && p->name; i++)
#090 {
#091 NEW(caller[i], FUNC);
#092 *caller[i] = *p;
#093 if (isint(p->type))
#094 caller[i]->type = promote(p->type);
#095
#096 caller[i]->sclass = AUTO;
#097
#098 if ('1' <= *p->name && *p->name <= '9')
#099 error("missing name for parameter %d to function `%s'/n", i + 1, id);
#100
#101 }
#102 caller[i] = NULL;
#103 }
#104
上面代码是生成旧风格和新风格的参数callee和caller,第一个是传入函数的参数列表,第二个是返回给调用函数的参数列表。

#105 for (i = 0; (p = callee[i]) != NULL; i++)
#106 {
#107 if (p->type->size == 0)
#108 {
#109 error("undefined size for parameter `%t %s'/n",
#110 p->type, p->name);
#111 caller[i]->type = p->type = inttype;
#112 }
#113 }
#114
上面代码判断参数传送的类型是否出错。

#115 if (Aflag >= 2 && sclass != STATIC && strcmp(id, "main") == 0)
#116 {
#117 if (ty->u.f.oldstyle)
#118 warning("`%t %s()' is a non-ANSI definition/n", rty, id);
#119 else if (!(rty == inttype
#120 && (n == 0 && callee[0] == NULL
#121 || n == 2 && callee[0]->type == inttype
#122 && isptr(callee[1]->type) && callee[1]->type->type == charptype
#123 && !variadic(ty))))
#124 warning("`%s' is a non-ANSI definition/n", typestring(ty, id));
#125 }
#126
上面处理main函数定义出错的处理。

#127 p = lookup(id, identifiers);
#128 if (p && isfunc(p->type) && p->defined)
#129 error("redefinition of `%s' previously defined at %w/n",
#130 p->name, &p->src);
#131
上面的代码是判断函数是否重复声明。

#132 //声明函数。
#133 cfunc = dclglobal(sclass, id, ty, &pt);
#134
上面的代码是处理函数全局定义。

#135 //生成第一个标号。
#136 cfunc->u.f.label = genlabel(1);
#137 cfunc->u.f.callee = callee;
#138 cfunc->u.f.pt = src;
#139 cfunc->defined = 1;
#140
#141 if (xref)
#142 use(cfunc, cfunc->src);
#143
#144 if (Pflag)
#145 printproto(cfunc, cfunc->u.f.callee);
#146
#147 if (ncalled >= 0)
#148 ncalled = findfunc(cfunc->name, pt.file);
#149
上面的代码保存函数的属性。

#150 labels = table(NULL, LABELS);
#151 stmtlabs = table(NULL, LABELS);
#152 refinc = 1.0;
#153 regcount = 0;
#154 codelist = &codehead;
#155 codelist->next = NULL;
#156
#157 if (!IR->wants_callb && isstruct(rty))
#158 retv = genident(AUTO, ptr(unqual(rty)), PARAM);
#159
上面的代码是准备生成代码。

#160 //分析函数定义里的复合语句。
#161 compound(0, NULL, 0);
#162
第161行是调用函数compound来分析复合语句。在C语言里由大括号组成的语句就是复合语句。

#163 definelab(cfunc->u.f.label);
#164 if (events.exit)
#165 apply(events.exit, cfunc, NULL);
#166
#167 walk(NULL, 0, 0);
#168
#169 exitscope();
#170 assert(level == PARAM);
#171
#172 foreach(identifiers, level, checkref, NULL);
#173 if (!IR->wants_callb && isstruct(rty))
#174 {
#175 Symbol *a;
#176 a = newarray(n + 2, sizeof *a, FUNC);
#177 a[0] = retv;
#178 memcpy(&a[1], callee, (n+1)*sizeof *callee);
#179 callee = a;
#180 a = newarray(n + 2, sizeof *a, FUNC);
#181 NEW(a[0], FUNC);
#182 *a[0] = *retv;
#183 memcpy(&a[1], caller, (n+1)*sizeof *callee);
#184 caller = a;
#185 }
#186
#187 if (!IR->wants_argb)
#188 {
#189 for (i = 0; caller[i]; i++)
#190 {
#191 if (isstruct(caller[i]->type))
#192 {
#193 caller[i]->type = ptr(caller[i]->type);
#194 callee[i]->type = ptr(callee[i]->type);
#195 caller[i]->structarg = callee[i]->structarg = 1;
#196 }
#197 }
#198 }
#199
#200 if (glevel > 1)
#201 for (i = 0; callee[i]; i++) callee[i]->sclass = AUTO;
#202
#203 if (cfunc->sclass != STATIC)
#204 (*IR->export)(cfunc);
#205
#206 if (glevel && IR->stabsym)
#207 {
#208 swtoseg(CODE); (*IR->stabsym)(cfunc);
#209 }
#210
#211 swtoseg(CODE);
#212
#213 (*IR->function)(cfunc, caller, callee, cfunc->u.f.ncalls);
#214
#215 if (glevel && IR->stabfend)
#216 (*IR->stabfend)(cfunc, lineno);
#217
#218 foreach(stmtlabs, LABELS, checklab, NULL);
#219
#220 exitscope();
#221
#222 expect('}');
#223 labels = stmtlabs = NULL;
#224 retv = NULL;
#225 cfunc = NULL;
#226 }
#227
上面这部分代码都是进行代码生成部份的处理。
函数定义的代码是很长的,已经粗略地分析它们的作用,下一次再来详细地分析它们。

LCC编译器的源程序分析(19)全局函数的定义
函数定义funcdefn处理里,已经准备好调用参数和参数返回,接着就是调用全局函数声明来处理。如下面的代码:
#132 //声明函数。
#133 cfunc = dclglobal(sclass, id, ty, &pt);
#134
上面的代码是处理函数全局定义。
现在就去就分析dclglobal函数的实现,它主要用来分析全局函数的。它的代码如下:
#001 //全局函数声明。
#002 static Symbol dclglobal(int sclass, char *id, Type ty, Coordinate *pos)
#003 {
第2行里传入的参数分析是:
sclass是这个函数名称存储类型。
id是函数的名称。
ty是函数的类型,包括返回类型。
pos是函数定义的源程序中位置。

#004 Symbol p;
#005
#006 if (sclass == 0)
#007 sclass = AUTO;
#008 else if (sclass != EXTERN && sclass != STATIC)
#009 {
#010 error("invalid storage class `%k' for `%t %s'/n",
#011 sclass, ty, id);
#012 sclass = AUTO;
#013 }
#014
第6行到第13行是处理存储类型。

#015 p = lookup(id, identifiers);
#016 if (p && p->scope == GLOBAL)
#017 {
#018 if (p->sclass != TYPEDEF && eqtype(ty, p->type, 1))
#019 ty = compose(ty, p->type);
#020 else
#021 error("redeclaration of `%s' previously declared at %w/n", p->name, &p->src);
#022
#023 if (!isfunc(ty) && p->defined && t == '=')
#024 error("redefinition of `%s' previously defined at %w/n", p->name, &p->src);
#025
#026 if (p->sclass == EXTERN && sclass == STATIC
#027 || p->sclass == STATIC && sclass == AUTO
#028 || p->sclass == AUTO && sclass == STATIC)
#029 warning("inconsistent linkage for `%s' previously declared at %w/n", p->name, &p->src);
#030
#031 }
#032
第15行是查找这个全局函数是否已经声明,如果已经声明就保存在p变量里。
第16行到第31行是找到这个全局函数已经声明后,就开始判断这个声明是否合法。这里也是进行很复杂的类型推断的,由于在例子里的简单类型是没有使用到,先把它们放下。

#033 if (p == NULL || p->scope != GLOBAL)
#034 {
#035 Symbol q = lookup(id, externals);
#036 if (q)
#037 {
#038 if (sclass == STATIC || !eqtype(ty, q->type, 1))
#039 warning("declaration of `%s' does not match previous declaration at %w/n", id, &q->src);
#040
#041 p = relocate(id, externals, globals);
#042 p->sclass = sclass;
#043 }
#044 else
#045 {
#046 p = install(id, &globals, GLOBAL, PERM);
#047 p->sclass = sclass;
#048
#049 //生成函数名称。
#050 (*IR->defsymbol)(p);
#051 }
#052
在第33行里,如果发现找到这个函数已经声明过,就会运行到条件语句里面。
第35行是查找这个函数是否已经在外面声明过,也就是使用external定义的函数。
第36行到第42行是找到这个函数已经在外面定义过,那么只需要重新找到原来的函数定义处理就行了。
第45行到第50行是处理本函数从没有定义过,因而就把它保存到全局函数声明表里,然后调用后端生成函数IR->defsymbol来创建本函数的名称。比如在NASM的后端生成代码里是生成$main名称。

#053 if (p->sclass != STATIC)
#054 {
#055 static int nglobals;
#056 nglobals++;
#057 if (Aflag >= 2 && nglobals == 512)
#058 warning("more than 511 external identifiers/n");
#059 }
第53行到第59行是统计全局函数定义的个数。

#060 }
#061 else if (p->sclass == EXTERN)
#062 p->sclass = sclass;
#063
第61行是处理这个函数定义外面定义的。

#064 p->type = ty;
#065 p->src = *pos;
#066 if (t == '=' && isfunc(p->type))
#067 {
#068 error("illegal initialization for `%s'/n", p->name);
#069 t = gettok();
#070 initializer(p->type, 0);
#071 }
#072 else if (t == '=')
#073 {
#074 initglobal(p, 0);
#075 if (glevel > 0 && IR->stabsym)
#076 {
#077 (*IR->stabsym)(p);
#078 swtoseg(p->u.seg);
#079 }
#080 }
#081 else if (p->sclass == STATIC && !isfunc(p->type)
#082 && p->type->size == 0)
#083 error("undefined size for `%t %s'/n", p->type, p->name);
#084 return p;
#085 }
上面这段代码是处理函数初始化。
在第84行里返回这个函数属性符号。

通过上面的处理,就已经把一个全局函数保存到全局函数表格globals,并且生成这个函数在生成代码里的函数名称$main。当然也进行了函数是否重复定义的处理。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: