LCC编译器的源程序分析(28)函数表达式语句

前面已经介绍了很多表达式,但还没有介绍函数表达式语句,那么在LCC里是怎么样处理函数调用,也就是函数表达式的呢?现在就来分析函数表达式的代码,函数调用是使用非常多的,因此分析这里的代码,需要非常仔细地查看。
从hello.i例子里,就可看到下面的函数表达式语句:
printf("nTest3 = %d\r\n",nTest3);
它是由ID名称printf、表达式("nTest3 = %d\r\n",nTest3)、分号组成的。通过调用函数expr0来处理函数表达式,由于前面已经介绍那些相关的表达式,现在只去分析函数postfix表达式就可以了。它是从函数unary里进入,如下面所示:
#001 static Tree unary(void)
#002 {
#003 Tree p;

#197 default:
#198 p = postfix(primary());

主要是调用函数postfix来处理。先调用函数primary来处理printf的ID名称,然后进入函数postfix处理后面的括号中的表达式,最后生成调用树的中间表示。下面就来分析函数postfix的代码,主要分析处理函数表达式的代码。
#001 static Tree postfix(Tree p)
#002 {
#003 for (;;)
#004 switch (t)
#005 {
第3行是处理所有ID后面的记号,比如函数表达式里的括号中内容。
第4行根据当前记号来识别自增、自减、数组、函数表达式。

#006 case INCR:
#007 p = tree(RIGHT, p->type,
#008 tree(RIGHT, p->type,
#009 p,
#010 incr(t, p, consttree(1, inttype))),
#011 p);
#012 t = gettok();
#013 break;
#014 case DECR:
#015 p = tree(RIGHT, p->type,
#016 tree(RIGHT, p->type,
#017 p,
#018 incr(t, p, consttree(1, inttype))),
#019 p);
#020 t = gettok();
#021 break;
#022 case '[':
#023 {
#024 Tree q;
#025 t = gettok();
#026 q = expr(']');
#027 if (YYnull)
#028 if (isptr(p->type))
#029 p = nullcheck(p);
#030 else if (isptr(q->type))
#031 q = nullcheck(q);
#032 p = (*optree['+'])(ADD, pointer(p), pointer(q));
#033 if (isptr(p->type) && isarray(p->type->type))
#034 p = retype(p, p->type->type);
#035 else
#036 p = rvalue(p);
#037 }
#038 break;
#039 case '(':
#040 {
#041 Type ty;
#042 Coordinate pt;
#043 p = pointer(p);
#044 if (isptr(p->type) && isfunc(p->type->type))
#045 ty = p->type->type;
#046 else
#047 {
#048 error("found ‘%t’ expected a function\n", p->type);
#049 ty = func(voidtype, NULL, 1);
#050 p = retype(p, ptr(ty));
#051 }
#052
#053 pt = src;
#054 t = gettok();
#055 p = call(p, ty, pt);
#056 }
#057 break;
第39行是括号开始的函数表达式。
第43行是处理函数的树节点指针。
第45行是获取函数返回类型。
第48行是出错处理。
第49行是设置函数返回值为空。
第50行生成返回的树节点。
第53行是保存当前函数的位置。
第54行是获取下一个记号。
第55行是调用函数call来生成调用函数树,后面再接着分析怎么样生成调用树的。

#058 case '.':
#059 t = gettok();
#060 if (t == ID)
#061 {
#062 if (isstruct(p->type))
#063 {
#064 Tree q = addrof(p);
#065 p = field(q, token);
#066 q = rightkid(q);
#067 if (isaddrop(q->op) && q->u.sym->temporary)
#068 p = tree(RIGHT, p->type, p, NULL);
#069 }
#070 else
#071 error("left operand of . has incompatible type ‘%t’\n",
#072 p->type);
#073 t = gettok();
#074 }
#075 else
#076 error("field name expected\n"); break;
#077 case DEREF:
#078 t = gettok();
#079 p = pointer(p);
#080 if (t == ID)
#081 {
#082 if (isptr(p->type) && isstruct(p->type->type))
#083 {
#084 if (YYnull)
#085 p = nullcheck(p);
#086 p = field(p, token);
#087 }
#088 else
#089 error("left operand of -> has incompatible type ‘%t’\n", p->type);
#090
#091 t = gettok();
#092 }
#093 else
#094 error("field name expected\n"); break;
#095 default:
#096 return p;
#097 }
#098 }

上面的函数postfix并没有处理函数括号里的表达式,而是调用函数call来处理的。下面来分析函数call怎么样处理函数参数传递,它的代码如下:
#001 Tree call(Tree f, Type fty, Coordinate src)
#002 {
#003 int n = 0;
#004 Tree args = NULL, r = NULL, e;
#005 Type *proto, rty = unqual(freturn(fty));
#006 Symbol t3 = NULL;
#007
#008 if (fty->u.f.oldstyle)
#009 proto = NULL;
#010 else
#011 proto = fty->u.f.proto;
#012
#013 if (hascall(f))
#014 r = f;
#015
#016 if (isstruct(rty))
#017 {
#018 t3 = temporary(AUTO, unqual(rty));
#019 if (rty->size == 0)
#020 error("illegal use of incomplete type ‘%t’\n", rty);
#021 }
#022
第8行判断是否使用旧风格的函数声明,如果使用新的函数声明,就在第11行里保存返回类型。
第13行是判断是否已经调用过这个函数。
第16行是判断返回类型是否结构类型,如果是结构类型,就需要创建临时返回符号t3。

第23行判断这个函数是否空的参数列表,如果是空的参数列表就不用去运行参数处理代码。如果非空的参数列表,就运行第24行for循环处理所有参数。
#023 if (t != ')')
#024 for (;;)
#025 {
#026 Tree q = pointer(expr1(0));
第26行处理调用函数的第一个参数,生成一棵表达式树返回。

#027 if (proto && *proto && *proto != voidtype)
#028 {
#029 Type aty;
#030 q = value(q);
#031 aty = assign(*proto, q);
#032 if (aty)
#033 q = cast(q, aty);
#034 else
#035 error("type error in argument %d to %s; found ‘%t’ expected ‘%t’\n", n + 1, funcname(f),
#036
#037 q->type, *proto);
#038 if ((isint(q->type) || isenum(q->type))
#039 && q->type->size != inttype->size)
#040 q = cast(q, promote(q->type));
#041 ++proto;
#042 }
#043 else
#044 {
#045 if (!fty->u.f.oldstyle && *proto == NULL)
#046 error("too many arguments to %s\n", funcname(f));
#047 q = value(q);
#048 if (isarray(q->type) || q->type->size == 0)
#049 error("type error in argument %d to %s; ‘%t’ is illegal\n", n + 1, funcname(f), q->type);
#050
#051 else
#052 q = cast(q, promote(q->type));
#053 }
#054
第27行判断参数与声明函数是否一样的类型,如果不一样第29行到第41行处理。如果一样,就跳到第45行到第52行处理。
第30行获取参数的类型。
第33行进行参数的类型转换。
第52行也是类型转换。

#055 if (!IR->wants_argb && isstruct(q->type))
#056 if (iscallb(q))
#057 q = addrof(q);
#058 else
#059 {
#060 Symbol t1 = temporary(AUTO, unqual(q->type));
#061 q = asgn(t1, q);
#062 q = tree(RIGHT, ptr(t1->type),
#063 root(q), lvalue(idtree(t1)));
#064 }
#065
#066 if (q->type->size == 0)
#067 q->type = inttype;
#068
#069 if (hascall(q))
#070 r = r ? tree(RIGHT, voidtype, r, q) : q;
#071
#072 args = tree(mkop(ARG, q->type), q->type, q, args);
#073 n++;
#074
#075 if (Aflag >= 2 && n == 32)
#076 warning("more than 31 arguments in a call to %s\n",
#077 funcname(f));
#078
#079 if (t != ',')
#080 break;
#081 t = gettok();
#082 }
#083
第55行是判断是否有返回值的处理,如果有返回值就不用运行第56行到第64行的代码。
第66行判断类型大小是否为0,如果是就需要初始化为缺省类型。
第72行是生成参数树。
第79行是判断是否还有下一个参数,如果没有就跳出for循环;如果有就继续处理下一个参数。

#084 expect(')');
#085
#086 if (proto && *proto && *proto != voidtype)
#087 error("insufficient number of arguments to %s\n",
#088 funcname(f));
#089
#090 if (r)
#091 args = tree(RIGHT, voidtype, r, args);
#092
#093 e = calltree(f, rty, args, t3);
#094
#095 if (events.calls)
#096 apply(events.calls, &src, &e);
#097
#098 return e;
#099 }
第84行是检查是否函数调用结束符号。
第90行判断是否已经调用,如果有调用就生成引用树。
第93行是调用函数calltree来生成调用树。主要把返回值、参数树等组成调用树返回。

上面分析了函数调用代码的处理,然后把调用函数生成树形的中间表示返回,再一步的处理就是根据调用树来进行DAG处理。

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License