X-Git-Url: http://git.sourceforge.jp/view?p=pf3gnuchains%2Fgcc-fork.git;a=blobdiff_plain;f=gcc%2Fintegrate.c;h=135bd1d38b85f193279a8a3fb39f3878292793ed;hp=7a1865881c66cb46220fbf8895252af09f001710;hb=b027ddbfed46c187415181270faa95d7e58e03b7;hpb=9830e26367f860c5d6d0da14e26d3be6ce8566af diff --git a/gcc/integrate.c b/gcc/integrate.c index 7a1865881c6..135bd1d38b8 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,59 +61,14 @@ 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 set_block_origin_self (tree); +static void set_block_abstract_flags (tree, int); -/* 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 - performs a lazy initialization of label_map, thereby avoiding huge memory - explosions when the label_map gets very large. */ - -rtx -get_label_from_map (map, i) - struct inline_remap *map; - int i; -{ - rtx x = map->label_map[i]; - - if (x == NULL_RTX) - x = map->label_map[i] = gen_label_rtx (); - - return x; -} /* 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 +81,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); @@ -422,2464 +160,6 @@ 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 (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, lineno); - - /* 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. - - We always return an rtx that is similar to that incoming rtx, with the - exception of possibly changing a REG to a SUBREG or vice versa. No - 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. - - 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; -{ - rtx copy, temp; - int i, j; - RTX_CODE code; - enum machine_mode mode; - const char *format_ptr; - int regno; - - if (orig == 0) - return 0; - - code = GET_CODE (orig); - mode = GET_MODE (orig); - - switch (code) - { - case REG: - /* 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 <= LAST_VIRTUAL_REGISTER - || (map->integrating - && DECL_SAVED_INSNS (map->fndecl)->internal_arg_pointer == orig)) - { - /* Some hard registers are also mapped, - but others are not translated. */ - if (map->reg_map[regno] != 0) - return map->reg_map[regno]; - - /* If this is the virtual frame pointer, make space in current - function's stack frame for the stack frame of the inline function. - - Copy the address of this area into a pseudo. Map - virtual_stack_vars_rtx to this pseudo and set up a constant - equivalence for it to be the address. This will substitute the - address into insns where it can be substituted and use the new - pseudo where it can't. */ - else if (regno == VIRTUAL_STACK_VARS_REGNUM) - { - rtx loc, seq; - int size = get_func_frame_size (DECL_SAVED_INSNS (map->fndecl)); -#ifdef FRAME_GROWS_DOWNWARD - int alignment - = (DECL_SAVED_INSNS (map->fndecl)->stack_alignment_needed - / BITS_PER_UNIT); - - /* In this case, virtual_stack_vars_rtx points to one byte - higher than the top of the frame area. So make sure we - allocate a big enough chunk to keep the frame pointer - aligned like a real one. */ - if (alignment) - size = CEIL_ROUND (size, alignment); -#endif - start_sequence (); - loc = assign_stack_temp (BLKmode, size, 1); - loc = XEXP (loc, 0); -#ifdef FRAME_GROWS_DOWNWARD - /* In this case, virtual_stack_vars_rtx points to one byte - higher than the top of the frame area. So compute the offset - to one byte higher than our substitute frame. */ - loc = plus_constant (loc, size); -#endif - map->reg_map[regno] = temp - = force_reg (Pmode, force_operand (loc, NULL_RTX)); - -#ifdef STACK_BOUNDARY - mark_reg_pointer (map->reg_map[regno], STACK_BOUNDARY); -#endif - - SET_CONST_EQUIV_DATA (map, temp, loc, CONST_AGE_PARM); - - seq = get_insns (); - end_sequence (); - 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))) - { - /* 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; - - start_sequence (); - loc = assign_stack_temp (BLKmode, size, 1); - loc = XEXP (loc, 0); - /* When arguments grow downward, the virtual incoming - args pointer points to the top of the argument block, - so the remapped location better do the same. */ -#ifdef ARGS_GROW_DOWNWARD - loc = plus_constant (loc, size); -#endif - map->reg_map[regno] = temp - = force_reg (Pmode, force_operand (loc, NULL_RTX)); - -#ifdef STACK_BOUNDARY - mark_reg_pointer (map->reg_map[regno], STACK_BOUNDARY); -#endif - - SET_CONST_EQUIV_DATA (map, temp, loc, CONST_AGE_PARM); - - seq = get_insns (); - end_sequence (); - 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; - - abort (); - } - if (map->reg_map[regno] == NULL) - { - 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])) - mark_reg_pointer (map->reg_map[regno], - map->regno_pointer_align[regno]); - } - return map->reg_map[regno]; - - case SUBREG: - copy = copy_rtx_and_substitute (SUBREG_REG (orig), map, for_lhs); - return simplify_gen_subreg (GET_MODE (orig), copy, - 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 - 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) - copy = SUBREG_REG (copy); - return gen_rtx_fmt_e (code, VOIDmode, copy); - - /* We need to handle "deleted" labels that appear in the DECL_RTL - of a LABEL_DECL. */ - case NOTE: - if (NOTE_LINE_NUMBER (orig) != NOTE_INSN_DELETED_LABEL) - break; - - /* ... FALLTHRU ... */ - case CODE_LABEL: - LABEL_PRESERVE_P (get_label_from_map (map, CODE_LABEL_NUMBER (orig))) - = LABEL_PRESERVE_P (orig); - return get_label_from_map (map, CODE_LABEL_NUMBER (orig)); - - case LABEL_REF: - copy - = gen_rtx_LABEL_REF - (mode, - LABEL_REF_NONLOCAL_P (orig) ? XEXP (orig, 0) - : get_label_from_map (map, CODE_LABEL_NUMBER (XEXP (orig, 0)))); - - LABEL_OUTSIDE_LOOP_P (copy) = LABEL_OUTSIDE_LOOP_P (orig); - - /* The fact that this label was previously nonlocal does not mean - it still is, so we must check if it is within the range of - this function's labels. */ - LABEL_REF_NONLOCAL_P (copy) - = (LABEL_REF_NONLOCAL_P (orig) - && ! (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: - case CC0: - case CONST_INT: - case CONST_VECTOR: - return orig; - - case SYMBOL_REF: - /* Symbols which represent the address of a label stored in the constant - pool must be modified to point to a constant pool entry for the - remapped label. Otherwise, symbols are returned unchanged. */ - if (CONSTANT_POOL_ADDRESS_P (orig)) - { - struct function *f = inlining ? inlining : 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) - return XEXP (force_const_mem - (GET_MODE (orig), - copy_rtx_and_substitute (constant, map, for_lhs)), - 0); - } - - return orig; - - case CONST_DOUBLE: - /* We have to make a new copy of this CONST_DOUBLE because don't want - to use the old value of CONST_DOUBLE_MEM. Also, this may be a - duplicate of a CONST_DOUBLE we have already seen. */ - if (GET_MODE_CLASS (GET_MODE (orig)) == MODE_FLOAT) - { - REAL_VALUE_TYPE d; - - REAL_VALUE_FROM_CONST_DOUBLE (d, orig); - return CONST_DOUBLE_FROM_REAL_VALUE (d, GET_MODE (orig)); - } - else - return immed_double_const (CONST_DOUBLE_LOW (orig), - 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: - /* If a single asm insn contains multiple output operands then - it contains multiple ASM_OPERANDS rtx's that share the input - and constraint vecs. We must make sure that the copied insn - continues to share it. */ - if (map->orig_asm_operands_vector == ASM_OPERANDS_INPUT_VEC (orig)) - { - copy = rtx_alloc (ASM_OPERANDS); - RTX_FLAG (copy, volatil) = RTX_FLAG (orig, volatil); - PUT_MODE (copy, GET_MODE (orig)); - ASM_OPERANDS_TEMPLATE (copy) = ASM_OPERANDS_TEMPLATE (orig); - ASM_OPERANDS_OUTPUT_CONSTRAINT (copy) - = ASM_OPERANDS_OUTPUT_CONSTRAINT (orig); - ASM_OPERANDS_OUTPUT_IDX (copy) = ASM_OPERANDS_OUTPUT_IDX (orig); - ASM_OPERANDS_INPUT_VEC (copy) = map->copy_asm_operands_vector; - ASM_OPERANDS_INPUT_CONSTRAINT_VEC (copy) - = map->copy_asm_constraints_vector; - ASM_OPERANDS_SOURCE_FILE (copy) = ASM_OPERANDS_SOURCE_FILE (orig); - ASM_OPERANDS_SOURCE_LINE (copy) = ASM_OPERANDS_SOURCE_LINE (orig); - 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 - { - rtx copy - = gen_rtx_MEM (GET_MODE (XEXP (orig, 0)), - copy_rtx_and_substitute (XEXP (XEXP (orig, 0), 0), - map, 0)); - - MEM_COPY_ATTRIBUTES (copy, XEXP (orig, 0)); - - return - 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. */ - case RETURN: - abort (); -#endif - - case SET: - /* If this is setting fp or ap, it means that we have a nonlocal goto. - Adjust the setting by the offset of the area we made. - If the nonlocal goto is into the current function, - this will result in unnecessarily bad code, but should work. */ - if (SET_DEST (orig) == virtual_stack_vars_rtx - || SET_DEST (orig) == virtual_incoming_args_rtx) - { - /* In case a translation hasn't occurred already, make one now. */ - rtx equiv_reg; - rtx equiv_loc; - HOST_WIDE_INT loc_offset; - - copy_rtx_and_substitute (SET_DEST (orig), map, for_lhs); - equiv_reg = map->reg_map[REGNO (SET_DEST (orig))]; - 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)); - - return gen_rtx_SET (VOIDmode, SET_DEST (orig), - force_operand - (plus_constant - (copy_rtx_and_substitute (SET_SRC (orig), - map, 0), - - loc_offset), - NULL_RTX)); - } - else - return gen_rtx_SET (VOIDmode, - copy_rtx_and_substitute (SET_DEST (orig), map, 1), - copy_rtx_and_substitute (SET_SRC (orig), map, 0)); - 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: - break; - } - - copy = rtx_alloc (code); - PUT_MODE (copy, mode); - RTX_FLAG (copy, in_struct) = RTX_FLAG (orig, in_struct); - RTX_FLAG (copy, volatil) = RTX_FLAG (orig, volatil); - RTX_FLAG (copy, unchanging) = RTX_FLAG (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': - /* Copy this through the wide int field; that's safest. */ - X0WINT (copy, i) = X0WINT (orig, i); - break; - - case 'e': - XEXP (copy, i) - = copy_rtx_and_substitute (XEXP (orig, i), map, for_lhs); - break; - - case 'u': - /* Change any references to old-insns to point to the - corresponding copied insns. */ - XEXP (copy, i) = map->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), - map, for_lhs); - } - break; - - case 'w': - XWINT (copy, i) = XWINT (orig, i); - break; - - case 'i': - XINT (copy, i) = XINT (orig, i); - break; - - case 's': - XSTR (copy, i) = XSTR (orig, i); - break; - - case 't': - XTREE (copy, i) = XTREE (orig, i); - break; - - default: - abort (); - } - } - - if (code == ASM_OPERANDS && map->orig_asm_operands_vector == 0) - { - map->orig_asm_operands_vector = ASM_OPERANDS_INPUT_VEC (orig); - map->copy_asm_operands_vector = ASM_OPERANDS_INPUT_VEC (copy); - map->copy_asm_constraints_vector - = ASM_OPERANDS_INPUT_CONSTRAINT_VEC (copy); - } - - return copy; -} - -/* Substitute known constant values into INSN, if that is valid. */ - -void -try_constants (insn, map) - rtx insn; - struct inline_remap *map; -{ - int i; - - map->num_sets = 0; - - /* First try just updating addresses, then other things. This is - important when we have something like the store of a constant - into memory and we can update the memory address but the machine - does not support a constant source. */ - subst_constants (&PATTERN (insn), insn, map, 1); - apply_change_group (); - subst_constants (&PATTERN (insn), insn, map, 0); - 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; -#ifdef HAVE_cc0 - map->last_cc0_value = 0; -#endif - - /* 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) - { - int regno = REGNO (map->equiv_sets[i].dest); - - MAYBE_EXTEND_CONST_EQUIV_VARRAY (map, regno); - if (VARRAY_CONST_EQUIV (map->const_equiv_varray, regno).rtx == 0 - /* Following clause is a hack to make case work where GNU C++ - reassigns a variable to make cse work right. */ - || ! rtx_equal_p (VARRAY_CONST_EQUIV (map->const_equiv_varray, - regno).rtx, - map->equiv_sets[i].equiv)) - SET_CONST_EQUIV_DATA (map, map->equiv_sets[i].dest, - map->equiv_sets[i].equiv, map->const_age); - } - else if (map->equiv_sets[i].dest == pc_rtx) - map->last_pc_value = map->equiv_sets[i].equiv; -#ifdef HAVE_cc0 - else if (map->equiv_sets[i].dest == cc0_rtx) - map->last_cc0_value = map->equiv_sets[i].equiv; -#endif - } -} - -/* Substitute known constants for pseudo regs in the contents of LOC, - which are part of INSN. - If INSN is zero, the substitution should always be done (this is used to - update DECL_RTL). - These changes are taken out by try_constants if the result is not valid. - - Note that we are more concerned with determining when the result of a SET - is a constant, for further propagation, than actually inserting constants - into insns; cse will do the latter task better. - - This function is also used to adjust address of items previously addressed - via the virtual stack variable or virtual incoming arguments registers. - - 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; -{ - rtx x = *loc; - int i, j; - enum rtx_code code; - const char *format_ptr; - int num_changes = num_validated_changes (); - rtx new = 0; - enum machine_mode op0_mode = MAX_MACHINE_MODE; - - code = GET_CODE (x); - - switch (code) - { - case PC: - case CONST_INT: - case CONST_DOUBLE: - case CONST_VECTOR: - case SYMBOL_REF: - case CONST: - case LABEL_REF: - case ADDRESS: - return; - -#ifdef HAVE_cc0 - case CC0: - if (! memonly) - validate_change (insn, loc, map->last_cc0_value, 1); - return; -#endif - - case USE: - 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) - subst_constants (&XEXP (XEXP (x, 0), 0), insn, map, 0); - return; - - case REG: - /* Substitute for parms and known constants. Don't replace - hard regs used as user variables with constants. */ - if (! memonly) - { - int regno = REGNO (x); - struct const_equiv_data *p; - - if (! (regno < FIRST_PSEUDO_REGISTER && REG_USERVAR_P (x)) - && (size_t) regno < VARRAY_SIZE (map->const_equiv_varray) - && (p = &VARRAY_CONST_EQUIV (map->const_equiv_varray, regno), - p->rtx != 0) - && p->age >= map->const_age) - validate_change (insn, loc, p->rtx, 1); - } - return; - - case SUBREG: - /* SUBREG applied to something other than a reg - should be treated as ordinary, since that must - 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) - { - rtx inner = SUBREG_REG (x); - rtx new = 0; - - /* We can't call subst_constants on &SUBREG_REG (x) because any - constant or SUBREG wouldn't be valid inside our SUBEG. Instead, - see what is inside, try to form the new SUBREG and see if that is - valid. We handle two cases: extracting a full word in an - 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)), - SUBREG_BYTE (x)); - - if (new) - validate_change (insn, loc, new, 1); - else - cancel_changes (num_changes); - - return; - } - break; - - case MEM: - subst_constants (&XEXP (x, 0), insn, map, 0); - - /* If a memory address got spoiled, change it back. */ - if (! memonly && insn != 0 && num_validated_changes () != num_changes - && ! memory_address_p (GET_MODE (x), XEXP (x, 0))) - cancel_changes (num_changes); - return; - - case SET: - { - /* Substitute constants in our source, and in any arguments to a - complex (e..g, ZERO_EXTRACT) destination, but not in the destination - itself. */ - rtx *dest_loc = &SET_DEST (x); - rtx dest = *dest_loc; - rtx src, tem; - enum machine_mode compare_mode = VOIDmode; - - /* If SET_SRC is a COMPARE which subst_constants would turn into - COMPARE of 2 VOIDmode constants, note the mode in which comparison - is to be done. */ - if (GET_CODE (SET_SRC (x)) == COMPARE) - { - src = SET_SRC (x); - if (GET_MODE_CLASS (GET_MODE (src)) == MODE_CC - || CC0_P (dest)) - { - compare_mode = GET_MODE (XEXP (src, 0)); - if (compare_mode == VOIDmode) - compare_mode = GET_MODE (XEXP (src, 1)); - } - } - - subst_constants (&SET_SRC (x), insn, map, memonly); - src = SET_SRC (x); - - while (GET_CODE (*dest_loc) == ZERO_EXTRACT - || GET_CODE (*dest_loc) == SUBREG - || GET_CODE (*dest_loc) == STRICT_LOW_PART) - { - if (GET_CODE (*dest_loc) == ZERO_EXTRACT) - { - subst_constants (&XEXP (*dest_loc, 1), insn, map, memonly); - subst_constants (&XEXP (*dest_loc, 2), insn, map, memonly); - } - dest_loc = &XEXP (*dest_loc, 0); - } - - /* Do substitute in the address of a destination in memory. */ - if (GET_CODE (*dest_loc) == MEM) - subst_constants (&XEXP (*dest_loc, 0), insn, map, 0); - - /* Check for the case of DEST a SUBREG, both it and the underlying - register are less than one word, and the SUBREG has the wider mode. - In the case, we are really setting the underlying register to the - source converted to the mode of DEST. So indicate that. */ - if (GET_CODE (dest) == SUBREG - && GET_MODE_SIZE (GET_MODE (dest)) <= UNITS_PER_WORD - && GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) <= UNITS_PER_WORD - && (GET_MODE_SIZE (GET_MODE (SUBREG_REG (dest))) - <= GET_MODE_SIZE (GET_MODE (dest))) - && (tem = gen_lowpart_if_possible (GET_MODE (SUBREG_REG (dest)), - src))) - src = tem, dest = SUBREG_REG (dest); - - /* If storing a recognizable value save it for later recording. */ - if ((map->num_sets < MAX_RECOG_OPERANDS) - && (CONSTANT_P (src) - || (GET_CODE (src) == REG - && (REGNO (src) == VIRTUAL_INCOMING_ARGS_REGNUM - || REGNO (src) == VIRTUAL_STACK_VARS_REGNUM)) - || (GET_CODE (src) == PLUS - && GET_CODE (XEXP (src, 0)) == REG - && (REGNO (XEXP (src, 0)) == VIRTUAL_INCOMING_ARGS_REGNUM - || REGNO (XEXP (src, 0)) == VIRTUAL_STACK_VARS_REGNUM) - && CONSTANT_P (XEXP (src, 1))) - || GET_CODE (src) == COMPARE - || CC0_P (dest) - || (dest == pc_rtx - && (src == pc_rtx || GET_CODE (src) == RETURN - || GET_CODE (src) == LABEL_REF)))) - { - /* Normally, this copy won't do anything. But, if SRC is a COMPARE - it will cause us to save the COMPARE with any constants - substituted, which is what we want for later. */ - rtx src_copy = copy_rtx (src); - map->equiv_sets[map->num_sets].equiv = src_copy; - map->equiv_sets[map->num_sets++].dest = dest; - if (compare_mode != VOIDmode - && GET_CODE (src) == COMPARE - && (GET_MODE_CLASS (GET_MODE (src)) == MODE_CC - || CC0_P (dest)) - && GET_MODE (XEXP (src, 0)) == VOIDmode - && GET_MODE (XEXP (src, 1)) == VOIDmode) - { - map->compare_src = src_copy; - map->compare_mode = compare_mode; - } - } - } - return; - - default: - break; - } - - format_ptr = GET_RTX_FORMAT (code); - - /* If the first operand is an expression, save its mode for later. */ - if (*format_ptr == 'e') - op0_mode = GET_MODE (XEXP (x, 0)); - - for (i = 0; i < GET_RTX_LENGTH (code); i++) - { - switch (*format_ptr++) - { - case '0': - break; - - case 'e': - if (XEXP (x, i)) - subst_constants (&XEXP (x, i), insn, map, memonly); - break; - - case 'u': - case 'i': - case 's': - case 'w': - case 'n': - case 't': - case 'B': - break; - - case 'E': - if (XVEC (x, i) != NULL && XVECLEN (x, i) != 0) - for (j = 0; j < XVECLEN (x, i); j++) - subst_constants (&XVECEXP (x, i, j), insn, map, memonly); - - break; - - default: - abort (); - } - } - - /* 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) - && CONSTANT_P (XEXP (x, 0)) && GET_CODE (XEXP (x, 1)) != CONST_INT) - { - rtx tem = XEXP (x, 0); - validate_change (insn, &XEXP (x, 0), XEXP (x, 1), 1); - validate_change (insn, &XEXP (x, 1), tem, 1); - } - - /* Simplify the expression in case we put in some constants. */ - if (! memonly) - switch (GET_RTX_CLASS (code)) - { - case '1': - if (op0_mode == MAX_MACHINE_MODE) - abort (); - new = simplify_unary_operation (code, GET_MODE (x), - XEXP (x, 0), op0_mode); - break; - - case '<': - { - 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, - 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': - new = simplify_binary_operation (code, GET_MODE (x), - XEXP (x, 0), XEXP (x, 1)); - break; - - case 'b': - case '3': - if (op0_mode == MAX_MACHINE_MODE) - abort (); - - if (code == IF_THEN_ELSE) - { - rtx op0 = XEXP (x, 0); - - if (GET_RTX_CLASS (GET_CODE (op0)) == '<' - && GET_MODE (op0) == VOIDmode - && ! side_effects_p (op0) - && XEXP (op0, 0) == map->compare_src - && GET_MODE (XEXP (op0, 1)) == VOIDmode) - { - /* 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) - new = XEXP (x, 2); - else if (temp == const1_rtx) - new = XEXP (x, 1); - } - } - if (!new) - new = simplify_ternary_operation (code, GET_MODE (x), op0_mode, - XEXP (x, 0), XEXP (x, 1), - XEXP (x, 2)); - break; - } - - if (new) - validate_change (insn, loc, new, 1); -} - -/* Show that register modified no longer contain known constants. We are - 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; -{ - int regno = -1; - enum machine_mode mode = VOIDmode; - - /* DEST is always the innermost thing set, except in the case of - SUBREGs of hard registers. */ - - if (GET_CODE (dest) == REG) - regno = REGNO (dest), mode = GET_MODE (dest); - else if (GET_CODE (dest) == SUBREG && GET_CODE (SUBREG_REG (dest)) == REG) - { - regno = REGNO (SUBREG_REG (dest)); - if (regno < FIRST_PSEUDO_REGISTER) - regno += subreg_regno_offset (REGNO (SUBREG_REG (dest)), - GET_MODE (SUBREG_REG (dest)), - SUBREG_BYTE (dest), - GET_MODE (dest)); - mode = GET_MODE (SUBREG_REG (dest)); - } - - if (regno >= 0) - { - unsigned int uregno = regno; - unsigned int last_reg = (uregno >= FIRST_PSEUDO_REGISTER ? uregno - : uregno + HARD_REGNO_NREGS (uregno, mode) - 1); - unsigned int i; - - /* Ignore virtual stack var or virtual arg register since those - are handled separately. */ - if (uregno != VIRTUAL_INCOMING_ARGS_REGNUM - && uregno != VIRTUAL_STACK_VARS_REGNUM) - for (i = uregno; i <= last_reg; i++) - if ((size_t) i < VARRAY_SIZE (global_const_equiv_varray)) - VARRAY_CONST_EQUIV (global_const_equiv_varray, i).rtx = 0; - } -} /* Given a pointer to some BLOCK node, if the BLOCK_ABSTRACT_ORIGIN for the given BLOCK node is NULL, set the BLOCK_ABSTRACT_ORIGIN for the node so @@ -2892,8 +172,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) { @@ -2931,8 +210,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) { @@ -2956,9 +234,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; @@ -2983,9 +259,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) @@ -3000,61 +274,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_filename = DECL_SOURCE_FILE (fndecl); - lineno = DECL_SOURCE_LINE (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; @@ -3069,10 +293,8 @@ get_hard_reg_initial_reg (fun, reg) return NULL_RTX; } -rtx -has_func_hard_reg_initial_val (fun, reg) - struct function *fun; - rtx reg; +static rtx +has_func_hard_reg_initial_val (struct function *fun, rtx reg) { struct initial_value_struct *ivs = fun->hard_reg_initial_vals; int i; @@ -3087,10 +309,8 @@ has_func_hard_reg_initial_val (fun, reg) return NULL_RTX; } -rtx -get_func_hard_reg_initial_val (fun, reg) - struct function *fun; - rtx reg; +static rtx +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); @@ -3100,20 +320,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; @@ -3123,40 +342,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; @@ -3171,14 +369,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; @@ -3194,9 +391,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