前面已经介绍了很多表达式,但还没有介绍函数表达式语句,那么在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
处理。