diff options
Diffstat (limited to 'gcc-1.40/integrate.c')
| -rw-r--r-- | gcc-1.40/integrate.c | 2049 |
1 files changed, 2049 insertions, 0 deletions
diff --git a/gcc-1.40/integrate.c b/gcc-1.40/integrate.c new file mode 100644 index 0000000..ed7a2cd --- /dev/null +++ b/gcc-1.40/integrate.c @@ -0,0 +1,2049 @@ +/* Procedure integration for GNU CC. + Copyright (C) 1988 Free Software Foundation, Inc. + Contributed by Michael Tiemann (tiemann@mcc.com) + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#include <stdio.h> + +#include "config.h" +#include "rtl.h" +#include "tree.h" +#include "flags.h" +#include "insn-flags.h" +#include "expr.h" + +#include "obstack.h" +#define obstack_chunk_alloc xmalloc +#define obstack_chunk_free free +extern int xmalloc (); +extern void free (); + +extern struct obstack permanent_obstack, maybepermanent_obstack; +extern struct obstack *rtl_obstack, *saveable_obstack, *current_obstack; + +extern rtx stack_slot_list; + +#define MIN(x,y) ((x < y) ? x : y) + +extern tree pushdecl (); +extern tree poplevel (); + +/* Default max number of insns a function can have and still be inline. + This is overridden on RISC machines. */ +#ifndef INTEGRATE_THRESHOLD +#define INTEGRATE_THRESHOLD(DECL) \ + (8 * (8 + list_length (DECL_ARGUMENTS (DECL)))) +#endif + +/* This is the target of the inline function being expanded, + or NULL if there is none. */ +static rtx inline_target; + +/* We must take special care not to disrupt life too severely + when performing procedure integration. One thing that that + involves is not creating illegitimate address which reload + cannot fix. Since we don't know what the frame pointer is + not capable of (in a machine independent way), we create + a pseudo-frame pointer which will have to do for now. */ +static rtx inline_fp_rtx; + +/* Convert old frame-pointer offsets to new. Parameters which only + produce values (no addresses, and are never assigned), map directly + to the pseudo-reg of the incoming value. Parameters that are + assigned to but do not have their address taken are given a fresh + pseudo-register. Parameters that have their address take are + given a fresh stack-slot. */ +static rtx *parm_map; + +/* This is used to prevent looking beyond parm_map. */ +static int parm_map_size; + +/* ?? Should this be done here?? It is not right now. + Keep track of whether a given pseudo-register is the sum + of the frame pointer and a const_int (or zero). */ +static char *fp_addr_p; + +/* For the local variables of the procdure being integrated that live + on the frame, FRAME_POINTER_DELTA says how much to change their + offsets by, so that they now live in the correct place on the + frame of the function being compiled. */ +static int fp_delta; + +/* When an insn is being copied by copy_rtx_and_substitute, + this is nonzero if we have copied an ASM_OPERANDS. + In that case, it is the original input-operand vector. + Likewise in copy_for_inline. */ +static rtvec orig_asm_operands_vector; + +/* When an insn is being copied by copy_rtx_and_substitute, + this is nonzero if we have copied an ASM_OPERANDS. + In that case, it is the copied input-operand vector. + Likewise in copy_for_inline. */ +static rtvec copy_asm_operands_vector; + +/* Likewise, this is the copied constraints vector. */ +static rtvec copy_asm_constraints_vector; + +/* Return a copy of an rtx (as needed), substituting pseudo-register, + labels, and frame-pointer offsets as necessary. */ +static rtx copy_rtx_and_substitute (); +/* Variant, used for memory addresses that are not memory_address_p. */ +static rtx copy_address (); + +/* Return the rtx corresponding to a given index in the stack arguments. */ +static rtx access_parm_map (); + +static void copy_parm_decls (); +static void copy_decl_tree (); +static int frame_pointer_sum_p (); +static rtx try_fold_cc0 (); + +/* We do some simple constant folding optimization. This optimization + really exists primarily to save time inlining a function. It + also helps users who ask for inline functions without -O. */ +static rtx fold_out_const_cc0 (); + +/* Zero if the current function (whose FUNCTION_DECL is FNDECL) + is safe and reasonable to integrate into other functions. + Nonzero means value is a warning message with a single %s + for the function's name. */ + +char * +function_cannot_inline_p (fndecl) + register tree fndecl; +{ + register rtx insn; + tree last = tree_last (TYPE_ARG_TYPES (TREE_TYPE (fndecl))); + int max_insns = INTEGRATE_THRESHOLD (fndecl); + register int ninsns = 0; + register tree parms; + + /* No inlines with varargs. `grokdeclarator' gives a warning + message about that if `inline' is specified. This code + it put in to catch the volunteers. */ + if (last && TREE_VALUE (last) != void_type_node) + return "varargs function cannot be inline"; + + if (current_function_calls_alloca) + return "function using alloca cannot be inline"; + + /* If its not even close, don't even look. */ + if (!TREE_INLINE (fndecl) && get_max_uid () > 3 * max_insns) + return "function too large to be inline"; + + /* We can't inline functions that return structures + the old-fashioned PCC way, copying into a static block. */ +#ifdef PCC_STATIC_STRUCT_RETURN + if (flag_pcc_struct_return + && (TYPE_MODE (TREE_TYPE (TREE_TYPE (fndecl))) == BLKmode + || RETURN_IN_MEMORY (TREE_TYPE (TREE_TYPE (fndecl))))) + return "inline functions not supported for this return value type"; +#endif + + /* Don't inline functions which have BLKmode arguments. + Don't inline functions that take the address of + a parameter and do not specify a function prototype. */ + for (parms = DECL_ARGUMENTS (fndecl); parms; parms = TREE_CHAIN (parms)) + { + if (TYPE_MODE (TREE_TYPE (parms)) == BLKmode) + return "function with large aggregate parameter cannot be inline"; + if (last == NULL_TREE && TREE_ADDRESSABLE (parms)) + return "no prototype, and parameter address used; cannot be inline"; + /* If an aggregate is thought of as "in memory" + then its components are referred to by narrower memory refs. + If the actual parameter is a reg, these refs can't be translated, + esp. since copy_rtx_and_substitute doesn't know whether it is + reading or writing. */ + if ((TREE_CODE (TREE_TYPE (parms)) == RECORD_TYPE + || TREE_CODE (TREE_TYPE (parms)) == UNION_TYPE) + && GET_CODE (DECL_RTL (parms)) == MEM) + return "address of an aggregate parameter is used; cannot be inline"; + } + + if (!TREE_INLINE (fndecl) && get_max_uid () > max_insns) + { + for (ninsns = 0, insn = get_first_nonparm_insn (); insn && ninsns < max_insns; + insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) == INSN + || GET_CODE (insn) == JUMP_INSN + || GET_CODE (insn) == CALL_INSN) + ninsns++; + } + + if (ninsns >= max_insns) + return "function too large to be inline"; + } + + return 0; +} + +/* Variables used within save_for_inline. */ + +/* Mapping from old pesudo-register to new pseudo-registers. + The first element of this map is reg_map[FIRST_PSEUDO_REGISTER]. + It is allocated in `save_for_inline' and `expand_inline_function', + and deallocated on exit from each of those routines. */ +static rtx *reg_map; + +/* Mapping from old code-labels to new code-labels. + The first element of this map is label_map[min_labelno]. + It is allocated in `save_for_inline' and `expand_inline_function', + and deallocated on exit from each of those routines. */ +static rtx *label_map; + +/* Mapping from old insn uid's to copied insns. + It is allocated in `save_for_inline' and `expand_inline_function', + and deallocated on exit from each of those routines. */ +static rtx *insn_map; + +/* Map pseudo reg number into the PARM_DECL for the parm living in the reg. + Zero for a reg that isn't a parm's home. + Only reg numbers less than max_parm_reg are mapped here. */ +static tree *parmdecl_map; + +/* Keep track of first pseudo-register beyond those that are parms. */ +static int max_parm_reg; + +/* Offset from arg ptr to the first parm of this inline function. */ +static int first_parm_offset; + +/* On machines that perform a function return with a single + instruction, such as the VAX, these return insns must be + mapped into branch statements. */ +extern rtx return_label; + +/* Copy an rtx for save_for_inline. */ +static rtx copy_for_inline (); + +/* Make the insns and PARM_DECLs of the current function permanent + and record other information in DECL_SAVED_INSNS to allow inlining + of this function in subsequent calls. */ + +void +save_for_inline (fndecl) + tree fndecl; +{ + extern rtx *regno_reg_rtx; /* in emit-rtl.c. */ + extern current_function_args_size; + + rtx first_insn, last_insn, insn; + rtx head, copy; + tree parms; + int max_labelno, min_labelno, i, len; + int max_reg; + int max_uid; + + /* Make and emit a return-label if we have not already done so. */ + + if (return_label == 0) + { + return_label = gen_label_rtx (); + emit_label (return_label); + } + + /* Get some bounds on the labels and registers used. */ + + max_labelno = max_label_num (); + min_labelno = get_first_label_num (); + max_parm_reg = max_parm_reg_num (); + max_reg = max_reg_num (); + + /* Set up PARMDECL_MAP which maps pseudo-reg number to its PARM_DECL. + + Set TREE_VOLATILE to 0 if the parm is in a register, otherwise 1. + Later we set TREE_READONLY to 0 if the parm is modified inside the fn. */ + + parmdecl_map = (tree *) alloca (max_parm_reg * sizeof (tree)); + bzero (parmdecl_map, max_parm_reg * sizeof (tree)); + + for (parms = DECL_ARGUMENTS (fndecl); parms; parms = TREE_CHAIN (parms)) + { + rtx p = DECL_RTL (parms); + + if (GET_CODE (p) == REG) + { + parmdecl_map[REGNO (p)] = parms; + TREE_VOLATILE (parms) = 0; + } + else + TREE_VOLATILE (parms) = 1; + TREE_READONLY (parms) = 1; + } + + /* The list of DECL_SAVES_INSNS, starts off with a header which + contains the following information: + + the first insn of the function (not including the insns that copy + parameters into registers). + the first label used by that function, + the last label used by that function, + and the total number of registers used. */ + + head = gen_inline_header_rtx (NULL, NULL, min_labelno, max_labelno, + max_parm_reg, max_reg, + current_function_args_size, stack_slot_list); + max_uid = INSN_UID (head); + + /* We have now allocated all that needs to be allocated permanently + on the rtx obstack. Set our high-water mark, so that we + can free the rest of this when the time comes. */ + + preserve_data (); + + /* Copy the chain insns of this function. + Install the copied chain as the insns of this function, + for continued compilation; + the original chain is recorded as the DECL_SAVED_INSNS + for inlining future calls. */ + + /* If there are insns that copy parms from the stack into pseudo registers, + those insns are not copied. `expand_inline_function' must + emit the correct code to handle such things. */ + + insn = get_insns (); + if (GET_CODE (insn) != NOTE) + abort (); + first_insn = rtx_alloc (NOTE); + NOTE_SOURCE_FILE (first_insn) = NOTE_SOURCE_FILE (insn); + NOTE_LINE_NUMBER (first_insn) = NOTE_LINE_NUMBER (insn); + INSN_UID (first_insn) = INSN_UID (insn); + PREV_INSN (first_insn) = NULL; + NEXT_INSN (first_insn) = NULL; + last_insn = first_insn; + + /* Each pseudo-reg in the old insn chain must have a unique rtx in the copy. + Make these new rtx's now, and install them in regno_reg_rtx, so they + will be the official pseudo-reg rtx's for the rest of compilation. */ + + reg_map = (rtx *) alloca ((max_reg + 1) * sizeof (rtx)); + + len = sizeof (struct rtx_def) + (GET_RTX_LENGTH (REG) - 1) * sizeof (rtunion); + for (i = max_reg - 1; i >= FIRST_PSEUDO_REGISTER; i--) + reg_map[i] = (rtx)obstack_copy (&maybepermanent_obstack, regno_reg_rtx[i], len); + bcopy (reg_map + FIRST_PSEUDO_REGISTER, + regno_reg_rtx + FIRST_PSEUDO_REGISTER, + (max_reg - FIRST_PSEUDO_REGISTER) * sizeof (rtx)); + + /* Likewise each label rtx must have a unique rtx as its copy. */ + + label_map = (rtx *)alloca ((max_labelno - min_labelno) * sizeof (rtx)); + label_map -= min_labelno; + + for (i = min_labelno; i < max_labelno; i++) + label_map[i] = gen_label_rtx (); + + /* Record the mapping of old insns to copied insns. */ + + insn_map = (rtx *) alloca (max_uid * sizeof (rtx)); + bzero (insn_map, max_uid * sizeof (rtx)); + + /* Now copy the chain of insns. */ + + for (insn = NEXT_INSN (insn); insn; insn = NEXT_INSN (insn)) + { + orig_asm_operands_vector = 0; + copy_asm_operands_vector = 0; + + switch (GET_CODE (insn)) + { + case NOTE: + /* No need to keep these. */ + if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED) + continue; + + copy = rtx_alloc (NOTE); + NOTE_SOURCE_FILE (copy) = NOTE_SOURCE_FILE (insn); + NOTE_LINE_NUMBER (copy) = NOTE_LINE_NUMBER (insn); + break; + + case INSN: + case CALL_INSN: + case JUMP_INSN: + copy = rtx_alloc (GET_CODE (insn)); + PATTERN (copy) = copy_for_inline (PATTERN (insn)); + INSN_CODE (copy) = -1; + LOG_LINKS (copy) = NULL; + RTX_INTEGRATED_P (copy) = RTX_INTEGRATED_P (insn); + break; + + case CODE_LABEL: + copy = label_map[CODE_LABEL_NUMBER (insn)]; + break; + + case BARRIER: + copy = rtx_alloc (BARRIER); + break; + + default: + abort (); + } + INSN_UID (copy) = INSN_UID (insn); + insn_map[INSN_UID (insn)] = copy; + NEXT_INSN (last_insn) = copy; + PREV_INSN (copy) = last_insn; + last_insn = copy; + } + + /* Now copy the reg notes of the insns. + Do this now because there can be forward references. */ + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN + || GET_CODE (insn) == CALL_INSN) + { + rtx copy = insn_map[INSN_UID (insn)]; + REG_NOTES (copy) = copy_for_inline (REG_NOTES (insn)); + } + + NEXT_INSN (last_insn) = NULL; + + NEXT_INSN (head) = get_first_nonparm_insn (); + FIRST_PARM_INSN (head) = get_insns (); + DECL_SAVED_INSNS (fndecl) = head; + DECL_FRAME_SIZE (fndecl) = get_frame_size (); + TREE_INLINE (fndecl) = 1; + + parmdecl_map = 0; + label_map = 0; + reg_map = 0; + return_label = 0; + + set_new_first_and_last_insn (first_insn, last_insn); +} + +/* Copy the rtx ORIG recursively, replacing pseudo-regs and labels + according to `reg_map' and `label_map'. + All other kinds of rtx are copied except those that can never be + changed during compilation. */ + +static rtx +copy_for_inline (orig) + rtx orig; +{ + register rtx x = orig; + register int i; + register enum rtx_code code; + register char *format_ptr; + + if (x == 0) + return x; + + code = GET_CODE (x); + + /* These types may be freely shared. */ + + switch (code) + { + case QUEUED: + case CONST_INT: + case CONST_DOUBLE: + case SYMBOL_REF: + case PC: + case CC0: + return x; + + case ASM_OPERANDS: + /* If a single asm insn contains multiple output operands + then it contains multiple ASM_OPERANDS rtx's that share operand 3. + We must make sure that the copied insn continues to share it. */ + if (orig_asm_operands_vector == XVEC (orig, 3)) + { + x = rtx_alloc (ASM_OPERANDS); + XSTR (x, 0) = XSTR (orig, 0); + XSTR (x, 1) = XSTR (orig, 1); + XINT (x, 2) = XINT (orig, 2); + XVEC (x, 3) = copy_asm_operands_vector; + XVEC (x, 4) = copy_asm_constraints_vector; + XSTR (x, 5) = XSTR (orig, 5); + XINT (x, 6) = XINT (orig, 6); + return x; + } + break; + + case MEM: + /* A MEM is allowed to be shared if its address is constant + or is a constant plus one of the special registers. */ + if (CONSTANT_ADDRESS_P (XEXP (x, 0))) + return x; +#if 0 /* This is turned off because it is possible for + unshare_all_rtl to copy the address, into memory that won't be saved. + Although the MEM can safely be shared, and won't be copied there, + the address itself cannot be shared, and may need to be copied. */ + if (GET_CODE (XEXP (x, 0)) == PLUS + && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG + && (REGNO (XEXP (XEXP (x, 0), 0)) == FRAME_POINTER_REGNUM + || REGNO (XEXP (XEXP (x, 0), 0)) == ARG_POINTER_REGNUM) + && CONSTANT_ADDRESS_P (XEXP (XEXP (x, 0), 1))) +#if 0 + /* This statement was accidentally deleted in the remote past. + Reinsert it for 1.37. Don't take the risk now. */ + return x; +#endif + if (GET_CODE (XEXP (x, 0)) == REG + && (REGNO (XEXP (x, 0)) == FRAME_POINTER_REGNUM + || REGNO (XEXP (x, 0)) == ARG_POINTER_REGNUM) + && CONSTANT_ADDRESS_P (XEXP (x, 1))) + return x; +#endif /* 0 */ + break; + + case LABEL_REF: + { + /* Must point to the new insn. */ + return gen_rtx (LABEL_REF, GET_MODE (orig), + label_map[CODE_LABEL_NUMBER (XEXP (orig, 0))]); + } + + case REG: + if (REGNO (x) >= FIRST_PSEUDO_REGISTER) + return reg_map [REGNO (x)]; + else + return x; + + /* If a parm that gets modified lives in a pseudo-reg, + set its TREE_VOLATILE to prevent certain optimizations. */ + case SET: + { + rtx dest = SET_DEST (x); + + if (GET_CODE (dest) == REG + && REGNO (dest) < max_parm_reg + && REGNO (dest) >= FIRST_PSEUDO_REGISTER + && parmdecl_map[REGNO (dest)] != 0) + TREE_READONLY (parmdecl_map[REGNO (dest)]) = 0; + } + break; + } + + /* Replace this rtx with a copy of itself. */ + + x = rtx_alloc (code); + bcopy (orig, x, (sizeof (*x) - sizeof (x->fld) + + sizeof (x->fld[0]) * GET_RTX_LENGTH (code))); + + /* Now scan the subexpressions recursively. + We can store any replaced subexpressions directly into X + since we know X is not shared! Any vectors in X + must be copied if X was copied. */ + + format_ptr = GET_RTX_FORMAT (code); + + for (i = 0; i < GET_RTX_LENGTH (code); i++) + { + switch (*format_ptr++) + { + case 'e': + XEXP (x, i) = copy_for_inline (XEXP (x, i)); + break; + + case 'u': + /* Change any references to old-insns to point to the + corresponding copied insns. */ + XEXP (x, i) = insn_map[INSN_UID (XEXP (x, i))]; + break; + + case 'E': + if (XVEC (x, i) != NULL && XVECLEN (x, i) != 0) + { + register int j; + + XVEC (x, i) = gen_rtvec_v (XVECLEN (x, i), &XVECEXP (x, i, 0)); + for (j = 0; j < XVECLEN (x, i); j++) + XVECEXP (x, i, j) + = copy_for_inline (XVECEXP (x, i, j)); + } + break; + } + } + + if (code == ASM_OPERANDS && orig_asm_operands_vector == 0) + { + orig_asm_operands_vector = XVEC (orig, 3); + copy_asm_operands_vector = XVEC (x, 3); + copy_asm_constraints_vector = XVEC (x, 4); + } + + return x; +} + +/* Integrate the procedure defined by FNDECL. Note that this function + may wind up calling itself. Since the static variables are not + reentrant, we do not assign them until after the possibility + or recursion is eliminated. + + If IGNORE is nonzero, do not produce a value. + Otherwise store the value in TARGET if it is nonzero and that is convenient. + + Value is: + (rtx)-1 if we could not substitute the function + 0 if we substituted it and it does not produce a value + else an rtx for where the value is stored. */ + +rtx +expand_inline_function (fndecl, parms, target, ignore, type, structure_value_addr) + tree fndecl, parms; + rtx target; + int ignore; + tree type; + rtx structure_value_addr; +{ + tree formal, actual; + rtx header = DECL_SAVED_INSNS (fndecl); + rtx insns = FIRST_FUNCTION_INSN (header); + rtx parm_insns = FIRST_PARM_INSN (header); + rtx insn; + int max_regno = MAX_REGNUM (header) + 1; + register int i; + int min_labelno = FIRST_LABELNO (header); + int max_labelno = LAST_LABELNO (header); + int nargs; + rtx *arg_vec; + rtx local_return_label = 0; + rtx follows_call = 0; + rtx this_struct_value_rtx = 0; + /* List of tree_list nodes with parm as purpose and its index as value. */ + tree must_load_parms = 0; + + if (max_regno < FIRST_PSEUDO_REGISTER) + abort (); + + nargs = list_length (DECL_ARGUMENTS (fndecl)); + + /* We expect PARMS to have the right length; don't crash if not. */ + if (list_length (parms) != nargs) + return (rtx)-1; + /* Also check that the parms type match. Since the appropriate + conversions or default promotions have already been applied, + the machine modes should match exactly. */ + for (formal = DECL_ARGUMENTS (fndecl), + actual = parms; + formal; + formal = TREE_CHAIN (formal), + actual = TREE_CHAIN (actual)) + { + tree arg = TREE_VALUE (actual); + enum machine_mode mode = TYPE_MODE (DECL_ARG_TYPE (formal)); + if (mode != TYPE_MODE (TREE_TYPE (arg))) + return (rtx)-1; + /* If they are block mode, the types should match exactly. */ + if (mode == BLKmode && TREE_TYPE (arg) != TREE_TYPE (formal)) + return (rtx)-1; + } + + /* Make a binding contour to keep inline cleanups called at + outer function-scope level from looking like they are shadowing + parameter declarations. */ + pushlevel (0); + + /* Make a fresh binding contour that we can easily remove. */ + pushlevel (0); + expand_start_bindings (0); + if (GET_CODE (parm_insns) == NOTE + && NOTE_LINE_NUMBER (parm_insns) < 0) + emit_note (NOTE_SOURCE_FILE (parm_insns), NOTE_LINE_NUMBER (parm_insns)); + + /* Get all the actual args as RTL, and store them in ARG_VEC. */ + + arg_vec = (rtx *)alloca (nargs * sizeof (rtx)); + + for (formal = DECL_ARGUMENTS (fndecl), + actual = parms, + i = 0; + formal; + formal = TREE_CHAIN (formal), + actual = TREE_CHAIN (actual), + i++) + { + /* Actual parameter, already converted to DECL_ARG_TYPE (formal). */ + tree arg = TREE_VALUE (actual); + /* Mode of the value supplied. */ + enum machine_mode tmode = TYPE_MODE (DECL_ARG_TYPE (formal)); + /* Mode of the variable used within the function. */ + enum machine_mode imode = TYPE_MODE (TREE_TYPE (formal)); + rtx copy; + + emit_note (DECL_SOURCE_FILE (formal), DECL_SOURCE_LINE (formal)); + + /* Make a place to hold the argument value, still in mode TMODE, + and put it in COPY. */ + if (TREE_ADDRESSABLE (formal)) + { + int size = int_size_in_bytes (DECL_ARG_TYPE (formal)); + copy = assign_stack_local (tmode, size); + if (!memory_address_p (DECL_MODE (formal), XEXP (copy, 0))) + copy = change_address (copy, VOIDmode, copy_rtx (XEXP (copy, 0))); + store_expr (arg, copy, 0); + } + else if (! TREE_READONLY (formal) + || TREE_VOLATILE (formal)) + { + /* If parm is modified or if it hasn't a pseudo reg, + we may not simply substitute the actual value; + copy it through a register. */ + copy = gen_reg_rtx (tmode); + REG_USERVAR_P (copy) = 1; + store_expr (arg, copy, 0); + } + else + { + copy = expand_expr (arg, 0, tmode, 0); + + /* We do not use CONSTANT_ADDRESS_P here because + the set of cases where that might make a difference + are a subset of the cases that arise even when + it is a CONSTANT_ADDRESS_P (i.e., fp_delta + gets into the act. */ + if (GET_CODE (copy) != REG && ! CONSTANT_P (copy)) + copy = copy_to_reg (copy); + } + /* If passed mode != nominal mode, COPY is now the passed mode. + Convert it to the nominal mode (i.e. truncate it). */ + if (tmode != imode) + copy = convert_to_mode (imode, copy, 0); + arg_vec[i] = copy; + } + + copy_parm_decls (DECL_ARGUMENTS (fndecl), arg_vec); + + /* Perform postincrements before actually calling the function. */ + emit_queue (); + + /* clean up stack so that variables might have smaller offsets. */ + do_pending_stack_adjust (); + + /* Pass the function the address in which to return a structure value. */ + if (structure_value_addr) + { + if (GET_CODE (structure_value_addr) == REG + && (struct_value_rtx == 0 || GET_CODE (struct_value_rtx) == MEM)) + this_struct_value_rtx = structure_value_addr; + else + this_struct_value_rtx = copy_to_mode_reg (Pmode, structure_value_addr); + } + + /* Now prepare for copying the insns. + Set up reg_map, parm_map and label_map saying how to translate + the pseudo-registers, stack-parm references and labels when copying. */ + + reg_map = (rtx *) alloca (max_regno * sizeof (rtx)); + bzero (reg_map, max_regno * sizeof (rtx)); + + parm_map_size = (FUNCTION_ARGS_SIZE (header) + UNITS_PER_WORD - 1) + / UNITS_PER_WORD; + parm_map = (rtx *)alloca (parm_map_size * sizeof (rtx)); + bzero (parm_map, (parm_map_size * sizeof (rtx))); + + /* Note that expand_expr (called above) can clobber first_parm_offset. */ + first_parm_offset = FIRST_PARM_OFFSET (fndecl); + parm_map -= first_parm_offset / UNITS_PER_WORD; + parm_map_size += first_parm_offset / UNITS_PER_WORD; + + if (DECL_ARGUMENTS (fndecl)) + { + tree decl = DECL_ARGUMENTS (fndecl); + + for (formal = decl, i = 0; formal; formal = TREE_CHAIN (formal), i++) + { + /* Create an entry in PARM_MAP that says what pseudo register + is associated with an address we might compute. */ + if (DECL_OFFSET (formal) >= 0) + { + /* This parameter has a home in the stack. */ + parm_map[DECL_OFFSET (formal) / BITS_PER_WORD] = arg_vec[i]; + } + else + { + /* Parameter that was passed in a register; + does it have a home on the stack (as a local)? */ + rtx frtx = DECL_RTL (formal); + rtx offset = 0; + if (GET_CODE (frtx) == MEM) + { + frtx = XEXP (frtx, 0); + if (GET_CODE (frtx) == PLUS) + { + if (XEXP (frtx, 0) == frame_pointer_rtx + && GET_CODE (XEXP (frtx, 1)) == CONST_INT) + offset = XEXP (frtx, 1); + else if (XEXP (frtx, 1) == frame_pointer_rtx + && GET_CODE (XEXP (frtx, 0)) == CONST_INT) + offset = XEXP (frtx, 0); +#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM + /* If there is a separate arg pointer + and REG_PARM_STACK_SPACE is defined, + parms passed in regs can be copied + to slots reached via the arg pointer. */ + if (XEXP (frtx, 0) == arg_pointer_rtx + && GET_CODE (XEXP (frtx, 1)) == CONST_INT) + offset = XEXP (frtx, 1); + else if (XEXP (frtx, 1) == arg_pointer_rtx + && GET_CODE (XEXP (frtx, 0)) == CONST_INT) + offset = XEXP (frtx, 0); +#endif + } + if (offset && INTVAL (offset) >= first_parm_offset) + parm_map[INTVAL (offset) / UNITS_PER_WORD] = arg_vec[i]; + else if (offset) + must_load_parms + = tree_cons (formal, build_int_2 (i, 0), + must_load_parms); + else if (TREE_TYPE (formal) != error_mark_node) + abort (); + } + else if (GET_CODE (frtx) != REG) + abort (); + } + /* Create an entry in REG_MAP that says what rtx is associated + with a pseudo register from the function being inlined. */ + if (GET_CODE (DECL_RTL (formal)) == REG) + reg_map[REGNO (DECL_RTL (formal))] = arg_vec[i]; + } + } + +#if 0 /* This was turned off when it was written, + because expand_call was changed not to need it. */ + /* Handle the case where our caller offers a register target + but the called function wants to return the value in memory. */ + if (this_struct_value_rtx == 0 + && aggregate_value_p (DECL_RESULT (fndecl))) + { + enum machine_mode mode1 = GET_MODE (DECL_RTL (DECL_RESULT (fndecl))); + this_struct_value_rtx + = assign_stack_local (mode1, GET_MODE_SIZE (mode1)); + target = 0; + } +#endif + + /* Make certain that we can accept struct_value_{incoming_rtx,rtx}, + and map it. */ + if (this_struct_value_rtx == 0) + ; + else if (GET_CODE (struct_value_incoming_rtx) == REG) + reg_map[REGNO (XEXP (DECL_RTL (DECL_RESULT (fndecl)), 0))] + = this_struct_value_rtx; + else if (GET_CODE (struct_value_incoming_rtx) == MEM + && XEXP (XEXP (struct_value_incoming_rtx, 0), 0) == frame_pointer_rtx + && GET_CODE (XEXP (XEXP (struct_value_incoming_rtx, 0), 1)) == CONST_INT) + reg_map[REGNO (XEXP (DECL_RTL (DECL_RESULT (fndecl)), 0))] + = this_struct_value_rtx; +#if 0 + parm_map[INTVAL (XEXP (XEXP (struct_value_incoming_rtx, 0), 1)) / UNITS_PER_WORD] + = this_struct_value_rtx; +#endif + else + abort (); + + label_map = (rtx *)alloca ((max_labelno - min_labelno) * sizeof (rtx)); + label_map -= min_labelno; + + for (i = min_labelno; i < max_labelno; i++) + label_map[i] = gen_label_rtx (); + + /* As we copy insns, record the correspondence, so that inter-insn + references can be copied into isomorphic structure. */ + + insn_map = (rtx *) alloca (INSN_UID (header) * sizeof (rtx)); + bzero (insn_map, INSN_UID (header) * sizeof (rtx)); + + /* Set up a target to translate the inline function's value-register. */ + + if (this_struct_value_rtx != 0 || TYPE_MODE (type) == VOIDmode) + inline_target = 0; + else + { + /* Machine mode function was declared to return. */ + enum machine_mode departing_mode = TYPE_MODE (type); + /* (Possibly wider) machine mode it actually computes + (for the sake of callers that fail to declare it right). */ + enum machine_mode arriving_mode + = TYPE_MODE (DECL_RESULT_TYPE (fndecl)); + + /* Don't use MEMs as direct targets because on some machines + substituting a MEM for a REG makes invalid insns. + Let the combiner substitute the MEM if that is valid. */ + if (target && GET_CODE (target) == REG + && GET_MODE (target) == departing_mode) + inline_target = target; + else + inline_target = target = gen_reg_rtx (departing_mode); + + /* If function's value was promoted before return, + avoid machine mode mismatch when we substitute INLINE_TARGET. + But TARGET is what we will return to the caller. */ + if (arriving_mode != departing_mode) + inline_target = gen_rtx (SUBREG, arriving_mode, target, 0); + } + + /* Make space in current function's stack frame + for the stack frame of the inline function. + Adjust all frame-pointer references by the difference + between the offset to this space + and the offset to the equivalent space in the inline + function's frame. + This difference equals the size of preexisting locals. */ + + fp_delta = get_frame_size (); +#ifdef FRAME_GROWS_DOWNWARD + fp_delta = - fp_delta; +#endif + + inline_fp_rtx + = copy_to_mode_reg (Pmode, + plus_constant (frame_pointer_rtx, fp_delta)); + + /* Now allocate the space for that to point at. */ + + assign_stack_local (VOIDmode, DECL_FRAME_SIZE (fndecl)); + + /* Load any parms represented as locals with the supplied values. + We couldn't do this above where the other parms' values are handled + because we need fp_delta to do it right. */ + while (must_load_parms) + { + rtx dest = DECL_RTL (TREE_PURPOSE (must_load_parms)); + int parm_num = TREE_INT_CST_LOW (TREE_VALUE (must_load_parms)); + emit_insn (gen_move_insn (copy_rtx_and_substitute (dest), + arg_vec[parm_num])); + must_load_parms = TREE_CHAIN (must_load_parms); + } + + /* Now copy the insns one by one. */ + + for (insn = insns; insn; insn = NEXT_INSN (insn)) + { + rtx copy, pattern, next = 0; + + orig_asm_operands_vector = 0; + copy_asm_operands_vector = 0; + + switch (GET_CODE (insn)) + { + case INSN: + pattern = PATTERN (insn); + + /* Special handling for the insn immediately after a CALL_INSN + that returned a value: + If it does copy the value, we must avoid the usual translation + of the return-register into INLINE_TARGET. + If it just USEs the value, the inline function expects it to + stay in the return-register and be returned, + so copy it into INLINE_TARGET. */ + + if (follows_call + /* Allow a stack-adjust, handled normally, to come in between + the call and the value-copying insn. */ + && ! (GET_CODE (pattern) == SET + && SET_DEST (pattern) == stack_pointer_rtx)) + { + if (GET_CODE (pattern) == SET + && rtx_equal_p (SET_SRC (pattern), follows_call)) + /* This insn copies the value: take special care to copy + that value to this insn's destination. */ + { + copy = emit_insn (gen_rtx (SET, VOIDmode, + copy_rtx_and_substitute (SET_DEST (pattern)), + follows_call)); + RTX_INTEGRATED_P (copy) = 1; + follows_call = 0; + break; + } + else if (GET_CODE (pattern) == USE + && rtx_equal_p (XEXP (pattern, 0), follows_call)) + /* This insn does nothing but says the value is expected + to flow through to the inline function's return-value. + Make that happen, then ignore this insn. */ + { + copy = emit_insn (gen_rtx (SET, VOIDmode, inline_target, + follows_call)); + RTX_INTEGRATED_P (copy) = 1; + follows_call = 0; + break; + } + /* If it does neither, this value must be ignored. */ + follows_call = 0; + } + + /* The (USE (REG n)) at return from the function should be ignored + since we are changing (REG n) into inline_target. */ + copy = 0; + if (GET_CODE (pattern) == USE + && GET_CODE (XEXP (pattern, 0)) == REG + && REG_FUNCTION_VALUE_P (XEXP (pattern, 0))) + break; + /* Ignore setting a function value that we don't want to use. */ + if (inline_target == 0 + && GET_CODE (pattern) == SET + && GET_CODE (SET_DEST (pattern)) == REG + && REG_FUNCTION_VALUE_P (SET_DEST (pattern))) + break; + + /* Try to do some quick constant folding here. + This will save save execution time of the compiler, + as well time and space of the program if done here. */ + if (GET_CODE (pattern) == SET + && SET_DEST (pattern) == cc0_rtx) + next = try_fold_cc0 (insn); + + if (next != 0) + { + insn = next; + } + else + { + rtx note = find_reg_note (insn, REG_EQUIV, 0); + + copy = emit_insn (copy_rtx_and_substitute (pattern)); + RTX_INTEGRATED_P (copy) = 1; + + /* If we are copying an insn that loads a constant, + record the constantness. */ + if (note) + REG_NOTES (copy) + = gen_rtx (EXPR_LIST, REG_EQUIV, XEXP (note, 0), + REG_NOTES (copy)); + } + break; + + case JUMP_INSN: + follows_call = 0; + if (GET_CODE (PATTERN (insn)) == RETURN) + { + if (local_return_label == 0) + local_return_label = gen_label_rtx (); + emit_jump (local_return_label); + break; + } + copy = emit_jump_insn (copy_rtx_and_substitute (PATTERN (insn))); + RTX_INTEGRATED_P (copy) = 1; + break; + + case CALL_INSN: +#if 0 + /* This should no longer be necessary now that references + to this function's return value are flagged to distinguish + them from other references to the same hard register. */ + { + rtx newbod; + /* If the call's body is (set (reg...) (call...)), + the register is a function return register, but DON'T + translate it into INLINE_TARGET because it describes the + called function, not the caller's return value. */ + if (GET_CODE (PATTERN (insn)) == SET) + newbod = gen_rtx (SET, VOIDmode, SET_DEST (PATTERN (insn)), + copy_rtx_and_substitute (SET_SRC (PATTERN (insn)))); + else if (GET_CODE (PATTERN (insn)) == PARALLEL + && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET) + { + register int j; + rtx newelem; + newbod = gen_rtx (PARALLEL, VOIDmode, + rtvec_alloc (XVECLEN (PATTERN (insn), 0))); + newelem = gen_rtx (SET, VOIDmode, + SET_DEST (XVECEXP (PATTERN (insn), 0, 0)), + copy_rtx_and_substitute (SET_SRC (XVECEXP (PATTERN (insn), 0, 0)))); + XVECEXP (newbod, 0, 0) = newelem; + for (j = 1; j < XVECLEN (newbod, 0); j++) + XVECEXP (newbod, 0, j) + = copy_rtx_and_substitute (XVECEXP (PATTERN (insn), 0, j)); + } + else + newbod = copy_rtx_and_substitute (PATTERN (insn)); + copy = emit_call_insn (newbod); + } +#else /* 1 */ + copy = emit_call_insn (copy_rtx_and_substitute (PATTERN (insn))); +#endif /* 1 */ + RTX_INTEGRATED_P (copy) = 1; + /* Special handling needed for the following INSN depending on + whether it copies the value from the fcn return reg. */ + if (GET_CODE (PATTERN (insn)) == SET) + follows_call = SET_DEST (PATTERN (insn)); + else if (GET_CODE (PATTERN (insn)) == PARALLEL + && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == SET) + follows_call = SET_DEST (XVECEXP (PATTERN (insn), 0, 0)); + break; + + case CODE_LABEL: + copy = emit_label (label_map[CODE_LABEL_NUMBER (insn)]); + follows_call = 0; + break; + + case BARRIER: + copy = emit_barrier (); + break; + + case NOTE: + if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_END + && NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_BEG) + copy = emit_note (NOTE_SOURCE_FILE (insn), NOTE_LINE_NUMBER (insn)); + else + copy = 0; + break; + + default: + abort (); + break; + } + + insn_map[INSN_UID (insn)] = copy; + } + + if (local_return_label) + emit_label (local_return_label); + + /* Make copies of the decls of the symbols in the inline function, so that + the copies of the variables get declared in the current function. */ + copy_decl_tree (DECL_INITIAL (fndecl), 0); + + /* End the scope containing the copied formal parameter variables. */ + + expand_end_bindings (getdecls (), 1, 1); + poplevel (1, 1, 0); + poplevel (0, 0, 0); + + emit_line_note (input_filename, lineno); + reg_map = NULL; + label_map = NULL; + + if (ignore || TYPE_MODE (type) == VOIDmode) + return 0; + + if (structure_value_addr) + { + if (target) + return target; + return gen_rtx (MEM, TYPE_MODE (type), + memory_address (BLKmode, structure_value_addr)); + } + + return target; +} + +/* Given a chain of PARM_DECLs, ARGS, and a vector of RTL homes VEC, + copy each decl into a VAR_DECL, push all of those decls + and give each one the corresponding home. */ + +static void +copy_parm_decls (args, vec) + tree args; + rtx *vec; +{ + register tree tail; + register int i; + + for (tail = args, i = 0; tail; tail = TREE_CHAIN (tail), i++) + { + register tree decl = build_decl (VAR_DECL, DECL_NAME (tail), + TREE_TYPE (tail)); + /* These args would always appear unused, if not for this. */ + TREE_USED (decl) = 1; + /* Prevent warning for shadowing with these. */ + TREE_INLINE (decl) = 1; + decl = pushdecl (decl); + DECL_RTL (decl) = vec[i]; + } +} + +/* Given a LET_STMT node, push decls and levels + so as to construct in the current function a tree of contexts + isomorphic to the one that is given. */ + +static void +copy_decl_tree (let, level) + tree let; + int level; +{ + tree t, node; + + pushlevel (0); + + for (t = STMT_VARS (let); t; t = TREE_CHAIN (t)) + { + tree d = build_decl (TREE_CODE (t), DECL_NAME (t), TREE_TYPE (t)); + DECL_SOURCE_LINE (d) = DECL_SOURCE_LINE (t); + DECL_SOURCE_FILE (d) = DECL_SOURCE_FILE (t); + if (DECL_RTL (t) != 0) + { + if (GET_CODE (DECL_RTL (t)) == MEM + && CONSTANT_ADDRESS_P (XEXP (DECL_RTL (t), 0))) + /* copy_rtx_and_substitute would call memory_address + which would copy the address into a register. + Then debugging-output wouldn't know how to handle it. */ + DECL_RTL (d) = DECL_RTL (t); + else + DECL_RTL (d) = copy_rtx_and_substitute (DECL_RTL (t)); + } + TREE_EXTERNAL (d) = TREE_EXTERNAL (t); + TREE_STATIC (d) = TREE_STATIC (t); + TREE_PUBLIC (d) = TREE_PUBLIC (t); + TREE_LITERAL (d) = TREE_LITERAL (t); + TREE_ADDRESSABLE (d) = TREE_ADDRESSABLE (t); + TREE_READONLY (d) = TREE_READONLY (t); + TREE_VOLATILE (d) = TREE_VOLATILE (t); + /* These args would always appear unused, if not for this. */ + TREE_USED (d) = 1; + /* Prevent warning for shadowing with these. */ + TREE_INLINE (d) = 1; + pushdecl (d); + } + + for (t = STMT_SUBBLOCKS (let); t; t = TREE_CHAIN (t)) + copy_decl_tree (t, level + 1); + + node = poplevel (level > 0, 0, 0); + if (node) + TREE_USED (node) = TREE_USED (let); +} + +/* Nonzero if X is a sum which has the frame pointer or arg pointers + as a term. */ + +static int +frame_pointer_sum_p (x) + rtx x; +{ + if (x == frame_pointer_rtx || x == arg_pointer_rtx) + return 1; + if (GET_CODE (x) == PLUS + && + (frame_pointer_sum_p (XEXP (x, 0)) || frame_pointer_sum_p (XEXP (x, 1)))) + return 1; + + return 0; +} + +/* Create a new copy of an rtx. + Recursively copies the operands of the rtx, + except for those few rtx codes that are sharable. */ + +static rtx +copy_rtx_and_substitute (orig) + register rtx orig; +{ + register rtx copy, temp; + register int i, j; + register RTX_CODE code; + register enum machine_mode mode; + register char *format_ptr; + int regno; + + if (orig == 0) + return 0; + + code = GET_CODE (orig); + mode = GET_MODE (orig); + + switch (code) + { + case REG: + /* If a frame-pointer register shows up, then we + must `fix' the reference. If the stack pointer + register shows up, it must be part of stack-adjustments + (*not* because we eliminated the frame pointer!). + Small hard registers are returned as-is. Pseudo-registers + go through their `reg_map'. */ + regno = REGNO (orig); + if (regno < FIRST_PSEUDO_REGISTER) + { + /* Some hard registers are also mapped, + but others are not translated. */ + if (reg_map[regno] != 0) + return reg_map[regno]; + if (REG_FUNCTION_VALUE_P (orig)) + { + /* This is a reference to the function return value. If + the function doesn't have a return value, error. + If it does, it may not be the same mode as `inline_target' + because SUBREG is not required for hard regs. + If not, adjust mode of inline_target to fit the context. */ + if (inline_target == 0) + abort (); + if (mode == GET_MODE (inline_target)) + return inline_target; + return gen_rtx (SUBREG, mode, inline_target, 0); + } + if (regno == FRAME_POINTER_REGNUM) + return plus_constant (orig, fp_delta); + return orig; + } + if (reg_map[regno] == NULL) + { + reg_map[regno] = gen_reg_rtx (mode); + REG_USERVAR_P (reg_map[regno]) = REG_USERVAR_P (orig); + } + return reg_map[regno]; + + case SUBREG: + copy = copy_rtx_and_substitute (SUBREG_REG (orig)); + /* SUBREG is ordinary, but don't make nested SUBREGs. */ + if (GET_CODE (copy) == SUBREG) + return gen_rtx (SUBREG, GET_MODE (orig), SUBREG_REG (copy), + SUBREG_WORD (orig) + SUBREG_WORD (copy)); + return gen_rtx (SUBREG, GET_MODE (orig), copy, + SUBREG_WORD (orig)); + + case CODE_LABEL: + return label_map[CODE_LABEL_NUMBER (orig)]; + + case LABEL_REF: + copy = rtx_alloc (LABEL_REF); + PUT_MODE (copy, mode); + XEXP (copy, 0) = label_map[CODE_LABEL_NUMBER (XEXP (orig, 0))]; + return copy; + + case PC: + case CC0: + case CONST_INT: + case CONST_DOUBLE: + case SYMBOL_REF: + return orig; + + case ASM_OPERANDS: + /* If a single asm insn contains multiple output operands + then it contains multiple ASM_OPERANDS rtx's that share operand 3. + We must make sure that the copied insn continues to share it. */ + if (orig_asm_operands_vector == XVEC (orig, 3)) + { + copy = rtx_alloc (ASM_OPERANDS); + XSTR (copy, 0) = XSTR (orig, 0); + XSTR (copy, 1) = XSTR (orig, 1); + XINT (copy, 2) = XINT (orig, 2); + XVEC (copy, 3) = copy_asm_operands_vector; + XVEC (copy, 4) = copy_asm_constraints_vector; + XSTR (copy, 5) = XSTR (orig, 5); + XINT (copy, 6) = XINT (orig, 6); + return copy; + } + break; + + case CALL: + /* This is given special treatment because the first + operand of a CALL is a (MEM ...) which may get + forced into a register for cse. This is undesirable + if function-address cse isn't wanted or if we won't do cse. */ +#ifndef NO_FUNCTION_CSE + if (! (optimize && ! flag_no_function_cse)) +#endif + return gen_rtx (CALL, GET_MODE (orig), + gen_rtx (MEM, GET_MODE (XEXP (orig, 0)), + copy_rtx_and_substitute (XEXP (XEXP (orig, 0), 0))), + copy_rtx_and_substitute (XEXP (orig, 1))); + break; + + case PLUS: + /* Note: the PLUS case is not nearly as careful as the MEM + case in terms of preserving addresses. The reason for this + is that it is expected that if a PLUS_EXPR turns out not + to be a legitimate address, reload can fix that up, without + doing major damage. However, a MEM rtx must preside + over a legitimate address. The MEM case has lots of hair + to deal with what happens when it sits on a PLUS... */ + /* Take care of the easy case quickly. */ + if (XEXP (orig, 0) == frame_pointer_rtx + || XEXP (orig, 1) == frame_pointer_rtx + || (ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM + && (XEXP (orig, 0) == arg_pointer_rtx + || XEXP (orig, 1) == arg_pointer_rtx))) + { + rtx reg; + if (XEXP (orig, 0) == frame_pointer_rtx + || XEXP (orig, 0) == arg_pointer_rtx) + reg = XEXP (orig, 0), copy = XEXP (orig, 1); + else + reg = XEXP (orig, 1), copy = XEXP (orig, 0); + + if (GET_CODE (copy) == CONST_INT) + { + int c = INTVAL (copy); + + if (reg == arg_pointer_rtx && c >= first_parm_offset + && (c / UNITS_PER_WORD) < parm_map_size) + { + copy = access_parm_map (c, VOIDmode); + if (GET_CODE (copy) != MEM) + /* Should not happen, because a parm we need to address + should not be living in a register. + (expand_inline_function copied it to a stack slot.) */ + abort (); + return XEXP (copy, 0); + } + return gen_rtx (PLUS, mode, + frame_pointer_rtx, + gen_rtx (CONST_INT, SImode, + c + fp_delta)); + } + copy = copy_rtx_and_substitute (copy); + temp = force_reg (mode, gen_rtx (PLUS, mode, frame_pointer_rtx, copy)); + return plus_constant (temp, fp_delta); + } + else if (frame_pointer_sum_p (orig)) + { + /* If we have a complex sum which has a frame pointer + in it, and it was a legitimate address, then + keep it that way. */ + if (memory_address_p (mode, orig)) + { + if (GET_CODE (XEXP (orig, 0)) == CONST_INT) + { + copy = copy_rtx_and_substitute (XEXP (orig, 1)); + temp = plus_constant (copy, INTVAL (XEXP (orig, 0))); + } + else if (GET_CODE (XEXP (orig, 1)) == CONST_INT) + { + copy = copy_rtx_and_substitute (XEXP (orig, 0)); + temp = plus_constant (copy, INTVAL (XEXP (orig, 1))); + } + else + { + temp = gen_rtx (PLUS, GET_MODE (orig), + copy_rtx_and_substitute (XEXP (orig, 0)), + copy_rtx_and_substitute (XEXP (orig, 1))); + } + temp = memory_address (mode, temp); + } + else + temp = gen_rtx (PLUS, GET_MODE (orig), + copy_rtx_and_substitute (XEXP (orig, 0)), + copy_rtx_and_substitute (XEXP (orig, 1))); + } + else + temp = gen_rtx (PLUS, GET_MODE (orig), + copy_rtx_and_substitute (XEXP (orig, 0)), + copy_rtx_and_substitute (XEXP (orig, 1))); + + return temp; + + case MEM: + /* Take care of easiest case here. */ + copy = XEXP (orig, 0); + if (copy == frame_pointer_rtx || copy == arg_pointer_rtx) + return gen_rtx (MEM, mode, + plus_constant (frame_pointer_rtx, fp_delta)); + + /* Allow a pushing-address even if that is not valid as an + ordinary memory address. It indicates we are inlining a special + push-insn. These must be copied; otherwise unshare_all_rtl + might clobber them to point at temporary rtl of this function. */ +#ifdef STACK_GROWS_DOWNWARD + if (GET_CODE (copy) == PRE_DEC && XEXP (copy, 0) == stack_pointer_rtx) + return gen_rtx (MEM, mode, copy_rtx_and_substitute (copy)); +#else + if (GET_CODE (copy) == PRE_INC && XEXP (copy, 0) == stack_pointer_rtx) + return gen_rtx (MEM, mode, copy_rtx_and_substitute (copy)); +#endif + + /* If this is some other sort of address that isn't generally valid, + break out all the registers referred to. */ + if (! memory_address_p (mode, copy)) + return gen_rtx (MEM, mode, copy_address (copy)); + + if (GET_CODE (copy) == PLUS) + { + if (XEXP (copy, 0) == frame_pointer_rtx + || XEXP (copy, 1) == frame_pointer_rtx + || (ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM + && (XEXP (copy, 0) == arg_pointer_rtx + || XEXP (copy, 1) == arg_pointer_rtx))) + { + rtx reg; + if (XEXP (copy, 0) == frame_pointer_rtx + || XEXP (copy, 0) == arg_pointer_rtx) + reg = XEXP (copy, 0), copy = XEXP (copy, 1); + else + reg = XEXP (copy, 1), copy = XEXP (copy, 0); + + if (GET_CODE (copy) == CONST_INT) + { + int c = INTVAL (copy); + + if (reg == arg_pointer_rtx && c >= first_parm_offset + && (c / UNITS_PER_WORD) < parm_map_size) + return access_parm_map (c, mode); + + temp = gen_rtx (PLUS, Pmode, + frame_pointer_rtx, + gen_rtx (CONST_INT, SImode, + c + fp_delta)); + if (! memory_address_p (Pmode, temp)) + return gen_rtx (MEM, mode, plus_constant (inline_fp_rtx, c)); + } + copy = copy_rtx_and_substitute (copy); + temp = gen_rtx (PLUS, Pmode, frame_pointer_rtx, copy); + temp = plus_constant (temp, fp_delta); + temp = memory_address (Pmode, temp); + } + else if (reg_mentioned_p (frame_pointer_rtx, copy) + || (ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM + && reg_mentioned_p (arg_pointer_rtx, copy))) + { + if (GET_CODE (XEXP (copy, 0)) == CONST_INT) + { + temp = copy_rtx_and_substitute (XEXP (copy, 1)); + temp = plus_constant (temp, INTVAL (XEXP (copy, 0))); + } + else if (GET_CODE (XEXP (copy, 1)) == CONST_INT) + { + temp = copy_rtx_and_substitute (XEXP (copy, 0)); + temp = plus_constant (temp, INTVAL (XEXP (copy, 1))); + } + else + { + temp = gen_rtx (PLUS, GET_MODE (copy), + copy_rtx_and_substitute (XEXP (copy, 0)), + copy_rtx_and_substitute (XEXP (copy, 1))); + } + } + else + { + if (GET_CODE (XEXP (copy, 1)) == CONST_INT) + temp = plus_constant (copy_rtx_and_substitute (XEXP (copy, 0)), + INTVAL (XEXP (copy, 1))); + else if (GET_CODE (XEXP (copy, 0)) == CONST_INT) + temp = plus_constant (copy_rtx_and_substitute (XEXP (copy, 1)), + INTVAL (XEXP (copy, 0))); + else + { + rtx left = copy_rtx_and_substitute (XEXP (copy, 0)); + rtx right = copy_rtx_and_substitute (XEXP (copy, 1)); + + temp = gen_rtx (PLUS, GET_MODE (copy), left, right); + } + } + } + else + temp = copy_rtx_and_substitute (copy); + + return change_address (orig, mode, temp); + + case RETURN: + abort (); + } + + copy = rtx_alloc (code); + PUT_MODE (copy, mode); + copy->in_struct = orig->in_struct; + copy->volatil = orig->volatil; + copy->unchanging = orig->unchanging; + + format_ptr = GET_RTX_FORMAT (GET_CODE (copy)); + + for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++) + { + switch (*format_ptr++) + { + case '0': + break; + + case 'e': + XEXP (copy, i) = copy_rtx_and_substitute (XEXP (orig, i)); + break; + + case 'u': + /* Change any references to old-insns to point to the + corresponding copied insns. */ + XEXP (copy, i) = insn_map[INSN_UID (XEXP (orig, i))]; + break; + + case 'E': + XVEC (copy, i) = XVEC (orig, i); + if (XVEC (orig, i) != NULL && XVECLEN (orig, i) != 0) + { + XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i)); + for (j = 0; j < XVECLEN (copy, i); j++) + XVECEXP (copy, i, j) = copy_rtx_and_substitute (XVECEXP (orig, i, j)); + } + break; + + case 'i': + XINT (copy, i) = XINT (orig, i); + break; + + case 's': + XSTR (copy, i) = XSTR (orig, i); + break; + + default: + abort (); + } + } + + if (code == ASM_OPERANDS && orig_asm_operands_vector == 0) + { + orig_asm_operands_vector = XVEC (orig, 3); + copy_asm_operands_vector = XVEC (copy, 3); + copy_asm_constraints_vector = XVEC (copy, 4); + } + + return copy; +} + +/* Get the value corresponding to an address relative to the arg pointer + at index RELADDRESS. MODE is the machine mode of the reference. + MODE is used only when the value is a REG. + Pass VOIDmode for MODE when the mode is not known; + in such cases, you should make sure the value is a MEM. */ + +static rtx +access_parm_map (reladdress, mode) + int reladdress; + enum machine_mode mode; +{ + /* Index in parm_map. */ + int index = reladdress / UNITS_PER_WORD; + /* Offset of the data being referenced + from the beginning of the value for that parm. */ + int offset = reladdress % UNITS_PER_WORD; + rtx copy; + + /* If we are referring to the middle of a multiword parm, + find the beginning of that parm. + OFFSET gets the offset of the reference from + the beginning of the parm. */ + + while (parm_map[index] == 0) + { + index--; + if (index < first_parm_offset / UNITS_PER_WORD) + /* If this abort happens, it means we need + to handle "decrementing" INDEX back far + enough to start looking among the reg parms + instead of the stack parms. What a mess! */ + abort (); + offset += UNITS_PER_WORD; + } + + copy = parm_map[index]; + +#ifdef BYTES_BIG_ENDIAN + /* Subtract from OFFSET the offset of where + the actual parm value would start. */ + if (GET_MODE_SIZE (GET_MODE (copy)) < UNITS_PER_WORD) + offset + -= (UNITS_PER_WORD + - GET_MODE_SIZE (GET_MODE (copy))); +#endif + + /* For memory ref, adjust it by the desired offset. */ + if (GET_CODE (copy) == MEM) + { + if (offset != 0) + return change_address (copy, mode, + plus_constant (XEXP (copy, 0), + offset)); + return copy; + } + + if (GET_CODE (copy) != REG && GET_CODE (copy) != SUBREG + && ! CONSTANT_P (copy)) + abort (); + if (mode == VOIDmode) + abort (); + + /* A REG cannot be offset by bytes, so use a subreg + (which is possible only in certain cases). */ + if (GET_MODE (copy) != mode + && GET_MODE (copy) != VOIDmode) + { + int word; + /* Crash if the portion of the arg wanted + is not the least significant. + Functions with refs to other parts of a + parameter should not be inline-- + see function_cannot_inline_p. */ +#ifdef BYTES_BIG_ENDIAN + if ((offset + GET_MODE_SIZE (mode)) % UNITS_PER_WORD + != GET_MODE_SIZE (GET_MODE (copy)) % UNITS_PER_WORD) + abort (); +#else + if ((offset % UNITS_PER_WORD) != 0) + abort (); +#endif + word = offset / UNITS_PER_WORD; + if (GET_CODE (copy) == SUBREG) + word += SUBREG_WORD (copy), copy = SUBREG_REG (copy); + if (CONSTANT_P (copy)) + copy = force_reg (GET_MODE (copy), copy); + return gen_rtx (SUBREG, mode, copy, word); + } + + return copy; +} + +/* Like copy_rtx_and_substitute but produces different output, suitable + for an ideosyncractic address that isn't memory_address_p. + The output resembles the input except that REGs and MEMs are replaced + with new psuedo registers. All the "real work" is done in separate + insns which set up the values of these new registers. */ + +static rtx +copy_address (orig) + register rtx orig; +{ + register rtx copy; + register int i, j; + register RTX_CODE code; + register enum machine_mode mode; + register char *format_ptr; + + if (orig == 0) + return 0; + + code = GET_CODE (orig); + mode = GET_MODE (orig); + + switch (code) + { + case REG: + if (REGNO (orig) != FRAME_POINTER_REGNUM) + return copy_rtx_and_substitute (orig); + return plus_constant (frame_pointer_rtx, fp_delta); + + case PLUS: + if (GET_CODE (XEXP (orig, 0)) == REG + && REGNO (XEXP (orig, 0)) == FRAME_POINTER_REGNUM) + return plus_constant (orig, fp_delta); + break; + + case MEM: + return copy_to_reg (copy_rtx_and_substitute (orig)); + + case CODE_LABEL: + case LABEL_REF: + return copy_rtx_and_substitute (orig); + + case PC: + case CC0: + case CONST_INT: + case CONST_DOUBLE: + case SYMBOL_REF: + return orig; + } + + copy = rtx_alloc (code); + PUT_MODE (copy, mode); + copy->in_struct = orig->in_struct; + copy->volatil = orig->volatil; + copy->unchanging = orig->unchanging; + + format_ptr = GET_RTX_FORMAT (GET_CODE (copy)); + + for (i = 0; i < GET_RTX_LENGTH (GET_CODE (copy)); i++) + { + switch (*format_ptr++) + { + case '0': + break; + + case 'e': + XEXP (copy, i) = copy_rtx_and_substitute (XEXP (orig, i)); + break; + + case 'u': + /* Change any references to old-insns to point to the + corresponding copied insns. */ + XEXP (copy, i) = insn_map[INSN_UID (XEXP (orig, i))]; + break; + + case 'E': + XVEC (copy, i) = XVEC (orig, i); + if (XVEC (orig, i) != NULL && XVECLEN (orig, i) != 0) + { + XVEC (copy, i) = rtvec_alloc (XVECLEN (orig, i)); + for (j = 0; j < XVECLEN (copy, i); j++) + XVECEXP (copy, i, j) = copy_rtx_and_substitute (XVECEXP (orig, i, j)); + } + break; + + case 'i': + XINT (copy, i) = XINT (orig, i); + break; + + case 's': + XSTR (copy, i) = XSTR (orig, i); + break; + + default: + abort (); + } + } + return copy; +} + +/* Attempt to simplify INSN while copying it from an inline fn, + assuming it is a SET that sets CC0. + + If we simplify it, we emit the appropriate insns and return + the last insn that we have handled (since we may handle the insn + that follows INSN as well as INSN itself). + + Otherwise we do nothing and return zero. */ + +static rtx +try_fold_cc0 (insn) + rtx insn; +{ + rtx cnst = copy_rtx_and_substitute (SET_SRC (PATTERN (insn))); + rtx pat, copy; + + if (CONSTANT_P (cnst) + /* @@ Cautious: Don't know how many of these tests we need. */ + && NEXT_INSN (insn) + && GET_CODE (pat = PATTERN (NEXT_INSN (insn))) == SET + && SET_DEST (pat) == pc_rtx + && GET_CODE (pat = SET_SRC (pat)) == IF_THEN_ELSE + && GET_RTX_LENGTH (GET_CODE (XEXP (pat, 0))) == 2) + { + rtx cnst2; + rtx cond = XEXP (pat, 0); + + if ((XEXP (cond, 0) == cc0_rtx + && CONSTANT_P (XEXP (cond, 1)) + && (cnst2 = XEXP (cond, 1))) + || (XEXP (cond, 1) == cc0_rtx + && CONSTANT_P (XEXP (cond, 0)) + && (cnst2 = XEXP (cond, 0)))) + { + copy = fold_out_const_cc0 (cond, XEXP (pat, 1), XEXP (pat, 2), + cnst, cnst2); + if (copy) + { + if (GET_CODE (copy) == LABEL_REF) + { + /* We will branch unconditionally to + the label specified by COPY. + Eliminate dead code by running down the + list of insn until we see a CODE_LABEL. + If the CODE_LABEL is the one specified + by COPY, we win, and can delete all code + up to (but not necessarily including) + that label. Otherwise only win a little: + emit the branch insn, and continue expanding. */ + rtx tmp = NEXT_INSN (insn); + while (tmp && GET_CODE (tmp) != CODE_LABEL) + tmp = NEXT_INSN (tmp); + if (! tmp) + abort (); + if (label_map[CODE_LABEL_NUMBER (tmp)] == XEXP (copy, 0)) + { + /* Big win. */ + return PREV_INSN (tmp); + } + else + { + /* Small win. Emit the unconditional branch, + followed by a BARRIER, so that jump optimization + will know what to do. */ + emit_jump (copy); + return NEXT_INSN (insn); + } + } + else if (copy == pc_rtx) + { + /* Do not take the branch, just fall through. + Jump optimize should handle the elimination of + dead code if appropriate. */ + return NEXT_INSN (insn); + } + else + abort (); + } + } + } + return 0; +} + +/* If (COND_RTX CNST1 CNST2) yield a result we can treat + as being constant, return THEN_RTX if the result is always + non-zero, and return ELSE_RTX otherwise. */ +static rtx +fold_out_const_cc0 (cond_rtx, then_rtx, else_rtx, cnst1, cnst2) + rtx cond_rtx, then_rtx, else_rtx; + rtx cnst1, cnst2; +{ + int value1, value2; + int int1 = GET_CODE (cnst1) == CONST_INT; + int int2 = GET_CODE (cnst2) == CONST_INT; + if (int1) + value1 = INTVAL (cnst1); + else + value1 = 1; + if (int2) + value2 = INTVAL (cnst2); + else + value2 = 1; + + switch (GET_CODE (cond_rtx)) + { + case NE: + if (int1 && int2) + if (value1 != value2) + return copy_rtx_and_substitute (then_rtx); + else + return copy_rtx_and_substitute (else_rtx); + if (value1 == 0 || value2 == 0) + return copy_rtx_and_substitute (then_rtx); + if (int1 == 0 && int2 == 0) + if (rtx_equal_p (cnst1, cnst2)) + return copy_rtx_and_substitute (else_rtx); + break; + case EQ: + if (int1 && int2) + if (value1 == value2) + return copy_rtx_and_substitute (then_rtx); + else + return copy_rtx_and_substitute (else_rtx); + if (value1 == 0 || value2 == 0) + return copy_rtx_and_substitute (else_rtx); + if (int1 == 0 && int2 == 0) + if (rtx_equal_p (cnst1, cnst2)) + return copy_rtx_and_substitute (then_rtx); + break; + case GE: + if (int1 && int2) + if (value1 >= value2) + return copy_rtx_and_substitute (then_rtx); + else + return copy_rtx_and_substitute (else_rtx); + if (value1 == 0) + return copy_rtx_and_substitute (else_rtx); + if (value2 == 0) + return copy_rtx_and_substitute (then_rtx); + break; + case GT: + if (int1 && int2) + if (value1 > value2) + return copy_rtx_and_substitute (then_rtx); + else + return copy_rtx_and_substitute (else_rtx); + if (value1 == 0) + return copy_rtx_and_substitute (else_rtx); + if (value2 == 0) + return copy_rtx_and_substitute (then_rtx); + break; + case LE: + if (int1 && int2) + if (value1 <= value2) + return copy_rtx_and_substitute (then_rtx); + else + return copy_rtx_and_substitute (else_rtx); + if (value1 == 0) + return copy_rtx_and_substitute (then_rtx); + if (value2 == 0) + return copy_rtx_and_substitute (else_rtx); + break; + case LT: + if (int1 && int2) + if (value1 < value2) + return copy_rtx_and_substitute (then_rtx); + else + return copy_rtx_and_substitute (else_rtx); + if (value1 == 0) + return copy_rtx_and_substitute (then_rtx); + if (value2 == 0) + return copy_rtx_and_substitute (else_rtx); + break; + case GEU: + if (int1 && int2) + if ((unsigned)value1 >= (unsigned)value2) + return copy_rtx_and_substitute (then_rtx); + else + return copy_rtx_and_substitute (else_rtx); + if (value1 == 0) + return copy_rtx_and_substitute (else_rtx); + if (value2 == 0) + return copy_rtx_and_substitute (then_rtx); + break; + case GTU: + if (int1 && int2) + if ((unsigned)value1 > (unsigned)value2) + return copy_rtx_and_substitute (then_rtx); + else + return copy_rtx_and_substitute (else_rtx); + if (value1 == 0) + return copy_rtx_and_substitute (else_rtx); + if (value2 == 0) + return copy_rtx_and_substitute (then_rtx); + break; + case LEU: + if (int1 && int2) + if ((unsigned)value1 <= (unsigned)value2) + return copy_rtx_and_substitute (then_rtx); + else + return copy_rtx_and_substitute (else_rtx); + if (value1 == 0) + return copy_rtx_and_substitute (then_rtx); + if (value2 == 0) + return copy_rtx_and_substitute (else_rtx); + break; + case LTU: + if (int1 && int2) + if ((unsigned)value1 < (unsigned)value2) + return copy_rtx_and_substitute (then_rtx); + else + return copy_rtx_and_substitute (else_rtx); + if (value1 == 0) + return copy_rtx_and_substitute (then_rtx); + if (value2 == 0) + return copy_rtx_and_substitute (else_rtx); + break; + } + /* Could not hack it. */ + return 0; +} + +/* Output the assembly language code for the function FNDECL + from its DECL_SAVED_INSNS. Used for inline functions that are output + at end of compilation instead of where they came in the source. */ + +void +output_inline_function (fndecl) + tree fndecl; +{ + rtx head = DECL_SAVED_INSNS (fndecl); + rtx last; + extern rtx stack_slot_list; + + temporary_allocation (); + + current_function_decl = fndecl; + + /* This call is only used to initialize global variables. */ + init_function_start (fndecl, "lossage", 1); + + /* Set stack frame size. */ + assign_stack_local (BLKmode, DECL_FRAME_SIZE (fndecl)); + + restore_reg_data (FIRST_PARM_INSN (head)); + + stack_slot_list = XEXP (head, 9); + + expand_function_end (DECL_SOURCE_FILE (fndecl), DECL_SOURCE_LINE (fndecl)); + + for (last = head; NEXT_INSN (last); last = NEXT_INSN (last)) + ; + + set_new_first_and_last_insn (FIRST_PARM_INSN (head), last); + + /* Compile this function all the way down to assembly code. */ + rest_of_compilation (fndecl); + + current_function_decl = 0; + + permanent_allocation (); +} |
