* add p cc
[mascara-docs.git] / compilers / pcc / pcc-1.0.0 / cc / ccom / inline.c
blob908c429ba9dd7d29d8e1e301f9e42ac36310039c
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.
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.
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.
28 #include "pass1.h"
30 #include <stdarg.h>
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.
40 static void printip(struct interpass *pole);
42 struct ntds {
43 int temp;
44 TWORD type;
45 union dimfun *df;
46 struct attr *attr;
50 * ilink from ipole points to the next struct in the list of functions.
52 static 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;
65 static SLIST_HEAD(, istat) ipole = { NULL, &ipole.q_forw };
66 static int nlabs;
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
75 int isinlining;
76 int inlnodecnt, inlstatcnt;
78 #define SZSI sizeof(struct istat)
79 #define ialloc() memset(permalloc(SZSI), 0, SZSI); inlstatcnt++
81 static void
82 tcnt(NODE *p, void *arg)
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);
94 static struct istat *
95 findfun(struct symtab *sp)
97 struct istat *is;
99 SLIST_FOREACH(is, &ipole, link)
100 if (is->sp == sp)
101 return is;
102 return NULL;
105 static void
106 refnode(struct symtab *sp)
108 struct interpass *ip;
110 SDEBUG(("refnode(%s)\n", sp->sname));
112 ip = permalloc(sizeof(*ip));
113 ip->type = IP_REF;
114 ip->ip_name = (char *)sp;
115 inline_addarg(ip);
118 void
119 inline_addarg(struct interpass *ip)
121 extern NODE *cftnod;
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);
134 * Called to setup for inlining of a new function.
136 void
137 inline_start(struct symtab *sp)
139 struct istat *is;
141 SDEBUG(("inline_start(\"%s\")\n", sp->sname));
143 if (isinlining)
144 cerror("already inlining function");
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);
155 cifun = is;
156 nlabs = 0;
157 isinlining++;
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.
166 void
167 inline_end()
170 SDEBUG(("inline_end()\n"));
172 if (sdebug)printip(&cifun->shead);
173 isinlining = 0;
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;
182 if (cifun->sp->sclass == EXTDEF) {
183 cifun->flags |= REFD;
184 inline_prtout();
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.
193 void
194 inline_ref(struct symtab *sp)
196 struct istat *w;
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;
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);
219 static void
220 puto(struct istat *w)
222 struct interpass_prolog *ipp, *epp, *pp;
223 struct interpass *ip, *nip;
224 extern int crslab;
225 int lbloff = 0;
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);
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;
251 case IP_REF:
252 inline_ref((struct symtab *)ip->ip_name);
253 break;
255 default:
256 nip = tmpalloc(sizeof(struct interpass));
257 *nip = *ip;
258 if (nip->type == IP_NODE) {
259 NODE *p;
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;
272 w->flags |= WRITTEN;
276 * printout functions that are referenced.
278 void
279 inline_prtout()
281 struct istat *w;
282 int gotone = 0;
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++;
293 if (gotone)
294 inline_prtout();
297 #if 1
298 static void
299 printip(struct interpass *pole)
301 static char *foo[] = {
302 0, "NODE", "PROLOG", "STKOFF", "EPILOG", "DEFLAB", "DEFNAM", "ASM" };
303 struct interpass *ip;
304 struct interpass_prolog *ipplg, *epplg;
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;
338 #endif
340 static int toff;
342 static NODE *
343 mnode(struct ntds *nt, NODE *p)
345 NODE *q;
346 int num = nt->temp + toff;
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);
360 return p;
363 static void
364 rtmps(NODE *p, void *arg)
366 if (p->n_op == TEMP)
367 regno(p) += toff;
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)
378 NODE *
379 inlinetree(struct symtab *sp, NODE *f, NODE *ap)
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;
388 if (is == NULL || nerrors) {
389 inline_ref(sp); /* prototype of not yet declared inline ftn */
390 return NIL;
393 SDEBUG(("inlinetree(%p,%p) OK %d\n", f, ap, is->flags & CANINL));
395 gainl = attr_find(sp->sap, GCC_ATYP_ALW_INL) != NULL;
397 if ((is->flags & CANINL) == 0 && gainl)
398 werror("cannot inline but always_inline");
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;
406 if (isinlining && cifun->sp == sp) {
407 /* Do not try to inline ourselves */
408 inline_ref(sp);
409 return NIL;
412 #ifdef mach_i386
413 if (kflag) {
414 is->flags |= REFD; /* if static inline, emit */
415 return NIL; /* XXX cannot handle hidden ebx arg */
417 #endif
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));
426 ipf = DLIST_NEXT(&is->shead, qelem); /* prolog */
427 ipl = DLIST_PREV(&is->shead, qelem); /* epilog */
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));
439 /* traverse until first real label */
440 ipf = DLIST_NEXT(ipf, qelem);
442 ipf = DLIST_NEXT(ipf, qelem);
443 while (ipf->type != IP_DEFLAB);
445 /* traverse backwards to last label */
447 ipl = DLIST_PREV(ipl, qelem);
448 while (ipl->type != IP_DEFLAB);
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);
466 #endif
467 send_passt(IP_NODE, p);
468 break;
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;
476 case IP_ASM:
477 send_passt(IP_ASM, ip->ip_asm);
478 break;
480 case IP_REF:
481 inline_ref((struct symtab *)ip->ip_name);
482 break;
484 default:
485 cerror("bad inline stmt %d", ip->type);
488 SDEBUG(("last label %d to %d\n", ip->ip_lbl, ip->ip_lbl + lmin));
489 send_passt(IP_DEFLAB, ip->ip_lbl + lmin);
491 branch(l2);
492 plabel(l0);
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);
502 if (is->nargs) {
503 p = mnode(&is->nt[is->nargs-1], ap);
504 rp = buildtree(COMOP, p, rp);
507 tfree(f);
508 return rp;
511 void
512 inline_args(struct symtab **sp, int nargs)
514 struct istat *cf;
515 int i;
517 SDEBUG(("inline_args\n"));
518 cf = cifun;
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.
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;
536 cf->nargs = nargs;
537 cf->flags |= CANINL;