|
|
1.1 root 1: /* Procedure integration for GNU CC.
2: Copyright (C) 1988 Free Software Foundation, Inc.
3: Contributed by Michael Tiemann ([email protected])
4:
5: This file is part of GNU CC.
6:
7: GNU CC is distributed in the hope that it will be useful,
8: but WITHOUT ANY WARRANTY. No author or distributor
9: accepts responsibility to anyone for the consequences of using it
10: or for whether it serves any particular purpose or works at all,
11: unless he says so in writing. Refer to the GNU CC General Public
12: License for full details.
13:
14: Everyone is granted permission to copy, modify and redistribute
15: GNU CC, but only under the conditions described in the
16: GNU CC General Public License. A copy of this license is
17: supposed to have been given to you along with GNU CC so you
18: can know your rights and responsibilities. It should be in a
19: file named COPYING. Among other things, the copyright notice
20: and this notice must be preserved on all copies. */
21:
22:
23: #include <ctype.h>
24: #include <stdio.h>
25:
26: #include "config.h"
27: #include "rtl.h"
28: #include "tree.h"
29: #include "flags.h"
30: #include "insn-flags.h"
31: #include "expr.h"
32:
33: #include "obstack.h"
34: #define obstack_chunk_alloc xmalloc
35: #define obstack_chunk_free free
36: extern int xmalloc ();
37: extern void free ();
38:
39: extern struct obstack permanent_obstack, maybepermanent_obstack;
40: extern struct obstack *rtl_obstack, *saveable_obstack, *current_obstack;
41:
42: #define MIN(x,y) ((x < y) ? x : y)
43:
44: extern tree pushdecl ();
1.1.1.5 root 45:
46: /* Default max number of insns a function can have and still be inline.
47: This is overridden on RISC machines. */
48: #ifndef INTEGRATE_THRESHOLD
49: #define INTEGRATE_THRESHOLD(DECL) \
50: (8 * (8 + list_length (DECL_ARGUMENTS (DECL)) + 16*TREE_INLINE (DECL)))
51: #endif
1.1 root 52:
53: /* This is the target of the inline function being expanded,
54: or NULL if there is none. */
55: static rtx inline_target;
56:
57: /* We must take special care not to disrupt life too severely
58: when performing procedure integration. One thing that that
59: involves is not creating illegitimate address which reload
60: cannot fix. Since we don't know what the frame pointer is
61: not capable of (in a machine independent way), we create
62: a pseudo-frame pointer which will have to do for now. */
63: static rtx inline_fp_rtx;
64:
65: /* Convert old frame-pointer offsets to new. Parameters which only
66: produce values (no addresses, and are never assigned), map directly
67: to the pseudo-reg of the incoming value. Parameters that are
68: assigned to but do not have their address taken are given a fresh
69: pseudo-register. Parameters that have their address take are
70: given a fresh stack-slot. */
71: static rtx *parm_map;
72:
73: /* ?? Should this be done here?? It is not right now.
74: Keep track of whether a given pseudo-register is the sum
75: of the frame pointer and a const_int (or zero). */
76: static char *fp_addr_p;
77:
78: /* For the local variables of the procdure being integrated that live
79: on the frame, FRAME_POINTER_DELTA says how much to change their
80: offsets by, so that they now live in the correct place on the
81: frame of the function being compiled. */
82: static int fp_delta;
83:
1.1.1.11! root 84: /* When an insn is being copied by copy_rtx_and_substitute,
! 85: this is nonzero if we have copied an ASM_OPERANDS.
! 86: In that case, it is the original input-operand vector.
! 87: Likewise in copy_for_inline. */
! 88: static rtvec orig_asm_operands_vector;
! 89:
! 90: /* When an insn is being copied by copy_rtx_and_substitute,
! 91: this is nonzero if we have copied an ASM_OPERANDS.
! 92: In that case, it is the copied input-operand vector.
! 93: Likewise in copy_for_inline. */
! 94: static rtvec copy_asm_operands_vector;
! 95:
1.1 root 96: /* Return a copy of an rtx (as needed), substituting pseudo-register,
97: labels, and frame-pointer offsets as necessary. */
98: static rtx copy_rtx_and_substitute ();
1.1.1.7 root 99: /* Variant, used for memory addresses that are not memory_address_p. */
100: static rtx copy_address ();
1.1 root 101:
102: static void copy_parm_decls ();
103: static void copy_decl_tree ();
104:
105: static rtx try_fold_cc0 ();
106:
107: /* We do some simple constant folding optimization. This optimization
108: really exists primarily to save time inlining a function. It
1.1.1.5 root 109: also helps users who ask for inline functions without -O. */
1.1 root 110: static rtx fold_out_const_cc0 ();
111:
112: /* Zero if the current function (whose FUNCTION_DECL is FNDECL)
113: is safe and reasonable to integrate into other functions.
114: Nonzero means value is a warning message with a single %s
115: for the function's name. */
116:
117: char *
118: function_cannot_inline_p (fndecl)
119: register tree fndecl;
120: {
121: register rtx insn;
122: tree last = tree_last (TYPE_ARG_TYPES (TREE_TYPE (fndecl)));
1.1.1.5 root 123: int max_insns = INTEGRATE_THRESHOLD (fndecl);
1.1 root 124: register int ninsns = 0;
125: register tree parms;
126:
127: /* No inlines with varargs. `grokdeclarator' gives a warning
128: message about that if `inline' is specified. This code
129: it put in to catch the volunteers. */
130: if (last && TREE_VALUE (last) != void_type_node)
1.1.1.7 root 131: return "varargs function cannot be inline";
1.1 root 132:
133: /* If its not even close, don't even look. */
134: if (get_max_uid () > 2 * max_insns)
1.1.1.7 root 135: return "function too large to be inline";
1.1 root 136:
1.1.1.11! root 137: /* If the structure value address comes in the stack,
! 138: we can't handle it. */
! 139: #if defined (STRUCT_VALUE) || defined (STRUCT_VALUE_INCOMING)
! 140: if (TYPE_MODE (TREE_TYPE (TREE_TYPE (fndecl))) == BLKmode)
! 141: return "function returning large aggregate cannot be inline";
! 142: #endif
! 143:
1.1 root 144: /* Don't inline functions which have BLKmode arguments.
145: Don't inline functions that take the address of
146: a parameter and do not specify a function prototype. */
147: for (parms = DECL_ARGUMENTS (fndecl); parms; parms = TREE_CHAIN (parms))
148: {
149: if (TYPE_MODE (TREE_TYPE (parms)) == BLKmode)
1.1.1.7 root 150: return "function with large aggregate parameter cannot be inline";
1.1 root 151: if (last == NULL_TREE && TREE_ADDRESSABLE (parms))
1.1.1.7 root 152: return "no prototype, and parameter address used; cannot be inline";
1.1.1.11! root 153: /* If an aggregate is thought of as "in memory"
! 154: then its components are referred to by narrower memory refs.
! 155: If the actual parameter is a reg, these refs can't be translated,
! 156: esp. since copy_rtx_and_substitute doesn't know whether it is
! 157: reading or writing. */
! 158: if ((TREE_CODE (TREE_TYPE (parms)) == RECORD_TYPE
! 159: || TREE_CODE (TREE_TYPE (parms)) == UNION_TYPE)
! 160: && GET_CODE (DECL_RTL (parms)) == MEM)
! 161: return "address of an aggregate parameter is used; cannot be inline";
1.1 root 162: }
163:
164: if (get_max_uid () > max_insns)
165: {
166: for (ninsns = 0, insn = get_first_nonparm_insn (); insn && ninsns < max_insns;
167: insn = NEXT_INSN (insn))
168: {
169: if (GET_CODE (insn) == INSN
170: || GET_CODE (insn) == JUMP_INSN
171: || GET_CODE (insn) == CALL_INSN)
172: ninsns++;
173: }
174:
175: if (ninsns >= max_insns)
1.1.1.7 root 176: return "function too large to be inline";
1.1 root 177: }
178:
179: return 0;
180: }
181:
182: /* Variables used within save_for_inline. */
183:
184: /* Mapping from old pesudo-register to new pseudo-registers.
185: The first element of this map is reg_map[FIRST_PSEUDO_REGISTER].
1.1.1.6 root 186: It is allocated in `save_for_inline' and `expand_inline_function',
1.1 root 187: and deallocated on exit from each of those routines. */
188: static rtx *reg_map;
189:
190: /* Mapping from old code-labels to new code-labels.
191: The first element of this map is label_map[min_labelno].
1.1.1.6 root 192: It is allocated in `save_for_inline' and `expand_inline_function',
1.1 root 193: and deallocated on exit from each of those routines. */
194: static rtx *label_map;
195:
1.1.1.6 root 196: /* Mapping from old insn uid's to copied insns.
197: It is allocated in `save_for_inline' and `expand_inline_function',
198: and deallocated on exit from each of those routines. */
199: static rtx *insn_map;
200:
1.1 root 201: /* Map pseudo reg number into the PARM_DECL for the parm living in the reg.
202: Zero for a reg that isn't a parm's home.
203: Only reg numbers less than max_parm_reg are mapped here. */
204: static tree *parmdecl_map;
205:
206: /* Keep track of first pseudo-register beyond those that are parms. */
207: static int max_parm_reg;
208:
1.1.1.7 root 209: /* Offset from arg ptr to the first parm of this inline function. */
210: static int first_parm_offset;
211:
1.1 root 212: /* On machines that perform a function return with a single
213: instruction, such as the VAX, these return insns must be
214: mapped into branch statements. */
215: extern rtx return_label;
216:
217: /* Copy an rtx for save_for_inline. */
218: static rtx copy_for_inline ();
219:
220: /* Make the insns and PARM_DECLs of the current function permanent
221: and record other information in DECL_SAVED_INSNS to allow inlining
222: of this function in subsequent calls. */
223:
224: void
225: save_for_inline (fndecl)
226: tree fndecl;
227: {
228: extern rtx *regno_reg_rtx; /* in emit-rtl.c. */
229: extern current_function_args_size;
230:
231: rtx first_insn, last_insn, insn;
232: rtx head, copy;
233: tree parms;
234: int max_labelno, min_labelno, i, len;
235: int max_reg;
1.1.1.6 root 236: int max_uid;
1.1 root 237:
238: /* Make and emit a return-label if we have not already done so. */
239:
240: if (return_label == 0)
241: {
242: return_label = gen_label_rtx ();
243: emit_label (return_label);
244: }
245:
246: /* Get some bounds on the labels and registers used. */
247:
248: max_labelno = max_label_num ();
249: min_labelno = get_first_label_num ();
250: max_parm_reg = max_parm_reg_num ();
251: max_reg = max_reg_num ();
252:
253: /* Set up PARMDECL_MAP which maps pseudo-reg number to its PARM_DECL.
254:
255: Set TREE_VOLATILE to 0 if the parm is in a register, otherwise 1.
256: Later we set TREE_READONLY to 0 if the parm is modified inside the fn. */
257:
258: parmdecl_map = (tree *) alloca (max_parm_reg * sizeof (tree));
1.1.1.3 root 259: bzero (parmdecl_map, max_parm_reg * sizeof (tree));
1.1 root 260:
261: for (parms = DECL_ARGUMENTS (fndecl); parms; parms = TREE_CHAIN (parms))
262: {
263: rtx p = DECL_RTL (parms);
264:
265: if (GET_CODE (p) == REG)
266: {
267: parmdecl_map[REGNO (p)] = parms;
268: TREE_VOLATILE (parms) = 0;
269: }
270: else
271: TREE_VOLATILE (parms) = 1;
272: TREE_READONLY (parms) = 1;
273: }
274:
275: /* The list of DECL_SAVES_INSNS, starts off with a header which
276: contains the following information:
277:
278: the first insn of the function (not including the insns that copy
279: parameters into registers).
280: the first label used by that function,
281: the last label used by that function,
282: and the total number of registers used. */
283:
284: head = gen_inline_header_rtx (NULL, NULL, min_labelno, max_labelno,
285: max_parm_reg, max_reg,
286: current_function_args_size);
1.1.1.6 root 287: max_uid = INSN_UID (head);
1.1 root 288:
289: /* We have now allocated all that needs to be allocated permanently
290: on the rtx obstack. Set our high-water mark, so that we
291: can free the rest of this when the time comes. */
292:
293: preserve_data ();
294:
295: /* Copy the chain insns of this function.
296: Install the copied chain as the insns of this function,
297: for continued compilation;
298: the original chain is recorded as the DECL_SAVED_INSNS
299: for inlining future calls. */
300:
301: /* If there are insns that copy parms from the stack into pseudo registers,
302: those insns are not copied. `expand_inline_function' must
303: emit the correct code to handle such things. */
304:
305: insn = get_insns ();
306: if (GET_CODE (insn) != NOTE)
307: abort ();
308: first_insn = rtx_alloc (NOTE);
309: NOTE_SOURCE_FILE (first_insn) = NOTE_SOURCE_FILE (insn);
310: NOTE_LINE_NUMBER (first_insn) = NOTE_LINE_NUMBER (insn);
311: INSN_UID (first_insn) = INSN_UID (insn);
312: PREV_INSN (first_insn) = NULL;
313: NEXT_INSN (first_insn) = NULL;
314: last_insn = first_insn;
315:
316: /* Each pseudo-reg in the old insn chain must have a unique rtx in the copy.
317: Make these new rtx's now, and install them in regno_reg_rtx, so they
318: will be the official pseudo-reg rtx's for the rest of compilation. */
319:
320: reg_map = (rtx *) alloca ((max_reg + 1) * sizeof (rtx));
321:
322: len = sizeof (struct rtx_def) + (GET_RTX_LENGTH (REG) - 1) * sizeof (rtunion);
323: for (i = max_reg - 1; i >= FIRST_PSEUDO_REGISTER; i--)
324: reg_map[i] = (rtx)obstack_copy (&maybepermanent_obstack, regno_reg_rtx[i], len);
325: bcopy (reg_map + FIRST_PSEUDO_REGISTER,
326: regno_reg_rtx + FIRST_PSEUDO_REGISTER,
1.1.1.7 root 327: (max_reg - FIRST_PSEUDO_REGISTER) * sizeof (rtx));
1.1 root 328:
329: /* Likewise each label rtx must have a unique rtx as its copy. */
330:
331: label_map = (rtx *)alloca ((max_labelno - min_labelno) * sizeof (rtx));
332: label_map -= min_labelno;
333:
334: for (i = min_labelno; i < max_labelno; i++)
335: label_map[i] = gen_label_rtx ();
336:
1.1.1.6 root 337: /* Record the mapping of old insns to copied insns. */
338:
339: insn_map = (rtx *) alloca (max_uid * sizeof (rtx));
340: bzero (insn_map, max_uid * sizeof (rtx));
341:
1.1 root 342: /* Now copy the chain of insns. */
343:
344: for (insn = NEXT_INSN (insn); insn; insn = NEXT_INSN (insn))
345: {
1.1.1.11! root 346: orig_asm_operands_vector = 0;
! 347: copy_asm_operands_vector = 0;
! 348:
1.1 root 349: switch (GET_CODE (insn))
350: {
351: case NOTE:
1.1.1.7 root 352: if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_END)
353: continue;
354:
1.1 root 355: copy = rtx_alloc (NOTE);
356: NOTE_SOURCE_FILE (copy) = NOTE_SOURCE_FILE (insn);
357: NOTE_LINE_NUMBER (copy) = NOTE_LINE_NUMBER (insn);
358: break;
359:
360: case INSN:
361: case CALL_INSN:
362: case JUMP_INSN:
363: copy = rtx_alloc (GET_CODE (insn));
364: PATTERN (copy) = copy_for_inline (PATTERN (insn));
365: INSN_CODE (copy) = -1;
366: LOG_LINKS (copy) = NULL;
367: REG_NOTES (copy) = copy_for_inline (REG_NOTES (insn));
368: break;
369:
370: case CODE_LABEL:
371: copy = label_map[CODE_LABEL_NUMBER (insn)];
372: break;
373:
374: case BARRIER:
375: copy = rtx_alloc (BARRIER);
376: break;
377:
378: default:
379: abort ();
380: }
381: INSN_UID (copy) = INSN_UID (insn);
1.1.1.6 root 382: insn_map[INSN_UID (insn)] = copy;
1.1 root 383: NEXT_INSN (last_insn) = copy;
384: PREV_INSN (copy) = last_insn;
385: last_insn = copy;
386: }
387:
388: NEXT_INSN (last_insn) = NULL;
389:
390: NEXT_INSN (head) = get_first_nonparm_insn ();
391: FIRST_PARM_INSN (head) = get_insns ();
392: DECL_SAVED_INSNS (fndecl) = head;
393: DECL_FRAME_SIZE (fndecl) = get_frame_size ();
394: TREE_INLINE (fndecl) = 1;
395:
396: parmdecl_map = 0;
397: label_map = 0;
398: reg_map = 0;
399: return_label = 0;
400:
401: set_new_first_and_last_insn (first_insn, last_insn);
402: }
403:
404: /* Copy the rtx ORIG recursively, replacing pseudo-regs and labels
405: according to `reg_map' and `label_map'.
406: All other kinds of rtx are copied except those that can never be
407: changed during compilation. */
408:
409: static rtx
410: copy_for_inline (orig)
411: rtx orig;
412: {
413: register rtx x = orig;
414: register int i;
415: register enum rtx_code code;
416: register char *format_ptr;
417:
418: if (x == 0)
419: return x;
420:
421: code = GET_CODE (x);
422:
423: /* These types may be freely shared. */
424:
425: switch (code)
426: {
427: case QUEUED:
428: case CONST_INT:
429: case CONST_DOUBLE:
430: case SYMBOL_REF:
431: case PC:
432: case CC0:
433: return x;
434:
1.1.1.11! root 435: case ASM_OPERANDS:
! 436: /* If a single asm insn contains multiple output operands
! 437: then it contains multiple ASM_OPERANDS rtx's that share operand 3.
! 438: We must make sure that the copied insn continues to share it. */
! 439: if (orig_asm_operands_vector == XVEC (orig, 3))
! 440: {
! 441: x = rtx_alloc (ASM_OPERANDS);
! 442: XSTR (x, 0) = XSTR (orig, 0);
! 443: XSTR (x, 1) = XSTR (orig, 1);
! 444: XINT (x, 2) = XINT (orig, 2);
! 445: XVEC (x, 3) = copy_asm_operands_vector;
! 446: XVEC (x, 4) = XVEC (orig, 4);
! 447: return x;
! 448: }
! 449: break;
! 450:
1.1 root 451: case MEM:
452: /* A MEM is allowed to be shared if its address is constant
453: or is a constant plus one of the special registers. */
454: if (CONSTANT_ADDRESS_P (XEXP (x, 0)))
455: return x;
456: if (GET_CODE (XEXP (x, 0)) == PLUS
457: && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG
458: && (REGNO (XEXP (XEXP (x, 0), 0)) == FRAME_POINTER_REGNUM
459: || REGNO (XEXP (XEXP (x, 0), 0)) == ARG_POINTER_REGNUM)
460: && CONSTANT_ADDRESS_P (XEXP (XEXP (x, 0), 1)))
461: if (GET_CODE (XEXP (x, 0)) == REG
462: && (REGNO (XEXP (x, 0)) == FRAME_POINTER_REGNUM
463: || REGNO (XEXP (x, 0)) == ARG_POINTER_REGNUM)
464: && CONSTANT_ADDRESS_P (XEXP (x, 1)))
465: return x;
466: break;
467:
468: case LABEL_REF:
469: {
470: /* Must point to the new insn. */
471: return gen_rtx (LABEL_REF, GET_MODE (orig),
472: label_map[CODE_LABEL_NUMBER (XEXP (orig, 0))]);
473: }
474:
475: case REG:
476: if (REGNO (x) >= FIRST_PSEUDO_REGISTER)
477: return reg_map [REGNO (x)];
478: else
479: return x;
480:
481: /* If a parm that gets modified lives in a pseudo-reg,
482: set its TREE_VOLATILE to prevent certain optimizations. */
483: case SET:
484: {
485: rtx dest = SET_DEST (x);
486:
487: if (GET_CODE (dest) == REG
488: && REGNO (dest) < max_parm_reg
1.1.1.3 root 489: && REGNO (dest) >= FIRST_PSEUDO_REGISTER
490: && parmdecl_map[REGNO (dest)] != 0)
1.1 root 491: TREE_READONLY (parmdecl_map[REGNO (dest)]) = 0;
492: }
493: break;
494: }
495:
496: /* Replace this rtx with a copy of itself. */
497:
498: x = rtx_alloc (code);
499: bcopy (orig, x, sizeof (int) * (GET_RTX_LENGTH (code) + 1));
500:
501: /* Now scan the subexpressions recursively.
502: We can store any replaced subexpressions directly into X
503: since we know X is not shared! Any vectors in X
504: must be copied if X was copied. */
505:
506: format_ptr = GET_RTX_FORMAT (code);
507:
508: for (i = 0; i < GET_RTX_LENGTH (code); i++)
509: {
510: switch (*format_ptr++)
511: {
512: case 'e':
513: XEXP (x, i) = copy_for_inline (XEXP (x, i));
514: break;
515:
1.1.1.6 root 516: case 'u':
517: /* Change any references to old-insns to point to the
518: corresponding copied insns. */
519: return insn_map[INSN_UID (XEXP (x, i))];
520:
1.1 root 521: case 'E':
1.1.1.6 root 522: if (XVEC (x, i) != NULL && XVECLEN (x, i) != 0)
1.1 root 523: {
524: register int j;
525:
526: XVEC (x, i) = gen_rtvec_v (XVECLEN (x, i), &XVECEXP (x, i, 0));
527: for (j = 0; j < XVECLEN (x, i); j++)
528: XVECEXP (x, i, j)
529: = copy_for_inline (XVECEXP (x, i, j));
530: }
531: break;
532: }
533: }
1.1.1.11! root 534:
! 535: if (code == ASM_OPERANDS && orig_asm_operands_vector == 0)
! 536: {
! 537: orig_asm_operands_vector = XVEC (orig, 3);
! 538: copy_asm_operands_vector = XVEC (x, 3);
! 539: }
! 540:
1.1 root 541: return x;
542: }
543:
544: /* Integrate the procedure defined by FNDECL. Note that this function
545: may wind up calling itself. Since the static variables are not
546: reentrant, we do not assign them until after the possibility
547: or recursion is eliminated.
548:
549: If IGNORE is nonzero, do not produce a value.
550: Otherwise store the value in TARGET if it is nonzero and that is convenient.
551:
552: Value is:
553: (rtx)-1 if we could not substitute the function
554: 0 if we substituted it and it does not produce a value
555: else an rtx for where the value is stored. */
556:
557: rtx
558: expand_inline_function (fndecl, parms, target, ignore, type, structure_value_addr)
559: tree fndecl, parms;
560: rtx target;
561: int ignore;
562: tree type;
563: rtx structure_value_addr;
564: {
565: tree formal, actual;
566: rtx header = DECL_SAVED_INSNS (fndecl);
567: rtx insns = FIRST_FUNCTION_INSN (header);
1.1.1.9 root 568: rtx insn;
1.1 root 569: int max_regno = MAX_REGNUM (header) + 1;
570: register int i;
571: int min_labelno = FIRST_LABELNO (header);
572: int max_labelno = LAST_LABELNO (header);
573: int nargs;
574: rtx *arg_vec;
1.1.1.9 root 575: rtx local_return_label = 0;
1.1 root 576: rtx follows_call = 0;
1.1.1.7 root 577: rtx this_struct_value_rtx = 0;
1.1 root 578:
579: if (max_regno < FIRST_PSEUDO_REGISTER)
1.1.1.3 root 580: abort ();
1.1 root 581:
582: nargs = list_length (DECL_ARGUMENTS (fndecl));
583:
1.1.1.7 root 584: first_parm_offset = FIRST_PARM_OFFSET (fndecl);
585:
1.1 root 586: /* We expect PARMS to have the right length; don't crash if not. */
587: if (list_length (parms) != nargs)
588: return (rtx)-1;
589:
590: /* Make a fresh binding contour that we can easily remove. */
591: pushlevel (0);
592: expand_start_bindings (0);
593:
594: /* Get all the actual args as RTL, and store them in ARG_VEC. */
595:
596: arg_vec = (rtx *)alloca (nargs * sizeof (rtx));
597:
598: for (formal = DECL_ARGUMENTS (fndecl),
599: actual = parms,
600: i = 0;
601: formal;
602: formal = TREE_CHAIN (formal),
603: actual = TREE_CHAIN (actual),
604: i++)
605: {
606: tree arg = TREE_VALUE (actual); /* this has already been converted */
607: enum machine_mode tmode = TYPE_MODE (TREE_TYPE (formal));
608: rtx copy;
609:
1.1.1.7 root 610: emit_note (DECL_SOURCE_FILE (formal), DECL_SOURCE_LINE (formal));
1.1 root 611:
612: if (TREE_ADDRESSABLE (formal))
613: {
614: int size = int_size_in_bytes (TREE_TYPE (formal));
615: copy = assign_stack_local (tmode, size);
616: store_expr (arg, copy, 0);
617: }
618: else if (! TREE_READONLY (formal)
619: || TREE_VOLATILE (formal))
620: {
621: /* If parm is modified or if it hasn't a pseudo reg,
622: we may not simply substitute the actual value;
623: copy it through a register. */
624: copy = gen_reg_rtx (tmode);
625: store_expr (arg, copy, 0);
626: }
627: else
628: {
629: copy = expand_expr (arg, 0, tmode, 0);
630:
631: /* We do not use CONSTANT_ADDRESS_P here because
632: the set of cases where that might make a difference
633: are a subset of the cases that arise even when
634: it is a CONSTANT_ADDRESS_P (i.e., fp_delta
635: gets into the act. */
636: if (GET_CODE (copy) != REG && ! CONSTANT_P (copy))
637: copy = copy_to_reg (copy);
638: }
639: arg_vec[i] = copy;
640: }
641:
642: copy_parm_decls (DECL_ARGUMENTS (fndecl), arg_vec);
643:
644: /* Perform postincrements before actually calling the function. */
645: emit_queue ();
646:
647: /* clean up stack so that variables might have smaller offsets. */
648: do_pending_stack_adjust ();
649:
650: /* Pass the function the address in which to return a structure value. */
651: if (structure_value_addr)
1.1.1.7 root 652: {
653: if (GET_CODE (struct_value_rtx) == MEM)
654: {
655: this_struct_value_rtx = force_reg (Pmode, structure_value_addr);
656: }
657: else
658: {
659: this_struct_value_rtx = struct_value_rtx;
660: emit_move_insn (this_struct_value_rtx, structure_value_addr);
661: }
662: }
1.1 root 663:
664: /* Now prepare for copying the insns.
665: Set up reg_map, parm_map and label_map saying how to translate
666: the pseudo-registers, stack-parm references and labels when copying. */
667:
668: reg_map = (rtx *) alloca (max_regno * sizeof (rtx));
669: bzero (reg_map, max_regno * sizeof (rtx));
670:
671: if (DECL_ARGUMENTS (fndecl))
672: {
673: tree decl = DECL_ARGUMENTS (fndecl);
674: int offset = FUNCTION_ARGS_SIZE (header);
1.1.1.7 root 675:
1.1 root 676: parm_map =
677: (rtx *)alloca ((offset / UNITS_PER_WORD) * sizeof (rtx));
678: bzero (parm_map, (offset / UNITS_PER_WORD) * sizeof (rtx));
1.1.1.7 root 679: parm_map -= first_parm_offset / UNITS_PER_WORD;
1.1 root 680:
681: for (formal = decl, i = 0; formal; formal = TREE_CHAIN (formal), i++)
682: {
683: /* Create an entry in PARM_MAP that says what pseudo register
684: is associated with an address we might compute. */
1.1.1.7 root 685: if (DECL_OFFSET (formal) >= 0)
686: {
687: /* This parameter has a home in the stack. */
688: parm_map[DECL_OFFSET (formal) / BITS_PER_WORD] = arg_vec[i];
689: }
690: else
691: {
692: /* Parameter that was passed in a register;
693: does it have a home on the stack (as a local)? */
694: rtx frtx = DECL_RTL (formal);
695: rtx offset = 0;
696: if (GET_CODE (frtx) == MEM)
697: {
698: frtx = XEXP (frtx, 0);
699: if (GET_CODE (frtx) == PLUS)
700: {
701: if (XEXP (frtx, 0) == frame_pointer_rtx
702: && GET_CODE (XEXP (frtx, 1)) == CONST_INT)
703: offset = XEXP (frtx, 1);
704: else if (XEXP (frtx, 1) == frame_pointer_rtx
705: && GET_CODE (XEXP (frtx, 0)) == CONST_INT)
706: offset = XEXP (frtx, 0);
707: }
708: if (offset)
709: parm_map[INTVAL (offset) / UNITS_PER_WORD] = arg_vec[i];
710: else abort ();
711: }
712: else if (GET_CODE (frtx) != REG)
713: abort ();
714: }
1.1 root 715: /* Create an entry in REG_MAP that says what rtx is associated
716: with a pseudo register from the function being inlined. */
717: if (GET_CODE (DECL_RTL (formal)) == REG)
718: reg_map[REGNO (DECL_RTL (formal))] = arg_vec[i];
719: }
1.1.1.7 root 720:
1.1.1.8 root 721: /* Make certain that we can accept struct_value_{incoming_rtx,rtx},
1.1.1.7 root 722: and map it. If it is a hard register, it is mapped automagically. */
723: if (GET_CODE (struct_value_incoming_rtx) == REG)
724: ;
725: else if (GET_CODE (struct_value_incoming_rtx) == MEM
726: && XEXP (XEXP (struct_value_incoming_rtx, 0), 0) == frame_pointer_rtx
727: && GET_CODE (XEXP (XEXP (struct_value_incoming_rtx, 0), 1)) == CONST_INT)
728: parm_map[INTVAL (XEXP (XEXP (struct_value_incoming_rtx, 0), 1)) / UNITS_PER_WORD]
729: = this_struct_value_rtx;
730: else
731: abort ();
1.1 root 732: }
733: else
734: {
735: parm_map = NULL;
736: }
737:
738: label_map = (rtx *)alloca ((max_labelno - min_labelno) * sizeof (rtx));
739: label_map -= min_labelno;
740:
741: for (i = min_labelno; i < max_labelno; i++)
742: label_map[i] = gen_label_rtx ();
743:
1.1.1.6 root 744: /* As we copy insns, record the correspondence, so that inter-insn
745: references can be copied into isomorphic structure. */
746:
747: insn_map = (rtx *) alloca (INSN_UID (header) * sizeof (rtx));
748: bzero (insn_map, INSN_UID (header) * sizeof (rtx));
749:
1.1 root 750: /* Set up a target to translate the inline function's value-register. */
751:
752: if (structure_value_addr != 0 || TYPE_MODE (type) == VOIDmode)
753: inline_target = 0;
754: else
1.1.1.3 root 755: {
756: /* Machine mode function was declared to return. */
757: enum machine_mode departing_mode = TYPE_MODE (type);
758: /* (Possibly wider) machine mode it actually computes
759: (for the sake of callers that fail to declare it right). */
760: enum machine_mode arriving_mode
1.1.1.10 root 761: = TYPE_MODE (DECL_RESULT_TYPE (fndecl));
1.1.1.3 root 762:
1.1.1.5 root 763: /* Don't use MEMs as direct targets because on some machines
764: substituting a MEM for a REG makes invalid insns.
765: Let the combiner substitute the MEM if that is valid. */
766: if (target && GET_CODE (target) == REG
767: && GET_MODE (target) == departing_mode)
1.1.1.3 root 768: inline_target = target;
769: else
770: inline_target = target = gen_reg_rtx (departing_mode);
771:
772: /* If function's value was promoted before return,
773: avoid machine mode mismatch when we substitute INLINE_TARGET.
774: But TARGET is what we will return to the caller. */
775: if (arriving_mode != departing_mode)
776: inline_target = gen_rtx (SUBREG, arriving_mode, target, 0);
777: }
1.1 root 778:
779: /* We are about to make space in this function's stack frame
780: for a copy of the stack frame of the inline function.
781: First, create an RTX that points to that stack frame
782: with the same offset usually used for the frame pointer.
783: This will be substituted for all frame-pointer references. */
784:
785: fp_delta = get_frame_size ();
786: #ifdef FRAME_GROWS_DOWNWARD
787: fp_delta = - fp_delta;
788: #endif
789: fp_delta -= STARTING_FRAME_OFFSET;
790:
791: inline_fp_rtx
792: = copy_to_mode_reg (Pmode,
793: plus_constant (frame_pointer_rtx, fp_delta));
794:
795: /* Now allocate the space for that to point at. */
796:
797: assign_stack_local (VOIDmode, DECL_FRAME_SIZE (fndecl));
798:
799: /* Now copy the insns one by one. */
800:
801: for (insn = insns; insn; insn = NEXT_INSN (insn))
802: {
803: rtx copy, pattern, next = 0;
804:
1.1.1.11! root 805: orig_asm_operands_vector = 0;
! 806: copy_asm_operands_vector = 0;
! 807:
1.1 root 808: switch (GET_CODE (insn))
809: {
810: case INSN:
811: pattern = PATTERN (insn);
812:
813: /* Special handling for the insn immediately after a CALL_INSN
814: that returned a value:
815: If it does copy the value, we must avoid the usual translation
816: of the return-register into INLINE_TARGET.
817: If it just USEs the value, the inline function expects it to
818: stay in the return-register and be returned,
819: so copy it into INLINE_TARGET. */
820:
821: if (follows_call
822: /* Allow a stack-adjust, handled normally, to come in between
823: the call and the value-copying insn. */
824: && ! (GET_CODE (pattern) == SET
825: && SET_DEST (pattern) == stack_pointer_rtx))
826: {
827: if (GET_CODE (pattern) == SET
828: && rtx_equal_p (SET_SRC (pattern), follows_call))
829: /* This insn copies the value: take special care to copy
830: that value to this insn's destination. */
831: {
832: copy = emit_insn (gen_rtx (SET, VOIDmode,
833: copy_rtx_and_substitute (SET_DEST (pattern)),
834: follows_call));
1.1.1.9 root 835: RTX_INTEGRATED_P (copy) = 1;
1.1 root 836: follows_call = 0;
837: break;
838: }
839: else if (GET_CODE (pattern) == USE
840: && rtx_equal_p (XEXP (pattern, 0), follows_call))
841: /* This insn does nothing but says the value is expected
842: to flow through to the inline function's return-value.
843: Make that happen, then ignore this insn. */
844: {
845: copy = emit_insn (gen_rtx (SET, VOIDmode, inline_target,
846: follows_call));
1.1.1.9 root 847: RTX_INTEGRATED_P (copy) = 1;
1.1 root 848: follows_call = 0;
849: break;
850: }
851: /* If it does neither, this value must be ignored. */
852: follows_call = 0;
853: }
854:
855: /* The (USE (REG n)) at return from the function should be ignored
856: since we are changing (REG n) into inline_target. */
1.1.1.6 root 857: copy = 0;
1.1 root 858: if (GET_CODE (pattern) == USE
859: && GET_CODE (XEXP (pattern, 0)) == REG
1.1.1.5 root 860: && REG_FUNCTION_VALUE_P (XEXP (pattern, 0)))
1.1 root 861: break;
862:
863: /* Try to do some quick constant folding here.
864: This will save save execution time of the compiler,
865: as well time and space of the program if done here. */
866: if (GET_CODE (pattern) == SET
867: && SET_DEST (pattern) == cc0_rtx)
868: next = try_fold_cc0 (insn);
869:
870: if (next != 0)
871: {
872: insn = next;
873: }
874: else
875: {
876: copy = emit_insn (copy_rtx_and_substitute (pattern));
1.1.1.9 root 877: RTX_INTEGRATED_P (copy) = 1;
1.1 root 878: }
879: break;
880:
881: case JUMP_INSN:
882: follows_call = 0;
883: if (GET_CODE (PATTERN (insn)) == RETURN)
884: {
1.1.1.9 root 885: if (local_return_label == 0)
886: local_return_label = gen_label_rtx ();
887: emit_jump (local_return_label);
1.1 root 888: break;
889: }
890: copy = emit_jump_insn (copy_rtx_and_substitute (PATTERN (insn)));
1.1.1.9 root 891: RTX_INTEGRATED_P (copy) = 1;
1.1 root 892: break;
893:
894: case CALL_INSN:
1.1.1.6 root 895: #if 0
896: /* This should no longer be necessary now that references
897: to this function's return value are flagged to distinguish
898: them from other references to the same hard register. */
1.1 root 899: {
900: rtx newbod;
901: /* If the call's body is (set (reg...) (call...)),
902: the register is a function return register, but DON'T
903: translate it into INLINE_TARGET because it describes the
904: called function, not the caller's return value. */
905: if (GET_CODE (PATTERN (insn)) == SET)
906: newbod = gen_rtx (SET, VOIDmode, SET_DEST (PATTERN (insn)),
907: copy_rtx_and_substitute (SET_SRC (PATTERN (insn))));
1.1.1.6 root 908: else if (GET_CODE (PATTERN (insn)) == PARALLEL
909: && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET)
910: {
911: register int j;
912: rtx newelem;
913: newbod = gen_rtx (PARALLEL, VOIDmode,
914: rtvec_alloc (XVECLEN (PATTERN (insn), 0)));
915: newelem = gen_rtx (SET, VOIDmode,
916: SET_DEST (XVECEXP (PATTERN (insn), 0, 0)),
917: copy_rtx_and_substitute (SET_SRC (XVECEXP (PATTERN (insn), 0, 0))));
918: XVECEXP (newbod, 0, 0) = newelem;
919: for (j = 1; j < XVECLEN (newbod, 0); j++)
920: XVECEXP (newbod, 0, j)
921: = copy_rtx_and_substitute (XVECEXP (PATTERN (insn), 0, j));
922: }
1.1 root 923: else
924: newbod = copy_rtx_and_substitute (PATTERN (insn));
925: copy = emit_call_insn (newbod);
926: }
1.1.1.6 root 927: #else /* 1 */
928: copy = emit_call_insn (copy_rtx_and_substitute (PATTERN (insn)));
929: #endif /* 1 */
1.1.1.9 root 930: RTX_INTEGRATED_P (copy) = 1;
1.1 root 931: /* Special handling needed for the following INSN depending on
932: whether it copies the value from the fcn return reg. */
933: if (GET_CODE (PATTERN (insn)) == SET)
934: follows_call = SET_DEST (PATTERN (insn));
935: break;
936:
937: case CODE_LABEL:
1.1.1.6 root 938: copy = emit_label (label_map[CODE_LABEL_NUMBER (insn)]);
1.1 root 939: follows_call = 0;
940: break;
941:
942: case BARRIER:
1.1.1.6 root 943: copy = emit_barrier ();
1.1 root 944: break;
945:
946: case NOTE:
1.1.1.7 root 947: if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_END)
948: copy = emit_note (NOTE_SOURCE_FILE (insn), NOTE_LINE_NUMBER (insn));
949: else
950: copy = 0;
1.1 root 951: break;
952:
953: default:
954: abort ();
955: break;
956: }
1.1.1.6 root 957:
958: insn_map[INSN_UID (insn)] = copy;
1.1 root 959: }
960:
1.1.1.9 root 961: if (local_return_label)
962: emit_label (local_return_label);
1.1 root 963:
964: /* Make copies of the decls of the symbols in the inline function, so that
965: the copies of the variables get declared in the current function. */
966: copy_decl_tree (DECL_INITIAL (fndecl), 0);
967:
968: /* End the scope containing the copied formal parameter variables. */
969:
1.1.1.10 root 970: expand_end_bindings (getdecls (), 1, 1);
1.1 root 971: poplevel (1, 1, 0);
972:
973: reg_map = NULL;
974: label_map = NULL;
975:
976: if (ignore || TYPE_MODE (type) == VOIDmode)
977: return 0;
978:
979: if (structure_value_addr)
980: {
981: if (target)
982: return target;
983: return gen_rtx (MEM, BLKmode,
984: memory_address (BLKmode, structure_value_addr));
985: }
986:
1.1.1.3 root 987: return target;
1.1 root 988: }
989:
990: /* Given a chain of PARM_DECLs, ARGS, and a vector of RTL homes VEC,
991: copy each decl into a VAR_DECL, push all of those decls
992: and give each one the corresponding home. */
993:
994: static void
995: copy_parm_decls (args, vec)
996: tree args;
997: rtx *vec;
998: {
999: register tree tail;
1000: register int i;
1001:
1002: for (tail = args, i = 0; tail; tail = TREE_CHAIN (tail), i++)
1003: {
1004: register tree decl = pushdecl (build_decl (VAR_DECL, DECL_NAME (tail),
1005: TREE_TYPE (tail)));
1.1.1.9 root 1006: /* These args would always appear unused, if not for this. */
1007: TREE_USED (decl) = 1;
1.1 root 1008: DECL_RTL (decl) = vec[i];
1009: }
1010: }
1011:
1012: /* Given a LET_STMT node, push decls and levels
1013: so as to construct in the current function a tree of contexts
1014: isomorphic to the one that is given. */
1015:
1016: static void
1017: copy_decl_tree (let, level)
1018: tree let;
1019: int level;
1020: {
1021: tree t;
1022:
1023: pushlevel (0);
1024:
1025: for (t = STMT_VARS (let); t; t = TREE_CHAIN (t))
1026: {
1027: tree d = build_decl (TREE_CODE (t), DECL_NAME (t), TREE_TYPE (t));
1028: DECL_SOURCE_LINE (d) = DECL_SOURCE_LINE (t);
1029: DECL_SOURCE_FILE (d) = DECL_SOURCE_FILE (t);
1030: if (DECL_RTL (t) != 0)
1.1.1.8 root 1031: {
1032: if (GET_CODE (DECL_RTL (t)) == MEM
1033: && CONSTANT_ADDRESS_P (XEXP (DECL_RTL (t), 0)))
1034: /* copy_rtx_and_substitute would call memory_address
1035: which would copy the address into a register.
1036: Then debugging-output wouldn't know how to handle it. */
1037: DECL_RTL (d) = DECL_RTL (t);
1038: else
1039: DECL_RTL (d) = copy_rtx_and_substitute (DECL_RTL (t));
1040: }
1.1 root 1041: TREE_EXTERNAL (d) = TREE_EXTERNAL (t);
1042: TREE_STATIC (d) = TREE_STATIC (t);
1043: TREE_PUBLIC (d) = TREE_PUBLIC (t);
1044: TREE_LITERAL (d) = TREE_LITERAL (t);
1045: TREE_ADDRESSABLE (d) = TREE_ADDRESSABLE (t);
1046: TREE_READONLY (d) = TREE_READONLY (t);
1047: TREE_VOLATILE (d) = TREE_VOLATILE (t);
1.1.1.9 root 1048: /* These args would always appear unused, if not for this. */
1049: TREE_USED (d) = 1;
1.1 root 1050: pushdecl (d);
1051: }
1052:
1053: for (t = STMT_BODY (let); t; t = TREE_CHAIN (t))
1054: copy_decl_tree (t, level + 1);
1055:
1056: poplevel (level > 0, 0, 0);
1057: }
1058:
1059: /* Create a new copy of an rtx.
1060: Recursively copies the operands of the rtx,
1061: except for those few rtx codes that are sharable. */
1062:
1063: static rtx
1064: copy_rtx_and_substitute (orig)
1065: register rtx orig;
1066: {
1067: register rtx copy, temp;
1068: register int i, j;
1069: register RTX_CODE code;
1070: register enum machine_mode mode;
1071: register char *format_ptr;
1072: int regno;
1073:
1074: if (orig == 0)
1075: return 0;
1076:
1077: code = GET_CODE (orig);
1078: mode = GET_MODE (orig);
1079:
1080: switch (code)
1081: {
1082: case REG:
1083: /* If a frame-pointer register shows up, then we
1084: must `fix' the reference. If the stack pointer
1085: register shows up, it must be part of stack-adjustments
1086: (*not* because we eliminated the frame pointer!).
1087: Small hard registers are returned as-is. Pseudo-registers
1088: go through their `reg_map'. */
1089: regno = REGNO (orig);
1090: if (regno < FIRST_PSEUDO_REGISTER)
1091: {
1.1.1.5 root 1092: if (REG_FUNCTION_VALUE_P (orig))
1.1.1.9 root 1093: {
1094: /* This is a reference to the function return value. If
1095: the function doesn't have a return value, error.
1096: If it does, it may not be the same mode as `inline_target'
1097: because SUBREG is not required for hard regs.
1098: If not, adjust mode of inline_target to fit the context. */
1099: if (inline_target == 0)
1100: abort ();
1101: if (mode == GET_MODE (inline_target))
1102: return inline_target;
1103: return gen_rtx (SUBREG, mode, inline_target, 0);
1104: }
1.1 root 1105: if (regno == FRAME_POINTER_REGNUM)
1106: return plus_constant (orig, fp_delta);
1107: return orig;
1108: }
1109: if (reg_map[regno] == NULL)
1110: reg_map[regno] = gen_reg_rtx (mode);
1111: return reg_map[regno];
1112:
1113: case CODE_LABEL:
1114: return label_map[CODE_LABEL_NUMBER (orig)];
1115:
1116: case LABEL_REF:
1117: copy = rtx_alloc (LABEL_REF);
1118: PUT_MODE (copy, mode);
1119: XEXP (copy, 0) = label_map[CODE_LABEL_NUMBER (XEXP (orig, 0))];
1120: return copy;
1121:
1122: case PC:
1123: case CC0:
1124: case CONST_INT:
1125: case CONST_DOUBLE:
1126: case SYMBOL_REF:
1127: return orig;
1128:
1.1.1.11! root 1129: case ASM_OPERANDS:
! 1130: /* If a single asm insn contains multiple output operands
! 1131: then it contains multiple ASM_OPERANDS rtx's that share operand 3.
! 1132: We must make sure that the copied insn continues to share it. */
! 1133: if (orig_asm_operands_vector == XVEC (orig, 3))
! 1134: {
! 1135: copy = rtx_alloc (ASM_OPERANDS);
! 1136: XSTR (copy, 0) = XSTR (orig, 0);
! 1137: XSTR (copy, 1) = XSTR (orig, 1);
! 1138: XINT (copy, 2) = XINT (orig, 2);
! 1139: XVEC (copy, 3) = copy_asm_operands_vector;
! 1140: XVEC (copy, 4) = XVEC (orig, 4);
! 1141: return copy;
! 1142: }
! 1143: break;
! 1144:
1.1.1.5 root 1145: case CALL:
1146: /* This is given special treatment because the first
1147: operand of a CALL is a (MEM ...) which may get
1148: forced into a register for cse. This is undesirable
1149: if function-address cse isn't wanted or if we won't do cse. */
1150: #ifndef NO_FUNCTION_CSE
1151: if (! (optimize && ! flag_no_function_cse))
1152: #endif
1153: return gen_rtx (CALL, GET_MODE (orig),
1154: gen_rtx (MEM, GET_MODE (XEXP (orig, 0)),
1155: copy_rtx_and_substitute (XEXP (XEXP (orig, 0), 0))),
1156: copy_rtx_and_substitute (XEXP (orig, 1)));
1157: break;
1158:
1.1 root 1159: case PLUS:
1160: /* Note: the PLUS case is not nearly as careful as the MEM
1161: case in terms of preserving addresses. The reason for this
1162: is that it is expected that if a PLUS_EXPR turns out not
1163: to be a legitimate address, reload can fix that up, without
1164: doing major damage. However, a MEM rtx must preside
1165: over a legitimate address. The MEM case has lots of hair
1166: to deal with what happens when it sits on a PLUS... */
1167: /* Take care of the easy case quickly. */
1168: if (XEXP (orig, 0) == frame_pointer_rtx
1169: || XEXP (orig, 1) == frame_pointer_rtx
1170: || (ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
1171: && (XEXP (orig, 0) == arg_pointer_rtx
1172: || XEXP (orig, 1) == arg_pointer_rtx)))
1173: {
1174: if (XEXP (orig, 0) == frame_pointer_rtx
1175: || XEXP (orig, 0) == arg_pointer_rtx)
1176: copy = XEXP (orig, 1);
1177: else
1178: copy = XEXP (orig, 0);
1179:
1180: if (GET_CODE (copy) == CONST_INT)
1181: {
1182: int c = INTVAL (copy);
1183:
1184: if (c > 0)
1185: {
1186: copy = parm_map[c / UNITS_PER_WORD];
1187: return XEXP (copy, 0);
1188: }
1189: return gen_rtx (PLUS, mode,
1190: frame_pointer_rtx,
1191: gen_rtx (CONST_INT, SImode,
1192: c + fp_delta));
1193: }
1194: copy = copy_rtx_and_substitute (copy);
1.1.1.6 root 1195: temp = force_reg (mode, gen_rtx (PLUS, mode, frame_pointer_rtx, copy));
1.1 root 1196: return plus_constant (temp, fp_delta);
1197: }
1198: else if (reg_mentioned_p (frame_pointer_rtx, orig)
1199: || (ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
1200: && reg_mentioned_p (arg_pointer_rtx, orig)))
1201: {
1202: /* If we have a complex sum which has a frame pointer
1203: in it, and it was a legitimate address, then
1204: keep it that way. */
1205: if (memory_address_p (mode, orig))
1206: {
1207: if (GET_CODE (XEXP (orig, 0)) == CONST_INT)
1208: {
1209: copy = copy_rtx_and_substitute (XEXP (orig, 1));
1210: temp = plus_constant (copy, INTVAL (XEXP (orig, 0)));
1211: }
1212: else if (GET_CODE (XEXP (orig, 1)) == CONST_INT)
1213: {
1214: copy = copy_rtx_and_substitute (XEXP (orig, 0));
1215: temp = plus_constant (copy, INTVAL (XEXP (orig, 1)));
1216: }
1217: else
1218: {
1219: temp = gen_rtx (PLUS, GET_MODE (orig),
1220: copy_rtx_and_substitute (XEXP (orig, 0)),
1221: copy_rtx_and_substitute (XEXP (orig, 1)));
1222: }
1223: temp = memory_address (mode, temp);
1224: }
1225: else
1226: temp = gen_rtx (PLUS, GET_MODE (orig),
1227: copy_rtx_and_substitute (XEXP (orig, 0)),
1228: copy_rtx_and_substitute (XEXP (orig, 1)));
1229: }
1230: else
1231: temp = gen_rtx (PLUS, GET_MODE (orig),
1232: copy_rtx_and_substitute (XEXP (orig, 0)),
1233: copy_rtx_and_substitute (XEXP (orig, 1)));
1234:
1235: return temp;
1.1.1.7 root 1236:
1.1 root 1237: case MEM:
1238: /* Take care of easiest case here. */
1239: copy = XEXP (orig, 0);
1240: if (copy == frame_pointer_rtx || copy == arg_pointer_rtx)
1241: return gen_rtx (MEM, mode,
1242: plus_constant (frame_pointer_rtx, fp_delta));
1.1.1.6 root 1243:
1244: /* Allow a pushing-address even if that is not valid as an
1245: ordinary memory address. It indicates we are inlining a special
1.1.1.9 root 1246: push-insn. These must be copied; otherwise unshare_all_rtl
1247: might clobber them to point at temporary rtl of this function. */
1.1.1.6 root 1248: #ifdef STACK_GROWS_DOWNWARD
1249: if (GET_CODE (copy) == PRE_DEC && XEXP (copy, 0) == stack_pointer_rtx)
1.1.1.9 root 1250: return gen_rtx (MEM, mode, copy_rtx_and_substitute (copy));
1.1.1.6 root 1251: #else
1252: if (GET_CODE (copy) == PRE_INC && XEXP (copy, 0) == stack_pointer_rtx)
1.1.1.9 root 1253: return gen_rtx (MEM, mode, copy_rtx_and_substitute (copy));
1.1.1.6 root 1254: #endif
1255:
1.1.1.7 root 1256: /* If this is some other sort of address that isn't generally valid,
1257: break out all the registers referred to. */
1258: if (! memory_address_p (mode, copy))
1259: return gen_rtx (MEM, mode, copy_address (copy));
1260:
1.1 root 1261: if (GET_CODE (copy) == PLUS)
1262: {
1263: if (XEXP (copy, 0) == frame_pointer_rtx
1264: || XEXP (copy, 1) == frame_pointer_rtx
1265: || (ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
1266: && (XEXP (copy, 0) == arg_pointer_rtx
1267: || XEXP (copy, 1) == arg_pointer_rtx)))
1268: {
1269: rtx reg;
1270: if (XEXP (copy, 0) == frame_pointer_rtx
1271: || XEXP (copy, 0) == arg_pointer_rtx)
1272: reg = XEXP (copy, 0), copy = XEXP (copy, 1);
1273: else
1274: reg = XEXP (copy, 1), copy = XEXP (copy, 0);
1275:
1276: if (GET_CODE (copy) == CONST_INT)
1277: {
1278: int c = INTVAL (copy);
1279:
1.1.1.7 root 1280: if (reg == arg_pointer_rtx && c >= first_parm_offset)
1.1 root 1281: {
1.1.1.10 root 1282: int index = c / UNITS_PER_WORD;
1283: int offset = c % UNITS_PER_WORD;
1284:
1285: /* If we are referring to the middle of a multiword parm,
1286: find the beginning of that parm.
1287: OFFSET gets the offset of the reference from
1288: the beginning of the parm. */
1289:
1290: while (parm_map[index] == 0)
1291: {
1292: index--;
1293: if (index < first_parm_offset / UNITS_PER_WORD)
1294: /* If this abort happens, it means we need
1295: to handle "decrementing" INDEX back far
1296: enough to start looking among the reg parms
1297: instead of the stack parms. What a mess! */
1298: abort ();
1299: offset += UNITS_PER_WORD;
1300: }
1301:
1302: copy = parm_map[index];
1303:
1.1.1.11! root 1304: #ifdef BYTES_BIG_ENDIAN
1.1.1.10 root 1305: /* Subtract from OFFSET the offset of where
1306: the actual parm value would start. */
1307: if (GET_MODE_SIZE (GET_MODE (copy)) < UNITS_PER_WORD)
1308: offset
1309: -= (UNITS_PER_WORD
1310: - GET_MODE_SIZE (GET_MODE (copy)));
1311: #endif
1.1 root 1312:
1313: /* If the MEM is only some of the bytes in the parm,
1.1.1.10 root 1314: effectively perform a SUBREG of the actual parm. */
1315: if ((GET_MODE (copy) != mode
1316: && GET_MODE (copy) != VOIDmode))
1317: {
1.1.1.11! root 1318: if (GET_CODE (copy) == MEM)
! 1319: return change_address (copy, mode,
! 1320: plus_constant (XEXP (copy, 0),
! 1321: offset));
! 1322: if (GET_CODE (copy) == REG)
! 1323: {
! 1324: /* Crash if the portion of the arg wanted
! 1325: is not the least significant.
! 1326: Functions with refs to other parts of a
! 1327: parameter should not be inline--
! 1328: see function_cannot_inline_p. */
! 1329: #ifdef BYTES_BIG_ENDIAN
! 1330: if (offset + GET_MODE_SIZE (mode)
! 1331: != GET_MODE_SIZE (GET_MODE (copy)))
! 1332: abort ();
! 1333: #else
! 1334: if (offset != 0)
! 1335: abort ();
! 1336: #endif
! 1337: return gen_rtx (SUBREG, mode, copy, 0);
! 1338: }
! 1339:
! 1340: abort ();
1.1.1.10 root 1341: }
1.1 root 1342: return copy;
1343: }
1344: temp = gen_rtx (PLUS, Pmode,
1345: frame_pointer_rtx,
1346: gen_rtx (CONST_INT, SImode,
1347: c + fp_delta));
1348: if (! memory_address_p (Pmode, temp))
1349: return gen_rtx (MEM, mode, plus_constant (inline_fp_rtx, c));
1350: }
1351: copy = copy_rtx_and_substitute (copy);
1352: temp = gen_rtx (PLUS, Pmode, frame_pointer_rtx, copy);
1353: temp = plus_constant (temp, fp_delta);
1354: temp = memory_address (Pmode, temp);
1355: }
1356: else if (reg_mentioned_p (frame_pointer_rtx, copy)
1357: || (ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
1358: && reg_mentioned_p (arg_pointer_rtx, copy)))
1359: {
1360: if (GET_CODE (XEXP (copy, 0)) == CONST_INT)
1361: {
1362: temp = copy_rtx_and_substitute (XEXP (copy, 1));
1363: temp = plus_constant (temp, INTVAL (XEXP (copy, 0)));
1364: }
1365: else if (GET_CODE (XEXP (copy, 1)) == CONST_INT)
1366: {
1367: temp = copy_rtx_and_substitute (XEXP (copy, 0));
1368: temp = plus_constant (temp, INTVAL (XEXP (copy, 1)));
1369: }
1370: else
1371: {
1372: temp = gen_rtx (PLUS, GET_MODE (copy),
1373: copy_rtx_and_substitute (XEXP (copy, 0)),
1374: copy_rtx_and_substitute (XEXP (copy, 1)));
1375: }
1376: }
1377: else
1378: {
1379: if (GET_CODE (XEXP (copy, 1)) == CONST_INT)
1380: temp = plus_constant (copy_rtx_and_substitute (XEXP (copy, 0)),
1381: INTVAL (XEXP (copy, 1)));
1382: else if (GET_CODE (XEXP (copy, 0)) == CONST_INT)
1383: temp = plus_constant (copy_rtx_and_substitute (XEXP (copy, 1)),
1384: INTVAL (XEXP (copy, 0)));
1385: else
1386: {
1387: rtx left = copy_rtx_and_substitute (XEXP (copy, 0));
1388: rtx right = copy_rtx_and_substitute (XEXP (copy, 1));
1389:
1390: temp = gen_rtx (PLUS, GET_MODE (copy), left, right);
1391: }
1392: }
1393: }
1394: else
1395: temp = copy_rtx_and_substitute (copy);
1396:
1397: return change_address (orig, mode, temp);
1398:
1399: case RETURN:
1400: abort ();
1401: }
1402:
1403: copy = rtx_alloc (code);
1404: PUT_MODE (copy, mode);
1405: copy->in_struct = orig->in_struct;
1406: copy->volatil = orig->volatil;
1407: copy->unchanging = orig->unchanging;
1408:
1409: format_ptr = GET_RTX_FORMAT (GET_CODE (copy));
1410:
1411: for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++)
1412: {
1413: switch (*format_ptr++)
1414: {
1415: case '0':
1416: break;
1417:
1418: case 'e':
1419: XEXP (copy, i) = copy_rtx_and_substitute (XEXP (orig, i));
1420: break;
1421:
1.1.1.6 root 1422: case 'u':
1423: /* Change any references to old-insns to point to the
1424: corresponding copied insns. */
1.1.1.9 root 1425: XEXP (copy, i) = insn_map[INSN_UID (XEXP (orig, i))];
1426: break;
1.1.1.6 root 1427:
1.1 root 1428: case 'E':
1429: XVEC (copy, i) = XVEC (orig, i);
1.1.1.6 root 1430: if (XVEC (orig, i) != NULL && XVECLEN (orig, i) != 0)
1.1 root 1431: {
1432: XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i));
1433: for (j = 0; j < XVECLEN (copy, i); j++)
1434: XVECEXP (copy, i, j) = copy_rtx_and_substitute (XVECEXP (orig, i, j));
1435: }
1436: break;
1437:
1438: case 'i':
1439: XINT (copy, i) = XINT (orig, i);
1440: break;
1441:
1442: case 's':
1443: XSTR (copy, i) = XSTR (orig, i);
1444: break;
1445:
1446: default:
1447: abort ();
1448: }
1449: }
1.1.1.11! root 1450:
! 1451: if (code == ASM_OPERANDS && orig_asm_operands_vector == 0)
! 1452: {
! 1453: orig_asm_operands_vector = XVEC (orig, 3);
! 1454: copy_asm_operands_vector = XVEC (copy, 3);
! 1455: }
! 1456:
1.1 root 1457: return copy;
1458: }
1459:
1.1.1.7 root 1460: /* Like copy_rtx_and_substitute but produces different output, suitable
1461: for an ideosyncractic address that isn't memory_address_p.
1462: The output resembles the input except that REGs and MEMs are replaced
1463: with new psuedo registers. All the "real work" is done in separate
1464: insns which set up the values of these new registers. */
1465:
1466: static rtx
1467: copy_address (orig)
1468: register rtx orig;
1469: {
1.1.1.9 root 1470: register rtx copy;
1.1.1.7 root 1471: register int i, j;
1472: register RTX_CODE code;
1473: register enum machine_mode mode;
1474: register char *format_ptr;
1475:
1476: if (orig == 0)
1477: return 0;
1478:
1479: code = GET_CODE (orig);
1480: mode = GET_MODE (orig);
1481:
1482: switch (code)
1483: {
1484: case REG:
1.1.1.9 root 1485: if (REGNO (orig) != FRAME_POINTER_REGNUM)
1486: return copy_rtx_and_substitute (orig);
1487: return plus_constant (frame_pointer_rtx, fp_delta);
1488:
1489: case PLUS:
1490: if (GET_CODE (XEXP (orig, 0)) == REG
1491: && REGNO (XEXP (orig, 0)) == FRAME_POINTER_REGNUM)
1492: return plus_constant (orig, fp_delta);
1493: break;
1494:
1.1.1.7 root 1495: case MEM:
1.1.1.9 root 1496: return copy_to_reg (copy_rtx_and_substitute (orig));
1.1.1.7 root 1497:
1498: case CODE_LABEL:
1499: case LABEL_REF:
1.1.1.9 root 1500: return copy_rtx_and_substitute (orig);
1.1.1.7 root 1501:
1502: case PC:
1503: case CC0:
1504: case CONST_INT:
1505: case CONST_DOUBLE:
1506: case SYMBOL_REF:
1507: return orig;
1508: }
1509:
1510: copy = rtx_alloc (code);
1511: PUT_MODE (copy, mode);
1512: copy->in_struct = orig->in_struct;
1513: copy->volatil = orig->volatil;
1514: copy->unchanging = orig->unchanging;
1515:
1516: format_ptr = GET_RTX_FORMAT (GET_CODE (copy));
1517:
1518: for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++)
1519: {
1520: switch (*format_ptr++)
1521: {
1522: case '0':
1523: break;
1524:
1525: case 'e':
1526: XEXP (copy, i) = copy_rtx_and_substitute (XEXP (orig, i));
1527: break;
1528:
1529: case 'u':
1530: /* Change any references to old-insns to point to the
1531: corresponding copied insns. */
1.1.1.9 root 1532: XEXP (copy, i) = insn_map[INSN_UID (XEXP (orig, i))];
1533: break;
1.1.1.7 root 1534:
1535: case 'E':
1536: XVEC (copy, i) = XVEC (orig, i);
1537: if (XVEC (orig, i) != NULL && XVECLEN (orig, i) != 0)
1538: {
1539: XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i));
1540: for (j = 0; j < XVECLEN (copy, i); j++)
1541: XVECEXP (copy, i, j) = copy_rtx_and_substitute (XVECEXP (orig, i, j));
1542: }
1543: break;
1544:
1545: case 'i':
1546: XINT (copy, i) = XINT (orig, i);
1547: break;
1548:
1549: case 's':
1550: XSTR (copy, i) = XSTR (orig, i);
1551: break;
1552:
1553: default:
1554: abort ();
1555: }
1556: }
1557: return copy;
1558: }
1559:
1.1 root 1560: /* Attempt to simplify INSN while copying it from an inline fn,
1561: assuming it is a SET that sets CC0.
1562:
1563: If we simplify it, we emit the appropriate insns and return
1564: the last insn that we have handled (since we may handle the insn
1565: that follows INSN as well as INSN itself).
1566:
1567: Otherwise we do nothing and return zero. */
1568:
1569: static rtx
1570: try_fold_cc0 (insn)
1571: rtx insn;
1572: {
1573: rtx cnst = copy_rtx_and_substitute (SET_SRC (PATTERN (insn)));
1574: rtx pat, copy;
1575:
1576: if (CONSTANT_P (cnst)
1577: /* @@ Cautious: Don't know how many of these tests we need. */
1578: && NEXT_INSN (insn)
1579: && GET_CODE (pat = PATTERN (NEXT_INSN (insn))) == SET
1580: && SET_DEST (pat) == pc_rtx
1581: && GET_CODE (pat = SET_SRC (pat)) == IF_THEN_ELSE
1582: && GET_RTX_LENGTH (GET_CODE (XEXP (pat, 0))) == 2)
1583: {
1584: rtx cnst2;
1585: rtx cond = XEXP (pat, 0);
1586:
1587: if ((XEXP (cond, 0) == cc0_rtx
1588: && CONSTANT_P (XEXP (cond, 1))
1589: && (cnst2 = XEXP (cond, 1)))
1590: || (XEXP (cond, 1) == cc0_rtx
1591: && CONSTANT_P (XEXP (cond, 0))
1592: && (cnst2 = XEXP (cond, 0))))
1593: {
1594: copy = fold_out_const_cc0 (cond, XEXP (pat, 1), XEXP (pat, 2),
1595: cnst, cnst2);
1596: if (copy)
1597: {
1598: if (GET_CODE (copy) == LABEL_REF)
1599: {
1600: /* We will branch unconditionally to
1601: the label specified by COPY.
1602: Eliminate dead code by running down the
1603: list of insn until we see a CODE_LABEL.
1604: If the CODE_LABEL is the one specified
1605: by COPY, we win, and can delete all code
1606: up to (but not necessarily including)
1607: that label. Otherwise only win a little:
1608: emit the branch insn, and continue expanding. */
1609: rtx tmp = NEXT_INSN (insn);
1610: while (tmp && GET_CODE (tmp) != CODE_LABEL)
1611: tmp = NEXT_INSN (tmp);
1612: if (! tmp)
1613: abort ();
1614: if (label_map[CODE_LABEL_NUMBER (tmp)] == XEXP (copy, 0))
1615: {
1616: /* Big win. */
1617: return PREV_INSN (tmp);
1618: }
1619: else
1620: {
1621: /* Small win. Emit the unconditional branch,
1622: followed by a BARRIER, so that jump optimization
1623: will know what to do. */
1624: emit_jump (copy);
1625: return NEXT_INSN (insn);
1626: }
1627: }
1628: else if (copy == pc_rtx)
1629: {
1630: /* Do not take the branch, just fall through.
1631: Jump optimize should handle the elimination of
1632: dead code if appropriate. */
1633: return NEXT_INSN (insn);
1634: }
1635: else
1636: abort ();
1637: }
1638: }
1639: }
1640: return 0;
1641: }
1642:
1643: /* If (COND_RTX CNST1 CNST2) yield a result we can treat
1644: as being constant, return THEN_RTX if the result is always
1645: non-zero, and return ELSE_RTX otherwise. */
1646: static rtx
1647: fold_out_const_cc0 (cond_rtx, then_rtx, else_rtx, cnst1, cnst2)
1648: rtx cond_rtx, then_rtx, else_rtx;
1649: rtx cnst1, cnst2;
1650: {
1651: int value1, value2;
1652: int int1 = GET_CODE (cnst1) == CONST_INT;
1653: int int2 = GET_CODE (cnst2) == CONST_INT;
1654: if (int1)
1655: value1 = INTVAL (cnst1);
1656: else
1657: value1 = 1;
1658: if (int2)
1659: value2 = INTVAL (cnst2);
1660: else
1661: value2 = 1;
1662:
1663: switch (GET_CODE (cond_rtx))
1664: {
1665: case NE:
1666: if (int1 && int2)
1667: if (value1 != value2)
1668: return copy_rtx_and_substitute (then_rtx);
1669: else
1670: return copy_rtx_and_substitute (else_rtx);
1671: if (value1 == 0 || value2 == 0)
1672: return copy_rtx_and_substitute (then_rtx);
1673: if (int1 == 0 && int2 == 0)
1674: if (rtx_equal_p (cnst1, cnst2))
1675: return copy_rtx_and_substitute (else_rtx);
1676: break;
1677: case EQ:
1678: if (int1 && int2)
1679: if (value1 == value2)
1680: return copy_rtx_and_substitute (then_rtx);
1681: else
1682: return copy_rtx_and_substitute (else_rtx);
1683: if (value1 == 0 || value2 == 0)
1684: return copy_rtx_and_substitute (else_rtx);
1685: if (int1 == 0 && int2 == 0)
1686: if (rtx_equal_p (cnst1, cnst2))
1687: return copy_rtx_and_substitute (then_rtx);
1688: break;
1689: case GE:
1690: if (int1 && int2)
1691: if (value1 >= value2)
1692: return copy_rtx_and_substitute (then_rtx);
1693: else
1694: return copy_rtx_and_substitute (else_rtx);
1695: if (value1 == 0)
1696: return copy_rtx_and_substitute (else_rtx);
1697: if (value2 == 0)
1698: return copy_rtx_and_substitute (then_rtx);
1699: break;
1700: case GT:
1701: if (int1 && int2)
1702: if (value1 > value2)
1703: return copy_rtx_and_substitute (then_rtx);
1704: else
1705: return copy_rtx_and_substitute (else_rtx);
1706: if (value1 == 0)
1707: return copy_rtx_and_substitute (else_rtx);
1708: if (value2 == 0)
1709: return copy_rtx_and_substitute (then_rtx);
1710: break;
1711: case LE:
1712: if (int1 && int2)
1713: if (value1 <= value2)
1714: return copy_rtx_and_substitute (then_rtx);
1715: else
1716: return copy_rtx_and_substitute (else_rtx);
1717: if (value1 == 0)
1718: return copy_rtx_and_substitute (then_rtx);
1719: if (value2 == 0)
1720: return copy_rtx_and_substitute (else_rtx);
1721: break;
1722: case LT:
1723: if (int1 && int2)
1724: if (value1 < value2)
1725: return copy_rtx_and_substitute (then_rtx);
1726: else
1727: return copy_rtx_and_substitute (else_rtx);
1728: if (value1 == 0)
1729: return copy_rtx_and_substitute (then_rtx);
1730: if (value2 == 0)
1731: return copy_rtx_and_substitute (else_rtx);
1732: break;
1733: case GEU:
1734: if (int1 && int2)
1735: if ((unsigned)value1 >= (unsigned)value2)
1736: return copy_rtx_and_substitute (then_rtx);
1737: else
1738: return copy_rtx_and_substitute (else_rtx);
1739: if (value1 == 0)
1740: return copy_rtx_and_substitute (else_rtx);
1741: if (value2 == 0)
1742: return copy_rtx_and_substitute (then_rtx);
1743: break;
1744: case GTU:
1745: if (int1 && int2)
1746: if ((unsigned)value1 > (unsigned)value2)
1747: return copy_rtx_and_substitute (then_rtx);
1748: else
1749: return copy_rtx_and_substitute (else_rtx);
1750: if (value1 == 0)
1751: return copy_rtx_and_substitute (else_rtx);
1752: if (value2 == 0)
1753: return copy_rtx_and_substitute (then_rtx);
1754: break;
1755: case LEU:
1756: if (int1 && int2)
1757: if ((unsigned)value1 <= (unsigned)value2)
1758: return copy_rtx_and_substitute (then_rtx);
1759: else
1760: return copy_rtx_and_substitute (else_rtx);
1761: if (value1 == 0)
1762: return copy_rtx_and_substitute (then_rtx);
1763: if (value2 == 0)
1764: return copy_rtx_and_substitute (else_rtx);
1765: break;
1766: case LTU:
1767: if (int1 && int2)
1768: if ((unsigned)value1 < (unsigned)value2)
1769: return copy_rtx_and_substitute (then_rtx);
1770: else
1771: return copy_rtx_and_substitute (else_rtx);
1772: if (value1 == 0)
1773: return copy_rtx_and_substitute (then_rtx);
1774: if (value2 == 0)
1775: return copy_rtx_and_substitute (else_rtx);
1776: break;
1777: }
1778: /* Could not hack it. */
1779: return 0;
1780: }
1781:
1782: /* Output the assembly language code for the function FNDECL
1783: from its DECL_SAVED_INSNS. Used for inline functions that are output
1784: at end of compilation instead of where they came in the source. */
1785:
1786: void
1787: output_inline_function (fndecl)
1788: tree fndecl;
1789: {
1790: rtx head = DECL_SAVED_INSNS (fndecl);
1791: rtx last;
1792:
1793: temporary_allocation ();
1794:
1.1.1.4 root 1795: current_function_decl = fndecl;
1796:
1.1 root 1797: /* This call is only used to initialize global variables.
1798: The rtl code it emits will be discarded below. */
1799: expand_function_start (fndecl);
1800:
1801: /* Set stack frame size. */
1802: assign_stack_local (BLKmode, DECL_FRAME_SIZE (fndecl));
1803:
1804: restore_reg_data (FIRST_PARM_INSN (head));
1805:
1.1.1.10 root 1806: expand_function_end (DECL_SOURCE_FILE (fndecl), DECL_SOURCE_LINE (fndecl));
1.1 root 1807:
1808: for (last = head; NEXT_INSN (last); last = NEXT_INSN (last))
1809: ;
1810:
1811: set_new_first_and_last_insn (FIRST_PARM_INSN (head), last);
1812:
1813: /* Compile this function all the way down to assembly code. */
1814: rest_of_compilation (fndecl);
1815:
1.1.1.4 root 1816: current_function_decl = 0;
1817:
1.1 root 1818: permanent_allocation ();
1819: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.