rtx argblock = 0;
CUMULATIVE_ARGS args_so_far;
struct arg { rtx value; enum machine_mode mode; rtx reg; int partial;
- struct args_size offset; struct args_size size; };
+ struct args_size offset; struct args_size size; rtx save_area; };
struct arg *argvec;
int old_inhibit_defer_pop = inhibit_defer_pop;
rtx call_fusage = 0;
+ /* Size of the stack reserved for parameter registers. */
+ int reg_parm_stack_space = 0;
+#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
+ /* Define the boundary of the register parm stack space that needs to be
+ save, if any. */
+ int low_to_save = -1, high_to_save;
+ rtx save_area = 0; /* Place that it is saved */
+#endif
+
+#ifdef ACCUMULATE_OUTGOING_ARGS
+ int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
+ char *initial_stack_usage_map = stack_usage_map;
+ int needed;
+#endif
+
+#ifdef REG_PARM_STACK_SPACE
+#ifdef MAYBE_REG_PARM_STACK_SPACE
+ reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE;
+#else
+ reg_parm_stack_space = REG_PARM_STACK_SPACE (fndecl);
+#endif
+#endif
VA_START (p, nargs);
library functions shouldn't have many args. */
argvec = (struct arg *) alloca (nargs * sizeof (struct arg));
+ bzero ((char *) argvec, nargs * sizeof (struct arg));
+
INIT_CUMULATIVE_ARGS (args_so_far, NULL_TREE, fun, 0);
)
args_size.constant += argvec[count].size.constant;
-#ifdef ACCUMULATE_OUTGOING_ARGS
- /* If this arg is actually passed on the stack, it might be
- clobbering something we already put there (this library call might
- be inside the evaluation of an argument to a function whose call
- requires the stack). This will only occur when the library call
- has sufficient args to run out of argument registers. Abort in
- this case; if this ever occurs, code must be added to save and
- restore the arg slot. */
-
- if (argvec[count].reg == 0 || argvec[count].partial != 0)
- abort ();
-#endif
-
FUNCTION_ARG_ADVANCE (args_so_far, mode, (tree) 0, 1);
}
va_end (p);
+#ifdef FINAL_REG_PARM_STACK_SPACE
+ reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant,
+ args_size.var);
+#endif
+
/* If this machine requires an external definition for library
functions, write one out. */
assemble_external_libcall (fun);
#ifdef REG_PARM_STACK_SPACE
args_size.constant = MAX (args_size.constant,
- REG_PARM_STACK_SPACE (NULL_TREE));
+ reg_parm_stack_space);
#ifndef OUTGOING_REG_PARM_STACK_SPACE
- args_size.constant -= REG_PARM_STACK_SPACE (NULL_TREE);
+ args_size.constant -= reg_parm_stack_space;
#endif
#endif
current_function_outgoing_args_size = args_size.constant;
#ifdef ACCUMULATE_OUTGOING_ARGS
- args_size.constant = 0;
+ /* 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.
+
+ Another approach might be to try to reorder the argument
+ evaluations to avoid this conflicting stack usage. */
+
+ needed = args_size.constant;
+#if defined(REG_PARM_STACK_SPACE) && ! defined(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 /* not ACCUMULATE_OUTGOING_ARGS */
#ifndef PUSH_ROUNDING
argblock = push_block (GEN_INT (args_size.constant), 0, 0);
#endif
+#endif
#ifdef PUSH_ARGS_REVERSED
#ifdef STACK_BOUNDARY
argnum = 0;
#endif
+#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
+ /* The argument list is the property of the called routine and it
+ may clobber it. If the fixed area has been used for previous
+ parameters, we must save and restore it.
+
+ Here we compute the boundary of the that needs to be saved, if any. */
+
+#ifdef ARGS_GROW_DOWNWARD
+ for (count = 0; count < reg_parm_stack_space + 1; count++)
+#else
+ for (count = 0; count < reg_parm_stack_space; count++)
+#endif
+ {
+ if (count >= highest_outgoing_arg_in_use
+ || stack_usage_map[count] == 0)
+ continue;
+
+ if (low_to_save == -1)
+ low_to_save = count;
+
+ high_to_save = count;
+ }
+
+ if (low_to_save >= 0)
+ {
+ int num_to_save = high_to_save - low_to_save + 1;
+ enum machine_mode save_mode
+ = mode_for_size (num_to_save * BITS_PER_UNIT, MODE_INT, 1);
+ rtx stack_area;
+
+ /* If we don't have the required alignment, must do this in BLKmode. */
+ if ((low_to_save & (MIN (GET_MODE_SIZE (save_mode),
+ BIGGEST_ALIGNMENT / UNITS_PER_WORD) - 1)))
+ save_mode = BLKmode;
+
+ stack_area = gen_rtx (MEM, save_mode,
+ memory_address (save_mode,
+
+#ifdef ARGS_GROW_DOWNWARD
+ plus_constant (argblock,
+ - high_to_save)
+#else
+ plus_constant (argblock,
+ low_to_save)
+#endif
+ ));
+ if (save_mode == BLKmode)
+ {
+ save_area = assign_stack_temp (BLKmode, num_to_save, 0);
+ MEM_IN_STRUCT_P (save_area) = 0;
+ emit_block_move (validize_mem (save_area), stack_area,
+ GEN_INT (num_to_save),
+ PARM_BOUNDARY / BITS_PER_UNIT);
+ }
+ else
+ {
+ save_area = gen_reg_rtx (save_mode);
+ emit_move_insn (save_area, stack_area);
+ }
+ }
+#endif
+
/* Push the args that need to be pushed. */
for (count = 0; count < nargs; count++, argnum += inc)
register rtx val = argvec[argnum].value;
rtx reg = argvec[argnum].reg;
int partial = argvec[argnum].partial;
+ int lower_bound, upper_bound, i;
if (! (reg != 0 && partial == 0))
- emit_push_insn (val, mode, NULL_TREE, NULL_RTX, 0, partial, reg, 0,
- argblock, GEN_INT (argvec[count].offset.constant));
- NO_DEFER_POP;
+ {
+#ifdef ACCUMULATE_OUTGOING_ARGS
+ /* If this is being stored into a pre-allocated, fixed-size, stack
+ area, save any previous data at that location. */
+
+#ifdef ARGS_GROW_DOWNWARD
+ /* stack_slot is negative, but we want to index stack_usage_map
+ with positive values. */
+ upper_bound = -argvec[count].offset.constant + 1;
+ lower_bound = upper_bound - argvec[count].size.constant;
+#else
+ lower_bound = argvec[count].offset.constant;
+ upper_bound = lower_bound + argvec[count].size.constant;
+#endif
+
+ for (i = lower_bound; i < upper_bound; i++)
+ if (stack_usage_map[i]
+#ifdef REG_PARM_STACK_SPACE
+ /* Don't store things in the fixed argument area at this point;
+ it has already been saved. */
+ && i > reg_parm_stack_space
+#endif
+ )
+ break;
+
+ if (i != upper_bound)
+ {
+ /* We need to make a save area. See what mode we can make it. */
+ enum machine_mode save_mode
+ = mode_for_size (argvec[count].size.constant * BITS_PER_UNIT,
+ MODE_INT, 1);
+ rtx stack_area
+ = gen_rtx (MEM, save_mode,
+ memory_address (save_mode, plus_constant (argblock,
+ argvec[count].offset.constant)));
+ argvec[count].save_area = gen_reg_rtx (save_mode);
+ emit_move_insn (argvec[count].save_area, stack_area);
+ }
+#endif
+ emit_push_insn (val, mode, NULL_TREE, NULL_RTX, 0, partial, reg, 0,
+ argblock, GEN_INT (argvec[count].offset.constant));
+
+#ifdef ACCUMULATE_OUTGOING_ARGS
+ /* Now mark the segment we just used. */
+ for (i = lower_bound; i < upper_bound; i++)
+ stack_usage_map[i] = 1;
+#endif
+
+ NO_DEFER_POP;
+ }
}
#ifndef PUSH_ARGS_REVERSED
/* Now restore inhibit_defer_pop to its actual original value. */
OK_DEFER_POP;
+
+#ifdef ACCUMULATE_OUTGOING_ARGS
+#ifdef REG_PARM_STACK_SPACE
+ if (save_area)
+ {
+ enum machine_mode save_mode = GET_MODE (save_area);
+ rtx stack_area
+ = gen_rtx (MEM, save_mode,
+ memory_address (save_mode,
+#ifdef ARGS_GROW_DOWNWARD
+ plus_constant (argblock, - high_to_save)
+#else
+ plus_constant (argblock, low_to_save)
+#endif
+ ));
+
+ if (save_mode != BLKmode)
+ emit_move_insn (stack_area, save_area);
+ else
+ emit_block_move (stack_area, validize_mem (save_area),
+ GEN_INT (high_to_save - low_to_save + 1),
+ PARM_BOUNDARY / BITS_PER_UNIT);
+ }
+#endif
+
+ /* If we saved any argument areas, restore them. */
+ for (count = 0; count < nargs; count++)
+ if (argvec[count].save_area)
+ {
+ enum machine_mode save_mode = GET_MODE (argvec[count].save_area);
+ rtx stack_area
+ = gen_rtx (MEM, save_mode,
+ memory_address (save_mode, plus_constant (argblock,
+ argvec[count].offset.constant)));
+
+ emit_move_insn (stack_area, argvec[count].save_area);
+ }
+
+ highest_outgoing_arg_in_use = initial_highest_arg_in_use;
+ stack_usage_map = initial_stack_usage_map;
+#endif
+
}
\f
/* Like emit_library_call except that an extra argument, VALUE,
rtx argblock = 0;
CUMULATIVE_ARGS args_so_far;
struct arg { rtx value; enum machine_mode mode; rtx reg; int partial;
- struct args_size offset; struct args_size size; };
+ struct args_size offset; struct args_size size; rtx save_area; };
struct arg *argvec;
int old_inhibit_defer_pop = inhibit_defer_pop;
rtx call_fusage = 0;
+ /* Size of the stack reserved for parameter registers. */
+ int reg_parm_stack_space = 0;
rtx mem_value = 0;
int pcc_struct_value = 0;
int struct_value_size = 0;
int is_const;
+ int needed;
+
+#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
+ /* Define the boundary of the register parm stack space that needs to be
+ save, if any. */
+ int low_to_save = -1, high_to_save;
+ rtx save_area = 0; /* Place that it is saved */
+#endif
+
+#ifdef ACCUMULATE_OUTGOING_ARGS
+ int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
+ char *initial_stack_usage_map = stack_usage_map;
+#endif
+
+#ifdef REG_PARM_STACK_SPACE
+#ifdef MAYBE_REG_PARM_STACK_SPACE
+ reg_parm_stack_space = MAYBE_REG_PARM_STACK_SPACE;
+#else
+ reg_parm_stack_space = REG_PARM_STACK_SPACE (fndecl);
+#endif
+#endif
VA_START (p, nargs);
library functions shouldn't have many args. */
argvec = (struct arg *) alloca ((nargs + 1) * sizeof (struct arg));
+ bzero ((char *) argvec, nargs * sizeof (struct arg));
INIT_CUMULATIVE_ARGS (args_so_far, NULL_TREE, fun, 0);
)
args_size.constant += argvec[count].size.constant;
-#ifdef ACCUMULATE_OUTGOING_ARGS
- /* If this arg is actually passed on the stack, it might be
- clobbering something we already put there (this library call might
- be inside the evaluation of an argument to a function whose call
- requires the stack). This will only occur when the library call
- has sufficient args to run out of argument registers. Abort in
- this case; if this ever occurs, code must be added to save and
- restore the arg slot. */
-
- if (argvec[count].reg == 0 || argvec[count].partial != 0)
- abort ();
-#endif
-
FUNCTION_ARG_ADVANCE (args_so_far, mode, (tree) 0, 1);
}
va_end (p);
+#ifdef FINAL_REG_PARM_STACK_SPACE
+ reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant,
+ args_size.var);
+#endif
/* If this machine requires an external definition for library
functions, write one out. */
assemble_external_libcall (fun);
#ifdef REG_PARM_STACK_SPACE
args_size.constant = MAX (args_size.constant,
- REG_PARM_STACK_SPACE (NULL_TREE));
+ reg_parm_stack_space);
#ifndef OUTGOING_REG_PARM_STACK_SPACE
- args_size.constant -= REG_PARM_STACK_SPACE (NULL_TREE);
+ args_size.constant -= reg_parm_stack_space);
#endif
#endif
current_function_outgoing_args_size = args_size.constant;
#ifdef ACCUMULATE_OUTGOING_ARGS
- args_size.constant = 0;
+ /* 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.
+
+ Another approach might be to try to reorder the argument
+ evaluations to avoid this conflicting stack usage. */
+
+ needed = args_size.constant;
+#if defined(REG_PARM_STACK_SPACE) && ! defined(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 /* not ACCUMULATE_OUTGOING_ARGS */
#ifndef PUSH_ROUNDING
argblock = push_block (GEN_INT (args_size.constant), 0, 0);
#endif
+#endif
#ifdef PUSH_ARGS_REVERSED
#ifdef STACK_BOUNDARY
argnum = 0;
#endif
+#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE)
+ /* The argument list is the property of the called routine and it
+ may clobber it. If the fixed area has been used for previous
+ parameters, we must save and restore it.
+
+ Here we compute the boundary of the that needs to be saved, if any. */
+
+#ifdef ARGS_GROW_DOWNWARD
+ for (count = 0; count < reg_parm_stack_space + 1; count++)
+#else
+ for (count = 0; count < reg_parm_stack_space; count++)
+#endif
+ {
+ if (count >= highest_outgoing_arg_in_use
+ || stack_usage_map[count] == 0)
+ continue;
+
+ if (low_to_save == -1)
+ low_to_save = count;
+
+ high_to_save = count;
+ }
+
+ if (low_to_save >= 0)
+ {
+ int num_to_save = high_to_save - low_to_save + 1;
+ enum machine_mode save_mode
+ = mode_for_size (num_to_save * BITS_PER_UNIT, MODE_INT, 1);
+ rtx stack_area;
+
+ /* If we don't have the required alignment, must do this in BLKmode. */
+ if ((low_to_save & (MIN (GET_MODE_SIZE (save_mode),
+ BIGGEST_ALIGNMENT / UNITS_PER_WORD) - 1)))
+ save_mode = BLKmode;
+
+ stack_area = gen_rtx (MEM, save_mode,
+ memory_address (save_mode,
+
+#ifdef ARGS_GROW_DOWNWARD
+ plus_constant (argblock,
+ - high_to_save)
+#else
+ plus_constant (argblock,
+ low_to_save)
+#endif
+ ));
+ if (save_mode == BLKmode)
+ {
+ save_area = assign_stack_temp (BLKmode, num_to_save, 0);
+ MEM_IN_STRUCT_P (save_area) = 0;
+ emit_block_move (validize_mem (save_area), stack_area,
+ GEN_INT (num_to_save),
+ PARM_BOUNDARY / BITS_PER_UNIT);
+ }
+ else
+ {
+ save_area = gen_reg_rtx (save_mode);
+ emit_move_insn (save_area, stack_area);
+ }
+ }
+#endif
+
/* Push the args that need to be pushed. */
for (count = 0; count < nargs; count++, argnum += inc)
register rtx val = argvec[argnum].value;
rtx reg = argvec[argnum].reg;
int partial = argvec[argnum].partial;
+ int lower_bound, upper_bound, i;
if (! (reg != 0 && partial == 0))
- emit_push_insn (val, mode, NULL_TREE, NULL_RTX, 0, partial, reg, 0,
- argblock, GEN_INT (argvec[count].offset.constant));
- NO_DEFER_POP;
+ {
+#ifdef ACCUMULATE_OUTGOING_ARGS
+ /* If this is being stored into a pre-allocated, fixed-size, stack
+ area, save any previous data at that location. */
+
+#ifdef ARGS_GROW_DOWNWARD
+ /* stack_slot is negative, but we want to index stack_usage_map
+ with positive values. */
+ upper_bound = -argvec[count].offset.constant + 1;
+ lower_bound = upper_bound - argvec[count].size.constant;
+#else
+ lower_bound = argvec[count].offset.constant;
+ upper_bound = lower_bound + argvec[count].size.constant;
+#endif
+
+ for (i = lower_bound; i < upper_bound; i++)
+ if (stack_usage_map[i]
+#ifdef REG_PARM_STACK_SPACE
+ /* Don't store things in the fixed argument area at this point;
+ it has already been saved. */
+ && i > reg_parm_stack_space
+#endif
+ )
+ break;
+
+ if (i != upper_bound)
+ {
+ /* We need to make a save area. See what mode we can make it. */
+ enum machine_mode save_mode
+ = mode_for_size (argvec[count].size.constant * BITS_PER_UNIT,
+ MODE_INT, 1);
+ rtx stack_area
+ = gen_rtx (MEM, save_mode,
+ memory_address (save_mode, plus_constant (argblock,
+ argvec[count].offset.constant)));
+ argvec[count].save_area = gen_reg_rtx (save_mode);
+ emit_move_insn (argvec[count].save_area, stack_area);
+ }
+#endif
+ emit_push_insn (val, mode, NULL_TREE, NULL_RTX, 0, partial, reg, 0,
+ argblock, GEN_INT (argvec[count].offset.constant));
+
+#ifdef ACCUMULATE_OUTGOING_ARGS
+ /* Now mark the segment we just used. */
+ for (i = lower_bound; i < upper_bound; i++)
+ stack_usage_map[i] = 1;
+#endif
+
+ NO_DEFER_POP;
+ }
}
#ifndef PUSH_ARGS_REVERSED
value = hard_libcall_value (outmode);
}
+#ifdef ACCUMULATE_OUTGOING_ARGS
+#ifdef REG_PARM_STACK_SPACE
+ if (save_area)
+ {
+ enum machine_mode save_mode = GET_MODE (save_area);
+ rtx stack_area
+ = gen_rtx (MEM, save_mode,
+ memory_address (save_mode,
+#ifdef ARGS_GROW_DOWNWARD
+ plus_constant (argblock, - high_to_save)
+#else
+ plus_constant (argblock, low_to_save)
+#endif
+ ));
+
+ if (save_mode != BLKmode)
+ emit_move_insn (stack_area, save_area);
+ else
+ emit_block_move (stack_area, validize_mem (save_area),
+ GEN_INT (high_to_save - low_to_save + 1),
+ PARM_BOUNDARY / BITS_PER_UNIT);
+ }
+#endif
+
+ /* If we saved any argument areas, restore them. */
+ for (count = 0; count < nargs; count++)
+ if (argvec[count].save_area)
+ {
+ enum machine_mode save_mode = GET_MODE (argvec[count].save_area);
+ rtx stack_area
+ = gen_rtx (MEM, save_mode,
+ memory_address (save_mode, plus_constant (argblock,
+ argvec[count].offset.constant)));
+
+ emit_move_insn (stack_area, argvec[count].save_area);
+ }
+
+ highest_outgoing_arg_in_use = initial_highest_arg_in_use;
+ stack_usage_map = initial_stack_usage_map;
+#endif
+
return value;
}
\f