/* Convert function calls to rtl insns, for GNU C compiler.
- Copyright (C) 1989-2014 Free Software Foundation, Inc.
+ Copyright (C) 1989-2016 Free Software Foundation, Inc.
This file is part of GCC.
#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "tm.h"
+#include "backend.h"
+#include "target.h"
#include "rtl.h"
#include "tree.h"
+#include "gimple.h"
+#include "predict.h"
+#include "tm_p.h"
+#include "stringpool.h"
+#include "expmed.h"
+#include "optabs.h"
+#include "emit-rtl.h"
+#include "cgraph.h"
+#include "diagnostic-core.h"
+#include "fold-const.h"
#include "stor-layout.h"
#include "varasm.h"
-#include "stringpool.h"
-#include "attribs.h"
-#include "basic-block.h"
-#include "tree-ssa-alias.h"
#include "internal-fn.h"
-#include "gimple-expr.h"
-#include "is-a.h"
-#include "gimple.h"
-#include "flags.h"
+#include "dojump.h"
+#include "explow.h"
+#include "calls.h"
#include "expr.h"
-#include "optabs.h"
-#include "libfuncs.h"
-#include "function.h"
-#include "regs.h"
-#include "diagnostic-core.h"
#include "output.h"
-#include "tm_p.h"
-#include "timevar.h"
-#include "sbitmap.h"
#include "langhooks.h"
-#include "target.h"
-#include "cgraph.h"
#include "except.h"
#include "dbgcnt.h"
#include "rtl-iter.h"
+#include "tree-chkp.h"
+#include "rtl-chkp.h"
+
/* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits. */
#define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
/* Tree node for this argument. */
tree tree_value;
/* Mode for value; TYPE_MODE unless promoted. */
- enum machine_mode mode;
+ machine_mode mode;
/* Current RTL value for argument, or 0 if it isn't precomputed. */
rtx value;
/* Initially-compute RTL value for argument; only for const functions. */
/* If REG is a PARALLEL, this is a copy of VALUE pulled into the correct
form for emit_group_move. */
rtx parallel_value;
+ /* If value is passed in neither reg nor stack, this field holds a number
+ of a special slot to be used. */
+ rtx special_slot;
+ /* For pointer bounds hold an index of parm bounds are bound to. -1 if
+ there is no such pointer. */
+ int pointer_arg;
+ /* If pointer_arg refers a structure, then pointer_offset holds an offset
+ of a pointer in this structure. */
+ int pointer_offset;
/* If REG was promoted from the actual mode of the argument expression,
indicates whether the promotion is sign- or zero-extended. */
int unsignedp;
HOST_WIDE_INT, rtx, rtx, int, rtx, int,
cumulative_args_t);
static void precompute_register_parameters (int, struct arg_data *, int *);
+static void store_bounds (struct arg_data *, struct arg_data *);
static int store_one_arg (struct arg_data *, rtx, int, int, int);
static void store_unaligned_arguments_into_pseudos (struct arg_data *, int);
static int finalize_must_preallocate (int, int, struct arg_data *,
static void load_register_parameters (struct arg_data *, int, rtx *, int,
int, int *);
static rtx emit_library_call_value_1 (int, rtx, rtx, enum libcall_type,
- enum machine_mode, int, va_list);
+ machine_mode, int, va_list);
static int special_function_p (const_tree, int);
static int check_sibcall_argument_overlap_1 (rtx);
static int check_sibcall_argument_overlap (rtx_insn *, struct arg_data *, int);
CALL_INSN_FUNCTION_USAGE information. */
rtx
-prepare_call_address (tree fndecl, rtx funexp, rtx static_chain_value,
+prepare_call_address (tree fndecl_or_type, rtx funexp, rtx static_chain_value,
rtx *call_fusage, int reg_parm_seen, int sibcallp)
{
/* Make a valid memory address and copy constants through pseudo-regs,
: memory_address (FUNCTION_MODE, funexp));
else if (! sibcallp)
{
-#ifndef NO_FUNCTION_CSE
- if (optimize && ! flag_no_function_cse)
+ if (!NO_FUNCTION_CSE && optimize && ! flag_no_function_cse)
funexp = force_reg (Pmode, funexp);
-#endif
}
- if (static_chain_value != 0)
+ if (static_chain_value != 0
+ && (TREE_CODE (fndecl_or_type) != FUNCTION_DECL
+ || DECL_STATIC_CHAIN (fndecl_or_type)))
{
rtx chain;
- gcc_assert (fndecl);
- chain = targetm.calls.static_chain (fndecl, false);
+ chain = targetm.calls.static_chain (fndecl_or_type, false);
static_chain_value = convert_memory_address (Pmode, static_chain_value);
emit_move_insn (chain, static_chain_value);
cumulative_args_t args_so_far ATTRIBUTE_UNUSED)
{
rtx rounded_stack_size_rtx = GEN_INT (rounded_stack_size);
- rtx_insn *call_insn;
- rtx call, funmem;
+ rtx call, funmem, pat;
int already_popped = 0;
HOST_WIDE_INT n_popped
= targetm.calls.return_pops_args (fndecl, funtype, stack_size);
else if (fntree)
set_mem_expr (funmem, build_simple_mem_ref (CALL_EXPR_FN (fntree)));
-#if defined (HAVE_sibcall_pop) && defined (HAVE_sibcall_value_pop)
- if ((ecf_flags & ECF_SIBCALL)
- && HAVE_sibcall_pop && HAVE_sibcall_value_pop
- && (n_popped > 0 || stack_size == 0))
+ if (ecf_flags & ECF_SIBCALL)
{
- rtx n_pop = GEN_INT (n_popped);
- rtx pat;
-
- /* If this subroutine pops its own args, record that in the call insn
- if possible, for the sake of frame pointer elimination. */
-
if (valreg)
- pat = GEN_SIBCALL_VALUE_POP (valreg, funmem, rounded_stack_size_rtx,
- next_arg_reg, n_pop);
+ pat = targetm.gen_sibcall_value (valreg, funmem,
+ rounded_stack_size_rtx,
+ next_arg_reg, NULL_RTX);
else
- pat = GEN_SIBCALL_POP (funmem, rounded_stack_size_rtx, next_arg_reg,
- n_pop);
-
- emit_call_insn (pat);
- already_popped = 1;
+ pat = targetm.gen_sibcall (funmem, rounded_stack_size_rtx,
+ next_arg_reg, GEN_INT (struct_value_size));
}
- else
-#endif
-
-#if defined (HAVE_call_pop) && defined (HAVE_call_value_pop)
/* If the target has "call" or "call_value" insns, then prefer them
if no arguments are actually popped. If the target does not have
"call" or "call_value" insns, then we must use the popping versions
even if the call has no arguments to pop. */
-#if defined (HAVE_call) && defined (HAVE_call_value)
- if (HAVE_call && HAVE_call_value && HAVE_call_pop && HAVE_call_value_pop
- && n_popped > 0)
-#else
- if (HAVE_call_pop && HAVE_call_value_pop)
-#endif
+ else if (n_popped > 0
+ || !(valreg
+ ? targetm.have_call_value ()
+ : targetm.have_call ()))
{
rtx n_pop = GEN_INT (n_popped);
- rtx pat;
/* If this subroutine pops its own args, record that in the call insn
if possible, for the sake of frame pointer elimination. */
if (valreg)
- pat = GEN_CALL_VALUE_POP (valreg, funmem, rounded_stack_size_rtx,
- next_arg_reg, n_pop);
+ pat = targetm.gen_call_value_pop (valreg, funmem,
+ rounded_stack_size_rtx,
+ next_arg_reg, n_pop);
else
- pat = GEN_CALL_POP (funmem, rounded_stack_size_rtx, next_arg_reg,
- n_pop);
+ pat = targetm.gen_call_pop (funmem, rounded_stack_size_rtx,
+ next_arg_reg, n_pop);
- emit_call_insn (pat);
already_popped = 1;
}
else
-#endif
-
-#if defined (HAVE_sibcall) && defined (HAVE_sibcall_value)
- if ((ecf_flags & ECF_SIBCALL)
- && HAVE_sibcall && HAVE_sibcall_value)
- {
- if (valreg)
- emit_call_insn (GEN_SIBCALL_VALUE (valreg, funmem,
- rounded_stack_size_rtx,
- next_arg_reg, NULL_RTX));
- else
- emit_call_insn (GEN_SIBCALL (funmem, rounded_stack_size_rtx,
- next_arg_reg,
- GEN_INT (struct_value_size)));
- }
- else
-#endif
-
-#if defined (HAVE_call) && defined (HAVE_call_value)
- if (HAVE_call && HAVE_call_value)
{
if (valreg)
- emit_call_insn (GEN_CALL_VALUE (valreg, funmem, rounded_stack_size_rtx,
- next_arg_reg, NULL_RTX));
+ pat = targetm.gen_call_value (valreg, funmem, rounded_stack_size_rtx,
+ next_arg_reg, NULL_RTX);
else
- emit_call_insn (GEN_CALL (funmem, rounded_stack_size_rtx, next_arg_reg,
- GEN_INT (struct_value_size)));
+ pat = targetm.gen_call (funmem, rounded_stack_size_rtx, next_arg_reg,
+ GEN_INT (struct_value_size));
}
- else
-#endif
- gcc_unreachable ();
+ emit_insn (pat);
/* Find the call we just emitted. */
- call_insn = last_call_insn ();
+ rtx_call_insn *call_insn = last_call_insn ();
/* Some target create a fresh MEM instead of reusing the one provided
above. Set its MEM_EXPR. */
&& MEM_EXPR (funmem) != NULL_TREE)
set_mem_expr (XEXP (call, 0), MEM_EXPR (funmem));
+ /* Mark instrumented calls. */
+ if (call && fntree)
+ CALL_EXPR_WITH_BOUNDS_P (call) = CALL_WITH_BOUNDS_P (fntree);
+
/* Put the register usage information there. */
add_function_usage_to (call_insn, call_fusage);
static int
special_function_p (const_tree fndecl, int flags)
{
- if (fndecl && DECL_NAME (fndecl)
- && IDENTIFIER_LENGTH (DECL_NAME (fndecl)) <= 17
+ tree name_decl = DECL_NAME (fndecl);
+
+ /* For instrumentation clones we want to derive flags
+ from the original name. */
+ if (cgraph_node::get (fndecl)
+ && cgraph_node::get (fndecl)->instrumentation_clone)
+ name_decl = DECL_NAME (cgraph_node::get (fndecl)->orig_decl);
+
+ if (fndecl && name_decl
+ && IDENTIFIER_LENGTH (name_decl) <= 17
/* Exclude functions not at the file scope, or not `extern',
since they are not the magic functions we would otherwise
think they are.
|| TREE_CODE (DECL_CONTEXT (fndecl)) == TRANSLATION_UNIT_DECL)
&& TREE_PUBLIC (fndecl))
{
- const char *name = IDENTIFIER_POINTER (DECL_NAME (fndecl));
+ const char *name = IDENTIFIER_POINTER (name_decl);
const char *tname = name;
/* We assume that alloca will always be called by name. It
makes no sense to pass it as a pointer-to-function to
anything that does not understand its behavior. */
- if (((IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 6
- && name[0] == 'a'
- && ! strcmp (name, "alloca"))
- || (IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 16
- && name[0] == '_'
- && ! strcmp (name, "__builtin_alloca"))))
+ if (IDENTIFIER_LENGTH (name_decl) == 6
+ && name[0] == 'a'
+ && ! strcmp (name, "alloca"))
flags |= ECF_MAY_BE_ALLOCA;
/* Disregard prefix _, __, __x or __builtin_. */
flags |= ECF_NORETURN;
}
+ if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
+ switch (DECL_FUNCTION_CODE (fndecl))
+ {
+ case BUILT_IN_ALLOCA:
+ case BUILT_IN_ALLOCA_WITH_ALIGN:
+ flags |= ECF_MAY_BE_ALLOCA;
+ break;
+ default:
+ break;
+ }
+
return flags;
}
/* Return true if STMT is an alloca call. */
bool
-gimple_alloca_call_p (const_gimple stmt)
+gimple_alloca_call_p (const gimple *stmt)
{
tree fndecl;
if (decl)
flags = flags_from_decl_or_type (decl);
+ else if (CALL_EXPR_FN (t) == NULL_TREE)
+ flags = internal_fn_flags (CALL_EXPR_IFN (t));
else
{
t = TREE_TYPE (CALL_EXPR_FN (t));
return flags;
}
+/* Return true if TYPE should be passed by invisible reference. */
+
+bool
+pass_by_reference (CUMULATIVE_ARGS *ca, machine_mode mode,
+ tree type, bool named_arg)
+{
+ if (type)
+ {
+ /* If this type contains non-trivial constructors, then it is
+ forbidden for the middle-end to create any new copies. */
+ if (TREE_ADDRESSABLE (type))
+ return true;
+
+ /* GCC post 3.4 passes *all* variable sized types by reference. */
+ if (!TYPE_SIZE (type) || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
+ return true;
+
+ /* If a record type should be passed the same as its first (and only)
+ member, use the type and mode of that member. */
+ if (TREE_CODE (type) == RECORD_TYPE && TYPE_TRANSPARENT_AGGR (type))
+ {
+ type = TREE_TYPE (first_field (type));
+ mode = TYPE_MODE (type);
+ }
+ }
+
+ return targetm.calls.pass_by_reference (pack_cumulative_args (ca), mode,
+ type, named_arg);
+}
+
+/* Return true if TYPE, which is passed by reference, should be callee
+ copied instead of caller copied. */
+
+bool
+reference_callee_copied (CUMULATIVE_ARGS *ca, machine_mode mode,
+ tree type, bool named_arg)
+{
+ if (type && TREE_ADDRESSABLE (type))
+ return false;
+ return targetm.calls.callee_copies (pack_cumulative_args (ca), mode, type,
+ named_arg);
+}
+
+
/* Precompute all register parameters as described by ARGS, storing values
into fields within the ARGS array.
|| (GET_CODE (args[i].value) == SUBREG
&& REG_P (SUBREG_REG (args[i].value)))))
&& args[i].mode != BLKmode
- && set_src_cost (args[i].value, optimize_insn_for_speed_p ())
- > COSTS_N_INSNS (1)
+ && (set_src_cost (args[i].value, args[i].mode,
+ optimize_insn_for_speed_p ())
+ > COSTS_N_INSNS (1))
&& ((*reg_parm_seen
&& targetm.small_register_classes_for_mode_p (args[i].mode))
|| optimize))
/* Compute the boundary of the area that needs to be saved, if any. */
high = reg_parm_stack_space;
-#ifdef ARGS_GROW_DOWNWARD
- high += 1;
-#endif
+ if (ARGS_GROW_DOWNWARD)
+ high += 1;
+
if (high > highest_outgoing_arg_in_use)
high = highest_outgoing_arg_in_use;
if (stack_usage_map[low] != 0)
{
int num_to_save;
- enum machine_mode save_mode;
+ machine_mode save_mode;
int delta;
rtx addr;
rtx stack_area;
BIGGEST_ALIGNMENT / UNITS_PER_WORD) - 1)))
save_mode = BLKmode;
-#ifdef ARGS_GROW_DOWNWARD
- delta = -high;
-#else
- delta = low;
-#endif
+ if (ARGS_GROW_DOWNWARD)
+ delta = -high;
+ else
+ delta = low;
+
addr = plus_constant (Pmode, argblock, delta);
stack_area = gen_rtx_MEM (save_mode, memory_address (save_mode, addr));
static void
restore_fixed_argument_area (rtx save_area, rtx argblock, int high_to_save, int low_to_save)
{
- enum machine_mode save_mode = GET_MODE (save_area);
+ machine_mode save_mode = GET_MODE (save_area);
int delta;
rtx addr, stack_area;
-#ifdef ARGS_GROW_DOWNWARD
- delta = -high_to_save;
-#else
- delta = low_to_save;
-#endif
+ if (ARGS_GROW_DOWNWARD)
+ delta = -high_to_save;
+ else
+ delta = low_to_save;
+
addr = plus_constant (Pmode, argblock, delta);
stack_area = gen_rtx_MEM (save_mode, memory_address (save_mode, addr));
set_mem_align (stack_area, PARM_BOUNDARY);
args[i].aligned_regs[j] = reg;
word = extract_bit_field (word, bitsize, 0, 1, NULL_RTX,
- word_mode, word_mode);
+ word_mode, word_mode, false);
/* There is no need to restrict this code to loading items
in TYPE_ALIGN sized hunks. The bitfield instructions can
bytes -= bitsize / BITS_PER_UNIT;
store_bit_field (reg, bitsize, endian_correction, 0, 0,
- word_mode, word);
+ word_mode, word, false);
}
}
}
and may be modified by this routine.
OLD_PENDING_ADJ, MUST_PREALLOCATE and FLAGS are pointers to integer
- flags which may may be modified by this routine.
+ flags which may be modified by this routine.
MAY_TAILCALL is cleared if we encounter an invisible pass-by-reference
that requires allocation of stack space.
args_size->constant = 0;
args_size->var = 0;
+ bitmap_obstack_initialize (NULL);
+
/* In this loop, we consider args in the order they are written.
We fill up ARGS from the back. */
i = num_actuals - 1;
{
- int j = i;
+ int j = i, ptr_arg = -1;
call_expr_arg_iterator iter;
tree arg;
+ bitmap slots = NULL;
if (struct_value_addr_value)
{
args[j].tree_value = struct_value_addr_value;
j--;
+
+ /* If we pass structure address then we need to
+ create bounds for it. Since created bounds is
+ a call statement, we expand it right here to avoid
+ fixing all other places where it may be expanded. */
+ if (CALL_WITH_BOUNDS_P (exp))
+ {
+ args[j].value = gen_reg_rtx (targetm.chkp_bound_mode ());
+ args[j].tree_value
+ = chkp_make_bounds_for_struct_addr (struct_value_addr_value);
+ expand_expr_real (args[j].tree_value, args[j].value, VOIDmode,
+ EXPAND_NORMAL, 0, false);
+ args[j].pointer_arg = j + 1;
+ j--;
+ }
}
FOR_EACH_CALL_EXPR_ARG (arg, iter, exp)
{
tree argtype = TREE_TYPE (arg);
+
+ /* Remember last param with pointer and associate it
+ with following pointer bounds. */
+ if (CALL_WITH_BOUNDS_P (exp)
+ && chkp_type_has_pointer (argtype))
+ {
+ if (slots)
+ BITMAP_FREE (slots);
+ ptr_arg = j;
+ if (!BOUNDED_TYPE_P (argtype))
+ {
+ slots = BITMAP_ALLOC (NULL);
+ chkp_find_bound_slots (argtype, slots);
+ }
+ }
+ else if (POINTER_BOUNDS_TYPE_P (argtype))
+ {
+ /* We expect bounds in instrumented calls only.
+ Otherwise it is a sign we lost flag due to some optimization
+ and may emit call args incorrectly. */
+ gcc_assert (CALL_WITH_BOUNDS_P (exp));
+
+ /* For structures look for the next available pointer. */
+ if (ptr_arg != -1 && slots)
+ {
+ unsigned bnd_no = bitmap_first_set_bit (slots);
+ args[j].pointer_offset =
+ bnd_no * POINTER_SIZE / BITS_PER_UNIT;
+
+ bitmap_clear_bit (slots, bnd_no);
+
+ /* Check we have no more pointers in the structure. */
+ if (bitmap_empty_p (slots))
+ BITMAP_FREE (slots);
+ }
+ args[j].pointer_arg = ptr_arg;
+
+ /* Check we covered all pointers in the previous
+ non bounds arg. */
+ if (!slots)
+ ptr_arg = -1;
+ }
+ else
+ ptr_arg = -1;
+
if (targetm.calls.split_complex_arg
&& argtype
&& TREE_CODE (argtype) == COMPLEX_TYPE
args[j].tree_value = arg;
j--;
}
+
+ if (slots)
+ BITMAP_FREE (slots);
}
+ bitmap_obstack_release (NULL);
+
/* I counts args in order (to be) pushed; ARGPOS counts in order written. */
for (argpos = 0; argpos < num_actuals; i--, argpos++)
{
tree type = TREE_TYPE (args[i].tree_value);
int unsignedp;
- enum machine_mode mode;
+ machine_mode mode;
/* Replace erroneous argument with constant zero. */
if (type == error_mark_node || !COMPLETE_TYPE_P (type))
&& TREE_CODE (base) != SSA_NAME
&& (!DECL_P (base) || MEM_P (DECL_RTL (base)))))
{
+ /* We may have turned the parameter value into an SSA name.
+ Go back to the original parameter so we can take the
+ address. */
+ if (TREE_CODE (args[i].tree_value) == SSA_NAME)
+ {
+ gcc_assert (SSA_NAME_IS_DEFAULT_DEF (args[i].tree_value));
+ args[i].tree_value = SSA_NAME_VAR (args[i].tree_value);
+ gcc_assert (TREE_CODE (args[i].tree_value) == PARM_DECL);
+ }
+ /* Argument setup code may have copied the value to register. We
+ revert that optimization now because the tail call code must
+ use the original location. */
+ if (TREE_CODE (args[i].tree_value) == PARM_DECL
+ && !MEM_P (DECL_RTL (args[i].tree_value))
+ && DECL_INCOMING_RTL (args[i].tree_value)
+ && MEM_P (DECL_INCOMING_RTL (args[i].tree_value)))
+ set_decl_rtl (args[i].tree_value,
+ DECL_INCOMING_RTL (args[i].tree_value));
+
mark_addressable (args[i].tree_value);
/* We can't use sibcalls if a callee-copied argument is
else
copy = assign_temp (type, 1, 0);
- store_expr (args[i].tree_value, copy, 0, false);
+ store_expr (args[i].tree_value, copy, 0, false, false);
/* Just change the const function to pure and then let
the next test clear the pure based on
args[i].reg = targetm.calls.function_arg (args_so_far, mode, type,
argpos < n_named_args);
+ if (args[i].reg && CONST_INT_P (args[i].reg))
+ {
+ args[i].special_slot = args[i].reg;
+ args[i].reg = NULL;
+ }
+
/* If this is a sibling call and the machine has register windows, the
register window has to be unwinded before calling the routine, so
arguments have to go into the incoming registers. */
|| (args[i].pass_on_stack && args[i].reg != 0))
*must_preallocate = 1;
+ /* No stack allocation and padding for bounds. */
+ if (POINTER_BOUNDS_P (args[i].tree_value))
+ ;
/* Compute the stack-size of this argument. */
- if (args[i].reg == 0 || args[i].partial != 0
- || reg_parm_stack_space > 0
- || args[i].pass_on_stack)
+ else if (args[i].reg == 0 || args[i].partial != 0
+ || reg_parm_stack_space > 0
+ || args[i].pass_on_stack)
locate_and_pad_parm (mode, type,
#ifdef STACK_PARMS_IN_REG_PARM_AREA
1,
for (i = 0; i < num_actuals; i++)
{
tree type;
- enum machine_mode mode;
+ machine_mode mode;
if (TREE_CODE (args[i].tree_value) != CALL_EXPR)
continue;
partial_seen = 1;
else if (partial_seen && args[i].reg == 0)
must_preallocate = 1;
+ /* We preallocate in case there are bounds passed
+ in the bounds table to have precomputed address
+ for bounds association. */
+ else if (POINTER_BOUNDS_P (args[i].tree_value)
+ && !args[i].reg)
+ must_preallocate = 1;
if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode
&& (TREE_CODE (args[i].tree_value) == CALL_EXPR
rtx addr;
unsigned int align, boundary;
unsigned int units_on_stack = 0;
- enum machine_mode partial_mode = VOIDmode;
+ machine_mode partial_mode = VOIDmode;
/* Skip this parm if it will not be passed on the stack. */
if (! args[i].pass_on_stack
&& args[i].partial == 0)
continue;
+ /* Pointer Bounds are never passed on the stack. */
+ if (POINTER_BOUNDS_P (args[i].tree_value))
+ continue;
+
if (CONST_INT_P (offset))
addr = plus_constant (Pmode, arg_reg, INTVAL (offset));
else
return true;
else
i = INTVAL (val);
-#ifdef STACK_GROWS_DOWNWARD
- i -= crtl->args.pretend_args_size;
-#else
- i += crtl->args.pretend_args_size;
-#endif
-#ifdef ARGS_GROW_DOWNWARD
- i = -i - size;
-#endif
+ if (STACK_GROWS_DOWNWARD)
+ i -= crtl->args.pretend_args_size;
+ else
+ i += crtl->args.pretend_args_size;
+
+
+ if (ARGS_GROW_DOWNWARD)
+ i = -i - size;
+
if (size > 0)
{
unsigned HOST_WIDE_INT k;
(XEXP (args[i].value, 0), size)))
*sibcall_failure = 1;
+ if (size % UNITS_PER_WORD == 0
+ || MEM_ALIGN (mem) % BITS_PER_WORD == 0)
+ move_block_to_reg (REGNO (reg), mem, nregs, args[i].mode);
+ else
+ {
+ if (nregs > 1)
+ move_block_to_reg (REGNO (reg), mem, nregs - 1,
+ args[i].mode);
+ rtx dest = gen_rtx_REG (word_mode, REGNO (reg) + nregs - 1);
+ unsigned int bitoff = (nregs - 1) * BITS_PER_WORD;
+ unsigned int bitsize = size * BITS_PER_UNIT - bitoff;
+ rtx x = extract_bit_field (mem, bitsize, bitoff, 1, dest,
+ word_mode, word_mode, false);
+ if (BYTES_BIG_ENDIAN)
+ x = expand_shift (LSHIFT_EXPR, word_mode, x,
+ BITS_PER_WORD - bitsize, dest, 1);
+ if (x != dest)
+ emit_move_insn (dest, x);
+ }
+
/* Handle a BLKmode that needs shifting. */
if (nregs == 1 && size < UNITS_PER_WORD
#ifdef BLOCK_REG_PADDING
#else
&& BYTES_BIG_ENDIAN
#endif
- )
+ )
{
- rtx tem = operand_subword_force (mem, 0, args[i].mode);
- rtx ri = gen_rtx_REG (word_mode, REGNO (reg));
- rtx x = gen_reg_rtx (word_mode);
+ rtx dest = gen_rtx_REG (word_mode, REGNO (reg));
int shift = (UNITS_PER_WORD - size) * BITS_PER_UNIT;
- enum tree_code dir = BYTES_BIG_ENDIAN ? RSHIFT_EXPR
- : LSHIFT_EXPR;
+ enum tree_code dir = (BYTES_BIG_ENDIAN
+ ? RSHIFT_EXPR : LSHIFT_EXPR);
+ rtx x;
- emit_move_insn (x, tem);
- x = expand_shift (dir, word_mode, x, shift, ri, 1);
- if (x != ri)
- emit_move_insn (ri, x);
+ x = expand_shift (dir, word_mode, dest, shift, dest, 1);
+ if (x != dest)
+ emit_move_insn (dest, x);
}
- else
- move_block_to_reg (REGNO (reg), mem, nregs, args[i].mode);
}
/* When a parameter is a block, and perhaps in other cases, it is
if (mark_stored_args_map)
{
-#ifdef ARGS_GROW_DOWNWARD
- low = -arg->locate.slot_offset.constant - arg->locate.size.constant;
-#else
- low = arg->locate.slot_offset.constant;
-#endif
+ if (ARGS_GROW_DOWNWARD)
+ low = -arg->locate.slot_offset.constant - arg->locate.size.constant;
+ else
+ low = arg->locate.slot_offset.constant;
for (high = low + arg->locate.size.constant; low < high; low++)
bitmap_set_bit (stored_args_map, low);
as specified by LEFT_P. Return true if some action was needed. */
bool
-shift_return_value (enum machine_mode mode, bool left_p, rtx value)
+shift_return_value (machine_mode mode, bool left_p, rtx value)
{
HOST_WIDE_INT shift;
/* Register in which non-BLKmode value will be returned,
or 0 if no value or if value is BLKmode. */
rtx valreg;
+ /* Register(s) in which bounds are returned. */
+ rtx valbnd = NULL;
/* Address where we should return a BLKmode value;
0 if value not BLKmode. */
rtx structure_value_addr = 0;
structure_value_addr_value =
make_tree (build_pointer_type (TREE_TYPE (funtype)), temp);
- structure_value_addr_parm = 1;
+ structure_value_addr_parm = CALL_WITH_BOUNDS_P (exp) ? 2 : 1;
}
/* Count the arguments and set NUM_ACTUALS. */
try_tail_call = 0;
/* Rest of purposes for tail call optimizations to fail. */
- if (
-#ifdef HAVE_sibcall_epilogue
- !HAVE_sibcall_epilogue
-#else
- 1
-#endif
- || !try_tail_call
+ if (!try_tail_call
+ || !targetm.have_sibcall_epilogue ()
/* Doing sibling call optimization needs some work, since
structure_value_addr can be allocated on the stack.
It does not seem worth the effort since few optimizable
return value. */
if (try_tail_call)
{
- enum machine_mode caller_mode, caller_promoted_mode;
- enum machine_mode callee_mode, callee_promoted_mode;
+ machine_mode caller_mode, caller_promoted_mode;
+ machine_mode callee_mode, callee_promoted_mode;
int caller_unsignedp, callee_unsignedp;
tree caller_res = DECL_RESULT (current_function_decl);
if (pass == 0)
{
argblock = crtl->args.internal_arg_pointer;
- argblock
-#ifdef STACK_GROWS_DOWNWARD
- = plus_constant (Pmode, argblock, crtl->args.pretend_args_size);
-#else
- = plus_constant (Pmode, argblock, -crtl->args.pretend_args_size);
-#endif
+ if (STACK_GROWS_DOWNWARD)
+ argblock
+ = plus_constant (Pmode, argblock, crtl->args.pretend_args_size);
+ else
+ argblock
+ = plus_constant (Pmode, argblock, -crtl->args.pretend_args_size);
+
stored_args_map = sbitmap_alloc (args_size.constant);
bitmap_clear (stored_args_map);
}
if (! OUTGOING_REG_PARM_STACK_SPACE ((!fndecl ? fntype : TREE_TYPE (fndecl))))
needed += reg_parm_stack_space;
-#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
+ if (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);
+
free (stack_usage_map_buf);
stack_usage_map_buf = XNEWVEC (char, highest_outgoing_arg_in_use);
stack_usage_map = stack_usage_map_buf;
else
{
argblock = push_block (GEN_INT (needed), 0, 0);
-#ifdef ARGS_GROW_DOWNWARD
- argblock = plus_constant (Pmode, argblock, needed);
-#endif
+ if (ARGS_GROW_DOWNWARD)
+ argblock = plus_constant (Pmode, argblock, needed);
}
/* We only really need to call `copy_to_reg' in the case
compute_argument_addresses (args, argblock, num_actuals);
+ /* Stack is properly aligned, pops can't safely be deferred during
+ the evaluation of the arguments. */
+ NO_DEFER_POP;
+
+ /* Precompute all register parameters. It isn't safe to compute
+ anything once we have started filling any specific hard regs.
+ TLS symbols sometimes need a call to resolve. Precompute
+ register parameters before any stack pointer manipulation
+ to avoid unaligned stack in the called function. */
+ precompute_register_parameters (num_actuals, args, ®_parm_seen);
+
+ OK_DEFER_POP;
+
/* Perform stack alignment before the first push (the last arg). */
if (argblock == 0
&& adjusted_args_size.constant > reg_parm_stack_space
funexp = rtx_for_function_call (fndecl, addr);
- /* Figure out the register where the value, if any, will come back. */
- valreg = 0;
- if (TYPE_MODE (rettype) != VOIDmode
- && ! structure_value_addr)
- {
- if (pcc_struct_value)
- valreg = hard_function_value (build_pointer_type (rettype),
- fndecl, NULL, (pass == 0));
- else
- valreg = hard_function_value (rettype, fndecl, fntype,
- (pass == 0));
-
- /* If VALREG is a PARALLEL whose first member has a zero
- offset, use that. This is for targets such as m68k that
- return the same value in multiple places. */
- if (GET_CODE (valreg) == PARALLEL)
- {
- rtx elem = XVECEXP (valreg, 0, 0);
- rtx where = XEXP (elem, 0);
- rtx offset = XEXP (elem, 1);
- if (offset == const0_rtx
- && GET_MODE (where) == GET_MODE (valreg))
- valreg = where;
- }
- }
-
- /* 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, ®_parm_seen);
-
if (CALL_EXPR_STATIC_CHAIN (exp))
static_chain_value = expand_normal (CALL_EXPR_STATIC_CHAIN (exp));
else
for (i = 0; i < num_actuals; i++)
{
- if (args[i].reg == 0 || args[i].pass_on_stack)
+ /* Delay bounds until all other args are stored. */
+ if (POINTER_BOUNDS_P (args[i].tree_value))
+ continue;
+ else if (args[i].reg == 0 || args[i].pass_on_stack)
{
rtx_insn *before_arg = get_last_insn ();
{
rtx_insn *before_arg = get_last_insn ();
+ /* On targets with weird calling conventions (e.g. PA) it's
+ hard to ensure that all cases of argument overlap between
+ stack and registers work. Play it safe and bail out. */
+ if (ARGS_GROW_DOWNWARD && !STACK_GROWS_DOWNWARD)
+ {
+ sibcall_failure = 1;
+ break;
+ }
+
if (store_one_arg (&args[i], argblock, flags,
adjusted_args_size.var != 0,
reg_parm_stack_space)
sibcall_failure = 1;
}
+ bool any_regs = false;
+ for (i = 0; i < num_actuals; i++)
+ if (args[i].reg != NULL_RTX)
+ {
+ any_regs = true;
+ targetm.calls.call_args (args[i].reg, funtype);
+ }
+ if (!any_regs)
+ targetm.calls.call_args (pc_rtx, funtype);
+
+ /* Figure out the register where the value, if any, will come back. */
+ valreg = 0;
+ valbnd = 0;
+ if (TYPE_MODE (rettype) != VOIDmode
+ && ! structure_value_addr)
+ {
+ if (pcc_struct_value)
+ {
+ valreg = hard_function_value (build_pointer_type (rettype),
+ fndecl, NULL, (pass == 0));
+ if (CALL_WITH_BOUNDS_P (exp))
+ valbnd = targetm.calls.
+ chkp_function_value_bounds (build_pointer_type (rettype),
+ fndecl, (pass == 0));
+ }
+ else
+ {
+ valreg = hard_function_value (rettype, fndecl, fntype,
+ (pass == 0));
+ if (CALL_WITH_BOUNDS_P (exp))
+ valbnd = targetm.calls.chkp_function_value_bounds (rettype,
+ fndecl,
+ (pass == 0));
+ }
+
+ /* If VALREG is a PARALLEL whose first member has a zero
+ offset, use that. This is for targets such as m68k that
+ return the same value in multiple places. */
+ if (GET_CODE (valreg) == PARALLEL)
+ {
+ rtx elem = XVECEXP (valreg, 0, 0);
+ rtx where = XEXP (elem, 0);
+ rtx offset = XEXP (elem, 1);
+ if (offset == const0_rtx
+ && GET_MODE (where) == GET_MODE (valreg))
+ valreg = where;
+ }
+ }
+
+ /* Store all bounds not passed in registers. */
+ for (i = 0; i < num_actuals; i++)
+ {
+ if (POINTER_BOUNDS_P (args[i].tree_value)
+ && !args[i].reg)
+ store_bounds (&args[i],
+ args[i].pointer_arg == -1
+ ? NULL
+ : &args[args[i].pointer_arg]);
+ }
+
/* If register arguments require space on the stack and stack space
was not preallocated, allocate stack space here for arguments
passed in registers. */
}
after_args = get_last_insn ();
- funexp = prepare_call_address (fndecl, funexp, static_chain_value,
- &call_fusage, reg_parm_seen, pass == 0);
+ funexp = prepare_call_address (fndecl ? fndecl : fntype, funexp,
+ static_chain_value, &call_fusage,
+ reg_parm_seen, pass == 0);
load_register_parameters (args, num_actuals, &call_fusage, flags,
pass == 0, &sibcall_failure);
&& GET_MODE (args[arg_nr].reg) == GET_MODE (valreg))
call_fusage
= gen_rtx_EXPR_LIST (TYPE_MODE (TREE_TYPE (args[arg_nr].tree_value)),
- gen_rtx_SET (VOIDmode, valreg, args[arg_nr].reg),
+ gen_rtx_SET (valreg, args[arg_nr].reg),
call_fusage);
}
/* All arguments and registers used for the call must be set up by
next_arg_reg, valreg, old_inhibit_defer_pop, call_fusage,
flags, args_so_far);
- if (flag_use_caller_save)
+ if (flag_ipa_ra)
{
rtx_call_insn *last;
rtx datum = NULL_RTX;
tree type = rettype;
int unsignedp = TYPE_UNSIGNED (type);
int offset = 0;
- enum machine_mode pmode;
+ machine_mode pmode;
/* Ensure we promote as expected, and get the new unsignedness. */
pmode = promote_function_mode (type, TYPE_MODE (type), &unsignedp,
for (i = 0; i < num_actuals; i++)
if (args[i].save_area)
{
- enum machine_mode save_mode = GET_MODE (args[i].save_area);
+ machine_mode save_mode = GET_MODE (args[i].save_area);
rtx stack_area
= gen_rtx_MEM (save_mode,
memory_address (save_mode,
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) && cfun->nonlocal_goto_save_area != 0)
- update_nonlocal_goto_save_area ();
+ /* If this was alloca, record the new stack level. */
+ if (flags & ECF_MAY_BE_ALLOCA)
+ record_new_stack_level ();
/* Free up storage we no longer need. */
for (i = 0; i < num_actuals; ++i)
free (args[i].aligned_regs);
+ targetm.calls.end_call_args ();
+
insns = get_insns ();
end_sequence ();
free (stack_usage_map_buf);
+ /* Join result with returned bounds so caller may use them if needed. */
+ target = chkp_join_splitted_slot (target, valbnd);
+
return target;
}
static rtx
emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
enum libcall_type fn_type,
- enum machine_mode outmode, int nargs, va_list p)
+ machine_mode outmode, int nargs, va_list p)
{
/* Total size in bytes of all the stack-parms scanned so far. */
struct args_size args_size;
struct arg
{
rtx value;
- enum machine_mode mode;
+ machine_mode mode;
rtx reg;
int partial;
struct locate_and_pad_arg_data locate;
int reg_parm_stack_space = 0;
int needed;
rtx_insn *before_call;
+ bool have_push_fusage;
tree tfom; /* type_for_mode (outmode, 0) */
#ifdef REG_PARM_STACK_SPACE
for (; count < nargs; count++)
{
rtx val = va_arg (p, rtx);
- enum machine_mode mode = (enum machine_mode) va_arg (p, int);
+ machine_mode mode = (machine_mode) va_arg (p, int);
int unsigned_p = 0;
/* We cannot convert the arg value to the mode the library wants here;
if (! OUTGOING_REG_PARM_STACK_SPACE ((!fndecl ? fntype : TREE_TYPE (fndecl))))
needed += reg_parm_stack_space;
-#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
+ if (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);
+
stack_usage_map_buf = XNEWVEC (char, highest_outgoing_arg_in_use);
stack_usage_map = stack_usage_map_buf;
}
#endif
+ /* When expanding a normal call, args are stored in push order,
+ which is the reverse of what we have here. */
+ bool any_regs = false;
+ for (int i = nargs; i-- > 0; )
+ if (argvec[i].reg != NULL_RTX)
+ {
+ targetm.calls.call_args (argvec[i].reg, NULL_TREE);
+ any_regs = true;
+ }
+ if (!any_regs)
+ targetm.calls.call_args (pc_rtx, NULL_TREE);
+
/* Push the args that need to be pushed. */
+ have_push_fusage = false;
+
/* ARGNUM indexes the ARGVEC array in the order in which the arguments
are to be pushed. */
for (count = 0; count < nargs; count++, argnum--)
{
- enum machine_mode mode = argvec[argnum].mode;
+ machine_mode mode = argvec[argnum].mode;
rtx val = argvec[argnum].value;
rtx reg = argvec[argnum].reg;
int partial = argvec[argnum].partial;
/* 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[argnum].locate.slot_offset.constant + 1;
- lower_bound = upper_bound - argvec[argnum].locate.size.constant;
-#else
- lower_bound = argvec[argnum].locate.slot_offset.constant;
- upper_bound = lower_bound + argvec[argnum].locate.size.constant;
-#endif
+ if (ARGS_GROW_DOWNWARD)
+ {
+ /* stack_slot is negative, but we want to index stack_usage_map
+ with positive values. */
+ upper_bound = -argvec[argnum].locate.slot_offset.constant + 1;
+ lower_bound = upper_bound - argvec[argnum].locate.size.constant;
+ }
+ else
+ {
+ lower_bound = argvec[argnum].locate.slot_offset.constant;
+ upper_bound = lower_bound + argvec[argnum].locate.size.constant;
+ }
i = lower_bound;
/* Don't worry about things in the fixed argument area;
/* We need to make a save area. */
unsigned int size
= argvec[argnum].locate.size.constant * BITS_PER_UNIT;
- enum machine_mode save_mode
+ machine_mode save_mode
= mode_for_size (size, MODE_INT, 1);
rtx adr
= plus_constant (Pmode, argblock,
partial, reg, 0, argblock,
GEN_INT (argvec[argnum].locate.offset.constant),
reg_parm_stack_space,
- ARGS_SIZE_RTX (argvec[argnum].locate.alignment_pad));
+ ARGS_SIZE_RTX (argvec[argnum].locate.alignment_pad), false);
/* Now mark the segment we just used. */
if (ACCUMULATE_OUTGOING_ARGS)
if (argblock)
use = plus_constant (Pmode, argblock,
argvec[argnum].locate.offset.constant);
+ else if (have_push_fusage)
+ continue;
else
- /* When arguments are pushed, trying to tell alias.c where
- exactly this argument is won't work, because the
- auto-increment causes confusion. So we merely indicate
- that we access something with a known mode somewhere on
- the stack. */
- use = gen_rtx_PLUS (Pmode, virtual_outgoing_args_rtx,
- gen_rtx_SCRATCH (Pmode));
+ {
+ /* When arguments are pushed, trying to tell alias.c where
+ exactly this argument is won't work, because the
+ auto-increment causes confusion. So we merely indicate
+ that we access something with a known mode somewhere on
+ the stack. */
+ use = gen_rtx_PLUS (Pmode, stack_pointer_rtx,
+ gen_rtx_SCRATCH (Pmode));
+ have_push_fusage = true;
+ }
use = gen_rtx_MEM (argvec[argnum].mode, use);
use = gen_rtx_USE (VOIDmode, use);
call_fusage = gen_rtx_EXPR_LIST (VOIDmode, use, call_fusage);
are to be pushed. */
for (count = 0; count < nargs; count++, argnum--)
{
- enum machine_mode mode = argvec[argnum].mode;
+ machine_mode mode = argvec[argnum].mode;
rtx val = argvec[argnum].value;
rtx reg = argvec[argnum].reg;
int partial = argvec[argnum].partial;
valreg,
old_inhibit_defer_pop + 1, call_fusage, flags, args_so_far);
- if (flag_use_caller_save)
+ if (flag_ipa_ra)
{
- rtx last, datum = orgfun;
+ rtx datum = orgfun;
gcc_assert (GET_CODE (datum) == SYMBOL_REF);
- last = last_call_insn ();
+ rtx_call_insn *last = last_call_insn ();
add_reg_note (last, REG_CALL_DECL, datum);
}
valreg = gen_rtx_REG (TYPE_MODE (tfom), REGNO (valreg));
}
+ targetm.calls.end_call_args ();
+
/* For calls to `setjmp', etc., inform function.c:setjmp_warnings
that it should complain if nonvolatile values are live. For
functions that cannot return, inform flow that control does not
for (count = 0; count < nargs; count++)
if (argvec[count].save_area)
{
- enum machine_mode save_mode = GET_MODE (argvec[count].save_area);
+ machine_mode save_mode = GET_MODE (argvec[count].save_area);
rtx adr = plus_constant (Pmode, argblock,
argvec[count].locate.offset.constant);
rtx stack_area = gen_rtx_MEM (save_mode,
void
emit_library_call (rtx orgfun, enum libcall_type fn_type,
- enum machine_mode outmode, int nargs, ...)
+ machine_mode outmode, int nargs, ...)
{
va_list p;
rtx
emit_library_call_value (rtx orgfun, rtx value,
enum libcall_type fn_type,
- enum machine_mode outmode, int nargs, ...)
+ machine_mode outmode, int nargs, ...)
{
rtx result;
va_list p;
return result;
}
\f
+
+/* Store pointer bounds argument ARG into Bounds Table entry
+ associated with PARM. */
+static void
+store_bounds (struct arg_data *arg, struct arg_data *parm)
+{
+ rtx slot = NULL, ptr = NULL, addr = NULL;
+
+ /* We may pass bounds not associated with any pointer. */
+ if (!parm)
+ {
+ gcc_assert (arg->special_slot);
+ slot = arg->special_slot;
+ ptr = const0_rtx;
+ }
+ /* Find pointer associated with bounds and where it is
+ passed. */
+ else
+ {
+ if (!parm->reg)
+ {
+ gcc_assert (!arg->special_slot);
+
+ addr = adjust_address (parm->stack, Pmode, arg->pointer_offset);
+ }
+ else if (REG_P (parm->reg))
+ {
+ gcc_assert (arg->special_slot);
+ slot = arg->special_slot;
+
+ if (MEM_P (parm->value))
+ addr = adjust_address (parm->value, Pmode, arg->pointer_offset);
+ else if (REG_P (parm->value))
+ ptr = gen_rtx_SUBREG (Pmode, parm->value, arg->pointer_offset);
+ else
+ {
+ gcc_assert (!arg->pointer_offset);
+ ptr = parm->value;
+ }
+ }
+ else
+ {
+ gcc_assert (GET_CODE (parm->reg) == PARALLEL);
+
+ gcc_assert (arg->special_slot);
+ slot = arg->special_slot;
+
+ if (parm->parallel_value)
+ ptr = chkp_get_value_with_offs (parm->parallel_value,
+ GEN_INT (arg->pointer_offset));
+ else
+ gcc_unreachable ();
+ }
+ }
+
+ /* Expand bounds. */
+ if (!arg->value)
+ arg->value = expand_normal (arg->tree_value);
+
+ targetm.calls.store_bounds_for_arg (ptr, addr, arg->value, slot);
+}
+
/* 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.
save any previous data at that location. */
if (argblock && ! variable_size && arg->stack)
{
-#ifdef ARGS_GROW_DOWNWARD
- /* stack_slot is negative, but we want to index stack_usage_map
- with positive values. */
- if (GET_CODE (XEXP (arg->stack_slot, 0)) == PLUS)
- upper_bound = -INTVAL (XEXP (XEXP (arg->stack_slot, 0), 1)) + 1;
- else
- upper_bound = 0;
+ if (ARGS_GROW_DOWNWARD)
+ {
+ /* stack_slot is negative, but we want to index stack_usage_map
+ with positive values. */
+ if (GET_CODE (XEXP (arg->stack_slot, 0)) == PLUS)
+ upper_bound = -INTVAL (XEXP (XEXP (arg->stack_slot, 0), 1)) + 1;
+ else
+ upper_bound = 0;
- lower_bound = upper_bound - arg->locate.size.constant;
-#else
- if (GET_CODE (XEXP (arg->stack_slot, 0)) == PLUS)
- lower_bound = INTVAL (XEXP (XEXP (arg->stack_slot, 0), 1));
+ lower_bound = upper_bound - arg->locate.size.constant;
+ }
else
- lower_bound = 0;
+ {
+ if (GET_CODE (XEXP (arg->stack_slot, 0)) == PLUS)
+ lower_bound = INTVAL (XEXP (XEXP (arg->stack_slot, 0), 1));
+ else
+ lower_bound = 0;
- upper_bound = lower_bound + arg->locate.size.constant;
-#endif
+ upper_bound = lower_bound + arg->locate.size.constant;
+ }
i = lower_bound;
/* Don't worry about things in the fixed argument area;
{
/* We need to make a save area. */
unsigned int size = arg->locate.size.constant * BITS_PER_UNIT;
- enum machine_mode save_mode = mode_for_size (size, MODE_INT, 1);
+ machine_mode save_mode = mode_for_size (size, MODE_INT, 1);
rtx adr = memory_address (save_mode, XEXP (arg->stack_slot, 0));
rtx stack_area = gen_rtx_MEM (save_mode, adr);
/* This isn't already where we want it on the stack, so put it there.
This can either be done with push or copy insns. */
- emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX,
+ if (!emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX,
parm_align, partial, reg, used - size, argblock,
ARGS_SIZE_RTX (arg->locate.offset), reg_parm_stack_space,
- ARGS_SIZE_RTX (arg->locate.alignment_pad));
+ ARGS_SIZE_RTX (arg->locate.alignment_pad), true))
+ sibcall_failure = 1;
/* Unless this is a partially-in-register argument, the argument is now
in the stack. */
if (XEXP (x, 0) != crtl->args.internal_arg_pointer)
i = INTVAL (XEXP (XEXP (x, 0), 1));
+ /* arg.locate doesn't contain the pretend_args_size offset,
+ it's part of argblock. Ensure we don't count it in I. */
+ if (STACK_GROWS_DOWNWARD)
+ i -= crtl->args.pretend_args_size;
+ else
+ i += crtl->args.pretend_args_size;
+
/* expand_call should ensure this. */
gcc_assert (!arg->locate.offset.var
&& arg->locate.size.var == 0
emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx,
parm_align, partial, reg, excess, argblock,
ARGS_SIZE_RTX (arg->locate.offset), reg_parm_stack_space,
- ARGS_SIZE_RTX (arg->locate.alignment_pad));
+ ARGS_SIZE_RTX (arg->locate.alignment_pad), false);
/* Unless this is a partially-in-register argument, the argument is now
in the stack.
/* Nonzero if we do not know how to pass TYPE solely in registers. */
bool
-must_pass_in_stack_var_size (enum machine_mode mode ATTRIBUTE_UNUSED,
+must_pass_in_stack_var_size (machine_mode mode ATTRIBUTE_UNUSED,
const_tree type)
{
if (!type)
/* ??? Should be able to merge these two by examining BLOCK_REG_PADDING. */
bool
-must_pass_in_stack_var_size_or_pad (enum machine_mode mode, const_tree type)
+must_pass_in_stack_var_size_or_pad (machine_mode mode, const_tree type)
{
if (!type)
return false;