diff options
Diffstat (limited to 'gcc/integrate.c')
-rw-r--r-- | gcc/integrate.c | 1847 |
1 files changed, 20 insertions, 1827 deletions
diff --git a/gcc/integrate.c b/gcc/integrate.c index 3a58f2cda04..de897176ea0 100644 --- a/gcc/integrate.c +++ b/gcc/integrate.c @@ -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,29 +61,10 @@ typedef struct initial_value_struct GTY(()) { initial_value_pair * GTY ((length ("%h.num_entries"))) entries; } initial_value_struct; -static void setup_initial_hard_reg_value_integration (struct function *, - struct inline_remap *); - -static rtvec initialize_for_inline (tree); -static void note_modified_parmregs (rtx, rtx, void *); -static void integrate_parm_decls (tree, struct inline_remap *, rtvec); -static tree integrate_decl_tree (tree, struct inline_remap *); static void subst_constants (rtx *, rtx, struct inline_remap *, int); static void set_block_origin_self (tree); static void set_block_abstract_flags (tree, int); -static void process_reg_param (struct inline_remap *, rtx, rtx); static void mark_stores (rtx, rtx, void *); -static void save_parm_insns (rtx, rtx); -static void copy_insn_list (rtx, struct inline_remap *, rtx); -static void copy_insn_notes (rtx, struct inline_remap *, int); -static int compare_blocks (const void *, const void *); -static int find_block (const void *, const void *); - -/* 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; /* 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 @@ -136,195 +104,7 @@ function_attribute_inlinable_p (tree 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 (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=<n>). 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_longjmp) - return N_("function using longjmp 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 it's 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 (tree fndecl) -{ - int i; - rtvec arg_vector; - tree parms; - - /* Clear out PARMDECL_MAP. It was allocated in the caller's frame. */ - memset (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. */ @@ -379,7 +159,10 @@ copy_decl_for_inlining (tree decl, tree from_fn, tree to_fn) 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; + DECL_TOO_LATE (copy) = 0; + } } /* Set the DECL_ABSTRACT_ORIGIN so the debugging routines know what @@ -412,1439 +195,13 @@ copy_decl_for_inlining (tree decl, tree from_fn, tree to_fn) return copy; } - -/* Make the insns and PARM_DECLs of the current function permanent - and record other information in DECL_STRUCT_FUNCTION 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 (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 = 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); - cfun->saved_for_inline = 1; - - /* 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 (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 (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 (struct inline_remap *map, rtx loc, rtx 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 (const void *v1, const void *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 (const void *v1, const void *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 (tree fndecl, tree parms, rtx target, int ignore, - tree type, rtx structure_value_addr) -{ - struct function *inlining_previous; - struct function *inl_f = DECL_STRUCT_FUNCTION (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 = xmalloc (nargs * sizeof (rtx)); - arg_trees = 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 = TYPE_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. */ - arg_vals[i] = convert_modes (pmode, - TYPE_MODE (TREE_TYPE (arg)), - expand_expr (arg, NULL_RTX, mode, - EXPAND_SUM), - unsignedp); - } - else - arg_vals[i] = expand_expr (arg, NULL_RTX, mode, EXPAND_SUM); - } - else - arg_vals[i] = 0; - - /* If the formal type was const but the actual was not, we might - end up here with an rtx wrongly tagged unchanging in the caller's - context. Fix that. */ - if (arg_vals[i] != 0 - && (GET_CODE (arg_vals[i]) == REG || GET_CODE (arg_vals[i]) == MEM) - && ! TREE_READONLY (TREE_VALUE (actual))) - RTX_UNCHANGING_P (arg_vals[i]) = 0; - - if (arg_vals[i] != 0 - && (! TREE_READONLY (formal) - /* If the parameter is not read-only, copy our argument through - a register. Also, we cannot use ARG_VALS[I] if it overlaps - TARGET in any way. In the inline function, they will likely - be two different pseudos, and `safe_from_p' will make all - sorts of smart assumptions about their not conflicting. - But if ARG_VALS[I] overlaps TARGET, these assumptions are - wrong, so put ARG_VALS[I] into a fresh register. - Don't worry about invisible references, since their stack - temps will never overlap the target. */ - || (target != 0 - && ! invisiref - && (GET_CODE (arg_vals[i]) == REG - || GET_CODE (arg_vals[i]) == SUBREG - || GET_CODE (arg_vals[i]) == MEM) - && reg_overlap_mentioned_p (arg_vals[i], target)) - /* ??? We must always copy a SUBREG into a REG, because it might - get substituted into an address, and not all ports correctly - handle SUBREGs in addresses. */ - || (GET_CODE (arg_vals[i]) == SUBREG))) - arg_vals[i] = copy_to_mode_reg (GET_MODE (loc), arg_vals[i]); - - if (arg_vals[i] != 0 && GET_CODE (arg_vals[i]) == REG - && POINTER_TYPE_P (TREE_TYPE (formal))) - mark_reg_pointer (arg_vals[i], - TYPE_ALIGN (TREE_TYPE (TREE_TYPE (formal)))); - } - - /* Allocate the structures we use to remap things. */ - - map = xcalloc (1, sizeof (struct inline_remap)); - map->fndecl = fndecl; - - VARRAY_TREE_INIT (map->block_map, 10, "block_map"); - map->reg_map = 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 = 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 = 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 (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_copy (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_STRUCT_FUNCTION (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_line_note (DECL_SOURCE_LOCATION (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), 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 (&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 (NOTE_INSN_REPEATED_LINE_NUMBER); - - emit_line_note (input_location); - - /* 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)), 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 (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_LOCATOR (copy) = INSN_LOCATOR (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_LOCATOR (copy) = INSN_LOCATOR (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_LOCATOR (copy) = INSN_LOCATOR (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_copy (insn); - if (!copy) - /*Copied a line note, but line numbering is off*/; - else if ((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 - = 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 (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 (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 (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 (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. @@ -1884,10 +241,7 @@ copy_rtx_and_substitute (rtx orig, struct inline_remap *map, int for_lhs) Small hard registers are returned as-is. Pseudo-registers go through their `reg_map'. */ regno = REGNO (orig); - if (regno <= LAST_VIRTUAL_REGISTER - || (map->integrating - && DECL_STRUCT_FUNCTION (map->fndecl)->internal_arg_pointer - == orig)) + if (regno <= LAST_VIRTUAL_REGISTER) { /* Some hard registers are also mapped, but others are not translated. */ @@ -1942,10 +296,7 @@ copy_rtx_and_substitute (rtx orig, struct inline_remap *map, int for_lhs) emit_insn_after (seq, map->insns_at_start); return temp; } - else if (regno == VIRTUAL_INCOMING_ARGS_REGNUM - || (map->integrating - && (DECL_STRUCT_FUNCTION (map->fndecl)->internal_arg_pointer - == orig))) + else if (regno == VIRTUAL_INCOMING_ARGS_REGNUM) { /* Do the same for a block to contain any arguments referenced in memory. */ @@ -1977,44 +328,17 @@ copy_rtx_and_substitute (rtx orig, struct inline_remap *map, int for_lhs) } 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); + 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 - 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]; + /* Must be unrolling loops or replicating code if we + reach here, so return the register unchanged. */ + return orig; } -#endif else return orig; @@ -2136,44 +460,14 @@ copy_rtx_and_substitute (rtx orig, struct inline_remap *map, int for_lhs) remapped label. Otherwise, symbols are returned unchanged. */ if (CONSTANT_POOL_ADDRESS_P (orig)) { - struct function *f = inlining ? inlining : cfun; + struct function *f = cfun; rtx constant = get_pool_constant_for_function (f, orig); - enum machine_mode const_mode = get_pool_mode_for_function (f, orig); - if (inlining) - { - rtx temp = force_const_mem (const_mode, - copy_rtx_and_substitute (constant, - map, 0)); - -#if 0 - /* Legitimizing the address here is incorrect. - - Since we had a SYMBOL_REF before, we can assume it is valid - to have one in this position in the insn. - - Also, change_address may create new registers. These - registers will not have valid reg_map entries. This can - cause try_constants() to fail because assumes that all - registers in the rtx have valid reg_map entries, and it may - end up replacing one of these new registers with junk. */ - - if (! memory_address_p (GET_MODE (temp), XEXP (temp, 0))) - temp = change_address (temp, GET_MODE (temp), XEXP (temp, 0)); -#endif - - temp = XEXP (temp, 0); - temp = convert_memory_address (GET_MODE (orig), temp); - return temp; - } - else if (GET_CODE (constant) == LABEL_REF) + if (GET_CODE (constant) == LABEL_REF) return XEXP (force_const_mem (GET_MODE (orig), copy_rtx_and_substitute (constant, map, for_lhs)), 0); } - else if (TREE_CONSTANT_POOL_ADDRESS_P (orig) && inlining) - notice_rtl_inlining_of_deferred_constant (); - return orig; case CONST_DOUBLE: @@ -2192,10 +486,6 @@ copy_rtx_and_substitute (rtx orig, struct inline_remap *map, int for_lhs) CONST_DOUBLE_HIGH (orig), VOIDmode); case CONST: - /* Make new constant pool entry for a constant - that was in the pool of the inline function. */ - if (RTX_INTEGRATED_P (orig)) - abort (); break; case ASM_OPERANDS: @@ -2244,7 +534,8 @@ copy_rtx_and_substitute (rtx orig, struct inline_remap *map, int for_lhs) break; #if 0 - /* Must be ifdefed out for loop unrolling to work. */ + /* Must be ifdefed out for loop unrolling to work. */ + /* ??? Is this for the old or the new unroller? */ case RETURN: abort (); #endif @@ -2284,46 +575,9 @@ copy_rtx_and_substitute (rtx orig, struct inline_remap *map, int for_lhs) break; case MEM: - if (inlining - && GET_CODE (XEXP (orig, 0)) == SYMBOL_REF - && CONSTANT_POOL_ADDRESS_P (XEXP (orig, 0))) - { - enum machine_mode const_mode - = get_pool_mode_for_function (inlining, XEXP (orig, 0)); - rtx constant - = get_pool_constant_for_function (inlining, XEXP (orig, 0)); - - constant = copy_rtx_and_substitute (constant, map, 0); - - /* If this was an address of a constant pool entry that itself - had to be placed in the constant pool, it might not be a - valid address. So the recursive call might have turned it - into a register. In that case, it isn't a constant any - more, so return it. This has the potential of changing a - MEM into a REG, but we'll assume that it safe. */ - if (! CONSTANT_P (constant)) - return constant; - - return validize_mem (force_const_mem (const_mode, constant)); - } - copy = gen_rtx_MEM (mode, copy_rtx_and_substitute (XEXP (orig, 0), map, 0)); MEM_COPY_ATTRIBUTES (copy, orig); - - /* If inlining and this is not for the LHS, turn off RTX_UNCHANGING_P - since this may be an indirect reference to a parameter and the - actual may not be readonly. */ - if (inlining && !for_lhs) - RTX_UNCHANGING_P (copy) = 0; - - /* If inlining, squish aliasing data that references the subroutine's - parameter list, since that's no longer applicable. */ - if (inlining && MEM_EXPR (copy) - && TREE_CODE (MEM_EXPR (copy)) == INDIRECT_REF - && TREE_CODE (TREE_OPERAND (MEM_EXPR (copy), 0)) == PARM_DECL) - set_mem_expr (copy, NULL_TREE); - return copy; default: @@ -2951,52 +1205,6 @@ set_decl_abstract_flags (tree decl, int setting) } } -/* Output the assembly language code for the function FNDECL from - its DECL_STRUCT_FUNCTION. 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 (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_STRUCT_FUNCTION (fndecl); - - old_cfun = cfun; - cfun = f; - current_function_decl = fndecl; - - set_new_last_label_num (f->inl_max_label_num); - - /* We're not deferring this any longer. */ - DECL_DEFER_OUTPUT (fndecl) = 0; - - /* If requested, suppress debugging information. */ - if (f->no_debugging_symbols) - { - write_symbols = NO_DEBUG; - debug_hooks = &do_nothing_debug_hooks; - } - - /* Make sure warnings emitted by the optimizers (e.g. control reaches - end of non-void function) is not wildly incorrect. */ - input_location = DECL_SOURCE_LOCATION (fndecl); - - /* Compile this function all the way down to assembly code. As a - side effect this destroys the saved RTL representation, but - that's okay, because we don't need to inline this anymore. */ - rest_of_compilation (fndecl); - DECL_INLINE (fndecl) = 0; - - cfun = old_cfun; - current_function_decl = old_cfun ? old_cfun->decl : 0; - write_symbols = old_write_symbols; - debug_hooks = old_debug_hooks; -} - - /* Functions to keep track of the values hard regs had at the start of the function. */ @@ -3076,21 +1284,6 @@ 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 (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 (void) { |