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]; 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 
上面这部分代码都是进行代码生成部份的处理。
函数定义的代码是很长的,已经粗略地分析它们的作用,下一次再来详细地分析它们。

你可能感兴趣的:(function,prototype,null,Parameters,compiler,编译器)