X-Git-Url: http://git.sourceforge.jp/view?p=pf3gnuchains%2Fgcc-fork.git;a=blobdiff_plain;f=gcc%2Fintegrate.c;h=fed0a2736d62703fd519b128fab7e0c6b5c5df16;hp=3c0b42ae01e8ce8a9ab193c771c5d56b21859a1e;hb=90df0abc7f06ba32b3ce67f52c01645052e70cbd;hpb=bca5a169dbd7511850392fce078fac9a2ce8b785 diff --git a/gcc/integrate.c b/gcc/integrate.c index 3c0b42ae01e..fed0a2736d6 100644 --- a/gcc/integrate.c +++ b/gcc/integrate.c @@ -1,6 +1,6 @@ /* Procedure integration for GCC. Copyright (C) 1988, 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, - 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. Contributed by Michael Tiemann (tiemann@cygnus.com) This file is part of GCC. @@ -41,26 +41,13 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "function.h" #include "toplev.h" #include "intl.h" -#include "loop.h" #include "params.h" #include "ggc.h" #include "target.h" #include "langhooks.h" -/* Similar, but round to the next highest integer that meets the - alignment. */ +/* Round to the next highest integer that meets the alignment. */ #define CEIL_ROUND(VALUE,ALIGN) (((VALUE) + (ALIGN) - 1) & ~((ALIGN)- 1)) - -/* Default max number of insns a function can have and still be inline. - This is overridden on RISC machines. */ -#ifndef INTEGRATE_THRESHOLD -/* Inlining small functions might save more space then not inlining at - all. Assume 1 instruction for the call and 1.5 insns per argument. */ -#define INTEGRATE_THRESHOLD(DECL) \ - (optimize_size \ - ? (1 + (3 * list_length (DECL_ARGUMENTS (DECL))) / 2) \ - : (8 * (8 + list_length (DECL_ARGUMENTS (DECL))))) -#endif /* Private type used by {get/has}_func_hard_reg_initial_val. */ @@ -74,35 +61,10 @@ typedef struct initial_value_struct GTY(()) { initial_value_pair * GTY ((length ("%h.num_entries"))) entries; } initial_value_struct; -static void setup_initial_hard_reg_value_integration PARAMS ((struct function *, struct inline_remap *)); - -static rtvec initialize_for_inline PARAMS ((tree)); -static void note_modified_parmregs PARAMS ((rtx, rtx, void *)); -static void integrate_parm_decls PARAMS ((tree, struct inline_remap *, - rtvec)); -static tree integrate_decl_tree PARAMS ((tree, - struct inline_remap *)); -static void subst_constants PARAMS ((rtx *, rtx, - struct inline_remap *, int)); -static void set_block_origin_self PARAMS ((tree)); -static void set_block_abstract_flags PARAMS ((tree, int)); -static void process_reg_param PARAMS ((struct inline_remap *, rtx, - rtx)); -void set_decl_abstract_flags PARAMS ((tree, int)); -static void mark_stores PARAMS ((rtx, rtx, void *)); -static void save_parm_insns PARAMS ((rtx, rtx)); -static void copy_insn_list PARAMS ((rtx, struct inline_remap *, - rtx)); -static void copy_insn_notes PARAMS ((rtx, struct inline_remap *, - int)); -static int compare_blocks PARAMS ((const PTR, const PTR)); -static int find_block PARAMS ((const PTR, const PTR)); - -/* Used by copy_rtx_and_substitute; this indicates whether the function is - called for the purpose of inlining or some other purpose (i.e. loop - unrolling). This affects how constant pool references are handled. - This variable contains the FUNCTION_DECL for the inlined function. */ -static struct function *inlining = 0; +static void subst_constants (rtx *, rtx, struct inline_remap *, int); +static void set_block_origin_self (tree); +static void set_block_abstract_flags (tree, int); +static void mark_stores (rtx, rtx, void *); /* Returns the Ith entry in the label_map contained in MAP. If the Ith entry has not yet been set, return a fresh label. This function @@ -110,9 +72,7 @@ static struct function *inlining = 0; explosions when the label_map gets very large. */ rtx -get_label_from_map (map, i) - struct inline_remap *map; - int i; +get_label_from_map (struct inline_remap *map, int i) { rtx x = map->label_map[i]; @@ -125,8 +85,7 @@ get_label_from_map (map, i) /* Return false if the function FNDECL cannot be inlined on account of its attributes, true otherwise. */ bool -function_attribute_inlinable_p (fndecl) - tree fndecl; +function_attribute_inlinable_p (tree fndecl) { if (targetm.attribute_table) { @@ -139,258 +98,54 @@ function_attribute_inlinable_p (fndecl) for (i = 0; targetm.attribute_table[i].name != NULL; i++) if (is_attribute_p (targetm.attribute_table[i].name, name)) - return (*targetm.function_attribute_inlinable_p) (fndecl); + return targetm.function_attribute_inlinable_p (fndecl); } } return true; } - -/* 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 msgid with a single %s - for the function's name. */ - -const char * -function_cannot_inline_p (fndecl) - tree fndecl; -{ - rtx insn; - tree last = tree_last (TYPE_ARG_TYPES (TREE_TYPE (fndecl))); - - /* For functions marked as inline increase the maximum size to - MAX_INLINE_INSNS_RTL (--param max-inline-insn-rtl=). For - regular functions use the limit given by INTEGRATE_THRESHOLD. - Note that the RTL inliner is not used by the languages that use - the tree inliner (C, C++). */ - - int max_insns = (DECL_INLINE (fndecl)) - ? (MAX_INLINE_INSNS_RTL - + 8 * list_length (DECL_ARGUMENTS (fndecl))) - : INTEGRATE_THRESHOLD (fndecl); - - int ninsns = 0; - tree parms; - - if (DECL_UNINLINABLE (fndecl)) - return N_("function cannot be inline"); - - /* No inlines with varargs. */ - if (last && TREE_VALUE (last) != void_type_node) - return N_("varargs function cannot be inline"); - - if (current_function_calls_alloca) - return N_("function using alloca cannot be inline"); - - if (current_function_calls_setjmp) - return N_("function using setjmp cannot be inline"); - - if (current_function_calls_eh_return) - return N_("function uses __builtin_eh_return"); - - if (current_function_contains_functions) - return N_("function with nested functions cannot be inline"); - - if (forced_labels) - return - N_("function with label addresses used in initializers cannot inline"); - - if (current_function_cannot_inline) - return current_function_cannot_inline; - - /* If its not even close, don't even look. */ - if (get_max_uid () > 3 * max_insns) - return N_("function too large to be inline"); - -#if 0 - /* Don't inline functions which do not specify a function prototype and - have BLKmode argument or take the address of a parameter. */ - for (parms = DECL_ARGUMENTS (fndecl); parms; parms = TREE_CHAIN (parms)) - { - if (TYPE_MODE (TREE_TYPE (parms)) == BLKmode) - TREE_ADDRESSABLE (parms) = 1; - if (last == NULL_TREE && TREE_ADDRESSABLE (parms)) - return N_("no prototype, and parameter address used; cannot be inline"); - } -#endif - - /* We can't inline functions that return structures - the old-fashioned PCC way, copying into a static block. */ - if (current_function_returns_pcc_struct) - return N_("inline functions not supported for this return value type"); - - /* We can't inline functions that return structures of varying size. */ - if (TREE_CODE (TREE_TYPE (TREE_TYPE (fndecl))) != VOID_TYPE - && int_size_in_bytes (TREE_TYPE (TREE_TYPE (fndecl))) < 0) - return N_("function with varying-size return value cannot be inline"); - - /* Cannot inline a function with a varying size argument or one that - receives a transparent union. */ - for (parms = DECL_ARGUMENTS (fndecl); parms; parms = TREE_CHAIN (parms)) - { - if (int_size_in_bytes (TREE_TYPE (parms)) < 0) - return N_("function with varying-size parameter cannot be inline"); - else if (TREE_CODE (TREE_TYPE (parms)) == UNION_TYPE - && TYPE_TRANSPARENT_UNION (TREE_TYPE (parms))) - return N_("function with transparent unit parameter cannot be inline"); - } - - if (get_max_uid () > max_insns) - { - for (ninsns = 0, insn = get_first_nonparm_insn (); - insn && ninsns < max_insns; - insn = NEXT_INSN (insn)) - if (INSN_P (insn)) - ninsns++; - - if (ninsns >= max_insns) - return N_("function too large to be inline"); - } - - /* We will not inline a function which uses computed goto. The addresses of - its local labels, which may be tucked into global storage, are of course - not constant across instantiations, which causes unexpected behavior. */ - if (current_function_has_computed_jump) - return N_("function with computed jump cannot inline"); - - /* We cannot inline a nested function that jumps to a nonlocal label. */ - if (current_function_has_nonlocal_goto) - return N_("function with nonlocal goto cannot be inline"); - - /* We can't inline functions that return a PARALLEL rtx. */ - if (DECL_RTL_SET_P (DECL_RESULT (fndecl))) - { - rtx result = DECL_RTL (DECL_RESULT (fndecl)); - if (GET_CODE (result) == PARALLEL) - return N_("inline functions not supported for this return value type"); - } - - /* If the function has a target specific attribute attached to it, - then we assume that we should not inline it. This can be overridden - by the target if it defines TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P. */ - if (!function_attribute_inlinable_p (fndecl)) - return N_("function with target specific attribute(s) cannot be inlined"); - - return NULL; -} -/* 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; - -/* In save_for_inline, nonzero if past the parm-initialization insns. */ -static int in_nonparm_insns; - -/* Subroutine for `save_for_inline'. Performs initialization - needed to save FNDECL's insns and info for future inline expansion. */ - -static rtvec -initialize_for_inline (fndecl) - tree fndecl; -{ - int i; - rtvec arg_vector; - tree parms; - - /* Clear out PARMDECL_MAP. It was allocated in the caller's frame. */ - memset ((char *) parmdecl_map, 0, max_parm_reg * sizeof (tree)); - arg_vector = rtvec_alloc (list_length (DECL_ARGUMENTS (fndecl))); - - for (parms = DECL_ARGUMENTS (fndecl), i = 0; - parms; - parms = TREE_CHAIN (parms), i++) - { - rtx p = DECL_RTL (parms); - - /* If we have (mem (addressof (mem ...))), use the inner MEM since - otherwise the copy_rtx call below will not unshare the MEM since - it shares ADDRESSOF. */ - if (GET_CODE (p) == MEM && GET_CODE (XEXP (p, 0)) == ADDRESSOF - && GET_CODE (XEXP (XEXP (p, 0), 0)) == MEM) - p = XEXP (XEXP (p, 0), 0); - - RTVEC_ELT (arg_vector, i) = p; - - if (GET_CODE (p) == REG) - parmdecl_map[REGNO (p)] = parms; - else if (GET_CODE (p) == CONCAT) - { - rtx preal = gen_realpart (GET_MODE (XEXP (p, 0)), p); - rtx pimag = gen_imagpart (GET_MODE (preal), p); - - if (GET_CODE (preal) == REG) - parmdecl_map[REGNO (preal)] = parms; - if (GET_CODE (pimag) == REG) - parmdecl_map[REGNO (pimag)] = parms; - } - - /* This flag is cleared later - if the function ever modifies the value of the parm. */ - TREE_READONLY (parms) = 1; - } - - return arg_vector; -} - -/* Copy NODE (which must be a DECL, but not a PARM_DECL). The DECL - originally was in the FROM_FN, but now it will be in the - TO_FN. */ +/* Copy NODE (which must be a DECL). The DECL originally was in the FROM_FN, + but now it will be in the TO_FN. */ tree -copy_decl_for_inlining (decl, from_fn, to_fn) - tree decl; - tree from_fn; - tree to_fn; +copy_decl_for_inlining (tree decl, tree from_fn, tree to_fn) { tree copy; /* Copy the declaration. */ if (TREE_CODE (decl) == PARM_DECL || TREE_CODE (decl) == RESULT_DECL) { - tree type; - int invisiref = 0; - - /* See if the frontend wants to pass this by invisible reference. */ - if (TREE_CODE (decl) == PARM_DECL - && DECL_ARG_TYPE (decl) != TREE_TYPE (decl) - && POINTER_TYPE_P (DECL_ARG_TYPE (decl)) - && TREE_TYPE (DECL_ARG_TYPE (decl)) == TREE_TYPE (decl)) - { - invisiref = 1; - type = DECL_ARG_TYPE (decl); - } - else - type = TREE_TYPE (decl); + tree type = TREE_TYPE (decl); - /* For a parameter, we must make an equivalent VAR_DECL, not a + /* For a parameter or result, we must make an equivalent VAR_DECL, not a new PARM_DECL. */ copy = build_decl (VAR_DECL, DECL_NAME (decl), type); - if (!invisiref) - { - TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (decl); - TREE_READONLY (copy) = TREE_READONLY (decl); - TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (decl); - } - else - { - TREE_ADDRESSABLE (copy) = 0; - TREE_READONLY (copy) = 1; - TREE_THIS_VOLATILE (copy) = 0; - } + TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (decl); + TREE_READONLY (copy) = TREE_READONLY (decl); + TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (decl); } else { copy = copy_node (decl); - (*lang_hooks.dup_lang_specific_decl) (copy); + /* The COPY is not abstract; it will be generated in TO_FN. */ + DECL_ABSTRACT (copy) = 0; + lang_hooks.dup_lang_specific_decl (copy); /* TREE_ADDRESSABLE isn't used to indicate that a label's address has been taken; it's for internal bookkeeping in expand_goto_internal. */ if (TREE_CODE (copy) == LABEL_DECL) - TREE_ADDRESSABLE (copy) = 0; + { + TREE_ADDRESSABLE (copy) = 0; + } } + /* Don't generate debug information for the copy if we wouldn't have + generated it for the copy either. */ + DECL_ARTIFICIAL (copy) = DECL_ARTIFICIAL (decl); + DECL_IGNORED_P (copy) = DECL_IGNORED_P (decl); + /* Set the DECL_ABSTRACT_ORIGIN so the debugging routines know what declaration inspired this copy. */ DECL_ABSTRACT_ORIGIN (copy) = DECL_ORIGIN (decl); @@ -421,1469 +176,13 @@ copy_decl_for_inlining (decl, from_fn, to_fn) return copy; } - -/* 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. - - This routine need not copy any insns because we are not going - to immediately compile the insns in the insn chain. There - are two cases when we would compile the insns for FNDECL: - (1) when FNDECL is expanded inline, and (2) when FNDECL needs to - be output at the end of other compilation, because somebody took - its address. In the first case, the insns of FNDECL are copied - as it is expanded inline, so FNDECL's saved insns are not - modified. In the second case, FNDECL is used for the last time, - so modifying the rtl is not a problem. - - We don't have to worry about FNDECL being inline expanded by - other functions which are written at the end of compilation - because flag_no_inline is turned on when we begin writing - functions at the end of compilation. */ - -void -save_for_inline (fndecl) - tree fndecl; -{ - rtx insn; - rtvec argvec; - rtx first_nonparm_insn; - - /* Set up PARMDECL_MAP which maps pseudo-reg number to its PARM_DECL. - Later we set TREE_READONLY to 0 if the parm is modified inside the fn. - Also set up ARG_VECTOR, which holds the unmodified DECL_RTX values - for the parms, prior to elimination of virtual registers. - These values are needed for substituting parms properly. */ - if (! flag_no_inline) - parmdecl_map = (tree *) xmalloc (max_parm_reg * sizeof (tree)); - - /* 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); - } - - if (! flag_no_inline) - argvec = initialize_for_inline (fndecl); - else - argvec = NULL; - - /* Delete basic block notes created by early run of find_basic_block. - The notes would be later used by find_basic_blocks to reuse the memory - for basic_block structures on already freed obstack. */ - for (insn = get_insns (); insn ; insn = NEXT_INSN (insn)) - if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) == NOTE_INSN_BASIC_BLOCK) - delete_related_insns (insn); - - /* 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 (); - - if (! flag_no_inline) - { - /* Get the insn which signals the end of parameter setup code. */ - first_nonparm_insn = get_first_nonparm_insn (); - - /* Now just scan the chain of insns to see what happens to our - PARM_DECLs. If a PARM_DECL is used but never modified, we - can substitute its rtl directly when expanding inline (and - perform constant folding when its incoming value is - constant). Otherwise, we have to copy its value into a new - register and track the new register's life. */ - in_nonparm_insns = 0; - save_parm_insns (insn, first_nonparm_insn); - - cfun->inl_max_label_num = max_label_num (); - cfun->inl_last_parm_insn = cfun->x_last_parm_insn; - cfun->original_arg_vector = argvec; - } - cfun->original_decl_initial = DECL_INITIAL (fndecl); - cfun->no_debugging_symbols = (write_symbols == NO_DEBUG); - DECL_SAVED_INSNS (fndecl) = cfun; - - /* Clean up. */ - if (! flag_no_inline) - free (parmdecl_map); -} - -/* Scan the chain of insns to see what happens to our PARM_DECLs. If a - PARM_DECL is used but never modified, we can substitute its rtl directly - when expanding inline (and perform constant folding when its incoming - value is constant). Otherwise, we have to copy its value into a new - register and track the new register's life. */ - -static void -save_parm_insns (insn, first_nonparm_insn) - rtx insn; - rtx first_nonparm_insn; -{ - if (insn == NULL_RTX) - return; - - for (insn = NEXT_INSN (insn); insn; insn = NEXT_INSN (insn)) - { - if (insn == first_nonparm_insn) - in_nonparm_insns = 1; - - if (INSN_P (insn)) - { - /* Record what interesting things happen to our parameters. */ - note_stores (PATTERN (insn), note_modified_parmregs, NULL); - - /* If this is a CALL_PLACEHOLDER insn then we need to look into the - three attached sequences: normal call, sibling call and tail - recursion. */ - if (GET_CODE (insn) == CALL_INSN - && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER) - { - int i; - - for (i = 0; i < 3; i++) - save_parm_insns (XEXP (PATTERN (insn), i), - first_nonparm_insn); - } - } - } -} -/* Note whether a parameter is modified or not. */ - -static void -note_modified_parmregs (reg, x, data) - rtx reg; - rtx x ATTRIBUTE_UNUSED; - void *data ATTRIBUTE_UNUSED; -{ - if (GET_CODE (reg) == REG && in_nonparm_insns - && REGNO (reg) < max_parm_reg - && REGNO (reg) >= FIRST_PSEUDO_REGISTER - && parmdecl_map[REGNO (reg)] != 0) - TREE_READONLY (parmdecl_map[REGNO (reg)]) = 0; -} - /* Unfortunately, we need a global copy of const_equiv map for communication with a function called from note_stores. Be *very* careful that this is used properly in the presence of recursion. */ varray_type global_const_equiv_varray; - -#define FIXED_BASE_PLUS_P(X) \ - (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \ - && GET_CODE (XEXP (X, 0)) == REG \ - && REGNO (XEXP (X, 0)) >= FIRST_VIRTUAL_REGISTER \ - && REGNO (XEXP (X, 0)) <= LAST_VIRTUAL_REGISTER) -/* Called to set up a mapping for the case where a parameter is in a - register. If it is read-only and our argument is a constant, set up the - constant equivalence. - - If LOC is REG_USERVAR_P, the usual case, COPY must also have that flag set - if it is a register. - - Also, don't allow hard registers here; they might not be valid when - substituted into insns. */ -static void -process_reg_param (map, loc, copy) - struct inline_remap *map; - rtx loc, copy; -{ - if ((GET_CODE (copy) != REG && GET_CODE (copy) != SUBREG) - || (GET_CODE (copy) == REG && REG_USERVAR_P (loc) - && ! REG_USERVAR_P (copy)) - || (GET_CODE (copy) == REG - && REGNO (copy) < FIRST_PSEUDO_REGISTER)) - { - rtx temp = copy_to_mode_reg (GET_MODE (loc), copy); - REG_USERVAR_P (temp) = REG_USERVAR_P (loc); - if (CONSTANT_P (copy) || FIXED_BASE_PLUS_P (copy)) - SET_CONST_EQUIV_DATA (map, temp, copy, CONST_AGE_PARM); - copy = temp; - } - map->reg_map[REGNO (loc)] = copy; -} - -/* Compare two BLOCKs for qsort. The key we sort on is the - BLOCK_ABSTRACT_ORIGIN of the blocks. We cannot just subtract the - two pointers, because it may overflow sizeof(int). */ - -static int -compare_blocks (v1, v2) - const PTR v1; - const PTR v2; -{ - tree b1 = *((const tree *) v1); - tree b2 = *((const tree *) v2); - char *p1 = (char *) BLOCK_ABSTRACT_ORIGIN (b1); - char *p2 = (char *) BLOCK_ABSTRACT_ORIGIN (b2); - - if (p1 == p2) - return 0; - return p1 < p2 ? -1 : 1; -} - -/* Compare two BLOCKs for bsearch. The first pointer corresponds to - an original block; the second to a remapped equivalent. */ - -static int -find_block (v1, v2) - const PTR v1; - const PTR v2; -{ - const union tree_node *b1 = (const union tree_node *) v1; - tree b2 = *((const tree *) v2); - char *p1 = (char *) b1; - char *p2 = (char *) BLOCK_ABSTRACT_ORIGIN (b2); - - if (p1 == p2) - return 0; - return p1 < p2 ? -1 : 1; -} - -/* 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 - of 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; -{ - struct function *inlining_previous; - struct function *inl_f = DECL_SAVED_INSNS (fndecl); - tree formal, actual, block; - rtx parm_insns = inl_f->emit->x_first_insn; - rtx insns = (inl_f->inl_last_parm_insn - ? NEXT_INSN (inl_f->inl_last_parm_insn) - : parm_insns); - tree *arg_trees; - rtx *arg_vals; - int max_regno; - int i; - int min_labelno = inl_f->emit->x_first_label_num; - int max_labelno = inl_f->inl_max_label_num; - int nargs; - rtx loc; - rtx stack_save = 0; - rtx temp; - struct inline_remap *map = 0; - rtvec arg_vector = inl_f->original_arg_vector; - rtx static_chain_value = 0; - int inl_max_uid; - int eh_region_offset; - - /* The pointer used to track the true location of the memory used - for MAP->LABEL_MAP. */ - rtx *real_label_map = 0; - - /* Allow for equivalences of the pseudos we make for virtual fp and ap. */ - max_regno = inl_f->emit->x_reg_rtx_no + 3; - if (max_regno < FIRST_PSEUDO_REGISTER) - abort (); - - /* Pull out the decl for the function definition; fndecl may be a - local declaration, which would break DECL_ABSTRACT_ORIGIN. */ - fndecl = inl_f->decl; - - nargs = list_length (DECL_ARGUMENTS (fndecl)); - - if (cfun->preferred_stack_boundary < inl_f->preferred_stack_boundary) - cfun->preferred_stack_boundary = inl_f->preferred_stack_boundary; - - /* Check that the parms type match and that sufficient arguments were - passed. 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; - enum machine_mode mode; - - if (actual == 0) - return (rtx) (size_t) -1; - - arg = TREE_VALUE (actual); - mode = TYPE_MODE (DECL_ARG_TYPE (formal)); - - if (arg == error_mark_node - || mode != TYPE_MODE (TREE_TYPE (arg)) - /* If they are block mode, the types should match exactly. - They don't match exactly if TREE_TYPE (FORMAL) == ERROR_MARK_NODE, - which could happen if the parameter has incomplete type. */ - || (mode == BLKmode - && (TYPE_MAIN_VARIANT (TREE_TYPE (arg)) - != TYPE_MAIN_VARIANT (TREE_TYPE (formal))))) - return (rtx) (size_t) -1; - } - - /* If there is a TARGET which is a readonly BLKmode MEM and DECL_RESULT - is also a mem, we are going to lose the readonly on the stores, so don't - inline. */ - if (target != 0 && GET_CODE (target) == MEM && GET_MODE (target) == BLKmode - && RTX_UNCHANGING_P (target) && DECL_RTL_SET_P (DECL_RESULT (fndecl)) - && GET_CODE (DECL_RTL (DECL_RESULT (fndecl))) == MEM) - return (rtx) (size_t) -1; - - /* Extra arguments are valid, but will be ignored below, so we must - evaluate them here for side-effects. */ - for (; actual; actual = TREE_CHAIN (actual)) - expand_expr (TREE_VALUE (actual), const0_rtx, - TYPE_MODE (TREE_TYPE (TREE_VALUE (actual))), 0); - - /* Expand the function arguments. Do this first so that any - new registers get created before we allocate the maps. */ - - arg_vals = (rtx *) xmalloc (nargs * sizeof (rtx)); - arg_trees = (tree *) xmalloc (nargs * sizeof (tree)); - - for (formal = DECL_ARGUMENTS (fndecl), actual = parms, i = 0; - formal; - formal = TREE_CHAIN (formal), actual = TREE_CHAIN (actual), i++) - { - /* Actual parameter, converted to the type of the argument within the - function. */ - tree arg = convert (TREE_TYPE (formal), TREE_VALUE (actual)); - /* Mode of the variable used within the function. */ - enum machine_mode mode = TYPE_MODE (TREE_TYPE (formal)); - int invisiref = 0; - - arg_trees[i] = arg; - loc = RTVEC_ELT (arg_vector, i); - - /* If this is an object passed by invisible reference, we copy the - object into a stack slot and save its address. If this will go - into memory, we do nothing now. Otherwise, we just expand the - argument. */ - if (GET_CODE (loc) == MEM && GET_CODE (XEXP (loc, 0)) == REG - && REGNO (XEXP (loc, 0)) > LAST_VIRTUAL_REGISTER) - { - rtx stack_slot = assign_temp (TREE_TYPE (arg), 1, 1, 1); - - store_expr (arg, stack_slot, 0); - arg_vals[i] = XEXP (stack_slot, 0); - invisiref = 1; - } - else if (GET_CODE (loc) != MEM) - { - if (GET_MODE (loc) != TYPE_MODE (TREE_TYPE (arg))) - { - int unsignedp = TREE_UNSIGNED (TREE_TYPE (formal)); - enum machine_mode pmode = TYPE_MODE (TREE_TYPE (formal)); - - pmode = promote_mode (TREE_TYPE (formal), pmode, - &unsignedp, 0); - - if (GET_MODE (loc) != pmode) - abort (); - - /* The mode if LOC and ARG can differ if LOC was a variable - that had its mode promoted via PROMOTED_MODE. */ - arg_vals[i] = convert_modes (pmode, - TYPE_MODE (TREE_TYPE (arg)), - expand_expr (arg, NULL_RTX, mode, - EXPAND_SUM), - unsignedp); - } - else - arg_vals[i] = expand_expr (arg, NULL_RTX, mode, EXPAND_SUM); - } - else - arg_vals[i] = 0; - - /* If the formal type was const but the actual was not, we might - end up here with an rtx wrongly tagged unchanging in the caller's - context. Fix that. */ - if (arg_vals[i] != 0 - && (GET_CODE (arg_vals[i]) == REG || GET_CODE (arg_vals[i]) == MEM) - && ! TREE_READONLY (TREE_VALUE (actual))) - RTX_UNCHANGING_P (arg_vals[i]) = 0; - - if (arg_vals[i] != 0 - && (! TREE_READONLY (formal) - /* If the parameter is not read-only, copy our argument through - a register. Also, we cannot use ARG_VALS[I] if it overlaps - TARGET in any way. In the inline function, they will likely - be two different pseudos, and `safe_from_p' will make all - sorts of smart assumptions about their not conflicting. - But if ARG_VALS[I] overlaps TARGET, these assumptions are - wrong, so put ARG_VALS[I] into a fresh register. - Don't worry about invisible references, since their stack - temps will never overlap the target. */ - || (target != 0 - && ! invisiref - && (GET_CODE (arg_vals[i]) == REG - || GET_CODE (arg_vals[i]) == SUBREG - || GET_CODE (arg_vals[i]) == MEM) - && reg_overlap_mentioned_p (arg_vals[i], target)) - /* ??? We must always copy a SUBREG into a REG, because it might - get substituted into an address, and not all ports correctly - handle SUBREGs in addresses. */ - || (GET_CODE (arg_vals[i]) == SUBREG))) - arg_vals[i] = copy_to_mode_reg (GET_MODE (loc), arg_vals[i]); - - if (arg_vals[i] != 0 && GET_CODE (arg_vals[i]) == REG - && POINTER_TYPE_P (TREE_TYPE (formal))) - mark_reg_pointer (arg_vals[i], - TYPE_ALIGN (TREE_TYPE (TREE_TYPE (formal)))); - } - - /* Allocate the structures we use to remap things. */ - - map = (struct inline_remap *) xcalloc (1, sizeof (struct inline_remap)); - map->fndecl = fndecl; - - VARRAY_TREE_INIT (map->block_map, 10, "block_map"); - map->reg_map = (rtx *) xcalloc (max_regno, sizeof (rtx)); - - /* We used to use alloca here, but the size of what it would try to - allocate would occasionally cause it to exceed the stack limit and - cause unpredictable core dumps. */ - real_label_map - = (rtx *) xmalloc ((max_labelno) * sizeof (rtx)); - map->label_map = real_label_map; - map->local_return_label = NULL_RTX; - - inl_max_uid = (inl_f->emit->x_cur_insn_uid + 1); - map->insn_map = (rtx *) xcalloc (inl_max_uid, sizeof (rtx)); - map->min_insnno = 0; - map->max_insnno = inl_max_uid; - - map->integrating = 1; - map->compare_src = NULL_RTX; - map->compare_mode = VOIDmode; - - /* const_equiv_varray maps pseudos in our routine to constants, so - it needs to be large enough for all our pseudos. This is the - number we are currently using plus the number in the called - routine, plus 15 for each arg, five to compute the virtual frame - pointer, and five for the return value. This should be enough - for most cases. We do not reference entries outside the range of - the map. - - ??? These numbers are quite arbitrary and were obtained by - experimentation. At some point, we should try to allocate the - table after all the parameters are set up so we can more accurately - estimate the number of pseudos we will need. */ - - VARRAY_CONST_EQUIV_INIT (map->const_equiv_varray, - (max_reg_num () - + (max_regno - FIRST_PSEUDO_REGISTER) - + 15 * nargs - + 10), - "expand_inline_function"); - map->const_age = 0; - - /* Record the current insn in case we have to set up pointers to frame - and argument memory blocks. If there are no insns yet, add a dummy - insn that can be used as an insertion point. */ - map->insns_at_start = get_last_insn (); - if (map->insns_at_start == 0) - map->insns_at_start = emit_note (NULL, NOTE_INSN_DELETED); - - map->regno_pointer_align = inl_f->emit->regno_pointer_align; - map->x_regno_reg_rtx = inl_f->emit->x_regno_reg_rtx; - - /* Update the outgoing argument size to allow for those in the inlined - function. */ - if (inl_f->outgoing_args_size > current_function_outgoing_args_size) - current_function_outgoing_args_size = inl_f->outgoing_args_size; - - /* If the inline function needs to make PIC references, that means - that this function's PIC offset table must be used. */ - if (inl_f->uses_pic_offset_table) - current_function_uses_pic_offset_table = 1; - - /* If this function needs a context, set it up. */ - if (inl_f->needs_context) - static_chain_value = lookup_static_chain (fndecl); - - /* If the inlined function calls __builtin_constant_p, then we'll - need to call purge_builtin_constant_p on this function. */ - if (inl_f->calls_constant_p) - current_function_calls_constant_p = 1; - - if (GET_CODE (parm_insns) == NOTE - && NOTE_LINE_NUMBER (parm_insns) > 0) - { - rtx note = emit_note (NOTE_SOURCE_FILE (parm_insns), - NOTE_LINE_NUMBER (parm_insns)); - if (note) - RTX_INTEGRATED_P (note) = 1; - } - - /* Process each argument. For each, set up things so that the function's - reference to the argument will refer to the argument being passed. - We only replace REG with REG here. Any simplifications are done - via const_equiv_map. - - We make two passes: In the first, we deal with parameters that will - be placed into registers, since we need to ensure that the allocated - register number fits in const_equiv_map. Then we store all non-register - parameters into their memory location. */ - - /* Don't try to free temp stack slots here, because we may put one of the - parameters into a temp stack slot. */ - - for (i = 0; i < nargs; i++) - { - rtx copy = arg_vals[i]; - - loc = RTVEC_ELT (arg_vector, i); - - /* There are three cases, each handled separately. */ - if (GET_CODE (loc) == MEM && GET_CODE (XEXP (loc, 0)) == REG - && REGNO (XEXP (loc, 0)) > LAST_VIRTUAL_REGISTER) - { - /* This must be an object passed by invisible reference (it could - also be a variable-sized object, but we forbid inlining functions - with variable-sized arguments). COPY is the address of the - actual value (this computation will cause it to be copied). We - map that address for the register, noting the actual address as - an equivalent in case it can be substituted into the insns. */ - - if (GET_CODE (copy) != REG) - { - temp = copy_addr_to_reg (copy); - if (CONSTANT_P (copy) || FIXED_BASE_PLUS_P (copy)) - SET_CONST_EQUIV_DATA (map, temp, copy, CONST_AGE_PARM); - copy = temp; - } - map->reg_map[REGNO (XEXP (loc, 0))] = copy; - } - else if (GET_CODE (loc) == MEM) - { - /* This is the case of a parameter that lives in memory. It - will live in the block we allocate in the called routine's - frame that simulates the incoming argument area. Do nothing - with the parameter now; we will call store_expr later. In - this case, however, we must ensure that the virtual stack and - incoming arg rtx values are expanded now so that we can be - sure we have enough slots in the const equiv map since the - store_expr call can easily blow the size estimate. */ - if (DECL_SAVED_INSNS (fndecl)->args_size != 0) - copy_rtx_and_substitute (virtual_incoming_args_rtx, map, 0); - } - else if (GET_CODE (loc) == REG) - process_reg_param (map, loc, copy); - else if (GET_CODE (loc) == CONCAT) - { - rtx locreal = gen_realpart (GET_MODE (XEXP (loc, 0)), loc); - rtx locimag = gen_imagpart (GET_MODE (XEXP (loc, 0)), loc); - rtx copyreal = gen_realpart (GET_MODE (locreal), copy); - rtx copyimag = gen_imagpart (GET_MODE (locimag), copy); - - process_reg_param (map, locreal, copyreal); - process_reg_param (map, locimag, copyimag); - } - else - abort (); - } - - /* Tell copy_rtx_and_substitute to handle constant pool SYMBOL_REFs - specially. This function can be called recursively, so we need to - save the previous value. */ - inlining_previous = inlining; - inlining = inl_f; - - /* Now do the parameters that will be placed in memory. */ - - for (formal = DECL_ARGUMENTS (fndecl), i = 0; - formal; formal = TREE_CHAIN (formal), i++) - { - loc = RTVEC_ELT (arg_vector, i); - - if (GET_CODE (loc) == MEM - /* Exclude case handled above. */ - && ! (GET_CODE (XEXP (loc, 0)) == REG - && REGNO (XEXP (loc, 0)) > LAST_VIRTUAL_REGISTER)) - { - rtx note = emit_note (DECL_SOURCE_FILE (formal), - DECL_SOURCE_LINE (formal)); - if (note) - RTX_INTEGRATED_P (note) = 1; - - /* Compute the address in the area we reserved and store the - value there. */ - temp = copy_rtx_and_substitute (loc, map, 1); - subst_constants (&temp, NULL_RTX, map, 1); - apply_change_group (); - if (! memory_address_p (GET_MODE (temp), XEXP (temp, 0))) - temp = change_address (temp, VOIDmode, XEXP (temp, 0)); - store_expr (arg_trees[i], temp, 0); - } - } - - /* Deal with the places that the function puts its result. - We are driven by what is placed into DECL_RESULT. - - Initially, we assume that we don't have anything special handling for - REG_FUNCTION_RETURN_VALUE_P. */ - - map->inline_target = 0; - loc = (DECL_RTL_SET_P (DECL_RESULT (fndecl)) - ? DECL_RTL (DECL_RESULT (fndecl)) : NULL_RTX); - - if (TYPE_MODE (type) == VOIDmode) - /* There is no return value to worry about. */ - ; - else if (GET_CODE (loc) == MEM) - { - if (GET_CODE (XEXP (loc, 0)) == ADDRESSOF) - { - temp = copy_rtx_and_substitute (loc, map, 1); - subst_constants (&temp, NULL_RTX, map, 1); - apply_change_group (); - target = temp; - } - else - { - if (! structure_value_addr - || ! aggregate_value_p (DECL_RESULT (fndecl))) - abort (); - - /* Pass the function the address in which to return a structure - value. Note that a constructor can cause someone to call us - with STRUCTURE_VALUE_ADDR, but the initialization takes place - via the first parameter, rather than the struct return address. - - We have two cases: If the address is a simple register - indirect, use the mapping mechanism to point that register to - our structure return address. Otherwise, store the structure - return value into the place that it will be referenced from. */ - - if (GET_CODE (XEXP (loc, 0)) == REG) - { - temp = force_operand (structure_value_addr, NULL_RTX); - temp = force_reg (Pmode, temp); - /* A virtual register might be invalid in an insn, because - it can cause trouble in reload. Since we don't have access - to the expanders at map translation time, make sure we have - a proper register now. - If a virtual register is actually valid, cse or combine - can put it into the mapped insns. */ - if (REGNO (temp) >= FIRST_VIRTUAL_REGISTER - && REGNO (temp) <= LAST_VIRTUAL_REGISTER) - temp = copy_to_mode_reg (Pmode, temp); - map->reg_map[REGNO (XEXP (loc, 0))] = temp; - - if (CONSTANT_P (structure_value_addr) - || GET_CODE (structure_value_addr) == ADDRESSOF - || (GET_CODE (structure_value_addr) == PLUS - && (XEXP (structure_value_addr, 0) - == virtual_stack_vars_rtx) - && (GET_CODE (XEXP (structure_value_addr, 1)) - == CONST_INT))) - { - SET_CONST_EQUIV_DATA (map, temp, structure_value_addr, - CONST_AGE_PARM); - } - } - else - { - temp = copy_rtx_and_substitute (loc, map, 1); - subst_constants (&temp, NULL_RTX, map, 0); - apply_change_group (); - emit_move_insn (temp, structure_value_addr); - } - } - } - else if (ignore) - /* We will ignore the result value, so don't look at its structure. - Note that preparations for an aggregate return value - do need to be made (above) even if it will be ignored. */ - ; - else if (GET_CODE (loc) == REG) - { - /* The function returns an object in a register and we use the return - value. Set up our target for remapping. */ - - /* 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). - We have to use the mode of the result's RTL, rather than - its type, since expand_function_start may have promoted it. */ - enum machine_mode arriving_mode - = GET_MODE (DECL_RTL (DECL_RESULT (fndecl))); - rtx reg_to_map; - - /* 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 == 0 || GET_CODE (target) != REG - || GET_MODE (target) != departing_mode) - { - /* Don't make BLKmode registers. If this looks like - a BLKmode object being returned in a register, get - the mode from that, otherwise abort. */ - if (departing_mode == BLKmode) - { - if (REG == GET_CODE (DECL_RTL (DECL_RESULT (fndecl)))) - { - departing_mode = GET_MODE (DECL_RTL (DECL_RESULT (fndecl))); - arriving_mode = departing_mode; - } - else - abort (); - } - - 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) - { - /* Avoid creating a paradoxical subreg wider than - BITS_PER_WORD, since that is illegal. */ - if (GET_MODE_BITSIZE (arriving_mode) > BITS_PER_WORD) - { - if (!TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (departing_mode), - GET_MODE_BITSIZE (arriving_mode))) - /* Maybe could be handled by using convert_move () ? */ - abort (); - reg_to_map = gen_reg_rtx (arriving_mode); - target = gen_lowpart (departing_mode, reg_to_map); - } - else - reg_to_map = gen_rtx_SUBREG (arriving_mode, target, 0); - } - else - reg_to_map = target; - - /* Usually, the result value is the machine's return register. - Sometimes it may be a pseudo. Handle both cases. */ - if (REG_FUNCTION_VALUE_P (loc)) - map->inline_target = reg_to_map; - else - map->reg_map[REGNO (loc)] = reg_to_map; - } - else if (GET_CODE (loc) == CONCAT) - { - enum machine_mode departing_mode = TYPE_MODE (type); - enum machine_mode arriving_mode - = GET_MODE (DECL_RTL (DECL_RESULT (fndecl))); - - if (departing_mode != arriving_mode) - abort (); - if (GET_CODE (XEXP (loc, 0)) != REG - || GET_CODE (XEXP (loc, 1)) != REG) - abort (); - - /* 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 == 0 || GET_CODE (target) != REG - || GET_MODE (target) != departing_mode) - target = gen_reg_rtx (departing_mode); - - if (GET_CODE (target) != CONCAT) - abort (); - - map->reg_map[REGNO (XEXP (loc, 0))] = XEXP (target, 0); - map->reg_map[REGNO (XEXP (loc, 1))] = XEXP (target, 1); - } - else - abort (); - - /* Remap the exception handler data pointer from one to the other. */ - temp = get_exception_pointer (inl_f); - if (temp) - map->reg_map[REGNO (temp)] = get_exception_pointer (cfun); - - /* Initialize label_map. get_label_from_map will actually make - the labels. */ - memset ((char *) &map->label_map[min_labelno], 0, - (max_labelno - min_labelno) * sizeof (rtx)); - - /* 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. Set - up things so that lookup_static_chain knows that to interpret registers - in SAVE_EXPRs for TYPE_SIZEs as local. */ - inline_function_decl = fndecl; - integrate_parm_decls (DECL_ARGUMENTS (fndecl), map, arg_vector); - block = integrate_decl_tree (inl_f->original_decl_initial, map); - BLOCK_ABSTRACT_ORIGIN (block) = DECL_ORIGIN (fndecl); - inline_function_decl = 0; - - /* Make a fresh binding contour that we can easily remove. Do this after - expanding our arguments so cleanups are properly scoped. */ - expand_start_bindings_and_block (0, block); - - /* Sort the block-map so that it will be easy to find remapped - blocks later. */ - qsort (&VARRAY_TREE (map->block_map, 0), - map->block_map->elements_used, - sizeof (tree), - compare_blocks); - - /* Perform postincrements before actually calling the function. */ - emit_queue (); - - /* Clean up stack so that variables might have smaller offsets. */ - do_pending_stack_adjust (); - - /* Save a copy of the location of const_equiv_varray for - mark_stores, called via note_stores. */ - global_const_equiv_varray = map->const_equiv_varray; - - /* If the called function does an alloca, save and restore the - stack pointer around the call. This saves stack space, but - also is required if this inline is being done between two - pushes. */ - if (inl_f->calls_alloca) - emit_stack_save (SAVE_BLOCK, &stack_save, NULL_RTX); - - /* Map pseudos used for initial hard reg values. */ - setup_initial_hard_reg_value_integration (inl_f, map); - - /* Now copy the insns one by one. */ - copy_insn_list (insns, map, static_chain_value); - - /* Duplicate the EH regions. This will create an offset from the - region numbers in the function we're inlining to the region - numbers in the calling function. This must wait until after - copy_insn_list, as we need the insn map to be complete. */ - eh_region_offset = duplicate_eh_regions (inl_f, map); - - /* Now copy the REG_NOTES for those insns. */ - copy_insn_notes (insns, map, eh_region_offset); - - /* If the insn sequence required one, emit the return label. */ - if (map->local_return_label) - emit_label (map->local_return_label); - - /* Restore the stack pointer if we saved it above. */ - if (inl_f->calls_alloca) - emit_stack_restore (SAVE_BLOCK, stack_save, NULL_RTX); - - if (! cfun->x_whole_function_mode_p) - /* In statement-at-a-time mode, we just tell the front-end to add - this block to the list of blocks at this binding level. We - can't do it the way it's done for function-at-a-time mode the - superblocks have not been created yet. */ - (*lang_hooks.decls.insert_block) (block); - else - { - BLOCK_CHAIN (block) - = BLOCK_CHAIN (DECL_INITIAL (current_function_decl)); - BLOCK_CHAIN (DECL_INITIAL (current_function_decl)) = block; - } - - /* End the scope containing the copied formal parameter variables - and copied LABEL_DECLs. We pass NULL_TREE for the variables list - here so that expand_end_bindings will not check for unused - variables. That's already been checked for when the inlined - function was defined. */ - expand_end_bindings (NULL_TREE, 1, 1); - - /* Must mark the line number note after inlined functions as a repeat, so - that the test coverage code can avoid counting the call twice. This - just tells the code to ignore the immediately following line note, since - there already exists a copy of this note before the expanded inline call. - This line number note is still needed for debugging though, so we can't - delete it. */ - if (flag_test_coverage) - emit_note (0, NOTE_INSN_REPEATED_LINE_NUMBER); - - emit_line_note (input_filename, input_line); - - /* If the function returns a BLKmode object in a register, copy it - out of the temp register into a BLKmode memory object. */ - if (target - && TYPE_MODE (TREE_TYPE (TREE_TYPE (fndecl))) == BLKmode - && ! aggregate_value_p (TREE_TYPE (TREE_TYPE (fndecl)))) - target = copy_blkmode_from_reg (0, target, TREE_TYPE (TREE_TYPE (fndecl))); - - if (structure_value_addr) - { - target = gen_rtx_MEM (TYPE_MODE (type), - memory_address (TYPE_MODE (type), - structure_value_addr)); - set_mem_attributes (target, type, 1); - } - - /* Make sure we free the things we explicitly allocated with xmalloc. */ - if (real_label_map) - free (real_label_map); - VARRAY_FREE (map->const_equiv_varray); - free (map->reg_map); - free (map->insn_map); - free (map); - free (arg_vals); - free (arg_trees); - - inlining = inlining_previous; - - return target; -} - -/* Make copies of each insn in the given list using the mapping - computed in expand_inline_function. This function may call itself for - insns containing sequences. - - Copying is done in two passes, first the insns and then their REG_NOTES. - - If static_chain_value is nonzero, it represents the context-pointer - register for the function. */ - -static void -copy_insn_list (insns, map, static_chain_value) - rtx insns; - struct inline_remap *map; - rtx static_chain_value; -{ - int i; - rtx insn; - rtx temp; -#ifdef HAVE_cc0 - rtx cc0_insn = 0; -#endif - rtx static_chain_mem = 0; - - /* Copy the insns one by one. Do this in two passes, first the insns and - then their REG_NOTES. */ - - /* This loop is very similar to the loop in copy_loop_body in unroll.c. */ - - for (insn = insns; insn; insn = NEXT_INSN (insn)) - { - rtx copy, pattern, set; - - map->orig_asm_operands_vector = 0; - - switch (GET_CODE (insn)) - { - case INSN: - pattern = PATTERN (insn); - set = single_set (insn); - copy = 0; - if (GET_CODE (pattern) == USE - && GET_CODE (XEXP (pattern, 0)) == REG - && REG_FUNCTION_VALUE_P (XEXP (pattern, 0))) - /* The (USE (REG n)) at return from the function should - be ignored since we are changing (REG n) into - inline_target. */ - break; - - /* Ignore setting a function value that we don't want to use. */ - if (map->inline_target == 0 - && set != 0 - && GET_CODE (SET_DEST (set)) == REG - && REG_FUNCTION_VALUE_P (SET_DEST (set))) - { - if (volatile_refs_p (SET_SRC (set))) - { - rtx new_set; - - /* If we must not delete the source, - load it into a new temporary. */ - copy = emit_insn (copy_rtx_and_substitute (pattern, map, 0)); - - new_set = single_set (copy); - if (new_set == 0) - abort (); - - SET_DEST (new_set) - = gen_reg_rtx (GET_MODE (SET_DEST (new_set))); - } - /* If the source and destination are the same and it - has a note on it, keep the insn. */ - else if (rtx_equal_p (SET_DEST (set), SET_SRC (set)) - && REG_NOTES (insn) != 0) - copy = emit_insn (copy_rtx_and_substitute (pattern, map, 0)); - else - break; - } - - /* Similarly if an ignored return value is clobbered. */ - else if (map->inline_target == 0 - && GET_CODE (pattern) == CLOBBER - && GET_CODE (XEXP (pattern, 0)) == REG - && REG_FUNCTION_VALUE_P (XEXP (pattern, 0))) - break; - - /* Look for the address of the static chain slot. The - rtx_equal_p comparisons against the - static_chain_incoming_rtx below may fail if the static - chain is in memory and the address specified is not - "legitimate". This happens on Xtensa where the static - chain is at a negative offset from argp and where only - positive offsets are legitimate. When the RTL is - generated, the address is "legitimized" by copying it - into a register, causing the rtx_equal_p comparisons to - fail. This workaround looks for code that sets a - register to the address of the static chain. Subsequent - memory references via that register can then be - identified as static chain references. We assume that - the register is only assigned once, and that the static - chain address is only live in one register at a time. */ - - else if (static_chain_value != 0 - && set != 0 - && GET_CODE (static_chain_incoming_rtx) == MEM - && GET_CODE (SET_DEST (set)) == REG - && rtx_equal_p (SET_SRC (set), - XEXP (static_chain_incoming_rtx, 0))) - { - static_chain_mem = - gen_rtx_MEM (GET_MODE (static_chain_incoming_rtx), - SET_DEST (set)); - - /* emit the instruction in case it is used for something - other than setting the static chain; if it's not used, - it can always be removed as dead code */ - copy = emit_insn (copy_rtx_and_substitute (pattern, map, 0)); - } - - /* If this is setting the static chain rtx, omit it. */ - else if (static_chain_value != 0 - && set != 0 - && (rtx_equal_p (SET_DEST (set), - static_chain_incoming_rtx) - || (static_chain_mem - && rtx_equal_p (SET_DEST (set), static_chain_mem)))) - break; - - /* If this is setting the static chain pseudo, set it from - the value we want to give it instead. */ - else if (static_chain_value != 0 - && set != 0 - && (rtx_equal_p (SET_SRC (set), - static_chain_incoming_rtx) - || (static_chain_mem - && rtx_equal_p (SET_SRC (set), static_chain_mem)))) - { - rtx newdest = copy_rtx_and_substitute (SET_DEST (set), map, 1); - - copy = emit_move_insn (newdest, static_chain_value); - if (GET_CODE (static_chain_incoming_rtx) != MEM) - static_chain_value = 0; - } - - /* If this is setting the virtual stack vars register, this must - be the code at the handler for a builtin longjmp. The value - saved in the setjmp buffer will be the address of the frame - we've made for this inlined instance within our frame. But we - know the offset of that value so we can use it to reconstruct - our virtual stack vars register from that value. If we are - copying it from the stack pointer, leave it unchanged. */ - else if (set != 0 - && rtx_equal_p (SET_DEST (set), virtual_stack_vars_rtx)) - { - HOST_WIDE_INT offset; - temp = map->reg_map[REGNO (SET_DEST (set))]; - temp = VARRAY_CONST_EQUIV (map->const_equiv_varray, - REGNO (temp)).rtx; - - if (rtx_equal_p (temp, virtual_stack_vars_rtx)) - offset = 0; - else if (GET_CODE (temp) == PLUS - && rtx_equal_p (XEXP (temp, 0), virtual_stack_vars_rtx) - && GET_CODE (XEXP (temp, 1)) == CONST_INT) - offset = INTVAL (XEXP (temp, 1)); - else - abort (); - - if (rtx_equal_p (SET_SRC (set), stack_pointer_rtx)) - temp = SET_SRC (set); - else - temp = force_operand (plus_constant (SET_SRC (set), - - offset), - NULL_RTX); - - copy = emit_move_insn (virtual_stack_vars_rtx, temp); - } - - else - copy = emit_insn (copy_rtx_and_substitute (pattern, map, 0)); - /* REG_NOTES will be copied later. */ - -#ifdef HAVE_cc0 - /* If this insn is setting CC0, it may need to look at - the insn that uses CC0 to see what type of insn it is. - In that case, the call to recog via validate_change will - fail. So don't substitute constants here. Instead, - do it when we emit the following insn. - - For example, see the pyr.md file. That machine has signed and - unsigned compares. The compare patterns must check the - following branch insn to see which what kind of compare to - emit. - - If the previous insn set CC0, substitute constants on it as - well. */ - if (sets_cc0_p (PATTERN (copy)) != 0) - cc0_insn = copy; - else - { - if (cc0_insn) - try_constants (cc0_insn, map); - cc0_insn = 0; - try_constants (copy, map); - } -#else - try_constants (copy, map); -#endif - INSN_SCOPE (copy) = INSN_SCOPE (insn); - break; - - case JUMP_INSN: - if (map->integrating && returnjump_p (insn)) - { - if (map->local_return_label == 0) - map->local_return_label = gen_label_rtx (); - pattern = gen_jump (map->local_return_label); - } - else - pattern = copy_rtx_and_substitute (PATTERN (insn), map, 0); - - copy = emit_jump_insn (pattern); - -#ifdef HAVE_cc0 - if (cc0_insn) - try_constants (cc0_insn, map); - cc0_insn = 0; -#endif - try_constants (copy, map); - INSN_SCOPE (copy) = INSN_SCOPE (insn); - - /* If this used to be a conditional jump insn but whose branch - direction is now know, we must do something special. */ - if (any_condjump_p (insn) && onlyjump_p (insn) && map->last_pc_value) - { -#ifdef HAVE_cc0 - /* If the previous insn set cc0 for us, delete it. */ - if (only_sets_cc0_p (PREV_INSN (copy))) - delete_related_insns (PREV_INSN (copy)); -#endif - - /* If this is now a no-op, delete it. */ - if (map->last_pc_value == pc_rtx) - { - delete_related_insns (copy); - copy = 0; - } - else - /* Otherwise, this is unconditional jump so we must put a - BARRIER after it. We could do some dead code elimination - here, but jump.c will do it just as well. */ - emit_barrier (); - } - break; - - case CALL_INSN: - /* If this is a CALL_PLACEHOLDER insn then we need to copy the - three attached sequences: normal call, sibling call and tail - recursion. */ - if (GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER) - { - rtx sequence[3]; - rtx tail_label; - - for (i = 0; i < 3; i++) - { - rtx seq; - - sequence[i] = NULL_RTX; - seq = XEXP (PATTERN (insn), i); - if (seq) - { - start_sequence (); - copy_insn_list (seq, map, static_chain_value); - sequence[i] = get_insns (); - end_sequence (); - } - } - - /* Find the new tail recursion label. - It will already be substituted into sequence[2]. */ - tail_label = copy_rtx_and_substitute (XEXP (PATTERN (insn), 3), - map, 0); - - copy = emit_call_insn (gen_rtx_CALL_PLACEHOLDER (VOIDmode, - sequence[0], - sequence[1], - sequence[2], - tail_label)); - break; - } - - pattern = copy_rtx_and_substitute (PATTERN (insn), map, 0); - copy = emit_call_insn (pattern); - - SIBLING_CALL_P (copy) = SIBLING_CALL_P (insn); - CONST_OR_PURE_CALL_P (copy) = CONST_OR_PURE_CALL_P (insn); - INSN_SCOPE (copy) = INSN_SCOPE (insn); - - /* Because the USAGE information potentially contains objects other - than hard registers, we need to copy it. */ - - CALL_INSN_FUNCTION_USAGE (copy) - = copy_rtx_and_substitute (CALL_INSN_FUNCTION_USAGE (insn), - map, 0); - -#ifdef HAVE_cc0 - if (cc0_insn) - try_constants (cc0_insn, map); - cc0_insn = 0; -#endif - try_constants (copy, map); - - /* Be lazy and assume CALL_INSNs clobber all hard registers. */ - for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) - VARRAY_CONST_EQUIV (map->const_equiv_varray, i).rtx = 0; - break; - - case CODE_LABEL: - copy = emit_label (get_label_from_map (map, - CODE_LABEL_NUMBER (insn))); - LABEL_NAME (copy) = LABEL_NAME (insn); - map->const_age++; - break; - - case BARRIER: - copy = emit_barrier (); - break; - - case NOTE: - if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED_LABEL) - { - copy = emit_label (get_label_from_map (map, - CODE_LABEL_NUMBER (insn))); - LABEL_NAME (copy) = NOTE_SOURCE_FILE (insn); - map->const_age++; - break; - } - - /* NOTE_INSN_FUNCTION_END and NOTE_INSN_FUNCTION_BEG are - discarded because it is important to have only one of - each in the current function. - - NOTE_INSN_DELETED notes aren't useful. */ - - if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_END - && NOTE_LINE_NUMBER (insn) != NOTE_INSN_FUNCTION_BEG - && NOTE_LINE_NUMBER (insn) != NOTE_INSN_DELETED) - { - copy = emit_note (NOTE_SOURCE_FILE (insn), - NOTE_LINE_NUMBER (insn)); - if (copy - && (NOTE_LINE_NUMBER (copy) == NOTE_INSN_BLOCK_BEG - || NOTE_LINE_NUMBER (copy) == NOTE_INSN_BLOCK_END) - && NOTE_BLOCK (insn)) - { - tree *mapped_block_p; - - mapped_block_p - = (tree *) bsearch (NOTE_BLOCK (insn), - &VARRAY_TREE (map->block_map, 0), - map->block_map->elements_used, - sizeof (tree), - find_block); - - if (!mapped_block_p) - abort (); - else - NOTE_BLOCK (copy) = *mapped_block_p; - } - else if (copy - && NOTE_LINE_NUMBER (copy) == NOTE_INSN_EXPECTED_VALUE) - NOTE_EXPECTED_VALUE (copy) - = copy_rtx_and_substitute (NOTE_EXPECTED_VALUE (insn), - map, 0); - } - else - copy = 0; - break; - - default: - abort (); - } - - if (copy) - RTX_INTEGRATED_P (copy) = 1; - - map->insn_map[INSN_UID (insn)] = copy; - } -} - -/* Copy the REG_NOTES. Increment const_age, so that only constants - from parameters can be substituted in. These are the only ones - that are valid across the entire function. */ - -static void -copy_insn_notes (insns, map, eh_region_offset) - rtx insns; - struct inline_remap *map; - int eh_region_offset; -{ - rtx insn, new_insn; - - map->const_age++; - for (insn = insns; insn; insn = NEXT_INSN (insn)) - { - if (! INSN_P (insn)) - continue; - - new_insn = map->insn_map[INSN_UID (insn)]; - if (! new_insn) - continue; - - if (REG_NOTES (insn)) - { - rtx next, note = copy_rtx_and_substitute (REG_NOTES (insn), map, 0); - - /* We must also do subst_constants, in case one of our parameters - has const type and constant value. */ - subst_constants (¬e, NULL_RTX, map, 0); - apply_change_group (); - REG_NOTES (new_insn) = note; - - /* Delete any REG_LABEL notes from the chain. Remap any - REG_EH_REGION notes. */ - for (; note; note = next) - { - next = XEXP (note, 1); - if (REG_NOTE_KIND (note) == REG_LABEL) - remove_note (new_insn, note); - else if (REG_NOTE_KIND (note) == REG_EH_REGION - && INTVAL (XEXP (note, 0)) > 0) - XEXP (note, 0) = GEN_INT (INTVAL (XEXP (note, 0)) - + eh_region_offset); - } - } - - if (GET_CODE (insn) == CALL_INSN - && GET_CODE (PATTERN (insn)) == CALL_PLACEHOLDER) - { - int i; - for (i = 0; i < 3; i++) - copy_insn_notes (XEXP (PATTERN (insn), i), map, eh_region_offset); - } - - if (GET_CODE (insn) == JUMP_INSN - && GET_CODE (PATTERN (insn)) == RESX) - XINT (PATTERN (new_insn), 0) += eh_region_offset; - } -} - -/* Given a chain of PARM_DECLs, ARGS, copy each decl into a VAR_DECL, - push all of those decls and give each one the corresponding home. */ - -static void -integrate_parm_decls (args, map, arg_vector) - tree args; - struct inline_remap *map; - rtvec arg_vector; -{ - tree tail; - int i; - - for (tail = args, i = 0; tail; tail = TREE_CHAIN (tail), i++) - { - tree decl = copy_decl_for_inlining (tail, map->fndecl, - current_function_decl); - rtx new_decl_rtl - = copy_rtx_and_substitute (RTVEC_ELT (arg_vector, i), map, 1); - - /* We really should be setting DECL_INCOMING_RTL to something reasonable - here, but that's going to require some more work. */ - /* DECL_INCOMING_RTL (decl) = ?; */ - /* Fully instantiate the address with the equivalent form so that the - debugging information contains the actual register, instead of the - virtual register. Do this by not passing an insn to - subst_constants. */ - subst_constants (&new_decl_rtl, NULL_RTX, map, 1); - apply_change_group (); - SET_DECL_RTL (decl, new_decl_rtl); - } -} - -/* Given a BLOCK node LET, push decls and levels so as to construct in the - current function a tree of contexts isomorphic to the one that is given. - - MAP, if nonzero, is a pointer to an inline_remap map which indicates how - registers used in the DECL_RTL field should be remapped. If it is zero, - no mapping is necessary. */ - -static tree -integrate_decl_tree (let, map) - tree let; - struct inline_remap *map; -{ - tree t; - tree new_block; - tree *next; - - new_block = make_node (BLOCK); - VARRAY_PUSH_TREE (map->block_map, new_block); - next = &BLOCK_VARS (new_block); - - for (t = BLOCK_VARS (let); t; t = TREE_CHAIN (t)) - { - tree d; - - d = copy_decl_for_inlining (t, map->fndecl, current_function_decl); - - if (DECL_RTL_SET_P (t)) - { - rtx r; - - SET_DECL_RTL (d, copy_rtx_and_substitute (DECL_RTL (t), map, 1)); - - /* Fully instantiate the address with the equivalent form so that the - debugging information contains the actual register, instead of the - virtual register. Do this by not passing an insn to - subst_constants. */ - r = DECL_RTL (d); - subst_constants (&r, NULL_RTX, map, 1); - SET_DECL_RTL (d, r); - - apply_change_group (); - } - - /* Add this declaration to the list of variables in the new - block. */ - *next = d; - next = &TREE_CHAIN (d); - } - - next = &BLOCK_SUBBLOCKS (new_block); - for (t = BLOCK_SUBBLOCKS (let); t; t = BLOCK_CHAIN (t)) - { - *next = integrate_decl_tree (t, map); - BLOCK_SUPERCONTEXT (*next) = new_block; - next = &BLOCK_CHAIN (*next); - } - - TREE_USED (new_block) = TREE_USED (let); - BLOCK_ABSTRACT_ORIGIN (new_block) = let; - - return new_block; -} - /* Create a new copy of an rtx. Recursively copies the operands of the rtx, except for those few rtx codes that are sharable. @@ -1892,18 +191,13 @@ integrate_decl_tree (let, map) rtl is ever emitted. If FOR_LHS is nonzero, if means we are processing something that will - be the LHS of a SET. In that case, we copy RTX_UNCHANGING_P even if - inlining since we need to be conservative in how it is set for - such cases. + be the LHS of a SET. Handle constants that need to be placed in the constant pool by calling `force_const_mem'. */ rtx -copy_rtx_and_substitute (orig, map, for_lhs) - rtx orig; - struct inline_remap *map; - int for_lhs; +copy_rtx_and_substitute (rtx orig, struct inline_remap *map, int for_lhs) { rtx copy, temp; int i, j; @@ -1926,9 +220,7 @@ copy_rtx_and_substitute (orig, map, for_lhs) Small hard registers are returned as-is. Pseudo-registers go through their `reg_map'. */ regno = REGNO (orig); - if (regno <= LAST_VIRTUAL_REGISTER - || (map->integrating - && DECL_SAVED_INSNS (map->fndecl)->internal_arg_pointer == orig)) + if (regno <= LAST_VIRTUAL_REGISTER) { /* Some hard registers are also mapped, but others are not translated. */ @@ -1946,10 +238,11 @@ copy_rtx_and_substitute (orig, map, for_lhs) else if (regno == VIRTUAL_STACK_VARS_REGNUM) { rtx loc, seq; - int size = get_func_frame_size (DECL_SAVED_INSNS (map->fndecl)); + int size + = get_func_frame_size (DECL_STRUCT_FUNCTION (map->fndecl)); #ifdef FRAME_GROWS_DOWNWARD int alignment - = (DECL_SAVED_INSNS (map->fndecl)->stack_alignment_needed + = (DECL_STRUCT_FUNCTION (map->fndecl)->stack_alignment_needed / BITS_PER_UNIT); /* In this case, virtual_stack_vars_rtx points to one byte @@ -1982,15 +275,12 @@ copy_rtx_and_substitute (orig, map, for_lhs) emit_insn_after (seq, map->insns_at_start); return temp; } - else if (regno == VIRTUAL_INCOMING_ARGS_REGNUM - || (map->integrating - && (DECL_SAVED_INSNS (map->fndecl)->internal_arg_pointer - == orig))) + else if (regno == VIRTUAL_INCOMING_ARGS_REGNUM) { /* Do the same for a block to contain any arguments referenced in memory. */ rtx loc, seq; - int size = DECL_SAVED_INSNS (map->fndecl)->args_size; + int size = DECL_STRUCT_FUNCTION (map->fndecl)->args_size; start_sequence (); loc = assign_stack_temp (BLKmode, size, 1); @@ -2015,46 +305,6 @@ copy_rtx_and_substitute (orig, map, for_lhs) emit_insn_after (seq, map->insns_at_start); return temp; } - else 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 the - mode doesn't agree, and it ain't BLKmode, make a SUBREG. */ - if (map->inline_target == 0) - { - if (rtx_equal_function_value_matters) - /* This is an ignored return value. We must not - leave it in with REG_FUNCTION_VALUE_P set, since - that would confuse subsequent inlining of the - current function into a later function. */ - return gen_rtx_REG (GET_MODE (orig), regno); - else - /* Must be unrolling loops or replicating code if we - reach here, so return the register unchanged. */ - return orig; - } - else if (GET_MODE (map->inline_target) != BLKmode - && mode != GET_MODE (map->inline_target)) - return gen_lowpart (mode, map->inline_target); - else - return map->inline_target; - } -#if defined (LEAF_REGISTERS) && defined (LEAF_REG_REMAP) - /* If leaf_renumber_regs_insn() might remap this register to - some other number, make sure we don't share it with the - inlined function, otherwise delayed optimization of the - inlined function may change it in place, breaking our - reference to it. We may still shared it within the - function, so create an entry for this register in the - reg_map. */ - if (map->integrating && regno < FIRST_PSEUDO_REGISTER - && LEAF_REGISTERS[regno] && LEAF_REG_REMAP (regno) != regno) - { - if (!map->leaf_reg_map[regno][mode]) - map->leaf_reg_map[regno][mode] = gen_rtx_REG (mode, regno); - return map->leaf_reg_map[regno][mode]; - } -#endif else return orig; @@ -2065,7 +315,6 @@ copy_rtx_and_substitute (orig, map, for_lhs) map->reg_map[regno] = gen_reg_rtx (mode); REG_USERVAR_P (map->reg_map[regno]) = REG_USERVAR_P (orig); REG_LOOP_TEST_P (map->reg_map[regno]) = REG_LOOP_TEST_P (orig); - RTX_UNCHANGING_P (map->reg_map[regno]) = RTX_UNCHANGING_P (orig); /* A reg with REG_FUNCTION_VALUE_P true will never reach here. */ if (REG_POINTER (map->x_regno_reg_rtx[regno])) @@ -2080,46 +329,11 @@ copy_rtx_and_substitute (orig, map, for_lhs) GET_MODE (SUBREG_REG (orig)), SUBREG_BYTE (orig)); - case ADDRESSOF: - copy = gen_rtx_ADDRESSOF (mode, - copy_rtx_and_substitute (XEXP (orig, 0), - map, for_lhs), - 0, ADDRESSOF_DECL (orig)); - regno = ADDRESSOF_REGNO (orig); - if (map->reg_map[regno]) - regno = REGNO (map->reg_map[regno]); - else if (regno > LAST_VIRTUAL_REGISTER) - { - temp = XEXP (orig, 0); - map->reg_map[regno] = gen_reg_rtx (GET_MODE (temp)); - REG_USERVAR_P (map->reg_map[regno]) = REG_USERVAR_P (temp); - REG_LOOP_TEST_P (map->reg_map[regno]) = REG_LOOP_TEST_P (temp); - RTX_UNCHANGING_P (map->reg_map[regno]) = RTX_UNCHANGING_P (temp); - /* A reg with REG_FUNCTION_VALUE_P true will never reach here. */ - - /* Objects may initially be represented as registers, but - but turned into a MEM if their address is taken by - put_var_into_stack. Therefore, the register table may have - entries which are MEMs. - - We briefly tried to clear such entries, but that ended up - cascading into many changes due to the optimizers not being - prepared for empty entries in the register table. So we've - decided to allow the MEMs in the register table for now. */ - if (REG_P (map->x_regno_reg_rtx[regno]) - && REG_POINTER (map->x_regno_reg_rtx[regno])) - mark_reg_pointer (map->reg_map[regno], - map->regno_pointer_align[regno]); - regno = REGNO (map->reg_map[regno]); - } - ADDRESSOF_REGNO (copy) = regno; - return copy; - case USE: case CLOBBER: /* USE and CLOBBER are ordinary, but we convert (use (subreg foo)) to (use foo) if the original insn didn't have a subreg. - Removing the subreg distorts the VAX movstrhi pattern + Removing the subreg distorts the VAX movmemhi pattern by changing the mode of an operand. */ copy = copy_rtx_and_substitute (XEXP (orig, 0), map, code == CLOBBER); if (GET_CODE (copy) == SUBREG && GET_CODE (XEXP (orig, 0)) != SUBREG) @@ -2132,7 +346,7 @@ copy_rtx_and_substitute (orig, map, for_lhs) if (NOTE_LINE_NUMBER (orig) != NOTE_INSN_DELETED_LABEL) break; - /* ... FALLTHRU ... */ + /* Fall through. */ case CODE_LABEL: LABEL_PRESERVE_P (get_label_from_map (map, CODE_LABEL_NUMBER (orig))) = LABEL_PRESERVE_P (orig); @@ -2155,13 +369,6 @@ copy_rtx_and_substitute (orig, map, for_lhs) && ! (CODE_LABEL_NUMBER (XEXP (copy, 0)) >= get_first_label_num () && CODE_LABEL_NUMBER (XEXP (copy, 0)) < max_label_num ())); - /* If we have made a nonlocal label local, it means that this - inlined call will be referring to our nonlocal goto handler. - So make sure we create one for this block; we normally would - not since this is not otherwise considered a "call". */ - if (LABEL_REF_NONLOCAL_P (orig) && ! LABEL_REF_NONLOCAL_P (copy)) - function_call_count++; - return copy; case PC: @@ -2176,46 +383,14 @@ copy_rtx_and_substitute (orig, map, for_lhs) remapped label. Otherwise, symbols are returned unchanged. */ if (CONSTANT_POOL_ADDRESS_P (orig)) { - struct function *f = inlining ? inlining : cfun; + struct function *f = cfun; rtx constant = get_pool_constant_for_function (f, orig); - enum machine_mode const_mode = get_pool_mode_for_function (f, orig); - if (inlining) - { - rtx temp = force_const_mem (const_mode, - copy_rtx_and_substitute (constant, - map, 0)); - -#if 0 - /* Legitimizing the address here is incorrect. - - Since we had a SYMBOL_REF before, we can assume it is valid - to have one in this position in the insn. - - Also, change_address may create new registers. These - registers will not have valid reg_map entries. This can - cause try_constants() to fail because assumes that all - registers in the rtx have valid reg_map entries, and it may - end up replacing one of these new registers with junk. */ - - if (! memory_address_p (GET_MODE (temp), XEXP (temp, 0))) - temp = change_address (temp, GET_MODE (temp), XEXP (temp, 0)); -#endif - - temp = XEXP (temp, 0); - -#ifdef POINTERS_EXTEND_UNSIGNED - if (GET_MODE (temp) != GET_MODE (orig)) - temp = convert_memory_address (GET_MODE (orig), temp); -#endif - return temp; - } - else if (GET_CODE (constant) == LABEL_REF) + if (GET_CODE (constant) == LABEL_REF) return XEXP (force_const_mem (GET_MODE (orig), copy_rtx_and_substitute (constant, map, for_lhs)), 0); } - return orig; case CONST_DOUBLE: @@ -2234,10 +409,6 @@ copy_rtx_and_substitute (orig, map, for_lhs) CONST_DOUBLE_HIGH (orig), VOIDmode); case CONST: - /* Make new constant pool entry for a constant - that was in the pool of the inline function. */ - if (RTX_INTEGRATED_P (orig)) - abort (); break; case ASM_OPERANDS: @@ -2257,8 +428,13 @@ copy_rtx_and_substitute (orig, map, for_lhs) ASM_OPERANDS_INPUT_VEC (copy) = map->copy_asm_operands_vector; ASM_OPERANDS_INPUT_CONSTRAINT_VEC (copy) = map->copy_asm_constraints_vector; +#ifdef USE_MAPPED_LOCATION + ASM_OPERANDS_SOURCE_LOCATION (copy) + = ASM_OPERANDS_SOURCE_LOCATION (orig); +#else ASM_OPERANDS_SOURCE_FILE (copy) = ASM_OPERANDS_SOURCE_FILE (orig); ASM_OPERANDS_SOURCE_LINE (copy) = ASM_OPERANDS_SOURCE_LINE (orig); +#endif return copy; } break; @@ -2280,13 +456,14 @@ copy_rtx_and_substitute (orig, map, for_lhs) MEM_COPY_ATTRIBUTES (copy, XEXP (orig, 0)); return - gen_rtx_CALL (GET_MODE (orig), copy, + gen_rtx_CALL (GET_MODE (orig), copy, copy_rtx_and_substitute (XEXP (orig, 1), map, 0)); } break; #if 0 - /* Must be ifdefed out for loop unrolling to work. */ + /* Must be ifdefed out for loop unrolling to work. */ + /* ??? Is this for the old or the new unroller? */ case RETURN: abort (); #endif @@ -2309,7 +486,7 @@ copy_rtx_and_substitute (orig, map, for_lhs) equiv_loc = VARRAY_CONST_EQUIV (map->const_equiv_varray, REGNO (equiv_reg)).rtx; loc_offset - = GET_CODE (equiv_loc) == REG ? 0 : INTVAL (XEXP (equiv_loc, 1)); + = REG_P (equiv_loc) ? 0 : INTVAL (XEXP (equiv_loc, 1)); return gen_rtx_SET (VOIDmode, SET_DEST (orig), force_operand @@ -2326,46 +503,9 @@ copy_rtx_and_substitute (orig, map, for_lhs) break; case MEM: - if (inlining - && GET_CODE (XEXP (orig, 0)) == SYMBOL_REF - && CONSTANT_POOL_ADDRESS_P (XEXP (orig, 0))) - { - enum machine_mode const_mode - = get_pool_mode_for_function (inlining, XEXP (orig, 0)); - rtx constant - = get_pool_constant_for_function (inlining, XEXP (orig, 0)); - - constant = copy_rtx_and_substitute (constant, map, 0); - - /* If this was an address of a constant pool entry that itself - had to be placed in the constant pool, it might not be a - valid address. So the recursive call might have turned it - into a register. In that case, it isn't a constant any - more, so return it. This has the potential of changing a - MEM into a REG, but we'll assume that it safe. */ - if (! CONSTANT_P (constant)) - return constant; - - return validize_mem (force_const_mem (const_mode, constant)); - } - copy = gen_rtx_MEM (mode, copy_rtx_and_substitute (XEXP (orig, 0), map, 0)); MEM_COPY_ATTRIBUTES (copy, orig); - - /* If inlining and this is not for the LHS, turn off RTX_UNCHANGING_P - since this may be an indirect reference to a parameter and the - actual may not be readonly. */ - if (inlining && !for_lhs) - RTX_UNCHANGING_P (copy) = 0; - - /* If inlining, squish aliasing data that references the subroutine's - parameter list, since that's no longer applicable. */ - if (inlining && MEM_EXPR (copy) - && TREE_CODE (MEM_EXPR (copy)) == INDIRECT_REF - && TREE_CODE (TREE_OPERAND (MEM_EXPR (copy), 0)) == PARM_DECL) - set_mem_expr (copy, NULL_TREE); - return copy; default: @@ -2385,8 +525,7 @@ copy_rtx_and_substitute (orig, map, for_lhs) switch (*format_ptr++) { case '0': - /* Copy this through the wide int field; that's safest. */ - X0WINT (copy, i) = X0WINT (orig, i); + X0ANY (copy, i) = X0ANY (orig, i); break; case 'e': @@ -2447,9 +586,7 @@ copy_rtx_and_substitute (orig, map, for_lhs) /* Substitute known constant values into INSN, if that is valid. */ void -try_constants (insn, map) - rtx insn; - struct inline_remap *map; +try_constants (rtx insn, struct inline_remap *map) { int i; @@ -2464,6 +601,14 @@ try_constants (insn, map) subst_constants (&PATTERN (insn), insn, map, 0); apply_change_group (); + /* Enforce consistency between the addresses in the regular insn flow + and the ones in CALL_INSN_FUNCTION_USAGE lists, if any. */ + if (CALL_P (insn) && CALL_INSN_FUNCTION_USAGE (insn)) + { + subst_constants (&CALL_INSN_FUNCTION_USAGE (insn), insn, map, 1); + apply_change_group (); + } + /* Show we don't know the value of anything stored or clobbered. */ note_stores (PATTERN (insn), mark_stores, NULL); map->last_pc_value = 0; @@ -2474,7 +619,7 @@ try_constants (insn, map) /* Set up any constant equivalences made in this insn. */ for (i = 0; i < map->num_sets; i++) { - if (GET_CODE (map->equiv_sets[i].dest) == REG) + if (REG_P (map->equiv_sets[i].dest)) { int regno = REGNO (map->equiv_sets[i].dest); @@ -2513,11 +658,7 @@ try_constants (insn, map) If MEMONLY is nonzero, only make changes inside a MEM. */ static void -subst_constants (loc, insn, map, memonly) - rtx *loc; - rtx insn; - struct inline_remap *map; - int memonly; +subst_constants (rtx *loc, rtx insn, struct inline_remap *map, int memonly) { rtx x = *loc; int i, j; @@ -2552,7 +693,7 @@ subst_constants (loc, insn, map, memonly) case CLOBBER: /* The only thing we can do with a USE or CLOBBER is possibly do some substitutions in a MEM within it. */ - if (GET_CODE (XEXP (x, 0)) == MEM) + if (MEM_P (XEXP (x, 0))) subst_constants (&XEXP (XEXP (x, 0), 0), insn, map, 0); return; @@ -2579,7 +720,7 @@ subst_constants (loc, insn, map, memonly) be a special hack and we don't know how to treat it specially. Consider for example mulsidi3 in m68k.md. Ordinary SUBREG of a REG needs this special treatment. */ - if (! memonly && GET_CODE (SUBREG_REG (x)) == REG) + if (! memonly && REG_P (SUBREG_REG (x))) { rtx inner = SUBREG_REG (x); rtx new = 0; @@ -2591,7 +732,7 @@ subst_constants (loc, insn, map, memonly) integral mode and extracting the low part. */ subst_constants (&inner, NULL_RTX, map, 0); new = simplify_gen_subreg (GET_MODE (x), inner, - GET_MODE (SUBREG_REG (x)), + GET_MODE (SUBREG_REG (x)), SUBREG_BYTE (x)); if (new) @@ -2653,7 +794,7 @@ subst_constants (loc, insn, map, memonly) } /* Do substitute in the address of a destination in memory. */ - if (GET_CODE (*dest_loc) == MEM) + if (MEM_P (*dest_loc)) subst_constants (&XEXP (*dest_loc, 0), insn, map, 0); /* Check for the case of DEST a SUBREG, both it and the underlying @@ -2672,11 +813,11 @@ subst_constants (loc, insn, map, memonly) /* If storing a recognizable value save it for later recording. */ if ((map->num_sets < MAX_RECOG_OPERANDS) && (CONSTANT_P (src) - || (GET_CODE (src) == REG + || (REG_P (src) && (REGNO (src) == VIRTUAL_INCOMING_ARGS_REGNUM || REGNO (src) == VIRTUAL_STACK_VARS_REGNUM)) || (GET_CODE (src) == PLUS - && GET_CODE (XEXP (src, 0)) == REG + && REG_P (XEXP (src, 0)) && (REGNO (XEXP (src, 0)) == VIRTUAL_INCOMING_ARGS_REGNUM || REGNO (XEXP (src, 0)) == VIRTUAL_STACK_VARS_REGNUM) && CONSTANT_P (XEXP (src, 1))) @@ -2752,7 +893,8 @@ subst_constants (loc, insn, map, memonly) /* If this is a commutative operation, move a constant to the second operand unless the second operand is already a CONST_INT. */ if (! memonly - && (GET_RTX_CLASS (code) == 'c' || code == NE || code == EQ) + && (GET_RTX_CLASS (code) == RTX_COMM_ARITH + || GET_RTX_CLASS (code) == RTX_COMM_COMPARE) && CONSTANT_P (XEXP (x, 0)) && GET_CODE (XEXP (x, 1)) != CONST_INT) { rtx tem = XEXP (x, 0); @@ -2764,48 +906,34 @@ subst_constants (loc, insn, map, memonly) if (! memonly) switch (GET_RTX_CLASS (code)) { - case '1': + case RTX_UNARY: if (op0_mode == MAX_MACHINE_MODE) abort (); new = simplify_unary_operation (code, GET_MODE (x), XEXP (x, 0), op0_mode); break; - case '<': + case RTX_COMPARE: + case RTX_COMM_COMPARE: { enum machine_mode op_mode = GET_MODE (XEXP (x, 0)); if (op_mode == VOIDmode) op_mode = GET_MODE (XEXP (x, 1)); - new = simplify_relational_operation (code, op_mode, + + new = simplify_relational_operation (code, GET_MODE (x), op_mode, XEXP (x, 0), XEXP (x, 1)); -#ifdef FLOAT_STORE_FLAG_VALUE - if (new != 0 && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT) - { - enum machine_mode mode = GET_MODE (x); - if (new == const0_rtx) - new = CONST0_RTX (mode); - else - { - REAL_VALUE_TYPE val; - - /* Avoid automatic aggregate initialization. */ - val = FLOAT_STORE_FLAG_VALUE (mode); - new = CONST_DOUBLE_FROM_REAL_VALUE (val, mode); - } - } -#endif break; } - case '2': - case 'c': + case RTX_BIN_ARITH: + case RTX_COMM_ARITH: new = simplify_binary_operation (code, GET_MODE (x), XEXP (x, 0), XEXP (x, 1)); break; - case 'b': - case '3': + case RTX_BITFIELD_OPS: + case RTX_TERNARY: if (op0_mode == MAX_MACHINE_MODE) abort (); @@ -2813,7 +941,7 @@ subst_constants (loc, insn, map, memonly) { rtx op0 = XEXP (x, 0); - if (GET_RTX_CLASS (GET_CODE (op0)) == '<' + if (COMPARISON_P (op0) && GET_MODE (op0) == VOIDmode && ! side_effects_p (op0) && XEXP (op0, 0) == map->compare_src @@ -2821,15 +949,18 @@ subst_constants (loc, insn, map, memonly) { /* We have compare of two VOIDmode constants for which we recorded the comparison mode. */ - rtx temp = - simplify_relational_operation (GET_CODE (op0), - map->compare_mode, - XEXP (op0, 0), - XEXP (op0, 1)); - - if (temp == const0_rtx) + rtx tem = + simplify_gen_relational (GET_CODE (op0), GET_MODE (op0), + map->compare_mode, XEXP (op0, 0), + XEXP (op0, 1)); + + if (GET_CODE (tem) != CONST_INT) + new = simplify_ternary_operation (code, GET_MODE (x), + op0_mode, tem, XEXP (x, 1), + XEXP (x, 2)); + else if (tem == const0_rtx) new = XEXP (x, 2); - else if (temp == const1_rtx) + else new = XEXP (x, 1); } } @@ -2838,6 +969,9 @@ subst_constants (loc, insn, map, memonly) XEXP (x, 0), XEXP (x, 1), XEXP (x, 2)); break; + + default: + break; } if (new) @@ -2848,10 +982,7 @@ subst_constants (loc, insn, map, memonly) called from note_stores with parts of the new insn. */ static void -mark_stores (dest, x, data) - rtx dest; - rtx x ATTRIBUTE_UNUSED; - void *data ATTRIBUTE_UNUSED; +mark_stores (rtx dest, rtx x ATTRIBUTE_UNUSED, void *data ATTRIBUTE_UNUSED) { int regno = -1; enum machine_mode mode = VOIDmode; @@ -2859,9 +990,9 @@ mark_stores (dest, x, data) /* DEST is always the innermost thing set, except in the case of SUBREGs of hard registers. */ - if (GET_CODE (dest) == REG) + if (REG_P (dest)) regno = REGNO (dest), mode = GET_MODE (dest); - else if (GET_CODE (dest) == SUBREG && GET_CODE (SUBREG_REG (dest)) == REG) + else if (GET_CODE (dest) == SUBREG && REG_P (SUBREG_REG (dest))) { regno = REGNO (SUBREG_REG (dest)); if (regno < FIRST_PSEUDO_REGISTER) @@ -2876,7 +1007,7 @@ mark_stores (dest, x, data) { unsigned int uregno = regno; unsigned int last_reg = (uregno >= FIRST_PSEUDO_REGISTER ? uregno - : uregno + HARD_REGNO_NREGS (uregno, mode) - 1); + : uregno + hard_regno_nregs[uregno][mode] - 1); unsigned int i; /* Ignore virtual stack var or virtual arg register since those @@ -2900,8 +1031,7 @@ mark_stores (dest, x, data) values to point to themselves. */ static void -set_block_origin_self (stmt) - tree stmt; +set_block_origin_self (tree stmt) { if (BLOCK_ABSTRACT_ORIGIN (stmt) == NULL_TREE) { @@ -2939,8 +1069,7 @@ set_block_origin_self (stmt) point to themselves. */ void -set_decl_origin_self (decl) - tree decl; +set_decl_origin_self (tree decl) { if (DECL_ABSTRACT_ORIGIN (decl) == NULL_TREE) { @@ -2964,9 +1093,7 @@ set_decl_origin_self (decl) (recursively) which are contained therein. */ static void -set_block_abstract_flags (stmt, setting) - tree stmt; - int setting; +set_block_abstract_flags (tree stmt, int setting) { tree local_decl; tree subblock; @@ -2991,9 +1118,7 @@ set_block_abstract_flags (stmt, setting) blocks and sub-blocks (recursively) to the same setting. */ void -set_decl_abstract_flags (decl, setting) - tree decl; - int setting; +set_decl_abstract_flags (tree decl, int setting) { DECL_ABSTRACT (decl) = setting; if (TREE_CODE (decl) == FUNCTION_DECL) @@ -3008,60 +1133,11 @@ set_decl_abstract_flags (decl, setting) } } -/* 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. */ - -static GTY(()) struct function *old_cfun; - -void -output_inline_function (fndecl) - tree fndecl; -{ - enum debug_info_type old_write_symbols = write_symbols; - const struct gcc_debug_hooks *const old_debug_hooks = debug_hooks; - struct function *f = DECL_SAVED_INSNS (fndecl); - - old_cfun = cfun; - cfun = f; - current_function_decl = fndecl; - - set_new_last_label_num (f->inl_max_label_num); - - /* We're not deferring this any longer. */ - DECL_DEFER_OUTPUT (fndecl) = 0; - - /* If requested, suppress debugging information. */ - if (f->no_debugging_symbols) - { - write_symbols = NO_DEBUG; - debug_hooks = &do_nothing_debug_hooks; - } - - /* Make sure warnings emitted by the optimizers (e.g. control reaches - end of non-void function) is not wildly incorrect. */ - input_location = DECL_SOURCE_LOCATION (fndecl); - - /* Compile this function all the way down to assembly code. As a - side effect this destroys the saved RTL representation, but - that's okay, because we don't need to inline this anymore. */ - rest_of_compilation (fndecl); - DECL_INLINE (fndecl) = 0; - - cfun = old_cfun; - current_function_decl = old_cfun ? old_cfun->decl : 0; - write_symbols = old_write_symbols; - debug_hooks = old_debug_hooks; -} - - /* Functions to keep track of the values hard regs had at the start of the function. */ rtx -get_hard_reg_initial_reg (fun, reg) - struct function *fun; - rtx reg; +get_hard_reg_initial_reg (struct function *fun, rtx reg) { struct initial_value_struct *ivs = fun->hard_reg_initial_vals; int i; @@ -3077,9 +1153,7 @@ get_hard_reg_initial_reg (fun, reg) } rtx -has_func_hard_reg_initial_val (fun, reg) - struct function *fun; - rtx reg; +has_func_hard_reg_initial_val (struct function *fun, rtx reg) { struct initial_value_struct *ivs = fun->hard_reg_initial_vals; int i; @@ -3095,9 +1169,7 @@ has_func_hard_reg_initial_val (fun, reg) } rtx -get_func_hard_reg_initial_val (fun, reg) - struct function *fun; - rtx reg; +get_func_hard_reg_initial_val (struct function *fun, rtx reg) { struct initial_value_struct *ivs = fun->hard_reg_initial_vals; rtx rv = has_func_hard_reg_initial_val (fun, reg); @@ -3107,20 +1179,19 @@ get_func_hard_reg_initial_val (fun, reg) if (ivs == 0) { - fun->hard_reg_initial_vals = (void *) ggc_alloc (sizeof (initial_value_struct)); + fun->hard_reg_initial_vals = ggc_alloc (sizeof (initial_value_struct)); ivs = fun->hard_reg_initial_vals; ivs->num_entries = 0; ivs->max_entries = 5; - ivs->entries = (initial_value_pair *) ggc_alloc (5 * sizeof (initial_value_pair)); + ivs->entries = ggc_alloc (5 * sizeof (initial_value_pair)); } if (ivs->num_entries >= ivs->max_entries) { ivs->max_entries += 5; - ivs->entries = - (initial_value_pair *) ggc_realloc (ivs->entries, - ivs->max_entries - * sizeof (initial_value_pair)); + ivs->entries = ggc_realloc (ivs->entries, + ivs->max_entries + * sizeof (initial_value_pair)); } ivs->entries[ivs->num_entries].hard_reg = reg; @@ -3130,40 +1201,19 @@ get_func_hard_reg_initial_val (fun, reg) } rtx -get_hard_reg_initial_val (mode, regno) - enum machine_mode mode; - int regno; +get_hard_reg_initial_val (enum machine_mode mode, int regno) { return get_func_hard_reg_initial_val (cfun, gen_rtx_REG (mode, regno)); } rtx -has_hard_reg_initial_val (mode, regno) - enum machine_mode mode; - int regno; +has_hard_reg_initial_val (enum machine_mode mode, int regno) { return has_func_hard_reg_initial_val (cfun, gen_rtx_REG (mode, regno)); } -static void -setup_initial_hard_reg_value_integration (inl_f, remap) - struct function *inl_f; - struct inline_remap *remap; -{ - struct initial_value_struct *ivs = inl_f->hard_reg_initial_vals; - int i; - - if (ivs == 0) - return; - - for (i = 0; i < ivs->num_entries; i ++) - remap->reg_map[REGNO (ivs->entries[i].pseudo)] - = get_func_hard_reg_initial_val (cfun, ivs->entries[i].hard_reg); -} - - void -emit_initial_value_sets () +emit_initial_value_sets (void) { struct initial_value_struct *ivs = cfun->hard_reg_initial_vals; int i; @@ -3178,14 +1228,13 @@ emit_initial_value_sets () seq = get_insns (); end_sequence (); - emit_insn_after (seq, get_insns ()); + emit_insn_after (seq, entry_of_function ()); } /* If the backend knows where to allocate pseudos for hard register initial values, register these allocations now. */ void -allocate_initial_values (reg_equiv_memory_loc) - rtx *reg_equiv_memory_loc ATTRIBUTE_UNUSED; +allocate_initial_values (rtx *reg_equiv_memory_loc ATTRIBUTE_UNUSED) { #ifdef ALLOCATE_INITIAL_VALUE struct initial_value_struct *ivs = cfun->hard_reg_initial_vals; @@ -3201,9 +1250,9 @@ allocate_initial_values (reg_equiv_memory_loc) if (x == NULL_RTX || REG_N_SETS (REGNO (ivs->entries[i].pseudo)) > 1) ; /* Do nothing. */ - else if (GET_CODE (x) == MEM) + else if (MEM_P (x)) reg_equiv_memory_loc[regno] = x; - else if (GET_CODE (x) == REG) + else if (REG_P (x)) { reg_renumber[regno] = REGNO (x); /* Poke the regno right into regno_reg_rtx