mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			7150 lines
		
	
	
		
			211 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			7150 lines
		
	
	
		
			211 KiB
		
	
	
	
		
			C
		
	
	
	
| /* Expands front end tree to back end RTL for GCC.
 | ||
|    Copyright (C) 1987, 1988, 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
 | ||
|    1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
 | ||
|    2010, 2011, 2012  Free Software Foundation, Inc.
 | ||
| 
 | ||
| This file is part of GCC.
 | ||
| 
 | ||
| GCC is free software; you can redistribute it and/or modify it under
 | ||
| the terms of the GNU General Public License as published by the Free
 | ||
| Software Foundation; either version 3, or (at your option) any later
 | ||
| version.
 | ||
| 
 | ||
| GCC is distributed in the hope that it will be useful, but WITHOUT ANY
 | ||
| WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | ||
| FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 | ||
| for more details.
 | ||
| 
 | ||
| You should have received a copy of the GNU General Public License
 | ||
| along with GCC; see the file COPYING3.  If not see
 | ||
| <http://www.gnu.org/licenses/>.  */
 | ||
| 
 | ||
| /* This file handles the generation of rtl code from tree structure
 | ||
|    at the level of the function as a whole.
 | ||
|    It creates the rtl expressions for parameters and auto variables
 | ||
|    and has full responsibility for allocating stack slots.
 | ||
| 
 | ||
|    `expand_function_start' is called at the beginning of a function,
 | ||
|    before the function body is parsed, and `expand_function_end' is
 | ||
|    called after parsing the body.
 | ||
| 
 | ||
|    Call `assign_stack_local' to allocate a stack slot for a local variable.
 | ||
|    This is usually done during the RTL generation for the function body,
 | ||
|    but it can also be done in the reload pass when a pseudo-register does
 | ||
|    not get a hard register.  */
 | ||
| 
 | ||
| #include "config.h"
 | ||
| #include "system.h"
 | ||
| #include "coretypes.h"
 | ||
| #include "tm.h"
 | ||
| #include "rtl-error.h"
 | ||
| #include "tree.h"
 | ||
| #include "flags.h"
 | ||
| #include "except.h"
 | ||
| #include "function.h"
 | ||
| #include "expr.h"
 | ||
| #include "optabs.h"
 | ||
| #include "libfuncs.h"
 | ||
| #include "regs.h"
 | ||
| #include "hard-reg-set.h"
 | ||
| #include "insn-config.h"
 | ||
| #include "recog.h"
 | ||
| #include "output.h"
 | ||
| #include "basic-block.h"
 | ||
| #include "hashtab.h"
 | ||
| #include "ggc.h"
 | ||
| #include "tm_p.h"
 | ||
| #include "langhooks.h"
 | ||
| #include "target.h"
 | ||
| #include "common/common-target.h"
 | ||
| #include "gimple.h"
 | ||
| #include "tree-pass.h"
 | ||
| #include "predict.h"
 | ||
| #include "df.h"
 | ||
| #include "vecprim.h"
 | ||
| #include "params.h"
 | ||
| #include "bb-reorder.h"
 | ||
| 
 | ||
| /* So we can assign to cfun in this file.  */
 | ||
| #undef cfun
 | ||
| 
 | ||
| #ifndef STACK_ALIGNMENT_NEEDED
 | ||
| #define STACK_ALIGNMENT_NEEDED 1
 | ||
| #endif
 | ||
| 
 | ||
| #define STACK_BYTES (STACK_BOUNDARY / BITS_PER_UNIT)
 | ||
| 
 | ||
| /* Some systems use __main in a way incompatible with its use in gcc, in these
 | ||
|    cases use the macros NAME__MAIN to give a quoted symbol and SYMBOL__MAIN to
 | ||
|    give the same symbol without quotes for an alternative entry point.  You
 | ||
|    must define both, or neither.  */
 | ||
| #ifndef NAME__MAIN
 | ||
| #define NAME__MAIN "__main"
 | ||
| #endif
 | ||
| 
 | ||
| /* Round a value to the lowest integer less than it that is a multiple of
 | ||
|    the required alignment.  Avoid using division in case the value is
 | ||
|    negative.  Assume the alignment is a power of two.  */
 | ||
| #define FLOOR_ROUND(VALUE,ALIGN) ((VALUE) & ~((ALIGN) - 1))
 | ||
| 
 | ||
| /* Similar, but round to the next highest integer that meets the
 | ||
|    alignment.  */
 | ||
| #define CEIL_ROUND(VALUE,ALIGN)	(((VALUE) + (ALIGN) - 1) & ~((ALIGN)- 1))
 | ||
| 
 | ||
| /* Nonzero once virtual register instantiation has been done.
 | ||
|    assign_stack_local uses frame_pointer_rtx when this is nonzero.
 | ||
|    calls.c:emit_library_call_value_1 uses it to set up
 | ||
|    post-instantiation libcalls.  */
 | ||
| int virtuals_instantiated;
 | ||
| 
 | ||
| /* Assign unique numbers to labels generated for profiling, debugging, etc.  */
 | ||
| static GTY(()) int funcdef_no;
 | ||
| 
 | ||
| /* These variables hold pointers to functions to create and destroy
 | ||
|    target specific, per-function data structures.  */
 | ||
| struct machine_function * (*init_machine_status) (void);
 | ||
| 
 | ||
| /* The currently compiled function.  */
 | ||
| struct function *cfun = 0;
 | ||
| 
 | ||
| /* These hashes record the prologue and epilogue insns.  */
 | ||
| static GTY((if_marked ("ggc_marked_p"), param_is (struct rtx_def)))
 | ||
|   htab_t prologue_insn_hash;
 | ||
| static GTY((if_marked ("ggc_marked_p"), param_is (struct rtx_def)))
 | ||
|   htab_t epilogue_insn_hash;
 | ||
| 
 | ||
| 
 | ||
| htab_t types_used_by_vars_hash = NULL;
 | ||
| VEC(tree,gc) *types_used_by_cur_var_decl;
 | ||
| 
 | ||
| /* Forward declarations.  */
 | ||
| 
 | ||
| static struct temp_slot *find_temp_slot_from_address (rtx);
 | ||
| static void pad_to_arg_alignment (struct args_size *, int, struct args_size *);
 | ||
| static void pad_below (struct args_size *, enum machine_mode, tree);
 | ||
| static void reorder_blocks_1 (rtx, tree, VEC(tree,heap) **);
 | ||
| static int all_blocks (tree, tree *);
 | ||
| static tree *get_block_vector (tree, int *);
 | ||
| extern tree debug_find_var_in_block_tree (tree, tree);
 | ||
| /* We always define `record_insns' even if it's not used so that we
 | ||
|    can always export `prologue_epilogue_contains'.  */
 | ||
| static void record_insns (rtx, rtx, htab_t *) ATTRIBUTE_UNUSED;
 | ||
| static bool contains (const_rtx, htab_t);
 | ||
| static void prepare_function_start (void);
 | ||
| static void do_clobber_return_reg (rtx, void *);
 | ||
| static void do_use_return_reg (rtx, void *);
 | ||
| static void set_insn_locators (rtx, int) ATTRIBUTE_UNUSED;
 | ||
| 
 | ||
| /* Stack of nested functions.  */
 | ||
| /* Keep track of the cfun stack.  */
 | ||
| 
 | ||
| typedef struct function *function_p;
 | ||
| 
 | ||
| DEF_VEC_P(function_p);
 | ||
| DEF_VEC_ALLOC_P(function_p,heap);
 | ||
| static VEC(function_p,heap) *function_context_stack;
 | ||
| 
 | ||
| /* Save the current context for compilation of a nested function.
 | ||
|    This is called from language-specific code.  */
 | ||
| 
 | ||
| void
 | ||
| push_function_context (void)
 | ||
| {
 | ||
|   if (cfun == 0)
 | ||
|     allocate_struct_function (NULL, false);
 | ||
| 
 | ||
|   VEC_safe_push (function_p, heap, function_context_stack, cfun);
 | ||
|   set_cfun (NULL);
 | ||
| }
 | ||
| 
 | ||
| /* Restore the last saved context, at the end of a nested function.
 | ||
|    This function is called from language-specific code.  */
 | ||
| 
 | ||
| void
 | ||
| pop_function_context (void)
 | ||
| {
 | ||
|   struct function *p = VEC_pop (function_p, function_context_stack);
 | ||
|   set_cfun (p);
 | ||
|   current_function_decl = p->decl;
 | ||
| 
 | ||
|   /* Reset variables that have known state during rtx generation.  */
 | ||
|   virtuals_instantiated = 0;
 | ||
|   generating_concat_p = 1;
 | ||
| }
 | ||
| 
 | ||
| /* Clear out all parts of the state in F that can safely be discarded
 | ||
|    after the function has been parsed, but not compiled, to let
 | ||
|    garbage collection reclaim the memory.  */
 | ||
| 
 | ||
| void
 | ||
| free_after_parsing (struct function *f)
 | ||
| {
 | ||
|   f->language = 0;
 | ||
| }
 | ||
| 
 | ||
| /* Clear out all parts of the state in F that can safely be discarded
 | ||
|    after the function has been compiled, to let garbage collection
 | ||
|    reclaim the memory.  */
 | ||
| 
 | ||
| void
 | ||
| free_after_compilation (struct function *f)
 | ||
| {
 | ||
|   prologue_insn_hash = NULL;
 | ||
|   epilogue_insn_hash = NULL;
 | ||
| 
 | ||
|   free (crtl->emit.regno_pointer_align);
 | ||
| 
 | ||
|   memset (crtl, 0, sizeof (struct rtl_data));
 | ||
|   f->eh = NULL;
 | ||
|   f->machine = NULL;
 | ||
|   f->cfg = NULL;
 | ||
| 
 | ||
|   regno_reg_rtx = NULL;
 | ||
|   insn_locators_free ();
 | ||
| }
 | ||
| 
 | ||
| /* Return size needed for stack frame based on slots so far allocated.
 | ||
|    This size counts from zero.  It is not rounded to PREFERRED_STACK_BOUNDARY;
 | ||
|    the caller may have to do that.  */
 | ||
| 
 | ||
| HOST_WIDE_INT
 | ||
| get_frame_size (void)
 | ||
| {
 | ||
|   if (FRAME_GROWS_DOWNWARD)
 | ||
|     return -frame_offset;
 | ||
|   else
 | ||
|     return frame_offset;
 | ||
| }
 | ||
| 
 | ||
| /* Issue an error message and return TRUE if frame OFFSET overflows in
 | ||
|    the signed target pointer arithmetics for function FUNC.  Otherwise
 | ||
|    return FALSE.  */
 | ||
| 
 | ||
| bool
 | ||
| frame_offset_overflow (HOST_WIDE_INT offset, tree func)
 | ||
| {
 | ||
|   unsigned HOST_WIDE_INT size = FRAME_GROWS_DOWNWARD ? -offset : offset;
 | ||
| 
 | ||
|   if (size > ((unsigned HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (Pmode) - 1))
 | ||
| 	       /* Leave room for the fixed part of the frame.  */
 | ||
| 	       - 64 * UNITS_PER_WORD)
 | ||
|     {
 | ||
|       error_at (DECL_SOURCE_LOCATION (func),
 | ||
| 		"total size of local objects too large");
 | ||
|       return TRUE;
 | ||
|     }
 | ||
| 
 | ||
|   return FALSE;
 | ||
| }
 | ||
| 
 | ||
| /* Return stack slot alignment in bits for TYPE and MODE.  */
 | ||
| 
 | ||
| static unsigned int
 | ||
| get_stack_local_alignment (tree type, enum machine_mode mode)
 | ||
| {
 | ||
|   unsigned int alignment;
 | ||
| 
 | ||
|   if (mode == BLKmode)
 | ||
|     alignment = BIGGEST_ALIGNMENT;
 | ||
|   else
 | ||
|     alignment = GET_MODE_ALIGNMENT (mode);
 | ||
| 
 | ||
|   /* Allow the frond-end to (possibly) increase the alignment of this
 | ||
|      stack slot.  */
 | ||
|   if (! type)
 | ||
|     type = lang_hooks.types.type_for_mode (mode, 0);
 | ||
| 
 | ||
|   return STACK_SLOT_ALIGNMENT (type, mode, alignment);
 | ||
| }
 | ||
| 
 | ||
| /* Determine whether it is possible to fit a stack slot of size SIZE and
 | ||
|    alignment ALIGNMENT into an area in the stack frame that starts at
 | ||
|    frame offset START and has a length of LENGTH.  If so, store the frame
 | ||
|    offset to be used for the stack slot in *POFFSET and return true;
 | ||
|    return false otherwise.  This function will extend the frame size when
 | ||
|    given a start/length pair that lies at the end of the frame.  */
 | ||
| 
 | ||
| static bool
 | ||
| try_fit_stack_local (HOST_WIDE_INT start, HOST_WIDE_INT length,
 | ||
| 		     HOST_WIDE_INT size, unsigned int alignment,
 | ||
| 		     HOST_WIDE_INT *poffset)
 | ||
| {
 | ||
|   HOST_WIDE_INT this_frame_offset;
 | ||
|   int frame_off, frame_alignment, frame_phase;
 | ||
| 
 | ||
|   /* Calculate how many bytes the start of local variables is off from
 | ||
|      stack alignment.  */
 | ||
|   frame_alignment = PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT;
 | ||
|   frame_off = STARTING_FRAME_OFFSET % frame_alignment;
 | ||
|   frame_phase = frame_off ? frame_alignment - frame_off : 0;
 | ||
| 
 | ||
|   /* Round the frame offset to the specified alignment.  */
 | ||
| 
 | ||
|   /*  We must be careful here, since FRAME_OFFSET might be negative and
 | ||
|       division with a negative dividend isn't as well defined as we might
 | ||
|       like.  So we instead assume that ALIGNMENT is a power of two and
 | ||
|       use logical operations which are unambiguous.  */
 | ||
|   if (FRAME_GROWS_DOWNWARD)
 | ||
|     this_frame_offset
 | ||
|       = (FLOOR_ROUND (start + length - size - frame_phase,
 | ||
| 		      (unsigned HOST_WIDE_INT) alignment)
 | ||
| 	 + frame_phase);
 | ||
|   else
 | ||
|     this_frame_offset
 | ||
|       = (CEIL_ROUND (start - frame_phase,
 | ||
| 		     (unsigned HOST_WIDE_INT) alignment)
 | ||
| 	 + frame_phase);
 | ||
| 
 | ||
|   /* See if it fits.  If this space is at the edge of the frame,
 | ||
|      consider extending the frame to make it fit.  Our caller relies on
 | ||
|      this when allocating a new slot.  */
 | ||
|   if (frame_offset == start && this_frame_offset < frame_offset)
 | ||
|     frame_offset = this_frame_offset;
 | ||
|   else if (this_frame_offset < start)
 | ||
|     return false;
 | ||
|   else if (start + length == frame_offset
 | ||
| 	   && this_frame_offset + size > start + length)
 | ||
|     frame_offset = this_frame_offset + size;
 | ||
|   else if (this_frame_offset + size > start + length)
 | ||
|     return false;
 | ||
| 
 | ||
|   *poffset = this_frame_offset;
 | ||
|   return true;
 | ||
| }
 | ||
| 
 | ||
| /* Create a new frame_space structure describing free space in the stack
 | ||
|    frame beginning at START and ending at END, and chain it into the
 | ||
|    function's frame_space_list.  */
 | ||
| 
 | ||
| static void
 | ||
| add_frame_space (HOST_WIDE_INT start, HOST_WIDE_INT end)
 | ||
| {
 | ||
|   struct frame_space *space = ggc_alloc_frame_space ();
 | ||
|   space->next = crtl->frame_space_list;
 | ||
|   crtl->frame_space_list = space;
 | ||
|   space->start = start;
 | ||
|   space->length = end - start;
 | ||
| }
 | ||
| 
 | ||
| /* Allocate a stack slot of SIZE bytes and return a MEM rtx for it
 | ||
|    with machine mode MODE.
 | ||
| 
 | ||
|    ALIGN controls the amount of alignment for the address of the slot:
 | ||
|    0 means according to MODE,
 | ||
|    -1 means use BIGGEST_ALIGNMENT and round size to multiple of that,
 | ||
|    -2 means use BITS_PER_UNIT,
 | ||
|    positive specifies alignment boundary in bits.
 | ||
| 
 | ||
|    KIND has ASLK_REDUCE_ALIGN bit set if it is OK to reduce
 | ||
|    alignment and ASLK_RECORD_PAD bit set if we should remember
 | ||
|    extra space we allocated for alignment purposes.  When we are
 | ||
|    called from assign_stack_temp_for_type, it is not set so we don't
 | ||
|    track the same stack slot in two independent lists.
 | ||
| 
 | ||
|    We do not round to stack_boundary here.  */
 | ||
| 
 | ||
| rtx
 | ||
| assign_stack_local_1 (enum machine_mode mode, HOST_WIDE_INT size,
 | ||
| 		      int align, int kind)
 | ||
| {
 | ||
|   rtx x, addr;
 | ||
|   int bigend_correction = 0;
 | ||
|   HOST_WIDE_INT slot_offset = 0, old_frame_offset;
 | ||
|   unsigned int alignment, alignment_in_bits;
 | ||
| 
 | ||
|   if (align == 0)
 | ||
|     {
 | ||
|       alignment = get_stack_local_alignment (NULL, mode);
 | ||
|       alignment /= BITS_PER_UNIT;
 | ||
|     }
 | ||
|   else if (align == -1)
 | ||
|     {
 | ||
|       alignment = BIGGEST_ALIGNMENT / BITS_PER_UNIT;
 | ||
|       size = CEIL_ROUND (size, alignment);
 | ||
|     }
 | ||
|   else if (align == -2)
 | ||
|     alignment = 1; /* BITS_PER_UNIT / BITS_PER_UNIT */
 | ||
|   else
 | ||
|     alignment = align / BITS_PER_UNIT;
 | ||
| 
 | ||
|   alignment_in_bits = alignment * BITS_PER_UNIT;
 | ||
| 
 | ||
|   /* Ignore alignment if it exceeds MAX_SUPPORTED_STACK_ALIGNMENT.  */
 | ||
|   if (alignment_in_bits > MAX_SUPPORTED_STACK_ALIGNMENT)
 | ||
|     {
 | ||
|       alignment_in_bits = MAX_SUPPORTED_STACK_ALIGNMENT;
 | ||
|       alignment = alignment_in_bits / BITS_PER_UNIT;
 | ||
|     }
 | ||
| 
 | ||
|   if (SUPPORTS_STACK_ALIGNMENT)
 | ||
|     {
 | ||
|       if (crtl->stack_alignment_estimated < alignment_in_bits)
 | ||
| 	{
 | ||
|           if (!crtl->stack_realign_processed)
 | ||
| 	    crtl->stack_alignment_estimated = alignment_in_bits;
 | ||
|           else
 | ||
| 	    {
 | ||
| 	      /* If stack is realigned and stack alignment value
 | ||
| 		 hasn't been finalized, it is OK not to increase
 | ||
| 		 stack_alignment_estimated.  The bigger alignment
 | ||
| 		 requirement is recorded in stack_alignment_needed
 | ||
| 		 below.  */
 | ||
| 	      gcc_assert (!crtl->stack_realign_finalized);
 | ||
| 	      if (!crtl->stack_realign_needed)
 | ||
| 		{
 | ||
| 		  /* It is OK to reduce the alignment as long as the
 | ||
| 		     requested size is 0 or the estimated stack
 | ||
| 		     alignment >= mode alignment.  */
 | ||
| 		  gcc_assert ((kind & ASLK_REDUCE_ALIGN)
 | ||
| 		              || size == 0
 | ||
| 			      || (crtl->stack_alignment_estimated
 | ||
| 				  >= GET_MODE_ALIGNMENT (mode)));
 | ||
| 		  alignment_in_bits = crtl->stack_alignment_estimated;
 | ||
| 		  alignment = alignment_in_bits / BITS_PER_UNIT;
 | ||
| 		}
 | ||
| 	    }
 | ||
| 	}
 | ||
|     }
 | ||
| 
 | ||
|   if (crtl->stack_alignment_needed < alignment_in_bits)
 | ||
|     crtl->stack_alignment_needed = alignment_in_bits;
 | ||
|   if (crtl->max_used_stack_slot_alignment < alignment_in_bits)
 | ||
|     crtl->max_used_stack_slot_alignment = alignment_in_bits;
 | ||
| 
 | ||
|   if (mode != BLKmode || size != 0)
 | ||
|     {
 | ||
|       if (kind & ASLK_RECORD_PAD)
 | ||
| 	{
 | ||
| 	  struct frame_space **psp;
 | ||
| 
 | ||
| 	  for (psp = &crtl->frame_space_list; *psp; psp = &(*psp)->next)
 | ||
| 	    {
 | ||
| 	      struct frame_space *space = *psp;
 | ||
| 	      if (!try_fit_stack_local (space->start, space->length, size,
 | ||
| 					alignment, &slot_offset))
 | ||
| 		continue;
 | ||
| 	      *psp = space->next;
 | ||
| 	      if (slot_offset > space->start)
 | ||
| 		add_frame_space (space->start, slot_offset);
 | ||
| 	      if (slot_offset + size < space->start + space->length)
 | ||
| 		add_frame_space (slot_offset + size,
 | ||
| 				 space->start + space->length);
 | ||
| 	      goto found_space;
 | ||
| 	    }
 | ||
| 	}
 | ||
|     }
 | ||
|   else if (!STACK_ALIGNMENT_NEEDED)
 | ||
|     {
 | ||
|       slot_offset = frame_offset;
 | ||
|       goto found_space;
 | ||
|     }
 | ||
| 
 | ||
|   old_frame_offset = frame_offset;
 | ||
| 
 | ||
|   if (FRAME_GROWS_DOWNWARD)
 | ||
|     {
 | ||
|       frame_offset -= size;
 | ||
|       try_fit_stack_local (frame_offset, size, size, alignment, &slot_offset);
 | ||
| 
 | ||
|       if (kind & ASLK_RECORD_PAD)
 | ||
| 	{
 | ||
| 	  if (slot_offset > frame_offset)
 | ||
| 	    add_frame_space (frame_offset, slot_offset);
 | ||
| 	  if (slot_offset + size < old_frame_offset)
 | ||
| 	    add_frame_space (slot_offset + size, old_frame_offset);
 | ||
| 	}
 | ||
|     }
 | ||
|   else
 | ||
|     {
 | ||
|       frame_offset += size;
 | ||
|       try_fit_stack_local (old_frame_offset, size, size, alignment, &slot_offset);
 | ||
| 
 | ||
|       if (kind & ASLK_RECORD_PAD)
 | ||
| 	{
 | ||
| 	  if (slot_offset > old_frame_offset)
 | ||
| 	    add_frame_space (old_frame_offset, slot_offset);
 | ||
| 	  if (slot_offset + size < frame_offset)
 | ||
| 	    add_frame_space (slot_offset + size, frame_offset);
 | ||
| 	}
 | ||
|     }
 | ||
| 
 | ||
|  found_space:
 | ||
|   /* On a big-endian machine, if we are allocating more space than we will use,
 | ||
|      use the least significant bytes of those that are allocated.  */
 | ||
|   if (BYTES_BIG_ENDIAN && mode != BLKmode && GET_MODE_SIZE (mode) < size)
 | ||
|     bigend_correction = size - GET_MODE_SIZE (mode);
 | ||
| 
 | ||
|   /* If we have already instantiated virtual registers, return the actual
 | ||
|      address relative to the frame pointer.  */
 | ||
|   if (virtuals_instantiated)
 | ||
|     addr = plus_constant (Pmode, frame_pointer_rtx,
 | ||
| 			  trunc_int_for_mode
 | ||
| 			  (slot_offset + bigend_correction
 | ||
| 			   + STARTING_FRAME_OFFSET, Pmode));
 | ||
|   else
 | ||
|     addr = plus_constant (Pmode, virtual_stack_vars_rtx,
 | ||
| 			  trunc_int_for_mode
 | ||
| 			  (slot_offset + bigend_correction,
 | ||
| 			   Pmode));
 | ||
| 
 | ||
|   x = gen_rtx_MEM (mode, addr);
 | ||
|   set_mem_align (x, alignment_in_bits);
 | ||
|   MEM_NOTRAP_P (x) = 1;
 | ||
| 
 | ||
|   stack_slot_list
 | ||
|     = gen_rtx_EXPR_LIST (VOIDmode, x, stack_slot_list);
 | ||
| 
 | ||
|   if (frame_offset_overflow (frame_offset, current_function_decl))
 | ||
|     frame_offset = 0;
 | ||
| 
 | ||
|   return x;
 | ||
| }
 | ||
| 
 | ||
| /* Wrap up assign_stack_local_1 with last parameter as false.  */
 | ||
| 
 | ||
| rtx
 | ||
| assign_stack_local (enum machine_mode mode, HOST_WIDE_INT size, int align)
 | ||
| {
 | ||
|   return assign_stack_local_1 (mode, size, align, ASLK_RECORD_PAD);
 | ||
| }
 | ||
| 
 | ||
| /* In order to evaluate some expressions, such as function calls returning
 | ||
|    structures in memory, we need to temporarily allocate stack locations.
 | ||
|    We record each allocated temporary in the following structure.
 | ||
| 
 | ||
|    Associated with each temporary slot is a nesting level.  When we pop up
 | ||
|    one level, all temporaries associated with the previous level are freed.
 | ||
|    Normally, all temporaries are freed after the execution of the statement
 | ||
|    in which they were created.  However, if we are inside a ({...}) grouping,
 | ||
|    the result may be in a temporary and hence must be preserved.  If the
 | ||
|    result could be in a temporary, we preserve it if we can determine which
 | ||
|    one it is in.  If we cannot determine which temporary may contain the
 | ||
|    result, all temporaries are preserved.  A temporary is preserved by
 | ||
|    pretending it was allocated at the previous nesting level.  */
 | ||
| 
 | ||
| struct GTY(()) temp_slot {
 | ||
|   /* Points to next temporary slot.  */
 | ||
|   struct temp_slot *next;
 | ||
|   /* Points to previous temporary slot.  */
 | ||
|   struct temp_slot *prev;
 | ||
|   /* The rtx to used to reference the slot.  */
 | ||
|   rtx slot;
 | ||
|   /* The size, in units, of the slot.  */
 | ||
|   HOST_WIDE_INT size;
 | ||
|   /* The type of the object in the slot, or zero if it doesn't correspond
 | ||
|      to a type.  We use this to determine whether a slot can be reused.
 | ||
|      It can be reused if objects of the type of the new slot will always
 | ||
|      conflict with objects of the type of the old slot.  */
 | ||
|   tree type;
 | ||
|   /* The alignment (in bits) of the slot.  */
 | ||
|   unsigned int align;
 | ||
|   /* Nonzero if this temporary is currently in use.  */
 | ||
|   char in_use;
 | ||
|   /* Nesting level at which this slot is being used.  */
 | ||
|   int level;
 | ||
|   /* The offset of the slot from the frame_pointer, including extra space
 | ||
|      for alignment.  This info is for combine_temp_slots.  */
 | ||
|   HOST_WIDE_INT base_offset;
 | ||
|   /* The size of the slot, including extra space for alignment.  This
 | ||
|      info is for combine_temp_slots.  */
 | ||
|   HOST_WIDE_INT full_size;
 | ||
| };
 | ||
| 
 | ||
| /* A table of addresses that represent a stack slot.  The table is a mapping
 | ||
|    from address RTXen to a temp slot.  */
 | ||
| static GTY((param_is(struct temp_slot_address_entry))) htab_t temp_slot_address_table;
 | ||
| static size_t n_temp_slots_in_use;
 | ||
| 
 | ||
| /* Entry for the above hash table.  */
 | ||
| struct GTY(()) temp_slot_address_entry {
 | ||
|   hashval_t hash;
 | ||
|   rtx address;
 | ||
|   struct temp_slot *temp_slot;
 | ||
| };
 | ||
| 
 | ||
| /* Removes temporary slot TEMP from LIST.  */
 | ||
| 
 | ||
| static void
 | ||
| cut_slot_from_list (struct temp_slot *temp, struct temp_slot **list)
 | ||
| {
 | ||
|   if (temp->next)
 | ||
|     temp->next->prev = temp->prev;
 | ||
|   if (temp->prev)
 | ||
|     temp->prev->next = temp->next;
 | ||
|   else
 | ||
|     *list = temp->next;
 | ||
| 
 | ||
|   temp->prev = temp->next = NULL;
 | ||
| }
 | ||
| 
 | ||
| /* Inserts temporary slot TEMP to LIST.  */
 | ||
| 
 | ||
| static void
 | ||
| insert_slot_to_list (struct temp_slot *temp, struct temp_slot **list)
 | ||
| {
 | ||
|   temp->next = *list;
 | ||
|   if (*list)
 | ||
|     (*list)->prev = temp;
 | ||
|   temp->prev = NULL;
 | ||
|   *list = temp;
 | ||
| }
 | ||
| 
 | ||
| /* Returns the list of used temp slots at LEVEL.  */
 | ||
| 
 | ||
| static struct temp_slot **
 | ||
| temp_slots_at_level (int level)
 | ||
| {
 | ||
|   if (level >= (int) VEC_length (temp_slot_p, used_temp_slots))
 | ||
|     VEC_safe_grow_cleared (temp_slot_p, gc, used_temp_slots, level + 1);
 | ||
| 
 | ||
|   return &(VEC_address (temp_slot_p, used_temp_slots)[level]);
 | ||
| }
 | ||
| 
 | ||
| /* Returns the maximal temporary slot level.  */
 | ||
| 
 | ||
| static int
 | ||
| max_slot_level (void)
 | ||
| {
 | ||
|   if (!used_temp_slots)
 | ||
|     return -1;
 | ||
| 
 | ||
|   return VEC_length (temp_slot_p, used_temp_slots) - 1;
 | ||
| }
 | ||
| 
 | ||
| /* Moves temporary slot TEMP to LEVEL.  */
 | ||
| 
 | ||
| static void
 | ||
| move_slot_to_level (struct temp_slot *temp, int level)
 | ||
| {
 | ||
|   cut_slot_from_list (temp, temp_slots_at_level (temp->level));
 | ||
|   insert_slot_to_list (temp, temp_slots_at_level (level));
 | ||
|   temp->level = level;
 | ||
| }
 | ||
| 
 | ||
| /* Make temporary slot TEMP available.  */
 | ||
| 
 | ||
| static void
 | ||
| make_slot_available (struct temp_slot *temp)
 | ||
| {
 | ||
|   cut_slot_from_list (temp, temp_slots_at_level (temp->level));
 | ||
|   insert_slot_to_list (temp, &avail_temp_slots);
 | ||
|   temp->in_use = 0;
 | ||
|   temp->level = -1;
 | ||
|   n_temp_slots_in_use--;
 | ||
| }
 | ||
| 
 | ||
| /* Compute the hash value for an address -> temp slot mapping.
 | ||
|    The value is cached on the mapping entry.  */
 | ||
| static hashval_t
 | ||
| temp_slot_address_compute_hash (struct temp_slot_address_entry *t)
 | ||
| {
 | ||
|   int do_not_record = 0;
 | ||
|   return hash_rtx (t->address, GET_MODE (t->address),
 | ||
| 		   &do_not_record, NULL, false);
 | ||
| }
 | ||
| 
 | ||
| /* Return the hash value for an address -> temp slot mapping.  */
 | ||
| static hashval_t
 | ||
| temp_slot_address_hash (const void *p)
 | ||
| {
 | ||
|   const struct temp_slot_address_entry *t;
 | ||
|   t = (const struct temp_slot_address_entry *) p;
 | ||
|   return t->hash;
 | ||
| }
 | ||
| 
 | ||
| /* Compare two address -> temp slot mapping entries.  */
 | ||
| static int
 | ||
| temp_slot_address_eq (const void *p1, const void *p2)
 | ||
| {
 | ||
|   const struct temp_slot_address_entry *t1, *t2;
 | ||
|   t1 = (const struct temp_slot_address_entry *) p1;
 | ||
|   t2 = (const struct temp_slot_address_entry *) p2;
 | ||
|   return exp_equiv_p (t1->address, t2->address, 0, true);
 | ||
| }
 | ||
| 
 | ||
| /* Add ADDRESS as an alias of TEMP_SLOT to the addess -> temp slot mapping.  */
 | ||
| static void
 | ||
| insert_temp_slot_address (rtx address, struct temp_slot *temp_slot)
 | ||
| {
 | ||
|   void **slot;
 | ||
|   struct temp_slot_address_entry *t = ggc_alloc_temp_slot_address_entry ();
 | ||
|   t->address = address;
 | ||
|   t->temp_slot = temp_slot;
 | ||
|   t->hash = temp_slot_address_compute_hash (t);
 | ||
|   slot = htab_find_slot_with_hash (temp_slot_address_table, t, t->hash, INSERT);
 | ||
|   *slot = t;
 | ||
| }
 | ||
| 
 | ||
| /* Remove an address -> temp slot mapping entry if the temp slot is
 | ||
|    not in use anymore.  Callback for remove_unused_temp_slot_addresses.  */
 | ||
| static int
 | ||
| remove_unused_temp_slot_addresses_1 (void **slot, void *data ATTRIBUTE_UNUSED)
 | ||
| {
 | ||
|   const struct temp_slot_address_entry *t;
 | ||
|   t = (const struct temp_slot_address_entry *) *slot;
 | ||
|   if (! t->temp_slot->in_use)
 | ||
|     htab_clear_slot (temp_slot_address_table, slot);
 | ||
|   return 1;
 | ||
| }
 | ||
| 
 | ||
| /* Remove all mappings of addresses to unused temp slots.  */
 | ||
| static void
 | ||
| remove_unused_temp_slot_addresses (void)
 | ||
| {
 | ||
|   /* Use quicker clearing if there aren't any active temp slots.  */
 | ||
|   if (n_temp_slots_in_use)
 | ||
|     htab_traverse (temp_slot_address_table,
 | ||
| 		   remove_unused_temp_slot_addresses_1,
 | ||
| 		   NULL);
 | ||
|   else
 | ||
|     htab_empty (temp_slot_address_table);
 | ||
| }
 | ||
| 
 | ||
| /* Find the temp slot corresponding to the object at address X.  */
 | ||
| 
 | ||
| static struct temp_slot *
 | ||
| find_temp_slot_from_address (rtx x)
 | ||
| {
 | ||
|   struct temp_slot *p;
 | ||
|   struct temp_slot_address_entry tmp, *t;
 | ||
| 
 | ||
|   /* First try the easy way:
 | ||
|      See if X exists in the address -> temp slot mapping.  */
 | ||
|   tmp.address = x;
 | ||
|   tmp.temp_slot = NULL;
 | ||
|   tmp.hash = temp_slot_address_compute_hash (&tmp);
 | ||
|   t = (struct temp_slot_address_entry *)
 | ||
|     htab_find_with_hash (temp_slot_address_table, &tmp, tmp.hash);
 | ||
|   if (t)
 | ||
|     return t->temp_slot;
 | ||
| 
 | ||
|   /* If we have a sum involving a register, see if it points to a temp
 | ||
|      slot.  */
 | ||
|   if (GET_CODE (x) == PLUS && REG_P (XEXP (x, 0))
 | ||
|       && (p = find_temp_slot_from_address (XEXP (x, 0))) != 0)
 | ||
|     return p;
 | ||
|   else if (GET_CODE (x) == PLUS && REG_P (XEXP (x, 1))
 | ||
| 	   && (p = find_temp_slot_from_address (XEXP (x, 1))) != 0)
 | ||
|     return p;
 | ||
| 
 | ||
|   /* Last resort: Address is a virtual stack var address.  */
 | ||
|   if (GET_CODE (x) == PLUS
 | ||
|       && XEXP (x, 0) == virtual_stack_vars_rtx
 | ||
|       && CONST_INT_P (XEXP (x, 1)))
 | ||
|     {
 | ||
|       int i;
 | ||
|       for (i = max_slot_level (); i >= 0; i--)
 | ||
| 	for (p = *temp_slots_at_level (i); p; p = p->next)
 | ||
| 	  {
 | ||
| 	    if (INTVAL (XEXP (x, 1)) >= p->base_offset
 | ||
| 		&& INTVAL (XEXP (x, 1)) < p->base_offset + p->full_size)
 | ||
| 	      return p;
 | ||
| 	  }
 | ||
|     }
 | ||
| 
 | ||
|   return NULL;
 | ||
| }
 | ||
| 
 | ||
| /* Allocate a temporary stack slot and record it for possible later
 | ||
|    reuse.
 | ||
| 
 | ||
|    MODE is the machine mode to be given to the returned rtx.
 | ||
| 
 | ||
|    SIZE is the size in units of the space required.  We do no rounding here
 | ||
|    since assign_stack_local will do any required rounding.
 | ||
| 
 | ||
|    TYPE is the type that will be used for the stack slot.  */
 | ||
| 
 | ||
| rtx
 | ||
| assign_stack_temp_for_type (enum machine_mode mode, HOST_WIDE_INT size,
 | ||
| 			    tree type)
 | ||
| {
 | ||
|   unsigned int align;
 | ||
|   struct temp_slot *p, *best_p = 0, *selected = NULL, **pp;
 | ||
|   rtx slot;
 | ||
| 
 | ||
|   /* If SIZE is -1 it means that somebody tried to allocate a temporary
 | ||
|      of a variable size.  */
 | ||
|   gcc_assert (size != -1);
 | ||
| 
 | ||
|   align = get_stack_local_alignment (type, mode);
 | ||
| 
 | ||
|   /* Try to find an available, already-allocated temporary of the proper
 | ||
|      mode which meets the size and alignment requirements.  Choose the
 | ||
|      smallest one with the closest alignment.
 | ||
| 
 | ||
|      If assign_stack_temp is called outside of the tree->rtl expansion,
 | ||
|      we cannot reuse the stack slots (that may still refer to
 | ||
|      VIRTUAL_STACK_VARS_REGNUM).  */
 | ||
|   if (!virtuals_instantiated)
 | ||
|     {
 | ||
|       for (p = avail_temp_slots; p; p = p->next)
 | ||
| 	{
 | ||
| 	  if (p->align >= align && p->size >= size
 | ||
| 	      && GET_MODE (p->slot) == mode
 | ||
| 	      && objects_must_conflict_p (p->type, type)
 | ||
| 	      && (best_p == 0 || best_p->size > p->size
 | ||
| 		  || (best_p->size == p->size && best_p->align > p->align)))
 | ||
| 	    {
 | ||
| 	      if (p->align == align && p->size == size)
 | ||
| 		{
 | ||
| 		  selected = p;
 | ||
| 		  cut_slot_from_list (selected, &avail_temp_slots);
 | ||
| 		  best_p = 0;
 | ||
| 		  break;
 | ||
| 		}
 | ||
| 	      best_p = p;
 | ||
| 	    }
 | ||
| 	}
 | ||
|     }
 | ||
| 
 | ||
|   /* Make our best, if any, the one to use.  */
 | ||
|   if (best_p)
 | ||
|     {
 | ||
|       selected = best_p;
 | ||
|       cut_slot_from_list (selected, &avail_temp_slots);
 | ||
| 
 | ||
|       /* If there are enough aligned bytes left over, make them into a new
 | ||
| 	 temp_slot so that the extra bytes don't get wasted.  Do this only
 | ||
| 	 for BLKmode slots, so that we can be sure of the alignment.  */
 | ||
|       if (GET_MODE (best_p->slot) == BLKmode)
 | ||
| 	{
 | ||
| 	  int alignment = best_p->align / BITS_PER_UNIT;
 | ||
| 	  HOST_WIDE_INT rounded_size = CEIL_ROUND (size, alignment);
 | ||
| 
 | ||
| 	  if (best_p->size - rounded_size >= alignment)
 | ||
| 	    {
 | ||
| 	      p = ggc_alloc_temp_slot ();
 | ||
| 	      p->in_use = 0;
 | ||
| 	      p->size = best_p->size - rounded_size;
 | ||
| 	      p->base_offset = best_p->base_offset + rounded_size;
 | ||
| 	      p->full_size = best_p->full_size - rounded_size;
 | ||
| 	      p->slot = adjust_address_nv (best_p->slot, BLKmode, rounded_size);
 | ||
| 	      p->align = best_p->align;
 | ||
| 	      p->type = best_p->type;
 | ||
| 	      insert_slot_to_list (p, &avail_temp_slots);
 | ||
| 
 | ||
| 	      stack_slot_list = gen_rtx_EXPR_LIST (VOIDmode, p->slot,
 | ||
| 						   stack_slot_list);
 | ||
| 
 | ||
| 	      best_p->size = rounded_size;
 | ||
| 	      best_p->full_size = rounded_size;
 | ||
| 	    }
 | ||
| 	}
 | ||
|     }
 | ||
| 
 | ||
|   /* If we still didn't find one, make a new temporary.  */
 | ||
|   if (selected == 0)
 | ||
|     {
 | ||
|       HOST_WIDE_INT frame_offset_old = frame_offset;
 | ||
| 
 | ||
|       p = ggc_alloc_temp_slot ();
 | ||
| 
 | ||
|       /* We are passing an explicit alignment request to assign_stack_local.
 | ||
| 	 One side effect of that is assign_stack_local will not round SIZE
 | ||
| 	 to ensure the frame offset remains suitably aligned.
 | ||
| 
 | ||
| 	 So for requests which depended on the rounding of SIZE, we go ahead
 | ||
| 	 and round it now.  We also make sure ALIGNMENT is at least
 | ||
| 	 BIGGEST_ALIGNMENT.  */
 | ||
|       gcc_assert (mode != BLKmode || align == BIGGEST_ALIGNMENT);
 | ||
|       p->slot = assign_stack_local_1 (mode,
 | ||
| 				      (mode == BLKmode
 | ||
| 				       ? CEIL_ROUND (size,
 | ||
| 						     (int) align
 | ||
| 						     / BITS_PER_UNIT)
 | ||
| 				       : size),
 | ||
| 				      align, 0);
 | ||
| 
 | ||
|       p->align = align;
 | ||
| 
 | ||
|       /* The following slot size computation is necessary because we don't
 | ||
| 	 know the actual size of the temporary slot until assign_stack_local
 | ||
| 	 has performed all the frame alignment and size rounding for the
 | ||
| 	 requested temporary.  Note that extra space added for alignment
 | ||
| 	 can be either above or below this stack slot depending on which
 | ||
| 	 way the frame grows.  We include the extra space if and only if it
 | ||
| 	 is above this slot.  */
 | ||
|       if (FRAME_GROWS_DOWNWARD)
 | ||
| 	p->size = frame_offset_old - frame_offset;
 | ||
|       else
 | ||
| 	p->size = size;
 | ||
| 
 | ||
|       /* Now define the fields used by combine_temp_slots.  */
 | ||
|       if (FRAME_GROWS_DOWNWARD)
 | ||
| 	{
 | ||
| 	  p->base_offset = frame_offset;
 | ||
| 	  p->full_size = frame_offset_old - frame_offset;
 | ||
| 	}
 | ||
|       else
 | ||
| 	{
 | ||
| 	  p->base_offset = frame_offset_old;
 | ||
| 	  p->full_size = frame_offset - frame_offset_old;
 | ||
| 	}
 | ||
| 
 | ||
|       selected = p;
 | ||
|     }
 | ||
| 
 | ||
|   p = selected;
 | ||
|   p->in_use = 1;
 | ||
|   p->type = type;
 | ||
|   p->level = temp_slot_level;
 | ||
|   n_temp_slots_in_use++;
 | ||
| 
 | ||
|   pp = temp_slots_at_level (p->level);
 | ||
|   insert_slot_to_list (p, pp);
 | ||
|   insert_temp_slot_address (XEXP (p->slot, 0), p);
 | ||
| 
 | ||
|   /* Create a new MEM rtx to avoid clobbering MEM flags of old slots.  */
 | ||
|   slot = gen_rtx_MEM (mode, XEXP (p->slot, 0));
 | ||
|   stack_slot_list = gen_rtx_EXPR_LIST (VOIDmode, slot, stack_slot_list);
 | ||
| 
 | ||
|   /* If we know the alias set for the memory that will be used, use
 | ||
|      it.  If there's no TYPE, then we don't know anything about the
 | ||
|      alias set for the memory.  */
 | ||
|   set_mem_alias_set (slot, type ? get_alias_set (type) : 0);
 | ||
|   set_mem_align (slot, align);
 | ||
| 
 | ||
|   /* If a type is specified, set the relevant flags.  */
 | ||
|   if (type != 0)
 | ||
|     MEM_VOLATILE_P (slot) = TYPE_VOLATILE (type);
 | ||
|   MEM_NOTRAP_P (slot) = 1;
 | ||
| 
 | ||
|   return slot;
 | ||
| }
 | ||
| 
 | ||
| /* Allocate a temporary stack slot and record it for possible later
 | ||
|    reuse.  First two arguments are same as in preceding function.  */
 | ||
| 
 | ||
| rtx
 | ||
| assign_stack_temp (enum machine_mode mode, HOST_WIDE_INT size)
 | ||
| {
 | ||
|   return assign_stack_temp_for_type (mode, size, NULL_TREE);
 | ||
| }
 | ||
| 
 | ||
| /* Assign a temporary.
 | ||
|    If TYPE_OR_DECL is a decl, then we are doing it on behalf of the decl
 | ||
|    and so that should be used in error messages.  In either case, we
 | ||
|    allocate of the given type.
 | ||
|    MEMORY_REQUIRED is 1 if the result must be addressable stack memory;
 | ||
|    it is 0 if a register is OK.
 | ||
|    DONT_PROMOTE is 1 if we should not promote values in register
 | ||
|    to wider modes.  */
 | ||
| 
 | ||
| rtx
 | ||
| assign_temp (tree type_or_decl, int memory_required,
 | ||
| 	     int dont_promote ATTRIBUTE_UNUSED)
 | ||
| {
 | ||
|   tree type, decl;
 | ||
|   enum machine_mode mode;
 | ||
| #ifdef PROMOTE_MODE
 | ||
|   int unsignedp;
 | ||
| #endif
 | ||
| 
 | ||
|   if (DECL_P (type_or_decl))
 | ||
|     decl = type_or_decl, type = TREE_TYPE (decl);
 | ||
|   else
 | ||
|     decl = NULL, type = type_or_decl;
 | ||
| 
 | ||
|   mode = TYPE_MODE (type);
 | ||
| #ifdef PROMOTE_MODE
 | ||
|   unsignedp = TYPE_UNSIGNED (type);
 | ||
| #endif
 | ||
| 
 | ||
|   if (mode == BLKmode || memory_required)
 | ||
|     {
 | ||
|       HOST_WIDE_INT size = int_size_in_bytes (type);
 | ||
|       rtx tmp;
 | ||
| 
 | ||
|       /* Zero sized arrays are GNU C extension.  Set size to 1 to avoid
 | ||
| 	 problems with allocating the stack space.  */
 | ||
|       if (size == 0)
 | ||
| 	size = 1;
 | ||
| 
 | ||
|       /* Unfortunately, we don't yet know how to allocate variable-sized
 | ||
| 	 temporaries.  However, sometimes we can find a fixed upper limit on
 | ||
| 	 the size, so try that instead.  */
 | ||
|       else if (size == -1)
 | ||
| 	size = max_int_size_in_bytes (type);
 | ||
| 
 | ||
|       /* The size of the temporary may be too large to fit into an integer.  */
 | ||
|       /* ??? Not sure this should happen except for user silliness, so limit
 | ||
| 	 this to things that aren't compiler-generated temporaries.  The
 | ||
| 	 rest of the time we'll die in assign_stack_temp_for_type.  */
 | ||
|       if (decl && size == -1
 | ||
| 	  && TREE_CODE (TYPE_SIZE_UNIT (type)) == INTEGER_CST)
 | ||
| 	{
 | ||
| 	  error ("size of variable %q+D is too large", decl);
 | ||
| 	  size = 1;
 | ||
| 	}
 | ||
| 
 | ||
|       tmp = assign_stack_temp_for_type (mode, size, type);
 | ||
|       return tmp;
 | ||
|     }
 | ||
| 
 | ||
| #ifdef PROMOTE_MODE
 | ||
|   if (! dont_promote)
 | ||
|     mode = promote_mode (type, mode, &unsignedp);
 | ||
| #endif
 | ||
| 
 | ||
|   return gen_reg_rtx (mode);
 | ||
| }
 | ||
| 
 | ||
| /* Combine temporary stack slots which are adjacent on the stack.
 | ||
| 
 | ||
|    This allows for better use of already allocated stack space.  This is only
 | ||
|    done for BLKmode slots because we can be sure that we won't have alignment
 | ||
|    problems in this case.  */
 | ||
| 
 | ||
| static void
 | ||
| combine_temp_slots (void)
 | ||
| {
 | ||
|   struct temp_slot *p, *q, *next, *next_q;
 | ||
|   int num_slots;
 | ||
| 
 | ||
|   /* We can't combine slots, because the information about which slot
 | ||
|      is in which alias set will be lost.  */
 | ||
|   if (flag_strict_aliasing)
 | ||
|     return;
 | ||
| 
 | ||
|   /* If there are a lot of temp slots, don't do anything unless
 | ||
|      high levels of optimization.  */
 | ||
|   if (! flag_expensive_optimizations)
 | ||
|     for (p = avail_temp_slots, num_slots = 0; p; p = p->next, num_slots++)
 | ||
|       if (num_slots > 100 || (num_slots > 10 && optimize == 0))
 | ||
| 	return;
 | ||
| 
 | ||
|   for (p = avail_temp_slots; p; p = next)
 | ||
|     {
 | ||
|       int delete_p = 0;
 | ||
| 
 | ||
|       next = p->next;
 | ||
| 
 | ||
|       if (GET_MODE (p->slot) != BLKmode)
 | ||
| 	continue;
 | ||
| 
 | ||
|       for (q = p->next; q; q = next_q)
 | ||
| 	{
 | ||
|        	  int delete_q = 0;
 | ||
| 
 | ||
| 	  next_q = q->next;
 | ||
| 
 | ||
| 	  if (GET_MODE (q->slot) != BLKmode)
 | ||
| 	    continue;
 | ||
| 
 | ||
| 	  if (p->base_offset + p->full_size == q->base_offset)
 | ||
| 	    {
 | ||
| 	      /* Q comes after P; combine Q into P.  */
 | ||
| 	      p->size += q->size;
 | ||
| 	      p->full_size += q->full_size;
 | ||
| 	      delete_q = 1;
 | ||
| 	    }
 | ||
| 	  else if (q->base_offset + q->full_size == p->base_offset)
 | ||
| 	    {
 | ||
| 	      /* P comes after Q; combine P into Q.  */
 | ||
| 	      q->size += p->size;
 | ||
| 	      q->full_size += p->full_size;
 | ||
| 	      delete_p = 1;
 | ||
| 	      break;
 | ||
| 	    }
 | ||
| 	  if (delete_q)
 | ||
| 	    cut_slot_from_list (q, &avail_temp_slots);
 | ||
| 	}
 | ||
| 
 | ||
|       /* Either delete P or advance past it.  */
 | ||
|       if (delete_p)
 | ||
| 	cut_slot_from_list (p, &avail_temp_slots);
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /* Indicate that NEW_RTX is an alternate way of referring to the temp
 | ||
|    slot that previously was known by OLD_RTX.  */
 | ||
| 
 | ||
| void
 | ||
| update_temp_slot_address (rtx old_rtx, rtx new_rtx)
 | ||
| {
 | ||
|   struct temp_slot *p;
 | ||
| 
 | ||
|   if (rtx_equal_p (old_rtx, new_rtx))
 | ||
|     return;
 | ||
| 
 | ||
|   p = find_temp_slot_from_address (old_rtx);
 | ||
| 
 | ||
|   /* If we didn't find one, see if both OLD_RTX is a PLUS.  If so, and
 | ||
|      NEW_RTX is a register, see if one operand of the PLUS is a
 | ||
|      temporary location.  If so, NEW_RTX points into it.  Otherwise,
 | ||
|      if both OLD_RTX and NEW_RTX are a PLUS and if there is a register
 | ||
|      in common between them.  If so, try a recursive call on those
 | ||
|      values.  */
 | ||
|   if (p == 0)
 | ||
|     {
 | ||
|       if (GET_CODE (old_rtx) != PLUS)
 | ||
| 	return;
 | ||
| 
 | ||
|       if (REG_P (new_rtx))
 | ||
| 	{
 | ||
| 	  update_temp_slot_address (XEXP (old_rtx, 0), new_rtx);
 | ||
| 	  update_temp_slot_address (XEXP (old_rtx, 1), new_rtx);
 | ||
| 	  return;
 | ||
| 	}
 | ||
|       else if (GET_CODE (new_rtx) != PLUS)
 | ||
| 	return;
 | ||
| 
 | ||
|       if (rtx_equal_p (XEXP (old_rtx, 0), XEXP (new_rtx, 0)))
 | ||
| 	update_temp_slot_address (XEXP (old_rtx, 1), XEXP (new_rtx, 1));
 | ||
|       else if (rtx_equal_p (XEXP (old_rtx, 1), XEXP (new_rtx, 0)))
 | ||
| 	update_temp_slot_address (XEXP (old_rtx, 0), XEXP (new_rtx, 1));
 | ||
|       else if (rtx_equal_p (XEXP (old_rtx, 0), XEXP (new_rtx, 1)))
 | ||
| 	update_temp_slot_address (XEXP (old_rtx, 1), XEXP (new_rtx, 0));
 | ||
|       else if (rtx_equal_p (XEXP (old_rtx, 1), XEXP (new_rtx, 1)))
 | ||
| 	update_temp_slot_address (XEXP (old_rtx, 0), XEXP (new_rtx, 0));
 | ||
| 
 | ||
|       return;
 | ||
|     }
 | ||
| 
 | ||
|   /* Otherwise add an alias for the temp's address.  */
 | ||
|   insert_temp_slot_address (new_rtx, p);
 | ||
| }
 | ||
| 
 | ||
| /* If X could be a reference to a temporary slot, mark that slot as
 | ||
|    belonging to the to one level higher than the current level.  If X
 | ||
|    matched one of our slots, just mark that one.  Otherwise, we can't
 | ||
|    easily predict which it is, so upgrade all of them.
 | ||
| 
 | ||
|    This is called when an ({...}) construct occurs and a statement
 | ||
|    returns a value in memory.  */
 | ||
| 
 | ||
| void
 | ||
| preserve_temp_slots (rtx x)
 | ||
| {
 | ||
|   struct temp_slot *p = 0, *next;
 | ||
| 
 | ||
|   if (x == 0)
 | ||
|     return;
 | ||
| 
 | ||
|   /* If X is a register that is being used as a pointer, see if we have
 | ||
|      a temporary slot we know it points to.  */
 | ||
|   if (REG_P (x) && REG_POINTER (x))
 | ||
|     p = find_temp_slot_from_address (x);
 | ||
| 
 | ||
|   /* If X is not in memory or is at a constant address, it cannot be in
 | ||
|      a temporary slot.  */
 | ||
|   if (p == 0 && (!MEM_P (x) || CONSTANT_P (XEXP (x, 0))))
 | ||
|     return;
 | ||
| 
 | ||
|   /* First see if we can find a match.  */
 | ||
|   if (p == 0)
 | ||
|     p = find_temp_slot_from_address (XEXP (x, 0));
 | ||
| 
 | ||
|   if (p != 0)
 | ||
|     {
 | ||
|       if (p->level == temp_slot_level)
 | ||
| 	move_slot_to_level (p, temp_slot_level - 1);
 | ||
|       return;
 | ||
|     }
 | ||
| 
 | ||
|   /* Otherwise, preserve all non-kept slots at this level.  */
 | ||
|   for (p = *temp_slots_at_level (temp_slot_level); p; p = next)
 | ||
|     {
 | ||
|       next = p->next;
 | ||
|       move_slot_to_level (p, temp_slot_level - 1);
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /* Free all temporaries used so far.  This is normally called at the
 | ||
|    end of generating code for a statement.  */
 | ||
| 
 | ||
| void
 | ||
| free_temp_slots (void)
 | ||
| {
 | ||
|   struct temp_slot *p, *next;
 | ||
|   bool some_available = false;
 | ||
| 
 | ||
|   for (p = *temp_slots_at_level (temp_slot_level); p; p = next)
 | ||
|     {
 | ||
|       next = p->next;
 | ||
|       make_slot_available (p);
 | ||
|       some_available = true;
 | ||
|     }
 | ||
| 
 | ||
|   if (some_available)
 | ||
|     {
 | ||
|       remove_unused_temp_slot_addresses ();
 | ||
|       combine_temp_slots ();
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /* Push deeper into the nesting level for stack temporaries.  */
 | ||
| 
 | ||
| void
 | ||
| push_temp_slots (void)
 | ||
| {
 | ||
|   temp_slot_level++;
 | ||
| }
 | ||
| 
 | ||
| /* Pop a temporary nesting level.  All slots in use in the current level
 | ||
|    are freed.  */
 | ||
| 
 | ||
| void
 | ||
| pop_temp_slots (void)
 | ||
| {
 | ||
|   free_temp_slots ();
 | ||
|   temp_slot_level--;
 | ||
| }
 | ||
| 
 | ||
| /* Initialize temporary slots.  */
 | ||
| 
 | ||
| void
 | ||
| init_temp_slots (void)
 | ||
| {
 | ||
|   /* We have not allocated any temporaries yet.  */
 | ||
|   avail_temp_slots = 0;
 | ||
|   used_temp_slots = 0;
 | ||
|   temp_slot_level = 0;
 | ||
|   n_temp_slots_in_use = 0;
 | ||
| 
 | ||
|   /* Set up the table to map addresses to temp slots.  */
 | ||
|   if (! temp_slot_address_table)
 | ||
|     temp_slot_address_table = htab_create_ggc (32,
 | ||
| 					       temp_slot_address_hash,
 | ||
| 					       temp_slot_address_eq,
 | ||
| 					       NULL);
 | ||
|   else
 | ||
|     htab_empty (temp_slot_address_table);
 | ||
| }
 | ||
| 
 | ||
| /* Functions and data structures to keep track of the values hard regs
 | ||
|    had at the start of the function.  */
 | ||
| 
 | ||
| /* Private type used by get_hard_reg_initial_reg, get_hard_reg_initial_val,
 | ||
|    and has_hard_reg_initial_val..  */
 | ||
| typedef struct GTY(()) initial_value_pair {
 | ||
|   rtx hard_reg;
 | ||
|   rtx pseudo;
 | ||
| } initial_value_pair;
 | ||
| /* ???  This could be a VEC but there is currently no way to define an
 | ||
|    opaque VEC type.  This could be worked around by defining struct
 | ||
|    initial_value_pair in function.h.  */
 | ||
| typedef struct GTY(()) initial_value_struct {
 | ||
|   int num_entries;
 | ||
|   int max_entries;
 | ||
|   initial_value_pair * GTY ((length ("%h.num_entries"))) entries;
 | ||
| } initial_value_struct;
 | ||
| 
 | ||
| /* If a pseudo represents an initial hard reg (or expression), return
 | ||
|    it, else return NULL_RTX.  */
 | ||
| 
 | ||
| rtx
 | ||
| get_hard_reg_initial_reg (rtx reg)
 | ||
| {
 | ||
|   struct initial_value_struct *ivs = crtl->hard_reg_initial_vals;
 | ||
|   int i;
 | ||
| 
 | ||
|   if (ivs == 0)
 | ||
|     return NULL_RTX;
 | ||
| 
 | ||
|   for (i = 0; i < ivs->num_entries; i++)
 | ||
|     if (rtx_equal_p (ivs->entries[i].pseudo, reg))
 | ||
|       return ivs->entries[i].hard_reg;
 | ||
| 
 | ||
|   return NULL_RTX;
 | ||
| }
 | ||
| 
 | ||
| /* Make sure that there's a pseudo register of mode MODE that stores the
 | ||
|    initial value of hard register REGNO.  Return an rtx for such a pseudo.  */
 | ||
| 
 | ||
| rtx
 | ||
| get_hard_reg_initial_val (enum machine_mode mode, unsigned int regno)
 | ||
| {
 | ||
|   struct initial_value_struct *ivs;
 | ||
|   rtx rv;
 | ||
| 
 | ||
|   rv = has_hard_reg_initial_val (mode, regno);
 | ||
|   if (rv)
 | ||
|     return rv;
 | ||
| 
 | ||
|   ivs = crtl->hard_reg_initial_vals;
 | ||
|   if (ivs == 0)
 | ||
|     {
 | ||
|       ivs = ggc_alloc_initial_value_struct ();
 | ||
|       ivs->num_entries = 0;
 | ||
|       ivs->max_entries = 5;
 | ||
|       ivs->entries = ggc_alloc_vec_initial_value_pair (5);
 | ||
|       crtl->hard_reg_initial_vals = ivs;
 | ||
|     }
 | ||
| 
 | ||
|   if (ivs->num_entries >= ivs->max_entries)
 | ||
|     {
 | ||
|       ivs->max_entries += 5;
 | ||
|       ivs->entries = GGC_RESIZEVEC (initial_value_pair, ivs->entries,
 | ||
| 				    ivs->max_entries);
 | ||
|     }
 | ||
| 
 | ||
|   ivs->entries[ivs->num_entries].hard_reg = gen_rtx_REG (mode, regno);
 | ||
|   ivs->entries[ivs->num_entries].pseudo = gen_reg_rtx (mode);
 | ||
| 
 | ||
|   return ivs->entries[ivs->num_entries++].pseudo;
 | ||
| }
 | ||
| 
 | ||
| /* See if get_hard_reg_initial_val has been used to create a pseudo
 | ||
|    for the initial value of hard register REGNO in mode MODE.  Return
 | ||
|    the associated pseudo if so, otherwise return NULL.  */
 | ||
| 
 | ||
| rtx
 | ||
| has_hard_reg_initial_val (enum machine_mode mode, unsigned int regno)
 | ||
| {
 | ||
|   struct initial_value_struct *ivs;
 | ||
|   int i;
 | ||
| 
 | ||
|   ivs = crtl->hard_reg_initial_vals;
 | ||
|   if (ivs != 0)
 | ||
|     for (i = 0; i < ivs->num_entries; i++)
 | ||
|       if (GET_MODE (ivs->entries[i].hard_reg) == mode
 | ||
| 	  && REGNO (ivs->entries[i].hard_reg) == regno)
 | ||
| 	return ivs->entries[i].pseudo;
 | ||
| 
 | ||
|   return NULL_RTX;
 | ||
| }
 | ||
| 
 | ||
| unsigned int
 | ||
| emit_initial_value_sets (void)
 | ||
| {
 | ||
|   struct initial_value_struct *ivs = crtl->hard_reg_initial_vals;
 | ||
|   int i;
 | ||
|   rtx seq;
 | ||
| 
 | ||
|   if (ivs == 0)
 | ||
|     return 0;
 | ||
| 
 | ||
|   start_sequence ();
 | ||
|   for (i = 0; i < ivs->num_entries; i++)
 | ||
|     emit_move_insn (ivs->entries[i].pseudo, ivs->entries[i].hard_reg);
 | ||
|   seq = get_insns ();
 | ||
|   end_sequence ();
 | ||
| 
 | ||
|   emit_insn_at_entry (seq);
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| /* Return the hardreg-pseudoreg initial values pair entry I and
 | ||
|    TRUE if I is a valid entry, or FALSE if I is not a valid entry.  */
 | ||
| bool
 | ||
| initial_value_entry (int i, rtx *hreg, rtx *preg)
 | ||
| {
 | ||
|   struct initial_value_struct *ivs = crtl->hard_reg_initial_vals;
 | ||
|   if (!ivs || i >= ivs->num_entries)
 | ||
|     return false;
 | ||
| 
 | ||
|   *hreg = ivs->entries[i].hard_reg;
 | ||
|   *preg = ivs->entries[i].pseudo;
 | ||
|   return true;
 | ||
| }
 | ||
| 
 | ||
| /* These routines are responsible for converting virtual register references
 | ||
|    to the actual hard register references once RTL generation is complete.
 | ||
| 
 | ||
|    The following four variables are used for communication between the
 | ||
|    routines.  They contain the offsets of the virtual registers from their
 | ||
|    respective hard registers.  */
 | ||
| 
 | ||
| static int in_arg_offset;
 | ||
| static int var_offset;
 | ||
| static int dynamic_offset;
 | ||
| static int out_arg_offset;
 | ||
| static int cfa_offset;
 | ||
| 
 | ||
| /* In most machines, the stack pointer register is equivalent to the bottom
 | ||
|    of the stack.  */
 | ||
| 
 | ||
| #ifndef STACK_POINTER_OFFSET
 | ||
| #define STACK_POINTER_OFFSET	0
 | ||
| #endif
 | ||
| 
 | ||
| /* If not defined, pick an appropriate default for the offset of dynamically
 | ||
|    allocated memory depending on the value of ACCUMULATE_OUTGOING_ARGS,
 | ||
|    REG_PARM_STACK_SPACE, and OUTGOING_REG_PARM_STACK_SPACE.  */
 | ||
| 
 | ||
| #ifndef STACK_DYNAMIC_OFFSET
 | ||
| 
 | ||
| /* The bottom of the stack points to the actual arguments.  If
 | ||
|    REG_PARM_STACK_SPACE is defined, this includes the space for the register
 | ||
|    parameters.  However, if OUTGOING_REG_PARM_STACK space is not defined,
 | ||
|    stack space for register parameters is not pushed by the caller, but
 | ||
|    rather part of the fixed stack areas and hence not included in
 | ||
|    `crtl->outgoing_args_size'.  Nevertheless, we must allow
 | ||
|    for it when allocating stack dynamic objects.  */
 | ||
| 
 | ||
| #if defined(REG_PARM_STACK_SPACE)
 | ||
| #define STACK_DYNAMIC_OFFSET(FNDECL)	\
 | ||
| ((ACCUMULATE_OUTGOING_ARGS						      \
 | ||
|   ? (crtl->outgoing_args_size				      \
 | ||
|      + (OUTGOING_REG_PARM_STACK_SPACE ((!(FNDECL) ? NULL_TREE : TREE_TYPE (FNDECL))) ? 0 \
 | ||
| 					       : REG_PARM_STACK_SPACE (FNDECL))) \
 | ||
|   : 0) + (STACK_POINTER_OFFSET))
 | ||
| #else
 | ||
| #define STACK_DYNAMIC_OFFSET(FNDECL)	\
 | ||
| ((ACCUMULATE_OUTGOING_ARGS ? crtl->outgoing_args_size : 0)	      \
 | ||
|  + (STACK_POINTER_OFFSET))
 | ||
| #endif
 | ||
| #endif
 | ||
| 
 | ||
| 
 | ||
| /* Given a piece of RTX and a pointer to a HOST_WIDE_INT, if the RTX
 | ||
|    is a virtual register, return the equivalent hard register and set the
 | ||
|    offset indirectly through the pointer.  Otherwise, return 0.  */
 | ||
| 
 | ||
| static rtx
 | ||
| instantiate_new_reg (rtx x, HOST_WIDE_INT *poffset)
 | ||
| {
 | ||
|   rtx new_rtx;
 | ||
|   HOST_WIDE_INT offset;
 | ||
| 
 | ||
|   if (x == virtual_incoming_args_rtx)
 | ||
|     {
 | ||
|       if (stack_realign_drap)
 | ||
|         {
 | ||
| 	  /* Replace virtual_incoming_args_rtx with internal arg
 | ||
| 	     pointer if DRAP is used to realign stack.  */
 | ||
|           new_rtx = crtl->args.internal_arg_pointer;
 | ||
|           offset = 0;
 | ||
|         }
 | ||
|       else
 | ||
|         new_rtx = arg_pointer_rtx, offset = in_arg_offset;
 | ||
|     }
 | ||
|   else if (x == virtual_stack_vars_rtx)
 | ||
|     new_rtx = frame_pointer_rtx, offset = var_offset;
 | ||
|   else if (x == virtual_stack_dynamic_rtx)
 | ||
|     new_rtx = stack_pointer_rtx, offset = dynamic_offset;
 | ||
|   else if (x == virtual_outgoing_args_rtx)
 | ||
|     new_rtx = stack_pointer_rtx, offset = out_arg_offset;
 | ||
|   else if (x == virtual_cfa_rtx)
 | ||
|     {
 | ||
| #ifdef FRAME_POINTER_CFA_OFFSET
 | ||
|       new_rtx = frame_pointer_rtx;
 | ||
| #else
 | ||
|       new_rtx = arg_pointer_rtx;
 | ||
| #endif
 | ||
|       offset = cfa_offset;
 | ||
|     }
 | ||
|   else if (x == virtual_preferred_stack_boundary_rtx)
 | ||
|     {
 | ||
|       new_rtx = GEN_INT (crtl->preferred_stack_boundary / BITS_PER_UNIT);
 | ||
|       offset = 0;
 | ||
|     }
 | ||
|   else
 | ||
|     return NULL_RTX;
 | ||
| 
 | ||
|   *poffset = offset;
 | ||
|   return new_rtx;
 | ||
| }
 | ||
| 
 | ||
| /* A subroutine of instantiate_virtual_regs, called via for_each_rtx.
 | ||
|    Instantiate any virtual registers present inside of *LOC.  The expression
 | ||
|    is simplified, as much as possible, but is not to be considered "valid"
 | ||
|    in any sense implied by the target.  If any change is made, set CHANGED
 | ||
|    to true.  */
 | ||
| 
 | ||
| static int
 | ||
| instantiate_virtual_regs_in_rtx (rtx *loc, void *data)
 | ||
| {
 | ||
|   HOST_WIDE_INT offset;
 | ||
|   bool *changed = (bool *) data;
 | ||
|   rtx x, new_rtx;
 | ||
| 
 | ||
|   x = *loc;
 | ||
|   if (x == 0)
 | ||
|     return 0;
 | ||
| 
 | ||
|   switch (GET_CODE (x))
 | ||
|     {
 | ||
|     case REG:
 | ||
|       new_rtx = instantiate_new_reg (x, &offset);
 | ||
|       if (new_rtx)
 | ||
| 	{
 | ||
| 	  *loc = plus_constant (GET_MODE (x), new_rtx, offset);
 | ||
| 	  if (changed)
 | ||
| 	    *changed = true;
 | ||
| 	}
 | ||
|       return -1;
 | ||
| 
 | ||
|     case PLUS:
 | ||
|       new_rtx = instantiate_new_reg (XEXP (x, 0), &offset);
 | ||
|       if (new_rtx)
 | ||
| 	{
 | ||
| 	  new_rtx = plus_constant (GET_MODE (x), new_rtx, offset);
 | ||
| 	  *loc = simplify_gen_binary (PLUS, GET_MODE (x), new_rtx, XEXP (x, 1));
 | ||
| 	  if (changed)
 | ||
| 	    *changed = true;
 | ||
| 	  return -1;
 | ||
| 	}
 | ||
| 
 | ||
|       /* FIXME -- from old code */
 | ||
| 	  /* If we have (plus (subreg (virtual-reg)) (const_int)), we know
 | ||
| 	     we can commute the PLUS and SUBREG because pointers into the
 | ||
| 	     frame are well-behaved.  */
 | ||
|       break;
 | ||
| 
 | ||
|     default:
 | ||
|       break;
 | ||
|     }
 | ||
| 
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| /* A subroutine of instantiate_virtual_regs_in_insn.  Return true if X
 | ||
|    matches the predicate for insn CODE operand OPERAND.  */
 | ||
| 
 | ||
| static int
 | ||
| safe_insn_predicate (int code, int operand, rtx x)
 | ||
| {
 | ||
|   return code < 0 || insn_operand_matches ((enum insn_code) code, operand, x);
 | ||
| }
 | ||
| 
 | ||
| /* A subroutine of instantiate_virtual_regs.  Instantiate any virtual
 | ||
|    registers present inside of insn.  The result will be a valid insn.  */
 | ||
| 
 | ||
| static void
 | ||
| instantiate_virtual_regs_in_insn (rtx insn)
 | ||
| {
 | ||
|   HOST_WIDE_INT offset;
 | ||
|   int insn_code, i;
 | ||
|   bool any_change = false;
 | ||
|   rtx set, new_rtx, x, seq;
 | ||
| 
 | ||
|   /* There are some special cases to be handled first.  */
 | ||
|   set = single_set (insn);
 | ||
|   if (set)
 | ||
|     {
 | ||
|       /* We're allowed to assign to a virtual register.  This is interpreted
 | ||
| 	 to mean that the underlying register gets assigned the inverse
 | ||
| 	 transformation.  This is used, for example, in the handling of
 | ||
| 	 non-local gotos.  */
 | ||
|       new_rtx = instantiate_new_reg (SET_DEST (set), &offset);
 | ||
|       if (new_rtx)
 | ||
| 	{
 | ||
| 	  start_sequence ();
 | ||
| 
 | ||
| 	  for_each_rtx (&SET_SRC (set), instantiate_virtual_regs_in_rtx, NULL);
 | ||
| 	  x = simplify_gen_binary (PLUS, GET_MODE (new_rtx), SET_SRC (set),
 | ||
| 				   GEN_INT (-offset));
 | ||
| 	  x = force_operand (x, new_rtx);
 | ||
| 	  if (x != new_rtx)
 | ||
| 	    emit_move_insn (new_rtx, x);
 | ||
| 
 | ||
| 	  seq = get_insns ();
 | ||
| 	  end_sequence ();
 | ||
| 
 | ||
| 	  emit_insn_before (seq, insn);
 | ||
| 	  delete_insn (insn);
 | ||
| 	  return;
 | ||
| 	}
 | ||
| 
 | ||
|       /* Handle a straight copy from a virtual register by generating a
 | ||
| 	 new add insn.  The difference between this and falling through
 | ||
| 	 to the generic case is avoiding a new pseudo and eliminating a
 | ||
| 	 move insn in the initial rtl stream.  */
 | ||
|       new_rtx = instantiate_new_reg (SET_SRC (set), &offset);
 | ||
|       if (new_rtx && offset != 0
 | ||
| 	  && REG_P (SET_DEST (set))
 | ||
| 	  && REGNO (SET_DEST (set)) > LAST_VIRTUAL_REGISTER)
 | ||
| 	{
 | ||
| 	  start_sequence ();
 | ||
| 
 | ||
| 	  x = expand_simple_binop (GET_MODE (SET_DEST (set)), PLUS,
 | ||
| 				   new_rtx, GEN_INT (offset), SET_DEST (set),
 | ||
| 				   1, OPTAB_LIB_WIDEN);
 | ||
| 	  if (x != SET_DEST (set))
 | ||
| 	    emit_move_insn (SET_DEST (set), x);
 | ||
| 
 | ||
| 	  seq = get_insns ();
 | ||
| 	  end_sequence ();
 | ||
| 
 | ||
| 	  emit_insn_before (seq, insn);
 | ||
| 	  delete_insn (insn);
 | ||
| 	  return;
 | ||
| 	}
 | ||
| 
 | ||
|       extract_insn (insn);
 | ||
|       insn_code = INSN_CODE (insn);
 | ||
| 
 | ||
|       /* Handle a plus involving a virtual register by determining if the
 | ||
| 	 operands remain valid if they're modified in place.  */
 | ||
|       if (GET_CODE (SET_SRC (set)) == PLUS
 | ||
| 	  && recog_data.n_operands >= 3
 | ||
| 	  && recog_data.operand_loc[1] == &XEXP (SET_SRC (set), 0)
 | ||
| 	  && recog_data.operand_loc[2] == &XEXP (SET_SRC (set), 1)
 | ||
| 	  && CONST_INT_P (recog_data.operand[2])
 | ||
| 	  && (new_rtx = instantiate_new_reg (recog_data.operand[1], &offset)))
 | ||
| 	{
 | ||
| 	  offset += INTVAL (recog_data.operand[2]);
 | ||
| 
 | ||
| 	  /* If the sum is zero, then replace with a plain move.  */
 | ||
| 	  if (offset == 0
 | ||
| 	      && REG_P (SET_DEST (set))
 | ||
| 	      && REGNO (SET_DEST (set)) > LAST_VIRTUAL_REGISTER)
 | ||
| 	    {
 | ||
| 	      start_sequence ();
 | ||
| 	      emit_move_insn (SET_DEST (set), new_rtx);
 | ||
| 	      seq = get_insns ();
 | ||
| 	      end_sequence ();
 | ||
| 
 | ||
| 	      emit_insn_before (seq, insn);
 | ||
| 	      delete_insn (insn);
 | ||
| 	      return;
 | ||
| 	    }
 | ||
| 
 | ||
| 	  x = gen_int_mode (offset, recog_data.operand_mode[2]);
 | ||
| 
 | ||
| 	  /* Using validate_change and apply_change_group here leaves
 | ||
| 	     recog_data in an invalid state.  Since we know exactly what
 | ||
| 	     we want to check, do those two by hand.  */
 | ||
| 	  if (safe_insn_predicate (insn_code, 1, new_rtx)
 | ||
| 	      && safe_insn_predicate (insn_code, 2, x))
 | ||
| 	    {
 | ||
| 	      *recog_data.operand_loc[1] = recog_data.operand[1] = new_rtx;
 | ||
| 	      *recog_data.operand_loc[2] = recog_data.operand[2] = x;
 | ||
| 	      any_change = true;
 | ||
| 
 | ||
| 	      /* Fall through into the regular operand fixup loop in
 | ||
| 		 order to take care of operands other than 1 and 2.  */
 | ||
| 	    }
 | ||
| 	}
 | ||
|     }
 | ||
|   else
 | ||
|     {
 | ||
|       extract_insn (insn);
 | ||
|       insn_code = INSN_CODE (insn);
 | ||
|     }
 | ||
| 
 | ||
|   /* In the general case, we expect virtual registers to appear only in
 | ||
|      operands, and then only as either bare registers or inside memories.  */
 | ||
|   for (i = 0; i < recog_data.n_operands; ++i)
 | ||
|     {
 | ||
|       x = recog_data.operand[i];
 | ||
|       switch (GET_CODE (x))
 | ||
| 	{
 | ||
| 	case MEM:
 | ||
| 	  {
 | ||
| 	    rtx addr = XEXP (x, 0);
 | ||
| 	    bool changed = false;
 | ||
| 
 | ||
| 	    for_each_rtx (&addr, instantiate_virtual_regs_in_rtx, &changed);
 | ||
| 	    if (!changed)
 | ||
| 	      continue;
 | ||
| 
 | ||
| 	    start_sequence ();
 | ||
| 	    x = replace_equiv_address (x, addr);
 | ||
| 	    /* It may happen that the address with the virtual reg
 | ||
| 	       was valid (e.g. based on the virtual stack reg, which might
 | ||
| 	       be acceptable to the predicates with all offsets), whereas
 | ||
| 	       the address now isn't anymore, for instance when the address
 | ||
| 	       is still offsetted, but the base reg isn't virtual-stack-reg
 | ||
| 	       anymore.  Below we would do a force_reg on the whole operand,
 | ||
| 	       but this insn might actually only accept memory.  Hence,
 | ||
| 	       before doing that last resort, try to reload the address into
 | ||
| 	       a register, so this operand stays a MEM.  */
 | ||
| 	    if (!safe_insn_predicate (insn_code, i, x))
 | ||
| 	      {
 | ||
| 		addr = force_reg (GET_MODE (addr), addr);
 | ||
| 		x = replace_equiv_address (x, addr);
 | ||
| 	      }
 | ||
| 	    seq = get_insns ();
 | ||
| 	    end_sequence ();
 | ||
| 	    if (seq)
 | ||
| 	      emit_insn_before (seq, insn);
 | ||
| 	  }
 | ||
| 	  break;
 | ||
| 
 | ||
| 	case REG:
 | ||
| 	  new_rtx = instantiate_new_reg (x, &offset);
 | ||
| 	  if (new_rtx == NULL)
 | ||
| 	    continue;
 | ||
| 	  if (offset == 0)
 | ||
| 	    x = new_rtx;
 | ||
| 	  else
 | ||
| 	    {
 | ||
| 	      start_sequence ();
 | ||
| 
 | ||
| 	      /* Careful, special mode predicates may have stuff in
 | ||
| 		 insn_data[insn_code].operand[i].mode that isn't useful
 | ||
| 		 to us for computing a new value.  */
 | ||
| 	      /* ??? Recognize address_operand and/or "p" constraints
 | ||
| 		 to see if (plus new offset) is a valid before we put
 | ||
| 		 this through expand_simple_binop.  */
 | ||
| 	      x = expand_simple_binop (GET_MODE (x), PLUS, new_rtx,
 | ||
| 				       GEN_INT (offset), NULL_RTX,
 | ||
| 				       1, OPTAB_LIB_WIDEN);
 | ||
| 	      seq = get_insns ();
 | ||
| 	      end_sequence ();
 | ||
| 	      emit_insn_before (seq, insn);
 | ||
| 	    }
 | ||
| 	  break;
 | ||
| 
 | ||
| 	case SUBREG:
 | ||
| 	  new_rtx = instantiate_new_reg (SUBREG_REG (x), &offset);
 | ||
| 	  if (new_rtx == NULL)
 | ||
| 	    continue;
 | ||
| 	  if (offset != 0)
 | ||
| 	    {
 | ||
| 	      start_sequence ();
 | ||
| 	      new_rtx = expand_simple_binop (GET_MODE (new_rtx), PLUS, new_rtx,
 | ||
| 					 GEN_INT (offset), NULL_RTX,
 | ||
| 					 1, OPTAB_LIB_WIDEN);
 | ||
| 	      seq = get_insns ();
 | ||
| 	      end_sequence ();
 | ||
| 	      emit_insn_before (seq, insn);
 | ||
| 	    }
 | ||
| 	  x = simplify_gen_subreg (recog_data.operand_mode[i], new_rtx,
 | ||
| 				   GET_MODE (new_rtx), SUBREG_BYTE (x));
 | ||
| 	  gcc_assert (x);
 | ||
| 	  break;
 | ||
| 
 | ||
| 	default:
 | ||
| 	  continue;
 | ||
| 	}
 | ||
| 
 | ||
|       /* At this point, X contains the new value for the operand.
 | ||
| 	 Validate the new value vs the insn predicate.  Note that
 | ||
| 	 asm insns will have insn_code -1 here.  */
 | ||
|       if (!safe_insn_predicate (insn_code, i, x))
 | ||
| 	{
 | ||
| 	  start_sequence ();
 | ||
| 	  if (REG_P (x))
 | ||
| 	    {
 | ||
| 	      gcc_assert (REGNO (x) <= LAST_VIRTUAL_REGISTER);
 | ||
| 	      x = copy_to_reg (x);
 | ||
| 	    }
 | ||
| 	  else
 | ||
| 	    x = force_reg (insn_data[insn_code].operand[i].mode, x);
 | ||
| 	  seq = get_insns ();
 | ||
| 	  end_sequence ();
 | ||
| 	  if (seq)
 | ||
| 	    emit_insn_before (seq, insn);
 | ||
| 	}
 | ||
| 
 | ||
|       *recog_data.operand_loc[i] = recog_data.operand[i] = x;
 | ||
|       any_change = true;
 | ||
|     }
 | ||
| 
 | ||
|   if (any_change)
 | ||
|     {
 | ||
|       /* Propagate operand changes into the duplicates.  */
 | ||
|       for (i = 0; i < recog_data.n_dups; ++i)
 | ||
| 	*recog_data.dup_loc[i]
 | ||
| 	  = copy_rtx (recog_data.operand[(unsigned)recog_data.dup_num[i]]);
 | ||
| 
 | ||
|       /* Force re-recognition of the instruction for validation.  */
 | ||
|       INSN_CODE (insn) = -1;
 | ||
|     }
 | ||
| 
 | ||
|   if (asm_noperands (PATTERN (insn)) >= 0)
 | ||
|     {
 | ||
|       if (!check_asm_operands (PATTERN (insn)))
 | ||
| 	{
 | ||
| 	  error_for_asm (insn, "impossible constraint in %<asm%>");
 | ||
| 	  delete_insn_and_edges (insn);
 | ||
| 	}
 | ||
|     }
 | ||
|   else
 | ||
|     {
 | ||
|       if (recog_memoized (insn) < 0)
 | ||
| 	fatal_insn_not_found (insn);
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /* Subroutine of instantiate_decls.  Given RTL representing a decl,
 | ||
|    do any instantiation required.  */
 | ||
| 
 | ||
| void
 | ||
| instantiate_decl_rtl (rtx x)
 | ||
| {
 | ||
|   rtx addr;
 | ||
| 
 | ||
|   if (x == 0)
 | ||
|     return;
 | ||
| 
 | ||
|   /* If this is a CONCAT, recurse for the pieces.  */
 | ||
|   if (GET_CODE (x) == CONCAT)
 | ||
|     {
 | ||
|       instantiate_decl_rtl (XEXP (x, 0));
 | ||
|       instantiate_decl_rtl (XEXP (x, 1));
 | ||
|       return;
 | ||
|     }
 | ||
| 
 | ||
|   /* If this is not a MEM, no need to do anything.  Similarly if the
 | ||
|      address is a constant or a register that is not a virtual register.  */
 | ||
|   if (!MEM_P (x))
 | ||
|     return;
 | ||
| 
 | ||
|   addr = XEXP (x, 0);
 | ||
|   if (CONSTANT_P (addr)
 | ||
|       || (REG_P (addr)
 | ||
| 	  && (REGNO (addr) < FIRST_VIRTUAL_REGISTER
 | ||
| 	      || REGNO (addr) > LAST_VIRTUAL_REGISTER)))
 | ||
|     return;
 | ||
| 
 | ||
|   for_each_rtx (&XEXP (x, 0), instantiate_virtual_regs_in_rtx, NULL);
 | ||
| }
 | ||
| 
 | ||
| /* Helper for instantiate_decls called via walk_tree: Process all decls
 | ||
|    in the given DECL_VALUE_EXPR.  */
 | ||
| 
 | ||
| static tree
 | ||
| instantiate_expr (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
 | ||
| {
 | ||
|   tree t = *tp;
 | ||
|   if (! EXPR_P (t))
 | ||
|     {
 | ||
|       *walk_subtrees = 0;
 | ||
|       if (DECL_P (t))
 | ||
| 	{
 | ||
| 	  if (DECL_RTL_SET_P (t))
 | ||
| 	    instantiate_decl_rtl (DECL_RTL (t));
 | ||
| 	  if (TREE_CODE (t) == PARM_DECL && DECL_NAMELESS (t)
 | ||
| 	      && DECL_INCOMING_RTL (t))
 | ||
| 	    instantiate_decl_rtl (DECL_INCOMING_RTL (t));
 | ||
| 	  if ((TREE_CODE (t) == VAR_DECL
 | ||
| 	       || TREE_CODE (t) == RESULT_DECL)
 | ||
| 	      && DECL_HAS_VALUE_EXPR_P (t))
 | ||
| 	    {
 | ||
| 	      tree v = DECL_VALUE_EXPR (t);
 | ||
| 	      walk_tree (&v, instantiate_expr, NULL, NULL);
 | ||
| 	    }
 | ||
| 	}
 | ||
|     }
 | ||
|   return NULL;
 | ||
| }
 | ||
| 
 | ||
| /* Subroutine of instantiate_decls: Process all decls in the given
 | ||
|    BLOCK node and all its subblocks.  */
 | ||
| 
 | ||
| static void
 | ||
| instantiate_decls_1 (tree let)
 | ||
| {
 | ||
|   tree t;
 | ||
| 
 | ||
|   for (t = BLOCK_VARS (let); t; t = DECL_CHAIN (t))
 | ||
|     {
 | ||
|       if (DECL_RTL_SET_P (t))
 | ||
| 	instantiate_decl_rtl (DECL_RTL (t));
 | ||
|       if (TREE_CODE (t) == VAR_DECL && DECL_HAS_VALUE_EXPR_P (t))
 | ||
| 	{
 | ||
| 	  tree v = DECL_VALUE_EXPR (t);
 | ||
| 	  walk_tree (&v, instantiate_expr, NULL, NULL);
 | ||
| 	}
 | ||
|     }
 | ||
| 
 | ||
|   /* Process all subblocks.  */
 | ||
|   for (t = BLOCK_SUBBLOCKS (let); t; t = BLOCK_CHAIN (t))
 | ||
|     instantiate_decls_1 (t);
 | ||
| }
 | ||
| 
 | ||
| /* Scan all decls in FNDECL (both variables and parameters) and instantiate
 | ||
|    all virtual registers in their DECL_RTL's.  */
 | ||
| 
 | ||
| static void
 | ||
| instantiate_decls (tree fndecl)
 | ||
| {
 | ||
|   tree decl;
 | ||
|   unsigned ix;
 | ||
| 
 | ||
|   /* Process all parameters of the function.  */
 | ||
|   for (decl = DECL_ARGUMENTS (fndecl); decl; decl = DECL_CHAIN (decl))
 | ||
|     {
 | ||
|       instantiate_decl_rtl (DECL_RTL (decl));
 | ||
|       instantiate_decl_rtl (DECL_INCOMING_RTL (decl));
 | ||
|       if (DECL_HAS_VALUE_EXPR_P (decl))
 | ||
| 	{
 | ||
| 	  tree v = DECL_VALUE_EXPR (decl);
 | ||
| 	  walk_tree (&v, instantiate_expr, NULL, NULL);
 | ||
| 	}
 | ||
|     }
 | ||
| 
 | ||
|   if ((decl = DECL_RESULT (fndecl))
 | ||
|       && TREE_CODE (decl) == RESULT_DECL)
 | ||
|     {
 | ||
|       if (DECL_RTL_SET_P (decl))
 | ||
| 	instantiate_decl_rtl (DECL_RTL (decl));
 | ||
|       if (DECL_HAS_VALUE_EXPR_P (decl))
 | ||
| 	{
 | ||
| 	  tree v = DECL_VALUE_EXPR (decl);
 | ||
| 	  walk_tree (&v, instantiate_expr, NULL, NULL);
 | ||
| 	}
 | ||
|     }
 | ||
| 
 | ||
|   /* Now process all variables defined in the function or its subblocks.  */
 | ||
|   instantiate_decls_1 (DECL_INITIAL (fndecl));
 | ||
| 
 | ||
|   FOR_EACH_LOCAL_DECL (cfun, ix, decl)
 | ||
|     if (DECL_RTL_SET_P (decl))
 | ||
|       instantiate_decl_rtl (DECL_RTL (decl));
 | ||
|   VEC_free (tree, gc, cfun->local_decls);
 | ||
| }
 | ||
| 
 | ||
| /* Pass through the INSNS of function FNDECL and convert virtual register
 | ||
|    references to hard register references.  */
 | ||
| 
 | ||
| static unsigned int
 | ||
| instantiate_virtual_regs (void)
 | ||
| {
 | ||
|   rtx insn;
 | ||
| 
 | ||
|   /* Compute the offsets to use for this function.  */
 | ||
|   in_arg_offset = FIRST_PARM_OFFSET (current_function_decl);
 | ||
|   var_offset = STARTING_FRAME_OFFSET;
 | ||
|   dynamic_offset = STACK_DYNAMIC_OFFSET (current_function_decl);
 | ||
|   out_arg_offset = STACK_POINTER_OFFSET;
 | ||
| #ifdef FRAME_POINTER_CFA_OFFSET
 | ||
|   cfa_offset = FRAME_POINTER_CFA_OFFSET (current_function_decl);
 | ||
| #else
 | ||
|   cfa_offset = ARG_POINTER_CFA_OFFSET (current_function_decl);
 | ||
| #endif
 | ||
| 
 | ||
|   /* Initialize recognition, indicating that volatile is OK.  */
 | ||
|   init_recog ();
 | ||
| 
 | ||
|   /* Scan through all the insns, instantiating every virtual register still
 | ||
|      present.  */
 | ||
|   for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
 | ||
|     if (INSN_P (insn))
 | ||
|       {
 | ||
| 	/* These patterns in the instruction stream can never be recognized.
 | ||
| 	   Fortunately, they shouldn't contain virtual registers either.  */
 | ||
| 	if (GET_CODE (PATTERN (insn)) == USE
 | ||
| 	    || GET_CODE (PATTERN (insn)) == CLOBBER
 | ||
| 	    || GET_CODE (PATTERN (insn)) == ADDR_VEC
 | ||
| 	    || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC
 | ||
| 	    || GET_CODE (PATTERN (insn)) == ASM_INPUT)
 | ||
| 	  continue;
 | ||
| 	else if (DEBUG_INSN_P (insn))
 | ||
| 	  for_each_rtx (&INSN_VAR_LOCATION (insn),
 | ||
| 			instantiate_virtual_regs_in_rtx, NULL);
 | ||
| 	else
 | ||
| 	  instantiate_virtual_regs_in_insn (insn);
 | ||
| 
 | ||
| 	if (INSN_DELETED_P (insn))
 | ||
| 	  continue;
 | ||
| 
 | ||
| 	for_each_rtx (®_NOTES (insn), instantiate_virtual_regs_in_rtx, NULL);
 | ||
| 
 | ||
| 	/* Instantiate any virtual registers in CALL_INSN_FUNCTION_USAGE.  */
 | ||
| 	if (CALL_P (insn))
 | ||
| 	  for_each_rtx (&CALL_INSN_FUNCTION_USAGE (insn),
 | ||
| 			instantiate_virtual_regs_in_rtx, NULL);
 | ||
|       }
 | ||
| 
 | ||
|   /* Instantiate the virtual registers in the DECLs for debugging purposes.  */
 | ||
|   instantiate_decls (current_function_decl);
 | ||
| 
 | ||
|   targetm.instantiate_decls ();
 | ||
| 
 | ||
|   /* Indicate that, from now on, assign_stack_local should use
 | ||
|      frame_pointer_rtx.  */
 | ||
|   virtuals_instantiated = 1;
 | ||
| 
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| struct rtl_opt_pass pass_instantiate_virtual_regs =
 | ||
| {
 | ||
|  {
 | ||
|   RTL_PASS,
 | ||
|   "vregs",                              /* name */
 | ||
|   NULL,                                 /* gate */
 | ||
|   instantiate_virtual_regs,             /* execute */
 | ||
|   NULL,                                 /* sub */
 | ||
|   NULL,                                 /* next */
 | ||
|   0,                                    /* static_pass_number */
 | ||
|   TV_NONE,                              /* tv_id */
 | ||
|   0,                                    /* properties_required */
 | ||
|   0,                                    /* properties_provided */
 | ||
|   0,                                    /* properties_destroyed */
 | ||
|   0,                                    /* todo_flags_start */
 | ||
|   0                                     /* todo_flags_finish */
 | ||
|  }
 | ||
| };
 | ||
| 
 | ||
| 
 | ||
| /* Return 1 if EXP is an aggregate type (or a value with aggregate type).
 | ||
|    This means a type for which function calls must pass an address to the
 | ||
|    function or get an address back from the function.
 | ||
|    EXP may be a type node or an expression (whose type is tested).  */
 | ||
| 
 | ||
| int
 | ||
| aggregate_value_p (const_tree exp, const_tree fntype)
 | ||
| {
 | ||
|   const_tree type = (TYPE_P (exp)) ? exp : TREE_TYPE (exp);
 | ||
|   int i, regno, nregs;
 | ||
|   rtx reg;
 | ||
| 
 | ||
|   if (fntype)
 | ||
|     switch (TREE_CODE (fntype))
 | ||
|       {
 | ||
|       case CALL_EXPR:
 | ||
| 	{
 | ||
| 	  tree fndecl = get_callee_fndecl (fntype);
 | ||
| 	  fntype = (fndecl
 | ||
| 		    ? TREE_TYPE (fndecl)
 | ||
| 		    : TREE_TYPE (TREE_TYPE (CALL_EXPR_FN (fntype))));
 | ||
| 	}
 | ||
| 	break;
 | ||
|       case FUNCTION_DECL:
 | ||
| 	fntype = TREE_TYPE (fntype);
 | ||
| 	break;
 | ||
|       case FUNCTION_TYPE:
 | ||
|       case METHOD_TYPE:
 | ||
|         break;
 | ||
|       case IDENTIFIER_NODE:
 | ||
| 	fntype = NULL_TREE;
 | ||
| 	break;
 | ||
|       default:
 | ||
| 	/* We don't expect other tree types here.  */
 | ||
| 	gcc_unreachable ();
 | ||
|       }
 | ||
| 
 | ||
|   if (VOID_TYPE_P (type))
 | ||
|     return 0;
 | ||
| 
 | ||
|   /* If a record should be passed the same as its first (and only) member
 | ||
|      don't pass it as an aggregate.  */
 | ||
|   if (TREE_CODE (type) == RECORD_TYPE && TYPE_TRANSPARENT_AGGR (type))
 | ||
|     return aggregate_value_p (first_field (type), fntype);
 | ||
| 
 | ||
|   /* If the front end has decided that this needs to be passed by
 | ||
|      reference, do so.  */
 | ||
|   if ((TREE_CODE (exp) == PARM_DECL || TREE_CODE (exp) == RESULT_DECL)
 | ||
|       && DECL_BY_REFERENCE (exp))
 | ||
|     return 1;
 | ||
| 
 | ||
|   /* Function types that are TREE_ADDRESSABLE force return in memory.  */
 | ||
|   if (fntype && TREE_ADDRESSABLE (fntype))
 | ||
|     return 1;
 | ||
| 
 | ||
|   /* Types that are TREE_ADDRESSABLE must be constructed in memory,
 | ||
|      and thus can't be returned in registers.  */
 | ||
|   if (TREE_ADDRESSABLE (type))
 | ||
|     return 1;
 | ||
| 
 | ||
|   if (flag_pcc_struct_return && AGGREGATE_TYPE_P (type))
 | ||
|     return 1;
 | ||
| 
 | ||
|   if (targetm.calls.return_in_memory (type, fntype))
 | ||
|     return 1;
 | ||
| 
 | ||
|   /* Make sure we have suitable call-clobbered regs to return
 | ||
|      the value in; if not, we must return it in memory.  */
 | ||
|   reg = hard_function_value (type, 0, fntype, 0);
 | ||
| 
 | ||
|   /* If we have something other than a REG (e.g. a PARALLEL), then assume
 | ||
|      it is OK.  */
 | ||
|   if (!REG_P (reg))
 | ||
|     return 0;
 | ||
| 
 | ||
|   regno = REGNO (reg);
 | ||
|   nregs = hard_regno_nregs[regno][TYPE_MODE (type)];
 | ||
|   for (i = 0; i < nregs; i++)
 | ||
|     if (! call_used_regs[regno + i])
 | ||
|       return 1;
 | ||
| 
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| /* Return true if we should assign DECL a pseudo register; false if it
 | ||
|    should live on the local stack.  */
 | ||
| 
 | ||
| bool
 | ||
| use_register_for_decl (const_tree decl)
 | ||
| {
 | ||
|   if (!targetm.calls.allocate_stack_slots_for_args())
 | ||
|     return true;
 | ||
| 
 | ||
|   /* Honor volatile.  */
 | ||
|   if (TREE_SIDE_EFFECTS (decl))
 | ||
|     return false;
 | ||
| 
 | ||
|   /* Honor addressability.  */
 | ||
|   if (TREE_ADDRESSABLE (decl))
 | ||
|     return false;
 | ||
| 
 | ||
|   /* Only register-like things go in registers.  */
 | ||
|   if (DECL_MODE (decl) == BLKmode)
 | ||
|     return false;
 | ||
| 
 | ||
|   /* If -ffloat-store specified, don't put explicit float variables
 | ||
|      into registers.  */
 | ||
|   /* ??? This should be checked after DECL_ARTIFICIAL, but tree-ssa
 | ||
|      propagates values across these stores, and it probably shouldn't.  */
 | ||
|   if (flag_float_store && FLOAT_TYPE_P (TREE_TYPE (decl)))
 | ||
|     return false;
 | ||
| 
 | ||
|   /* If we're not interested in tracking debugging information for
 | ||
|      this decl, then we can certainly put it in a register.  */
 | ||
|   if (DECL_IGNORED_P (decl))
 | ||
|     return true;
 | ||
| 
 | ||
|   if (optimize)
 | ||
|     return true;
 | ||
| 
 | ||
|   if (!DECL_REGISTER (decl))
 | ||
|     return false;
 | ||
| 
 | ||
|   switch (TREE_CODE (TREE_TYPE (decl)))
 | ||
|     {
 | ||
|     case RECORD_TYPE:
 | ||
|     case UNION_TYPE:
 | ||
|     case QUAL_UNION_TYPE:
 | ||
|       /* When not optimizing, disregard register keyword for variables with
 | ||
| 	 types containing methods, otherwise the methods won't be callable
 | ||
| 	 from the debugger.  */
 | ||
|       if (TYPE_METHODS (TREE_TYPE (decl)))
 | ||
| 	return false;
 | ||
|       break;
 | ||
|     default:
 | ||
|       break;
 | ||
|     }
 | ||
| 
 | ||
|   return true;
 | ||
| }
 | ||
| 
 | ||
| /* Return true if TYPE should be passed by invisible reference.  */
 | ||
| 
 | ||
| bool
 | ||
| pass_by_reference (CUMULATIVE_ARGS *ca, enum machine_mode mode,
 | ||
| 		   tree type, bool named_arg)
 | ||
| {
 | ||
|   if (type)
 | ||
|     {
 | ||
|       /* If this type contains non-trivial constructors, then it is
 | ||
| 	 forbidden for the middle-end to create any new copies.  */
 | ||
|       if (TREE_ADDRESSABLE (type))
 | ||
| 	return true;
 | ||
| 
 | ||
|       /* GCC post 3.4 passes *all* variable sized types by reference.  */
 | ||
|       if (!TYPE_SIZE (type) || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
 | ||
| 	return true;
 | ||
| 
 | ||
|       /* If a record type should be passed the same as its first (and only)
 | ||
| 	 member, use the type and mode of that member.  */
 | ||
|       if (TREE_CODE (type) == RECORD_TYPE && TYPE_TRANSPARENT_AGGR (type))
 | ||
| 	{
 | ||
| 	  type = TREE_TYPE (first_field (type));
 | ||
| 	  mode = TYPE_MODE (type);
 | ||
| 	}
 | ||
|     }
 | ||
| 
 | ||
|   return targetm.calls.pass_by_reference (pack_cumulative_args (ca), mode,
 | ||
| 					  type, named_arg);
 | ||
| }
 | ||
| 
 | ||
| /* Return true if TYPE, which is passed by reference, should be callee
 | ||
|    copied instead of caller copied.  */
 | ||
| 
 | ||
| bool
 | ||
| reference_callee_copied (CUMULATIVE_ARGS *ca, enum machine_mode mode,
 | ||
| 			 tree type, bool named_arg)
 | ||
| {
 | ||
|   if (type && TREE_ADDRESSABLE (type))
 | ||
|     return false;
 | ||
|   return targetm.calls.callee_copies (pack_cumulative_args (ca), mode, type,
 | ||
| 				      named_arg);
 | ||
| }
 | ||
| 
 | ||
| /* Structures to communicate between the subroutines of assign_parms.
 | ||
|    The first holds data persistent across all parameters, the second
 | ||
|    is cleared out for each parameter.  */
 | ||
| 
 | ||
| struct assign_parm_data_all
 | ||
| {
 | ||
|   /* When INIT_CUMULATIVE_ARGS gets revamped, allocating CUMULATIVE_ARGS
 | ||
|      should become a job of the target or otherwise encapsulated.  */
 | ||
|   CUMULATIVE_ARGS args_so_far_v;
 | ||
|   cumulative_args_t args_so_far;
 | ||
|   struct args_size stack_args_size;
 | ||
|   tree function_result_decl;
 | ||
|   tree orig_fnargs;
 | ||
|   rtx first_conversion_insn;
 | ||
|   rtx last_conversion_insn;
 | ||
|   HOST_WIDE_INT pretend_args_size;
 | ||
|   HOST_WIDE_INT extra_pretend_bytes;
 | ||
|   int reg_parm_stack_space;
 | ||
| };
 | ||
| 
 | ||
| struct assign_parm_data_one
 | ||
| {
 | ||
|   tree nominal_type;
 | ||
|   tree passed_type;
 | ||
|   rtx entry_parm;
 | ||
|   rtx stack_parm;
 | ||
|   enum machine_mode nominal_mode;
 | ||
|   enum machine_mode passed_mode;
 | ||
|   enum machine_mode promoted_mode;
 | ||
|   struct locate_and_pad_arg_data locate;
 | ||
|   int partial;
 | ||
|   BOOL_BITFIELD named_arg : 1;
 | ||
|   BOOL_BITFIELD passed_pointer : 1;
 | ||
|   BOOL_BITFIELD on_stack : 1;
 | ||
|   BOOL_BITFIELD loaded_in_reg : 1;
 | ||
| };
 | ||
| 
 | ||
| /* A subroutine of assign_parms.  Initialize ALL.  */
 | ||
| 
 | ||
| static void
 | ||
| assign_parms_initialize_all (struct assign_parm_data_all *all)
 | ||
| {
 | ||
|   tree fntype ATTRIBUTE_UNUSED;
 | ||
| 
 | ||
|   memset (all, 0, sizeof (*all));
 | ||
| 
 | ||
|   fntype = TREE_TYPE (current_function_decl);
 | ||
| 
 | ||
| #ifdef INIT_CUMULATIVE_INCOMING_ARGS
 | ||
|   INIT_CUMULATIVE_INCOMING_ARGS (all->args_so_far_v, fntype, NULL_RTX);
 | ||
| #else
 | ||
|   INIT_CUMULATIVE_ARGS (all->args_so_far_v, fntype, NULL_RTX,
 | ||
| 			current_function_decl, -1);
 | ||
| #endif
 | ||
|   all->args_so_far = pack_cumulative_args (&all->args_so_far_v);
 | ||
| 
 | ||
| #ifdef REG_PARM_STACK_SPACE
 | ||
|   all->reg_parm_stack_space = REG_PARM_STACK_SPACE (current_function_decl);
 | ||
| #endif
 | ||
| }
 | ||
| 
 | ||
| /* If ARGS contains entries with complex types, split the entry into two
 | ||
|    entries of the component type.  Return a new list of substitutions are
 | ||
|    needed, else the old list.  */
 | ||
| 
 | ||
| static void
 | ||
| split_complex_args (VEC(tree, heap) **args)
 | ||
| {
 | ||
|   unsigned i;
 | ||
|   tree p;
 | ||
| 
 | ||
|   FOR_EACH_VEC_ELT (tree, *args, i, p)
 | ||
|     {
 | ||
|       tree type = TREE_TYPE (p);
 | ||
|       if (TREE_CODE (type) == COMPLEX_TYPE
 | ||
| 	  && targetm.calls.split_complex_arg (type))
 | ||
| 	{
 | ||
| 	  tree decl;
 | ||
| 	  tree subtype = TREE_TYPE (type);
 | ||
| 	  bool addressable = TREE_ADDRESSABLE (p);
 | ||
| 
 | ||
| 	  /* Rewrite the PARM_DECL's type with its component.  */
 | ||
| 	  p = copy_node (p);
 | ||
| 	  TREE_TYPE (p) = subtype;
 | ||
| 	  DECL_ARG_TYPE (p) = TREE_TYPE (DECL_ARG_TYPE (p));
 | ||
| 	  DECL_MODE (p) = VOIDmode;
 | ||
| 	  DECL_SIZE (p) = NULL;
 | ||
| 	  DECL_SIZE_UNIT (p) = NULL;
 | ||
| 	  /* If this arg must go in memory, put it in a pseudo here.
 | ||
| 	     We can't allow it to go in memory as per normal parms,
 | ||
| 	     because the usual place might not have the imag part
 | ||
| 	     adjacent to the real part.  */
 | ||
| 	  DECL_ARTIFICIAL (p) = addressable;
 | ||
| 	  DECL_IGNORED_P (p) = addressable;
 | ||
| 	  TREE_ADDRESSABLE (p) = 0;
 | ||
| 	  layout_decl (p, 0);
 | ||
| 	  VEC_replace (tree, *args, i, p);
 | ||
| 
 | ||
| 	  /* Build a second synthetic decl.  */
 | ||
| 	  decl = build_decl (EXPR_LOCATION (p),
 | ||
| 			     PARM_DECL, NULL_TREE, subtype);
 | ||
| 	  DECL_ARG_TYPE (decl) = DECL_ARG_TYPE (p);
 | ||
| 	  DECL_ARTIFICIAL (decl) = addressable;
 | ||
| 	  DECL_IGNORED_P (decl) = addressable;
 | ||
| 	  layout_decl (decl, 0);
 | ||
| 	  VEC_safe_insert (tree, heap, *args, ++i, decl);
 | ||
| 	}
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /* A subroutine of assign_parms.  Adjust the parameter list to incorporate
 | ||
|    the hidden struct return argument, and (abi willing) complex args.
 | ||
|    Return the new parameter list.  */
 | ||
| 
 | ||
| static VEC(tree, heap) *
 | ||
| assign_parms_augmented_arg_list (struct assign_parm_data_all *all)
 | ||
| {
 | ||
|   tree fndecl = current_function_decl;
 | ||
|   tree fntype = TREE_TYPE (fndecl);
 | ||
|   VEC(tree, heap) *fnargs = NULL;
 | ||
|   tree arg;
 | ||
| 
 | ||
|   for (arg = DECL_ARGUMENTS (fndecl); arg; arg = DECL_CHAIN (arg))
 | ||
|     VEC_safe_push (tree, heap, fnargs, arg);
 | ||
| 
 | ||
|   all->orig_fnargs = DECL_ARGUMENTS (fndecl);
 | ||
| 
 | ||
|   /* If struct value address is treated as the first argument, make it so.  */
 | ||
|   if (aggregate_value_p (DECL_RESULT (fndecl), fndecl)
 | ||
|       && ! cfun->returns_pcc_struct
 | ||
|       && targetm.calls.struct_value_rtx (TREE_TYPE (fndecl), 1) == 0)
 | ||
|     {
 | ||
|       tree type = build_pointer_type (TREE_TYPE (fntype));
 | ||
|       tree decl;
 | ||
| 
 | ||
|       decl = build_decl (DECL_SOURCE_LOCATION (fndecl),
 | ||
| 			 PARM_DECL, get_identifier (".result_ptr"), type);
 | ||
|       DECL_ARG_TYPE (decl) = type;
 | ||
|       DECL_ARTIFICIAL (decl) = 1;
 | ||
|       DECL_NAMELESS (decl) = 1;
 | ||
|       TREE_CONSTANT (decl) = 1;
 | ||
| 
 | ||
|       DECL_CHAIN (decl) = all->orig_fnargs;
 | ||
|       all->orig_fnargs = decl;
 | ||
|       VEC_safe_insert (tree, heap, fnargs, 0, decl);
 | ||
| 
 | ||
|       all->function_result_decl = decl;
 | ||
|     }
 | ||
| 
 | ||
|   /* If the target wants to split complex arguments into scalars, do so.  */
 | ||
|   if (targetm.calls.split_complex_arg)
 | ||
|     split_complex_args (&fnargs);
 | ||
| 
 | ||
|   return fnargs;
 | ||
| }
 | ||
| 
 | ||
| /* A subroutine of assign_parms.  Examine PARM and pull out type and mode
 | ||
|    data for the parameter.  Incorporate ABI specifics such as pass-by-
 | ||
|    reference and type promotion.  */
 | ||
| 
 | ||
| static void
 | ||
| assign_parm_find_data_types (struct assign_parm_data_all *all, tree parm,
 | ||
| 			     struct assign_parm_data_one *data)
 | ||
| {
 | ||
|   tree nominal_type, passed_type;
 | ||
|   enum machine_mode nominal_mode, passed_mode, promoted_mode;
 | ||
|   int unsignedp;
 | ||
| 
 | ||
|   memset (data, 0, sizeof (*data));
 | ||
| 
 | ||
|   /* NAMED_ARG is a misnomer.  We really mean 'non-variadic'. */
 | ||
|   if (!cfun->stdarg)
 | ||
|     data->named_arg = 1;  /* No variadic parms.  */
 | ||
|   else if (DECL_CHAIN (parm))
 | ||
|     data->named_arg = 1;  /* Not the last non-variadic parm. */
 | ||
|   else if (targetm.calls.strict_argument_naming (all->args_so_far))
 | ||
|     data->named_arg = 1;  /* Only variadic ones are unnamed.  */
 | ||
|   else
 | ||
|     data->named_arg = 0;  /* Treat as variadic.  */
 | ||
| 
 | ||
|   nominal_type = TREE_TYPE (parm);
 | ||
|   passed_type = DECL_ARG_TYPE (parm);
 | ||
| 
 | ||
|   /* Look out for errors propagating this far.  Also, if the parameter's
 | ||
|      type is void then its value doesn't matter.  */
 | ||
|   if (TREE_TYPE (parm) == error_mark_node
 | ||
|       /* This can happen after weird syntax errors
 | ||
| 	 or if an enum type is defined among the parms.  */
 | ||
|       || TREE_CODE (parm) != PARM_DECL
 | ||
|       || passed_type == NULL
 | ||
|       || VOID_TYPE_P (nominal_type))
 | ||
|     {
 | ||
|       nominal_type = passed_type = void_type_node;
 | ||
|       nominal_mode = passed_mode = promoted_mode = VOIDmode;
 | ||
|       goto egress;
 | ||
|     }
 | ||
| 
 | ||
|   /* Find mode of arg as it is passed, and mode of arg as it should be
 | ||
|      during execution of this function.  */
 | ||
|   passed_mode = TYPE_MODE (passed_type);
 | ||
|   nominal_mode = TYPE_MODE (nominal_type);
 | ||
| 
 | ||
|   /* If the parm is to be passed as a transparent union or record, use the
 | ||
|      type of the first field for the tests below.  We have already verified
 | ||
|      that the modes are the same.  */
 | ||
|   if ((TREE_CODE (passed_type) == UNION_TYPE
 | ||
|        || TREE_CODE (passed_type) == RECORD_TYPE)
 | ||
|       && TYPE_TRANSPARENT_AGGR (passed_type))
 | ||
|     passed_type = TREE_TYPE (first_field (passed_type));
 | ||
| 
 | ||
|   /* See if this arg was passed by invisible reference.  */
 | ||
|   if (pass_by_reference (&all->args_so_far_v, passed_mode,
 | ||
| 			 passed_type, data->named_arg))
 | ||
|     {
 | ||
|       passed_type = nominal_type = build_pointer_type (passed_type);
 | ||
|       data->passed_pointer = true;
 | ||
|       passed_mode = nominal_mode = Pmode;
 | ||
|     }
 | ||
| 
 | ||
|   /* Find mode as it is passed by the ABI.  */
 | ||
|   unsignedp = TYPE_UNSIGNED (passed_type);
 | ||
|   promoted_mode = promote_function_mode (passed_type, passed_mode, &unsignedp,
 | ||
| 				         TREE_TYPE (current_function_decl), 0);
 | ||
| 
 | ||
|  egress:
 | ||
|   data->nominal_type = nominal_type;
 | ||
|   data->passed_type = passed_type;
 | ||
|   data->nominal_mode = nominal_mode;
 | ||
|   data->passed_mode = passed_mode;
 | ||
|   data->promoted_mode = promoted_mode;
 | ||
| }
 | ||
| 
 | ||
| /* A subroutine of assign_parms.  Invoke setup_incoming_varargs.  */
 | ||
| 
 | ||
| static void
 | ||
| assign_parms_setup_varargs (struct assign_parm_data_all *all,
 | ||
| 			    struct assign_parm_data_one *data, bool no_rtl)
 | ||
| {
 | ||
|   int varargs_pretend_bytes = 0;
 | ||
| 
 | ||
|   targetm.calls.setup_incoming_varargs (all->args_so_far,
 | ||
| 					data->promoted_mode,
 | ||
| 					data->passed_type,
 | ||
| 					&varargs_pretend_bytes, no_rtl);
 | ||
| 
 | ||
|   /* If the back-end has requested extra stack space, record how much is
 | ||
|      needed.  Do not change pretend_args_size otherwise since it may be
 | ||
|      nonzero from an earlier partial argument.  */
 | ||
|   if (varargs_pretend_bytes > 0)
 | ||
|     all->pretend_args_size = varargs_pretend_bytes;
 | ||
| }
 | ||
| 
 | ||
| /* A subroutine of assign_parms.  Set DATA->ENTRY_PARM corresponding to
 | ||
|    the incoming location of the current parameter.  */
 | ||
| 
 | ||
| static void
 | ||
| assign_parm_find_entry_rtl (struct assign_parm_data_all *all,
 | ||
| 			    struct assign_parm_data_one *data)
 | ||
| {
 | ||
|   HOST_WIDE_INT pretend_bytes = 0;
 | ||
|   rtx entry_parm;
 | ||
|   bool in_regs;
 | ||
| 
 | ||
|   if (data->promoted_mode == VOIDmode)
 | ||
|     {
 | ||
|       data->entry_parm = data->stack_parm = const0_rtx;
 | ||
|       return;
 | ||
|     }
 | ||
| 
 | ||
|   entry_parm = targetm.calls.function_incoming_arg (all->args_so_far,
 | ||
| 						    data->promoted_mode,
 | ||
| 						    data->passed_type,
 | ||
| 						    data->named_arg);
 | ||
| 
 | ||
|   if (entry_parm == 0)
 | ||
|     data->promoted_mode = data->passed_mode;
 | ||
| 
 | ||
|   /* Determine parm's home in the stack, in case it arrives in the stack
 | ||
|      or we should pretend it did.  Compute the stack position and rtx where
 | ||
|      the argument arrives and its size.
 | ||
| 
 | ||
|      There is one complexity here:  If this was a parameter that would
 | ||
|      have been passed in registers, but wasn't only because it is
 | ||
|      __builtin_va_alist, we want locate_and_pad_parm to treat it as if
 | ||
|      it came in a register so that REG_PARM_STACK_SPACE isn't skipped.
 | ||
|      In this case, we call FUNCTION_ARG with NAMED set to 1 instead of 0
 | ||
|      as it was the previous time.  */
 | ||
|   in_regs = entry_parm != 0;
 | ||
| #ifdef STACK_PARMS_IN_REG_PARM_AREA
 | ||
|   in_regs = true;
 | ||
| #endif
 | ||
|   if (!in_regs && !data->named_arg)
 | ||
|     {
 | ||
|       if (targetm.calls.pretend_outgoing_varargs_named (all->args_so_far))
 | ||
| 	{
 | ||
| 	  rtx tem;
 | ||
| 	  tem = targetm.calls.function_incoming_arg (all->args_so_far,
 | ||
| 						     data->promoted_mode,
 | ||
| 						     data->passed_type, true);
 | ||
| 	  in_regs = tem != NULL;
 | ||
| 	}
 | ||
|     }
 | ||
| 
 | ||
|   /* If this parameter was passed both in registers and in the stack, use
 | ||
|      the copy on the stack.  */
 | ||
|   if (targetm.calls.must_pass_in_stack (data->promoted_mode,
 | ||
| 					data->passed_type))
 | ||
|     entry_parm = 0;
 | ||
| 
 | ||
|   if (entry_parm)
 | ||
|     {
 | ||
|       int partial;
 | ||
| 
 | ||
|       partial = targetm.calls.arg_partial_bytes (all->args_so_far,
 | ||
| 						 data->promoted_mode,
 | ||
| 						 data->passed_type,
 | ||
| 						 data->named_arg);
 | ||
|       data->partial = partial;
 | ||
| 
 | ||
|       /* The caller might already have allocated stack space for the
 | ||
| 	 register parameters.  */
 | ||
|       if (partial != 0 && all->reg_parm_stack_space == 0)
 | ||
| 	{
 | ||
| 	  /* Part of this argument is passed in registers and part
 | ||
| 	     is passed on the stack.  Ask the prologue code to extend
 | ||
| 	     the stack part so that we can recreate the full value.
 | ||
| 
 | ||
| 	     PRETEND_BYTES is the size of the registers we need to store.
 | ||
| 	     CURRENT_FUNCTION_PRETEND_ARGS_SIZE is the amount of extra
 | ||
| 	     stack space that the prologue should allocate.
 | ||
| 
 | ||
| 	     Internally, gcc assumes that the argument pointer is aligned
 | ||
| 	     to STACK_BOUNDARY bits.  This is used both for alignment
 | ||
| 	     optimizations (see init_emit) and to locate arguments that are
 | ||
| 	     aligned to more than PARM_BOUNDARY bits.  We must preserve this
 | ||
| 	     invariant by rounding CURRENT_FUNCTION_PRETEND_ARGS_SIZE up to
 | ||
| 	     a stack boundary.  */
 | ||
| 
 | ||
| 	  /* We assume at most one partial arg, and it must be the first
 | ||
| 	     argument on the stack.  */
 | ||
| 	  gcc_assert (!all->extra_pretend_bytes && !all->pretend_args_size);
 | ||
| 
 | ||
| 	  pretend_bytes = partial;
 | ||
| 	  all->pretend_args_size = CEIL_ROUND (pretend_bytes, STACK_BYTES);
 | ||
| 
 | ||
| 	  /* We want to align relative to the actual stack pointer, so
 | ||
| 	     don't include this in the stack size until later.  */
 | ||
| 	  all->extra_pretend_bytes = all->pretend_args_size;
 | ||
| 	}
 | ||
|     }
 | ||
| 
 | ||
|   locate_and_pad_parm (data->promoted_mode, data->passed_type, in_regs,
 | ||
| 		       entry_parm ? data->partial : 0, current_function_decl,
 | ||
| 		       &all->stack_args_size, &data->locate);
 | ||
| 
 | ||
|   /* Update parm_stack_boundary if this parameter is passed in the
 | ||
|      stack.  */
 | ||
|   if (!in_regs && crtl->parm_stack_boundary < data->locate.boundary)
 | ||
|     crtl->parm_stack_boundary = data->locate.boundary;
 | ||
| 
 | ||
|   /* Adjust offsets to include the pretend args.  */
 | ||
|   pretend_bytes = all->extra_pretend_bytes - pretend_bytes;
 | ||
|   data->locate.slot_offset.constant += pretend_bytes;
 | ||
|   data->locate.offset.constant += pretend_bytes;
 | ||
| 
 | ||
|   data->entry_parm = entry_parm;
 | ||
| }
 | ||
| 
 | ||
| /* A subroutine of assign_parms.  If there is actually space on the stack
 | ||
|    for this parm, count it in stack_args_size and return true.  */
 | ||
| 
 | ||
| static bool
 | ||
| assign_parm_is_stack_parm (struct assign_parm_data_all *all,
 | ||
| 			   struct assign_parm_data_one *data)
 | ||
| {
 | ||
|   /* Trivially true if we've no incoming register.  */
 | ||
|   if (data->entry_parm == NULL)
 | ||
|     ;
 | ||
|   /* Also true if we're partially in registers and partially not,
 | ||
|      since we've arranged to drop the entire argument on the stack.  */
 | ||
|   else if (data->partial != 0)
 | ||
|     ;
 | ||
|   /* Also true if the target says that it's passed in both registers
 | ||
|      and on the stack.  */
 | ||
|   else if (GET_CODE (data->entry_parm) == PARALLEL
 | ||
| 	   && XEXP (XVECEXP (data->entry_parm, 0, 0), 0) == NULL_RTX)
 | ||
|     ;
 | ||
|   /* Also true if the target says that there's stack allocated for
 | ||
|      all register parameters.  */
 | ||
|   else if (all->reg_parm_stack_space > 0)
 | ||
|     ;
 | ||
|   /* Otherwise, no, this parameter has no ABI defined stack slot.  */
 | ||
|   else
 | ||
|     return false;
 | ||
| 
 | ||
|   all->stack_args_size.constant += data->locate.size.constant;
 | ||
|   if (data->locate.size.var)
 | ||
|     ADD_PARM_SIZE (all->stack_args_size, data->locate.size.var);
 | ||
| 
 | ||
|   return true;
 | ||
| }
 | ||
| 
 | ||
| /* A subroutine of assign_parms.  Given that this parameter is allocated
 | ||
|    stack space by the ABI, find it.  */
 | ||
| 
 | ||
| static void
 | ||
| assign_parm_find_stack_rtl (tree parm, struct assign_parm_data_one *data)
 | ||
| {
 | ||
|   rtx offset_rtx, stack_parm;
 | ||
|   unsigned int align, boundary;
 | ||
| 
 | ||
|   /* If we're passing this arg using a reg, make its stack home the
 | ||
|      aligned stack slot.  */
 | ||
|   if (data->entry_parm)
 | ||
|     offset_rtx = ARGS_SIZE_RTX (data->locate.slot_offset);
 | ||
|   else
 | ||
|     offset_rtx = ARGS_SIZE_RTX (data->locate.offset);
 | ||
| 
 | ||
|   stack_parm = crtl->args.internal_arg_pointer;
 | ||
|   if (offset_rtx != const0_rtx)
 | ||
|     stack_parm = gen_rtx_PLUS (Pmode, stack_parm, offset_rtx);
 | ||
|   stack_parm = gen_rtx_MEM (data->promoted_mode, stack_parm);
 | ||
| 
 | ||
|   if (!data->passed_pointer)
 | ||
|     {
 | ||
|       set_mem_attributes (stack_parm, parm, 1);
 | ||
|       /* set_mem_attributes could set MEM_SIZE to the passed mode's size,
 | ||
| 	 while promoted mode's size is needed.  */
 | ||
|       if (data->promoted_mode != BLKmode
 | ||
| 	  && data->promoted_mode != DECL_MODE (parm))
 | ||
| 	{
 | ||
| 	  set_mem_size (stack_parm, GET_MODE_SIZE (data->promoted_mode));
 | ||
| 	  if (MEM_EXPR (stack_parm) && MEM_OFFSET_KNOWN_P (stack_parm))
 | ||
| 	    {
 | ||
| 	      int offset = subreg_lowpart_offset (DECL_MODE (parm),
 | ||
| 						  data->promoted_mode);
 | ||
| 	      if (offset)
 | ||
| 		set_mem_offset (stack_parm, MEM_OFFSET (stack_parm) - offset);
 | ||
| 	    }
 | ||
| 	}
 | ||
|     }
 | ||
| 
 | ||
|   boundary = data->locate.boundary;
 | ||
|   align = BITS_PER_UNIT;
 | ||
| 
 | ||
|   /* If we're padding upward, we know that the alignment of the slot
 | ||
|      is TARGET_FUNCTION_ARG_BOUNDARY.  If we're using slot_offset, we're
 | ||
|      intentionally forcing upward padding.  Otherwise we have to come
 | ||
|      up with a guess at the alignment based on OFFSET_RTX.  */
 | ||
|   if (data->locate.where_pad != downward || data->entry_parm)
 | ||
|     align = boundary;
 | ||
|   else if (CONST_INT_P (offset_rtx))
 | ||
|     {
 | ||
|       align = INTVAL (offset_rtx) * BITS_PER_UNIT | boundary;
 | ||
|       align = align & -align;
 | ||
|     }
 | ||
|   set_mem_align (stack_parm, align);
 | ||
| 
 | ||
|   if (data->entry_parm)
 | ||
|     set_reg_attrs_for_parm (data->entry_parm, stack_parm);
 | ||
| 
 | ||
|   data->stack_parm = stack_parm;
 | ||
| }
 | ||
| 
 | ||
| /* A subroutine of assign_parms.  Adjust DATA->ENTRY_RTL such that it's
 | ||
|    always valid and contiguous.  */
 | ||
| 
 | ||
| static void
 | ||
| assign_parm_adjust_entry_rtl (struct assign_parm_data_one *data)
 | ||
| {
 | ||
|   rtx entry_parm = data->entry_parm;
 | ||
|   rtx stack_parm = data->stack_parm;
 | ||
| 
 | ||
|   /* If this parm was passed part in regs and part in memory, pretend it
 | ||
|      arrived entirely in memory by pushing the register-part onto the stack.
 | ||
|      In the special case of a DImode or DFmode that is split, we could put
 | ||
|      it together in a pseudoreg directly, but for now that's not worth
 | ||
|      bothering with.  */
 | ||
|   if (data->partial != 0)
 | ||
|     {
 | ||
|       /* Handle calls that pass values in multiple non-contiguous
 | ||
| 	 locations.  The Irix 6 ABI has examples of this.  */
 | ||
|       if (GET_CODE (entry_parm) == PARALLEL)
 | ||
| 	emit_group_store (validize_mem (stack_parm), entry_parm,
 | ||
| 			  data->passed_type,
 | ||
| 			  int_size_in_bytes (data->passed_type));
 | ||
|       else
 | ||
| 	{
 | ||
| 	  gcc_assert (data->partial % UNITS_PER_WORD == 0);
 | ||
| 	  move_block_from_reg (REGNO (entry_parm), validize_mem (stack_parm),
 | ||
| 			       data->partial / UNITS_PER_WORD);
 | ||
| 	}
 | ||
| 
 | ||
|       entry_parm = stack_parm;
 | ||
|     }
 | ||
| 
 | ||
|   /* If we didn't decide this parm came in a register, by default it came
 | ||
|      on the stack.  */
 | ||
|   else if (entry_parm == NULL)
 | ||
|     entry_parm = stack_parm;
 | ||
| 
 | ||
|   /* When an argument is passed in multiple locations, we can't make use
 | ||
|      of this information, but we can save some copying if the whole argument
 | ||
|      is passed in a single register.  */
 | ||
|   else if (GET_CODE (entry_parm) == PARALLEL
 | ||
| 	   && data->nominal_mode != BLKmode
 | ||
| 	   && data->passed_mode != BLKmode)
 | ||
|     {
 | ||
|       size_t i, len = XVECLEN (entry_parm, 0);
 | ||
| 
 | ||
|       for (i = 0; i < len; i++)
 | ||
| 	if (XEXP (XVECEXP (entry_parm, 0, i), 0) != NULL_RTX
 | ||
| 	    && REG_P (XEXP (XVECEXP (entry_parm, 0, i), 0))
 | ||
| 	    && (GET_MODE (XEXP (XVECEXP (entry_parm, 0, i), 0))
 | ||
| 		== data->passed_mode)
 | ||
| 	    && INTVAL (XEXP (XVECEXP (entry_parm, 0, i), 1)) == 0)
 | ||
| 	  {
 | ||
| 	    entry_parm = XEXP (XVECEXP (entry_parm, 0, i), 0);
 | ||
| 	    break;
 | ||
| 	  }
 | ||
|     }
 | ||
| 
 | ||
|   data->entry_parm = entry_parm;
 | ||
| }
 | ||
| 
 | ||
| /* A subroutine of assign_parms.  Reconstitute any values which were
 | ||
|    passed in multiple registers and would fit in a single register.  */
 | ||
| 
 | ||
| static void
 | ||
| assign_parm_remove_parallels (struct assign_parm_data_one *data)
 | ||
| {
 | ||
|   rtx entry_parm = data->entry_parm;
 | ||
| 
 | ||
|   /* Convert the PARALLEL to a REG of the same mode as the parallel.
 | ||
|      This can be done with register operations rather than on the
 | ||
|      stack, even if we will store the reconstituted parameter on the
 | ||
|      stack later.  */
 | ||
|   if (GET_CODE (entry_parm) == PARALLEL && GET_MODE (entry_parm) != BLKmode)
 | ||
|     {
 | ||
|       rtx parmreg = gen_reg_rtx (GET_MODE (entry_parm));
 | ||
|       emit_group_store (parmreg, entry_parm, data->passed_type,
 | ||
| 			GET_MODE_SIZE (GET_MODE (entry_parm)));
 | ||
|       entry_parm = parmreg;
 | ||
|     }
 | ||
| 
 | ||
|   data->entry_parm = entry_parm;
 | ||
| }
 | ||
| 
 | ||
| /* A subroutine of assign_parms.  Adjust DATA->STACK_RTL such that it's
 | ||
|    always valid and properly aligned.  */
 | ||
| 
 | ||
| static void
 | ||
| assign_parm_adjust_stack_rtl (struct assign_parm_data_one *data)
 | ||
| {
 | ||
|   rtx stack_parm = data->stack_parm;
 | ||
| 
 | ||
|   /* If we can't trust the parm stack slot to be aligned enough for its
 | ||
|      ultimate type, don't use that slot after entry.  We'll make another
 | ||
|      stack slot, if we need one.  */
 | ||
|   if (stack_parm
 | ||
|       && ((STRICT_ALIGNMENT
 | ||
| 	   && GET_MODE_ALIGNMENT (data->nominal_mode) > MEM_ALIGN (stack_parm))
 | ||
| 	  || (data->nominal_type
 | ||
| 	      && TYPE_ALIGN (data->nominal_type) > MEM_ALIGN (stack_parm)
 | ||
| 	      && MEM_ALIGN (stack_parm) < PREFERRED_STACK_BOUNDARY)))
 | ||
|     stack_parm = NULL;
 | ||
| 
 | ||
|   /* If parm was passed in memory, and we need to convert it on entry,
 | ||
|      don't store it back in that same slot.  */
 | ||
|   else if (data->entry_parm == stack_parm
 | ||
| 	   && data->nominal_mode != BLKmode
 | ||
| 	   && data->nominal_mode != data->passed_mode)
 | ||
|     stack_parm = NULL;
 | ||
| 
 | ||
|   /* If stack protection is in effect for this function, don't leave any
 | ||
|      pointers in their passed stack slots.  */
 | ||
|   else if (crtl->stack_protect_guard
 | ||
| 	   && (flag_stack_protect == 2
 | ||
| 	       || data->passed_pointer
 | ||
| 	       || POINTER_TYPE_P (data->nominal_type)))
 | ||
|     stack_parm = NULL;
 | ||
| 
 | ||
|   data->stack_parm = stack_parm;
 | ||
| }
 | ||
| 
 | ||
| /* A subroutine of assign_parms.  Return true if the current parameter
 | ||
|    should be stored as a BLKmode in the current frame.  */
 | ||
| 
 | ||
| static bool
 | ||
| assign_parm_setup_block_p (struct assign_parm_data_one *data)
 | ||
| {
 | ||
|   if (data->nominal_mode == BLKmode)
 | ||
|     return true;
 | ||
|   if (GET_MODE (data->entry_parm) == BLKmode)
 | ||
|     return true;
 | ||
| 
 | ||
| #ifdef BLOCK_REG_PADDING
 | ||
|   /* Only assign_parm_setup_block knows how to deal with register arguments
 | ||
|      that are padded at the least significant end.  */
 | ||
|   if (REG_P (data->entry_parm)
 | ||
|       && GET_MODE_SIZE (data->promoted_mode) < UNITS_PER_WORD
 | ||
|       && (BLOCK_REG_PADDING (data->passed_mode, data->passed_type, 1)
 | ||
| 	  == (BYTES_BIG_ENDIAN ? upward : downward)))
 | ||
|     return true;
 | ||
| #endif
 | ||
| 
 | ||
|   return false;
 | ||
| }
 | ||
| 
 | ||
| /* A subroutine of assign_parms.  Arrange for the parameter to be
 | ||
|    present and valid in DATA->STACK_RTL.  */
 | ||
| 
 | ||
| static void
 | ||
| assign_parm_setup_block (struct assign_parm_data_all *all,
 | ||
| 			 tree parm, struct assign_parm_data_one *data)
 | ||
| {
 | ||
|   rtx entry_parm = data->entry_parm;
 | ||
|   rtx stack_parm = data->stack_parm;
 | ||
|   HOST_WIDE_INT size;
 | ||
|   HOST_WIDE_INT size_stored;
 | ||
| 
 | ||
|   if (GET_CODE (entry_parm) == PARALLEL)
 | ||
|     entry_parm = emit_group_move_into_temps (entry_parm);
 | ||
| 
 | ||
|   size = int_size_in_bytes (data->passed_type);
 | ||
|   size_stored = CEIL_ROUND (size, UNITS_PER_WORD);
 | ||
|   if (stack_parm == 0)
 | ||
|     {
 | ||
|       DECL_ALIGN (parm) = MAX (DECL_ALIGN (parm), BITS_PER_WORD);
 | ||
|       stack_parm = assign_stack_local (BLKmode, size_stored,
 | ||
| 				       DECL_ALIGN (parm));
 | ||
|       if (GET_MODE_SIZE (GET_MODE (entry_parm)) == size)
 | ||
| 	PUT_MODE (stack_parm, GET_MODE (entry_parm));
 | ||
|       set_mem_attributes (stack_parm, parm, 1);
 | ||
|     }
 | ||
| 
 | ||
|   /* If a BLKmode arrives in registers, copy it to a stack slot.  Handle
 | ||
|      calls that pass values in multiple non-contiguous locations.  */
 | ||
|   if (REG_P (entry_parm) || GET_CODE (entry_parm) == PARALLEL)
 | ||
|     {
 | ||
|       rtx mem;
 | ||
| 
 | ||
|       /* Note that we will be storing an integral number of words.
 | ||
| 	 So we have to be careful to ensure that we allocate an
 | ||
| 	 integral number of words.  We do this above when we call
 | ||
| 	 assign_stack_local if space was not allocated in the argument
 | ||
| 	 list.  If it was, this will not work if PARM_BOUNDARY is not
 | ||
| 	 a multiple of BITS_PER_WORD.  It isn't clear how to fix this
 | ||
| 	 if it becomes a problem.  Exception is when BLKmode arrives
 | ||
| 	 with arguments not conforming to word_mode.  */
 | ||
| 
 | ||
|       if (data->stack_parm == 0)
 | ||
| 	;
 | ||
|       else if (GET_CODE (entry_parm) == PARALLEL)
 | ||
| 	;
 | ||
|       else
 | ||
| 	gcc_assert (!size || !(PARM_BOUNDARY % BITS_PER_WORD));
 | ||
| 
 | ||
|       mem = validize_mem (stack_parm);
 | ||
| 
 | ||
|       /* Handle values in multiple non-contiguous locations.  */
 | ||
|       if (GET_CODE (entry_parm) == PARALLEL)
 | ||
| 	{
 | ||
| 	  push_to_sequence2 (all->first_conversion_insn,
 | ||
| 			     all->last_conversion_insn);
 | ||
| 	  emit_group_store (mem, entry_parm, data->passed_type, size);
 | ||
| 	  all->first_conversion_insn = get_insns ();
 | ||
| 	  all->last_conversion_insn = get_last_insn ();
 | ||
| 	  end_sequence ();
 | ||
| 	}
 | ||
| 
 | ||
|       else if (size == 0)
 | ||
| 	;
 | ||
| 
 | ||
|       /* If SIZE is that of a mode no bigger than a word, just use
 | ||
| 	 that mode's store operation.  */
 | ||
|       else if (size <= UNITS_PER_WORD)
 | ||
| 	{
 | ||
| 	  enum machine_mode mode
 | ||
| 	    = mode_for_size (size * BITS_PER_UNIT, MODE_INT, 0);
 | ||
| 
 | ||
| 	  if (mode != BLKmode
 | ||
| #ifdef BLOCK_REG_PADDING
 | ||
| 	      && (size == UNITS_PER_WORD
 | ||
| 		  || (BLOCK_REG_PADDING (mode, data->passed_type, 1)
 | ||
| 		      != (BYTES_BIG_ENDIAN ? upward : downward)))
 | ||
| #endif
 | ||
| 	      )
 | ||
| 	    {
 | ||
| 	      rtx reg;
 | ||
| 
 | ||
| 	      /* We are really truncating a word_mode value containing
 | ||
| 		 SIZE bytes into a value of mode MODE.  If such an
 | ||
| 		 operation requires no actual instructions, we can refer
 | ||
| 		 to the value directly in mode MODE, otherwise we must
 | ||
| 		 start with the register in word_mode and explicitly
 | ||
| 		 convert it.  */
 | ||
| 	      if (TRULY_NOOP_TRUNCATION (size * BITS_PER_UNIT, BITS_PER_WORD))
 | ||
| 		reg = gen_rtx_REG (mode, REGNO (entry_parm));
 | ||
| 	      else
 | ||
| 		{
 | ||
| 		  reg = gen_rtx_REG (word_mode, REGNO (entry_parm));
 | ||
| 		  reg = convert_to_mode (mode, copy_to_reg (reg), 1);
 | ||
| 		}
 | ||
| 	      emit_move_insn (change_address (mem, mode, 0), reg);
 | ||
| 	    }
 | ||
| 
 | ||
| 	  /* Blocks smaller than a word on a BYTES_BIG_ENDIAN
 | ||
| 	     machine must be aligned to the left before storing
 | ||
| 	     to memory.  Note that the previous test doesn't
 | ||
| 	     handle all cases (e.g. SIZE == 3).  */
 | ||
| 	  else if (size != UNITS_PER_WORD
 | ||
| #ifdef BLOCK_REG_PADDING
 | ||
| 		   && (BLOCK_REG_PADDING (mode, data->passed_type, 1)
 | ||
| 		       == downward)
 | ||
| #else
 | ||
| 		   && BYTES_BIG_ENDIAN
 | ||
| #endif
 | ||
| 		   )
 | ||
| 	    {
 | ||
| 	      rtx tem, x;
 | ||
| 	      int by = (UNITS_PER_WORD - size) * BITS_PER_UNIT;
 | ||
| 	      rtx reg = gen_rtx_REG (word_mode, REGNO (entry_parm));
 | ||
| 
 | ||
| 	      x = expand_shift (LSHIFT_EXPR, word_mode, reg, by, NULL_RTX, 1);
 | ||
| 	      tem = change_address (mem, word_mode, 0);
 | ||
| 	      emit_move_insn (tem, x);
 | ||
| 	    }
 | ||
| 	  else
 | ||
| 	    move_block_from_reg (REGNO (entry_parm), mem,
 | ||
| 				 size_stored / UNITS_PER_WORD);
 | ||
| 	}
 | ||
|       else
 | ||
| 	move_block_from_reg (REGNO (entry_parm), mem,
 | ||
| 			     size_stored / UNITS_PER_WORD);
 | ||
|     }
 | ||
|   else if (data->stack_parm == 0)
 | ||
|     {
 | ||
|       push_to_sequence2 (all->first_conversion_insn, all->last_conversion_insn);
 | ||
|       emit_block_move (stack_parm, data->entry_parm, GEN_INT (size),
 | ||
| 		       BLOCK_OP_NORMAL);
 | ||
|       all->first_conversion_insn = get_insns ();
 | ||
|       all->last_conversion_insn = get_last_insn ();
 | ||
|       end_sequence ();
 | ||
|     }
 | ||
| 
 | ||
|   data->stack_parm = stack_parm;
 | ||
|   SET_DECL_RTL (parm, stack_parm);
 | ||
| }
 | ||
| 
 | ||
| /* A subroutine of assign_parms.  Allocate a pseudo to hold the current
 | ||
|    parameter.  Get it there.  Perform all ABI specified conversions.  */
 | ||
| 
 | ||
| static void
 | ||
| assign_parm_setup_reg (struct assign_parm_data_all *all, tree parm,
 | ||
| 		       struct assign_parm_data_one *data)
 | ||
| {
 | ||
|   rtx parmreg, validated_mem;
 | ||
|   rtx equiv_stack_parm;
 | ||
|   enum machine_mode promoted_nominal_mode;
 | ||
|   int unsignedp = TYPE_UNSIGNED (TREE_TYPE (parm));
 | ||
|   bool did_conversion = false;
 | ||
|   bool need_conversion, moved;
 | ||
| 
 | ||
|   /* Store the parm in a pseudoregister during the function, but we may
 | ||
|      need to do it in a wider mode.  Using 2 here makes the result
 | ||
|      consistent with promote_decl_mode and thus expand_expr_real_1.  */
 | ||
|   promoted_nominal_mode
 | ||
|     = promote_function_mode (data->nominal_type, data->nominal_mode, &unsignedp,
 | ||
| 			     TREE_TYPE (current_function_decl), 2);
 | ||
| 
 | ||
|   parmreg = gen_reg_rtx (promoted_nominal_mode);
 | ||
| 
 | ||
|   if (!DECL_ARTIFICIAL (parm))
 | ||
|     mark_user_reg (parmreg);
 | ||
| 
 | ||
|   /* If this was an item that we received a pointer to,
 | ||
|      set DECL_RTL appropriately.  */
 | ||
|   if (data->passed_pointer)
 | ||
|     {
 | ||
|       rtx x = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (data->passed_type)), parmreg);
 | ||
|       set_mem_attributes (x, parm, 1);
 | ||
|       SET_DECL_RTL (parm, x);
 | ||
|     }
 | ||
|   else
 | ||
|     SET_DECL_RTL (parm, parmreg);
 | ||
| 
 | ||
|   assign_parm_remove_parallels (data);
 | ||
| 
 | ||
|   /* Copy the value into the register, thus bridging between
 | ||
|      assign_parm_find_data_types and expand_expr_real_1.  */
 | ||
| 
 | ||
|   equiv_stack_parm = data->stack_parm;
 | ||
|   validated_mem = validize_mem (data->entry_parm);
 | ||
| 
 | ||
|   need_conversion = (data->nominal_mode != data->passed_mode
 | ||
| 		     || promoted_nominal_mode != data->promoted_mode);
 | ||
|   moved = false;
 | ||
| 
 | ||
|   if (need_conversion
 | ||
|       && GET_MODE_CLASS (data->nominal_mode) == MODE_INT
 | ||
|       && data->nominal_mode == data->passed_mode
 | ||
|       && data->nominal_mode == GET_MODE (data->entry_parm))
 | ||
|     {
 | ||
|       /* ENTRY_PARM has been converted to PROMOTED_MODE, its
 | ||
| 	 mode, by the caller.  We now have to convert it to
 | ||
| 	 NOMINAL_MODE, if different.  However, PARMREG may be in
 | ||
| 	 a different mode than NOMINAL_MODE if it is being stored
 | ||
| 	 promoted.
 | ||
| 
 | ||
| 	 If ENTRY_PARM is a hard register, it might be in a register
 | ||
| 	 not valid for operating in its mode (e.g., an odd-numbered
 | ||
| 	 register for a DFmode).  In that case, moves are the only
 | ||
| 	 thing valid, so we can't do a convert from there.  This
 | ||
| 	 occurs when the calling sequence allow such misaligned
 | ||
| 	 usages.
 | ||
| 
 | ||
| 	 In addition, the conversion may involve a call, which could
 | ||
| 	 clobber parameters which haven't been copied to pseudo
 | ||
| 	 registers yet.
 | ||
| 
 | ||
| 	 First, we try to emit an insn which performs the necessary
 | ||
| 	 conversion.  We verify that this insn does not clobber any
 | ||
| 	 hard registers.  */
 | ||
| 
 | ||
|       enum insn_code icode;
 | ||
|       rtx op0, op1;
 | ||
| 
 | ||
|       icode = can_extend_p (promoted_nominal_mode, data->passed_mode,
 | ||
| 			    unsignedp);
 | ||
| 
 | ||
|       op0 = parmreg;
 | ||
|       op1 = validated_mem;
 | ||
|       if (icode != CODE_FOR_nothing
 | ||
| 	  && insn_operand_matches (icode, 0, op0)
 | ||
| 	  && insn_operand_matches (icode, 1, op1))
 | ||
| 	{
 | ||
| 	  enum rtx_code code = unsignedp ? ZERO_EXTEND : SIGN_EXTEND;
 | ||
| 	  rtx insn, insns, t = op1;
 | ||
| 	  HARD_REG_SET hardregs;
 | ||
| 
 | ||
| 	  start_sequence ();
 | ||
| 	  /* If op1 is a hard register that is likely spilled, first
 | ||
| 	     force it into a pseudo, otherwise combiner might extend
 | ||
| 	     its lifetime too much.  */
 | ||
| 	  if (GET_CODE (t) == SUBREG)
 | ||
| 	    t = SUBREG_REG (t);
 | ||
| 	  if (REG_P (t)
 | ||
| 	      && HARD_REGISTER_P (t)
 | ||
| 	      && ! TEST_HARD_REG_BIT (fixed_reg_set, REGNO (t))
 | ||
| 	      && targetm.class_likely_spilled_p (REGNO_REG_CLASS (REGNO (t))))
 | ||
| 	    {
 | ||
| 	      t = gen_reg_rtx (GET_MODE (op1));
 | ||
| 	      emit_move_insn (t, op1);
 | ||
| 	    }
 | ||
| 	  else
 | ||
| 	    t = op1;
 | ||
| 	  insn = gen_extend_insn (op0, t, promoted_nominal_mode,
 | ||
| 				  data->passed_mode, unsignedp);
 | ||
| 	  emit_insn (insn);
 | ||
| 	  insns = get_insns ();
 | ||
| 
 | ||
| 	  moved = true;
 | ||
| 	  CLEAR_HARD_REG_SET (hardregs);
 | ||
| 	  for (insn = insns; insn && moved; insn = NEXT_INSN (insn))
 | ||
| 	    {
 | ||
| 	      if (INSN_P (insn))
 | ||
| 		note_stores (PATTERN (insn), record_hard_reg_sets,
 | ||
| 			     &hardregs);
 | ||
| 	      if (!hard_reg_set_empty_p (hardregs))
 | ||
| 		moved = false;
 | ||
| 	    }
 | ||
| 
 | ||
| 	  end_sequence ();
 | ||
| 
 | ||
| 	  if (moved)
 | ||
| 	    {
 | ||
| 	      emit_insn (insns);
 | ||
| 	      if (equiv_stack_parm != NULL_RTX)
 | ||
| 		equiv_stack_parm = gen_rtx_fmt_e (code, GET_MODE (parmreg),
 | ||
| 						  equiv_stack_parm);
 | ||
| 	    }
 | ||
| 	}
 | ||
|     }
 | ||
| 
 | ||
|   if (moved)
 | ||
|     /* Nothing to do.  */
 | ||
|     ;
 | ||
|   else if (need_conversion)
 | ||
|     {
 | ||
|       /* We did not have an insn to convert directly, or the sequence
 | ||
| 	 generated appeared unsafe.  We must first copy the parm to a
 | ||
| 	 pseudo reg, and save the conversion until after all
 | ||
| 	 parameters have been moved.  */
 | ||
| 
 | ||
|       int save_tree_used;
 | ||
|       rtx tempreg = gen_reg_rtx (GET_MODE (data->entry_parm));
 | ||
| 
 | ||
|       emit_move_insn (tempreg, validated_mem);
 | ||
| 
 | ||
|       push_to_sequence2 (all->first_conversion_insn, all->last_conversion_insn);
 | ||
|       tempreg = convert_to_mode (data->nominal_mode, tempreg, unsignedp);
 | ||
| 
 | ||
|       if (GET_CODE (tempreg) == SUBREG
 | ||
| 	  && GET_MODE (tempreg) == data->nominal_mode
 | ||
| 	  && REG_P (SUBREG_REG (tempreg))
 | ||
| 	  && data->nominal_mode == data->passed_mode
 | ||
| 	  && GET_MODE (SUBREG_REG (tempreg)) == GET_MODE (data->entry_parm)
 | ||
| 	  && GET_MODE_SIZE (GET_MODE (tempreg))
 | ||
| 	     < GET_MODE_SIZE (GET_MODE (data->entry_parm)))
 | ||
| 	{
 | ||
| 	  /* The argument is already sign/zero extended, so note it
 | ||
| 	     into the subreg.  */
 | ||
| 	  SUBREG_PROMOTED_VAR_P (tempreg) = 1;
 | ||
| 	  SUBREG_PROMOTED_UNSIGNED_SET (tempreg, unsignedp);
 | ||
| 	}
 | ||
| 
 | ||
|       /* TREE_USED gets set erroneously during expand_assignment.  */
 | ||
|       save_tree_used = TREE_USED (parm);
 | ||
|       expand_assignment (parm, make_tree (data->nominal_type, tempreg), false);
 | ||
|       TREE_USED (parm) = save_tree_used;
 | ||
|       all->first_conversion_insn = get_insns ();
 | ||
|       all->last_conversion_insn = get_last_insn ();
 | ||
|       end_sequence ();
 | ||
| 
 | ||
|       did_conversion = true;
 | ||
|     }
 | ||
|   else
 | ||
|     emit_move_insn (parmreg, validated_mem);
 | ||
| 
 | ||
|   /* If we were passed a pointer but the actual value can safely live
 | ||
|      in a register, put it in one.  */
 | ||
|   if (data->passed_pointer
 | ||
|       && TYPE_MODE (TREE_TYPE (parm)) != BLKmode
 | ||
|       /* If by-reference argument was promoted, demote it.  */
 | ||
|       && (TYPE_MODE (TREE_TYPE (parm)) != GET_MODE (DECL_RTL (parm))
 | ||
| 	  || use_register_for_decl (parm)))
 | ||
|     {
 | ||
|       /* We can't use nominal_mode, because it will have been set to
 | ||
| 	 Pmode above.  We must use the actual mode of the parm.  */
 | ||
|       parmreg = gen_reg_rtx (TYPE_MODE (TREE_TYPE (parm)));
 | ||
|       mark_user_reg (parmreg);
 | ||
| 
 | ||
|       if (GET_MODE (parmreg) != GET_MODE (DECL_RTL (parm)))
 | ||
| 	{
 | ||
| 	  rtx tempreg = gen_reg_rtx (GET_MODE (DECL_RTL (parm)));
 | ||
| 	  int unsigned_p = TYPE_UNSIGNED (TREE_TYPE (parm));
 | ||
| 
 | ||
| 	  push_to_sequence2 (all->first_conversion_insn,
 | ||
| 			     all->last_conversion_insn);
 | ||
| 	  emit_move_insn (tempreg, DECL_RTL (parm));
 | ||
| 	  tempreg = convert_to_mode (GET_MODE (parmreg), tempreg, unsigned_p);
 | ||
| 	  emit_move_insn (parmreg, tempreg);
 | ||
| 	  all->first_conversion_insn = get_insns ();
 | ||
| 	  all->last_conversion_insn = get_last_insn ();
 | ||
| 	  end_sequence ();
 | ||
| 
 | ||
| 	  did_conversion = true;
 | ||
| 	}
 | ||
|       else
 | ||
| 	emit_move_insn (parmreg, DECL_RTL (parm));
 | ||
| 
 | ||
|       SET_DECL_RTL (parm, parmreg);
 | ||
| 
 | ||
|       /* STACK_PARM is the pointer, not the parm, and PARMREG is
 | ||
| 	 now the parm.  */
 | ||
|       data->stack_parm = NULL;
 | ||
|     }
 | ||
| 
 | ||
|   /* Mark the register as eliminable if we did no conversion and it was
 | ||
|      copied from memory at a fixed offset, and the arg pointer was not
 | ||
|      copied to a pseudo-reg.  If the arg pointer is a pseudo reg or the
 | ||
|      offset formed an invalid address, such memory-equivalences as we
 | ||
|      make here would screw up life analysis for it.  */
 | ||
|   if (data->nominal_mode == data->passed_mode
 | ||
|       && !did_conversion
 | ||
|       && data->stack_parm != 0
 | ||
|       && MEM_P (data->stack_parm)
 | ||
|       && data->locate.offset.var == 0
 | ||
|       && reg_mentioned_p (virtual_incoming_args_rtx,
 | ||
| 			  XEXP (data->stack_parm, 0)))
 | ||
|     {
 | ||
|       rtx linsn = get_last_insn ();
 | ||
|       rtx sinsn, set;
 | ||
| 
 | ||
|       /* Mark complex types separately.  */
 | ||
|       if (GET_CODE (parmreg) == CONCAT)
 | ||
| 	{
 | ||
| 	  enum machine_mode submode
 | ||
| 	    = GET_MODE_INNER (GET_MODE (parmreg));
 | ||
| 	  int regnor = REGNO (XEXP (parmreg, 0));
 | ||
| 	  int regnoi = REGNO (XEXP (parmreg, 1));
 | ||
| 	  rtx stackr = adjust_address_nv (data->stack_parm, submode, 0);
 | ||
| 	  rtx stacki = adjust_address_nv (data->stack_parm, submode,
 | ||
| 					  GET_MODE_SIZE (submode));
 | ||
| 
 | ||
| 	  /* Scan backwards for the set of the real and
 | ||
| 	     imaginary parts.  */
 | ||
| 	  for (sinsn = linsn; sinsn != 0;
 | ||
| 	       sinsn = prev_nonnote_insn (sinsn))
 | ||
| 	    {
 | ||
| 	      set = single_set (sinsn);
 | ||
| 	      if (set == 0)
 | ||
| 		continue;
 | ||
| 
 | ||
| 	      if (SET_DEST (set) == regno_reg_rtx [regnoi])
 | ||
| 		set_unique_reg_note (sinsn, REG_EQUIV, stacki);
 | ||
| 	      else if (SET_DEST (set) == regno_reg_rtx [regnor])
 | ||
| 		set_unique_reg_note (sinsn, REG_EQUIV, stackr);
 | ||
| 	    }
 | ||
| 	}
 | ||
|       else 
 | ||
| 	set_dst_reg_note (linsn, REG_EQUIV, equiv_stack_parm, parmreg);
 | ||
|     }
 | ||
| 
 | ||
|   /* For pointer data type, suggest pointer register.  */
 | ||
|   if (POINTER_TYPE_P (TREE_TYPE (parm)))
 | ||
|     mark_reg_pointer (parmreg,
 | ||
| 		      TYPE_ALIGN (TREE_TYPE (TREE_TYPE (parm))));
 | ||
| }
 | ||
| 
 | ||
| /* A subroutine of assign_parms.  Allocate stack space to hold the current
 | ||
|    parameter.  Get it there.  Perform all ABI specified conversions.  */
 | ||
| 
 | ||
| static void
 | ||
| assign_parm_setup_stack (struct assign_parm_data_all *all, tree parm,
 | ||
| 		         struct assign_parm_data_one *data)
 | ||
| {
 | ||
|   /* Value must be stored in the stack slot STACK_PARM during function
 | ||
|      execution.  */
 | ||
|   bool to_conversion = false;
 | ||
| 
 | ||
|   assign_parm_remove_parallels (data);
 | ||
| 
 | ||
|   if (data->promoted_mode != data->nominal_mode)
 | ||
|     {
 | ||
|       /* Conversion is required.  */
 | ||
|       rtx tempreg = gen_reg_rtx (GET_MODE (data->entry_parm));
 | ||
| 
 | ||
|       emit_move_insn (tempreg, validize_mem (data->entry_parm));
 | ||
| 
 | ||
|       push_to_sequence2 (all->first_conversion_insn, all->last_conversion_insn);
 | ||
|       to_conversion = true;
 | ||
| 
 | ||
|       data->entry_parm = convert_to_mode (data->nominal_mode, tempreg,
 | ||
| 					  TYPE_UNSIGNED (TREE_TYPE (parm)));
 | ||
| 
 | ||
|       if (data->stack_parm)
 | ||
| 	{
 | ||
| 	  int offset = subreg_lowpart_offset (data->nominal_mode,
 | ||
| 					      GET_MODE (data->stack_parm));
 | ||
| 	  /* ??? This may need a big-endian conversion on sparc64.  */
 | ||
| 	  data->stack_parm
 | ||
| 	    = adjust_address (data->stack_parm, data->nominal_mode, 0);
 | ||
| 	  if (offset && MEM_OFFSET_KNOWN_P (data->stack_parm))
 | ||
| 	    set_mem_offset (data->stack_parm,
 | ||
| 			    MEM_OFFSET (data->stack_parm) + offset);
 | ||
| 	}
 | ||
|     }
 | ||
| 
 | ||
|   if (data->entry_parm != data->stack_parm)
 | ||
|     {
 | ||
|       rtx src, dest;
 | ||
| 
 | ||
|       if (data->stack_parm == 0)
 | ||
| 	{
 | ||
| 	  int align = STACK_SLOT_ALIGNMENT (data->passed_type,
 | ||
| 					    GET_MODE (data->entry_parm),
 | ||
| 					    TYPE_ALIGN (data->passed_type));
 | ||
| 	  data->stack_parm
 | ||
| 	    = assign_stack_local (GET_MODE (data->entry_parm),
 | ||
| 				  GET_MODE_SIZE (GET_MODE (data->entry_parm)),
 | ||
| 				  align);
 | ||
| 	  set_mem_attributes (data->stack_parm, parm, 1);
 | ||
| 	}
 | ||
| 
 | ||
|       dest = validize_mem (data->stack_parm);
 | ||
|       src = validize_mem (data->entry_parm);
 | ||
| 
 | ||
|       if (MEM_P (src))
 | ||
| 	{
 | ||
| 	  /* Use a block move to handle potentially misaligned entry_parm.  */
 | ||
| 	  if (!to_conversion)
 | ||
| 	    push_to_sequence2 (all->first_conversion_insn,
 | ||
| 			       all->last_conversion_insn);
 | ||
| 	  to_conversion = true;
 | ||
| 
 | ||
| 	  emit_block_move (dest, src,
 | ||
| 			   GEN_INT (int_size_in_bytes (data->passed_type)),
 | ||
| 			   BLOCK_OP_NORMAL);
 | ||
| 	}
 | ||
|       else
 | ||
| 	emit_move_insn (dest, src);
 | ||
|     }
 | ||
| 
 | ||
|   if (to_conversion)
 | ||
|     {
 | ||
|       all->first_conversion_insn = get_insns ();
 | ||
|       all->last_conversion_insn = get_last_insn ();
 | ||
|       end_sequence ();
 | ||
|     }
 | ||
| 
 | ||
|   SET_DECL_RTL (parm, data->stack_parm);
 | ||
| }
 | ||
| 
 | ||
| /* A subroutine of assign_parms.  If the ABI splits complex arguments, then
 | ||
|    undo the frobbing that we did in assign_parms_augmented_arg_list.  */
 | ||
| 
 | ||
| static void
 | ||
| assign_parms_unsplit_complex (struct assign_parm_data_all *all,
 | ||
| 			      VEC(tree, heap) *fnargs)
 | ||
| {
 | ||
|   tree parm;
 | ||
|   tree orig_fnargs = all->orig_fnargs;
 | ||
|   unsigned i = 0;
 | ||
| 
 | ||
|   for (parm = orig_fnargs; parm; parm = TREE_CHAIN (parm), ++i)
 | ||
|     {
 | ||
|       if (TREE_CODE (TREE_TYPE (parm)) == COMPLEX_TYPE
 | ||
| 	  && targetm.calls.split_complex_arg (TREE_TYPE (parm)))
 | ||
| 	{
 | ||
| 	  rtx tmp, real, imag;
 | ||
| 	  enum machine_mode inner = GET_MODE_INNER (DECL_MODE (parm));
 | ||
| 
 | ||
| 	  real = DECL_RTL (VEC_index (tree, fnargs, i));
 | ||
| 	  imag = DECL_RTL (VEC_index (tree, fnargs, i + 1));
 | ||
| 	  if (inner != GET_MODE (real))
 | ||
| 	    {
 | ||
| 	      real = gen_lowpart_SUBREG (inner, real);
 | ||
| 	      imag = gen_lowpart_SUBREG (inner, imag);
 | ||
| 	    }
 | ||
| 
 | ||
| 	  if (TREE_ADDRESSABLE (parm))
 | ||
| 	    {
 | ||
| 	      rtx rmem, imem;
 | ||
| 	      HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (parm));
 | ||
| 	      int align = STACK_SLOT_ALIGNMENT (TREE_TYPE (parm),
 | ||
| 						DECL_MODE (parm),
 | ||
| 						TYPE_ALIGN (TREE_TYPE (parm)));
 | ||
| 
 | ||
| 	      /* split_complex_arg put the real and imag parts in
 | ||
| 		 pseudos.  Move them to memory.  */
 | ||
| 	      tmp = assign_stack_local (DECL_MODE (parm), size, align);
 | ||
| 	      set_mem_attributes (tmp, parm, 1);
 | ||
| 	      rmem = adjust_address_nv (tmp, inner, 0);
 | ||
| 	      imem = adjust_address_nv (tmp, inner, GET_MODE_SIZE (inner));
 | ||
| 	      push_to_sequence2 (all->first_conversion_insn,
 | ||
| 				 all->last_conversion_insn);
 | ||
| 	      emit_move_insn (rmem, real);
 | ||
| 	      emit_move_insn (imem, imag);
 | ||
| 	      all->first_conversion_insn = get_insns ();
 | ||
| 	      all->last_conversion_insn = get_last_insn ();
 | ||
| 	      end_sequence ();
 | ||
| 	    }
 | ||
| 	  else
 | ||
| 	    tmp = gen_rtx_CONCAT (DECL_MODE (parm), real, imag);
 | ||
| 	  SET_DECL_RTL (parm, tmp);
 | ||
| 
 | ||
| 	  real = DECL_INCOMING_RTL (VEC_index (tree, fnargs, i));
 | ||
| 	  imag = DECL_INCOMING_RTL (VEC_index (tree, fnargs, i + 1));
 | ||
| 	  if (inner != GET_MODE (real))
 | ||
| 	    {
 | ||
| 	      real = gen_lowpart_SUBREG (inner, real);
 | ||
| 	      imag = gen_lowpart_SUBREG (inner, imag);
 | ||
| 	    }
 | ||
| 	  tmp = gen_rtx_CONCAT (DECL_MODE (parm), real, imag);
 | ||
| 	  set_decl_incoming_rtl (parm, tmp, false);
 | ||
| 	  i++;
 | ||
| 	}
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /* Assign RTL expressions to the function's parameters.  This may involve
 | ||
|    copying them into registers and using those registers as the DECL_RTL.  */
 | ||
| 
 | ||
| static void
 | ||
| assign_parms (tree fndecl)
 | ||
| {
 | ||
|   struct assign_parm_data_all all;
 | ||
|   tree parm;
 | ||
|   VEC(tree, heap) *fnargs;
 | ||
|   unsigned i;
 | ||
| 
 | ||
|   crtl->args.internal_arg_pointer
 | ||
|     = targetm.calls.internal_arg_pointer ();
 | ||
| 
 | ||
|   assign_parms_initialize_all (&all);
 | ||
|   fnargs = assign_parms_augmented_arg_list (&all);
 | ||
| 
 | ||
|   FOR_EACH_VEC_ELT (tree, fnargs, i, parm)
 | ||
|     {
 | ||
|       struct assign_parm_data_one data;
 | ||
| 
 | ||
|       /* Extract the type of PARM; adjust it according to ABI.  */
 | ||
|       assign_parm_find_data_types (&all, parm, &data);
 | ||
| 
 | ||
|       /* Early out for errors and void parameters.  */
 | ||
|       if (data.passed_mode == VOIDmode)
 | ||
| 	{
 | ||
| 	  SET_DECL_RTL (parm, const0_rtx);
 | ||
| 	  DECL_INCOMING_RTL (parm) = DECL_RTL (parm);
 | ||
| 	  continue;
 | ||
| 	}
 | ||
| 
 | ||
|       /* Estimate stack alignment from parameter alignment.  */
 | ||
|       if (SUPPORTS_STACK_ALIGNMENT)
 | ||
|         {
 | ||
|           unsigned int align
 | ||
| 	    = targetm.calls.function_arg_boundary (data.promoted_mode,
 | ||
| 						   data.passed_type);
 | ||
| 	  align = MINIMUM_ALIGNMENT (data.passed_type, data.promoted_mode,
 | ||
| 				     align);
 | ||
| 	  if (TYPE_ALIGN (data.nominal_type) > align)
 | ||
| 	    align = MINIMUM_ALIGNMENT (data.nominal_type,
 | ||
| 				       TYPE_MODE (data.nominal_type),
 | ||
| 				       TYPE_ALIGN (data.nominal_type));
 | ||
| 	  if (crtl->stack_alignment_estimated < align)
 | ||
| 	    {
 | ||
| 	      gcc_assert (!crtl->stack_realign_processed);
 | ||
| 	      crtl->stack_alignment_estimated = align;
 | ||
| 	    }
 | ||
| 	}
 | ||
| 
 | ||
|       if (cfun->stdarg && !DECL_CHAIN (parm))
 | ||
| 	assign_parms_setup_varargs (&all, &data, false);
 | ||
| 
 | ||
|       /* Find out where the parameter arrives in this function.  */
 | ||
|       assign_parm_find_entry_rtl (&all, &data);
 | ||
| 
 | ||
|       /* Find out where stack space for this parameter might be.  */
 | ||
|       if (assign_parm_is_stack_parm (&all, &data))
 | ||
| 	{
 | ||
| 	  assign_parm_find_stack_rtl (parm, &data);
 | ||
| 	  assign_parm_adjust_entry_rtl (&data);
 | ||
| 	}
 | ||
| 
 | ||
|       /* Record permanently how this parm was passed.  */
 | ||
|       if (data.passed_pointer)
 | ||
| 	{
 | ||
| 	  rtx incoming_rtl
 | ||
| 	    = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (data.passed_type)),
 | ||
| 			   data.entry_parm);
 | ||
| 	  set_decl_incoming_rtl (parm, incoming_rtl, true);
 | ||
| 	}
 | ||
|       else
 | ||
| 	set_decl_incoming_rtl (parm, data.entry_parm, false);
 | ||
| 
 | ||
|       /* Update info on where next arg arrives in registers.  */
 | ||
|       targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode,
 | ||
| 					  data.passed_type, data.named_arg);
 | ||
| 
 | ||
|       assign_parm_adjust_stack_rtl (&data);
 | ||
| 
 | ||
|       if (assign_parm_setup_block_p (&data))
 | ||
| 	assign_parm_setup_block (&all, parm, &data);
 | ||
|       else if (data.passed_pointer || use_register_for_decl (parm))
 | ||
| 	assign_parm_setup_reg (&all, parm, &data);
 | ||
|       else
 | ||
| 	assign_parm_setup_stack (&all, parm, &data);
 | ||
|     }
 | ||
| 
 | ||
|   if (targetm.calls.split_complex_arg)
 | ||
|     assign_parms_unsplit_complex (&all, fnargs);
 | ||
| 
 | ||
|   VEC_free (tree, heap, fnargs);
 | ||
| 
 | ||
|   /* Output all parameter conversion instructions (possibly including calls)
 | ||
|      now that all parameters have been copied out of hard registers.  */
 | ||
|   emit_insn (all.first_conversion_insn);
 | ||
| 
 | ||
|   /* Estimate reload stack alignment from scalar return mode.  */
 | ||
|   if (SUPPORTS_STACK_ALIGNMENT)
 | ||
|     {
 | ||
|       if (DECL_RESULT (fndecl))
 | ||
| 	{
 | ||
| 	  tree type = TREE_TYPE (DECL_RESULT (fndecl));
 | ||
| 	  enum machine_mode mode = TYPE_MODE (type);
 | ||
| 
 | ||
| 	  if (mode != BLKmode
 | ||
| 	      && mode != VOIDmode
 | ||
| 	      && !AGGREGATE_TYPE_P (type))
 | ||
| 	    {
 | ||
| 	      unsigned int align = GET_MODE_ALIGNMENT (mode);
 | ||
| 	      if (crtl->stack_alignment_estimated < align)
 | ||
| 		{
 | ||
| 		  gcc_assert (!crtl->stack_realign_processed);
 | ||
| 		  crtl->stack_alignment_estimated = align;
 | ||
| 		}
 | ||
| 	    }
 | ||
| 	}
 | ||
|     }
 | ||
| 
 | ||
|   /* If we are receiving a struct value address as the first argument, set up
 | ||
|      the RTL for the function result. As this might require code to convert
 | ||
|      the transmitted address to Pmode, we do this here to ensure that possible
 | ||
|      preliminary conversions of the address have been emitted already.  */
 | ||
|   if (all.function_result_decl)
 | ||
|     {
 | ||
|       tree result = DECL_RESULT (current_function_decl);
 | ||
|       rtx addr = DECL_RTL (all.function_result_decl);
 | ||
|       rtx x;
 | ||
| 
 | ||
|       if (DECL_BY_REFERENCE (result))
 | ||
| 	{
 | ||
| 	  SET_DECL_VALUE_EXPR (result, all.function_result_decl);
 | ||
| 	  x = addr;
 | ||
| 	}
 | ||
|       else
 | ||
| 	{
 | ||
| 	  SET_DECL_VALUE_EXPR (result,
 | ||
| 			       build1 (INDIRECT_REF, TREE_TYPE (result),
 | ||
| 				       all.function_result_decl));
 | ||
| 	  addr = convert_memory_address (Pmode, addr);
 | ||
| 	  x = gen_rtx_MEM (DECL_MODE (result), addr);
 | ||
| 	  set_mem_attributes (x, result, 1);
 | ||
| 	}
 | ||
| 
 | ||
|       DECL_HAS_VALUE_EXPR_P (result) = 1;
 | ||
| 
 | ||
|       SET_DECL_RTL (result, x);
 | ||
|     }
 | ||
| 
 | ||
|   /* We have aligned all the args, so add space for the pretend args.  */
 | ||
|   crtl->args.pretend_args_size = all.pretend_args_size;
 | ||
|   all.stack_args_size.constant += all.extra_pretend_bytes;
 | ||
|   crtl->args.size = all.stack_args_size.constant;
 | ||
| 
 | ||
|   /* Adjust function incoming argument size for alignment and
 | ||
|      minimum length.  */
 | ||
| 
 | ||
| #ifdef REG_PARM_STACK_SPACE
 | ||
|   crtl->args.size = MAX (crtl->args.size,
 | ||
| 				    REG_PARM_STACK_SPACE (fndecl));
 | ||
| #endif
 | ||
| 
 | ||
|   crtl->args.size = CEIL_ROUND (crtl->args.size,
 | ||
| 					   PARM_BOUNDARY / BITS_PER_UNIT);
 | ||
| 
 | ||
| #ifdef ARGS_GROW_DOWNWARD
 | ||
|   crtl->args.arg_offset_rtx
 | ||
|     = (all.stack_args_size.var == 0 ? GEN_INT (-all.stack_args_size.constant)
 | ||
|        : expand_expr (size_diffop (all.stack_args_size.var,
 | ||
| 				   size_int (-all.stack_args_size.constant)),
 | ||
| 		      NULL_RTX, VOIDmode, EXPAND_NORMAL));
 | ||
| #else
 | ||
|   crtl->args.arg_offset_rtx = ARGS_SIZE_RTX (all.stack_args_size);
 | ||
| #endif
 | ||
| 
 | ||
|   /* See how many bytes, if any, of its args a function should try to pop
 | ||
|      on return.  */
 | ||
| 
 | ||
|   crtl->args.pops_args = targetm.calls.return_pops_args (fndecl,
 | ||
| 							 TREE_TYPE (fndecl),
 | ||
| 							 crtl->args.size);
 | ||
| 
 | ||
|   /* For stdarg.h function, save info about
 | ||
|      regs and stack space used by the named args.  */
 | ||
| 
 | ||
|   crtl->args.info = all.args_so_far_v;
 | ||
| 
 | ||
|   /* Set the rtx used for the function return value.  Put this in its
 | ||
|      own variable so any optimizers that need this information don't have
 | ||
|      to include tree.h.  Do this here so it gets done when an inlined
 | ||
|      function gets output.  */
 | ||
| 
 | ||
|   crtl->return_rtx
 | ||
|     = (DECL_RTL_SET_P (DECL_RESULT (fndecl))
 | ||
|        ? DECL_RTL (DECL_RESULT (fndecl)) : NULL_RTX);
 | ||
| 
 | ||
|   /* If scalar return value was computed in a pseudo-reg, or was a named
 | ||
|      return value that got dumped to the stack, copy that to the hard
 | ||
|      return register.  */
 | ||
|   if (DECL_RTL_SET_P (DECL_RESULT (fndecl)))
 | ||
|     {
 | ||
|       tree decl_result = DECL_RESULT (fndecl);
 | ||
|       rtx decl_rtl = DECL_RTL (decl_result);
 | ||
| 
 | ||
|       if (REG_P (decl_rtl)
 | ||
| 	  ? REGNO (decl_rtl) >= FIRST_PSEUDO_REGISTER
 | ||
| 	  : DECL_REGISTER (decl_result))
 | ||
| 	{
 | ||
| 	  rtx real_decl_rtl;
 | ||
| 
 | ||
| 	  real_decl_rtl = targetm.calls.function_value (TREE_TYPE (decl_result),
 | ||
| 							fndecl, true);
 | ||
| 	  REG_FUNCTION_VALUE_P (real_decl_rtl) = 1;
 | ||
| 	  /* The delay slot scheduler assumes that crtl->return_rtx
 | ||
| 	     holds the hard register containing the return value, not a
 | ||
| 	     temporary pseudo.  */
 | ||
| 	  crtl->return_rtx = real_decl_rtl;
 | ||
| 	}
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /* A subroutine of gimplify_parameters, invoked via walk_tree.
 | ||
|    For all seen types, gimplify their sizes.  */
 | ||
| 
 | ||
| static tree
 | ||
| gimplify_parm_type (tree *tp, int *walk_subtrees, void *data)
 | ||
| {
 | ||
|   tree t = *tp;
 | ||
| 
 | ||
|   *walk_subtrees = 0;
 | ||
|   if (TYPE_P (t))
 | ||
|     {
 | ||
|       if (POINTER_TYPE_P (t))
 | ||
| 	*walk_subtrees = 1;
 | ||
|       else if (TYPE_SIZE (t) && !TREE_CONSTANT (TYPE_SIZE (t))
 | ||
| 	       && !TYPE_SIZES_GIMPLIFIED (t))
 | ||
| 	{
 | ||
| 	  gimplify_type_sizes (t, (gimple_seq *) data);
 | ||
| 	  *walk_subtrees = 1;
 | ||
| 	}
 | ||
|     }
 | ||
| 
 | ||
|   return NULL;
 | ||
| }
 | ||
| 
 | ||
| /* Gimplify the parameter list for current_function_decl.  This involves
 | ||
|    evaluating SAVE_EXPRs of variable sized parameters and generating code
 | ||
|    to implement callee-copies reference parameters.  Returns a sequence of
 | ||
|    statements to add to the beginning of the function.  */
 | ||
| 
 | ||
| gimple_seq
 | ||
| gimplify_parameters (void)
 | ||
| {
 | ||
|   struct assign_parm_data_all all;
 | ||
|   tree parm;
 | ||
|   gimple_seq stmts = NULL;
 | ||
|   VEC(tree, heap) *fnargs;
 | ||
|   unsigned i;
 | ||
| 
 | ||
|   assign_parms_initialize_all (&all);
 | ||
|   fnargs = assign_parms_augmented_arg_list (&all);
 | ||
| 
 | ||
|   FOR_EACH_VEC_ELT (tree, fnargs, i, parm)
 | ||
|     {
 | ||
|       struct assign_parm_data_one data;
 | ||
| 
 | ||
|       /* Extract the type of PARM; adjust it according to ABI.  */
 | ||
|       assign_parm_find_data_types (&all, parm, &data);
 | ||
| 
 | ||
|       /* Early out for errors and void parameters.  */
 | ||
|       if (data.passed_mode == VOIDmode || DECL_SIZE (parm) == NULL)
 | ||
| 	continue;
 | ||
| 
 | ||
|       /* Update info on where next arg arrives in registers.  */
 | ||
|       targetm.calls.function_arg_advance (all.args_so_far, data.promoted_mode,
 | ||
| 					  data.passed_type, data.named_arg);
 | ||
| 
 | ||
|       /* ??? Once upon a time variable_size stuffed parameter list
 | ||
| 	 SAVE_EXPRs (amongst others) onto a pending sizes list.  This
 | ||
| 	 turned out to be less than manageable in the gimple world.
 | ||
| 	 Now we have to hunt them down ourselves.  */
 | ||
|       walk_tree_without_duplicates (&data.passed_type,
 | ||
| 				    gimplify_parm_type, &stmts);
 | ||
| 
 | ||
|       if (TREE_CODE (DECL_SIZE_UNIT (parm)) != INTEGER_CST)
 | ||
| 	{
 | ||
| 	  gimplify_one_sizepos (&DECL_SIZE (parm), &stmts);
 | ||
| 	  gimplify_one_sizepos (&DECL_SIZE_UNIT (parm), &stmts);
 | ||
| 	}
 | ||
| 
 | ||
|       if (data.passed_pointer)
 | ||
| 	{
 | ||
|           tree type = TREE_TYPE (data.passed_type);
 | ||
| 	  if (reference_callee_copied (&all.args_so_far_v, TYPE_MODE (type),
 | ||
| 				       type, data.named_arg))
 | ||
| 	    {
 | ||
| 	      tree local, t;
 | ||
| 
 | ||
| 	      /* For constant-sized objects, this is trivial; for
 | ||
| 		 variable-sized objects, we have to play games.  */
 | ||
| 	      if (TREE_CODE (DECL_SIZE_UNIT (parm)) == INTEGER_CST
 | ||
| 		  && !(flag_stack_check == GENERIC_STACK_CHECK
 | ||
| 		       && compare_tree_int (DECL_SIZE_UNIT (parm),
 | ||
| 					    STACK_CHECK_MAX_VAR_SIZE) > 0))
 | ||
| 		{
 | ||
| 		  local = create_tmp_var (type, get_name (parm));
 | ||
| 		  DECL_IGNORED_P (local) = 0;
 | ||
| 		  /* If PARM was addressable, move that flag over
 | ||
| 		     to the local copy, as its address will be taken,
 | ||
| 		     not the PARMs.  Keep the parms address taken
 | ||
| 		     as we'll query that flag during gimplification.  */
 | ||
| 		  if (TREE_ADDRESSABLE (parm))
 | ||
| 		    TREE_ADDRESSABLE (local) = 1;
 | ||
| 		  else if (TREE_CODE (type) == COMPLEX_TYPE
 | ||
| 			   || TREE_CODE (type) == VECTOR_TYPE)
 | ||
| 		    DECL_GIMPLE_REG_P (local) = 1;
 | ||
| 		}
 | ||
| 	      else
 | ||
| 		{
 | ||
| 		  tree ptr_type, addr;
 | ||
| 
 | ||
| 		  ptr_type = build_pointer_type (type);
 | ||
| 		  addr = create_tmp_reg (ptr_type, get_name (parm));
 | ||
| 		  DECL_IGNORED_P (addr) = 0;
 | ||
| 		  local = build_fold_indirect_ref (addr);
 | ||
| 
 | ||
| 		  t = builtin_decl_explicit (BUILT_IN_ALLOCA_WITH_ALIGN);
 | ||
| 		  t = build_call_expr (t, 2, DECL_SIZE_UNIT (parm),
 | ||
| 				       size_int (DECL_ALIGN (parm)));
 | ||
| 
 | ||
| 		  /* The call has been built for a variable-sized object.  */
 | ||
| 		  CALL_ALLOCA_FOR_VAR_P (t) = 1;
 | ||
| 		  t = fold_convert (ptr_type, t);
 | ||
| 		  t = build2 (MODIFY_EXPR, TREE_TYPE (addr), addr, t);
 | ||
| 		  gimplify_and_add (t, &stmts);
 | ||
| 		}
 | ||
| 
 | ||
| 	      gimplify_assign (local, parm, &stmts);
 | ||
| 
 | ||
| 	      SET_DECL_VALUE_EXPR (parm, local);
 | ||
| 	      DECL_HAS_VALUE_EXPR_P (parm) = 1;
 | ||
| 	    }
 | ||
| 	}
 | ||
|     }
 | ||
| 
 | ||
|   VEC_free (tree, heap, fnargs);
 | ||
| 
 | ||
|   return stmts;
 | ||
| }
 | ||
| 
 | ||
| /* Compute the size and offset from the start of the stacked arguments for a
 | ||
|    parm passed in mode PASSED_MODE and with type TYPE.
 | ||
| 
 | ||
|    INITIAL_OFFSET_PTR points to the current offset into the stacked
 | ||
|    arguments.
 | ||
| 
 | ||
|    The starting offset and size for this parm are returned in
 | ||
|    LOCATE->OFFSET and LOCATE->SIZE, respectively.  When IN_REGS is
 | ||
|    nonzero, the offset is that of stack slot, which is returned in
 | ||
|    LOCATE->SLOT_OFFSET.  LOCATE->ALIGNMENT_PAD is the amount of
 | ||
|    padding required from the initial offset ptr to the stack slot.
 | ||
| 
 | ||
|    IN_REGS is nonzero if the argument will be passed in registers.  It will
 | ||
|    never be set if REG_PARM_STACK_SPACE is not defined.
 | ||
| 
 | ||
|    FNDECL is the function in which the argument was defined.
 | ||
| 
 | ||
|    There are two types of rounding that are done.  The first, controlled by
 | ||
|    TARGET_FUNCTION_ARG_BOUNDARY, forces the offset from the start of the
 | ||
|    argument list to be aligned to the specific boundary (in bits).  This
 | ||
|    rounding affects the initial and starting offsets, but not the argument
 | ||
|    size.
 | ||
| 
 | ||
|    The second, controlled by FUNCTION_ARG_PADDING and PARM_BOUNDARY,
 | ||
|    optionally rounds the size of the parm to PARM_BOUNDARY.  The
 | ||
|    initial offset is not affected by this rounding, while the size always
 | ||
|    is and the starting offset may be.  */
 | ||
| 
 | ||
| /*  LOCATE->OFFSET will be negative for ARGS_GROW_DOWNWARD case;
 | ||
|     INITIAL_OFFSET_PTR is positive because locate_and_pad_parm's
 | ||
|     callers pass in the total size of args so far as
 | ||
|     INITIAL_OFFSET_PTR.  LOCATE->SIZE is always positive.  */
 | ||
| 
 | ||
| void
 | ||
| locate_and_pad_parm (enum machine_mode passed_mode, tree type, int in_regs,
 | ||
| 		     int partial, tree fndecl ATTRIBUTE_UNUSED,
 | ||
| 		     struct args_size *initial_offset_ptr,
 | ||
| 		     struct locate_and_pad_arg_data *locate)
 | ||
| {
 | ||
|   tree sizetree;
 | ||
|   enum direction where_pad;
 | ||
|   unsigned int boundary, round_boundary;
 | ||
|   int reg_parm_stack_space = 0;
 | ||
|   int part_size_in_regs;
 | ||
| 
 | ||
| #ifdef REG_PARM_STACK_SPACE
 | ||
|   reg_parm_stack_space = REG_PARM_STACK_SPACE (fndecl);
 | ||
| 
 | ||
|   /* If we have found a stack parm before we reach the end of the
 | ||
|      area reserved for registers, skip that area.  */
 | ||
|   if (! in_regs)
 | ||
|     {
 | ||
|       if (reg_parm_stack_space > 0)
 | ||
| 	{
 | ||
| 	  if (initial_offset_ptr->var)
 | ||
| 	    {
 | ||
| 	      initial_offset_ptr->var
 | ||
| 		= size_binop (MAX_EXPR, ARGS_SIZE_TREE (*initial_offset_ptr),
 | ||
| 			      ssize_int (reg_parm_stack_space));
 | ||
| 	      initial_offset_ptr->constant = 0;
 | ||
| 	    }
 | ||
| 	  else if (initial_offset_ptr->constant < reg_parm_stack_space)
 | ||
| 	    initial_offset_ptr->constant = reg_parm_stack_space;
 | ||
| 	}
 | ||
|     }
 | ||
| #endif /* REG_PARM_STACK_SPACE */
 | ||
| 
 | ||
|   part_size_in_regs = (reg_parm_stack_space == 0 ? partial : 0);
 | ||
| 
 | ||
|   sizetree
 | ||
|     = type ? size_in_bytes (type) : size_int (GET_MODE_SIZE (passed_mode));
 | ||
|   where_pad = FUNCTION_ARG_PADDING (passed_mode, type);
 | ||
|   boundary = targetm.calls.function_arg_boundary (passed_mode, type);
 | ||
|   round_boundary = targetm.calls.function_arg_round_boundary (passed_mode,
 | ||
| 							      type);
 | ||
|   locate->where_pad = where_pad;
 | ||
| 
 | ||
|   /* Alignment can't exceed MAX_SUPPORTED_STACK_ALIGNMENT.  */
 | ||
|   if (boundary > MAX_SUPPORTED_STACK_ALIGNMENT)
 | ||
|     boundary = MAX_SUPPORTED_STACK_ALIGNMENT;
 | ||
| 
 | ||
|   locate->boundary = boundary;
 | ||
| 
 | ||
|   if (SUPPORTS_STACK_ALIGNMENT)
 | ||
|     {
 | ||
|       /* stack_alignment_estimated can't change after stack has been
 | ||
| 	 realigned.  */
 | ||
|       if (crtl->stack_alignment_estimated < boundary)
 | ||
|         {
 | ||
|           if (!crtl->stack_realign_processed)
 | ||
| 	    crtl->stack_alignment_estimated = boundary;
 | ||
| 	  else
 | ||
| 	    {
 | ||
| 	      /* If stack is realigned and stack alignment value
 | ||
| 		 hasn't been finalized, it is OK not to increase
 | ||
| 		 stack_alignment_estimated.  The bigger alignment
 | ||
| 		 requirement is recorded in stack_alignment_needed
 | ||
| 		 below.  */
 | ||
| 	      gcc_assert (!crtl->stack_realign_finalized
 | ||
| 			  && crtl->stack_realign_needed);
 | ||
| 	    }
 | ||
| 	}
 | ||
|     }
 | ||
| 
 | ||
|   /* Remember if the outgoing parameter requires extra alignment on the
 | ||
|      calling function side.  */
 | ||
|   if (crtl->stack_alignment_needed < boundary)
 | ||
|     crtl->stack_alignment_needed = boundary;
 | ||
|   if (crtl->preferred_stack_boundary < boundary)
 | ||
|     crtl->preferred_stack_boundary = boundary;
 | ||
| 
 | ||
| #ifdef ARGS_GROW_DOWNWARD
 | ||
|   locate->slot_offset.constant = -initial_offset_ptr->constant;
 | ||
|   if (initial_offset_ptr->var)
 | ||
|     locate->slot_offset.var = size_binop (MINUS_EXPR, ssize_int (0),
 | ||
| 					  initial_offset_ptr->var);
 | ||
| 
 | ||
|   {
 | ||
|     tree s2 = sizetree;
 | ||
|     if (where_pad != none
 | ||
| 	&& (!host_integerp (sizetree, 1)
 | ||
| 	    || (tree_low_cst (sizetree, 1) * BITS_PER_UNIT) % round_boundary))
 | ||
|       s2 = round_up (s2, round_boundary / BITS_PER_UNIT);
 | ||
|     SUB_PARM_SIZE (locate->slot_offset, s2);
 | ||
|   }
 | ||
| 
 | ||
|   locate->slot_offset.constant += part_size_in_regs;
 | ||
| 
 | ||
|   if (!in_regs
 | ||
| #ifdef REG_PARM_STACK_SPACE
 | ||
|       || REG_PARM_STACK_SPACE (fndecl) > 0
 | ||
| #endif
 | ||
|      )
 | ||
|     pad_to_arg_alignment (&locate->slot_offset, boundary,
 | ||
| 			  &locate->alignment_pad);
 | ||
| 
 | ||
|   locate->size.constant = (-initial_offset_ptr->constant
 | ||
| 			   - locate->slot_offset.constant);
 | ||
|   if (initial_offset_ptr->var)
 | ||
|     locate->size.var = size_binop (MINUS_EXPR,
 | ||
| 				   size_binop (MINUS_EXPR,
 | ||
| 					       ssize_int (0),
 | ||
| 					       initial_offset_ptr->var),
 | ||
| 				   locate->slot_offset.var);
 | ||
| 
 | ||
|   /* Pad_below needs the pre-rounded size to know how much to pad
 | ||
|      below.  */
 | ||
|   locate->offset = locate->slot_offset;
 | ||
|   if (where_pad == downward)
 | ||
|     pad_below (&locate->offset, passed_mode, sizetree);
 | ||
| 
 | ||
| #else /* !ARGS_GROW_DOWNWARD */
 | ||
|   if (!in_regs
 | ||
| #ifdef REG_PARM_STACK_SPACE
 | ||
|       || REG_PARM_STACK_SPACE (fndecl) > 0
 | ||
| #endif
 | ||
|       )
 | ||
|     pad_to_arg_alignment (initial_offset_ptr, boundary,
 | ||
| 			  &locate->alignment_pad);
 | ||
|   locate->slot_offset = *initial_offset_ptr;
 | ||
| 
 | ||
| #ifdef PUSH_ROUNDING
 | ||
|   if (passed_mode != BLKmode)
 | ||
|     sizetree = size_int (PUSH_ROUNDING (TREE_INT_CST_LOW (sizetree)));
 | ||
| #endif
 | ||
| 
 | ||
|   /* Pad_below needs the pre-rounded size to know how much to pad below
 | ||
|      so this must be done before rounding up.  */
 | ||
|   locate->offset = locate->slot_offset;
 | ||
|   if (where_pad == downward)
 | ||
|     pad_below (&locate->offset, passed_mode, sizetree);
 | ||
| 
 | ||
|   if (where_pad != none
 | ||
|       && (!host_integerp (sizetree, 1)
 | ||
| 	  || (tree_low_cst (sizetree, 1) * BITS_PER_UNIT) % round_boundary))
 | ||
|     sizetree = round_up (sizetree, round_boundary / BITS_PER_UNIT);
 | ||
| 
 | ||
|   ADD_PARM_SIZE (locate->size, sizetree);
 | ||
| 
 | ||
|   locate->size.constant -= part_size_in_regs;
 | ||
| #endif /* ARGS_GROW_DOWNWARD */
 | ||
| 
 | ||
| #ifdef FUNCTION_ARG_OFFSET
 | ||
|   locate->offset.constant += FUNCTION_ARG_OFFSET (passed_mode, type);
 | ||
| #endif
 | ||
| }
 | ||
| 
 | ||
| /* Round the stack offset in *OFFSET_PTR up to a multiple of BOUNDARY.
 | ||
|    BOUNDARY is measured in bits, but must be a multiple of a storage unit.  */
 | ||
| 
 | ||
| static void
 | ||
| pad_to_arg_alignment (struct args_size *offset_ptr, int boundary,
 | ||
| 		      struct args_size *alignment_pad)
 | ||
| {
 | ||
|   tree save_var = NULL_TREE;
 | ||
|   HOST_WIDE_INT save_constant = 0;
 | ||
|   int boundary_in_bytes = boundary / BITS_PER_UNIT;
 | ||
|   HOST_WIDE_INT sp_offset = STACK_POINTER_OFFSET;
 | ||
| 
 | ||
| #ifdef SPARC_STACK_BOUNDARY_HACK
 | ||
|   /* ??? The SPARC port may claim a STACK_BOUNDARY higher than
 | ||
|      the real alignment of %sp.  However, when it does this, the
 | ||
|      alignment of %sp+STACK_POINTER_OFFSET is STACK_BOUNDARY.  */
 | ||
|   if (SPARC_STACK_BOUNDARY_HACK)
 | ||
|     sp_offset = 0;
 | ||
| #endif
 | ||
| 
 | ||
|   if (boundary > PARM_BOUNDARY)
 | ||
|     {
 | ||
|       save_var = offset_ptr->var;
 | ||
|       save_constant = offset_ptr->constant;
 | ||
|     }
 | ||
| 
 | ||
|   alignment_pad->var = NULL_TREE;
 | ||
|   alignment_pad->constant = 0;
 | ||
| 
 | ||
|   if (boundary > BITS_PER_UNIT)
 | ||
|     {
 | ||
|       if (offset_ptr->var)
 | ||
| 	{
 | ||
| 	  tree sp_offset_tree = ssize_int (sp_offset);
 | ||
| 	  tree offset = size_binop (PLUS_EXPR,
 | ||
| 				    ARGS_SIZE_TREE (*offset_ptr),
 | ||
| 				    sp_offset_tree);
 | ||
| #ifdef ARGS_GROW_DOWNWARD
 | ||
| 	  tree rounded = round_down (offset, boundary / BITS_PER_UNIT);
 | ||
| #else
 | ||
| 	  tree rounded = round_up   (offset, boundary / BITS_PER_UNIT);
 | ||
| #endif
 | ||
| 
 | ||
| 	  offset_ptr->var = size_binop (MINUS_EXPR, rounded, sp_offset_tree);
 | ||
| 	  /* ARGS_SIZE_TREE includes constant term.  */
 | ||
| 	  offset_ptr->constant = 0;
 | ||
| 	  if (boundary > PARM_BOUNDARY)
 | ||
| 	    alignment_pad->var = size_binop (MINUS_EXPR, offset_ptr->var,
 | ||
| 					     save_var);
 | ||
| 	}
 | ||
|       else
 | ||
| 	{
 | ||
| 	  offset_ptr->constant = -sp_offset +
 | ||
| #ifdef ARGS_GROW_DOWNWARD
 | ||
| 	    FLOOR_ROUND (offset_ptr->constant + sp_offset, boundary_in_bytes);
 | ||
| #else
 | ||
| 	    CEIL_ROUND (offset_ptr->constant + sp_offset, boundary_in_bytes);
 | ||
| #endif
 | ||
| 	    if (boundary > PARM_BOUNDARY)
 | ||
| 	      alignment_pad->constant = offset_ptr->constant - save_constant;
 | ||
| 	}
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| static void
 | ||
| pad_below (struct args_size *offset_ptr, enum machine_mode passed_mode, tree sizetree)
 | ||
| {
 | ||
|   if (passed_mode != BLKmode)
 | ||
|     {
 | ||
|       if (GET_MODE_BITSIZE (passed_mode) % PARM_BOUNDARY)
 | ||
| 	offset_ptr->constant
 | ||
| 	  += (((GET_MODE_BITSIZE (passed_mode) + PARM_BOUNDARY - 1)
 | ||
| 	       / PARM_BOUNDARY * PARM_BOUNDARY / BITS_PER_UNIT)
 | ||
| 	      - GET_MODE_SIZE (passed_mode));
 | ||
|     }
 | ||
|   else
 | ||
|     {
 | ||
|       if (TREE_CODE (sizetree) != INTEGER_CST
 | ||
| 	  || (TREE_INT_CST_LOW (sizetree) * BITS_PER_UNIT) % PARM_BOUNDARY)
 | ||
| 	{
 | ||
| 	  /* Round the size up to multiple of PARM_BOUNDARY bits.  */
 | ||
| 	  tree s2 = round_up (sizetree, PARM_BOUNDARY / BITS_PER_UNIT);
 | ||
| 	  /* Add it in.  */
 | ||
| 	  ADD_PARM_SIZE (*offset_ptr, s2);
 | ||
| 	  SUB_PARM_SIZE (*offset_ptr, sizetree);
 | ||
| 	}
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| /* True if register REGNO was alive at a place where `setjmp' was
 | ||
|    called and was set more than once or is an argument.  Such regs may
 | ||
|    be clobbered by `longjmp'.  */
 | ||
| 
 | ||
| static bool
 | ||
| regno_clobbered_at_setjmp (bitmap setjmp_crosses, int regno)
 | ||
| {
 | ||
|   /* There appear to be cases where some local vars never reach the
 | ||
|      backend but have bogus regnos.  */
 | ||
|   if (regno >= max_reg_num ())
 | ||
|     return false;
 | ||
| 
 | ||
|   return ((REG_N_SETS (regno) > 1
 | ||
| 	   || REGNO_REG_SET_P (df_get_live_out (ENTRY_BLOCK_PTR), regno))
 | ||
| 	  && REGNO_REG_SET_P (setjmp_crosses, regno));
 | ||
| }
 | ||
| 
 | ||
| /* Walk the tree of blocks describing the binding levels within a
 | ||
|    function and warn about variables the might be killed by setjmp or
 | ||
|    vfork.  This is done after calling flow_analysis before register
 | ||
|    allocation since that will clobber the pseudo-regs to hard
 | ||
|    regs.  */
 | ||
| 
 | ||
| static void
 | ||
| setjmp_vars_warning (bitmap setjmp_crosses, tree block)
 | ||
| {
 | ||
|   tree decl, sub;
 | ||
| 
 | ||
|   for (decl = BLOCK_VARS (block); decl; decl = DECL_CHAIN (decl))
 | ||
|     {
 | ||
|       if (TREE_CODE (decl) == VAR_DECL
 | ||
| 	  && DECL_RTL_SET_P (decl)
 | ||
| 	  && REG_P (DECL_RTL (decl))
 | ||
| 	  && regno_clobbered_at_setjmp (setjmp_crosses, REGNO (DECL_RTL (decl))))
 | ||
| 	warning (OPT_Wclobbered, "variable %q+D might be clobbered by"
 | ||
|                  " %<longjmp%> or %<vfork%>", decl);
 | ||
|     }
 | ||
| 
 | ||
|   for (sub = BLOCK_SUBBLOCKS (block); sub; sub = BLOCK_CHAIN (sub))
 | ||
|     setjmp_vars_warning (setjmp_crosses, sub);
 | ||
| }
 | ||
| 
 | ||
| /* Do the appropriate part of setjmp_vars_warning
 | ||
|    but for arguments instead of local variables.  */
 | ||
| 
 | ||
| static void
 | ||
| setjmp_args_warning (bitmap setjmp_crosses)
 | ||
| {
 | ||
|   tree decl;
 | ||
|   for (decl = DECL_ARGUMENTS (current_function_decl);
 | ||
|        decl; decl = DECL_CHAIN (decl))
 | ||
|     if (DECL_RTL (decl) != 0
 | ||
| 	&& REG_P (DECL_RTL (decl))
 | ||
| 	&& regno_clobbered_at_setjmp (setjmp_crosses, REGNO (DECL_RTL (decl))))
 | ||
|       warning (OPT_Wclobbered,
 | ||
|                "argument %q+D might be clobbered by %<longjmp%> or %<vfork%>",
 | ||
| 	       decl);
 | ||
| }
 | ||
| 
 | ||
| /* Generate warning messages for variables live across setjmp.  */
 | ||
| 
 | ||
| void
 | ||
| generate_setjmp_warnings (void)
 | ||
| {
 | ||
|   bitmap setjmp_crosses = regstat_get_setjmp_crosses ();
 | ||
| 
 | ||
|   if (n_basic_blocks == NUM_FIXED_BLOCKS
 | ||
|       || bitmap_empty_p (setjmp_crosses))
 | ||
|     return;
 | ||
| 
 | ||
|   setjmp_vars_warning (setjmp_crosses, DECL_INITIAL (current_function_decl));
 | ||
|   setjmp_args_warning (setjmp_crosses);
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| /* Reverse the order of elements in the fragment chain T of blocks,
 | ||
|    and return the new head of the chain (old last element).
 | ||
|    In addition to that clear BLOCK_SAME_RANGE flags when needed
 | ||
|    and adjust BLOCK_SUPERCONTEXT from the super fragment to
 | ||
|    its super fragment origin.  */
 | ||
| 
 | ||
| static tree
 | ||
| block_fragments_nreverse (tree t)
 | ||
| {
 | ||
|   tree prev = 0, block, next, prev_super = 0;
 | ||
|   tree super = BLOCK_SUPERCONTEXT (t);
 | ||
|   if (BLOCK_FRAGMENT_ORIGIN (super))
 | ||
|     super = BLOCK_FRAGMENT_ORIGIN (super);
 | ||
|   for (block = t; block; block = next)
 | ||
|     {
 | ||
|       next = BLOCK_FRAGMENT_CHAIN (block);
 | ||
|       BLOCK_FRAGMENT_CHAIN (block) = prev;
 | ||
|       if ((prev && !BLOCK_SAME_RANGE (prev))
 | ||
| 	  || (BLOCK_FRAGMENT_CHAIN (BLOCK_SUPERCONTEXT (block))
 | ||
| 	      != prev_super))
 | ||
| 	BLOCK_SAME_RANGE (block) = 0;
 | ||
|       prev_super = BLOCK_SUPERCONTEXT (block);
 | ||
|       BLOCK_SUPERCONTEXT (block) = super;
 | ||
|       prev = block;
 | ||
|     }
 | ||
|   t = BLOCK_FRAGMENT_ORIGIN (t);
 | ||
|   if (BLOCK_FRAGMENT_CHAIN (BLOCK_SUPERCONTEXT (t))
 | ||
|       != prev_super)
 | ||
|     BLOCK_SAME_RANGE (t) = 0;
 | ||
|   BLOCK_SUPERCONTEXT (t) = super;
 | ||
|   return prev;
 | ||
| }
 | ||
| 
 | ||
| /* Reverse the order of elements in the chain T of blocks,
 | ||
|    and return the new head of the chain (old last element).
 | ||
|    Also do the same on subblocks and reverse the order of elements
 | ||
|    in BLOCK_FRAGMENT_CHAIN as well.  */
 | ||
| 
 | ||
| static tree
 | ||
| blocks_nreverse_all (tree t)
 | ||
| {
 | ||
|   tree prev = 0, block, next;
 | ||
|   for (block = t; block; block = next)
 | ||
|     {
 | ||
|       next = BLOCK_CHAIN (block);
 | ||
|       BLOCK_CHAIN (block) = prev;
 | ||
|       if (BLOCK_FRAGMENT_CHAIN (block)
 | ||
| 	  && BLOCK_FRAGMENT_ORIGIN (block) == NULL_TREE)
 | ||
| 	{
 | ||
| 	  BLOCK_FRAGMENT_CHAIN (block)
 | ||
| 	    = block_fragments_nreverse (BLOCK_FRAGMENT_CHAIN (block));
 | ||
| 	  if (!BLOCK_SAME_RANGE (BLOCK_FRAGMENT_CHAIN (block)))
 | ||
| 	    BLOCK_SAME_RANGE (block) = 0;
 | ||
| 	}
 | ||
|       BLOCK_SUBBLOCKS (block) = blocks_nreverse_all (BLOCK_SUBBLOCKS (block));
 | ||
|       prev = block;
 | ||
|     }
 | ||
|   return prev;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| /* Identify BLOCKs referenced by more than one NOTE_INSN_BLOCK_{BEG,END},
 | ||
|    and create duplicate blocks.  */
 | ||
| /* ??? Need an option to either create block fragments or to create
 | ||
|    abstract origin duplicates of a source block.  It really depends
 | ||
|    on what optimization has been performed.  */
 | ||
| 
 | ||
| void
 | ||
| reorder_blocks (void)
 | ||
| {
 | ||
|   tree block = DECL_INITIAL (current_function_decl);
 | ||
|   VEC(tree,heap) *block_stack;
 | ||
| 
 | ||
|   if (block == NULL_TREE)
 | ||
|     return;
 | ||
| 
 | ||
|   block_stack = VEC_alloc (tree, heap, 10);
 | ||
| 
 | ||
|   /* Reset the TREE_ASM_WRITTEN bit for all blocks.  */
 | ||
|   clear_block_marks (block);
 | ||
| 
 | ||
|   /* Prune the old trees away, so that they don't get in the way.  */
 | ||
|   BLOCK_SUBBLOCKS (block) = NULL_TREE;
 | ||
|   BLOCK_CHAIN (block) = NULL_TREE;
 | ||
| 
 | ||
|   /* Recreate the block tree from the note nesting.  */
 | ||
|   reorder_blocks_1 (get_insns (), block, &block_stack);
 | ||
|   BLOCK_SUBBLOCKS (block) = blocks_nreverse_all (BLOCK_SUBBLOCKS (block));
 | ||
| 
 | ||
|   VEC_free (tree, heap, block_stack);
 | ||
| }
 | ||
| 
 | ||
| /* Helper function for reorder_blocks.  Reset TREE_ASM_WRITTEN.  */
 | ||
| 
 | ||
| void
 | ||
| clear_block_marks (tree block)
 | ||
| {
 | ||
|   while (block)
 | ||
|     {
 | ||
|       TREE_ASM_WRITTEN (block) = 0;
 | ||
|       clear_block_marks (BLOCK_SUBBLOCKS (block));
 | ||
|       block = BLOCK_CHAIN (block);
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| static void
 | ||
| reorder_blocks_1 (rtx insns, tree current_block, VEC(tree,heap) **p_block_stack)
 | ||
| {
 | ||
|   rtx insn;
 | ||
|   tree prev_beg = NULL_TREE, prev_end = NULL_TREE;
 | ||
| 
 | ||
|   for (insn = insns; insn; insn = NEXT_INSN (insn))
 | ||
|     {
 | ||
|       if (NOTE_P (insn))
 | ||
| 	{
 | ||
| 	  if (NOTE_KIND (insn) == NOTE_INSN_BLOCK_BEG)
 | ||
| 	    {
 | ||
| 	      tree block = NOTE_BLOCK (insn);
 | ||
| 	      tree origin;
 | ||
| 
 | ||
| 	      gcc_assert (BLOCK_FRAGMENT_ORIGIN (block) == NULL_TREE);
 | ||
| 	      origin = block;
 | ||
| 
 | ||
| 	      if (prev_end)
 | ||
| 		BLOCK_SAME_RANGE (prev_end) = 0;
 | ||
| 	      prev_end = NULL_TREE;
 | ||
| 
 | ||
| 	      /* If we have seen this block before, that means it now
 | ||
| 		 spans multiple address regions.  Create a new fragment.  */
 | ||
| 	      if (TREE_ASM_WRITTEN (block))
 | ||
| 		{
 | ||
| 		  tree new_block = copy_node (block);
 | ||
| 
 | ||
| 		  BLOCK_SAME_RANGE (new_block) = 0;
 | ||
| 		  BLOCK_FRAGMENT_ORIGIN (new_block) = origin;
 | ||
| 		  BLOCK_FRAGMENT_CHAIN (new_block)
 | ||
| 		    = BLOCK_FRAGMENT_CHAIN (origin);
 | ||
| 		  BLOCK_FRAGMENT_CHAIN (origin) = new_block;
 | ||
| 
 | ||
| 		  NOTE_BLOCK (insn) = new_block;
 | ||
| 		  block = new_block;
 | ||
| 		}
 | ||
| 
 | ||
| 	      if (prev_beg == current_block && prev_beg)
 | ||
| 		BLOCK_SAME_RANGE (block) = 1;
 | ||
| 
 | ||
| 	      prev_beg = origin;
 | ||
| 
 | ||
| 	      BLOCK_SUBBLOCKS (block) = 0;
 | ||
| 	      TREE_ASM_WRITTEN (block) = 1;
 | ||
| 	      /* When there's only one block for the entire function,
 | ||
| 		 current_block == block and we mustn't do this, it
 | ||
| 		 will cause infinite recursion.  */
 | ||
| 	      if (block != current_block)
 | ||
| 		{
 | ||
| 		  tree super;
 | ||
| 		  if (block != origin)
 | ||
| 		    gcc_assert (BLOCK_SUPERCONTEXT (origin) == current_block
 | ||
| 				|| BLOCK_FRAGMENT_ORIGIN (BLOCK_SUPERCONTEXT
 | ||
| 								      (origin))
 | ||
| 				   == current_block);
 | ||
| 		  if (VEC_empty (tree, *p_block_stack))
 | ||
| 		    super = current_block;
 | ||
| 		  else
 | ||
| 		    {
 | ||
| 		      super = VEC_last (tree, *p_block_stack);
 | ||
| 		      gcc_assert (super == current_block
 | ||
| 				  || BLOCK_FRAGMENT_ORIGIN (super)
 | ||
| 				     == current_block);
 | ||
| 		    }
 | ||
| 		  BLOCK_SUPERCONTEXT (block) = super;
 | ||
| 		  BLOCK_CHAIN (block) = BLOCK_SUBBLOCKS (current_block);
 | ||
| 		  BLOCK_SUBBLOCKS (current_block) = block;
 | ||
| 		  current_block = origin;
 | ||
| 		}
 | ||
| 	      VEC_safe_push (tree, heap, *p_block_stack, block);
 | ||
| 	    }
 | ||
| 	  else if (NOTE_KIND (insn) == NOTE_INSN_BLOCK_END)
 | ||
| 	    {
 | ||
| 	      NOTE_BLOCK (insn) = VEC_pop (tree, *p_block_stack);
 | ||
| 	      current_block = BLOCK_SUPERCONTEXT (current_block);
 | ||
| 	      if (BLOCK_FRAGMENT_ORIGIN (current_block))
 | ||
| 		current_block = BLOCK_FRAGMENT_ORIGIN (current_block);
 | ||
| 	      prev_beg = NULL_TREE;
 | ||
| 	      prev_end = BLOCK_SAME_RANGE (NOTE_BLOCK (insn))
 | ||
| 			 ? NOTE_BLOCK (insn) : NULL_TREE;
 | ||
| 	    }
 | ||
| 	}
 | ||
|       else
 | ||
| 	{
 | ||
| 	  prev_beg = NULL_TREE;
 | ||
| 	  if (prev_end)
 | ||
| 	    BLOCK_SAME_RANGE (prev_end) = 0;
 | ||
| 	  prev_end = NULL_TREE;
 | ||
| 	}
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /* Reverse the order of elements in the chain T of blocks,
 | ||
|    and return the new head of the chain (old last element).  */
 | ||
| 
 | ||
| tree
 | ||
| blocks_nreverse (tree t)
 | ||
| {
 | ||
|   tree prev = 0, block, next;
 | ||
|   for (block = t; block; block = next)
 | ||
|     {
 | ||
|       next = BLOCK_CHAIN (block);
 | ||
|       BLOCK_CHAIN (block) = prev;
 | ||
|       prev = block;
 | ||
|     }
 | ||
|   return prev;
 | ||
| }
 | ||
| 
 | ||
| /* Concatenate two chains of blocks (chained through BLOCK_CHAIN)
 | ||
|    by modifying the last node in chain 1 to point to chain 2.  */
 | ||
| 
 | ||
| tree
 | ||
| block_chainon (tree op1, tree op2)
 | ||
| {
 | ||
|   tree t1;
 | ||
| 
 | ||
|   if (!op1)
 | ||
|     return op2;
 | ||
|   if (!op2)
 | ||
|     return op1;
 | ||
| 
 | ||
|   for (t1 = op1; BLOCK_CHAIN (t1); t1 = BLOCK_CHAIN (t1))
 | ||
|     continue;
 | ||
|   BLOCK_CHAIN (t1) = op2;
 | ||
| 
 | ||
| #ifdef ENABLE_TREE_CHECKING
 | ||
|   {
 | ||
|     tree t2;
 | ||
|     for (t2 = op2; t2; t2 = BLOCK_CHAIN (t2))
 | ||
|       gcc_assert (t2 != t1);
 | ||
|   }
 | ||
| #endif
 | ||
| 
 | ||
|   return op1;
 | ||
| }
 | ||
| 
 | ||
| /* Count the subblocks of the list starting with BLOCK.  If VECTOR is
 | ||
|    non-NULL, list them all into VECTOR, in a depth-first preorder
 | ||
|    traversal of the block tree.  Also clear TREE_ASM_WRITTEN in all
 | ||
|    blocks.  */
 | ||
| 
 | ||
| static int
 | ||
| all_blocks (tree block, tree *vector)
 | ||
| {
 | ||
|   int n_blocks = 0;
 | ||
| 
 | ||
|   while (block)
 | ||
|     {
 | ||
|       TREE_ASM_WRITTEN (block) = 0;
 | ||
| 
 | ||
|       /* Record this block.  */
 | ||
|       if (vector)
 | ||
| 	vector[n_blocks] = block;
 | ||
| 
 | ||
|       ++n_blocks;
 | ||
| 
 | ||
|       /* Record the subblocks, and their subblocks...  */
 | ||
|       n_blocks += all_blocks (BLOCK_SUBBLOCKS (block),
 | ||
| 			      vector ? vector + n_blocks : 0);
 | ||
|       block = BLOCK_CHAIN (block);
 | ||
|     }
 | ||
| 
 | ||
|   return n_blocks;
 | ||
| }
 | ||
| 
 | ||
| /* Return a vector containing all the blocks rooted at BLOCK.  The
 | ||
|    number of elements in the vector is stored in N_BLOCKS_P.  The
 | ||
|    vector is dynamically allocated; it is the caller's responsibility
 | ||
|    to call `free' on the pointer returned.  */
 | ||
| 
 | ||
| static tree *
 | ||
| get_block_vector (tree block, int *n_blocks_p)
 | ||
| {
 | ||
|   tree *block_vector;
 | ||
| 
 | ||
|   *n_blocks_p = all_blocks (block, NULL);
 | ||
|   block_vector = XNEWVEC (tree, *n_blocks_p);
 | ||
|   all_blocks (block, block_vector);
 | ||
| 
 | ||
|   return block_vector;
 | ||
| }
 | ||
| 
 | ||
| static GTY(()) int next_block_index = 2;
 | ||
| 
 | ||
| /* Set BLOCK_NUMBER for all the blocks in FN.  */
 | ||
| 
 | ||
| void
 | ||
| number_blocks (tree fn)
 | ||
| {
 | ||
|   int i;
 | ||
|   int n_blocks;
 | ||
|   tree *block_vector;
 | ||
| 
 | ||
|   /* For SDB and XCOFF debugging output, we start numbering the blocks
 | ||
|      from 1 within each function, rather than keeping a running
 | ||
|      count.  */
 | ||
| #if defined (SDB_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO)
 | ||
|   if (write_symbols == SDB_DEBUG || write_symbols == XCOFF_DEBUG)
 | ||
|     next_block_index = 1;
 | ||
| #endif
 | ||
| 
 | ||
|   block_vector = get_block_vector (DECL_INITIAL (fn), &n_blocks);
 | ||
| 
 | ||
|   /* The top-level BLOCK isn't numbered at all.  */
 | ||
|   for (i = 1; i < n_blocks; ++i)
 | ||
|     /* We number the blocks from two.  */
 | ||
|     BLOCK_NUMBER (block_vector[i]) = next_block_index++;
 | ||
| 
 | ||
|   free (block_vector);
 | ||
| 
 | ||
|   return;
 | ||
| }
 | ||
| 
 | ||
| /* If VAR is present in a subblock of BLOCK, return the subblock.  */
 | ||
| 
 | ||
| DEBUG_FUNCTION tree
 | ||
| debug_find_var_in_block_tree (tree var, tree block)
 | ||
| {
 | ||
|   tree t;
 | ||
| 
 | ||
|   for (t = BLOCK_VARS (block); t; t = TREE_CHAIN (t))
 | ||
|     if (t == var)
 | ||
|       return block;
 | ||
| 
 | ||
|   for (t = BLOCK_SUBBLOCKS (block); t; t = TREE_CHAIN (t))
 | ||
|     {
 | ||
|       tree ret = debug_find_var_in_block_tree (var, t);
 | ||
|       if (ret)
 | ||
| 	return ret;
 | ||
|     }
 | ||
| 
 | ||
|   return NULL_TREE;
 | ||
| }
 | ||
| 
 | ||
| /* Keep track of whether we're in a dummy function context.  If we are,
 | ||
|    we don't want to invoke the set_current_function hook, because we'll
 | ||
|    get into trouble if the hook calls target_reinit () recursively or
 | ||
|    when the initial initialization is not yet complete.  */
 | ||
| 
 | ||
| static bool in_dummy_function;
 | ||
| 
 | ||
| /* Invoke the target hook when setting cfun.  Update the optimization options
 | ||
|    if the function uses different options than the default.  */
 | ||
| 
 | ||
| static void
 | ||
| invoke_set_current_function_hook (tree fndecl)
 | ||
| {
 | ||
|   if (!in_dummy_function)
 | ||
|     {
 | ||
|       tree opts = ((fndecl)
 | ||
| 		   ? DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fndecl)
 | ||
| 		   : optimization_default_node);
 | ||
| 
 | ||
|       if (!opts)
 | ||
| 	opts = optimization_default_node;
 | ||
| 
 | ||
|       /* Change optimization options if needed.  */
 | ||
|       if (optimization_current_node != opts)
 | ||
| 	{
 | ||
| 	  optimization_current_node = opts;
 | ||
| 	  cl_optimization_restore (&global_options, TREE_OPTIMIZATION (opts));
 | ||
| 	}
 | ||
| 
 | ||
|       targetm.set_current_function (fndecl);
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /* cfun should never be set directly; use this function.  */
 | ||
| 
 | ||
| void
 | ||
| set_cfun (struct function *new_cfun)
 | ||
| {
 | ||
|   if (cfun != new_cfun)
 | ||
|     {
 | ||
|       cfun = new_cfun;
 | ||
|       invoke_set_current_function_hook (new_cfun ? new_cfun->decl : NULL_TREE);
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /* Initialized with NOGC, making this poisonous to the garbage collector.  */
 | ||
| 
 | ||
| static VEC(function_p,heap) *cfun_stack;
 | ||
| 
 | ||
| /* Push the current cfun onto the stack, and set cfun to new_cfun.  */
 | ||
| 
 | ||
| void
 | ||
| push_cfun (struct function *new_cfun)
 | ||
| {
 | ||
|   VEC_safe_push (function_p, heap, cfun_stack, cfun);
 | ||
|   set_cfun (new_cfun);
 | ||
| }
 | ||
| 
 | ||
| /* Pop cfun from the stack.  */
 | ||
| 
 | ||
| void
 | ||
| pop_cfun (void)
 | ||
| {
 | ||
|   struct function *new_cfun = VEC_pop (function_p, cfun_stack);
 | ||
|   set_cfun (new_cfun);
 | ||
| }
 | ||
| 
 | ||
| /* Return value of funcdef and increase it.  */
 | ||
| int
 | ||
| get_next_funcdef_no (void)
 | ||
| {
 | ||
|   return funcdef_no++;
 | ||
| }
 | ||
| 
 | ||
| /* Return value of funcdef.  */
 | ||
| int
 | ||
| get_last_funcdef_no (void)
 | ||
| {
 | ||
|   return funcdef_no;
 | ||
| }
 | ||
| 
 | ||
| /* Allocate a function structure for FNDECL and set its contents
 | ||
|    to the defaults.  Set cfun to the newly-allocated object.
 | ||
|    Some of the helper functions invoked during initialization assume
 | ||
|    that cfun has already been set.  Therefore, assign the new object
 | ||
|    directly into cfun and invoke the back end hook explicitly at the
 | ||
|    very end, rather than initializing a temporary and calling set_cfun
 | ||
|    on it.
 | ||
| 
 | ||
|    ABSTRACT_P is true if this is a function that will never be seen by
 | ||
|    the middle-end.  Such functions are front-end concepts (like C++
 | ||
|    function templates) that do not correspond directly to functions
 | ||
|    placed in object files.  */
 | ||
| 
 | ||
| void
 | ||
| allocate_struct_function (tree fndecl, bool abstract_p)
 | ||
| {
 | ||
|   tree result;
 | ||
|   tree fntype = fndecl ? TREE_TYPE (fndecl) : NULL_TREE;
 | ||
| 
 | ||
|   cfun = ggc_alloc_cleared_function ();
 | ||
| 
 | ||
|   init_eh_for_function ();
 | ||
| 
 | ||
|   if (init_machine_status)
 | ||
|     cfun->machine = (*init_machine_status) ();
 | ||
| 
 | ||
| #ifdef OVERRIDE_ABI_FORMAT
 | ||
|   OVERRIDE_ABI_FORMAT (fndecl);
 | ||
| #endif
 | ||
| 
 | ||
|   invoke_set_current_function_hook (fndecl);
 | ||
| 
 | ||
|   if (fndecl != NULL_TREE)
 | ||
|     {
 | ||
|       DECL_STRUCT_FUNCTION (fndecl) = cfun;
 | ||
|       cfun->decl = fndecl;
 | ||
|       current_function_funcdef_no = get_next_funcdef_no ();
 | ||
| 
 | ||
|       result = DECL_RESULT (fndecl);
 | ||
|       if (!abstract_p && aggregate_value_p (result, fndecl))
 | ||
| 	{
 | ||
| #ifdef PCC_STATIC_STRUCT_RETURN
 | ||
| 	  cfun->returns_pcc_struct = 1;
 | ||
| #endif
 | ||
| 	  cfun->returns_struct = 1;
 | ||
| 	}
 | ||
| 
 | ||
|       cfun->stdarg = stdarg_p (fntype);
 | ||
| 
 | ||
|       /* Assume all registers in stdarg functions need to be saved.  */
 | ||
|       cfun->va_list_gpr_size = VA_LIST_MAX_GPR_SIZE;
 | ||
|       cfun->va_list_fpr_size = VA_LIST_MAX_FPR_SIZE;
 | ||
| 
 | ||
|       /* ??? This could be set on a per-function basis by the front-end
 | ||
|          but is this worth the hassle?  */
 | ||
|       cfun->can_throw_non_call_exceptions = flag_non_call_exceptions;
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /* This is like allocate_struct_function, but pushes a new cfun for FNDECL
 | ||
|    instead of just setting it.  */
 | ||
| 
 | ||
| void
 | ||
| push_struct_function (tree fndecl)
 | ||
| {
 | ||
|   VEC_safe_push (function_p, heap, cfun_stack, cfun);
 | ||
|   allocate_struct_function (fndecl, false);
 | ||
| }
 | ||
| 
 | ||
| /* Reset crtl and other non-struct-function variables to defaults as
 | ||
|    appropriate for emitting rtl at the start of a function.  */
 | ||
| 
 | ||
| static void
 | ||
| prepare_function_start (void)
 | ||
| {
 | ||
|   gcc_assert (!crtl->emit.x_last_insn);
 | ||
|   init_temp_slots ();
 | ||
|   init_emit ();
 | ||
|   init_varasm_status ();
 | ||
|   init_expr ();
 | ||
|   default_rtl_profile ();
 | ||
| 
 | ||
|   if (flag_stack_usage_info)
 | ||
|     {
 | ||
|       cfun->su = ggc_alloc_cleared_stack_usage ();
 | ||
|       cfun->su->static_stack_size = -1;
 | ||
|     }
 | ||
| 
 | ||
|   cse_not_expected = ! optimize;
 | ||
| 
 | ||
|   /* Caller save not needed yet.  */
 | ||
|   caller_save_needed = 0;
 | ||
| 
 | ||
|   /* We haven't done register allocation yet.  */
 | ||
|   reg_renumber = 0;
 | ||
| 
 | ||
|   /* Indicate that we have not instantiated virtual registers yet.  */
 | ||
|   virtuals_instantiated = 0;
 | ||
| 
 | ||
|   /* Indicate that we want CONCATs now.  */
 | ||
|   generating_concat_p = 1;
 | ||
| 
 | ||
|   /* Indicate we have no need of a frame pointer yet.  */
 | ||
|   frame_pointer_needed = 0;
 | ||
| }
 | ||
| 
 | ||
| /* Initialize the rtl expansion mechanism so that we can do simple things
 | ||
|    like generate sequences.  This is used to provide a context during global
 | ||
|    initialization of some passes.  You must call expand_dummy_function_end
 | ||
|    to exit this context.  */
 | ||
| 
 | ||
| void
 | ||
| init_dummy_function_start (void)
 | ||
| {
 | ||
|   gcc_assert (!in_dummy_function);
 | ||
|   in_dummy_function = true;
 | ||
|   push_struct_function (NULL_TREE);
 | ||
|   prepare_function_start ();
 | ||
| }
 | ||
| 
 | ||
| /* Generate RTL for the start of the function SUBR (a FUNCTION_DECL tree node)
 | ||
|    and initialize static variables for generating RTL for the statements
 | ||
|    of the function.  */
 | ||
| 
 | ||
| void
 | ||
| init_function_start (tree subr)
 | ||
| {
 | ||
|   if (subr && DECL_STRUCT_FUNCTION (subr))
 | ||
|     set_cfun (DECL_STRUCT_FUNCTION (subr));
 | ||
|   else
 | ||
|     allocate_struct_function (subr, false);
 | ||
|   prepare_function_start ();
 | ||
|   decide_function_section (subr);
 | ||
| 
 | ||
|   /* Warn if this value is an aggregate type,
 | ||
|      regardless of which calling convention we are using for it.  */
 | ||
|   if (AGGREGATE_TYPE_P (TREE_TYPE (DECL_RESULT (subr))))
 | ||
|     warning (OPT_Waggregate_return, "function returns an aggregate");
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| void
 | ||
| expand_main_function (void)
 | ||
| {
 | ||
| #if (defined(INVOKE__main)				\
 | ||
|      || (!defined(HAS_INIT_SECTION)			\
 | ||
| 	 && !defined(INIT_SECTION_ASM_OP)		\
 | ||
| 	 && !defined(INIT_ARRAY_SECTION_ASM_OP)))
 | ||
|   emit_library_call (init_one_libfunc (NAME__MAIN), LCT_NORMAL, VOIDmode, 0);
 | ||
| #endif
 | ||
| }
 | ||
| 
 | ||
| /* Expand code to initialize the stack_protect_guard.  This is invoked at
 | ||
|    the beginning of a function to be protected.  */
 | ||
| 
 | ||
| #ifndef HAVE_stack_protect_set
 | ||
| # define HAVE_stack_protect_set		0
 | ||
| # define gen_stack_protect_set(x,y)	(gcc_unreachable (), NULL_RTX)
 | ||
| #endif
 | ||
| 
 | ||
| void
 | ||
| stack_protect_prologue (void)
 | ||
| {
 | ||
|   tree guard_decl = targetm.stack_protect_guard ();
 | ||
|   rtx x, y;
 | ||
| 
 | ||
|   x = expand_normal (crtl->stack_protect_guard);
 | ||
|   y = expand_normal (guard_decl);
 | ||
| 
 | ||
|   /* Allow the target to copy from Y to X without leaking Y into a
 | ||
|      register.  */
 | ||
|   if (HAVE_stack_protect_set)
 | ||
|     {
 | ||
|       rtx insn = gen_stack_protect_set (x, y);
 | ||
|       if (insn)
 | ||
| 	{
 | ||
| 	  emit_insn (insn);
 | ||
| 	  return;
 | ||
| 	}
 | ||
|     }
 | ||
| 
 | ||
|   /* Otherwise do a straight move.  */
 | ||
|   emit_move_insn (x, y);
 | ||
| }
 | ||
| 
 | ||
| /* Expand code to verify the stack_protect_guard.  This is invoked at
 | ||
|    the end of a function to be protected.  */
 | ||
| 
 | ||
| #ifndef HAVE_stack_protect_test
 | ||
| # define HAVE_stack_protect_test		0
 | ||
| # define gen_stack_protect_test(x, y, z)	(gcc_unreachable (), NULL_RTX)
 | ||
| #endif
 | ||
| 
 | ||
| void
 | ||
| stack_protect_epilogue (void)
 | ||
| {
 | ||
|   tree guard_decl = targetm.stack_protect_guard ();
 | ||
|   rtx label = gen_label_rtx ();
 | ||
|   rtx x, y, tmp;
 | ||
| 
 | ||
|   x = expand_normal (crtl->stack_protect_guard);
 | ||
|   y = expand_normal (guard_decl);
 | ||
| 
 | ||
|   /* Allow the target to compare Y with X without leaking either into
 | ||
|      a register.  */
 | ||
|   switch (HAVE_stack_protect_test != 0)
 | ||
|     {
 | ||
|     case 1:
 | ||
|       tmp = gen_stack_protect_test (x, y, label);
 | ||
|       if (tmp)
 | ||
| 	{
 | ||
| 	  emit_insn (tmp);
 | ||
| 	  break;
 | ||
| 	}
 | ||
|       /* FALLTHRU */
 | ||
| 
 | ||
|     default:
 | ||
|       emit_cmp_and_jump_insns (x, y, EQ, NULL_RTX, ptr_mode, 1, label);
 | ||
|       break;
 | ||
|     }
 | ||
| 
 | ||
|   /* The noreturn predictor has been moved to the tree level.  The rtl-level
 | ||
|      predictors estimate this branch about 20%, which isn't enough to get
 | ||
|      things moved out of line.  Since this is the only extant case of adding
 | ||
|      a noreturn function at the rtl level, it doesn't seem worth doing ought
 | ||
|      except adding the prediction by hand.  */
 | ||
|   tmp = get_last_insn ();
 | ||
|   if (JUMP_P (tmp))
 | ||
|     predict_insn_def (tmp, PRED_NORETURN, TAKEN);
 | ||
| 
 | ||
|   expand_call (targetm.stack_protect_fail (), NULL_RTX, /*ignore=*/true);
 | ||
|   free_temp_slots ();
 | ||
|   emit_label (label);
 | ||
| }
 | ||
| 
 | ||
| /* Start the RTL for a new function, and set variables used for
 | ||
|    emitting RTL.
 | ||
|    SUBR is the FUNCTION_DECL node.
 | ||
|    PARMS_HAVE_CLEANUPS is nonzero if there are cleanups associated with
 | ||
|    the function's parameters, which must be run at any return statement.  */
 | ||
| 
 | ||
| void
 | ||
| expand_function_start (tree subr)
 | ||
| {
 | ||
|   /* Make sure volatile mem refs aren't considered
 | ||
|      valid operands of arithmetic insns.  */
 | ||
|   init_recog_no_volatile ();
 | ||
| 
 | ||
|   crtl->profile
 | ||
|     = (profile_flag
 | ||
|        && ! DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (subr));
 | ||
| 
 | ||
|   crtl->limit_stack
 | ||
|     = (stack_limit_rtx != NULL_RTX && ! DECL_NO_LIMIT_STACK (subr));
 | ||
| 
 | ||
|   /* Make the label for return statements to jump to.  Do not special
 | ||
|      case machines with special return instructions -- they will be
 | ||
|      handled later during jump, ifcvt, or epilogue creation.  */
 | ||
|   return_label = gen_label_rtx ();
 | ||
| 
 | ||
|   /* Initialize rtx used to return the value.  */
 | ||
|   /* Do this before assign_parms so that we copy the struct value address
 | ||
|      before any library calls that assign parms might generate.  */
 | ||
| 
 | ||
|   /* Decide whether to return the value in memory or in a register.  */
 | ||
|   if (aggregate_value_p (DECL_RESULT (subr), subr))
 | ||
|     {
 | ||
|       /* Returning something that won't go in a register.  */
 | ||
|       rtx value_address = 0;
 | ||
| 
 | ||
| #ifdef PCC_STATIC_STRUCT_RETURN
 | ||
|       if (cfun->returns_pcc_struct)
 | ||
| 	{
 | ||
| 	  int size = int_size_in_bytes (TREE_TYPE (DECL_RESULT (subr)));
 | ||
| 	  value_address = assemble_static_space (size);
 | ||
| 	}
 | ||
|       else
 | ||
| #endif
 | ||
| 	{
 | ||
| 	  rtx sv = targetm.calls.struct_value_rtx (TREE_TYPE (subr), 2);
 | ||
| 	  /* Expect to be passed the address of a place to store the value.
 | ||
| 	     If it is passed as an argument, assign_parms will take care of
 | ||
| 	     it.  */
 | ||
| 	  if (sv)
 | ||
| 	    {
 | ||
| 	      value_address = gen_reg_rtx (Pmode);
 | ||
| 	      emit_move_insn (value_address, sv);
 | ||
| 	    }
 | ||
| 	}
 | ||
|       if (value_address)
 | ||
| 	{
 | ||
| 	  rtx x = value_address;
 | ||
| 	  if (!DECL_BY_REFERENCE (DECL_RESULT (subr)))
 | ||
| 	    {
 | ||
| 	      x = gen_rtx_MEM (DECL_MODE (DECL_RESULT (subr)), x);
 | ||
| 	      set_mem_attributes (x, DECL_RESULT (subr), 1);
 | ||
| 	    }
 | ||
| 	  SET_DECL_RTL (DECL_RESULT (subr), x);
 | ||
| 	}
 | ||
|     }
 | ||
|   else if (DECL_MODE (DECL_RESULT (subr)) == VOIDmode)
 | ||
|     /* If return mode is void, this decl rtl should not be used.  */
 | ||
|     SET_DECL_RTL (DECL_RESULT (subr), NULL_RTX);
 | ||
|   else
 | ||
|     {
 | ||
|       /* Compute the return values into a pseudo reg, which we will copy
 | ||
| 	 into the true return register after the cleanups are done.  */
 | ||
|       tree return_type = TREE_TYPE (DECL_RESULT (subr));
 | ||
|       if (TYPE_MODE (return_type) != BLKmode
 | ||
| 	  && targetm.calls.return_in_msb (return_type))
 | ||
| 	/* expand_function_end will insert the appropriate padding in
 | ||
| 	   this case.  Use the return value's natural (unpadded) mode
 | ||
| 	   within the function proper.  */
 | ||
| 	SET_DECL_RTL (DECL_RESULT (subr),
 | ||
| 		      gen_reg_rtx (TYPE_MODE (return_type)));
 | ||
|       else
 | ||
| 	{
 | ||
| 	  /* In order to figure out what mode to use for the pseudo, we
 | ||
| 	     figure out what the mode of the eventual return register will
 | ||
| 	     actually be, and use that.  */
 | ||
| 	  rtx hard_reg = hard_function_value (return_type, subr, 0, 1);
 | ||
| 
 | ||
| 	  /* Structures that are returned in registers are not
 | ||
| 	     aggregate_value_p, so we may see a PARALLEL or a REG.  */
 | ||
| 	  if (REG_P (hard_reg))
 | ||
| 	    SET_DECL_RTL (DECL_RESULT (subr),
 | ||
| 			  gen_reg_rtx (GET_MODE (hard_reg)));
 | ||
| 	  else
 | ||
| 	    {
 | ||
| 	      gcc_assert (GET_CODE (hard_reg) == PARALLEL);
 | ||
| 	      SET_DECL_RTL (DECL_RESULT (subr), gen_group_rtx (hard_reg));
 | ||
| 	    }
 | ||
| 	}
 | ||
| 
 | ||
|       /* Set DECL_REGISTER flag so that expand_function_end will copy the
 | ||
| 	 result to the real return register(s).  */
 | ||
|       DECL_REGISTER (DECL_RESULT (subr)) = 1;
 | ||
|     }
 | ||
| 
 | ||
|   /* Initialize rtx for parameters and local variables.
 | ||
|      In some cases this requires emitting insns.  */
 | ||
|   assign_parms (subr);
 | ||
| 
 | ||
|   /* If function gets a static chain arg, store it.  */
 | ||
|   if (cfun->static_chain_decl)
 | ||
|     {
 | ||
|       tree parm = cfun->static_chain_decl;
 | ||
|       rtx local, chain, insn;
 | ||
| 
 | ||
|       local = gen_reg_rtx (Pmode);
 | ||
|       chain = targetm.calls.static_chain (current_function_decl, true);
 | ||
| 
 | ||
|       set_decl_incoming_rtl (parm, chain, false);
 | ||
|       SET_DECL_RTL (parm, local);
 | ||
|       mark_reg_pointer (local, TYPE_ALIGN (TREE_TYPE (TREE_TYPE (parm))));
 | ||
| 
 | ||
|       insn = emit_move_insn (local, chain);
 | ||
| 
 | ||
|       /* Mark the register as eliminable, similar to parameters.  */
 | ||
|       if (MEM_P (chain)
 | ||
| 	  && reg_mentioned_p (arg_pointer_rtx, XEXP (chain, 0)))
 | ||
| 	set_dst_reg_note (insn, REG_EQUIV, chain, local);
 | ||
|     }
 | ||
| 
 | ||
|   /* If the function receives a non-local goto, then store the
 | ||
|      bits we need to restore the frame pointer.  */
 | ||
|   if (cfun->nonlocal_goto_save_area)
 | ||
|     {
 | ||
|       tree t_save;
 | ||
|       rtx r_save;
 | ||
| 
 | ||
|       tree var = TREE_OPERAND (cfun->nonlocal_goto_save_area, 0);
 | ||
|       gcc_assert (DECL_RTL_SET_P (var));
 | ||
| 
 | ||
|       t_save = build4 (ARRAY_REF,
 | ||
| 		       TREE_TYPE (TREE_TYPE (cfun->nonlocal_goto_save_area)),
 | ||
| 		       cfun->nonlocal_goto_save_area,
 | ||
| 		       integer_zero_node, NULL_TREE, NULL_TREE);
 | ||
|       r_save = expand_expr (t_save, NULL_RTX, VOIDmode, EXPAND_WRITE);
 | ||
|       gcc_assert (GET_MODE (r_save) == Pmode);
 | ||
| 
 | ||
|       emit_move_insn (r_save, targetm.builtin_setjmp_frame_value ());
 | ||
|       update_nonlocal_goto_save_area ();
 | ||
|     }
 | ||
| 
 | ||
|   /* The following was moved from init_function_start.
 | ||
|      The move is supposed to make sdb output more accurate.  */
 | ||
|   /* Indicate the beginning of the function body,
 | ||
|      as opposed to parm setup.  */
 | ||
|   emit_note (NOTE_INSN_FUNCTION_BEG);
 | ||
| 
 | ||
|   gcc_assert (NOTE_P (get_last_insn ()));
 | ||
| 
 | ||
|   parm_birth_insn = get_last_insn ();
 | ||
| 
 | ||
|   if (crtl->profile)
 | ||
|     {
 | ||
| #ifdef PROFILE_HOOK
 | ||
|       PROFILE_HOOK (current_function_funcdef_no);
 | ||
| #endif
 | ||
|     }
 | ||
| 
 | ||
|   /* If we are doing generic stack checking, the probe should go here.  */
 | ||
|   if (flag_stack_check == GENERIC_STACK_CHECK)
 | ||
|     stack_check_probe_note = emit_note (NOTE_INSN_DELETED);
 | ||
| }
 | ||
| 
 | ||
| /* Undo the effects of init_dummy_function_start.  */
 | ||
| void
 | ||
| expand_dummy_function_end (void)
 | ||
| {
 | ||
|   gcc_assert (in_dummy_function);
 | ||
| 
 | ||
|   /* End any sequences that failed to be closed due to syntax errors.  */
 | ||
|   while (in_sequence_p ())
 | ||
|     end_sequence ();
 | ||
| 
 | ||
|   /* Outside function body, can't compute type's actual size
 | ||
|      until next function's body starts.  */
 | ||
| 
 | ||
|   free_after_parsing (cfun);
 | ||
|   free_after_compilation (cfun);
 | ||
|   pop_cfun ();
 | ||
|   in_dummy_function = false;
 | ||
| }
 | ||
| 
 | ||
| /* Call DOIT for each hard register used as a return value from
 | ||
|    the current function.  */
 | ||
| 
 | ||
| void
 | ||
| diddle_return_value (void (*doit) (rtx, void *), void *arg)
 | ||
| {
 | ||
|   rtx outgoing = crtl->return_rtx;
 | ||
| 
 | ||
|   if (! outgoing)
 | ||
|     return;
 | ||
| 
 | ||
|   if (REG_P (outgoing))
 | ||
|     (*doit) (outgoing, arg);
 | ||
|   else if (GET_CODE (outgoing) == PARALLEL)
 | ||
|     {
 | ||
|       int i;
 | ||
| 
 | ||
|       for (i = 0; i < XVECLEN (outgoing, 0); i++)
 | ||
| 	{
 | ||
| 	  rtx x = XEXP (XVECEXP (outgoing, 0, i), 0);
 | ||
| 
 | ||
| 	  if (REG_P (x) && REGNO (x) < FIRST_PSEUDO_REGISTER)
 | ||
| 	    (*doit) (x, arg);
 | ||
| 	}
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| static void
 | ||
| do_clobber_return_reg (rtx reg, void *arg ATTRIBUTE_UNUSED)
 | ||
| {
 | ||
|   emit_clobber (reg);
 | ||
| }
 | ||
| 
 | ||
| void
 | ||
| clobber_return_register (void)
 | ||
| {
 | ||
|   diddle_return_value (do_clobber_return_reg, NULL);
 | ||
| 
 | ||
|   /* In case we do use pseudo to return value, clobber it too.  */
 | ||
|   if (DECL_RTL_SET_P (DECL_RESULT (current_function_decl)))
 | ||
|     {
 | ||
|       tree decl_result = DECL_RESULT (current_function_decl);
 | ||
|       rtx decl_rtl = DECL_RTL (decl_result);
 | ||
|       if (REG_P (decl_rtl) && REGNO (decl_rtl) >= FIRST_PSEUDO_REGISTER)
 | ||
| 	{
 | ||
| 	  do_clobber_return_reg (decl_rtl, NULL);
 | ||
| 	}
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| static void
 | ||
| do_use_return_reg (rtx reg, void *arg ATTRIBUTE_UNUSED)
 | ||
| {
 | ||
|   emit_use (reg);
 | ||
| }
 | ||
| 
 | ||
| static void
 | ||
| use_return_register (void)
 | ||
| {
 | ||
|   diddle_return_value (do_use_return_reg, NULL);
 | ||
| }
 | ||
| 
 | ||
| /* Possibly warn about unused parameters.  */
 | ||
| void
 | ||
| do_warn_unused_parameter (tree fn)
 | ||
| {
 | ||
|   tree decl;
 | ||
| 
 | ||
|   for (decl = DECL_ARGUMENTS (fn);
 | ||
|        decl; decl = DECL_CHAIN (decl))
 | ||
|     if (!TREE_USED (decl) && TREE_CODE (decl) == PARM_DECL
 | ||
| 	&& DECL_NAME (decl) && !DECL_ARTIFICIAL (decl)
 | ||
| 	&& !TREE_NO_WARNING (decl))
 | ||
|       warning (OPT_Wunused_parameter, "unused parameter %q+D", decl);
 | ||
| }
 | ||
| 
 | ||
| static GTY(()) rtx initial_trampoline;
 | ||
| 
 | ||
| /* Generate RTL for the end of the current function.  */
 | ||
| 
 | ||
| void
 | ||
| expand_function_end (void)
 | ||
| {
 | ||
|   rtx clobber_after;
 | ||
| 
 | ||
|   /* If arg_pointer_save_area was referenced only from a nested
 | ||
|      function, we will not have initialized it yet.  Do that now.  */
 | ||
|   if (arg_pointer_save_area && ! crtl->arg_pointer_save_area_init)
 | ||
|     get_arg_pointer_save_area ();
 | ||
| 
 | ||
|   /* If we are doing generic stack checking and this function makes calls,
 | ||
|      do a stack probe at the start of the function to ensure we have enough
 | ||
|      space for another stack frame.  */
 | ||
|   if (flag_stack_check == GENERIC_STACK_CHECK)
 | ||
|     {
 | ||
|       rtx insn, seq;
 | ||
| 
 | ||
|       for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
 | ||
| 	if (CALL_P (insn))
 | ||
| 	  {
 | ||
| 	    rtx max_frame_size = GEN_INT (STACK_CHECK_MAX_FRAME_SIZE);
 | ||
| 	    start_sequence ();
 | ||
| 	    if (STACK_CHECK_MOVING_SP)
 | ||
| 	      anti_adjust_stack_and_probe (max_frame_size, true);
 | ||
| 	    else
 | ||
| 	      probe_stack_range (STACK_OLD_CHECK_PROTECT, max_frame_size);
 | ||
| 	    seq = get_insns ();
 | ||
| 	    end_sequence ();
 | ||
| 	    set_insn_locators (seq, prologue_locator);
 | ||
| 	    emit_insn_before (seq, stack_check_probe_note);
 | ||
| 	    break;
 | ||
| 	  }
 | ||
|     }
 | ||
| 
 | ||
|   /* End any sequences that failed to be closed due to syntax errors.  */
 | ||
|   while (in_sequence_p ())
 | ||
|     end_sequence ();
 | ||
| 
 | ||
|   clear_pending_stack_adjust ();
 | ||
|   do_pending_stack_adjust ();
 | ||
| 
 | ||
|   /* Output a linenumber for the end of the function.
 | ||
|      SDB depends on this.  */
 | ||
|   set_curr_insn_source_location (input_location);
 | ||
| 
 | ||
|   /* Before the return label (if any), clobber the return
 | ||
|      registers so that they are not propagated live to the rest of
 | ||
|      the function.  This can only happen with functions that drop
 | ||
|      through; if there had been a return statement, there would
 | ||
|      have either been a return rtx, or a jump to the return label.
 | ||
| 
 | ||
|      We delay actual code generation after the current_function_value_rtx
 | ||
|      is computed.  */
 | ||
|   clobber_after = get_last_insn ();
 | ||
| 
 | ||
|   /* Output the label for the actual return from the function.  */
 | ||
|   emit_label (return_label);
 | ||
| 
 | ||
|   if (targetm_common.except_unwind_info (&global_options) == UI_SJLJ)
 | ||
|     {
 | ||
|       /* Let except.c know where it should emit the call to unregister
 | ||
| 	 the function context for sjlj exceptions.  */
 | ||
|       if (flag_exceptions)
 | ||
| 	sjlj_emit_function_exit_after (get_last_insn ());
 | ||
|     }
 | ||
|   else
 | ||
|     {
 | ||
|       /* We want to ensure that instructions that may trap are not
 | ||
| 	 moved into the epilogue by scheduling, because we don't
 | ||
| 	 always emit unwind information for the epilogue.  */
 | ||
|       if (cfun->can_throw_non_call_exceptions)
 | ||
| 	emit_insn (gen_blockage ());
 | ||
|     }
 | ||
| 
 | ||
|   /* If this is an implementation of throw, do what's necessary to
 | ||
|      communicate between __builtin_eh_return and the epilogue.  */
 | ||
|   expand_eh_return ();
 | ||
| 
 | ||
|   /* If scalar return value was computed in a pseudo-reg, or was a named
 | ||
|      return value that got dumped to the stack, copy that to the hard
 | ||
|      return register.  */
 | ||
|   if (DECL_RTL_SET_P (DECL_RESULT (current_function_decl)))
 | ||
|     {
 | ||
|       tree decl_result = DECL_RESULT (current_function_decl);
 | ||
|       rtx decl_rtl = DECL_RTL (decl_result);
 | ||
| 
 | ||
|       if (REG_P (decl_rtl)
 | ||
| 	  ? REGNO (decl_rtl) >= FIRST_PSEUDO_REGISTER
 | ||
| 	  : DECL_REGISTER (decl_result))
 | ||
| 	{
 | ||
| 	  rtx real_decl_rtl = crtl->return_rtx;
 | ||
| 
 | ||
| 	  /* This should be set in assign_parms.  */
 | ||
| 	  gcc_assert (REG_FUNCTION_VALUE_P (real_decl_rtl));
 | ||
| 
 | ||
| 	  /* If this is a BLKmode structure being returned in registers,
 | ||
| 	     then use the mode computed in expand_return.  Note that if
 | ||
| 	     decl_rtl is memory, then its mode may have been changed,
 | ||
| 	     but that crtl->return_rtx has not.  */
 | ||
| 	  if (GET_MODE (real_decl_rtl) == BLKmode)
 | ||
| 	    PUT_MODE (real_decl_rtl, GET_MODE (decl_rtl));
 | ||
| 
 | ||
| 	  /* If a non-BLKmode return value should be padded at the least
 | ||
| 	     significant end of the register, shift it left by the appropriate
 | ||
| 	     amount.  BLKmode results are handled using the group load/store
 | ||
| 	     machinery.  */
 | ||
| 	  if (TYPE_MODE (TREE_TYPE (decl_result)) != BLKmode
 | ||
| 	      && targetm.calls.return_in_msb (TREE_TYPE (decl_result)))
 | ||
| 	    {
 | ||
| 	      emit_move_insn (gen_rtx_REG (GET_MODE (decl_rtl),
 | ||
| 					   REGNO (real_decl_rtl)),
 | ||
| 			      decl_rtl);
 | ||
| 	      shift_return_value (GET_MODE (decl_rtl), true, real_decl_rtl);
 | ||
| 	    }
 | ||
| 	  /* If a named return value dumped decl_return to memory, then
 | ||
| 	     we may need to re-do the PROMOTE_MODE signed/unsigned
 | ||
| 	     extension.  */
 | ||
| 	  else if (GET_MODE (real_decl_rtl) != GET_MODE (decl_rtl))
 | ||
| 	    {
 | ||
| 	      int unsignedp = TYPE_UNSIGNED (TREE_TYPE (decl_result));
 | ||
| 	      promote_function_mode (TREE_TYPE (decl_result),
 | ||
| 				     GET_MODE (decl_rtl), &unsignedp,
 | ||
| 				     TREE_TYPE (current_function_decl), 1);
 | ||
| 
 | ||
| 	      convert_move (real_decl_rtl, decl_rtl, unsignedp);
 | ||
| 	    }
 | ||
| 	  else if (GET_CODE (real_decl_rtl) == PARALLEL)
 | ||
| 	    {
 | ||
| 	      /* If expand_function_start has created a PARALLEL for decl_rtl,
 | ||
| 		 move the result to the real return registers.  Otherwise, do
 | ||
| 		 a group load from decl_rtl for a named return.  */
 | ||
| 	      if (GET_CODE (decl_rtl) == PARALLEL)
 | ||
| 		emit_group_move (real_decl_rtl, decl_rtl);
 | ||
| 	      else
 | ||
| 		emit_group_load (real_decl_rtl, decl_rtl,
 | ||
| 				 TREE_TYPE (decl_result),
 | ||
| 				 int_size_in_bytes (TREE_TYPE (decl_result)));
 | ||
| 	    }
 | ||
| 	  /* In the case of complex integer modes smaller than a word, we'll
 | ||
| 	     need to generate some non-trivial bitfield insertions.  Do that
 | ||
| 	     on a pseudo and not the hard register.  */
 | ||
| 	  else if (GET_CODE (decl_rtl) == CONCAT
 | ||
| 		   && GET_MODE_CLASS (GET_MODE (decl_rtl)) == MODE_COMPLEX_INT
 | ||
| 		   && GET_MODE_BITSIZE (GET_MODE (decl_rtl)) <= BITS_PER_WORD)
 | ||
| 	    {
 | ||
| 	      int old_generating_concat_p;
 | ||
| 	      rtx tmp;
 | ||
| 
 | ||
| 	      old_generating_concat_p = generating_concat_p;
 | ||
| 	      generating_concat_p = 0;
 | ||
| 	      tmp = gen_reg_rtx (GET_MODE (decl_rtl));
 | ||
| 	      generating_concat_p = old_generating_concat_p;
 | ||
| 
 | ||
| 	      emit_move_insn (tmp, decl_rtl);
 | ||
| 	      emit_move_insn (real_decl_rtl, tmp);
 | ||
| 	    }
 | ||
| 	  else
 | ||
| 	    emit_move_insn (real_decl_rtl, decl_rtl);
 | ||
| 	}
 | ||
|     }
 | ||
| 
 | ||
|   /* If returning a structure, arrange to return the address of the value
 | ||
|      in a place where debuggers expect to find it.
 | ||
| 
 | ||
|      If returning a structure PCC style,
 | ||
|      the caller also depends on this value.
 | ||
|      And cfun->returns_pcc_struct is not necessarily set.  */
 | ||
|   if (cfun->returns_struct
 | ||
|       || cfun->returns_pcc_struct)
 | ||
|     {
 | ||
|       rtx value_address = DECL_RTL (DECL_RESULT (current_function_decl));
 | ||
|       tree type = TREE_TYPE (DECL_RESULT (current_function_decl));
 | ||
|       rtx outgoing;
 | ||
| 
 | ||
|       if (DECL_BY_REFERENCE (DECL_RESULT (current_function_decl)))
 | ||
| 	type = TREE_TYPE (type);
 | ||
|       else
 | ||
| 	value_address = XEXP (value_address, 0);
 | ||
| 
 | ||
|       outgoing = targetm.calls.function_value (build_pointer_type (type),
 | ||
| 					       current_function_decl, true);
 | ||
| 
 | ||
|       /* Mark this as a function return value so integrate will delete the
 | ||
| 	 assignment and USE below when inlining this function.  */
 | ||
|       REG_FUNCTION_VALUE_P (outgoing) = 1;
 | ||
| 
 | ||
|       /* The address may be ptr_mode and OUTGOING may be Pmode.  */
 | ||
|       value_address = convert_memory_address (GET_MODE (outgoing),
 | ||
| 					      value_address);
 | ||
| 
 | ||
|       emit_move_insn (outgoing, value_address);
 | ||
| 
 | ||
|       /* Show return register used to hold result (in this case the address
 | ||
| 	 of the result.  */
 | ||
|       crtl->return_rtx = outgoing;
 | ||
|     }
 | ||
| 
 | ||
|   /* Emit the actual code to clobber return register.  */
 | ||
|   {
 | ||
|     rtx seq;
 | ||
| 
 | ||
|     start_sequence ();
 | ||
|     clobber_return_register ();
 | ||
|     seq = get_insns ();
 | ||
|     end_sequence ();
 | ||
| 
 | ||
|     emit_insn_after (seq, clobber_after);
 | ||
|   }
 | ||
| 
 | ||
|   /* Output the label for the naked return from the function.  */
 | ||
|   if (naked_return_label)
 | ||
|     emit_label (naked_return_label);
 | ||
| 
 | ||
|   /* @@@ This is a kludge.  We want to ensure that instructions that
 | ||
|      may trap are not moved into the epilogue by scheduling, because
 | ||
|      we don't always emit unwind information for the epilogue.  */
 | ||
|   if (cfun->can_throw_non_call_exceptions
 | ||
|       && targetm_common.except_unwind_info (&global_options) != UI_SJLJ)
 | ||
|     emit_insn (gen_blockage ());
 | ||
| 
 | ||
|   /* If stack protection is enabled for this function, check the guard.  */
 | ||
|   if (crtl->stack_protect_guard)
 | ||
|     stack_protect_epilogue ();
 | ||
| 
 | ||
|   /* If we had calls to alloca, and this machine needs
 | ||
|      an accurate stack pointer to exit the function,
 | ||
|      insert some code to save and restore the stack pointer.  */
 | ||
|   if (! EXIT_IGNORE_STACK
 | ||
|       && cfun->calls_alloca)
 | ||
|     {
 | ||
|       rtx tem = 0, seq;
 | ||
| 
 | ||
|       start_sequence ();
 | ||
|       emit_stack_save (SAVE_FUNCTION, &tem);
 | ||
|       seq = get_insns ();
 | ||
|       end_sequence ();
 | ||
|       emit_insn_before (seq, parm_birth_insn);
 | ||
| 
 | ||
|       emit_stack_restore (SAVE_FUNCTION, tem);
 | ||
|     }
 | ||
| 
 | ||
|   /* ??? This should no longer be necessary since stupid is no longer with
 | ||
|      us, but there are some parts of the compiler (eg reload_combine, and
 | ||
|      sh mach_dep_reorg) that still try and compute their own lifetime info
 | ||
|      instead of using the general framework.  */
 | ||
|   use_return_register ();
 | ||
| }
 | ||
| 
 | ||
| rtx
 | ||
| get_arg_pointer_save_area (void)
 | ||
| {
 | ||
|   rtx ret = arg_pointer_save_area;
 | ||
| 
 | ||
|   if (! ret)
 | ||
|     {
 | ||
|       ret = assign_stack_local (Pmode, GET_MODE_SIZE (Pmode), 0);
 | ||
|       arg_pointer_save_area = ret;
 | ||
|     }
 | ||
| 
 | ||
|   if (! crtl->arg_pointer_save_area_init)
 | ||
|     {
 | ||
|       rtx seq;
 | ||
| 
 | ||
|       /* Save the arg pointer at the beginning of the function.  The
 | ||
| 	 generated stack slot may not be a valid memory address, so we
 | ||
| 	 have to check it and fix it if necessary.  */
 | ||
|       start_sequence ();
 | ||
|       emit_move_insn (validize_mem (ret),
 | ||
|                       crtl->args.internal_arg_pointer);
 | ||
|       seq = get_insns ();
 | ||
|       end_sequence ();
 | ||
| 
 | ||
|       push_topmost_sequence ();
 | ||
|       emit_insn_after (seq, entry_of_function ());
 | ||
|       pop_topmost_sequence ();
 | ||
| 
 | ||
|       crtl->arg_pointer_save_area_init = true;
 | ||
|     }
 | ||
| 
 | ||
|   return ret;
 | ||
| }
 | ||
| 
 | ||
| /* Add a list of INSNS to the hash HASHP, possibly allocating HASHP
 | ||
|    for the first time.  */
 | ||
| 
 | ||
| static void
 | ||
| record_insns (rtx insns, rtx end, htab_t *hashp)
 | ||
| {
 | ||
|   rtx tmp;
 | ||
|   htab_t hash = *hashp;
 | ||
| 
 | ||
|   if (hash == NULL)
 | ||
|     *hashp = hash
 | ||
|       = htab_create_ggc (17, htab_hash_pointer, htab_eq_pointer, NULL);
 | ||
| 
 | ||
|   for (tmp = insns; tmp != end; tmp = NEXT_INSN (tmp))
 | ||
|     {
 | ||
|       void **slot = htab_find_slot (hash, tmp, INSERT);
 | ||
|       gcc_assert (*slot == NULL);
 | ||
|       *slot = tmp;
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /* INSN has been duplicated or replaced by as COPY, perhaps by duplicating a
 | ||
|    basic block, splitting or peepholes.  If INSN is a prologue or epilogue
 | ||
|    insn, then record COPY as well.  */
 | ||
| 
 | ||
| void
 | ||
| maybe_copy_prologue_epilogue_insn (rtx insn, rtx copy)
 | ||
| {
 | ||
|   htab_t hash;
 | ||
|   void **slot;
 | ||
| 
 | ||
|   hash = epilogue_insn_hash;
 | ||
|   if (!hash || !htab_find (hash, insn))
 | ||
|     {
 | ||
|       hash = prologue_insn_hash;
 | ||
|       if (!hash || !htab_find (hash, insn))
 | ||
| 	return;
 | ||
|     }
 | ||
| 
 | ||
|   slot = htab_find_slot (hash, copy, INSERT);
 | ||
|   gcc_assert (*slot == NULL);
 | ||
|   *slot = copy;
 | ||
| }
 | ||
| 
 | ||
| /* Set the locator of the insn chain starting at INSN to LOC.  */
 | ||
| static void
 | ||
| set_insn_locators (rtx insn, int loc)
 | ||
| {
 | ||
|   while (insn != NULL_RTX)
 | ||
|     {
 | ||
|       if (INSN_P (insn))
 | ||
| 	INSN_LOCATOR (insn) = loc;
 | ||
|       insn = NEXT_INSN (insn);
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /* Determine if any INSNs in HASH are, or are part of, INSN.  Because
 | ||
|    we can be running after reorg, SEQUENCE rtl is possible.  */
 | ||
| 
 | ||
| static bool
 | ||
| contains (const_rtx insn, htab_t hash)
 | ||
| {
 | ||
|   if (hash == NULL)
 | ||
|     return false;
 | ||
| 
 | ||
|   if (NONJUMP_INSN_P (insn) && GET_CODE (PATTERN (insn)) == SEQUENCE)
 | ||
|     {
 | ||
|       int i;
 | ||
|       for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
 | ||
| 	if (htab_find (hash, XVECEXP (PATTERN (insn), 0, i)))
 | ||
| 	  return true;
 | ||
|       return false;
 | ||
|     }
 | ||
| 
 | ||
|   return htab_find (hash, insn) != NULL;
 | ||
| }
 | ||
| 
 | ||
| int
 | ||
| prologue_epilogue_contains (const_rtx insn)
 | ||
| {
 | ||
|   if (contains (insn, prologue_insn_hash))
 | ||
|     return 1;
 | ||
|   if (contains (insn, epilogue_insn_hash))
 | ||
|     return 1;
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| #ifdef HAVE_simple_return
 | ||
| 
 | ||
| /* Return true if INSN requires the stack frame to be set up.
 | ||
|    PROLOGUE_USED contains the hard registers used in the function
 | ||
|    prologue.  SET_UP_BY_PROLOGUE is the set of registers we expect the
 | ||
|    prologue to set up for the function.  */
 | ||
| bool
 | ||
| requires_stack_frame_p (rtx insn, HARD_REG_SET prologue_used,
 | ||
| 			HARD_REG_SET set_up_by_prologue)
 | ||
| {
 | ||
|   df_ref *df_rec;
 | ||
|   HARD_REG_SET hardregs;
 | ||
|   unsigned regno;
 | ||
| 
 | ||
|   if (CALL_P (insn))
 | ||
|     return !SIBLING_CALL_P (insn);
 | ||
| 
 | ||
|   /* We need a frame to get the unique CFA expected by the unwinder.  */
 | ||
|   if (cfun->can_throw_non_call_exceptions && can_throw_internal (insn))
 | ||
|     return true;
 | ||
| 
 | ||
|   CLEAR_HARD_REG_SET (hardregs);
 | ||
|   for (df_rec = DF_INSN_DEFS (insn); *df_rec; df_rec++)
 | ||
|     {
 | ||
|       rtx dreg = DF_REF_REG (*df_rec);
 | ||
| 
 | ||
|       if (!REG_P (dreg))
 | ||
| 	continue;
 | ||
| 
 | ||
|       add_to_hard_reg_set (&hardregs, GET_MODE (dreg),
 | ||
| 			   REGNO (dreg));
 | ||
|     }
 | ||
|   if (hard_reg_set_intersect_p (hardregs, prologue_used))
 | ||
|     return true;
 | ||
|   AND_COMPL_HARD_REG_SET (hardregs, call_used_reg_set);
 | ||
|   for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
 | ||
|     if (TEST_HARD_REG_BIT (hardregs, regno)
 | ||
| 	&& df_regs_ever_live_p (regno))
 | ||
|       return true;
 | ||
| 
 | ||
|   for (df_rec = DF_INSN_USES (insn); *df_rec; df_rec++)
 | ||
|     {
 | ||
|       rtx reg = DF_REF_REG (*df_rec);
 | ||
| 
 | ||
|       if (!REG_P (reg))
 | ||
| 	continue;
 | ||
| 
 | ||
|       add_to_hard_reg_set (&hardregs, GET_MODE (reg),
 | ||
| 			   REGNO (reg));
 | ||
|     }
 | ||
|   if (hard_reg_set_intersect_p (hardregs, set_up_by_prologue))
 | ||
|     return true;
 | ||
| 
 | ||
|   return false;
 | ||
| }
 | ||
| 
 | ||
| /* See whether BB has a single successor that uses [REGNO, END_REGNO),
 | ||
|    and if BB is its only predecessor.  Return that block if so,
 | ||
|    otherwise return null.  */
 | ||
| 
 | ||
| static basic_block
 | ||
| next_block_for_reg (basic_block bb, int regno, int end_regno)
 | ||
| {
 | ||
|   edge e, live_edge;
 | ||
|   edge_iterator ei;
 | ||
|   bitmap live;
 | ||
|   int i;
 | ||
| 
 | ||
|   live_edge = NULL;
 | ||
|   FOR_EACH_EDGE (e, ei, bb->succs)
 | ||
|     {
 | ||
|       live = df_get_live_in (e->dest);
 | ||
|       for (i = regno; i < end_regno; i++)
 | ||
| 	if (REGNO_REG_SET_P (live, i))
 | ||
| 	  {
 | ||
| 	    if (live_edge && live_edge != e)
 | ||
| 	      return NULL;
 | ||
| 	    live_edge = e;
 | ||
| 	  }
 | ||
|     }
 | ||
| 
 | ||
|   /* We can sometimes encounter dead code.  Don't try to move it
 | ||
|      into the exit block.  */
 | ||
|   if (!live_edge || live_edge->dest == EXIT_BLOCK_PTR)
 | ||
|     return NULL;
 | ||
| 
 | ||
|   /* Reject targets of abnormal edges.  This is needed for correctness
 | ||
|      on ports like Alpha and MIPS, whose pic_offset_table_rtx can die on
 | ||
|      exception edges even though it is generally treated as call-saved
 | ||
|      for the majority of the compilation.  Moving across abnormal edges
 | ||
|      isn't going to be interesting for shrink-wrap usage anyway.  */
 | ||
|   if (live_edge->flags & EDGE_ABNORMAL)
 | ||
|     return NULL;
 | ||
| 
 | ||
|   if (EDGE_COUNT (live_edge->dest->preds) > 1)
 | ||
|     return NULL;
 | ||
| 
 | ||
|   return live_edge->dest;
 | ||
| }
 | ||
| 
 | ||
| /* Try to move INSN from BB to a successor.  Return true on success.
 | ||
|    USES and DEFS are the set of registers that are used and defined
 | ||
|    after INSN in BB.  */
 | ||
| 
 | ||
| static bool
 | ||
| move_insn_for_shrink_wrap (basic_block bb, rtx insn,
 | ||
| 			   const HARD_REG_SET uses,
 | ||
| 			   const HARD_REG_SET defs)
 | ||
| {
 | ||
|   rtx set, src, dest;
 | ||
|   bitmap live_out, live_in, bb_uses, bb_defs;
 | ||
|   unsigned int i, dregno, end_dregno, sregno, end_sregno;
 | ||
|   basic_block next_block;
 | ||
| 
 | ||
|   /* Look for a simple register copy.  */
 | ||
|   set = single_set (insn);
 | ||
|   if (!set)
 | ||
|     return false;
 | ||
|   src = SET_SRC (set);
 | ||
|   dest = SET_DEST (set);
 | ||
|   if (!REG_P (dest) || !REG_P (src))
 | ||
|     return false;
 | ||
| 
 | ||
|   /* Make sure that the source register isn't defined later in BB.  */
 | ||
|   sregno = REGNO (src);
 | ||
|   end_sregno = END_REGNO (src);
 | ||
|   if (overlaps_hard_reg_set_p (defs, GET_MODE (src), sregno))
 | ||
|     return false;
 | ||
| 
 | ||
|   /* Make sure that the destination register isn't referenced later in BB.  */
 | ||
|   dregno = REGNO (dest);
 | ||
|   end_dregno = END_REGNO (dest);
 | ||
|   if (overlaps_hard_reg_set_p (uses, GET_MODE (dest), dregno)
 | ||
|       || overlaps_hard_reg_set_p (defs, GET_MODE (dest), dregno))
 | ||
|     return false;
 | ||
| 
 | ||
|   /* See whether there is a successor block to which we could move INSN.  */
 | ||
|   next_block = next_block_for_reg (bb, dregno, end_dregno);
 | ||
|   if (!next_block)
 | ||
|     return false;
 | ||
| 
 | ||
|   /* At this point we are committed to moving INSN, but let's try to
 | ||
|      move it as far as we can.  */
 | ||
|   do
 | ||
|     {
 | ||
|       live_out = df_get_live_out (bb);
 | ||
|       live_in = df_get_live_in (next_block);
 | ||
|       bb = next_block;
 | ||
| 
 | ||
|       /* Check whether BB uses DEST or clobbers DEST.  We need to add
 | ||
| 	 INSN to BB if so.  Either way, DEST is no longer live on entry,
 | ||
| 	 except for any part that overlaps SRC (next loop).  */
 | ||
|       bb_uses = &DF_LR_BB_INFO (bb)->use;
 | ||
|       bb_defs = &DF_LR_BB_INFO (bb)->def;
 | ||
|       for (i = dregno; i < end_dregno; i++)
 | ||
| 	{
 | ||
| 	  if (REGNO_REG_SET_P (bb_uses, i) || REGNO_REG_SET_P (bb_defs, i))
 | ||
| 	    next_block = NULL;
 | ||
| 	  CLEAR_REGNO_REG_SET (live_out, i);
 | ||
| 	  CLEAR_REGNO_REG_SET (live_in, i);
 | ||
| 	}
 | ||
| 
 | ||
|       /* Check whether BB clobbers SRC.  We need to add INSN to BB if so.
 | ||
| 	 Either way, SRC is now live on entry.  */
 | ||
|       for (i = sregno; i < end_sregno; i++)
 | ||
| 	{
 | ||
| 	  if (REGNO_REG_SET_P (bb_defs, i))
 | ||
| 	    next_block = NULL;
 | ||
| 	  SET_REGNO_REG_SET (live_out, i);
 | ||
| 	  SET_REGNO_REG_SET (live_in, i);
 | ||
| 	}
 | ||
| 
 | ||
|       /* If we don't need to add the move to BB, look for a single
 | ||
| 	 successor block.  */
 | ||
|       if (next_block)
 | ||
| 	next_block = next_block_for_reg (next_block, dregno, end_dregno);
 | ||
|     }
 | ||
|   while (next_block);
 | ||
| 
 | ||
|   /* BB now defines DEST.  It only uses the parts of DEST that overlap SRC
 | ||
|      (next loop).  */
 | ||
|   for (i = dregno; i < end_dregno; i++)
 | ||
|     {
 | ||
|       CLEAR_REGNO_REG_SET (bb_uses, i);
 | ||
|       SET_REGNO_REG_SET (bb_defs, i);
 | ||
|     }
 | ||
| 
 | ||
|   /* BB now uses SRC.  */
 | ||
|   for (i = sregno; i < end_sregno; i++)
 | ||
|     SET_REGNO_REG_SET (bb_uses, i);
 | ||
| 
 | ||
|   emit_insn_after (PATTERN (insn), bb_note (bb));
 | ||
|   delete_insn (insn);
 | ||
|   return true;
 | ||
| }
 | ||
| 
 | ||
| /* Look for register copies in the first block of the function, and move
 | ||
|    them down into successor blocks if the register is used only on one
 | ||
|    path.  This exposes more opportunities for shrink-wrapping.  These
 | ||
|    kinds of sets often occur when incoming argument registers are moved
 | ||
|    to call-saved registers because their values are live across one or
 | ||
|    more calls during the function.  */
 | ||
| 
 | ||
| static void
 | ||
| prepare_shrink_wrap (basic_block entry_block)
 | ||
| {
 | ||
|   rtx insn, curr, x;
 | ||
|   HARD_REG_SET uses, defs;
 | ||
|   df_ref *ref;
 | ||
| 
 | ||
|   CLEAR_HARD_REG_SET (uses);
 | ||
|   CLEAR_HARD_REG_SET (defs);
 | ||
|   FOR_BB_INSNS_REVERSE_SAFE (entry_block, insn, curr)
 | ||
|     if (NONDEBUG_INSN_P (insn)
 | ||
| 	&& !move_insn_for_shrink_wrap (entry_block, insn, uses, defs))
 | ||
|       {
 | ||
| 	/* Add all defined registers to DEFs.  */
 | ||
| 	for (ref = DF_INSN_DEFS (insn); *ref; ref++)
 | ||
| 	  {
 | ||
| 	    x = DF_REF_REG (*ref);
 | ||
| 	    if (REG_P (x) && HARD_REGISTER_P (x))
 | ||
| 	      SET_HARD_REG_BIT (defs, REGNO (x));
 | ||
| 	  }
 | ||
| 
 | ||
| 	/* Add all used registers to USESs.  */
 | ||
| 	for (ref = DF_INSN_USES (insn); *ref; ref++)
 | ||
| 	  {
 | ||
| 	    x = DF_REF_REG (*ref);
 | ||
| 	    if (REG_P (x) && HARD_REGISTER_P (x))
 | ||
| 	      SET_HARD_REG_BIT (uses, REGNO (x));
 | ||
| 	  }
 | ||
|       }
 | ||
| }
 | ||
| 
 | ||
| #endif
 | ||
| 
 | ||
| #ifdef HAVE_return
 | ||
| /* Insert use of return register before the end of BB.  */
 | ||
| 
 | ||
| static void
 | ||
| emit_use_return_register_into_block (basic_block bb)
 | ||
| {
 | ||
|   rtx seq;
 | ||
|   start_sequence ();
 | ||
|   use_return_register ();
 | ||
|   seq = get_insns ();
 | ||
|   end_sequence ();
 | ||
|   emit_insn_before (seq, BB_END (bb));
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| /* Create a return pattern, either simple_return or return, depending on
 | ||
|    simple_p.  */
 | ||
| 
 | ||
| static rtx
 | ||
| gen_return_pattern (bool simple_p)
 | ||
| {
 | ||
| #ifdef HAVE_simple_return
 | ||
|   return simple_p ? gen_simple_return () : gen_return ();
 | ||
| #else
 | ||
|   gcc_assert (!simple_p);
 | ||
|   return gen_return ();
 | ||
| #endif
 | ||
| }
 | ||
| 
 | ||
| /* Insert an appropriate return pattern at the end of block BB.  This
 | ||
|    also means updating block_for_insn appropriately.  SIMPLE_P is
 | ||
|    the same as in gen_return_pattern and passed to it.  */
 | ||
| 
 | ||
| static void
 | ||
| emit_return_into_block (bool simple_p, basic_block bb)
 | ||
| {
 | ||
|   rtx jump, pat;
 | ||
|   jump = emit_jump_insn_after (gen_return_pattern (simple_p), BB_END (bb));
 | ||
|   pat = PATTERN (jump);
 | ||
|   if (GET_CODE (pat) == PARALLEL)
 | ||
|     pat = XVECEXP (pat, 0, 0);
 | ||
|   gcc_assert (ANY_RETURN_P (pat));
 | ||
|   JUMP_LABEL (jump) = pat;
 | ||
| }
 | ||
| #endif
 | ||
| 
 | ||
| /* Set JUMP_LABEL for a return insn.  */
 | ||
| 
 | ||
| void
 | ||
| set_return_jump_label (rtx returnjump)
 | ||
| {
 | ||
|   rtx pat = PATTERN (returnjump);
 | ||
|   if (GET_CODE (pat) == PARALLEL)
 | ||
|     pat = XVECEXP (pat, 0, 0);
 | ||
|   if (ANY_RETURN_P (pat))
 | ||
|     JUMP_LABEL (returnjump) = pat;
 | ||
|   else
 | ||
|     JUMP_LABEL (returnjump) = ret_rtx;
 | ||
| }
 | ||
| 
 | ||
| #ifdef HAVE_simple_return
 | ||
| /* Create a copy of BB instructions and insert at BEFORE.  Redirect
 | ||
|    preds of BB to COPY_BB if they don't appear in NEED_PROLOGUE.  */
 | ||
| static void
 | ||
| dup_block_and_redirect (basic_block bb, basic_block copy_bb, rtx before,
 | ||
| 			bitmap_head *need_prologue)
 | ||
| {
 | ||
|   edge_iterator ei;
 | ||
|   edge e;
 | ||
|   rtx insn = BB_END (bb);
 | ||
| 
 | ||
|   /* We know BB has a single successor, so there is no need to copy a
 | ||
|      simple jump at the end of BB.  */
 | ||
|   if (simplejump_p (insn))
 | ||
|     insn = PREV_INSN (insn);
 | ||
| 
 | ||
|   start_sequence ();
 | ||
|   duplicate_insn_chain (BB_HEAD (bb), insn);
 | ||
|   if (dump_file)
 | ||
|     {
 | ||
|       unsigned count = 0;
 | ||
|       for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
 | ||
| 	if (active_insn_p (insn))
 | ||
| 	  ++count;
 | ||
|       fprintf (dump_file, "Duplicating bb %d to bb %d, %u active insns.\n",
 | ||
| 	       bb->index, copy_bb->index, count);
 | ||
|     }
 | ||
|   insn = get_insns ();
 | ||
|   end_sequence ();
 | ||
|   emit_insn_before (insn, before);
 | ||
| 
 | ||
|   /* Redirect all the paths that need no prologue into copy_bb.  */
 | ||
|   for (ei = ei_start (bb->preds); (e = ei_safe_edge (ei)); )
 | ||
|     if (!bitmap_bit_p (need_prologue, e->src->index))
 | ||
|       {
 | ||
| 	redirect_edge_and_branch_force (e, copy_bb);
 | ||
| 	continue;
 | ||
|       }
 | ||
|     else
 | ||
|       ei_next (&ei);
 | ||
| }
 | ||
| #endif
 | ||
| 
 | ||
| #if defined (HAVE_return) || defined (HAVE_simple_return)
 | ||
| /* Return true if there are any active insns between HEAD and TAIL.  */
 | ||
| static bool
 | ||
| active_insn_between (rtx head, rtx tail)
 | ||
| {
 | ||
|   while (tail)
 | ||
|     {
 | ||
|       if (active_insn_p (tail))
 | ||
| 	return true;
 | ||
|       if (tail == head)
 | ||
| 	return false;
 | ||
|       tail = PREV_INSN (tail);
 | ||
|     }
 | ||
|   return false;
 | ||
| }
 | ||
| 
 | ||
| /* LAST_BB is a block that exits, and empty of active instructions.
 | ||
|    Examine its predecessors for jumps that can be converted to
 | ||
|    (conditional) returns.  */
 | ||
| static VEC (edge, heap) *
 | ||
| convert_jumps_to_returns (basic_block last_bb, bool simple_p,
 | ||
| 			  VEC (edge, heap) *unconverted ATTRIBUTE_UNUSED)
 | ||
| {
 | ||
|   int i;
 | ||
|   basic_block bb;
 | ||
|   rtx label;
 | ||
|   edge_iterator ei;
 | ||
|   edge e;
 | ||
|   VEC(basic_block,heap) *src_bbs;
 | ||
| 
 | ||
|   src_bbs = VEC_alloc (basic_block, heap, EDGE_COUNT (last_bb->preds));
 | ||
|   FOR_EACH_EDGE (e, ei, last_bb->preds)
 | ||
|     if (e->src != ENTRY_BLOCK_PTR)
 | ||
|       VEC_quick_push (basic_block, src_bbs, e->src);
 | ||
| 
 | ||
|   label = BB_HEAD (last_bb);
 | ||
| 
 | ||
|   FOR_EACH_VEC_ELT (basic_block, src_bbs, i, bb)
 | ||
|     {
 | ||
|       rtx jump = BB_END (bb);
 | ||
| 
 | ||
|       if (!JUMP_P (jump) || JUMP_LABEL (jump) != label)
 | ||
| 	continue;
 | ||
| 
 | ||
|       e = find_edge (bb, last_bb);
 | ||
| 
 | ||
|       /* If we have an unconditional jump, we can replace that
 | ||
| 	 with a simple return instruction.  */
 | ||
|       if (simplejump_p (jump))
 | ||
| 	{
 | ||
| 	  /* The use of the return register might be present in the exit
 | ||
| 	     fallthru block.  Either:
 | ||
| 	     - removing the use is safe, and we should remove the use in
 | ||
| 	     the exit fallthru block, or
 | ||
| 	     - removing the use is not safe, and we should add it here.
 | ||
| 	     For now, we conservatively choose the latter.  Either of the
 | ||
| 	     2 helps in crossjumping.  */
 | ||
| 	  emit_use_return_register_into_block (bb);
 | ||
| 
 | ||
| 	  emit_return_into_block (simple_p, bb);
 | ||
| 	  delete_insn (jump);
 | ||
| 	}
 | ||
| 
 | ||
|       /* If we have a conditional jump branching to the last
 | ||
| 	 block, we can try to replace that with a conditional
 | ||
| 	 return instruction.  */
 | ||
|       else if (condjump_p (jump))
 | ||
| 	{
 | ||
| 	  rtx dest;
 | ||
| 
 | ||
| 	  if (simple_p)
 | ||
| 	    dest = simple_return_rtx;
 | ||
| 	  else
 | ||
| 	    dest = ret_rtx;
 | ||
| 	  if (!redirect_jump (jump, dest, 0))
 | ||
| 	    {
 | ||
| #ifdef HAVE_simple_return
 | ||
| 	      if (simple_p)
 | ||
| 		{
 | ||
| 		  if (dump_file)
 | ||
| 		    fprintf (dump_file,
 | ||
| 			     "Failed to redirect bb %d branch.\n", bb->index);
 | ||
| 		  VEC_safe_push (edge, heap, unconverted, e);
 | ||
| 		}
 | ||
| #endif
 | ||
| 	      continue;
 | ||
| 	    }
 | ||
| 
 | ||
| 	  /* See comment in simplejump_p case above.  */
 | ||
| 	  emit_use_return_register_into_block (bb);
 | ||
| 
 | ||
| 	  /* If this block has only one successor, it both jumps
 | ||
| 	     and falls through to the fallthru block, so we can't
 | ||
| 	     delete the edge.  */
 | ||
| 	  if (single_succ_p (bb))
 | ||
| 	    continue;
 | ||
| 	}
 | ||
|       else
 | ||
| 	{
 | ||
| #ifdef HAVE_simple_return
 | ||
| 	  if (simple_p)
 | ||
| 	    {
 | ||
| 	      if (dump_file)
 | ||
| 		fprintf (dump_file,
 | ||
| 			 "Failed to redirect bb %d branch.\n", bb->index);
 | ||
| 	      VEC_safe_push (edge, heap, unconverted, e);
 | ||
| 	    }
 | ||
| #endif
 | ||
| 	  continue;
 | ||
| 	}
 | ||
| 
 | ||
|       /* Fix up the CFG for the successful change we just made.  */
 | ||
|       redirect_edge_succ (e, EXIT_BLOCK_PTR);
 | ||
|       e->flags &= ~EDGE_CROSSING;
 | ||
|     }
 | ||
|   VEC_free (basic_block, heap, src_bbs);
 | ||
|   return unconverted;
 | ||
| }
 | ||
| 
 | ||
| /* Emit a return insn for the exit fallthru block.  */
 | ||
| static basic_block
 | ||
| emit_return_for_exit (edge exit_fallthru_edge, bool simple_p)
 | ||
| {
 | ||
|   basic_block last_bb = exit_fallthru_edge->src;
 | ||
| 
 | ||
|   if (JUMP_P (BB_END (last_bb)))
 | ||
|     {
 | ||
|       last_bb = split_edge (exit_fallthru_edge);
 | ||
|       exit_fallthru_edge = single_succ_edge (last_bb);
 | ||
|     }
 | ||
|   emit_barrier_after (BB_END (last_bb));
 | ||
|   emit_return_into_block (simple_p, last_bb);
 | ||
|   exit_fallthru_edge->flags &= ~EDGE_FALLTHRU;
 | ||
|   return last_bb;
 | ||
| }
 | ||
| #endif
 | ||
| 
 | ||
| 
 | ||
| /* Generate the prologue and epilogue RTL if the machine supports it.  Thread
 | ||
|    this into place with notes indicating where the prologue ends and where
 | ||
|    the epilogue begins.  Update the basic block information when possible.
 | ||
| 
 | ||
|    Notes on epilogue placement:
 | ||
|    There are several kinds of edges to the exit block:
 | ||
|    * a single fallthru edge from LAST_BB
 | ||
|    * possibly, edges from blocks containing sibcalls
 | ||
|    * possibly, fake edges from infinite loops
 | ||
| 
 | ||
|    The epilogue is always emitted on the fallthru edge from the last basic
 | ||
|    block in the function, LAST_BB, into the exit block.
 | ||
| 
 | ||
|    If LAST_BB is empty except for a label, it is the target of every
 | ||
|    other basic block in the function that ends in a return.  If a
 | ||
|    target has a return or simple_return pattern (possibly with
 | ||
|    conditional variants), these basic blocks can be changed so that a
 | ||
|    return insn is emitted into them, and their target is adjusted to
 | ||
|    the real exit block.
 | ||
| 
 | ||
|    Notes on shrink wrapping: We implement a fairly conservative
 | ||
|    version of shrink-wrapping rather than the textbook one.  We only
 | ||
|    generate a single prologue and a single epilogue.  This is
 | ||
|    sufficient to catch a number of interesting cases involving early
 | ||
|    exits.
 | ||
| 
 | ||
|    First, we identify the blocks that require the prologue to occur before
 | ||
|    them.  These are the ones that modify a call-saved register, or reference
 | ||
|    any of the stack or frame pointer registers.  To simplify things, we then
 | ||
|    mark everything reachable from these blocks as also requiring a prologue.
 | ||
|    This takes care of loops automatically, and avoids the need to examine
 | ||
|    whether MEMs reference the frame, since it is sufficient to check for
 | ||
|    occurrences of the stack or frame pointer.
 | ||
| 
 | ||
|    We then compute the set of blocks for which the need for a prologue
 | ||
|    is anticipatable (borrowing terminology from the shrink-wrapping
 | ||
|    description in Muchnick's book).  These are the blocks which either
 | ||
|    require a prologue themselves, or those that have only successors
 | ||
|    where the prologue is anticipatable.  The prologue needs to be
 | ||
|    inserted on all edges from BB1->BB2 where BB2 is in ANTIC and BB1
 | ||
|    is not.  For the moment, we ensure that only one such edge exists.
 | ||
| 
 | ||
|    The epilogue is placed as described above, but we make a
 | ||
|    distinction between inserting return and simple_return patterns
 | ||
|    when modifying other blocks that end in a return.  Blocks that end
 | ||
|    in a sibcall omit the sibcall_epilogue if the block is not in
 | ||
|    ANTIC.  */
 | ||
| 
 | ||
| static void
 | ||
| thread_prologue_and_epilogue_insns (void)
 | ||
| {
 | ||
|   bool inserted;
 | ||
| #ifdef HAVE_simple_return
 | ||
|   VEC (edge, heap) *unconverted_simple_returns = NULL;
 | ||
|   bool nonempty_prologue;
 | ||
|   bitmap_head bb_flags;
 | ||
|   unsigned max_grow_size;
 | ||
| #endif
 | ||
|   rtx returnjump;
 | ||
|   rtx seq ATTRIBUTE_UNUSED, epilogue_end ATTRIBUTE_UNUSED;
 | ||
|   rtx prologue_seq ATTRIBUTE_UNUSED, split_prologue_seq ATTRIBUTE_UNUSED;
 | ||
|   edge e, entry_edge, orig_entry_edge, exit_fallthru_edge;
 | ||
|   edge_iterator ei;
 | ||
| 
 | ||
|   df_analyze ();
 | ||
| 
 | ||
|   rtl_profile_for_bb (ENTRY_BLOCK_PTR);
 | ||
| 
 | ||
|   inserted = false;
 | ||
|   seq = NULL_RTX;
 | ||
|   epilogue_end = NULL_RTX;
 | ||
|   returnjump = NULL_RTX;
 | ||
| 
 | ||
|   /* Can't deal with multiple successors of the entry block at the
 | ||
|      moment.  Function should always have at least one entry
 | ||
|      point.  */
 | ||
|   gcc_assert (single_succ_p (ENTRY_BLOCK_PTR));
 | ||
|   entry_edge = single_succ_edge (ENTRY_BLOCK_PTR);
 | ||
|   orig_entry_edge = entry_edge;
 | ||
| 
 | ||
|   split_prologue_seq = NULL_RTX;
 | ||
|   if (flag_split_stack
 | ||
|       && (lookup_attribute ("no_split_stack", DECL_ATTRIBUTES (cfun->decl))
 | ||
| 	  == NULL))
 | ||
|     {
 | ||
| #ifndef HAVE_split_stack_prologue
 | ||
|       gcc_unreachable ();
 | ||
| #else
 | ||
|       gcc_assert (HAVE_split_stack_prologue);
 | ||
| 
 | ||
|       start_sequence ();
 | ||
|       emit_insn (gen_split_stack_prologue ());
 | ||
|       split_prologue_seq = get_insns ();
 | ||
|       end_sequence ();
 | ||
| 
 | ||
|       record_insns (split_prologue_seq, NULL, &prologue_insn_hash);
 | ||
|       set_insn_locators (split_prologue_seq, prologue_locator);
 | ||
| #endif
 | ||
|     }
 | ||
| 
 | ||
|   prologue_seq = NULL_RTX;
 | ||
| #ifdef HAVE_prologue
 | ||
|   if (HAVE_prologue)
 | ||
|     {
 | ||
|       start_sequence ();
 | ||
|       seq = gen_prologue ();
 | ||
|       emit_insn (seq);
 | ||
| 
 | ||
|       /* Insert an explicit USE for the frame pointer
 | ||
|          if the profiling is on and the frame pointer is required.  */
 | ||
|       if (crtl->profile && frame_pointer_needed)
 | ||
| 	emit_use (hard_frame_pointer_rtx);
 | ||
| 
 | ||
|       /* Retain a map of the prologue insns.  */
 | ||
|       record_insns (seq, NULL, &prologue_insn_hash);
 | ||
|       emit_note (NOTE_INSN_PROLOGUE_END);
 | ||
| 
 | ||
|       /* Ensure that instructions are not moved into the prologue when
 | ||
| 	 profiling is on.  The call to the profiling routine can be
 | ||
| 	 emitted within the live range of a call-clobbered register.  */
 | ||
|       if (!targetm.profile_before_prologue () && crtl->profile)
 | ||
|         emit_insn (gen_blockage ());
 | ||
| 
 | ||
|       prologue_seq = get_insns ();
 | ||
|       end_sequence ();
 | ||
|       set_insn_locators (prologue_seq, prologue_locator);
 | ||
|     }
 | ||
| #endif
 | ||
| 
 | ||
| #ifdef HAVE_simple_return
 | ||
|   bitmap_initialize (&bb_flags, &bitmap_default_obstack);
 | ||
| 
 | ||
|   /* Try to perform a kind of shrink-wrapping, making sure the
 | ||
|      prologue/epilogue is emitted only around those parts of the
 | ||
|      function that require it.  */
 | ||
| 
 | ||
|   nonempty_prologue = false;
 | ||
|   for (seq = prologue_seq; seq; seq = NEXT_INSN (seq))
 | ||
|     if (!NOTE_P (seq) || NOTE_KIND (seq) != NOTE_INSN_PROLOGUE_END)
 | ||
|       {
 | ||
| 	nonempty_prologue = true;
 | ||
| 	break;
 | ||
|       }
 | ||
|       
 | ||
|   if (flag_shrink_wrap && HAVE_simple_return
 | ||
|       && (targetm.profile_before_prologue () || !crtl->profile)
 | ||
|       && nonempty_prologue && !crtl->calls_eh_return)
 | ||
|     {
 | ||
|       HARD_REG_SET prologue_clobbered, prologue_used, live_on_edge;
 | ||
|       struct hard_reg_set_container set_up_by_prologue;
 | ||
|       rtx p_insn;
 | ||
|       VEC(basic_block, heap) *vec;
 | ||
|       basic_block bb;
 | ||
|       bitmap_head bb_antic_flags;
 | ||
|       bitmap_head bb_on_list;
 | ||
|       bitmap_head bb_tail;
 | ||
| 
 | ||
|       if (dump_file)
 | ||
| 	fprintf (dump_file, "Attempting shrink-wrapping optimization.\n");
 | ||
| 
 | ||
|       /* Compute the registers set and used in the prologue.  */
 | ||
|       CLEAR_HARD_REG_SET (prologue_clobbered);
 | ||
|       CLEAR_HARD_REG_SET (prologue_used);
 | ||
|       for (p_insn = prologue_seq; p_insn; p_insn = NEXT_INSN (p_insn))
 | ||
| 	{
 | ||
| 	  HARD_REG_SET this_used;
 | ||
| 	  if (!NONDEBUG_INSN_P (p_insn))
 | ||
| 	    continue;
 | ||
| 
 | ||
| 	  CLEAR_HARD_REG_SET (this_used);
 | ||
| 	  note_uses (&PATTERN (p_insn), record_hard_reg_uses,
 | ||
| 		     &this_used);
 | ||
| 	  AND_COMPL_HARD_REG_SET (this_used, prologue_clobbered);
 | ||
| 	  IOR_HARD_REG_SET (prologue_used, this_used);
 | ||
| 	  note_stores (PATTERN (p_insn), record_hard_reg_sets,
 | ||
| 		       &prologue_clobbered);
 | ||
| 	}
 | ||
| 
 | ||
|       prepare_shrink_wrap (entry_edge->dest);
 | ||
| 
 | ||
|       bitmap_initialize (&bb_antic_flags, &bitmap_default_obstack);
 | ||
|       bitmap_initialize (&bb_on_list, &bitmap_default_obstack);
 | ||
|       bitmap_initialize (&bb_tail, &bitmap_default_obstack);
 | ||
| 
 | ||
|       /* Find the set of basic blocks that require a stack frame,
 | ||
| 	 and blocks that are too big to be duplicated.  */
 | ||
| 
 | ||
|       vec = VEC_alloc (basic_block, heap, n_basic_blocks);
 | ||
| 
 | ||
|       CLEAR_HARD_REG_SET (set_up_by_prologue.set);
 | ||
|       add_to_hard_reg_set (&set_up_by_prologue.set, Pmode,
 | ||
| 			   STACK_POINTER_REGNUM);
 | ||
|       add_to_hard_reg_set (&set_up_by_prologue.set, Pmode, ARG_POINTER_REGNUM);
 | ||
|       if (frame_pointer_needed)
 | ||
| 	add_to_hard_reg_set (&set_up_by_prologue.set, Pmode,
 | ||
| 			     HARD_FRAME_POINTER_REGNUM);
 | ||
|       if (pic_offset_table_rtx)
 | ||
| 	add_to_hard_reg_set (&set_up_by_prologue.set, Pmode,
 | ||
| 			     PIC_OFFSET_TABLE_REGNUM);
 | ||
|       if (stack_realign_drap && crtl->drap_reg)
 | ||
| 	add_to_hard_reg_set (&set_up_by_prologue.set,
 | ||
| 			     GET_MODE (crtl->drap_reg),
 | ||
| 			     REGNO (crtl->drap_reg));
 | ||
|       if (targetm.set_up_by_prologue)
 | ||
| 	targetm.set_up_by_prologue (&set_up_by_prologue);
 | ||
| 
 | ||
|       /* We don't use a different max size depending on
 | ||
| 	 optimize_bb_for_speed_p because increasing shrink-wrapping
 | ||
| 	 opportunities by duplicating tail blocks can actually result
 | ||
| 	 in an overall decrease in code size.  */
 | ||
|       max_grow_size = get_uncond_jump_length ();
 | ||
|       max_grow_size *= PARAM_VALUE (PARAM_MAX_GROW_COPY_BB_INSNS);
 | ||
| 
 | ||
|       FOR_EACH_BB (bb)
 | ||
| 	{
 | ||
| 	  rtx insn;
 | ||
| 	  unsigned size = 0;
 | ||
| 
 | ||
| 	  FOR_BB_INSNS (bb, insn)
 | ||
| 	    if (NONDEBUG_INSN_P (insn))
 | ||
| 	      {
 | ||
| 		if (requires_stack_frame_p (insn, prologue_used,
 | ||
| 					    set_up_by_prologue.set))
 | ||
| 		  {
 | ||
| 		    if (bb == entry_edge->dest)
 | ||
| 		      goto fail_shrinkwrap;
 | ||
| 		    bitmap_set_bit (&bb_flags, bb->index);
 | ||
| 		    VEC_quick_push (basic_block, vec, bb);
 | ||
| 		    break;
 | ||
| 		  }
 | ||
| 		else if (size <= max_grow_size)
 | ||
| 		  {
 | ||
| 		    size += get_attr_min_length (insn);
 | ||
| 		    if (size > max_grow_size)
 | ||
| 		      bitmap_set_bit (&bb_on_list, bb->index);
 | ||
| 		  }
 | ||
| 	      }
 | ||
| 	}
 | ||
| 
 | ||
|       /* Blocks that really need a prologue, or are too big for tails.  */
 | ||
|       bitmap_ior_into (&bb_on_list, &bb_flags);
 | ||
| 
 | ||
|       /* For every basic block that needs a prologue, mark all blocks
 | ||
| 	 reachable from it, so as to ensure they are also seen as
 | ||
| 	 requiring a prologue.  */
 | ||
|       while (!VEC_empty (basic_block, vec))
 | ||
| 	{
 | ||
| 	  basic_block tmp_bb = VEC_pop (basic_block, vec);
 | ||
| 
 | ||
| 	  FOR_EACH_EDGE (e, ei, tmp_bb->succs)
 | ||
| 	    if (e->dest != EXIT_BLOCK_PTR
 | ||
| 		&& bitmap_set_bit (&bb_flags, e->dest->index))
 | ||
| 	      VEC_quick_push (basic_block, vec, e->dest);
 | ||
| 	}
 | ||
| 
 | ||
|       /* Find the set of basic blocks that need no prologue, have a
 | ||
| 	 single successor, can be duplicated, meet a max size
 | ||
| 	 requirement, and go to the exit via like blocks.  */
 | ||
|       VEC_quick_push (basic_block, vec, EXIT_BLOCK_PTR);
 | ||
|       while (!VEC_empty (basic_block, vec))
 | ||
| 	{
 | ||
| 	  basic_block tmp_bb = VEC_pop (basic_block, vec);
 | ||
| 
 | ||
| 	  FOR_EACH_EDGE (e, ei, tmp_bb->preds)
 | ||
| 	    if (single_succ_p (e->src)
 | ||
| 		&& !bitmap_bit_p (&bb_on_list, e->src->index)
 | ||
| 		&& can_duplicate_block_p (e->src))
 | ||
| 	      {
 | ||
| 		edge pe;
 | ||
| 		edge_iterator pei;
 | ||
| 
 | ||
| 		/* If there is predecessor of e->src which doesn't
 | ||
| 		   need prologue and the edge is complex,
 | ||
| 		   we might not be able to redirect the branch
 | ||
| 		   to a copy of e->src.  */
 | ||
| 		FOR_EACH_EDGE (pe, pei, e->src->preds)
 | ||
| 		  if ((pe->flags & EDGE_COMPLEX) != 0
 | ||
| 		      && !bitmap_bit_p (&bb_flags, pe->src->index))
 | ||
| 		    break;
 | ||
| 		if (pe == NULL && bitmap_set_bit (&bb_tail, e->src->index))
 | ||
| 		  VEC_quick_push (basic_block, vec, e->src);
 | ||
| 	      }
 | ||
| 	}
 | ||
| 
 | ||
|       /* Now walk backwards from every block that is marked as needing
 | ||
| 	 a prologue to compute the bb_antic_flags bitmap.  Exclude
 | ||
| 	 tail blocks; They can be duplicated to be used on paths not
 | ||
| 	 needing a prologue.  */
 | ||
|       bitmap_clear (&bb_on_list);
 | ||
|       bitmap_and_compl (&bb_antic_flags, &bb_flags, &bb_tail);
 | ||
|       FOR_EACH_BB (bb)
 | ||
| 	{
 | ||
| 	  if (!bitmap_bit_p (&bb_antic_flags, bb->index))
 | ||
| 	    continue;
 | ||
| 	  FOR_EACH_EDGE (e, ei, bb->preds)
 | ||
| 	    if (!bitmap_bit_p (&bb_antic_flags, e->src->index)
 | ||
| 		&& bitmap_set_bit (&bb_on_list, e->src->index))
 | ||
| 	      VEC_quick_push (basic_block, vec, e->src);
 | ||
| 	}
 | ||
|       while (!VEC_empty (basic_block, vec))
 | ||
| 	{
 | ||
| 	  basic_block tmp_bb = VEC_pop (basic_block, vec);
 | ||
| 	  bool all_set = true;
 | ||
| 
 | ||
| 	  bitmap_clear_bit (&bb_on_list, tmp_bb->index);
 | ||
| 	  FOR_EACH_EDGE (e, ei, tmp_bb->succs)
 | ||
| 	    if (!bitmap_bit_p (&bb_antic_flags, e->dest->index))
 | ||
| 	      {
 | ||
| 		all_set = false;
 | ||
| 		break;
 | ||
| 	      }
 | ||
| 
 | ||
| 	  if (all_set)
 | ||
| 	    {
 | ||
| 	      bitmap_set_bit (&bb_antic_flags, tmp_bb->index);
 | ||
| 	      FOR_EACH_EDGE (e, ei, tmp_bb->preds)
 | ||
| 		if (!bitmap_bit_p (&bb_antic_flags, e->src->index)
 | ||
| 		    && bitmap_set_bit (&bb_on_list, e->src->index))
 | ||
| 		  VEC_quick_push (basic_block, vec, e->src);
 | ||
| 	    }
 | ||
| 	}
 | ||
|       /* Find exactly one edge that leads to a block in ANTIC from
 | ||
| 	 a block that isn't.  */
 | ||
|       if (!bitmap_bit_p (&bb_antic_flags, entry_edge->dest->index))
 | ||
| 	FOR_EACH_BB (bb)
 | ||
| 	  {
 | ||
| 	    if (!bitmap_bit_p (&bb_antic_flags, bb->index))
 | ||
| 	      continue;
 | ||
| 	    FOR_EACH_EDGE (e, ei, bb->preds)
 | ||
| 	      if (!bitmap_bit_p (&bb_antic_flags, e->src->index))
 | ||
| 		{
 | ||
| 		  if (entry_edge != orig_entry_edge)
 | ||
| 		    {
 | ||
| 		      entry_edge = orig_entry_edge;
 | ||
| 		      if (dump_file)
 | ||
| 			fprintf (dump_file, "More than one candidate edge.\n");
 | ||
| 		      goto fail_shrinkwrap;
 | ||
| 		    }
 | ||
| 		  if (dump_file)
 | ||
| 		    fprintf (dump_file, "Found candidate edge for "
 | ||
| 			     "shrink-wrapping, %d->%d.\n", e->src->index,
 | ||
| 			     e->dest->index);
 | ||
| 		  entry_edge = e;
 | ||
| 		}
 | ||
| 	  }
 | ||
| 
 | ||
|       if (entry_edge != orig_entry_edge)
 | ||
| 	{
 | ||
| 	  /* Test whether the prologue is known to clobber any register
 | ||
| 	     (other than FP or SP) which are live on the edge.  */
 | ||
| 	  CLEAR_HARD_REG_BIT (prologue_clobbered, STACK_POINTER_REGNUM);
 | ||
| 	  if (frame_pointer_needed)
 | ||
| 	    CLEAR_HARD_REG_BIT (prologue_clobbered, HARD_FRAME_POINTER_REGNUM);
 | ||
| 	  CLEAR_HARD_REG_SET (live_on_edge);
 | ||
| 	  reg_set_to_hard_reg_set (&live_on_edge,
 | ||
| 				   df_get_live_in (entry_edge->dest));
 | ||
| 	  if (hard_reg_set_intersect_p (live_on_edge, prologue_clobbered))
 | ||
| 	    {
 | ||
| 	      entry_edge = orig_entry_edge;
 | ||
| 	      if (dump_file)
 | ||
| 		fprintf (dump_file,
 | ||
| 			 "Shrink-wrapping aborted due to clobber.\n");
 | ||
| 	    }
 | ||
| 	}
 | ||
|       if (entry_edge != orig_entry_edge)
 | ||
| 	{
 | ||
| 	  crtl->shrink_wrapped = true;
 | ||
| 	  if (dump_file)
 | ||
| 	    fprintf (dump_file, "Performing shrink-wrapping.\n");
 | ||
| 
 | ||
| 	  /* Find tail blocks reachable from both blocks needing a
 | ||
| 	     prologue and blocks not needing a prologue.  */
 | ||
| 	  if (!bitmap_empty_p (&bb_tail))
 | ||
| 	    FOR_EACH_BB (bb)
 | ||
| 	      {
 | ||
| 		bool some_pro, some_no_pro;
 | ||
| 		if (!bitmap_bit_p (&bb_tail, bb->index))
 | ||
| 		  continue;
 | ||
| 		some_pro = some_no_pro = false;
 | ||
| 		FOR_EACH_EDGE (e, ei, bb->preds)
 | ||
| 		  {
 | ||
| 		    if (bitmap_bit_p (&bb_flags, e->src->index))
 | ||
| 		      some_pro = true;
 | ||
| 		    else
 | ||
| 		      some_no_pro = true;
 | ||
| 		  }
 | ||
| 		if (some_pro && some_no_pro)
 | ||
| 		  VEC_quick_push (basic_block, vec, bb);
 | ||
| 		else
 | ||
| 		  bitmap_clear_bit (&bb_tail, bb->index);
 | ||
| 	      }
 | ||
| 	  /* Find the head of each tail.  */
 | ||
| 	  while (!VEC_empty (basic_block, vec))
 | ||
| 	    {
 | ||
| 	      basic_block tbb = VEC_pop (basic_block, vec);
 | ||
| 
 | ||
| 	      if (!bitmap_bit_p (&bb_tail, tbb->index))
 | ||
| 		continue;
 | ||
| 
 | ||
| 	      while (single_succ_p (tbb))
 | ||
| 		{
 | ||
| 		  tbb = single_succ (tbb);
 | ||
| 		  bitmap_clear_bit (&bb_tail, tbb->index);
 | ||
| 		}
 | ||
| 	    }
 | ||
| 	  /* Now duplicate the tails.  */
 | ||
| 	  if (!bitmap_empty_p (&bb_tail))
 | ||
| 	    FOR_EACH_BB_REVERSE (bb)
 | ||
| 	      {
 | ||
| 		basic_block copy_bb, tbb;
 | ||
| 		rtx insert_point;
 | ||
| 		int eflags;
 | ||
| 
 | ||
| 		if (!bitmap_clear_bit (&bb_tail, bb->index))
 | ||
| 		  continue;
 | ||
| 
 | ||
| 		/* Create a copy of BB, instructions and all, for
 | ||
| 		   use on paths that don't need a prologue.
 | ||
| 		   Ideal placement of the copy is on a fall-thru edge
 | ||
| 		   or after a block that would jump to the copy.  */ 
 | ||
| 		FOR_EACH_EDGE (e, ei, bb->preds)
 | ||
| 		  if (!bitmap_bit_p (&bb_flags, e->src->index)
 | ||
| 		      && single_succ_p (e->src))
 | ||
| 		    break;
 | ||
| 		if (e)
 | ||
| 		  {
 | ||
| 		    copy_bb = create_basic_block (NEXT_INSN (BB_END (e->src)),
 | ||
| 						  NULL_RTX, e->src);
 | ||
| 		    BB_COPY_PARTITION (copy_bb, e->src);
 | ||
| 		  }
 | ||
| 		else
 | ||
| 		  {
 | ||
| 		    /* Otherwise put the copy at the end of the function.  */
 | ||
| 		    copy_bb = create_basic_block (NULL_RTX, NULL_RTX,
 | ||
| 						  EXIT_BLOCK_PTR->prev_bb);
 | ||
| 		    BB_COPY_PARTITION (copy_bb, bb);
 | ||
| 		  }
 | ||
| 
 | ||
| 		insert_point = emit_note_after (NOTE_INSN_DELETED,
 | ||
| 						BB_END (copy_bb));
 | ||
| 		emit_barrier_after (BB_END (copy_bb));
 | ||
| 
 | ||
| 		tbb = bb;
 | ||
| 		while (1)
 | ||
| 		  {
 | ||
| 		    dup_block_and_redirect (tbb, copy_bb, insert_point,
 | ||
| 					    &bb_flags);
 | ||
| 		    tbb = single_succ (tbb);
 | ||
| 		    if (tbb == EXIT_BLOCK_PTR)
 | ||
| 		      break;
 | ||
| 		    e = split_block (copy_bb, PREV_INSN (insert_point));
 | ||
| 		    copy_bb = e->dest;
 | ||
| 		  }
 | ||
| 
 | ||
| 		/* Quiet verify_flow_info by (ab)using EDGE_FAKE.
 | ||
| 		   We have yet to add a simple_return to the tails,
 | ||
| 		   as we'd like to first convert_jumps_to_returns in
 | ||
| 		   case the block is no longer used after that.  */
 | ||
| 		eflags = EDGE_FAKE;
 | ||
| 		if (CALL_P (PREV_INSN (insert_point))
 | ||
| 		    && SIBLING_CALL_P (PREV_INSN (insert_point)))
 | ||
| 		  eflags = EDGE_SIBCALL | EDGE_ABNORMAL;
 | ||
| 		make_single_succ_edge (copy_bb, EXIT_BLOCK_PTR, eflags);
 | ||
| 
 | ||
| 		/* verify_flow_info doesn't like a note after a
 | ||
| 		   sibling call.  */
 | ||
| 		delete_insn (insert_point);
 | ||
| 		if (bitmap_empty_p (&bb_tail))
 | ||
| 		  break;
 | ||
| 	      }
 | ||
| 	}
 | ||
| 
 | ||
|     fail_shrinkwrap:
 | ||
|       bitmap_clear (&bb_tail);
 | ||
|       bitmap_clear (&bb_antic_flags);
 | ||
|       bitmap_clear (&bb_on_list);
 | ||
|       VEC_free (basic_block, heap, vec);
 | ||
|     }
 | ||
| #endif
 | ||
| 
 | ||
|   if (split_prologue_seq != NULL_RTX)
 | ||
|     {
 | ||
|       insert_insn_on_edge (split_prologue_seq, orig_entry_edge);
 | ||
|       inserted = true;
 | ||
|     }
 | ||
|   if (prologue_seq != NULL_RTX)
 | ||
|     {
 | ||
|       insert_insn_on_edge (prologue_seq, entry_edge);
 | ||
|       inserted = true;
 | ||
|     }
 | ||
| 
 | ||
|   /* If the exit block has no non-fake predecessors, we don't need
 | ||
|      an epilogue.  */
 | ||
|   FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
 | ||
|     if ((e->flags & EDGE_FAKE) == 0)
 | ||
|       break;
 | ||
|   if (e == NULL)
 | ||
|     goto epilogue_done;
 | ||
| 
 | ||
|   rtl_profile_for_bb (EXIT_BLOCK_PTR);
 | ||
| 
 | ||
|   exit_fallthru_edge = find_fallthru_edge (EXIT_BLOCK_PTR->preds);
 | ||
| 
 | ||
|   /* If we're allowed to generate a simple return instruction, then by
 | ||
|      definition we don't need a full epilogue.  If the last basic
 | ||
|      block before the exit block does not contain active instructions,
 | ||
|      examine its predecessors and try to emit (conditional) return
 | ||
|      instructions.  */
 | ||
| #ifdef HAVE_simple_return
 | ||
|   if (entry_edge != orig_entry_edge)
 | ||
|     {
 | ||
|       if (optimize)
 | ||
| 	{
 | ||
| 	  unsigned i, last;
 | ||
| 
 | ||
| 	  /* convert_jumps_to_returns may add to EXIT_BLOCK_PTR->preds
 | ||
| 	     (but won't remove).  Stop at end of current preds.  */
 | ||
| 	  last = EDGE_COUNT (EXIT_BLOCK_PTR->preds);
 | ||
| 	  for (i = 0; i < last; i++)
 | ||
| 	    {
 | ||
| 	      e = EDGE_I (EXIT_BLOCK_PTR->preds, i);
 | ||
| 	      if (LABEL_P (BB_HEAD (e->src))
 | ||
| 		  && !bitmap_bit_p (&bb_flags, e->src->index)
 | ||
| 		  && !active_insn_between (BB_HEAD (e->src), BB_END (e->src)))
 | ||
| 		unconverted_simple_returns
 | ||
| 		  = convert_jumps_to_returns (e->src, true,
 | ||
| 					      unconverted_simple_returns);
 | ||
| 	    }
 | ||
| 	}
 | ||
| 
 | ||
|       if (exit_fallthru_edge != NULL
 | ||
| 	  && EDGE_COUNT (exit_fallthru_edge->src->preds) != 0
 | ||
| 	  && !bitmap_bit_p (&bb_flags, exit_fallthru_edge->src->index))
 | ||
| 	{
 | ||
| 	  basic_block last_bb;
 | ||
| 
 | ||
| 	  last_bb = emit_return_for_exit (exit_fallthru_edge, true);
 | ||
| 	  returnjump = BB_END (last_bb);
 | ||
| 	  exit_fallthru_edge = NULL;
 | ||
| 	}
 | ||
|     }
 | ||
| #endif
 | ||
| #ifdef HAVE_return
 | ||
|   if (HAVE_return)
 | ||
|     {
 | ||
|       if (exit_fallthru_edge == NULL)
 | ||
| 	goto epilogue_done;
 | ||
| 
 | ||
|       if (optimize)
 | ||
| 	{
 | ||
| 	  basic_block last_bb = exit_fallthru_edge->src;
 | ||
| 
 | ||
| 	  if (LABEL_P (BB_HEAD (last_bb))
 | ||
| 	      && !active_insn_between (BB_HEAD (last_bb), BB_END (last_bb)))
 | ||
| 	    convert_jumps_to_returns (last_bb, false, NULL);
 | ||
| 
 | ||
| 	  if (EDGE_COUNT (last_bb->preds) != 0
 | ||
| 	      && single_succ_p (last_bb))
 | ||
| 	    {
 | ||
| 	      last_bb = emit_return_for_exit (exit_fallthru_edge, false);
 | ||
| 	      epilogue_end = returnjump = BB_END (last_bb);
 | ||
| #ifdef HAVE_simple_return
 | ||
| 	      /* Emitting the return may add a basic block.
 | ||
| 		 Fix bb_flags for the added block.  */
 | ||
| 	      if (last_bb != exit_fallthru_edge->src)
 | ||
| 		bitmap_set_bit (&bb_flags, last_bb->index);
 | ||
| #endif
 | ||
| 	      goto epilogue_done;
 | ||
| 	    }
 | ||
| 	}
 | ||
|     }
 | ||
| #endif
 | ||
| 
 | ||
|   /* A small fib -- epilogue is not yet completed, but we wish to re-use
 | ||
|      this marker for the splits of EH_RETURN patterns, and nothing else
 | ||
|      uses the flag in the meantime.  */
 | ||
|   epilogue_completed = 1;
 | ||
| 
 | ||
| #ifdef HAVE_eh_return
 | ||
|   /* Find non-fallthru edges that end with EH_RETURN instructions.  On
 | ||
|      some targets, these get split to a special version of the epilogue
 | ||
|      code.  In order to be able to properly annotate these with unwind
 | ||
|      info, try to split them now.  If we get a valid split, drop an
 | ||
|      EPILOGUE_BEG note and mark the insns as epilogue insns.  */
 | ||
|   FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
 | ||
|     {
 | ||
|       rtx prev, last, trial;
 | ||
| 
 | ||
|       if (e->flags & EDGE_FALLTHRU)
 | ||
| 	continue;
 | ||
|       last = BB_END (e->src);
 | ||
|       if (!eh_returnjump_p (last))
 | ||
| 	continue;
 | ||
| 
 | ||
|       prev = PREV_INSN (last);
 | ||
|       trial = try_split (PATTERN (last), last, 1);
 | ||
|       if (trial == last)
 | ||
| 	continue;
 | ||
| 
 | ||
|       record_insns (NEXT_INSN (prev), NEXT_INSN (trial), &epilogue_insn_hash);
 | ||
|       emit_note_after (NOTE_INSN_EPILOGUE_BEG, prev);
 | ||
|     }
 | ||
| #endif
 | ||
| 
 | ||
|   /* If nothing falls through into the exit block, we don't need an
 | ||
|      epilogue.  */
 | ||
| 
 | ||
|   if (exit_fallthru_edge == NULL)
 | ||
|     goto epilogue_done;
 | ||
| 
 | ||
| #ifdef HAVE_epilogue
 | ||
|   if (HAVE_epilogue)
 | ||
|     {
 | ||
|       start_sequence ();
 | ||
|       epilogue_end = emit_note (NOTE_INSN_EPILOGUE_BEG);
 | ||
|       seq = gen_epilogue ();
 | ||
|       if (seq)
 | ||
| 	emit_jump_insn (seq);
 | ||
| 
 | ||
|       /* Retain a map of the epilogue insns.  */
 | ||
|       record_insns (seq, NULL, &epilogue_insn_hash);
 | ||
|       set_insn_locators (seq, epilogue_locator);
 | ||
| 
 | ||
|       seq = get_insns ();
 | ||
|       returnjump = get_last_insn ();
 | ||
|       end_sequence ();
 | ||
| 
 | ||
|       insert_insn_on_edge (seq, exit_fallthru_edge);
 | ||
|       inserted = true;
 | ||
| 
 | ||
|       if (JUMP_P (returnjump))
 | ||
| 	set_return_jump_label (returnjump);
 | ||
|     }
 | ||
|   else
 | ||
| #endif
 | ||
|     {
 | ||
|       basic_block cur_bb;
 | ||
| 
 | ||
|       if (! next_active_insn (BB_END (exit_fallthru_edge->src)))
 | ||
| 	goto epilogue_done;
 | ||
|       /* We have a fall-through edge to the exit block, the source is not
 | ||
|          at the end of the function, and there will be an assembler epilogue
 | ||
|          at the end of the function.
 | ||
|          We can't use force_nonfallthru here, because that would try to
 | ||
| 	 use return.  Inserting a jump 'by hand' is extremely messy, so
 | ||
| 	 we take advantage of cfg_layout_finalize using
 | ||
| 	 fixup_fallthru_exit_predecessor.  */
 | ||
|       cfg_layout_initialize (0);
 | ||
|       FOR_EACH_BB (cur_bb)
 | ||
| 	if (cur_bb->index >= NUM_FIXED_BLOCKS
 | ||
| 	    && cur_bb->next_bb->index >= NUM_FIXED_BLOCKS)
 | ||
| 	  cur_bb->aux = cur_bb->next_bb;
 | ||
|       cfg_layout_finalize ();
 | ||
|     }
 | ||
| 
 | ||
| epilogue_done:
 | ||
| 
 | ||
|   default_rtl_profile ();
 | ||
| 
 | ||
|   if (inserted)
 | ||
|     {
 | ||
|       sbitmap blocks;
 | ||
| 
 | ||
|       commit_edge_insertions ();
 | ||
| 
 | ||
|       /* Look for basic blocks within the prologue insns.  */
 | ||
|       blocks = sbitmap_alloc (last_basic_block);
 | ||
|       sbitmap_zero (blocks);
 | ||
|       SET_BIT (blocks, entry_edge->dest->index);
 | ||
|       SET_BIT (blocks, orig_entry_edge->dest->index);
 | ||
|       find_many_sub_basic_blocks (blocks);
 | ||
|       sbitmap_free (blocks);
 | ||
| 
 | ||
|       /* The epilogue insns we inserted may cause the exit edge to no longer
 | ||
| 	 be fallthru.  */
 | ||
|       FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
 | ||
| 	{
 | ||
| 	  if (((e->flags & EDGE_FALLTHRU) != 0)
 | ||
| 	      && returnjump_p (BB_END (e->src)))
 | ||
| 	    e->flags &= ~EDGE_FALLTHRU;
 | ||
| 	}
 | ||
|     }
 | ||
| 
 | ||
| #ifdef HAVE_simple_return
 | ||
|   /* If there were branches to an empty LAST_BB which we tried to
 | ||
|      convert to conditional simple_returns, but couldn't for some
 | ||
|      reason, create a block to hold a simple_return insn and redirect
 | ||
|      those remaining edges.  */
 | ||
|   if (!VEC_empty (edge, unconverted_simple_returns))
 | ||
|     {
 | ||
|       basic_block simple_return_block_hot = NULL;
 | ||
|       basic_block simple_return_block_cold = NULL;
 | ||
|       edge pending_edge_hot = NULL;
 | ||
|       edge pending_edge_cold = NULL;
 | ||
|       basic_block exit_pred = EXIT_BLOCK_PTR->prev_bb;
 | ||
|       int i;
 | ||
| 
 | ||
|       gcc_assert (entry_edge != orig_entry_edge);
 | ||
| 
 | ||
|       /* See if we can reuse the last insn that was emitted for the
 | ||
| 	 epilogue.  */
 | ||
|       if (returnjump != NULL_RTX
 | ||
| 	  && JUMP_LABEL (returnjump) == simple_return_rtx)
 | ||
| 	{
 | ||
| 	  e = split_block (BLOCK_FOR_INSN (returnjump), PREV_INSN (returnjump));
 | ||
| 	  if (BB_PARTITION (e->src) == BB_HOT_PARTITION)
 | ||
| 	    simple_return_block_hot = e->dest;
 | ||
| 	  else
 | ||
| 	    simple_return_block_cold = e->dest;
 | ||
| 	}
 | ||
| 
 | ||
|       /* Also check returns we might need to add to tail blocks.  */
 | ||
|       FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
 | ||
| 	if (EDGE_COUNT (e->src->preds) != 0
 | ||
| 	    && (e->flags & EDGE_FAKE) != 0
 | ||
| 	    && !bitmap_bit_p (&bb_flags, e->src->index))
 | ||
| 	  {
 | ||
| 	    if (BB_PARTITION (e->src) == BB_HOT_PARTITION)
 | ||
| 	      pending_edge_hot = e;
 | ||
| 	    else
 | ||
| 	      pending_edge_cold = e;
 | ||
| 	  }
 | ||
| 
 | ||
|       FOR_EACH_VEC_ELT (edge, unconverted_simple_returns, i, e)
 | ||
| 	{
 | ||
| 	  basic_block *pdest_bb;
 | ||
| 	  edge pending;
 | ||
| 
 | ||
| 	  if (BB_PARTITION (e->src) == BB_HOT_PARTITION)
 | ||
| 	    {
 | ||
| 	      pdest_bb = &simple_return_block_hot;
 | ||
| 	      pending = pending_edge_hot;
 | ||
| 	    }
 | ||
| 	  else
 | ||
| 	    {
 | ||
| 	      pdest_bb = &simple_return_block_cold;
 | ||
| 	      pending = pending_edge_cold;
 | ||
| 	    }
 | ||
| 
 | ||
| 	  if (*pdest_bb == NULL && pending != NULL)
 | ||
| 	    {
 | ||
| 	      emit_return_into_block (true, pending->src);
 | ||
| 	      pending->flags &= ~(EDGE_FALLTHRU | EDGE_FAKE);
 | ||
| 	      *pdest_bb = pending->src;
 | ||
| 	    }
 | ||
| 	  else if (*pdest_bb == NULL)
 | ||
| 	    {
 | ||
| 	      basic_block bb;
 | ||
| 	      rtx start;
 | ||
| 
 | ||
| 	      bb = create_basic_block (NULL, NULL, exit_pred);
 | ||
| 	      BB_COPY_PARTITION (bb, e->src);
 | ||
| 	      start = emit_jump_insn_after (gen_simple_return (),
 | ||
| 					    BB_END (bb));
 | ||
| 	      JUMP_LABEL (start) = simple_return_rtx;
 | ||
| 	      emit_barrier_after (start);
 | ||
| 
 | ||
| 	      *pdest_bb = bb;
 | ||
| 	      make_edge (bb, EXIT_BLOCK_PTR, 0);
 | ||
| 	    }
 | ||
| 	  redirect_edge_and_branch_force (e, *pdest_bb);
 | ||
| 	}
 | ||
|       VEC_free (edge, heap, unconverted_simple_returns);
 | ||
|     }
 | ||
| 
 | ||
|   if (entry_edge != orig_entry_edge)
 | ||
|     {
 | ||
|       FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
 | ||
| 	if (EDGE_COUNT (e->src->preds) != 0
 | ||
| 	    && (e->flags & EDGE_FAKE) != 0
 | ||
| 	    && !bitmap_bit_p (&bb_flags, e->src->index))
 | ||
| 	  {
 | ||
| 	    emit_return_into_block (true, e->src);
 | ||
| 	    e->flags &= ~(EDGE_FALLTHRU | EDGE_FAKE);
 | ||
| 	  }
 | ||
|     }
 | ||
| #endif
 | ||
| 
 | ||
| #ifdef HAVE_sibcall_epilogue
 | ||
|   /* Emit sibling epilogues before any sibling call sites.  */
 | ||
|   for (ei = ei_start (EXIT_BLOCK_PTR->preds); (e = ei_safe_edge (ei)); )
 | ||
|     {
 | ||
|       basic_block bb = e->src;
 | ||
|       rtx insn = BB_END (bb);
 | ||
|       rtx ep_seq;
 | ||
| 
 | ||
|       if (!CALL_P (insn)
 | ||
| 	  || ! SIBLING_CALL_P (insn)
 | ||
| #ifdef HAVE_simple_return
 | ||
| 	  || (entry_edge != orig_entry_edge
 | ||
| 	      && !bitmap_bit_p (&bb_flags, bb->index))
 | ||
| #endif
 | ||
| 	  )
 | ||
| 	{
 | ||
| 	  ei_next (&ei);
 | ||
| 	  continue;
 | ||
| 	}
 | ||
| 
 | ||
|       ep_seq = gen_sibcall_epilogue ();
 | ||
|       if (ep_seq)
 | ||
| 	{
 | ||
| 	  start_sequence ();
 | ||
| 	  emit_note (NOTE_INSN_EPILOGUE_BEG);
 | ||
| 	  emit_insn (ep_seq);
 | ||
| 	  seq = get_insns ();
 | ||
| 	  end_sequence ();
 | ||
| 
 | ||
| 	  /* Retain a map of the epilogue insns.  Used in life analysis to
 | ||
| 	     avoid getting rid of sibcall epilogue insns.  Do this before we
 | ||
| 	     actually emit the sequence.  */
 | ||
| 	  record_insns (seq, NULL, &epilogue_insn_hash);
 | ||
| 	  set_insn_locators (seq, epilogue_locator);
 | ||
| 
 | ||
| 	  emit_insn_before (seq, insn);
 | ||
| 	}
 | ||
|       ei_next (&ei);
 | ||
|     }
 | ||
| #endif
 | ||
| 
 | ||
| #ifdef HAVE_epilogue
 | ||
|   if (epilogue_end)
 | ||
|     {
 | ||
|       rtx insn, next;
 | ||
| 
 | ||
|       /* Similarly, move any line notes that appear after the epilogue.
 | ||
|          There is no need, however, to be quite so anal about the existence
 | ||
| 	 of such a note.  Also possibly move
 | ||
| 	 NOTE_INSN_FUNCTION_BEG notes, as those can be relevant for debug
 | ||
| 	 info generation.  */
 | ||
|       for (insn = epilogue_end; insn; insn = next)
 | ||
| 	{
 | ||
| 	  next = NEXT_INSN (insn);
 | ||
| 	  if (NOTE_P (insn)
 | ||
| 	      && (NOTE_KIND (insn) == NOTE_INSN_FUNCTION_BEG))
 | ||
| 	    reorder_insns (insn, insn, PREV_INSN (epilogue_end));
 | ||
| 	}
 | ||
|     }
 | ||
| #endif
 | ||
| 
 | ||
| #ifdef HAVE_simple_return
 | ||
|   bitmap_clear (&bb_flags);
 | ||
| #endif
 | ||
| 
 | ||
|   /* Threading the prologue and epilogue changes the artificial refs
 | ||
|      in the entry and exit blocks.  */
 | ||
|   epilogue_completed = 1;
 | ||
|   df_update_entry_exit_and_calls ();
 | ||
| }
 | ||
| 
 | ||
| /* Reposition the prologue-end and epilogue-begin notes after
 | ||
|    instruction scheduling.  */
 | ||
| 
 | ||
| void
 | ||
| reposition_prologue_and_epilogue_notes (void)
 | ||
| {
 | ||
| #if defined (HAVE_prologue) || defined (HAVE_epilogue) \
 | ||
|     || defined (HAVE_sibcall_epilogue)
 | ||
|   /* Since the hash table is created on demand, the fact that it is
 | ||
|      non-null is a signal that it is non-empty.  */
 | ||
|   if (prologue_insn_hash != NULL)
 | ||
|     {
 | ||
|       size_t len = htab_elements (prologue_insn_hash);
 | ||
|       rtx insn, last = NULL, note = NULL;
 | ||
| 
 | ||
|       /* Scan from the beginning until we reach the last prologue insn.  */
 | ||
|       /* ??? While we do have the CFG intact, there are two problems:
 | ||
| 	 (1) The prologue can contain loops (typically probing the stack),
 | ||
| 	     which means that the end of the prologue isn't in the first bb.
 | ||
| 	 (2) Sometimes the PROLOGUE_END note gets pushed into the next bb.  */
 | ||
|       for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
 | ||
| 	{
 | ||
| 	  if (NOTE_P (insn))
 | ||
| 	    {
 | ||
| 	      if (NOTE_KIND (insn) == NOTE_INSN_PROLOGUE_END)
 | ||
| 		note = insn;
 | ||
| 	    }
 | ||
| 	  else if (contains (insn, prologue_insn_hash))
 | ||
| 	    {
 | ||
| 	      last = insn;
 | ||
| 	      if (--len == 0)
 | ||
| 		break;
 | ||
| 	    }
 | ||
| 	}
 | ||
| 
 | ||
|       if (last)
 | ||
| 	{
 | ||
| 	  if (note == NULL)
 | ||
| 	    {
 | ||
| 	      /* Scan forward looking for the PROLOGUE_END note.  It should
 | ||
| 		 be right at the beginning of the block, possibly with other
 | ||
| 		 insn notes that got moved there.  */
 | ||
| 	      for (note = NEXT_INSN (last); ; note = NEXT_INSN (note))
 | ||
| 		{
 | ||
| 		  if (NOTE_P (note)
 | ||
| 		      && NOTE_KIND (note) == NOTE_INSN_PROLOGUE_END)
 | ||
| 		    break;
 | ||
| 		}
 | ||
| 	    }
 | ||
| 
 | ||
| 	  /* Avoid placing note between CODE_LABEL and BASIC_BLOCK note.  */
 | ||
| 	  if (LABEL_P (last))
 | ||
| 	    last = NEXT_INSN (last);
 | ||
| 	  reorder_insns (note, note, last);
 | ||
| 	}
 | ||
|     }
 | ||
| 
 | ||
|   if (epilogue_insn_hash != NULL)
 | ||
|     {
 | ||
|       edge_iterator ei;
 | ||
|       edge e;
 | ||
| 
 | ||
|       FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
 | ||
| 	{
 | ||
| 	  rtx insn, first = NULL, note = NULL;
 | ||
| 	  basic_block bb = e->src;
 | ||
| 
 | ||
| 	  /* Scan from the beginning until we reach the first epilogue insn. */
 | ||
| 	  FOR_BB_INSNS (bb, insn)
 | ||
| 	    {
 | ||
| 	      if (NOTE_P (insn))
 | ||
| 		{
 | ||
| 		  if (NOTE_KIND (insn) == NOTE_INSN_EPILOGUE_BEG)
 | ||
| 		    {
 | ||
| 		      note = insn;
 | ||
| 		      if (first != NULL)
 | ||
| 			break;
 | ||
| 		    }
 | ||
| 		}
 | ||
| 	      else if (first == NULL && contains (insn, epilogue_insn_hash))
 | ||
| 		{
 | ||
| 		  first = insn;
 | ||
| 		  if (note != NULL)
 | ||
| 		    break;
 | ||
| 		}
 | ||
| 	    }
 | ||
| 
 | ||
| 	  if (note)
 | ||
| 	    {
 | ||
| 	      /* If the function has a single basic block, and no real
 | ||
| 		 epilogue insns (e.g. sibcall with no cleanup), the
 | ||
| 		 epilogue note can get scheduled before the prologue
 | ||
| 		 note.  If we have frame related prologue insns, having
 | ||
| 		 them scanned during the epilogue will result in a crash.
 | ||
| 		 In this case re-order the epilogue note to just before
 | ||
| 		 the last insn in the block.  */
 | ||
| 	      if (first == NULL)
 | ||
| 		first = BB_END (bb);
 | ||
| 
 | ||
| 	      if (PREV_INSN (first) != note)
 | ||
| 		reorder_insns (note, note, PREV_INSN (first));
 | ||
| 	    }
 | ||
| 	}
 | ||
|     }
 | ||
| #endif /* HAVE_prologue or HAVE_epilogue */
 | ||
| }
 | ||
| 
 | ||
| /* Returns the name of function FN.  */
 | ||
| const char *
 | ||
| function_name (struct function *fn)
 | ||
| {
 | ||
|   if (fn == NULL)
 | ||
|     return "(nofn)";
 | ||
|   return lang_hooks.decl_printable_name (fn->decl, 2);
 | ||
| }
 | ||
| 
 | ||
| /* Returns the name of the current function.  */
 | ||
| const char *
 | ||
| current_function_name (void)
 | ||
| {
 | ||
|   return function_name (cfun);
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| static unsigned int
 | ||
| rest_of_handle_check_leaf_regs (void)
 | ||
| {
 | ||
| #ifdef LEAF_REGISTERS
 | ||
|   crtl->uses_only_leaf_regs
 | ||
|     = optimize > 0 && only_leaf_regs_used () && leaf_function_p ();
 | ||
| #endif
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| /* Insert a TYPE into the used types hash table of CFUN.  */
 | ||
| 
 | ||
| static void
 | ||
| used_types_insert_helper (tree type, struct function *func)
 | ||
| {
 | ||
|   if (type != NULL && func != NULL)
 | ||
|     {
 | ||
|       void **slot;
 | ||
| 
 | ||
|       if (func->used_types_hash == NULL)
 | ||
| 	func->used_types_hash = htab_create_ggc (37, htab_hash_pointer,
 | ||
| 						 htab_eq_pointer, NULL);
 | ||
|       slot = htab_find_slot (func->used_types_hash, type, INSERT);
 | ||
|       if (*slot == NULL)
 | ||
| 	*slot = type;
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /* Given a type, insert it into the used hash table in cfun.  */
 | ||
| void
 | ||
| used_types_insert (tree t)
 | ||
| {
 | ||
|   while (POINTER_TYPE_P (t) || TREE_CODE (t) == ARRAY_TYPE)
 | ||
|     if (TYPE_NAME (t))
 | ||
|       break;
 | ||
|     else
 | ||
|       t = TREE_TYPE (t);
 | ||
|   if (TREE_CODE (t) == ERROR_MARK)
 | ||
|     return;
 | ||
|   if (TYPE_NAME (t) == NULL_TREE
 | ||
|       || TYPE_NAME (t) == TYPE_NAME (TYPE_MAIN_VARIANT (t)))
 | ||
|     t = TYPE_MAIN_VARIANT (t);
 | ||
|   if (debug_info_level > DINFO_LEVEL_NONE)
 | ||
|     {
 | ||
|       if (cfun)
 | ||
| 	used_types_insert_helper (t, cfun);
 | ||
|       else
 | ||
| 	/* So this might be a type referenced by a global variable.
 | ||
| 	   Record that type so that we can later decide to emit its debug
 | ||
| 	   information.  */
 | ||
|         VEC_safe_push (tree, gc, types_used_by_cur_var_decl, t);
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| /* Helper to Hash a struct types_used_by_vars_entry.  */
 | ||
| 
 | ||
| static hashval_t
 | ||
| hash_types_used_by_vars_entry (const struct types_used_by_vars_entry *entry)
 | ||
| {
 | ||
|   gcc_assert (entry && entry->var_decl && entry->type);
 | ||
| 
 | ||
|   return iterative_hash_object (entry->type,
 | ||
| 				iterative_hash_object (entry->var_decl, 0));
 | ||
| }
 | ||
| 
 | ||
| /* Hash function of the types_used_by_vars_entry hash table.  */
 | ||
| 
 | ||
| hashval_t
 | ||
| types_used_by_vars_do_hash (const void *x)
 | ||
| {
 | ||
|   const struct types_used_by_vars_entry *entry =
 | ||
|     (const struct types_used_by_vars_entry *) x;
 | ||
| 
 | ||
|   return hash_types_used_by_vars_entry (entry);
 | ||
| }
 | ||
| 
 | ||
| /*Equality function of the types_used_by_vars_entry hash table.  */
 | ||
| 
 | ||
| int
 | ||
| types_used_by_vars_eq (const void *x1, const void *x2)
 | ||
| {
 | ||
|   const struct types_used_by_vars_entry *e1 =
 | ||
|     (const struct types_used_by_vars_entry *) x1;
 | ||
|   const struct types_used_by_vars_entry *e2 =
 | ||
|     (const struct types_used_by_vars_entry *)x2;
 | ||
| 
 | ||
|   return (e1->var_decl == e2->var_decl && e1->type == e2->type);
 | ||
| }
 | ||
| 
 | ||
| /* Inserts an entry into the types_used_by_vars_hash hash table. */
 | ||
| 
 | ||
| void
 | ||
| types_used_by_var_decl_insert (tree type, tree var_decl)
 | ||
| {
 | ||
|   if (type != NULL && var_decl != NULL)
 | ||
|     {
 | ||
|       void **slot;
 | ||
|       struct types_used_by_vars_entry e;
 | ||
|       e.var_decl = var_decl;
 | ||
|       e.type = type;
 | ||
|       if (types_used_by_vars_hash == NULL)
 | ||
| 	types_used_by_vars_hash =
 | ||
| 	  htab_create_ggc (37, types_used_by_vars_do_hash,
 | ||
| 			   types_used_by_vars_eq, NULL);
 | ||
|       slot = htab_find_slot_with_hash (types_used_by_vars_hash, &e,
 | ||
| 				       hash_types_used_by_vars_entry (&e), INSERT);
 | ||
|       if (*slot == NULL)
 | ||
| 	{
 | ||
| 	  struct types_used_by_vars_entry *entry;
 | ||
| 	  entry = ggc_alloc_types_used_by_vars_entry ();
 | ||
| 	  entry->type = type;
 | ||
| 	  entry->var_decl = var_decl;
 | ||
| 	  *slot = entry;
 | ||
| 	}
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| struct rtl_opt_pass pass_leaf_regs =
 | ||
| {
 | ||
|  {
 | ||
|   RTL_PASS,
 | ||
|   "*leaf_regs",                         /* name */
 | ||
|   NULL,                                 /* gate */
 | ||
|   rest_of_handle_check_leaf_regs,       /* execute */
 | ||
|   NULL,                                 /* sub */
 | ||
|   NULL,                                 /* next */
 | ||
|   0,                                    /* static_pass_number */
 | ||
|   TV_NONE,                              /* tv_id */
 | ||
|   0,                                    /* properties_required */
 | ||
|   0,                                    /* properties_provided */
 | ||
|   0,                                    /* properties_destroyed */
 | ||
|   0,                                    /* todo_flags_start */
 | ||
|   0                                     /* todo_flags_finish */
 | ||
|  }
 | ||
| };
 | ||
| 
 | ||
| static unsigned int
 | ||
| rest_of_handle_thread_prologue_and_epilogue (void)
 | ||
| {
 | ||
|   if (optimize)
 | ||
|     cleanup_cfg (CLEANUP_EXPENSIVE);
 | ||
| 
 | ||
|   /* On some machines, the prologue and epilogue code, or parts thereof,
 | ||
|      can be represented as RTL.  Doing so lets us schedule insns between
 | ||
|      it and the rest of the code and also allows delayed branch
 | ||
|      scheduling to operate in the epilogue.  */
 | ||
|   thread_prologue_and_epilogue_insns ();
 | ||
| 
 | ||
|   /* The stack usage info is finalized during prologue expansion.  */
 | ||
|   if (flag_stack_usage_info)
 | ||
|     output_stack_usage ();
 | ||
| 
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| struct rtl_opt_pass pass_thread_prologue_and_epilogue =
 | ||
| {
 | ||
|  {
 | ||
|   RTL_PASS,
 | ||
|   "pro_and_epilogue",                   /* name */
 | ||
|   NULL,                                 /* gate */
 | ||
|   rest_of_handle_thread_prologue_and_epilogue, /* execute */
 | ||
|   NULL,                                 /* sub */
 | ||
|   NULL,                                 /* next */
 | ||
|   0,                                    /* static_pass_number */
 | ||
|   TV_THREAD_PROLOGUE_AND_EPILOGUE,      /* tv_id */
 | ||
|   0,                                    /* properties_required */
 | ||
|   0,                                    /* properties_provided */
 | ||
|   0,                                    /* properties_destroyed */
 | ||
|   TODO_verify_flow,                     /* todo_flags_start */
 | ||
|   TODO_df_verify |
 | ||
|   TODO_df_finish | TODO_verify_rtl_sharing |
 | ||
|   TODO_ggc_collect                      /* todo_flags_finish */
 | ||
|  }
 | ||
| };
 | ||
| 
 | ||
| 
 | ||
| /* This mini-pass fixes fall-out from SSA in asm statements that have
 | ||
|    in-out constraints.  Say you start with
 | ||
| 
 | ||
|      orig = inout;
 | ||
|      asm ("": "+mr" (inout));
 | ||
|      use (orig);
 | ||
| 
 | ||
|    which is transformed very early to use explicit output and match operands:
 | ||
| 
 | ||
|      orig = inout;
 | ||
|      asm ("": "=mr" (inout) : "0" (inout));
 | ||
|      use (orig);
 | ||
| 
 | ||
|    Or, after SSA and copyprop,
 | ||
| 
 | ||
|      asm ("": "=mr" (inout_2) : "0" (inout_1));
 | ||
|      use (inout_1);
 | ||
| 
 | ||
|    Clearly inout_2 and inout_1 can't be coalesced easily anymore, as
 | ||
|    they represent two separate values, so they will get different pseudo
 | ||
|    registers during expansion.  Then, since the two operands need to match
 | ||
|    per the constraints, but use different pseudo registers, reload can
 | ||
|    only register a reload for these operands.  But reloads can only be
 | ||
|    satisfied by hardregs, not by memory, so we need a register for this
 | ||
|    reload, just because we are presented with non-matching operands.
 | ||
|    So, even though we allow memory for this operand, no memory can be
 | ||
|    used for it, just because the two operands don't match.  This can
 | ||
|    cause reload failures on register-starved targets.
 | ||
| 
 | ||
|    So it's a symptom of reload not being able to use memory for reloads
 | ||
|    or, alternatively it's also a symptom of both operands not coming into
 | ||
|    reload as matching (in which case the pseudo could go to memory just
 | ||
|    fine, as the alternative allows it, and no reload would be necessary).
 | ||
|    We fix the latter problem here, by transforming
 | ||
| 
 | ||
|      asm ("": "=mr" (inout_2) : "0" (inout_1));
 | ||
| 
 | ||
|    back to
 | ||
| 
 | ||
|      inout_2 = inout_1;
 | ||
|      asm ("": "=mr" (inout_2) : "0" (inout_2));  */
 | ||
| 
 | ||
| static void
 | ||
| match_asm_constraints_1 (rtx insn, rtx *p_sets, int noutputs)
 | ||
| {
 | ||
|   int i;
 | ||
|   bool changed = false;
 | ||
|   rtx op = SET_SRC (p_sets[0]);
 | ||
|   int ninputs = ASM_OPERANDS_INPUT_LENGTH (op);
 | ||
|   rtvec inputs = ASM_OPERANDS_INPUT_VEC (op);
 | ||
|   bool *output_matched = XALLOCAVEC (bool, noutputs);
 | ||
| 
 | ||
|   memset (output_matched, 0, noutputs * sizeof (bool));
 | ||
|   for (i = 0; i < ninputs; i++)
 | ||
|     {
 | ||
|       rtx input, output, insns;
 | ||
|       const char *constraint = ASM_OPERANDS_INPUT_CONSTRAINT (op, i);
 | ||
|       char *end;
 | ||
|       int match, j;
 | ||
| 
 | ||
|       if (*constraint == '%')
 | ||
| 	constraint++;
 | ||
| 
 | ||
|       match = strtoul (constraint, &end, 10);
 | ||
|       if (end == constraint)
 | ||
| 	continue;
 | ||
| 
 | ||
|       gcc_assert (match < noutputs);
 | ||
|       output = SET_DEST (p_sets[match]);
 | ||
|       input = RTVEC_ELT (inputs, i);
 | ||
|       /* Only do the transformation for pseudos.  */
 | ||
|       if (! REG_P (output)
 | ||
| 	  || rtx_equal_p (output, input)
 | ||
| 	  || (GET_MODE (input) != VOIDmode
 | ||
| 	      && GET_MODE (input) != GET_MODE (output)))
 | ||
| 	continue;
 | ||
| 
 | ||
|       /* We can't do anything if the output is also used as input,
 | ||
| 	 as we're going to overwrite it.  */
 | ||
|       for (j = 0; j < ninputs; j++)
 | ||
|         if (reg_overlap_mentioned_p (output, RTVEC_ELT (inputs, j)))
 | ||
| 	  break;
 | ||
|       if (j != ninputs)
 | ||
| 	continue;
 | ||
| 
 | ||
|       /* Avoid changing the same input several times.  For
 | ||
| 	 asm ("" : "=mr" (out1), "=mr" (out2) : "0" (in), "1" (in));
 | ||
| 	 only change in once (to out1), rather than changing it
 | ||
| 	 first to out1 and afterwards to out2.  */
 | ||
|       if (i > 0)
 | ||
| 	{
 | ||
| 	  for (j = 0; j < noutputs; j++)
 | ||
| 	    if (output_matched[j] && input == SET_DEST (p_sets[j]))
 | ||
| 	      break;
 | ||
| 	  if (j != noutputs)
 | ||
| 	    continue;
 | ||
| 	}
 | ||
|       output_matched[match] = true;
 | ||
| 
 | ||
|       start_sequence ();
 | ||
|       emit_move_insn (output, input);
 | ||
|       insns = get_insns ();
 | ||
|       end_sequence ();
 | ||
|       emit_insn_before (insns, insn);
 | ||
| 
 | ||
|       /* Now replace all mentions of the input with output.  We can't
 | ||
| 	 just replace the occurrence in inputs[i], as the register might
 | ||
| 	 also be used in some other input (or even in an address of an
 | ||
| 	 output), which would mean possibly increasing the number of
 | ||
| 	 inputs by one (namely 'output' in addition), which might pose
 | ||
| 	 a too complicated problem for reload to solve.  E.g. this situation:
 | ||
| 
 | ||
| 	   asm ("" : "=r" (output), "=m" (input) : "0" (input))
 | ||
| 
 | ||
| 	 Here 'input' is used in two occurrences as input (once for the
 | ||
| 	 input operand, once for the address in the second output operand).
 | ||
| 	 If we would replace only the occurrence of the input operand (to
 | ||
| 	 make the matching) we would be left with this:
 | ||
| 
 | ||
| 	   output = input
 | ||
| 	   asm ("" : "=r" (output), "=m" (input) : "0" (output))
 | ||
| 
 | ||
| 	 Now we suddenly have two different input values (containing the same
 | ||
| 	 value, but different pseudos) where we formerly had only one.
 | ||
| 	 With more complicated asms this might lead to reload failures
 | ||
| 	 which wouldn't have happen without this pass.  So, iterate over
 | ||
| 	 all operands and replace all occurrences of the register used.  */
 | ||
|       for (j = 0; j < noutputs; j++)
 | ||
| 	if (!rtx_equal_p (SET_DEST (p_sets[j]), input)
 | ||
| 	    && reg_overlap_mentioned_p (input, SET_DEST (p_sets[j])))
 | ||
| 	  SET_DEST (p_sets[j]) = replace_rtx (SET_DEST (p_sets[j]),
 | ||
| 					      input, output);
 | ||
|       for (j = 0; j < ninputs; j++)
 | ||
| 	if (reg_overlap_mentioned_p (input, RTVEC_ELT (inputs, j)))
 | ||
| 	  RTVEC_ELT (inputs, j) = replace_rtx (RTVEC_ELT (inputs, j),
 | ||
| 					       input, output);
 | ||
| 
 | ||
|       changed = true;
 | ||
|     }
 | ||
| 
 | ||
|   if (changed)
 | ||
|     df_insn_rescan (insn);
 | ||
| }
 | ||
| 
 | ||
| static unsigned
 | ||
| rest_of_match_asm_constraints (void)
 | ||
| {
 | ||
|   basic_block bb;
 | ||
|   rtx insn, pat, *p_sets;
 | ||
|   int noutputs;
 | ||
| 
 | ||
|   if (!crtl->has_asm_statement)
 | ||
|     return 0;
 | ||
| 
 | ||
|   df_set_flags (DF_DEFER_INSN_RESCAN);
 | ||
|   FOR_EACH_BB (bb)
 | ||
|     {
 | ||
|       FOR_BB_INSNS (bb, insn)
 | ||
| 	{
 | ||
| 	  if (!INSN_P (insn))
 | ||
| 	    continue;
 | ||
| 
 | ||
| 	  pat = PATTERN (insn);
 | ||
| 	  if (GET_CODE (pat) == PARALLEL)
 | ||
| 	    p_sets = &XVECEXP (pat, 0, 0), noutputs = XVECLEN (pat, 0);
 | ||
| 	  else if (GET_CODE (pat) == SET)
 | ||
| 	    p_sets = &PATTERN (insn), noutputs = 1;
 | ||
| 	  else
 | ||
| 	    continue;
 | ||
| 
 | ||
| 	  if (GET_CODE (*p_sets) == SET
 | ||
| 	      && GET_CODE (SET_SRC (*p_sets)) == ASM_OPERANDS)
 | ||
| 	    match_asm_constraints_1 (insn, p_sets, noutputs);
 | ||
| 	 }
 | ||
|     }
 | ||
| 
 | ||
|   return TODO_df_finish;
 | ||
| }
 | ||
| 
 | ||
| struct rtl_opt_pass pass_match_asm_constraints =
 | ||
| {
 | ||
|  {
 | ||
|   RTL_PASS,
 | ||
|   "asmcons",				/* name */
 | ||
|   NULL,					/* gate */
 | ||
|   rest_of_match_asm_constraints,	/* execute */
 | ||
|   NULL,                                 /* sub */
 | ||
|   NULL,                                 /* next */
 | ||
|   0,                                    /* static_pass_number */
 | ||
|   TV_NONE,				/* tv_id */
 | ||
|   0,                                    /* properties_required */
 | ||
|   0,                                    /* properties_provided */
 | ||
|   0,                                    /* properties_destroyed */
 | ||
|   0,					/* todo_flags_start */
 | ||
|   0                                     /* todo_flags_finish */
 | ||
|  }
 | ||
| };
 | ||
| 
 | ||
| 
 | ||
| #include "gt-function.h"
 |