This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

More calls.c cleanups


Hi
Here is next patch for calls.c.  It breaks out the emiting code and
avoids the sibbling call loop.  Instead I´ve created functions for
the more complex common code and made two separate gen_ functions
for these.
It introduces about one page of redundancy between gen_tail_call and
gen_normal_call, but it is just straighforward code calling other functions,
so I hope it will not add much of maitenance cost. I believe that the
code is somewhat easier to understand now since it is clear what is
done for sibbling calls and what isn´t (there was done some unnecesary
stack alignmenting and so in sibbling call code I´ve avoided).

The functions now have an extreme amount of parameters, but I plan to address
this in future patch  (the current patch is large ounght as it is) by grouping
information about arguments into single structure.  This will simplify calling
of some other functions as well.  Originally I tought about creating one large
structure holding information about call, but this is probably not good idea,
since it will hide dependencies between functions.

Some oddities has been shown while reordering the code:

Following ones I´ve fixed:
 - When allocating preallocated block, the reg_parm_stack_space was added
   twice to the block size. Once for args_size in compure_arg_size and
   second time in the allocating code.  I´ve ifdefed out the code for the
   moment and will remove it in future patch in case no one will complain.
   (keeping that code would require new arguments to be added to functions
    so I´ve decided to be less conservative here)
 - In ACCUMULATE_OUTGOING_ARGS arm of allocatin code whas reloaded
   argblock twice into register.  Originally these was disjunct blocks
   I´ve merged in my ACCUMULATE_OUTGOING_ARGS code so I expect this was
   just omision.

Following ones I would like to fix separately:
 - emit_queue is called after load_register_parameters.  I think this can
   cause potential crashes on SRC machines, since we enlarge lifetimes of
   hard registers.  I am not familiar with the emit_queue, but once
   I am having all registers precalcualted and stored as pseudos,
   may emit_queue clobber them? If not it may be safe to move it before
   load_register_parameters.
 - funexpr and some other variables wasn´t propertly restored after sibling
   call sequence.  I guess this worked because for cases we generate sibling
   calls we don´t clobber them.  I am now having them as local variables
   so they are restored automagically.
 - There is do_pending_stack_adjust at the start of sibling call code.
   Is it neccesary?  I don´t think so.  I believe I´ve already sent a patch to
   remove it, so perhaps this run into some problems.
   I will try to remove it as separate patch.


Also it is quite anoying that target expression is constructed in the gen_call.
I would like to change this in separate patch too, since there are some
ordering issues with deallocation code that needs to be resolved first.

Last notable change is that I´ve cleaned somehow the stack level handling
code.  Now all relevant data are saved to stack_level structure and
I use save_stack_level and restore_stack_level to do the trick instead
of doing that by hand at multiple places in the code.

In future I would like to move these into explow and teach them how
to restore stack level optimally (ie to not use pseudo when the stack
what adjusted by constant amount) and use this code unconditionally to cleanup
after call.  This will simplify number of alignment issues.

Bootstrapped on i386 (in normal and -fno-push-args mode) and Alpha,
regression tested on i386 olny.

Don Mai 11 15:11:36 CEST 2000  Jan Hubicka  <jh@suse.cz>

	* calls.c (struct stack_level):  New.
	(precompute_register_parameters): Do not return reg_stack_seen.
	(save_stack_level, restore_stack_level): New.
	(store_args): Break out from ...
	(gen_tail_call): Break out from ...; Tweak commentary, avoid
	unnecesary stack alignmenting code; allow NORETURN sibcalls to
	be generated even when the cleanups are pending.
	(gen_normal_call): Break out from ...
	(argument_block): Break out from ...; Do not add reg_parm_stack_space
	to the block size; avoid copying of argblock to reg twice.
	(cleanup_args): Break out from ...
	(expand_call): ... here; Rename reg_parm_seen to have_reg_parm
	and compute it; Add old_stack_level variable and initialize it;
	move calculation of funexp, valreg and reg_parm_stack_space out of the
	sibbling call loop;
	(prepare_call_address): Rename reg_parm_seen to have_reg_parm.
	(initialize_argument_information):  Replace arguments
	old_pending_adj and old_stack_level by old_stack_level.
	* store_one_arg (variable_size): Remove unnecesary ATTRIBUTE_UNUSED
	flag on parameter.

Index: egcs/gcc/calls.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/calls.c,v
retrieving revision 1.132
diff -c -3 -p -r1.132 calls.c
*** calls.c	2000/05/10 02:53:43	1.132
--- calls.c	2000/05/11 13:11:10
*************** struct arg_data
*** 131,136 ****
--- 131,147 ----
    struct args_size alignment_pad;
  };
  
+ /* Information about stack we need to restore to deallocate
+    dynamically allocated stack space.  */
+ struct stack_level
+   {
+     rtx value;
+     int pending_adj;
+     int stack_arg_under_construction;
+     int highest_arg_in_use;
+     char *stack_usage_map;
+   };
+ 
  /* A vector of one char per byte of stack space.  A byte if non-zero if
     the corresponding stack location has been used.
     This vector is used to prevent a function call within an argument from
*************** static void emit_call_1		PARAMS ((rtx, t
*** 176,185 ****
  					 HOST_WIDE_INT, HOST_WIDE_INT, rtx,
  					 rtx, int, rtx, int));
  static void precompute_register_parameters	PARAMS ((int,
! 							 struct arg_data *,
! 							 int *));
  static void store_one_arg	PARAMS ((struct arg_data *, rtx, int, int,
  					 int));
  static void store_unaligned_arguments_into_pseudos PARAMS ((struct arg_data *,
  							    int));
  static int finalize_must_preallocate		PARAMS ((int, int,
--- 187,197 ----
  					 HOST_WIDE_INT, HOST_WIDE_INT, rtx,
  					 rtx, int, rtx, int));
  static void precompute_register_parameters	PARAMS ((int,
! 							 struct arg_data *));
  static void store_one_arg	PARAMS ((struct arg_data *, rtx, int, int,
  					 int));
+ static void store_args		PARAMS ((struct arg_data *, int, rtx, int,
+ 					 int, int));
  static void store_unaligned_arguments_into_pseudos PARAMS ((struct arg_data *,
  							    int));
  static int finalize_must_preallocate		PARAMS ((int, int,
*************** static void initialize_argument_informat
*** 195,201 ****
  							 struct args_size *,
  							 int, tree, tree,
  							 CUMULATIVE_ARGS *,
! 							 int, rtx *, int *,
  							 int *, int *));
  static void compute_argument_addresses		PARAMS ((struct arg_data *,
  							 rtx, int));
--- 207,213 ----
  							 struct args_size *,
  							 int, tree, tree,
  							 CUMULATIVE_ARGS *,
! 							 int, struct stack_level *,
  							 int *, int *));
  static void compute_argument_addresses		PARAMS ((struct arg_data *,
  							 rtx, int));
*************** static int combine_pending_stack_adjustm
*** 217,222 ****
--- 229,249 ----
  static rtx save_fixed_argument_area	PARAMS ((int, rtx, int *, int *));
  static void restore_fixed_argument_area	PARAMS ((rtx, rtx, int, int));
  #endif
+ static rtx gen_normal_call PARAMS ((tree, tree, tree, rtx, int,
+ 				    struct args_size *, struct arg_data *, int,
+ 				    int, rtx *, rtx *, int, CUMULATIVE_ARGS,
+ 				    HOST_WIDE_INT, int, int,
+ 				    struct stack_level *, rtx, int, int, int));
+ static rtx gen_tail_call PARAMS ((tree, tree, rtx, int, struct args_size *,
+ 				  struct arg_data *, int, int, rtx, int,
+ 				  CUMULATIVE_ARGS, HOST_WIDE_INT));
+ static void save_stack_level PARAMS ((struct stack_level *level));
+ static void restore_stack_level PARAMS ((struct stack_level *level));
+ static void cleanup_args PARAMS ((struct arg_data *, int));
+ static rtx argument_block PARAMS ((struct args_size *, int,
+ 				   struct stack_level *, int, int, int));
+ 
+ 
  
  /* If WHICH is 1, return 1 if EXP contains a call to the built-in function
     `alloca'.
*************** calls_function_1 (exp, which)
*** 344,354 ****
     CALL_INSN_FUNCTION_USAGE information.  */
  
  rtx
! prepare_call_address (funexp, fndecl, call_fusage, reg_parm_seen)
       rtx funexp;
       tree fndecl;
       rtx *call_fusage;
!      int reg_parm_seen;
  {
    rtx static_chain_value = 0;
  
--- 371,381 ----
     CALL_INSN_FUNCTION_USAGE information.  */
  
  rtx
! prepare_call_address (funexp, fndecl, call_fusage, have_reg_parm)
       rtx funexp;
       tree fndecl;
       rtx *call_fusage;
!      int have_reg_parm;
  {
    rtx static_chain_value = 0;
  
*************** prepare_call_address (funexp, fndecl, ca
*** 363,369 ****
    if (GET_CODE (funexp) != SYMBOL_REF)
      /* If we are using registers for parameters, force the
         function address into a register now.  */
!     funexp = ((SMALL_REGISTER_CLASSES && reg_parm_seen)
  	      ? force_not_mem (memory_address (FUNCTION_MODE, funexp))
  	      : memory_address (FUNCTION_MODE, funexp));
    else
--- 390,396 ----
    if (GET_CODE (funexp) != SYMBOL_REF)
      /* If we are using registers for parameters, force the
         function address into a register now.  */
!     funexp = ((SMALL_REGISTER_CLASSES && have_reg_parm)
  	      ? force_not_mem (memory_address (FUNCTION_MODE, funexp))
  	      : memory_address (FUNCTION_MODE, funexp));
    else
*************** flags_from_decl_or_type (exp)
*** 799,818 ****
     Set REG_PARM_SEEN if we encounter a register parameter.  */
  
  static void
! precompute_register_parameters (num_actuals, args, reg_parm_seen)
       int num_actuals;
       struct arg_data *args;
-      int *reg_parm_seen;
  {
    int i;
! 
!   *reg_parm_seen = 0;
  
    for (i = 0; i < num_actuals; i++)
      if (args[i].reg != 0 && ! args[i].pass_on_stack)
        {
! 	*reg_parm_seen = 1;
! 
  	if (args[i].value == 0)
  	  {
  	    push_temp_slots ();
--- 826,842 ----
     Set REG_PARM_SEEN if we encounter a register parameter.  */
  
  static void
! precompute_register_parameters (num_actuals, args)
       int num_actuals;
       struct arg_data *args;
  {
    int i;
!   int reg_parm_seen = 0;
  
    for (i = 0; i < num_actuals; i++)
      if (args[i].reg != 0 && ! args[i].pass_on_stack)
        {
! 	reg_parm_seen = 1;
  	if (args[i].value == 0)
  	  {
  	    push_temp_slots ();
*************** precompute_register_parameters (num_actu
*** 848,854 ****
  		    && GET_CODE (SUBREG_REG (args[i].value)) == REG)))
  	    && args[i].mode != BLKmode
  	    && rtx_cost (args[i].value, SET) > 2
! 	    && ((SMALL_REGISTER_CLASSES && *reg_parm_seen)
  		|| preserve_subexpressions_p ()))
  	  args[i].value = copy_to_mode_reg (args[i].mode, args[i].value);
        }
--- 872,878 ----
  		    && GET_CODE (SUBREG_REG (args[i].value)) == REG)))
  	    && args[i].mode != BLKmode
  	    && rtx_cost (args[i].value, SET) > 2
! 	    && ((SMALL_REGISTER_CLASSES && reg_parm_seen)
  		|| preserve_subexpressions_p ()))
  	  args[i].value = copy_to_mode_reg (args[i].mode, args[i].value);
        }
*************** store_unaligned_arguments_into_pseudos (
*** 1032,1037 ****
--- 1056,1091 ----
        }
  }
  
+ /*  Save stack level to the structure level.  After call to restore_stack_level
+     the stack pointer will be restored to the same place as well as all stack
+     related variables.  */
+ static void
+ save_stack_level (level)
+ 	struct stack_level *level;
+ {
+    emit_stack_save (SAVE_BLOCK, &level->value, NULL_RTX);
+    level->pending_adj = pending_stack_adjust;
+    pending_stack_adjust = 0;
+    /* stack_arg_under_construction says whether a stack arg is
+       being constructed at the old stack level.  Pushing the stack
+       gets a clean outgoing argument block.  */
+    level->stack_arg_under_construction = stack_arg_under_construction;
+    stack_arg_under_construction = 0;
+    level->highest_arg_in_use = highest_outgoing_arg_in_use;
+    level->stack_usage_map = stack_usage_map;
+ }
+ /*  Save stack level for later restore.  */
+ static void
+ restore_stack_level (level)
+ 	struct stack_level *level;
+ {
+    emit_stack_restore (SAVE_BLOCK, level->value, NULL_RTX);
+    pending_stack_adjust = level->pending_adj;
+    stack_arg_under_construction = level->stack_arg_under_construction;
+    highest_outgoing_arg_in_use = level->highest_arg_in_use;
+    stack_usage_map = level->stack_usage_map;
+ }
+ 
  /* Fill in ARGS_SIZE and ARGS array based on the parameters found in
     ACTPARMS. 
  
*************** static void
*** 1057,1063 ****
  initialize_argument_information (num_actuals, args, args_size, n_named_args,
  				 actparms, fndecl, args_so_far,
  				 reg_parm_stack_space, old_stack_level,
! 				 old_pending_adj, must_preallocate,
  				 ecf_flags)
       int num_actuals ATTRIBUTE_UNUSED;
       struct arg_data *args;
--- 1111,1117 ----
  initialize_argument_information (num_actuals, args, args_size, n_named_args,
  				 actparms, fndecl, args_so_far,
  				 reg_parm_stack_space, old_stack_level,
! 				 must_preallocate,
  				 ecf_flags)
       int num_actuals ATTRIBUTE_UNUSED;
       struct arg_data *args;
*************** initialize_argument_information (num_act
*** 1067,1074 ****
       tree fndecl;
       CUMULATIVE_ARGS *args_so_far;
       int reg_parm_stack_space;
!      rtx *old_stack_level;
!      int *old_pending_adj;
       int *must_preallocate;
       int *ecf_flags;
  {
--- 1121,1127 ----
       tree fndecl;
       CUMULATIVE_ARGS *args_so_far;
       int reg_parm_stack_space;
!      struct stack_level *old_stack_level;
       int *must_preallocate;
       int *ecf_flags;
  {
*************** initialize_argument_information (num_act
*** 1192,1203 ****
  		     for it.  */
  		  rtx size_rtx = expr_size (TREE_VALUE (p));
  
! 		  if (*old_stack_level == 0)
! 		    {
! 		      emit_stack_save (SAVE_BLOCK, old_stack_level, NULL_RTX);
! 		      *old_pending_adj = pending_stack_adjust;
! 		      pending_stack_adjust = 0;
! 		    }
  
  		  copy = gen_rtx_MEM (BLKmode,
  				      allocate_dynamic_stack_space (size_rtx,
--- 1245,1252 ----
  		     for it.  */
  		  rtx size_rtx = expr_size (TREE_VALUE (p));
  
! 		  if (old_stack_level->value == 0)
! 		    save_stack_level (old_stack_level);
  
  		  copy = gen_rtx_MEM (BLKmode,
  				      allocate_dynamic_stack_space (size_rtx,
*************** combine_pending_stack_adjustment_and_cal
*** 1913,1919 ****
--- 1962,2759 ----
  
    return adjustment;
  }
+ /* Cleanup args structure after call was emitted.  Free storage allocated
+    during emitting process.  */
+ void
+ cleanup_args (args, num_actuals)
+      struct arg_data *args;
+      int num_actuals;
+ {
+   int i;
+   /* Free up storage we no longer need.  */
+   for (i = 0; i < num_actuals; ++i)
+     {
+       if (args[i].aligned_regs)
+ 	free (args[i].aligned_regs);
+ 	  args[i].value = 0;
+       args[i].aligned_regs = 0;
+       args[i].stack = 0;
+     }
+ }
+ 						 
+ /*  Prepare the block outgoing arguments will be stored to.  This is necesarry
+     only when we don´t have push instruction or we don´t want to use them.
+     
+     We may allocate new block or use outgoing arguments area when we do
+     ACCUMULATE_OUTGOING_ARGS.  */
+ static rtx
+ argument_block (args_size, unadjusted_args_size, old_stack_level, 
+ 	        reg_parm_stack_space, preferred_stack_boundary,
+ 		must_preallocate)
+      struct args_size *args_size;
+      int unadjusted_args_size;
+      struct stack_level *old_stack_level;
+      int reg_parm_stack_space;
+      int preferred_stack_boundary;
+      int must_preallocate;
+ {
+    rtx argblock = NULL_RTX;
+    int preferred_unit_stack_boundary = preferred_stack_boundary / BITS_PER_UNIT;
+    char *initial_stack_usage_map = stack_usage_map;
+    int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
+ 
+    /* If we have no actual push instructions, or shouldn't use them,
+       make space for all args right now.  */
+    if (args_size->var != 0)
+      {
+        if (old_stack_level->value == 0)
+ 	 save_stack_level (old_stack_level);
+        argblock = push_block (ARGS_SIZE_RTX (*args_size), 0, 0);
+      }
+    else
+      {
+        /* Note that we must go through the motions of allocating an argument
+ 	  block even if the size is zero because we may be storing args
+ 	  in the area reserved for register arguments, which may be part of
+ 	  the stack frame.  */
+ 
+        int needed = args_size->constant;
+ 
+        /* Store the maximum argument space used.  It will be pushed by
+ 	  the prologue (for ACCUMULATE_OUTGOING_ARGS, or stack overflow
+ 	  checking).  */
+ 
+        if (needed > current_function_outgoing_args_size)
+ 	 current_function_outgoing_args_size = needed;
+ 
+        if (must_preallocate)
+ 	 {
+ 	   if (ACCUMULATE_OUTGOING_ARGS)
+ 	     {
+ 	       /* Since the stack pointer will never be pushed, it is
+ 		  possible for the evaluation of a parm to clobber
+ 		  something we have already written to the stack.
+ 		  Since most function calls on RISC machines do not use
+ 		  the stack, this is uncommon, but must work correctly.
+ 
+ 		  Therefore, we save any area of the stack that was already
+ 		  written and that we are using.  Here we set up to do this
+ 		  by making a new stack usage map from the old one.  The
+ 		  actual save will be done by store_one_arg. 
+ 
+ 		  Another approach might be to try to reorder the argument
+ 		  evaluations to avoid this conflicting stack usage.  */
+ 
+ 	       /* ??? This don´t seems to be neccesary, since
+ 		  compute_argument_block_size already added reg_parm_stack_space
+ 		  to the args_size.  */
+ #if 0
+ 	       /* Since we will be writing into the entire argument area,
+ 		  the map must be allocated for its entire size, not just
+ 		  the part that is the responsibility of the caller.  */
+ 	       needed += reg_parm_stack_space;
+ #endif
+ 
+ #ifdef ARGS_GROW_DOWNWARD
+ 	       highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
+ 						  needed + 1);
+ #else
+ 	       highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
+ 						  needed);
+ #endif
+ 	       stack_usage_map
+ 		 = (char *) xmalloc (highest_outgoing_arg_in_use);
+ 
+ 	       if (initial_highest_arg_in_use)
+ 		 bcopy (initial_stack_usage_map, stack_usage_map,
+ 			initial_highest_arg_in_use);
+ 
+ 	       if (initial_highest_arg_in_use != highest_outgoing_arg_in_use)
+ 		 bzero (&stack_usage_map[initial_highest_arg_in_use],
+ 			(highest_outgoing_arg_in_use
+ 			 - initial_highest_arg_in_use));
+ 	       needed = 0;
+ 
+ 	       /* The address of the outgoing argument list must not be
+ 		  copied to a register here, because argblock would be left
+ 		  pointing to the wrong place after the call to
+ 		  allocate_dynamic_stack_space below. */
+ 
+ 	       argblock = virtual_outgoing_args_rtx;
+ 	     }
+ 	   else
+ 	     {
+ 	       if (inhibit_defer_pop == 0)
+ 		 {
+ 		   /* Try to reuse some or all of the pending_stack_adjust
+ 		      to get this space.  */
+ 		   needed
+ 		     = (combine_pending_stack_adjustment_and_call 
+ 			(unadjusted_args_size,
+ 			 args_size,
+ 			 preferred_unit_stack_boundary));
+ 
+ 		   /* combine_pending_stack_adjustment_and_call computes
+ 		      an adjustment before the arguments are allocated.
+ 		      Account for them and see whether or not the stack
+ 		      needs to go up or down.  */
+ 		   needed = unadjusted_args_size - needed;
+ 
+ 		   if (needed < 0)
+ 		     {
+ 		       /* We're releasing stack space.  */
+ 		       /* ??? We can avoid any adjustment at all if we're
+ 			  already aligned.  FIXME.  */
+ 		       pending_stack_adjust = -needed;
+ 		       do_pending_stack_adjust ();
+ 		       needed = 0;
+ 		     }
+ 		   else 
+ 		     /* We need to allocate space.  We'll do that in
+ 			push_block below.  */
+ 		     pending_stack_adjust = 0;
+ 		 }
+ 
+ 	       /* Special case this because overhead of `push_block' in
+ 		  this case is non-trivial.  */
+ 	       if (needed == 0)
+ 		 argblock = virtual_outgoing_args_rtx;
+ 	       else
+ 		 argblock = push_block (GEN_INT (needed), 0, 0);
+ 
+ 	       /* We only really need to call `copy_to_reg' in the case
+ 		  where push insns are going to be used to pass ARGBLOCK
+ 		  to a function call in ARGS.  In that case, the stack
+ 		  pointer changes value from the allocation point to the
+ 		  call point, and hence the value of
+ 		  VIRTUAL_OUTGOING_ARGS_RTX changes as well.  But might
+ 		  as well always do it.  */
+ 	       argblock = copy_to_reg (argblock);
+ 
+ 	       /* The save/restore code in store_one_arg handles all
+ 		  cases except one: a constructor call (including a C
+ 		  function returning a BLKmode struct) to initialize
+ 		  an argument.  */
+ 	       if (stack_arg_under_construction)
+ 		 {
+ #ifndef OUTGOING_REG_PARM_STACK_SPACE
+ 		   rtx push_size = GEN_INT (reg_parm_stack_space
+ 					    + args_size->constant);
+ #else
+ 		   rtx push_size = GEN_INT (args_size->constant);
+ #endif
+ 		   if (old_stack_level->value == 0)
+ 		     {
+ 		       save_stack_level (old_stack_level);
+ 		       /* Make a new map for the new argument list.  */
+ 		       stack_usage_map = (char *)
+ 			 malloc (highest_outgoing_arg_in_use);
+ 		       bzero (stack_usage_map, highest_outgoing_arg_in_use);
+ 		       highest_outgoing_arg_in_use = 0;
+ 		     }
+ 		   allocate_dynamic_stack_space (push_size, NULL_RTX,
+ 						 BITS_PER_UNIT);
+ 		 }
+ 	     }
+ 	 }
+      }
+    return argblock;
+ }
+ /* Generate insn sequence for sibling call.  Return sequence containing the
+    call or 0 on failure.  We take care to save an context before emitting
+    and restore it at the end, so this sequence can be safely ignored.  
+ 
+    Similar code exists in emit_normal_cal_insns too.
+ */
+ static rtx
+ gen_tail_call (fndecl, funtype, funexp, flags, real_args_size, args,
+ 	       num_actuals, reg_parm_stack_space, valreg,
+ 	       have_reg_parm, args_so_far, struct_value_size)
+      tree fndecl;
+      tree funtype;
+      rtx funexp;
+      int flags;
+      struct args_size *real_args_size;
+      struct arg_data *args;
+      int num_actuals;
+      int reg_parm_stack_space;
+      rtx valreg;
+      int have_reg_parm;
+      CUMULATIVE_ARGS args_so_far;
+      HOST_WIDE_INT struct_value_size;
+ {
+   int save_pending_stack_adjust, save_stack_pointer_delta;
+   int sibcall_failure = 0;
+   rtx insns;
+   rtx call_fusage = NULL_RTX;
+   rtx next_arg_reg;
+   rtx last;
+   /* We must make our own copy to avoid compute_args_size from clobbering it.  */
+   struct args_size args_size = *real_args_size;
+   /* Size of arguments before any adjustments (such as rounding).  */
+   int unadjusted_args_size;
+   int save_inhibit_defer_pop;
+ 
+   /* Save context we can clobber.  */
+   save_pending_stack_adjust = pending_stack_adjust;
+   save_stack_pointer_delta = stack_pointer_delta;
+   save_inhibit_defer_pop = inhibit_defer_pop;
+ 
+   flags |= ECF_SIBCALL;
+ 
+   if (any_pending_cleanups (1))
+     abort();
+ 
+   /* We know at this point that there are not currently any
+      pending cleanups.  If, however, in the process of evaluating
+      the arguments we were to create some, we'll need to be
+      able to get rid of them.  */
+   expand_start_target_temps ();
+ 
+   start_sequence ();
+ 
+   /* ??? This call is most probably not neccesary, but I don`t want to run
+      into problems now and will try to remove it in next patch.  */
+   do_pending_stack_adjust ();
+ 
+   /* Push the temporary stack slot level so that we can free any
+      temporaries we make.  */
+   push_temp_slots ();
+ 
+   /* Compute the actual size of the argument block required.  The variable
+      and constant sizes must be combined.  We don´t do any rounding, since
+      we are overwriting already propertly alligned incomming argument block.
+    */
+   unadjusted_args_size
+     = compute_argument_block_size (reg_parm_stack_space, &args_size, 0);
+ 
+   compute_argument_addresses (args, virtual_incoming_args_rtx, num_actuals);
+ 
+   /* Precompute all register parameters.  It isn't safe to compute anything
+      once we have started filling any specific hard regs.  */
+   precompute_register_parameters (num_actuals, args);
+ 
+   /* Now store arguments passed on the stack.  */
+   store_args (args, num_actuals, virtual_incoming_args_rtx, flags, reg_parm_stack_space,
+ 	      args_size.var != 0);
+ 
+   funexp = prepare_call_address (funexp, fndecl, &call_fusage,
+ 				 have_reg_parm);
+ 
+   /* Load parameters into registers.  This ought to appear as
+      very last call before emit_call_1 to minimize hard register
+      lifetimes and avoid crashes on SMALL_REGISTER_CLASSES machines.  */
+   load_register_parameters (args, num_actuals, &call_fusage);
+     
+   /* Perform postincrements before actually calling the function.  */
+   emit_queue ();
+ 
+   /* Set up next argument register.  For sibling calls on machines
+      with register windows this should be the incoming register.  */
+ #ifdef FUNCTION_INCOMING_ARG
+   if (pass == 0)
+     next_arg_reg = FUNCTION_INCOMING_ARG (args_so_far, VOIDmode,
+ 					  void_type_node, 1);
+  else
+ #endif
+     next_arg_reg = FUNCTION_ARG (args_so_far, VOIDmode,
+ 				  void_type_node, 1);
+ 
+    /* Generate the actual call instruction.  */
+   emit_call_1 (funexp, fndecl, funtype, unadjusted_args_size,
+ 	       args_size.constant, struct_value_size,
+ 	       next_arg_reg, valreg, save_inhibit_defer_pop, call_fusage,
+ 	       flags);
+   /* Inform flow.c control does not fall through.  */
+ 
+   last = get_last_insn ();
+   while (GET_CODE (last) != CALL_INSN)
+     last = PREV_INSN (last);
+   emit_barrier_after (last);
+ 
+   /* OK.  Bad luck, we need cleanups so we can´t do sibbling call
+      unless the function is noreturn.  */
+   if (any_pending_cleanups (1) && !(flags & ECF_NORETURN))
+      sibcall_failure = 1;
+ 
+   /* Undo the fake expand_start_target_temps we did earlier.  If
+      there had been any cleanups created, we've already set
+      sibcall_failure.  */
+   expand_end_target_temps ();
+   pop_temp_slots ();
+ 
+   insns = get_insns ();
+   end_sequence ();
+ 
+   /* Restore context.  inhibit_defer_pop is restored by emit_call_1.  */
+   pending_stack_adjust = save_pending_stack_adjust;
+   stack_pointer_delta = save_stack_pointer_delta;
+   cleanup_args (args, num_actuals);
+ 
+   if (sibcall_failure)
+     return NULL;
+   return insns;
+ }
+ /* Generate insn sequence for a call.  Return sequence containing the
+    call.  The code follows same roadmap as emit_tail_call_insns above,
+    but is somewhat more complex because of stack allocation/alignment
+    issues and possible collision in ACCUMULATE_OUTGOING_ARGS mode.  */
+ static rtx
+ gen_normal_call (exp, fndecl, funtype, funexp, flags, real_args_size, args,
+ 	         num_actuals, reg_parm_stack_space, valreg, target,
+ 		 have_reg_parm, args_so_far, struct_value_size,
+ 		 preferred_stack_boundary, must_preallocate,
+ 		 old_stack_level, structure_value_addr,
+ 		 structure_value_addr_parm, ignore, pcc_struct_value)
+      tree exp;
+      tree fndecl;
+      tree funtype;
+      rtx funexp;
+      int flags;
+      struct args_size *real_args_size;
+      struct arg_data *args;
+      int num_actuals;
+      int reg_parm_stack_space;
+      rtx *valreg;
+      rtx *target;
+      int have_reg_parm;
+      CUMULATIVE_ARGS args_so_far;
+      HOST_WIDE_INT struct_value_size;
+      int preferred_stack_boundary;
+      int must_preallocate;
+      struct stack_level *old_stack_level;
+      rtx structure_value_addr;
+      int structure_value_addr_parm;
+      int ignore;
+      int pcc_struct_value;
+ {
+   rtx insns;
+   rtx call_fusage = NULL_RTX;
+   rtx next_arg_reg;
+   /* Address of space preallocated for stack parms
+      (on machines that lack push insns), or 0 if space not preallocated.  */
+   rtx argblock;
+   /* We must make our own copy to avoid compute_args_size from clobbering it.  */
+   struct args_size args_size = *real_args_size;
+   /* Size of arguments before any adjustments (such as rounding).  */
+   int unadjusted_args_size;
+   int old_stack_allocated;
+   int preferred_unit_stack_boundary = preferred_stack_boundary / BITS_PER_UNIT;
+   int i;
+   int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
+   char *initial_stack_usage_map = stack_usage_map;
+   int old_inhibit_defer_pop = inhibit_defer_pop;
+ 
+   start_sequence ();
+ 
+   /* Don't let pending stack adjusts add up to too much.
+      Also, do all pending adjustments now if there is any chance
+      this might be a call to alloca */
+       if (pending_stack_adjust >= 32
+ 	  || (pending_stack_adjust > 0 && (flags & ECF_MAY_BE_ALLOCA)))
+ 	do_pending_stack_adjust ();
+ 
+   /* Push the temporary stack slot level so that we can free any
+      temporaries we make.  */
+   push_temp_slots ();
+ 
+   /* Precompute any arguments as needed.  */
+   precompute_arguments (flags, num_actuals, args);
+ 
+   /* Now we are about to start emitting insns that can be deleted
+      if a libcall is deleted.  For ECF_MALLOC, the sequence is
+      generated too to make possible tracking down the return value.  */
+   if (flags & (ECF_CONST | ECF_PURE | ECF_MALLOC))
+     {
+       start_sequence ();
+       NO_DEFER_POP;
+     }
+ 
+   /* Compute the actual size of the argument block required.  The variable
+      and constant sizes must be combined, the size may have to be rounded,
+      and there may be a minimum required size.  */
+    unadjusted_args_size
+      = compute_argument_block_size (reg_parm_stack_space, &args_size,
+ 				    preferred_stack_boundary);
+ 
+   /* Sanity checking - the amount stack allocated before call and after call
+      must match.  */
+   old_stack_allocated =  stack_pointer_delta - pending_stack_adjust;
+ 
+   argblock = argument_block (&args_size, unadjusted_args_size, old_stack_level, 
+ 			     reg_parm_stack_space, preferred_stack_boundary,
+ 			     must_preallocate);
+ 
+   /* Don't try to defer pops if preallocating, not even from the first arg,
+      since ARGBLOCK probably refers to the SP.  */
+   if (argblock)
+     NO_DEFER_POP;
+ 
+   compute_argument_addresses (args, argblock, num_actuals);
+ 
+ #ifdef PREFERRED_STACK_BOUNDARY
+    /* If we push args individually in reverse order, perform stack alignment
+       before the first push (the last arg).  */
+    if (PUSH_ARGS_REVERSED && argblock == 0
+        && args_size.constant != unadjusted_args_size)
+      {
+        /* When the stack adjustment is pending, we get better code
+ 	  by combining the adjustments.  */
+        if (pending_stack_adjust 
+ 	   && ! (flags & (ECF_CONST | ECF_PURE))
+ 	   && ! inhibit_defer_pop)
+ 	 {
+ 	   pending_stack_adjust
+ 	     = (combine_pending_stack_adjustment_and_call 
+ 		(unadjusted_args_size,
+ 		 &args_size,
+ 		 preferred_unit_stack_boundary));
+ 	   do_pending_stack_adjust ();
+ 	 }
+        else 
+ 	 anti_adjust_stack (GEN_INT (args_size.constant
+ 				     - unadjusted_args_size));
+      }
+    /* Now that the stack is properly aligned, pops can't safely
+       be deferred during the evaluation of the arguments.  */
+    NO_DEFER_POP;
+ #endif
+ 
+   /* Precompute all register parameters.  It isn't safe to compute anything
+      once we have started filling any specific hard regs.  */
+   precompute_register_parameters (num_actuals, args);
+ 
+ #ifdef REG_PARM_STACK_SPACE
+   /* Save the fixed argument area if it's part of the caller's frame and
+      is clobbered by argument setup for this call.  */
+   if (ACCUMULATE_OUTGOING_ARGS)
+     save_area = save_fixed_argument_area (reg_parm_stack_space, argblock,
+ 					  &low_to_save, &high_to_save);
+ #endif
+ 
+   /* Now store arguments passed on the stack.  */
+   store_args (args, num_actuals, argblock, flags, reg_parm_stack_space,
+ 	      args_size.var != 0);
+ 
+ #ifdef PREFERRED_STACK_BOUNDARY
+   /* If we pushed args in forward order, perform stack alignment
+      after pushing the last arg.  */
+   if (!PUSH_ARGS_REVERSED && argblock == 0)
+     anti_adjust_stack (GEN_INT (args_size.constant
+ 				- unadjusted_args_size));
+ #endif
+   /* If register arguments require space on the stack and stack space
+      was not preallocated, allocate stack space here for arguments
+      passed in registers.  */
+ #ifdef OUTGOING_REG_PARM_STACK_SPACE
+   if (!ACCUMULATE_OUTGOING_ARGS
+        && must_preallocate == 0 && reg_parm_stack_space > 0)
+     anti_adjust_stack (GEN_INT (reg_parm_stack_space));
+ #endif
+ 
+   /* Pass the function the address in which to return a
+      structure value.  */
+   if (structure_value_addr && ! structure_value_addr_parm)
+     {
+       emit_move_insn (struct_value_rtx,
+ 		      force_reg (Pmode,
+ 				 force_operand (structure_value_addr,
+ 						NULL_RTX)));
+ 
+       /* Mark the memory for the aggregate as write-only.  */
+       if (current_function_check_memory_usage)
+ 	emit_library_call (chkr_set_right_libfunc, 1,
+ 			   VOIDmode, 3,
+ 			   structure_value_addr, ptr_mode, 
+ 			   GEN_INT (struct_value_size),
+ 			   TYPE_MODE (sizetype),
+ 			   GEN_INT (MEMORY_USE_WO),
+ 			   TYPE_MODE (integer_type_node));
+ 
+       if (GET_CODE (struct_value_rtx) == REG)
+ 	use_reg (&call_fusage, struct_value_rtx);
+     }
+ 
+   funexp = prepare_call_address (funexp, fndecl, &call_fusage,
+ 				 have_reg_parm);
+ 
+   /* Load parameters into registers.  This ought to appear as
+      very last call before emit_call_1 to minimize hard register
+      lifetimes and avoid crashes on SMALL_REGISTER_CLASSES machines.  */
+   load_register_parameters (args, num_actuals, &call_fusage);
+     
+   /* Perform postincrements before actually calling the function.  */
+   emit_queue ();
+ 
+   next_arg_reg = FUNCTION_ARG (args_so_far, VOIDmode,
+ 				void_type_node, 1);
+ 
+ #ifdef PREFERRED_STACK_BOUNDARY
+   /* Stack must be properly aligned now.  */
+   if (stack_pointer_delta % preferred_unit_stack_boundary)
+     abort ();
+ #endif
+ 
+   /* Generate the actual call instruction.  */
+   emit_call_1 (funexp, fndecl, funtype, unadjusted_args_size,
+ 	       args_size.constant, struct_value_size,
+ 	       next_arg_reg, *valreg, old_inhibit_defer_pop, call_fusage,
+ 	       flags);
+ 
+   /* Verify that we've deallocated all the stack we used.  */
+   if (old_stack_allocated != stack_pointer_delta - pending_stack_adjust)
+     abort();
+ 
+ 
+   /* If call is cse'able, make appropriate pair of reg-notes around it.
+      Test valreg so we don't crash; may safely ignore `const'
+      if return type is void.  Disable for PARALLEL return values, because
+      we have no way to move such values into a pseudo register.  */
+   if ((flags & (ECF_CONST | ECF_PURE))
+       && *valreg != 0 && GET_CODE (*valreg) != PARALLEL)
+     {
+       rtx note = 0;
+       rtx temp = gen_reg_rtx (GET_MODE (*valreg));
+       rtx insns;
+ 
+       /* Mark the return value as a pointer if needed.  */
+       if (TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE)
+ 	mark_reg_pointer (temp, TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp))));
+ 
+       /* Construct an "equal form" for the value which mentions all the
+ 	 arguments in order as well as the function name.  */
+       for (i = 0; i < num_actuals; i++)
+ 	note = gen_rtx_EXPR_LIST (VOIDmode, args[i].initial_value, note);
+       note = gen_rtx_EXPR_LIST (VOIDmode, funexp, note);
+ 
+       insns = get_insns ();
+       end_sequence ();
+ 
+       if (flags & ECF_PURE)
+ 	note = gen_rtx_EXPR_LIST (VOIDmode,
+ 	   gen_rtx_USE (VOIDmode,
+ 			gen_rtx_MEM (BLKmode,
+ 				     gen_rtx_SCRATCH (VOIDmode))), note);
+ 
+       emit_libcall_block (insns, temp, *valreg, note);
+ 
+       *valreg = temp;
+     }
+   else if (flags & (ECF_CONST | ECF_PURE))
+     {
+       /* Otherwise, just write out the sequence without a note.  */
+       rtx insns = get_insns ();
+ 
+       end_sequence ();
+       emit_insns (insns);
+     }
+   else if (flags & ECF_MALLOC)
+     {
+       rtx temp = gen_reg_rtx (GET_MODE (*valreg));
+       rtx last, insns;
  
+       /* The return value from a malloc-like function is a pointer. */
+       if (TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE)
+ 	mark_reg_pointer (temp, BIGGEST_ALIGNMENT);
+ 
+       emit_move_insn (temp, *valreg);
+ 
+       /* The return value from a malloc-like function can not alias
+ 	 anything else.  */
+       last = get_last_insn ();
+       REG_NOTES (last) = 
+ 	gen_rtx_EXPR_LIST (REG_NOALIAS, temp, REG_NOTES (last));
+ 
+       /* Write out the sequence.  */
+       insns = get_insns ();
+       end_sequence ();
+       emit_insns (insns);
+       *valreg = temp;
+     }
+ 
+   /* For calls to `setjmp', etc., inform flow.c it should complain
+      if nonvolatile values are live.  For functions that cannot return,
+      inform flow that control does not fall through.  */
+ 
+   if ((flags & (ECF_RETURNS_TWICE | ECF_NORETURN | ECF_LONGJMP)))
+     {
+       /* The barrier or NOTE_INSN_SETJMP note must be emitted
+ 	 immediately after the CALL_INSN.  Some ports emit more
+ 	 than just a CALL_INSN above, so we must search for it here.  */
+ 
+       rtx last = get_last_insn ();
+       while (GET_CODE (last) != CALL_INSN)
+ 	last = PREV_INSN (last);
+ 
+       if (flags & ECF_RETURNS_TWICE)
+ 	{
+ 	  emit_note_after (NOTE_INSN_SETJMP, last);
+ 	  current_function_calls_setjmp = 1;
+ 	}
+       else
+ 	emit_barrier_after (last);
+     }
+ 
+   if (flags & ECF_LONGJMP)
+     current_function_calls_longjmp = 1;
+ 
+   /* If this function is returning into a memory location marked as
+      readonly, it means it is initializing that location.  But we normally
+      treat functions as not clobbering such locations, so we need to
+      specify that this one does.  */
+   if (*target != 0 && GET_CODE (*target) == MEM
+       && structure_value_addr != 0 && RTX_UNCHANGING_P (*target))
+     emit_insn (gen_rtx_CLOBBER (VOIDmode, *target));
+ 
+   /* If value type not void, construct an rtx for the value.  */
+ 
+   /* If there are cleanups to be called, don't use a hard reg as target.
+      We need to double check this and see if it matters anymore.  */
+   if (any_pending_cleanups (1))
+     {
+       if (*target && REG_P (*target)
+ 	  && REGNO (*target) < FIRST_PSEUDO_REGISTER)
+ 	*target = 0;
+     }
+ 
+   if (TYPE_MODE (TREE_TYPE (exp)) == VOIDmode
+       || ignore)
+     {
+       *target = const0_rtx;
+     }
+   else if (structure_value_addr)
+     {
+       if (*target == 0 || GET_CODE (*target) != MEM)
+ 	{
+ 	  *target = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)),
+ 				memory_address (TYPE_MODE (TREE_TYPE (exp)),
+ 						structure_value_addr));
+ 	  MEM_SET_IN_STRUCT_P (*target,
+ 			       AGGREGATE_TYPE_P (TREE_TYPE (exp)));
+ 	}
+     }
+   else if (pcc_struct_value)
+     {
+       /* This is the special C++ case where we need to
+ 	 know what the true target was.  We take care to
+ 	 never use this value more than once in one expression.  */
+       *target = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)),
+ 			    copy_to_reg (*valreg));
+       MEM_SET_IN_STRUCT_P (*target, AGGREGATE_TYPE_P (TREE_TYPE (exp)));
+     }
+   /* Handle calls that return values in multiple non-contiguous locations.
+      The Irix 6 ABI has examples of this.  */
+   else if (GET_CODE (*valreg) == PARALLEL)
+     {
+       int bytes = int_size_in_bytes (TREE_TYPE (exp));
+ 
+       if (*target == 0)
+ 	{
+ 	  *target = assign_stack_temp (TYPE_MODE (TREE_TYPE (exp)),
+ 				      bytes, 0);
+ 	  MEM_SET_IN_STRUCT_P (*target, AGGREGATE_TYPE_P (TREE_TYPE (exp)));
+ 	  preserve_temp_slots (*target);
+ 	}
+ 
+       if (! rtx_equal_p (*target, *valreg))
+ 	emit_group_store (*target, *valreg, bytes,
+ 			  TYPE_ALIGN (TREE_TYPE (exp)));
+     }
+   else if (*target
+ 	   && GET_MODE (*target) == TYPE_MODE (TREE_TYPE (exp))
+ 	   && GET_MODE (*target) == GET_MODE (*valreg))
+     {
+       /* TARGET and VALREG cannot be equal at this point because the
+ 	 latter would not have REG_FUNCTION_VALUE_P true, while the
+ 	 former would if it were referring to the same register.
+ 
+ 	 If they refer to the same register, this move will be a no-op,
+ 	 except when function inlining is being done.  */
+       emit_move_insn (*target, *valreg);
+     }
+   else if (TYPE_MODE (TREE_TYPE (exp)) == BLKmode)
+     *target = copy_blkmode_from_reg (*target, *valreg, TREE_TYPE (exp));
+   else
+      *target = copy_to_reg (*valreg);
+ 
+ #ifdef PROMOTE_FUNCTION_RETURN
+   /* If we promoted this return value, make the proper SUBREG.  TARGET
+      might be const0_rtx here, so be careful.  */
+   if (GET_CODE (*target) == REG
+       && TYPE_MODE (TREE_TYPE (exp)) != BLKmode
+       && GET_MODE (*target) != TYPE_MODE (TREE_TYPE (exp)))
+     {
+       tree type = TREE_TYPE (exp);
+       int unsignedp = TREE_UNSIGNED (type);
+ 
+       /* If we don't promote as expected, something is wrong.  */
+       if (GET_MODE (*target)
+ 	  != promote_mode (type, TYPE_MODE (type), &unsignedp, 1))
+ 	abort ();
+ 
+       *target = gen_rtx_SUBREG (TYPE_MODE (type), *target, 0);
+       SUBREG_PROMOTED_VAR_P (*target) = 1;
+       SUBREG_PROMOTED_UNSIGNED_P (*target) = unsignedp;
+     }
+ #endif
+ 
+ 
+   /* If size of args is variable or this was a constructor call for a stack
+      argument, restore saved stack-pointer value.  */
+ 
+   if (old_stack_level->value)
+     restore_stack_level (old_stack_level);
+   else if (ACCUMULATE_OUTGOING_ARGS)
+     {
+ #ifdef REG_PARM_STACK_SPACE
+       if (save_area)
+ 	{
+ 	  restore_fixed_argument_area (save_area, argblock,
+ 				       high_to_save, low_to_save);
+ 	}
+ #endif
+ 
+       /* If we saved any argument areas, restore them.  */
+       for (i = 0; i < num_actuals; i++)
+ 	if (args[i].save_area)
+ 	  {
+ 	    enum machine_mode save_mode = GET_MODE (args[i].save_area);
+ 	    rtx stack_area
+ 	      = gen_rtx_MEM (save_mode,
+ 			     memory_address (save_mode,
+ 					     XEXP (args[i].stack_slot, 0)));
+ 
+ 	    if (save_mode != BLKmode)
+ 	      emit_move_insn (stack_area, args[i].save_area);
+ 	    else
+ 	      emit_block_move (stack_area,
+ 			       validize_mem (args[i].save_area),
+ 			       GEN_INT (args[i].size.constant),
+ 			       PARM_BOUNDARY);
+ 	  }
+ 
+       highest_outgoing_arg_in_use = initial_highest_arg_in_use;
+       stack_usage_map = initial_stack_usage_map;
+     }
+ 
+   /* If this was alloca, record the new stack level for nonlocal gotos.  
+      Check for the handler slots since we might not have a save area
+      for non-local gotos.  */
+ 
+   if ((flags & ECF_MAY_BE_ALLOCA) && nonlocal_goto_handler_slots != 0)
+     emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, NULL_RTX);
+ 
+   pop_temp_slots ();
+ 
+   /* Free up storage we no longer need.  */
+   cleanup_args (args, num_actuals);
+ 
+   insns = get_insns ();
+   end_sequence ();
+ 
+   return insns;
+ }
+ 
  /* Generate all the code for a function call
     and return an rtx for its value.
     Store the value in TARGET (specified as an rtx) if convenient.
*************** expand_call (exp, target, ignore)
*** 1981,1992 ****
  
    /* Total size in bytes of all the stack-parms scanned so far.  */
    struct args_size args_size;
-   /* Size of arguments before any adjustments (such as rounding).  */
-   int unadjusted_args_size;
    /* Data on reg parms scanned so far.  */
    CUMULATIVE_ARGS args_so_far;
!   /* Nonzero if a reg parm has been scanned.  */
!   int reg_parm_seen;
    /* Nonzero if this is an indirect function call.  */
  
    /* Nonzero if we must avoid push-insns in the args for this call. 
--- 2821,2830 ----
  
    /* Total size in bytes of all the stack-parms scanned so far.  */
    struct args_size args_size;
    /* Data on reg parms scanned so far.  */
    CUMULATIVE_ARGS args_so_far;
!   /* Nonzero if function does have register parameter.  */
!   int have_reg_parm = 0;
    /* Nonzero if this is an indirect function call.  */
  
    /* Nonzero if we must avoid push-insns in the args for this call. 
*************** expand_call (exp, target, ignore)
*** 2000,2009 ****
    /* Size of the stack reserved for parameter registers.  */
    int reg_parm_stack_space = 0;
  
-   /* Address of space preallocated for stack parms
-      (on machines that lack push insns), or 0 if space not preallocated.  */
-   rtx argblock = 0;
- 
    /* Mask of ECF_ flags.  */
    int flags = 0;
    /* Nonzero if this is a call to an inline function.  */
--- 2838,2843 ----
*************** expand_call (exp, target, ignore)
*** 2015,2035 ****
    rtx save_area = 0;		/* Place that it is saved */
  #endif
  
-   int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
-   char *initial_stack_usage_map = stack_usage_map;
-   int old_stack_arg_under_construction = 0;
- 
-   rtx old_stack_level = 0;
-   int old_pending_adj = 0;
-   int old_inhibit_defer_pop = inhibit_defer_pop;
-   int old_stack_allocated;
-   rtx call_fusage;
    register tree p;
    register int i;
    /* The alignment of the stack, in bits.  */
    HOST_WIDE_INT preferred_stack_boundary;
    /* The alignment of the stack, in bytes.  */
    HOST_WIDE_INT preferred_unit_stack_boundary;
  
    /* The value of the function call can be put in a hard register.  But
       if -fcheck-memory-usage, code which invokes functions (and thus
--- 2849,2865 ----
    rtx save_area = 0;		/* Place that it is saved */
  #endif
  
    register tree p;
    register int i;
    /* The alignment of the stack, in bits.  */
    HOST_WIDE_INT preferred_stack_boundary;
    /* The alignment of the stack, in bytes.  */
    HOST_WIDE_INT preferred_unit_stack_boundary;
+   struct stack_level old_stack_level;
+ 
+   /* We use value of 0 to indicate that we didn't save stack
+      level yet.  */
+   old_stack_level.value = 0;
  
    /* The value of the function call can be put in a hard register.  But
       if -fcheck-memory-usage, code which invokes functions (and thus
*************** expand_call (exp, target, ignore)
*** 2179,2184 ****
--- 3009,3016 ----
      abort ();
    funtype = TREE_TYPE (funtype);
  
+   funexp = rtx_for_function_call (fndecl, exp);
+ 
    /* See if this is a call to a function that can return more than once
       or a call to longjmp or malloc.  */
    flags |= special_function_p (fndecl, flags);
*************** expand_call (exp, target, ignore)
*** 2262,2270 ****
    initialize_argument_information (num_actuals, args, &args_size,
  				   n_named_args, actparms, fndecl,
  				   &args_so_far, reg_parm_stack_space,
! 				   &old_stack_level, &old_pending_adj,
  				   &must_preallocate, &flags);
  
    if (args_size.var)
      {
        /* If this function requires a variable-sized argument list, don't
--- 3094,3107 ----
    initialize_argument_information (num_actuals, args, &args_size,
  				   n_named_args, actparms, fndecl,
  				   &args_so_far, reg_parm_stack_space,
! 				   &old_stack_level,
  				   &must_preallocate, &flags);
  
+   /* Check whether we have any argument passed in register.  */
+   for (i = 0; i < num_actuals; i++)
+     if (args[i].reg != 0 && ! args[i].pass_on_stack)
+ 	have_reg_parm = 1;
+ 
    if (args_size.var)
      {
        /* If this function requires a variable-sized argument list, don't
*************** expand_call (exp, target, ignore)
*** 2281,2286 ****
--- 3118,3128 ----
  						num_actuals, args,
  						&args_size);
  
+ #ifdef FINAL_REG_PARM_STACK_SPACE
+       reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant,
+ 							 args_size.var);
+ #endif
+ 
    /* If the structure value address will reference the stack pointer, we
       must stabilize it.  We don't need to do this if we know that we are
       not going to adjust the stack pointer in processing this call.  */
*************** expand_call (exp, target, ignore)
*** 2293,2298 ****
--- 3135,3153 ----
  	  || (!ACCUMULATE_OUTGOING_ARGS && args_size.constant)))
      structure_value_addr = copy_to_reg (structure_value_addr);
  
+   /* Figure out the register where the value, if any, will come back.  */
+   valreg = 0;
+   if (TYPE_MODE (TREE_TYPE (exp)) != VOIDmode
+       && ! structure_value_addr)
+     {
+       if (pcc_struct_value)
+ 	valreg = hard_function_value (build_pointer_type (TREE_TYPE (exp)),
+ 				      fndecl, (pass == 0));
+       else
+ 	valreg = hard_function_value (TREE_TYPE (exp), fndecl, (pass == 0));
+     }
+ 
+ 
    /* Tail calls can make things harder to debug, and we're traditionally
       pushed these optimizations into -O2.  Don't try if we're already
       expanding a call, as that means we're an argument.  Similarly, if
*************** expand_call (exp, target, ignore)
*** 2310,2315 ****
--- 3165,3174 ----
        || !rtx_equal_function_value_matters
        || !stmt_loop_nest_empty ()
        || any_pending_cleanups (1)
+       /* Variable sized arguments and constructor calls allocate
+ 	 temporary dynamic stack slots.  We can´t do sibbling call
+ 	 optimization for these.  */
+       || old_stack_level.value
        || args_size.var)
      try_tail_call = try_tail_recursion = 0;
  
*************** expand_call (exp, target, ignore)
*** 2341,2346 ****
--- 3200,3210 ----
  	 before the sibcall_epilogue.  */
        || fndecl == NULL_TREE
        || (flags & (ECF_RETURNS_TWICE | ECF_LONGJMP))
+       /* Some ABIs define return values in multiple distinc registers.
+ 	 We don´t support tail call yet in this case.  
+ 	 ??? I believe that just disabling this test will make the calls
+ 	 working, but I would rather to test this first.
+        */
        || !FUNCTION_OK_FOR_SIBCALL (fndecl)
        /* If this function requires more stack slots than the current
  	 function, we cannot change it into a sibling call.  */
*************** expand_call (exp, target, ignore)
*** 2461,2466 ****
--- 3325,3331 ----
  			 VOIDmode, 0);
      }
  
+ 
    /* Ensure current function's preferred stack boundary is at least
       what we need.  We don't have to increase alignment for recursive
       functions.  */
*************** expand_call (exp, target, ignore)
*** 2472,3225 ****
  
    function_call_count++;
  
!   /* We want to make two insn chains; one for a sibling call, the other
!      for a normal call.  We will select one of the two chains after
!      initial RTL generation is complete.  */
!   for (pass = 0; pass < 2; pass++)
      {
!       int sibcall_failure = 0;
!       /* We want to emit ay pending stack adjustments before the tail
! 	 recursion "call".  That way we know any adjustment after the tail
! 	 recursion call can be ignored if we indeed use the tail recursion
! 	 call expansion.  */
!       int save_pending_stack_adjust;
!       int save_stack_pointer_delta;
!       rtx insns;
!       rtx before_call, next_arg_reg;
! 
!       if (pass == 0)
! 	{
! 	  if (! try_tail_call)
! 	    continue;
! 
! 	  /* Emit any queued insns now; otherwise they would end up in
!              only one of the alternates.  */
! 	  emit_queue ();
! 
! 	  /* We know at this point that there are not currently any
! 	     pending cleanups.  If, however, in the process of evaluating
! 	     the arguments we were to create some, we'll need to be
! 	     able to get rid of them.  */
! 	  expand_start_target_temps ();
! 
! 	  /* State variables we need to save and restore between
! 	     iterations.  */
! 	  save_pending_stack_adjust = pending_stack_adjust;
! 	  save_stack_pointer_delta = stack_pointer_delta;
! 	}
!       if (pass)
! 	flags &= ~ECF_SIBCALL;
!       else
! 	flags |= ECF_SIBCALL;
! 
!       /* Other state variables that we must reinitialize each time
! 	 through the loop (that are not initialized by the loop itself).  */
!       argblock = 0;
!       call_fusage = 0;
! 
!       /* Start a new sequence for the normal call case. 
! 
! 	 From this point on, if the sibling call fails, we want to set
! 	 sibcall_failure instead of continuing the loop.  */
!       start_sequence ();
! 
!       /* When calling a const function, we must pop the stack args right away,
! 	 so that the pop is deleted or moved with the call.  */
!       if (flags & (ECF_CONST | ECF_PURE))
! 	NO_DEFER_POP;
! 
!       /* Don't let pending stack adjusts add up to too much.
! 	 Also, do all pending adjustments now if there is any chance
! 	 this might be a call to alloca or if we are expanding a sibling
! 	 call sequence.  */
!       if (pending_stack_adjust >= 32
! 	  || (pending_stack_adjust > 0 && (flags & ECF_MAY_BE_ALLOCA))
! 	  || pass == 0)
! 	do_pending_stack_adjust ();
  
-       /* Push the temporary stack slot level so that we can free any
- 	 temporaries we make.  */
-       push_temp_slots ();
- 
- 
- #ifdef FINAL_REG_PARM_STACK_SPACE
-       reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant,
- 							 args_size.var);
- #endif
-       /* Precompute any arguments as needed.  */
-       if (pass)
- 	precompute_arguments (flags, num_actuals, args);
- 
-       /* Now we are about to start emitting insns that can be deleted
- 	 if a libcall is deleted.  */
-       if (flags & (ECF_CONST | ECF_PURE | ECF_MALLOC))
- 	start_sequence ();
- 
-       /* Compute the actual size of the argument block required.  The variable
- 	 and constant sizes must be combined, the size may have to be rounded,
- 	 and there may be a minimum required size.  When generating a sibcall
- 	 pattern, do not round up, since we'll be re-using whatever space our
- 	 caller provided.  */
-       unadjusted_args_size
- 	= compute_argument_block_size (reg_parm_stack_space, &args_size,
- 				       (pass == 0 ? 0
- 					: preferred_stack_boundary));
- 
-       old_stack_allocated =  stack_pointer_delta - pending_stack_adjust;
- 
-       /* The argument block when performing a sibling call is the
-          incoming argument block.  */
-       if (pass == 0)
- 	argblock = virtual_incoming_args_rtx;
- 
-       /* If we have no actual push instructions, or shouldn't use them,
- 	 make space for all args right now.  */
-       else if (args_size.var != 0)
- 	{
- 	  if (old_stack_level == 0)
- 	    {
- 	      emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX);
- 	      old_pending_adj = pending_stack_adjust;
- 	      pending_stack_adjust = 0;
- 	      /* stack_arg_under_construction says whether a stack arg is
- 		 being constructed at the old stack level.  Pushing the stack
- 		 gets a clean outgoing argument block.  */
- 	      old_stack_arg_under_construction = stack_arg_under_construction;
- 	      stack_arg_under_construction = 0;
- 	    }
- 	  argblock = push_block (ARGS_SIZE_RTX (args_size), 0, 0);
- 	}
-       else
- 	{
- 	  /* Note that we must go through the motions of allocating an argument
- 	     block even if the size is zero because we may be storing args
- 	     in the area reserved for register arguments, which may be part of
- 	     the stack frame.  */
- 
- 	  int needed = args_size.constant;
- 
- 	  /* Store the maximum argument space used.  It will be pushed by
- 	     the prologue (if ACCUMULATE_OUTGOING_ARGS, or stack overflow
- 	     checking).  */
- 
- 	  if (needed > current_function_outgoing_args_size)
- 	    current_function_outgoing_args_size = needed;
- 
- 	  if (must_preallocate)
- 	    {
- 	      if (ACCUMULATE_OUTGOING_ARGS)
- 		{
- 		  /* Since the stack pointer will never be pushed, it is
- 		     possible for the evaluation of a parm to clobber
- 		     something we have already written to the stack.
- 		     Since most function calls on RISC machines do not use
- 		     the stack, this is uncommon, but must work correctly.
- 
- 		     Therefore, we save any area of the stack that was already
- 		     written and that we are using.  Here we set up to do this
- 		     by making a new stack usage map from the old one.  The
- 		     actual save will be done by store_one_arg. 
- 
- 		     Another approach might be to try to reorder the argument
- 		     evaluations to avoid this conflicting stack usage.  */
- 
- #ifndef OUTGOING_REG_PARM_STACK_SPACE
- 		  /* Since we will be writing into the entire argument area,
- 		     the map must be allocated for its entire size, not just
- 		     the part that is the responsibility of the caller.  */
- 		  needed += reg_parm_stack_space;
- #endif
- 
- #ifdef ARGS_GROW_DOWNWARD
- 		  highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
- 						     needed + 1);
- #else
- 		  highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
- 						     needed);
- #endif
- 		  stack_usage_map
- 		    = (char *) alloca (highest_outgoing_arg_in_use);
- 
- 		  if (initial_highest_arg_in_use)
- 		    bcopy (initial_stack_usage_map, stack_usage_map,
- 			   initial_highest_arg_in_use);
- 
- 		  if (initial_highest_arg_in_use != highest_outgoing_arg_in_use)
- 		    bzero (&stack_usage_map[initial_highest_arg_in_use],
- 			   (highest_outgoing_arg_in_use
- 			    - initial_highest_arg_in_use));
- 		  needed = 0;
- 
- 		  /* The address of the outgoing argument list must not be
- 		     copied to a register here, because argblock would be left
- 		     pointing to the wrong place after the call to
- 		     allocate_dynamic_stack_space below. */
- 
- 		  argblock = virtual_outgoing_args_rtx;
- 	        }
- 	      else
- 		{
- 		  if (inhibit_defer_pop == 0)
- 		    {
- 		      /* Try to reuse some or all of the pending_stack_adjust
- 			 to get this space.  */
- 		      needed
- 			= (combine_pending_stack_adjustment_and_call 
- 			   (unadjusted_args_size,
- 			    &args_size,
- 			    preferred_unit_stack_boundary));
- 
- 		      /* combine_pending_stack_adjustment_and_call computes
- 			 an adjustment before the arguments are allocated.
- 			 Account for them and see whether or not the stack
- 			 needs to go up or down.  */
- 		      needed = unadjusted_args_size - needed;
- 
- 		      if (needed < 0)
- 			{
- 			  /* We're releasing stack space.  */
- 			  /* ??? We can avoid any adjustment at all if we're
- 			     already aligned.  FIXME.  */
- 			  pending_stack_adjust = -needed;
- 			  do_pending_stack_adjust ();
- 			  needed = 0;
- 			}
- 		      else 
- 			/* We need to allocate space.  We'll do that in
- 			   push_block below.  */
- 			pending_stack_adjust = 0;
- 		    }
- 
- 		  /* Special case this because overhead of `push_block' in
- 		     this case is non-trivial.  */
- 		  if (needed == 0)
- 		    argblock = virtual_outgoing_args_rtx;
- 		  else
- 		    argblock = push_block (GEN_INT (needed), 0, 0);
- 
- 		  /* We only really need to call `copy_to_reg' in the case
- 		     where push insns are going to be used to pass ARGBLOCK
- 		     to a function call in ARGS.  In that case, the stack
- 		     pointer changes value from the allocation point to the
- 		     call point, and hence the value of
- 		     VIRTUAL_OUTGOING_ARGS_RTX changes as well.  But might
- 		     as well always do it.  */
- 		  argblock = copy_to_reg (argblock);
- 
- 		  /* The save/restore code in store_one_arg handles all
- 		     cases except one: a constructor call (including a C
- 		     function returning a BLKmode struct) to initialize
- 		     an argument.  */
- 		  if (stack_arg_under_construction)
- 		    {
- #ifndef OUTGOING_REG_PARM_STACK_SPACE
- 		      rtx push_size = GEN_INT (reg_parm_stack_space
- 					       + args_size.constant);
- #else
- 		      rtx push_size = GEN_INT (args_size.constant);
- #endif
- 		      if (old_stack_level == 0)
- 			{
- 			  emit_stack_save (SAVE_BLOCK, &old_stack_level,
- 					   NULL_RTX);
- 			  old_pending_adj = pending_stack_adjust;
- 			  pending_stack_adjust = 0;
- 			  /* stack_arg_under_construction says whether a stack
- 			     arg is being constructed at the old stack level.
- 			     Pushing the stack gets a clean outgoing argument
- 			     block.  */
- 			  old_stack_arg_under_construction
- 			    = stack_arg_under_construction;
- 			  stack_arg_under_construction = 0;
- 			  /* Make a new map for the new argument list.  */
- 			  stack_usage_map = (char *)
- 			    alloca (highest_outgoing_arg_in_use);
- 			  bzero (stack_usage_map, highest_outgoing_arg_in_use);
- 			  highest_outgoing_arg_in_use = 0;
- 			}
- 		      allocate_dynamic_stack_space (push_size, NULL_RTX,
- 						    BITS_PER_UNIT);
- 		    }
- 		  /* If argument evaluation might modify the stack pointer,
- 		     copy the address of the argument list to a register.  */
- 		  for (i = 0; i < num_actuals; i++)
- 		    if (args[i].pass_on_stack)
- 		      {
- 			argblock = copy_addr_to_reg (argblock);
- 			break;
- 		      }
- 		}
- 	    }
- 	}
- 
-       compute_argument_addresses (args, argblock, num_actuals);
- 
- #ifdef PREFERRED_STACK_BOUNDARY
-       /* If we push args individually in reverse order, perform stack alignment
- 	 before the first push (the last arg).  */
-       if (PUSH_ARGS_REVERSED && argblock == 0
- 	  && args_size.constant != unadjusted_args_size)
- 	{
- 	  /* When the stack adjustment is pending, we get better code
- 	     by combining the adjustments.  */
- 	  if (pending_stack_adjust 
- 	      && ! (flags & (ECF_CONST | ECF_PURE))
- 	      && ! inhibit_defer_pop)
- 	    {
- 	      pending_stack_adjust
- 		= (combine_pending_stack_adjustment_and_call 
- 		   (unadjusted_args_size,
- 		    &args_size,
- 		    preferred_unit_stack_boundary));
- 	      do_pending_stack_adjust ();
- 	    }
- 	  else if (argblock == 0)
- 	    anti_adjust_stack (GEN_INT (args_size.constant
- 					- unadjusted_args_size));
- 	}
-       /* Now that the stack is properly aligned, pops can't safely
- 	 be deferred during the evaluation of the arguments.  */
-       NO_DEFER_POP;
- #endif
- 
-       /* Don't try to defer pops if preallocating, not even from the first arg,
- 	 since ARGBLOCK probably refers to the SP.  */
-       if (argblock)
- 	NO_DEFER_POP;
- 
-       funexp = rtx_for_function_call (fndecl, exp);
- 
-       /* Figure out the register where the value, if any, will come back.  */
-       valreg = 0;
-       if (TYPE_MODE (TREE_TYPE (exp)) != VOIDmode
- 	  && ! structure_value_addr)
- 	{
- 	  if (pcc_struct_value)
- 	    valreg = hard_function_value (build_pointer_type (TREE_TYPE (exp)),
- 					  fndecl, (pass == 0));
- 	  else
- 	    valreg = hard_function_value (TREE_TYPE (exp), fndecl, (pass == 0));
- 	}
- 
-       /* Precompute all register parameters.  It isn't safe to compute anything
- 	 once we have started filling any specific hard regs.  */
-       precompute_register_parameters (num_actuals, args, &reg_parm_seen);
- 
- #ifdef REG_PARM_STACK_SPACE
-       /* Save the fixed argument area if it's part of the caller's frame and
- 	 is clobbered by argument setup for this call.  */
-       if (ACCUMULATE_OUTGOING_ARGS && pass)
- 	save_area = save_fixed_argument_area (reg_parm_stack_space, argblock,
- 					      &low_to_save, &high_to_save);
- #endif
- 
-       /* Now store (and compute if necessary) all non-register parms.
- 	 These come before register parms, since they can require block-moves,
- 	 which could clobber the registers used for register parms.
- 	 Parms which have partial registers are not stored here,
- 	 but we do preallocate space here if they want that.  */
- 
-       for (i = 0; i < num_actuals; i++)
- 	if (args[i].reg == 0 || args[i].pass_on_stack)
- 	  store_one_arg (&args[i], argblock, flags,
- 			 args_size.var != 0, reg_parm_stack_space);
- 
-       /* If we have a parm that is passed in registers but not in memory
- 	 and whose alignment does not permit a direct copy into registers,
- 	 make a group of pseudos that correspond to each register that we
- 	 will later fill.  */
-       if (STRICT_ALIGNMENT)
- 	store_unaligned_arguments_into_pseudos (args, num_actuals);
- 
-       /* Now store any partially-in-registers parm.
- 	 This is the last place a block-move can happen.  */
-       if (reg_parm_seen)
- 	for (i = 0; i < num_actuals; i++)
- 	  if (args[i].partial != 0 && ! args[i].pass_on_stack)
- 	    store_one_arg (&args[i], argblock, flags,
- 			   args_size.var != 0, reg_parm_stack_space);
- 
- #ifdef PREFERRED_STACK_BOUNDARY
-       /* If we pushed args in forward order, perform stack alignment
- 	 after pushing the last arg.  */
-       if (!PUSH_ARGS_REVERSED && argblock == 0)
- 	anti_adjust_stack (GEN_INT (args_size.constant
- 				    - unadjusted_args_size));
- #endif
- 
-       /* If register arguments require space on the stack and stack space
- 	 was not preallocated, allocate stack space here for arguments
- 	 passed in registers.  */
- #ifdef OUTGOING_REG_PARM_STACK_SPACE
-       if (!ACCUMULATE_OUTGOING_ARGS
- 	   && must_preallocate == 0 && reg_parm_stack_space > 0)
- 	anti_adjust_stack (GEN_INT (reg_parm_stack_space));
- #endif
- 
-       /* Pass the function the address in which to return a
- 	 structure value.  */
-       if (pass != 0 && structure_value_addr && ! structure_value_addr_parm)
- 	{
- 	  emit_move_insn (struct_value_rtx,
- 			  force_reg (Pmode,
- 				     force_operand (structure_value_addr,
- 						    NULL_RTX)));
- 
- 	  /* Mark the memory for the aggregate as write-only.  */
- 	  if (current_function_check_memory_usage)
- 	    emit_library_call (chkr_set_right_libfunc, 1,
- 			       VOIDmode, 3,
- 			       structure_value_addr, ptr_mode, 
- 			       GEN_INT (struct_value_size),
- 			       TYPE_MODE (sizetype),
- 			       GEN_INT (MEMORY_USE_WO),
- 			       TYPE_MODE (integer_type_node));
- 
- 	  if (GET_CODE (struct_value_rtx) == REG)
- 	    use_reg (&call_fusage, struct_value_rtx);
- 	}
- 
-       funexp = prepare_call_address (funexp, fndecl, &call_fusage,
- 				     reg_parm_seen);
- 
-       load_register_parameters (args, num_actuals, &call_fusage);
-      
-       /* Perform postincrements before actually calling the function.  */
-       emit_queue ();
- 
-       /* Save a pointer to the last insn before the call, so that we can
- 	 later safely search backwards to find the CALL_INSN.  */
-       before_call = get_last_insn ();
- 
-       /* Set up next argument register.  For sibling calls on machines
- 	 with register windows this should be the incoming register.  */
- #ifdef FUNCTION_INCOMING_ARG
-       if (pass == 0)
- 	next_arg_reg = FUNCTION_INCOMING_ARG (args_so_far, VOIDmode,
- 					      void_type_node, 1);
-       else
- #endif
- 	next_arg_reg = FUNCTION_ARG (args_so_far, VOIDmode,
- 				     void_type_node, 1);
- 
-       /* All arguments and registers used for the call must be set up by
- 	 now!  */
- 
- #ifdef PREFERRED_STACK_BOUNDARY
-       /* Stack must be properly aligned now.  */
-       if (pass && stack_pointer_delta % preferred_unit_stack_boundary)
- 	abort ();
- #endif
- 
-       /* Generate the actual call instruction.  */
-       emit_call_1 (funexp, fndecl, funtype, unadjusted_args_size,
- 		   args_size.constant, struct_value_size,
- 		   next_arg_reg, valreg, old_inhibit_defer_pop, call_fusage,
- 		   flags);
- 
-       /* Verify that we've deallocated all the stack we used.  */
-       if (pass
-           && old_stack_allocated != stack_pointer_delta - pending_stack_adjust)
- 	abort();
- 
-       /* If call is cse'able, make appropriate pair of reg-notes around it.
- 	 Test valreg so we don't crash; may safely ignore `const'
- 	 if return type is void.  Disable for PARALLEL return values, because
- 	 we have no way to move such values into a pseudo register.  */
-       if (pass
- 	  && (flags & (ECF_CONST | ECF_PURE))
- 	  && valreg != 0 && GET_CODE (valreg) != PARALLEL)
- 	{
- 	  rtx note = 0;
- 	  rtx temp = gen_reg_rtx (GET_MODE (valreg));
- 	  rtx insns;
- 
- 	  /* Mark the return value as a pointer if needed.  */
- 	  if (TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE)
- 	    mark_reg_pointer (temp, TYPE_ALIGN (TREE_TYPE (TREE_TYPE (exp))));
- 
- 	  /* Construct an "equal form" for the value which mentions all the
- 	     arguments in order as well as the function name.  */
- 	  for (i = 0; i < num_actuals; i++)
- 	    note = gen_rtx_EXPR_LIST (VOIDmode, args[i].initial_value, note);
- 	  note = gen_rtx_EXPR_LIST (VOIDmode, funexp, note);
- 
- 	  insns = get_insns ();
- 	  end_sequence ();
- 
- 	  if (flags & ECF_PURE)
- 	    note = gen_rtx_EXPR_LIST (VOIDmode,
- 	       gen_rtx_USE (VOIDmode,
- 			    gen_rtx_MEM (BLKmode,
- 				    	 gen_rtx_SCRATCH (VOIDmode))), note);
- 
- 	  emit_libcall_block (insns, temp, valreg, note);
-   
- 	  valreg = temp;
- 	}
-       else if (flags & (ECF_CONST | ECF_PURE))
- 	{
- 	  /* Otherwise, just write out the sequence without a note.  */
- 	  rtx insns = get_insns ();
- 
- 	  end_sequence ();
- 	  emit_insns (insns);
- 	}
-       else if (flags & ECF_MALLOC)
- 	{
- 	  rtx temp = gen_reg_rtx (GET_MODE (valreg));
- 	  rtx last, insns;
- 
- 	  /* The return value from a malloc-like function is a pointer. */
- 	  if (TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE)
- 	    mark_reg_pointer (temp, BIGGEST_ALIGNMENT);
- 
- 	  emit_move_insn (temp, valreg);
- 
- 	  /* The return value from a malloc-like function can not alias
- 	     anything else.  */
- 	  last = get_last_insn ();
- 	  REG_NOTES (last) = 
- 	    gen_rtx_EXPR_LIST (REG_NOALIAS, temp, REG_NOTES (last));
- 
- 	  /* Write out the sequence.  */
- 	  insns = get_insns ();
- 	  end_sequence ();
- 	  emit_insns (insns);
- 	  valreg = temp;
- 	}
- 
-       /* For calls to `setjmp', etc., inform flow.c it should complain
- 	 if nonvolatile values are live.  For functions that cannot return,
- 	 inform flow that control does not fall through.  */
- 
-       if ((flags & (ECF_RETURNS_TWICE | ECF_NORETURN | ECF_LONGJMP)) || pass == 0)
- 	{
- 	  /* The barrier or NOTE_INSN_SETJMP note must be emitted
- 	     immediately after the CALL_INSN.  Some ports emit more
- 	     than just a CALL_INSN above, so we must search for it here.  */
- 
- 	  rtx last = get_last_insn ();
- 	  while (GET_CODE (last) != CALL_INSN)
- 	    {
- 	      last = PREV_INSN (last);
- 	      /* There was no CALL_INSN?  */
- 	      if (last == before_call)
- 		abort ();
- 	    }
- 
- 	  if (flags & ECF_RETURNS_TWICE)
- 	    {
- 	      emit_note_after (NOTE_INSN_SETJMP, last);
- 	      current_function_calls_setjmp = 1;
- 	    }
- 	  else
- 	    emit_barrier_after (last);
- 	}
- 
-       if (flags & ECF_LONGJMP)
- 	current_function_calls_longjmp = 1;
- 
-       /* If this function is returning into a memory location marked as
- 	 readonly, it means it is initializing that location.  But we normally
- 	 treat functions as not clobbering such locations, so we need to
- 	 specify that this one does.  */
-       if (target != 0 && GET_CODE (target) == MEM
- 	  && structure_value_addr != 0 && RTX_UNCHANGING_P (target))
- 	emit_insn (gen_rtx_CLOBBER (VOIDmode, target));
- 
-       /* If value type not void, return an rtx for the value.  */
- 
-       /* If there are cleanups to be called, don't use a hard reg as target.
- 	 We need to double check this and see if it matters anymore.  */
-       if (any_pending_cleanups (1))
- 	{
- 	  if (target && REG_P (target)
- 	      && REGNO (target) < FIRST_PSEUDO_REGISTER)
- 	    target = 0;
- 	  sibcall_failure = 1;
- 	}
- 
-       if (TYPE_MODE (TREE_TYPE (exp)) == VOIDmode
- 	  || ignore)
- 	{
- 	  target = const0_rtx;
- 	}
-       else if (structure_value_addr)
- 	{
- 	  if (target == 0 || GET_CODE (target) != MEM)
- 	    {
- 	      target = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)),
- 				    memory_address (TYPE_MODE (TREE_TYPE (exp)),
- 						    structure_value_addr));
- 	      MEM_SET_IN_STRUCT_P (target,
- 				   AGGREGATE_TYPE_P (TREE_TYPE (exp)));
- 	    }
- 	}
-       else if (pcc_struct_value)
- 	{
- 	  /* This is the special C++ case where we need to
- 	     know what the true target was.  We take care to
- 	     never use this value more than once in one expression.  */
- 	  target = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)),
- 				copy_to_reg (valreg));
- 	  MEM_SET_IN_STRUCT_P (target, AGGREGATE_TYPE_P (TREE_TYPE (exp)));
- 	}
-       /* Handle calls that return values in multiple non-contiguous locations.
- 	 The Irix 6 ABI has examples of this.  */
-       else if (GET_CODE (valreg) == PARALLEL)
- 	{
- 	  int bytes = int_size_in_bytes (TREE_TYPE (exp));
- 
- 	  if (target == 0)
- 	    {
- 	      target = assign_stack_temp (TYPE_MODE (TREE_TYPE (exp)),
- 					  bytes, 0);
- 	      MEM_SET_IN_STRUCT_P (target, AGGREGATE_TYPE_P (TREE_TYPE (exp)));
- 	      preserve_temp_slots (target);
- 	    }
- 
- 	  if (! rtx_equal_p (target, valreg))
- 	    emit_group_store (target, valreg, bytes,
- 			      TYPE_ALIGN (TREE_TYPE (exp)));
- 
- 	  /* We can not support sibling calls for this case.  */
- 	  sibcall_failure = 1;
- 	}
-       else if (target
- 	       && GET_MODE (target) == TYPE_MODE (TREE_TYPE (exp))
- 	       && GET_MODE (target) == GET_MODE (valreg))
- 	{
- 	  /* TARGET and VALREG cannot be equal at this point because the
- 	     latter would not have REG_FUNCTION_VALUE_P true, while the
- 	     former would if it were referring to the same register.
- 
- 	     If they refer to the same register, this move will be a no-op,
- 	     except when function inlining is being done.  */
- 	  emit_move_insn (target, valreg);
- 	}
-       else if (TYPE_MODE (TREE_TYPE (exp)) == BLKmode)
- 	target = copy_blkmode_from_reg (target, valreg, TREE_TYPE (exp));
-       else
- 	target = copy_to_reg (valreg);
- 
- #ifdef PROMOTE_FUNCTION_RETURN
-       /* If we promoted this return value, make the proper SUBREG.  TARGET
- 	 might be const0_rtx here, so be careful.  */
-       if (GET_CODE (target) == REG
- 	  && TYPE_MODE (TREE_TYPE (exp)) != BLKmode
- 	  && GET_MODE (target) != TYPE_MODE (TREE_TYPE (exp)))
- 	{
- 	  tree type = TREE_TYPE (exp);
- 	  int unsignedp = TREE_UNSIGNED (type);
- 
- 	  /* If we don't promote as expected, something is wrong.  */
- 	  if (GET_MODE (target)
- 	      != promote_mode (type, TYPE_MODE (type), &unsignedp, 1))
- 	    abort ();
- 
- 	  target = gen_rtx_SUBREG (TYPE_MODE (type), target, 0);
- 	  SUBREG_PROMOTED_VAR_P (target) = 1;
- 	  SUBREG_PROMOTED_UNSIGNED_P (target) = unsignedp;
- 	}
- #endif
- 
-       /* If size of args is variable or this was a constructor call for a stack
- 	 argument, restore saved stack-pointer value.  */
- 
-       if (old_stack_level)
- 	{
- 	  emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX);
- 	  pending_stack_adjust = old_pending_adj;
- 	  stack_arg_under_construction = old_stack_arg_under_construction;
- 	  highest_outgoing_arg_in_use = initial_highest_arg_in_use;
- 	  stack_usage_map = initial_stack_usage_map;
- 	  sibcall_failure = 1;
- 	}
-       else if (ACCUMULATE_OUTGOING_ARGS && pass)
- 	{
- #ifdef REG_PARM_STACK_SPACE
- 	  if (save_area)
- 	    {
- 	      restore_fixed_argument_area (save_area, argblock,
- 					   high_to_save, low_to_save);
- 	    }
- #endif
- 
- 	  /* If we saved any argument areas, restore them.  */
- 	  for (i = 0; i < num_actuals; i++)
- 	    if (args[i].save_area)
- 	      {
- 		enum machine_mode save_mode = GET_MODE (args[i].save_area);
- 		rtx stack_area
- 		  = gen_rtx_MEM (save_mode,
- 				 memory_address (save_mode,
- 						 XEXP (args[i].stack_slot, 0)));
- 
- 		if (save_mode != BLKmode)
- 		  emit_move_insn (stack_area, args[i].save_area);
- 		else
- 		  emit_block_move (stack_area,
- 				   validize_mem (args[i].save_area),
- 				   GEN_INT (args[i].size.constant),
- 				   PARM_BOUNDARY);
- 	      }
- 
- 	  highest_outgoing_arg_in_use = initial_highest_arg_in_use;
- 	  stack_usage_map = initial_stack_usage_map;
- 	}
- 
-       /* If this was alloca, record the new stack level for nonlocal gotos.  
- 	 Check for the handler slots since we might not have a save area
- 	 for non-local gotos.  */
- 
-       if ((flags & ECF_MAY_BE_ALLOCA) && nonlocal_goto_handler_slots != 0)
- 	emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, NULL_RTX);
- 
-       pop_temp_slots ();
- 
-       /* Free up storage we no longer need.  */
-       for (i = 0; i < num_actuals; ++i)
- 	if (args[i].aligned_regs)
- 	  free (args[i].aligned_regs);
- 
-       if (pass == 0)
- 	{
- 	  /* Undo the fake expand_start_target_temps we did earlier.  If
- 	     there had been any cleanups created, we've already set
- 	     sibcall_failure.  */
- 	  expand_end_target_temps ();
- 	}
- 
-       insns = get_insns ();
-       end_sequence ();
- 
-       if (pass == 0)
- 	{
- 	  tail_call_insns = insns;
- 
- 	  /* If something prevents making this a sibling call,
- 	     zero out the sequence.  */
- 	  if (sibcall_failure)
- 	    tail_call_insns = NULL_RTX;
- 	  /* Restore the pending stack adjustment now that we have
- 	     finished generating the sibling call sequence.  */
- 
- 	  pending_stack_adjust = save_pending_stack_adjust;
- 	  stack_pointer_delta = save_stack_pointer_delta;
- 
- 	  /* Prepare arg structure for next iteration.  */
- 	  for (i = 0 ; i < num_actuals ; i++)
- 	    {
- 	      args[i].value = 0;
- 	      args[i].aligned_regs = 0;
- 	      args[i].stack = 0;
- 	    }
- 	}
-       else
- 	normal_call_insns = insns;
-     }
- 
    /* The function optimize_sibling_and_tail_recursive_calls doesn't
       handle CALL_PLACEHOLDERs inside other CALL_PLACEHOLDERs.  This
       can happen if the arguments to this function call an inline
--- 3337,3362 ----
  
    function_call_count++;
  
!   if (try_tail_call)
      {
!        /* Emit any queued insns now; otherwise they would end up in
! 	  only one of the alternates.  */
!        emit_queue ();
!        tail_call_insns = gen_tail_call
! 			   (fndecl, funtype, funexp, flags, &args_size,
! 			    args, num_actuals, reg_parm_stack_space,
! 			    valreg, have_reg_parm, args_so_far,
! 			    struct_value_size);
!     }
! 
!   normal_call_insns = gen_normal_call
! 		      (exp, fndecl, funtype, funexp, flags, &args_size,
! 		       args, num_actuals, reg_parm_stack_space,
! 		       &valreg, &target, have_reg_parm, args_so_far,
! 		       struct_value_size, preferred_stack_boundary,
! 		       must_preallocate, &old_stack_level, structure_value_addr,
! 		       structure_value_addr_parm, ignore, pcc_struct_value);
  
    /* The function optimize_sibling_and_tail_recursive_calls doesn't
       handle CALL_PLACEHOLDERs inside other CALL_PLACEHOLDERs.  This
       can happen if the arguments to this function call an inline
*************** target_for_arg (type, size, args_addr, o
*** 4065,4070 ****
--- 4202,4246 ----
  }
  #endif
  
+ /* Store the arguments.  */
+ 
+ static void
+ store_args (args, num_actuals, argblock, flags, reg_parm_stack_space,
+ 	    variable_size)
+      struct arg_data *args;
+      int num_actuals;
+      rtx argblock;
+      int flags;
+      int reg_parm_stack_space;
+      int variable_size;
+ {
+   int i;
+   /* Now store (and compute if necessary) all non-register parms.
+      These come before register parms, since they can require block-moves,
+      which could clobber the registers used for register parms.
+      Parms which have partial registers are not stored here,
+      but we do preallocate space here if they want that.  */
+ 
+   for (i = 0; i < num_actuals; i++)
+     if (args[i].reg == 0 || args[i].pass_on_stack)
+       store_one_arg (&args[i], argblock, flags,
+ 		     variable_size, reg_parm_stack_space);
+ 
+   /* If we have a parm that is passed in registers but not in memory
+      and whose alignment does not permit a direct copy into registers,
+      make a group of pseudos that correspond to each register that we
+      will later fill.  */
+   if (STRICT_ALIGNMENT)
+     store_unaligned_arguments_into_pseudos (args, num_actuals);
+ 
+   /* Now store any partially-in-registers parm.
+      This is the last place a block-move can happen.  */
+   for (i = 0; i < num_actuals; i++)
+     if (args[i].partial != 0 && ! args[i].pass_on_stack)
+       store_one_arg (&args[i], argblock, flags,
+ 		     variable_size, reg_parm_stack_space);
+ }
+ 
  /* Store a single argument for a function call
     into the register or memory area where it must be passed.
     *ARG describes the argument value and where to pass it.
*************** store_one_arg (arg, argblock, flags, var
*** 4087,4093 ****
       struct arg_data *arg;
       rtx argblock;
       int flags;
!      int variable_size ATTRIBUTE_UNUSED;
       int reg_parm_stack_space;
  {
    register tree pval = arg->tree_value;
--- 4263,4269 ----
       struct arg_data *arg;
       rtx argblock;
       int flags;
!      int variable_size;
       int reg_parm_stack_space;
  {
    register tree pval = arg->tree_value;

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]