source: mainline/uspace/app/pcc/cc/ccom/inline.c@ 503e4e3

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 503e4e3 was a7de7182, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 14 years ago

Added pcc source tree (contents of pcc-1.0.0.tgz)

  • Property mode set to 100644
File size: 13.0 KB
Line 
1/* $Id: inline.c,v 1.37.2.2 2011/02/26 11:31:45 ragge Exp $ */
2/*
3 * Copyright (c) 2003, 2008 Anders Magnusson (ragge@ludd.luth.se).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27
28#include "pass1.h"
29
30#include <stdarg.h>
31
32/*
33 * Simple description of how the inlining works:
34 * A function found with the keyword "inline" is always saved.
35 * If it also has the keyword "extern" it is written out thereafter.
36 * If it has the keyword "static" it will be written out if it is referenced.
37 * inlining will only be done if -xinline is given, and only if it is
38 * possible to inline the function.
39 */
40static void printip(struct interpass *pole);
41
42struct ntds {
43 int temp;
44 TWORD type;
45 union dimfun *df;
46 struct attr *attr;
47};
48
49/*
50 * ilink from ipole points to the next struct in the list of functions.
51 */
52static struct istat {
53 SLIST_ENTRY(istat) link;
54 struct symtab *sp;
55 int flags;
56#define CANINL 1 /* function is possible to inline */
57#define WRITTEN 2 /* function is written out */
58#define REFD 4 /* Referenced but not yet written out */
59 struct ntds *nt;/* Array of arg temp type data */
60 int nargs; /* number of args in array */
61 int retval; /* number of return temporary, if any */
62 struct interpass shead;
63} *cifun;
64
65static SLIST_HEAD(, istat) ipole = { NULL, &ipole.q_forw };
66static int nlabs;
67
68#define IP_REF (MAXIP+1)
69#ifdef PCC_DEBUG
70#define SDEBUG(x) if (sdebug) printf x
71#else
72#define SDEBUG(x)
73#endif
74
75int isinlining;
76int inlnodecnt, inlstatcnt;
77
78#define SZSI sizeof(struct istat)
79#define ialloc() memset(permalloc(SZSI), 0, SZSI); inlstatcnt++
80
81static void
82tcnt(NODE *p, void *arg)
83{
84 inlnodecnt++;
85 if (nlabs > 1 && (p->n_op == REG || p->n_op == OREG) &&
86 regno(p) == FPREG)
87 SLIST_FIRST(&ipole)->flags &= ~CANINL; /* no stack refs */
88 if (p->n_op == NAME || p->n_op == ICON)
89 p->n_sp = NULL; /* let symtabs be freed for inline funcs */
90 if (nflag)
91 printf("locking node %p\n", p);
92}
93
94static struct istat *
95findfun(struct symtab *sp)
96{
97 struct istat *is;
98
99 SLIST_FOREACH(is, &ipole, link)
100 if (is->sp == sp)
101 return is;
102 return NULL;
103}
104
105static void
106refnode(struct symtab *sp)
107{
108 struct interpass *ip;
109
110 SDEBUG(("refnode(%s)\n", sp->sname));
111
112 ip = permalloc(sizeof(*ip));
113 ip->type = IP_REF;
114 ip->ip_name = (char *)sp;
115 inline_addarg(ip);
116}
117
118void
119inline_addarg(struct interpass *ip)
120{
121 extern NODE *cftnod;
122
123 SDEBUG(("inline_addarg(%p)\n", ip));
124 DLIST_INSERT_BEFORE(&cifun->shead, ip, qelem);
125 if (ip->type == IP_DEFLAB)
126 nlabs++;
127 if (ip->type == IP_NODE)
128 walkf(ip->ip_node, tcnt, 0); /* Count as saved */
129 if (cftnod)
130 cifun->retval = regno(cftnod);
131}
132
133/*
134 * Called to setup for inlining of a new function.
135 */
136void
137inline_start(struct symtab *sp)
138{
139 struct istat *is;
140
141 SDEBUG(("inline_start(\"%s\")\n", sp->sname));
142
143 if (isinlining)
144 cerror("already inlining function");
145
146 if ((is = findfun(sp)) != 0) {
147 if (!DLIST_ISEMPTY(&is->shead, qelem))
148 uerror("inline function already defined");
149 } else {
150 is = ialloc();
151 is->sp = sp;
152 SLIST_INSERT_FIRST(&ipole, is, link);
153 DLIST_INIT(&is->shead, qelem);
154 }
155 cifun = is;
156 nlabs = 0;
157 isinlining++;
158}
159
160/*
161 * End of an inline function. In C99 an inline function declared "extern"
162 * should also have external linkage and are therefore printed out.
163 * But; this is the opposite for gcc inline functions, hence special
164 * care must be taken to handle that specific case.
165 */
166void
167inline_end()
168{
169
170 SDEBUG(("inline_end()\n"));
171
172 if (sdebug)printip(&cifun->shead);
173 isinlining = 0;
174
175 if (attr_find(cifun->sp->sap, GCC_ATYP_GNU_INLINE)) {
176 if (cifun->sp->sclass == EXTDEF)
177 cifun->sp->sclass = 0;
178 else
179 cifun->sp->sclass = EXTDEF;
180 }
181
182 if (cifun->sp->sclass == EXTDEF) {
183 cifun->flags |= REFD;
184 inline_prtout();
185 }
186}
187
188/*
189 * Called when an inline function is found, to be sure that it will
190 * be written out.
191 * The function may not be defined when inline_ref() is called.
192 */
193void
194inline_ref(struct symtab *sp)
195{
196 struct istat *w;
197
198 SDEBUG(("inline_ref(\"%s\")\n", sp->sname));
199 if (sp->sclass == SNULL)
200 return; /* only inline, no references */
201 if (isinlining) {
202 refnode(sp);
203 } else {
204 SLIST_FOREACH(w,&ipole, link) {
205 if (w->sp != sp)
206 continue;
207 w->flags |= REFD;
208 return;
209 }
210 /* function not yet defined, print out when found */
211 w = ialloc();
212 w->sp = sp;
213 w->flags |= REFD;
214 SLIST_INSERT_FIRST(&ipole, w, link);
215 DLIST_INIT(&w->shead, qelem);
216 }
217}
218
219static void
220puto(struct istat *w)
221{
222 struct interpass_prolog *ipp, *epp, *pp;
223 struct interpass *ip, *nip;
224 extern int crslab;
225 int lbloff = 0;
226
227 /* Copy the saved function and print it out */
228 ipp = 0; /* XXX data flow analysis */
229 DLIST_FOREACH(ip, &w->shead, qelem) {
230 switch (ip->type) {
231 case IP_EPILOG:
232 case IP_PROLOG:
233 if (ip->type == IP_PROLOG) {
234 ipp = (struct interpass_prolog *)ip;
235 /* fix label offsets */
236 lbloff = crslab - ipp->ip_lblnum;
237 } else {
238 epp = (struct interpass_prolog *)ip;
239 crslab += (epp->ip_lblnum - ipp->ip_lblnum);
240 }
241 pp = tmpalloc(sizeof(struct interpass_prolog));
242 memcpy(pp, ip, sizeof(struct interpass_prolog));
243 pp->ip_lblnum += lbloff;
244#ifdef PCC_DEBUG
245 if (ip->type == IP_EPILOG && crslab != pp->ip_lblnum)
246 cerror("puto: %d != %d", crslab, pp->ip_lblnum);
247#endif
248 pass2_compile((struct interpass *)pp);
249 break;
250
251 case IP_REF:
252 inline_ref((struct symtab *)ip->ip_name);
253 break;
254
255 default:
256 nip = tmpalloc(sizeof(struct interpass));
257 *nip = *ip;
258 if (nip->type == IP_NODE) {
259 NODE *p;
260
261 p = nip->ip_node = ccopy(nip->ip_node);
262 if (p->n_op == GOTO)
263 p->n_left->n_lval += lbloff;
264 else if (p->n_op == CBRANCH)
265 p->n_right->n_lval += lbloff;
266 } else if (nip->type == IP_DEFLAB)
267 nip->ip_lbl += lbloff;
268 pass2_compile(nip);
269 break;
270 }
271 }
272 w->flags |= WRITTEN;
273}
274
275/*
276 * printout functions that are referenced.
277 */
278void
279inline_prtout()
280{
281 struct istat *w;
282 int gotone = 0;
283
284 SLIST_FOREACH(w, &ipole, link) {
285 if ((w->flags & (REFD|WRITTEN)) == REFD &&
286 !DLIST_ISEMPTY(&w->shead, qelem)) {
287 defloc(w->sp);
288 puto(w);
289 w->flags |= WRITTEN;
290 gotone++;
291 }
292 }
293 if (gotone)
294 inline_prtout();
295}
296
297#if 1
298static void
299printip(struct interpass *pole)
300{
301 static char *foo[] = {
302 0, "NODE", "PROLOG", "STKOFF", "EPILOG", "DEFLAB", "DEFNAM", "ASM" };
303 struct interpass *ip;
304 struct interpass_prolog *ipplg, *epplg;
305
306 DLIST_FOREACH(ip, pole, qelem) {
307 if (ip->type > MAXIP)
308 printf("IP(%d) (%p): ", ip->type, ip);
309 else
310 printf("%s (%p): ", foo[ip->type], ip);
311 switch (ip->type) {
312 case IP_NODE: printf("\n");
313#ifdef PCC_DEBUG
314 fwalk(ip->ip_node, eprint, 0); break;
315#endif
316 case IP_PROLOG:
317 ipplg = (struct interpass_prolog *)ip;
318 printf("%s %s regs %lx autos %d mintemp %d minlbl %d\n",
319 ipplg->ipp_name, ipplg->ipp_vis ? "(local)" : "",
320 (long)ipplg->ipp_regs[0], ipplg->ipp_autos,
321 ipplg->ip_tmpnum, ipplg->ip_lblnum);
322 break;
323 case IP_EPILOG:
324 epplg = (struct interpass_prolog *)ip;
325 printf("%s %s regs %lx autos %d mintemp %d minlbl %d\n",
326 epplg->ipp_name, epplg->ipp_vis ? "(local)" : "",
327 (long)epplg->ipp_regs[0], epplg->ipp_autos,
328 epplg->ip_tmpnum, epplg->ip_lblnum);
329 break;
330 case IP_DEFLAB: printf(LABFMT "\n", ip->ip_lbl); break;
331 case IP_DEFNAM: printf("\n"); break;
332 case IP_ASM: printf("%s\n", ip->ip_asm); break;
333 default:
334 break;
335 }
336 }
337}
338#endif
339
340static int toff;
341
342static NODE *
343mnode(struct ntds *nt, NODE *p)
344{
345 NODE *q;
346 int num = nt->temp + toff;
347
348 if (p->n_op == CM) {
349 q = p->n_right;
350 q = tempnode(num, nt->type, nt->df, nt->attr);
351 nt--;
352 p->n_right = buildtree(ASSIGN, q, p->n_right);
353 p->n_left = mnode(nt, p->n_left);
354 p->n_op = COMOP;
355 } else {
356 p = pconvert(p);
357 q = tempnode(num, nt->type, nt->df, nt->attr);
358 p = buildtree(ASSIGN, q, p);
359 }
360 return p;
361}
362
363static void
364rtmps(NODE *p, void *arg)
365{
366 if (p->n_op == TEMP)
367 regno(p) += toff;
368}
369
370/*
371 * Inline a function. Returns the return value.
372 * There are two major things that must be converted when
373 * inlining a function:
374 * - Label numbers must be updated with an offset.
375 * - The stack block must be relocated (add to REG or OREG).
376 * - Temporaries should be updated (but no must)
377 */
378NODE *
379inlinetree(struct symtab *sp, NODE *f, NODE *ap)
380{
381 extern int crslab, tvaloff;
382 struct istat *is = findfun(sp);
383 struct interpass *ip, *ipf, *ipl;
384 int lmin, stksz, l0, l1, l2, gainl;
385 OFFSZ stkoff;
386 NODE *p, *rp;
387
388 if (is == NULL || nerrors) {
389 inline_ref(sp); /* prototype of not yet declared inline ftn */
390 return NIL;
391 }
392
393 SDEBUG(("inlinetree(%p,%p) OK %d\n", f, ap, is->flags & CANINL));
394
395 gainl = attr_find(sp->sap, GCC_ATYP_ALW_INL) != NULL;
396
397 if ((is->flags & CANINL) == 0 && gainl)
398 werror("cannot inline but always_inline");
399
400 if ((is->flags & CANINL) == 0 || (xinline == 0 && gainl == 0)) {
401 if (is->sp->sclass == STATIC || is->sp->sclass == USTATIC)
402 inline_ref(sp);
403 return NIL;
404 }
405
406 if (isinlining && cifun->sp == sp) {
407 /* Do not try to inline ourselves */
408 inline_ref(sp);
409 return NIL;
410 }
411
412#ifdef mach_i386
413 if (kflag) {
414 is->flags |= REFD; /* if static inline, emit */
415 return NIL; /* XXX cannot handle hidden ebx arg */
416 }
417#endif
418
419 stkoff = stksz = 0;
420 /* emit jumps to surround inline function */
421 branch(l0 = getlab());
422 plabel(l1 = getlab());
423 l2 = getlab();
424 SDEBUG(("branch labels %d,%d,%d\n", l0, l1, l2));
425
426 ipf = DLIST_NEXT(&is->shead, qelem); /* prolog */
427 ipl = DLIST_PREV(&is->shead, qelem); /* epilog */
428
429 /* Fix label & temp offsets */
430#define IPP(x) ((struct interpass_prolog *)x)
431 SDEBUG(("pre-offsets crslab %d tvaloff %d\n", crslab, tvaloff));
432 lmin = crslab - IPP(ipf)->ip_lblnum;
433 crslab += (IPP(ipl)->ip_lblnum - IPP(ipf)->ip_lblnum) + 1;
434 toff = tvaloff - IPP(ipf)->ip_tmpnum;
435 tvaloff += (IPP(ipl)->ip_tmpnum - IPP(ipf)->ip_tmpnum) + 1;
436 SDEBUG(("offsets crslab %d lmin %d tvaloff %d toff %d\n",
437 crslab, lmin, tvaloff, toff));
438
439 /* traverse until first real label */
440 ipf = DLIST_NEXT(ipf, qelem);
441 do
442 ipf = DLIST_NEXT(ipf, qelem);
443 while (ipf->type != IP_DEFLAB);
444
445 /* traverse backwards to last label */
446 do
447 ipl = DLIST_PREV(ipl, qelem);
448 while (ipl->type != IP_DEFLAB);
449
450 /* So, walk over all statements and emit them */
451 for (ip = ipf; ip != ipl; ip = DLIST_NEXT(ip, qelem)) {
452 switch (ip->type) {
453 case IP_NODE:
454 p = ccopy(ip->ip_node);
455 if (p->n_op == GOTO)
456 p->n_left->n_lval += lmin;
457 else if (p->n_op == CBRANCH)
458 p->n_right->n_lval += lmin;
459 walkf(p, rtmps, 0);
460#ifdef PCC_DEBUG
461 if (sdebug) {
462 printf("converted node\n");
463 fwalk(ip->ip_node, eprint, 0);
464 fwalk(p, eprint, 0);
465 }
466#endif
467 send_passt(IP_NODE, p);
468 break;
469
470 case IP_DEFLAB:
471 SDEBUG(("converted label %d to %d\n",
472 ip->ip_lbl, ip->ip_lbl + lmin));
473 send_passt(IP_DEFLAB, ip->ip_lbl + lmin);
474 break;
475
476 case IP_ASM:
477 send_passt(IP_ASM, ip->ip_asm);
478 break;
479
480 case IP_REF:
481 inline_ref((struct symtab *)ip->ip_name);
482 break;
483
484 default:
485 cerror("bad inline stmt %d", ip->type);
486 }
487 }
488 SDEBUG(("last label %d to %d\n", ip->ip_lbl, ip->ip_lbl + lmin));
489 send_passt(IP_DEFLAB, ip->ip_lbl + lmin);
490
491 branch(l2);
492 plabel(l0);
493
494 rp = block(GOTO, bcon(l1), NIL, INT, 0, MKAP(INT));
495 if (is->retval)
496 p = tempnode(is->retval + toff, DECREF(sp->stype),
497 sp->sdf, sp->sap);
498 else
499 p = bcon(0);
500 rp = buildtree(COMOP, rp, p);
501
502 if (is->nargs) {
503 p = mnode(&is->nt[is->nargs-1], ap);
504 rp = buildtree(COMOP, p, rp);
505 }
506
507 tfree(f);
508 return rp;
509}
510
511void
512inline_args(struct symtab **sp, int nargs)
513{
514 struct istat *cf;
515 int i;
516
517 SDEBUG(("inline_args\n"));
518 cf = cifun;
519 /*
520 * First handle arguments. We currently do not inline anything if:
521 * - function has varargs
522 * - function args are volatile, checked if no temp node is asg'd.
523 */
524 if (nargs) {
525 for (i = 0; i < nargs; i++)
526 if ((sp[i]->sflags & STNODE) == 0)
527 return; /* not temporary */
528 cf->nt = permalloc(sizeof(struct ntds)*nargs);
529 for (i = 0; i < nargs; i++) {
530 cf->nt[i].temp = sp[i]->soffset;
531 cf->nt[i].type = sp[i]->stype;
532 cf->nt[i].df = sp[i]->sdf;
533 cf->nt[i].attr = sp[i]->sap;
534 }
535 }
536 cf->nargs = nargs;
537 cf->flags |= CANINL;
538}
Note: See TracBrowser for help on using the repository browser.