1 /* $Id: inline.c,v 1.37.2.2 2011/02/26 11:31:45 ragge Exp $ */
3 * Copyright (c) 2003, 2008 Anders Magnusson (ragge@ludd.luth.se).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
);
50 * ilink from ipole points to the next struct in the list of functions.
53 SLIST_ENTRY(istat
) link
;
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
;
65 static SLIST_HEAD(, istat
) ipole
= { NULL
, &ipole
.q_forw
};
68 #define IP_REF (MAXIP+1)
70 #define SDEBUG(x) if (sdebug) printf x
76 int inlnodecnt
, inlstatcnt
;
78 #define SZSI sizeof(struct istat)
79 #define ialloc() memset(permalloc(SZSI), 0, SZSI); inlstatcnt++
82 tcnt(NODE
*p
, void *arg
)
85 if (nlabs
> 1 && (p
->n_op
== REG
|| p
->n_op
== OREG
) &&
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 */
91 printf("locking node %p\n", p
);
95 findfun(struct symtab
*sp
)
99 SLIST_FOREACH(is
, &ipole
, link
)
106 refnode(struct symtab
*sp
)
108 struct interpass
*ip
;
110 SDEBUG(("refnode(%s)\n", sp
->sname
));
112 ip
= permalloc(sizeof(*ip
));
114 ip
->ip_name
= (char *)sp
;
119 inline_addarg(struct interpass
*ip
)
123 SDEBUG(("inline_addarg(%p)\n", ip
));
124 DLIST_INSERT_BEFORE(&cifun
->shead
, ip
, qelem
);
125 if (ip
->type
== IP_DEFLAB
)
127 if (ip
->type
== IP_NODE
)
128 walkf(ip
->ip_node
, tcnt
, 0); /* Count as saved */
130 cifun
->retval
= regno(cftnod
);
134 * Called to setup for inlining of a new function.
137 inline_start(struct symtab
*sp
)
141 SDEBUG(("inline_start(\"%s\")\n", sp
->sname
));
144 cerror("already inlining function");
146 if ((is
= findfun(sp
)) != 0) {
147 if (!DLIST_ISEMPTY(&is
->shead
, qelem
))
148 uerror("inline function already defined");
152 SLIST_INSERT_FIRST(&ipole
, is
, link
);
153 DLIST_INIT(&is
->shead
, qelem
);
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.
170 SDEBUG(("inline_end()\n"));
172 if (sdebug
)printip(&cifun
->shead
);
175 if (attr_find(cifun
->sp
->sap
, GCC_ATYP_GNU_INLINE
)) {
176 if (cifun
->sp
->sclass
== EXTDEF
)
177 cifun
->sp
->sclass
= 0;
179 cifun
->sp
->sclass
= EXTDEF
;
182 if (cifun
->sp
->sclass
== EXTDEF
) {
183 cifun
->flags
|= REFD
;
189 * Called when an inline function is found, to be sure that it will
191 * The function may not be defined when inline_ref() is called.
194 inline_ref(struct symtab
*sp
)
198 SDEBUG(("inline_ref(\"%s\")\n", sp
->sname
));
199 if (sp
->sclass
== SNULL
)
200 return; /* only inline, no references */
204 SLIST_FOREACH(w
,&ipole
, link
) {
210 /* function not yet defined, print out when found */
214 SLIST_INSERT_FIRST(&ipole
, w
, link
);
215 DLIST_INIT(&w
->shead
, qelem
);
220 puto(struct istat
*w
)
222 struct interpass_prolog
*ipp
, *epp
, *pp
;
223 struct interpass
*ip
, *nip
;
227 /* Copy the saved function and print it out */
228 ipp
= 0; /* XXX data flow analysis */
229 DLIST_FOREACH(ip
, &w
->shead
, qelem
) {
233 if (ip
->type
== IP_PROLOG
) {
234 ipp
= (struct interpass_prolog
*)ip
;
235 /* fix label offsets */
236 lbloff
= crslab
- ipp
->ip_lblnum
;
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
;
245 if (ip
->type
== IP_EPILOG
&& crslab
!= pp
->ip_lblnum
)
246 cerror("puto: %d != %d", crslab
, pp
->ip_lblnum
);
248 pass2_compile((struct interpass
*)pp
);
252 inline_ref((struct symtab
*)ip
->ip_name
);
256 nip
= tmpalloc(sizeof(struct interpass
));
258 if (nip
->type
== IP_NODE
) {
261 p
= nip
->ip_node
= ccopy(nip
->ip_node
);
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
;
276 * printout functions that are referenced.
284 SLIST_FOREACH(w
, &ipole
, link
) {
285 if ((w
->flags
& (REFD
|WRITTEN
)) == REFD
&&
286 !DLIST_ISEMPTY(&w
->shead
, qelem
)) {
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
);
310 printf("%s (%p): ", foo
[ip
->type
], ip
);
312 case IP_NODE
: printf("\n");
314 fwalk(ip
->ip_node
, eprint
, 0); break;
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
);
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
);
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;
343 mnode(struct ntds
*nt
, NODE
*p
)
346 int num
= nt
->temp
+ toff
;
350 q
= tempnode(num
, nt
->type
, nt
->df
, nt
->attr
);
352 p
->n_right
= buildtree(ASSIGN
, q
, p
->n_right
);
353 p
->n_left
= mnode(nt
, p
->n_left
);
357 q
= tempnode(num
, nt
->type
, nt
->df
, nt
->attr
);
358 p
= buildtree(ASSIGN
, q
, p
);
364 rtmps(NODE
*p
, void *arg
)
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)
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
;
388 if (is
== NULL
|| nerrors
) {
389 inline_ref(sp
); /* prototype of not yet declared inline ftn */
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
)
406 if (isinlining
&& cifun
->sp
== sp
) {
407 /* Do not try to inline ourselves */
414 is
->flags
|= REFD
; /* if static inline, emit */
415 return NIL
; /* XXX cannot handle hidden ebx arg */
420 /* emit jumps to surround inline function */
421 branch(l0
= getlab());
422 plabel(l1
= 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
)) {
454 p
= ccopy(ip
->ip_node
);
456 p
->n_left
->n_lval
+= lmin
;
457 else if (p
->n_op
== CBRANCH
)
458 p
->n_right
->n_lval
+= lmin
;
462 printf("converted node\n");
463 fwalk(ip
->ip_node
, eprint
, 0);
467 send_passt(IP_NODE
, p
);
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
);
477 send_passt(IP_ASM
, ip
->ip_asm
);
481 inline_ref((struct symtab
*)ip
->ip_name
);
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
);
494 rp
= block(GOTO
, bcon(l1
), NIL
, INT
, 0, MKAP(INT
));
496 p
= tempnode(is
->retval
+ toff
, DECREF(sp
->stype
),
500 rp
= buildtree(COMOP
, rp
, p
);
503 p
= mnode(&is
->nt
[is
->nargs
-1], ap
);
504 rp
= buildtree(COMOP
, p
, rp
);
512 inline_args(struct symtab
**sp
, int nargs
)
517 SDEBUG(("inline_args\n"));
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.
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
;