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

 /***********************************
 *作者:蔡军生
 *出处: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]; 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。当然也进行了函数是否重复定义的处理。
 
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值