]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/calls.c
c++: Handle multiple aggregate overloads [PR95319].
[thirdparty/gcc.git] / gcc / calls.c
index d5bd5049cb94fd57aafe1b20bbe152d9f6880ddc..8041388c1d202199240223210edafa6749ed5a88 100644 (file)
@@ -1,5 +1,5 @@
 /* Convert function calls to rtl insns, for GNU C compiler.
-   Copyright (C) 1989-2017 Free Software Foundation, Inc.
+   Copyright (C) 1989-2020 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -47,13 +47,16 @@ along with GCC; see the file COPYING3.  If not see
 #include "except.h"
 #include "dbgcnt.h"
 #include "rtl-iter.h"
-#include "tree-chkp.h"
 #include "tree-vrp.h"
 #include "tree-ssanames.h"
-#include "rtl-chkp.h"
+#include "tree-ssa-strlen.h"
 #include "intl.h"
 #include "stringpool.h"
+#include "hash-map.h"
+#include "hash-traits.h"
 #include "attribs.h"
+#include "builtins.h"
+#include "gimple-fold.h"
 
 /* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits.  */
 #define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
@@ -81,15 +84,6 @@ struct arg_data
   /* 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;
@@ -127,7 +121,11 @@ struct arg_data
 static char *stack_usage_map;
 
 /* Size of STACK_USAGE_MAP.  */
-static int highest_outgoing_arg_in_use;
+static unsigned int highest_outgoing_arg_in_use;
+
+/* Assume that any stack location at this byte index is used,
+   without checking the contents of stack_usage_map.  */
+static unsigned HOST_WIDE_INT stack_usage_watermark = HOST_WIDE_INT_M1U;
 
 /* A bitmap of virtual-incoming stack space.  Bit is set if the corresponding
    stack location's tail call argument has been already stored into the stack.
@@ -136,6 +134,10 @@ static int highest_outgoing_arg_in_use;
    overwritten with tail call arguments.  */
 static sbitmap stored_args_map;
 
+/* Assume that any virtual-incoming location at this byte index has been
+   stored, without checking the contents of stored_args_map.  */
+static unsigned HOST_WIDE_INT stored_args_watermark;
+
 /* stack_arg_under_construction is nonzero when an argument may be
    initialized with a constructor call (including a C function that
    returns a BLKmode struct) and expand_call must take special action
@@ -143,35 +145,20 @@ static sbitmap stored_args_map;
    argument list for the constructor call.  */
 static int stack_arg_under_construction;
 
-static void emit_call_1 (rtx, tree, tree, tree, HOST_WIDE_INT, HOST_WIDE_INT,
-                        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 *,
                                      struct args_size *);
 static void precompute_arguments (int, struct arg_data *);
-static int compute_argument_block_size (int, struct args_size *, tree, tree, int);
-static void initialize_argument_information (int, struct arg_data *,
-                                            struct args_size *, int,
-                                            tree, tree,
-                                            tree, tree, cumulative_args_t, int,
-                                            rtx *, int *, int *, int *,
-                                            bool *, bool);
 static void compute_argument_addresses (struct arg_data *, rtx, int);
 static rtx rtx_for_function_call (tree, tree);
 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,
-                                     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);
 
-static int combine_pending_stack_adjustment_and_call (int, struct args_size *,
-                                                     unsigned int);
 static tree split_complex_types (tree);
 
 #ifdef REG_PARM_STACK_SPACE
@@ -179,6 +166,46 @@ static rtx save_fixed_argument_area (int, rtx, int *, int *);
 static void restore_fixed_argument_area (rtx, rtx, int, int);
 #endif
 \f
+/* Return true if bytes [LOWER_BOUND, UPPER_BOUND) of the outgoing
+   stack region might already be in use.  */
+
+static bool
+stack_region_maybe_used_p (poly_uint64 lower_bound, poly_uint64 upper_bound,
+                          unsigned int reg_parm_stack_space)
+{
+  unsigned HOST_WIDE_INT const_lower, const_upper;
+  const_lower = constant_lower_bound (lower_bound);
+  if (!upper_bound.is_constant (&const_upper))
+    const_upper = HOST_WIDE_INT_M1U;
+
+  if (const_upper > stack_usage_watermark)
+    return true;
+
+  /* Don't worry about things in the fixed argument area;
+     it has already been saved.  */
+  const_lower = MAX (const_lower, reg_parm_stack_space);
+  const_upper = MIN (const_upper, highest_outgoing_arg_in_use);
+  for (unsigned HOST_WIDE_INT i = const_lower; i < const_upper; ++i)
+    if (stack_usage_map[i])
+      return true;
+  return false;
+}
+
+/* Record that bytes [LOWER_BOUND, UPPER_BOUND) of the outgoing
+   stack region are now in use.  */
+
+static void
+mark_stack_region_used (poly_uint64 lower_bound, poly_uint64 upper_bound)
+{
+  unsigned HOST_WIDE_INT const_lower, const_upper;
+  const_lower = constant_lower_bound (lower_bound);
+  if (upper_bound.is_constant (&const_upper))
+    for (unsigned HOST_WIDE_INT i = const_lower; i < const_upper; ++i)
+      stack_usage_map[i] = 1;
+  else
+    stack_usage_watermark = MIN (stack_usage_watermark, const_lower);
+}
+
 /* Force FUNEXP into a form suitable for the address of a CALL,
    and return that as an rtx.  Also load the static chain register
    if FNDECL is a nested function.
@@ -321,7 +348,8 @@ prepare_call_address (tree fndecl_or_type, rtx funexp, rtx static_chain_value,
    It is zero if this call doesn't want a structure value.
 
    NEXT_ARG_REG is the rtx that results from executing
-     targetm.calls.function_arg (&args_so_far, VOIDmode, void_type_node, true)
+     targetm.calls.function_arg (&args_so_far,
+                                function_arg_info::end_marker ());
    just after all the args have had their registers assigned.
    This could be whatever you like, but normally it is the first
    arg-register beyond those used for args in this call,
@@ -341,17 +369,17 @@ prepare_call_address (tree fndecl_or_type, rtx funexp, rtx static_chain_value,
 static void
 emit_call_1 (rtx funexp, tree fntree ATTRIBUTE_UNUSED, tree fndecl ATTRIBUTE_UNUSED,
             tree funtype ATTRIBUTE_UNUSED,
-            HOST_WIDE_INT stack_size ATTRIBUTE_UNUSED,
-            HOST_WIDE_INT rounded_stack_size,
-            HOST_WIDE_INT struct_value_size ATTRIBUTE_UNUSED,
+            poly_int64 stack_size ATTRIBUTE_UNUSED,
+            poly_int64 rounded_stack_size,
+            poly_int64 struct_value_size ATTRIBUTE_UNUSED,
             rtx next_arg_reg ATTRIBUTE_UNUSED, rtx valreg,
             int old_inhibit_defer_pop, rtx call_fusage, int ecf_flags,
             cumulative_args_t args_so_far ATTRIBUTE_UNUSED)
 {
-  rtx rounded_stack_size_rtx = GEN_INT (rounded_stack_size);
+  rtx rounded_stack_size_rtx = gen_int_mode (rounded_stack_size, Pmode);
   rtx call, funmem, pat;
   int already_popped = 0;
-  HOST_WIDE_INT n_popped = 0;
+  poly_int64 n_popped = 0;
 
   /* Sibling call patterns never pop arguments (no sibcall(_value)_pop
      patterns exist).  Any popping that the callee does on return will
@@ -403,18 +431,19 @@ emit_call_1 (rtx funexp, tree fntree ATTRIBUTE_UNUSED, tree fndecl ATTRIBUTE_UNU
                                         next_arg_reg, NULL_RTX);
       else
        pat = targetm.gen_sibcall (funmem, rounded_stack_size_rtx,
-                                  next_arg_reg, GEN_INT (struct_value_size));
+                                  next_arg_reg,
+                                  gen_int_mode (struct_value_size, Pmode));
     }
   /* 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.  */
-  else if (n_popped > 0
+  else if (maybe_ne (n_popped, 0)
           || !(valreg
                ? targetm.have_call_value ()
                : targetm.have_call ()))
     {
-      rtx n_pop = GEN_INT (n_popped);
+      rtx n_pop = gen_int_mode (n_popped, Pmode);
 
       /* If this subroutine pops its own args, record that in the call insn
         if possible, for the sake of frame pointer elimination.  */
@@ -436,7 +465,7 @@ emit_call_1 (rtx funexp, tree fntree ATTRIBUTE_UNUSED, tree fndecl ATTRIBUTE_UNU
                                      next_arg_reg, NULL_RTX);
       else
        pat = targetm.gen_call (funmem, rounded_stack_size_rtx, next_arg_reg,
-                               GEN_INT (struct_value_size));
+                               gen_int_mode (struct_value_size, Pmode));
     }
   emit_insn (pat);
 
@@ -451,10 +480,6 @@ emit_call_1 (rtx funexp, tree fntree ATTRIBUTE_UNUSED, tree fndecl ATTRIBUTE_UNU
       && 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);
 
@@ -488,7 +513,7 @@ emit_call_1 (rtx funexp, tree fntree ATTRIBUTE_UNUSED, tree fndecl ATTRIBUTE_UNU
      if the context of the call as a whole permits.  */
   inhibit_defer_pop = old_inhibit_defer_pop;
 
-  if (n_popped > 0)
+  if (maybe_ne (n_popped, 0))
     {
       if (!already_popped)
        CALL_INSN_FUNCTION_USAGE (call_insn)
@@ -496,10 +521,10 @@ emit_call_1 (rtx funexp, tree fntree ATTRIBUTE_UNUSED, tree fndecl ATTRIBUTE_UNU
                               gen_rtx_CLOBBER (VOIDmode, stack_pointer_rtx),
                               CALL_INSN_FUNCTION_USAGE (call_insn));
       rounded_stack_size -= n_popped;
-      rounded_stack_size_rtx = GEN_INT (rounded_stack_size);
+      rounded_stack_size_rtx = gen_int_mode (rounded_stack_size, Pmode);
       stack_pointer_delta -= n_popped;
 
-      add_reg_note (call_insn, REG_ARGS_SIZE, GEN_INT (stack_pointer_delta));
+      add_args_size_note (call_insn, stack_pointer_delta);
 
       /* If popup is needed, stack realign must use DRAP  */
       if (SUPPORTS_STACK_ALIGNMENT)
@@ -509,7 +534,7 @@ emit_call_1 (rtx funexp, tree fntree ATTRIBUTE_UNUSED, tree fndecl ATTRIBUTE_UNU
      REG_ARGS_SIZE note to prevent crossjumping of calls with different
      args sizes.  */
   else if (!ACCUMULATE_OUTGOING_ARGS && (ecf_flags & ECF_NORETURN) != 0)
-    add_reg_note (call_insn, REG_ARGS_SIZE, GEN_INT (stack_pointer_delta));
+    add_args_size_note (call_insn, stack_pointer_delta);
 
   if (!ACCUMULATE_OUTGOING_ARGS)
     {
@@ -520,7 +545,7 @@ emit_call_1 (rtx funexp, tree fntree ATTRIBUTE_UNUSED, tree fndecl ATTRIBUTE_UNU
         If returning from the subroutine does pop the args, indicate that the
         stack pointer will be changed.  */
 
-      if (rounded_stack_size != 0)
+      if (maybe_ne (rounded_stack_size, 0))
        {
          if (ecf_flags & ECF_NORETURN)
            /* Just pretend we did the pop.  */
@@ -543,8 +568,8 @@ emit_call_1 (rtx funexp, tree fntree ATTRIBUTE_UNUSED, tree fndecl ATTRIBUTE_UNU
 
      ??? It will be worthwhile to enable combine_stack_adjustments even for
      such machines.  */
-  else if (n_popped)
-    anti_adjust_stack (GEN_INT (n_popped));
+  else if (maybe_ne (n_popped, 0))
+    anti_adjust_stack (gen_int_mode (n_popped, Pmode));
 }
 
 /* Determine if the function identified by FNDECL is one with
@@ -561,24 +586,8 @@ special_function_p (const_tree fndecl, int flags)
 {
   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) <= 11
-      /* Exclude functions not at the file scope, or not `extern',
-        since they are not the magic functions we would otherwise
-        think they are.
-        FIXME: this should be handled with attributes, not with this
-        hacky imitation of DECL_ASSEMBLER_NAME.  It's (also) wrong
-        because you can declare fork() inside a function if you
-        wish.  */
-      && (DECL_CONTEXT (fndecl) == NULL_TREE
-         || TREE_CODE (DECL_CONTEXT (fndecl)) == TRANSLATION_UNIT_DECL)
-      && TREE_PUBLIC (fndecl))
+  if (maybe_special_function_p (fndecl)
+      && IDENTIFIER_LENGTH (name_decl) <= 11)
     {
       const char *name = IDENTIFIER_POINTER (name_decl);
       const char *tname = name;
@@ -609,16 +618,9 @@ special_function_p (const_tree fndecl, int flags)
        flags |= ECF_RETURNS_TWICE;
     }
 
-  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;
-      }
+  if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
+      && ALLOCA_FUNCTION_CODE_P (DECL_FUNCTION_CODE (fndecl)))
+    flags |= ECF_MAY_BE_ALLOCA;
 
   return flags;
 }
@@ -697,12 +699,11 @@ gimple_alloca_call_p (const gimple *stmt)
     return false;
 
   fndecl = gimple_call_fndecl (stmt);
-  if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
+  if (fndecl && fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
     switch (DECL_FUNCTION_CODE (fndecl))
       {
-      case BUILT_IN_ALLOCA:
-      case BUILT_IN_ALLOCA_WITH_ALIGN:
-        return true;
+      CASE_BUILT_IN_ALLOCA:
+       return gimple_call_num_args (stmt) > 0;
       default:
        break;
       }
@@ -721,8 +722,7 @@ alloca_call_p (const_tree exp)
       && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
     switch (DECL_FUNCTION_CODE (fndecl))
       {
-      case BUILT_IN_ALLOCA:
-      case BUILT_IN_ALLOCA_WITH_ALIGN:
+      CASE_BUILT_IN_ALLOCA:
         return true;
       default:
        break;
@@ -890,13 +890,12 @@ call_expr_flags (const_tree t)
   return flags;
 }
 
-/* Return true if TYPE should be passed by invisible reference.  */
+/* Return true if ARG should be passed by invisible reference.  */
 
 bool
-pass_by_reference (CUMULATIVE_ARGS *ca, machine_mode mode,
-                  tree type, bool named_arg)
+pass_by_reference (CUMULATIVE_ARGS *ca, function_arg_info arg)
 {
-  if (type)
+  if (tree type = arg.type)
     {
       /* If this type contains non-trivial constructors, then it is
         forbidden for the middle-end to create any new copies.  */
@@ -904,33 +903,56 @@ pass_by_reference (CUMULATIVE_ARGS *ca, machine_mode mode,
        return true;
 
       /* GCC post 3.4 passes *all* variable sized types by reference.  */
-      if (!TYPE_SIZE (type) || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
+      if (!TYPE_SIZE (type) || !poly_int_tree_p (TYPE_SIZE (type)))
        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);
+         arg.type = TREE_TYPE (first_field (type));
+         arg.mode = TYPE_MODE (arg.type);
        }
     }
 
-  return targetm.calls.pass_by_reference (pack_cumulative_args (ca), mode,
-                                         type, named_arg);
+  return targetm.calls.pass_by_reference (pack_cumulative_args (ca), arg);
 }
 
-/* Return true if TYPE, which is passed by reference, should be callee
+/* Return true if TYPE should be passed by reference when passed to
+   the "..." arguments of a function.  */
+
+bool
+pass_va_arg_by_reference (tree type)
+{
+  return pass_by_reference (NULL, function_arg_info (type, /*named=*/false));
+}
+
+/* Decide whether ARG, which occurs in the state described by CA,
+   should be passed by reference.  Return true if so and update
+   ARG accordingly.  */
+
+bool
+apply_pass_by_reference_rules (CUMULATIVE_ARGS *ca, function_arg_info &arg)
+{
+  if (pass_by_reference (ca, arg))
+    {
+      arg.type = build_pointer_type (arg.type);
+      arg.mode = TYPE_MODE (arg.type);
+      arg.pass_by_reference = true;
+      return true;
+    }
+  return false;
+}
+
+/* Return true if ARG, 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)
+reference_callee_copied (CUMULATIVE_ARGS *ca, const function_arg_info &arg)
 {
-  if (type && TREE_ADDRESSABLE (type))
+  if (arg.type && TREE_ADDRESSABLE (arg.type))
     return false;
-  return targetm.calls.callee_copies (pack_cumulative_args (ca), mode, type,
-                                     named_arg);
+  return targetm.calls.callee_copies (pack_cumulative_args (ca), arg);
 }
 
 
@@ -1019,8 +1041,8 @@ precompute_register_parameters (int num_actuals, struct arg_data *args,
 static rtx
 save_fixed_argument_area (int reg_parm_stack_space, rtx argblock, int *low_to_save, int *high_to_save)
 {
-  int low;
-  int high;
+  unsigned int low;
+  unsigned int high;
 
   /* Compute the boundary of the area that needs to be saved, if any.  */
   high = reg_parm_stack_space;
@@ -1031,7 +1053,7 @@ save_fixed_argument_area (int reg_parm_stack_space, rtx argblock, int *low_to_sa
     high = highest_outgoing_arg_in_use;
 
   for (low = 0; low < high; low++)
-    if (stack_usage_map[low] != 0)
+    if (stack_usage_map[low] != 0 || low >= stack_usage_watermark)
       {
        int num_to_save;
        machine_mode save_mode;
@@ -1156,7 +1178,7 @@ store_unaligned_arguments_into_pseudos (struct arg_data *args, int num_actuals)
 #ifdef BLOCK_REG_PADDING
            && (BLOCK_REG_PADDING (args[i].mode,
                                   TREE_TYPE (args[i].tree_value), 1)
-               == downward)
+               == PAD_DOWNWARD)
 #else
            && BYTES_BIG_ENDIAN
 #endif
@@ -1203,77 +1225,34 @@ static GTY(()) tree alloc_object_size_limit;
 static tree
 alloc_max_size (void)
 {
-  if (!alloc_object_size_limit)
-    {
-      alloc_object_size_limit = TYPE_MAX_VALUE (ssizetype);
+  if (alloc_object_size_limit)
+    return alloc_object_size_limit;
 
-      if (warn_alloc_size_limit)
-       {
-         char *end = NULL;
-         errno = 0;
-         unsigned HOST_WIDE_INT unit = 1;
-         unsigned HOST_WIDE_INT limit
-           = strtoull (warn_alloc_size_limit, &end, 10);
+  HOST_WIDE_INT limit = warn_alloc_size_limit;
+  if (limit == HOST_WIDE_INT_MAX)
+    limit = tree_to_shwi (TYPE_MAX_VALUE (ptrdiff_type_node));
 
-         if (!errno)
-           {
-             if (end && *end)
-               {
-                 /* Numeric option arguments are at most INT_MAX.  Make it
-                    possible to specify a larger value by accepting common
-                    suffixes.  */
-                 if (!strcmp (end, "kB"))
-                   unit = 1000;
-                 else if (!strcasecmp (end, "KiB") || strcmp (end, "KB"))
-                   unit = 1024;
-                 else if (!strcmp (end, "MB"))
-                   unit = HOST_WIDE_INT_UC (1000) * 1000;
-                 else if (!strcasecmp (end, "MiB"))
-                   unit = HOST_WIDE_INT_UC (1024) * 1024;
-                 else if (!strcasecmp (end, "GB"))
-                   unit = HOST_WIDE_INT_UC (1000) * 1000 * 1000;
-                 else if (!strcasecmp (end, "GiB"))
-                   unit = HOST_WIDE_INT_UC (1024) * 1024 * 1024;
-                 else if (!strcasecmp (end, "TB"))
-                   unit = HOST_WIDE_INT_UC (1000) * 1000 * 1000 * 1000;
-                 else if (!strcasecmp (end, "TiB"))
-                   unit = HOST_WIDE_INT_UC (1024) * 1024 * 1024 * 1024;
-                 else if (!strcasecmp (end, "PB"))
-                   unit = HOST_WIDE_INT_UC (1000) * 1000 * 1000 * 1000 * 1000;
-                 else if (!strcasecmp (end, "PiB"))
-                   unit = HOST_WIDE_INT_UC (1024) * 1024 * 1024 * 1024 * 1024;
-                 else if (!strcasecmp (end, "EB"))
-                   unit = HOST_WIDE_INT_UC (1000) * 1000 * 1000 * 1000 * 1000
-                          * 1000;
-                 else if (!strcasecmp (end, "EiB"))
-                   unit = HOST_WIDE_INT_UC (1024) * 1024 * 1024 * 1024 * 1024
-                          * 1024;
-                 else
-                   unit = 0;
-               }
+  alloc_object_size_limit = build_int_cst (size_type_node, limit);
 
-             if (unit)
-               {
-                 wide_int w = wi::uhwi (limit, HOST_BITS_PER_WIDE_INT + 64);
-                 w *= unit;
-                 if (wi::ltu_p (w, alloc_object_size_limit))
-                   alloc_object_size_limit = wide_int_to_tree (ssizetype, w);
-               }
-           }
-       }
-    }
   return alloc_object_size_limit;
 }
 
 /* Return true when EXP's range can be determined and set RANGE[] to it
-   after adjusting it if necessary to make EXP a valid size argument to
-   an allocation function declared with attribute alloc_size (whose
-   argument may be signed), or to a string manipulation function like
-   memset.  */
+   after adjusting it if necessary to make EXP a represents a valid size
+   of object, or a valid size argument to an allocation function declared
+   with attribute alloc_size (whose argument may be signed), or to a string
+   manipulation function like memset.  When ALLOW_ZERO is true, allow
+   returning a range of [0, 0] for a size in an anti-range [1, N] where
+   N > PTRDIFF_MAX.  A zero range is a (nearly) invalid argument to
+   allocation functions like malloc but it is a valid argument to
+   functions like memset.  */
 
 bool
-get_size_range (tree exp, tree range[2])
+get_size_range (tree exp, tree range[2], bool allow_zero /* = false */)
 {
+  if (!exp)
+    return false;
+
   if (tree_fits_uhwi_p (exp))
     {
       /* EXP is a constant.  */
@@ -1281,23 +1260,34 @@ get_size_range (tree exp, tree range[2])
       return true;
     }
 
+  tree exptype = TREE_TYPE (exp);
+  bool integral = INTEGRAL_TYPE_P (exptype);
+
   wide_int min, max;
-  enum value_range_type range_type
-    = ((TREE_CODE (exp) == SSA_NAME && INTEGRAL_TYPE_P (TREE_TYPE (exp)))
-       ? get_range_info (exp, &min, &max) : VR_VARYING);
+  enum value_range_kind range_type;
+
+  if (integral)
+    range_type = determine_value_range (exp, &min, &max);
+  else
+    range_type = VR_VARYING;
 
   if (range_type == VR_VARYING)
     {
-      /* No range information available.  */
+      if (integral)
+       {
+         /* Use the full range of the type of the expression when
+            no value range information is available.  */
+         range[0] = TYPE_MIN_VALUE (exptype);
+         range[1] = TYPE_MAX_VALUE (exptype);
+         return true;
+       }
+
       range[0] = NULL_TREE;
       range[1] = NULL_TREE;
       return false;
     }
 
-  tree exptype = TREE_TYPE (exp);
   unsigned expprec = TYPE_PRECISION (exptype);
-  wide_int wzero = wi::zero (expprec);
-  wide_int wmaxval = wide_int (TYPE_MAX_VALUE (exptype));
 
   bool signed_p = !TYPE_UNSIGNED (exptype);
 
@@ -1305,7 +1295,7 @@ get_size_range (tree exp, tree range[2])
     {
       if (signed_p)
        {
-         if (wi::les_p (max, wzero))
+         if (wi::les_p (max, 0))
            {
              /* EXP is not in a strictly negative range.  That means
                 it must be in some (not necessarily strictly) positive
@@ -1313,37 +1303,42 @@ get_size_range (tree exp, tree range[2])
                 conversions negative values end up converted to large
                 positive values, and otherwise they are not valid sizes,
                 the resulting range is in both cases [0, TYPE_MAX].  */
-             min = wzero;
-             max = wmaxval;
+             min = wi::zero (expprec);
+             max = wi::to_wide (TYPE_MAX_VALUE (exptype));
            }
-         else if (wi::les_p (min - 1, wzero))
+         else if (wi::les_p (min - 1, 0))
            {
              /* EXP is not in a negative-positive range.  That means EXP
                 is either negative, or greater than max.  Since negative
                 sizes are invalid make the range [MAX + 1, TYPE_MAX].  */
              min = max + 1;
-             max = wmaxval;
+             max = wi::to_wide (TYPE_MAX_VALUE (exptype));
            }
          else
            {
              max = min - 1;
-             min = wzero;
+             min = wi::zero (expprec);
            }
        }
-      else if (wi::eq_p (wzero, min - 1))
+      else if (wi::eq_p (0, min - 1))
        {
          /* EXP is unsigned and not in the range [1, MAX].  That means
             it's either zero or greater than MAX.  Even though 0 would
-            normally be detected by -Walloc-zero set the range to
-            [MAX, TYPE_MAX] so that when MAX is greater than the limit
-            the whole range is diagnosed.  */
-         min = max + 1;
-         max = wmaxval;
+            normally be detected by -Walloc-zero, unless ALLOW_ZERO
+            is true, set the range to [MAX, TYPE_MAX] so that when MAX
+            is greater than the limit the whole range is diagnosed.  */
+         if (allow_zero)
+           min = max = wi::zero (expprec);
+         else
+           {
+             min = max + 1;
+             max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+           }
        }
       else
        {
          max = min - 1;
-         min = wzero;
+         min = wi::zero (expprec);
        }
     }
 
@@ -1356,9 +1351,10 @@ get_size_range (tree exp, tree range[2])
 /* Diagnose a call EXP to function FN decorated with attribute alloc_size
    whose argument numbers given by IDX with values given by ARGS exceed
    the maximum object size or cause an unsigned oveflow (wrapping) when
-   multiplied.  When ARGS[0] is null the function does nothing.  ARGS[1]
-   may be null for functions like malloc, and non-null for those like
-   calloc that are decorated with a two-argument attribute alloc_size.  */
+   multiplied.  FN is null when EXP is a call via a function pointer.
+   When ARGS[0] is null the function does nothing.  ARGS[1] may be null
+   for functions like malloc, and non-null for those like calloc that
+   are decorated with a two-argument attribute alloc_size.  */
 
 void
 maybe_warn_alloc_args_overflow (tree fn, tree exp, tree args[2], int idx[2])
@@ -1371,6 +1367,7 @@ maybe_warn_alloc_args_overflow (tree fn, tree exp, tree args[2], int idx[2])
 
   location_t loc = EXPR_LOCATION (exp);
 
+  tree fntype = fn ? TREE_TYPE (fn) : TREE_TYPE (TREE_TYPE (exp));
   bool warned = false;
 
   /* Validate each argument individually.  */
@@ -1396,11 +1393,10 @@ maybe_warn_alloc_args_overflow (tree fn, tree exp, tree args[2], int idx[2])
                 friends.
                 Also avoid issuing the warning for calls to function named
                 "alloca".  */
-             if ((DECL_FUNCTION_CODE (fn) == BUILT_IN_ALLOCA
-                  && IDENTIFIER_LENGTH (DECL_NAME (fn)) != 6)
-                 || (DECL_FUNCTION_CODE (fn) != BUILT_IN_ALLOCA
-                     && !lookup_attribute ("returns_nonnull",
-                                           TYPE_ATTRIBUTES (TREE_TYPE (fn)))))
+             if (fn && fndecl_built_in_p (fn, BUILT_IN_ALLOCA)
+                 ? IDENTIFIER_LENGTH (DECL_NAME (fn)) != 6
+                 : !lookup_attribute ("returns_nonnull",
+                                      TYPE_ATTRIBUTES (fntype)))
                warned = warning_at (loc, OPT_Walloc_zero,
                                     "%Kargument %i value is zero",
                                     exp, idx[i] + 1);
@@ -1412,9 +1408,10 @@ maybe_warn_alloc_args_overflow (tree fn, tree exp, tree args[2], int idx[2])
                 size overflow.  There's no good way to detect C++98 here
                 so avoid diagnosing these calls for all C++ modes.  */
              if (i == 0
+                 && fn
                  && !args[1]
                  && lang_GNU_CXX ()
-                 && DECL_IS_OPERATOR_NEW (fn)
+                 && DECL_IS_OPERATOR_NEW_P (fn)
                  && integer_all_onesp (args[i]))
                continue;
 
@@ -1465,7 +1462,7 @@ maybe_warn_alloc_args_overflow (tree fn, tree exp, tree args[2], int idx[2])
       wide_int x = wi::to_wide (argrange[0][0], szprec);
       wide_int y = wi::to_wide (argrange[1][0], szprec);
 
-      bool vflow;
+      wi::overflow_type vflow;
       wide_int prod = wi::umul (x, y, &vflow);
 
       if (vflow)
@@ -1495,7 +1492,7 @@ maybe_warn_alloc_args_overflow (tree fn, tree exp, tree args[2], int idx[2])
        }
     }
 
-  if (warned)
+  if (warned && fn)
     {
       location_t fnloc = DECL_SOURCE_LOCATION (fn);
 
@@ -1508,6 +1505,353 @@ maybe_warn_alloc_args_overflow (tree fn, tree exp, tree args[2], int idx[2])
     }
 }
 
+/* If EXPR refers to a character array or pointer declared attribute
+   nonstring return a decl for that array or pointer and set *REF to
+   the referenced enclosing object or pointer.  Otherwise returns
+   null.  */
+
+tree
+get_attr_nonstring_decl (tree expr, tree *ref)
+{
+  tree decl = expr;
+  tree var = NULL_TREE;
+  if (TREE_CODE (decl) == SSA_NAME)
+    {
+      gimple *def = SSA_NAME_DEF_STMT (decl);
+
+      if (is_gimple_assign (def))
+       {
+         tree_code code = gimple_assign_rhs_code (def);
+         if (code == ADDR_EXPR
+             || code == COMPONENT_REF
+             || code == VAR_DECL)
+           decl = gimple_assign_rhs1 (def);
+       }
+      else
+       var = SSA_NAME_VAR (decl);
+    }
+
+  if (TREE_CODE (decl) == ADDR_EXPR)
+    decl = TREE_OPERAND (decl, 0);
+
+  /* To simplify calling code, store the referenced DECL regardless of
+     the attribute determined below, but avoid storing the SSA_NAME_VAR
+     obtained above (it's not useful for dataflow purposes).  */
+  if (ref)
+    *ref = decl;
+
+  /* Use the SSA_NAME_VAR that was determined above to see if it's
+     declared nonstring.  Otherwise drill down into the referenced
+     DECL.  */
+  if (var)
+    decl = var;
+  else if (TREE_CODE (decl) == ARRAY_REF)
+    decl = TREE_OPERAND (decl, 0);
+  else if (TREE_CODE (decl) == COMPONENT_REF)
+    decl = TREE_OPERAND (decl, 1);
+  else if (TREE_CODE (decl) == MEM_REF)
+    return get_attr_nonstring_decl (TREE_OPERAND (decl, 0), ref);
+
+  if (DECL_P (decl)
+      && lookup_attribute ("nonstring", DECL_ATTRIBUTES (decl)))
+    return decl;
+
+  return NULL_TREE;
+}
+
+/* Warn about passing a non-string array/pointer to a function that
+   expects a nul-terminated string argument.  */
+
+void
+maybe_warn_nonstring_arg (tree fndecl, tree exp)
+{
+  if (!fndecl || !fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
+    return;
+
+  if (TREE_NO_WARNING (exp) || !warn_stringop_overflow)
+    return;
+
+  /* Avoid clearly invalid calls (more checking done below).  */
+  unsigned nargs = call_expr_nargs (exp);
+  if (!nargs)
+    return;
+
+  /* The bound argument to a bounded string function like strncpy.  */
+  tree bound = NULL_TREE;
+
+  /* The longest known or possible string argument to one of the comparison
+     functions.  If the length is less than the bound it is used instead.
+     Since the length is only used for warning and not for code generation
+     disable strict mode in the calls to get_range_strlen below.  */
+  tree maxlen = NULL_TREE;
+
+  /* It's safe to call "bounded" string functions with a non-string
+     argument since the functions provide an explicit bound for this
+     purpose.  The exception is strncat where the bound may refer to
+     either the destination or the source.  */
+  int fncode = DECL_FUNCTION_CODE (fndecl);
+  switch (fncode)
+    {
+    case BUILT_IN_STRCMP:
+    case BUILT_IN_STRNCMP:
+    case BUILT_IN_STRNCASECMP:
+      {
+       /* For these, if one argument refers to one or more of a set
+          of string constants or arrays of known size, determine
+          the range of their known or possible lengths and use it
+          conservatively as the bound for the unbounded function,
+          and to adjust the range of the bound of the bounded ones.  */
+       for (unsigned argno = 0;
+            argno < MIN (nargs, 2)
+              && !(maxlen && TREE_CODE (maxlen) == INTEGER_CST); argno++)
+         {
+           tree arg = CALL_EXPR_ARG (exp, argno);
+           if (!get_attr_nonstring_decl (arg))
+             {
+               c_strlen_data lendata = { };
+               /* Set MAXBOUND to an arbitrary non-null non-integer
+                  node as a request to have it set to the length of
+                  the longest string in a PHI.  */
+               lendata.maxbound = arg;
+               get_range_strlen (arg, &lendata, /* eltsize = */ 1);
+               maxlen = lendata.maxbound;
+             }
+         }
+      }
+      /* Fall through.  */
+
+    case BUILT_IN_STRNCAT:
+    case BUILT_IN_STPNCPY:
+    case BUILT_IN_STRNCPY:
+      if (nargs > 2)
+       bound = CALL_EXPR_ARG (exp, 2);
+      break;
+
+    case BUILT_IN_STRNDUP:
+      if (nargs > 1)
+       bound = CALL_EXPR_ARG (exp, 1);
+      break;
+
+    case BUILT_IN_STRNLEN:
+      {
+       tree arg = CALL_EXPR_ARG (exp, 0);
+       if (!get_attr_nonstring_decl (arg))
+         {
+           c_strlen_data lendata = { };
+           /* Set MAXBOUND to an arbitrary non-null non-integer
+              node as a request to have it set to the length of
+              the longest string in a PHI.  */
+           lendata.maxbound = arg;
+           get_range_strlen (arg, &lendata, /* eltsize = */ 1);
+           maxlen = lendata.maxbound;
+         }
+       if (nargs > 1)
+         bound = CALL_EXPR_ARG (exp, 1);
+       break;
+      }
+
+    default:
+      break;
+    }
+
+  /* Determine the range of the bound argument (if specified).  */
+  tree bndrng[2] = { NULL_TREE, NULL_TREE };
+  if (bound)
+    {
+      STRIP_NOPS (bound);
+      get_size_range (bound, bndrng);
+    }
+
+  location_t loc = EXPR_LOCATION (exp);
+
+  if (bndrng[0])
+    {
+      /* Diagnose excessive bound prior the adjustment below and
+        regardless of attribute nonstring.  */
+      tree maxobjsize = max_object_size ();
+      if (tree_int_cst_lt (maxobjsize, bndrng[0]))
+       {
+         if (tree_int_cst_equal (bndrng[0], bndrng[1]))
+           warning_at (loc, OPT_Wstringop_overflow_,
+                       "%K%qD specified bound %E "
+                       "exceeds maximum object size %E",
+                       exp, fndecl, bndrng[0], maxobjsize);
+         else
+           warning_at (loc, OPT_Wstringop_overflow_,
+                       "%K%qD specified bound [%E, %E] "
+                       "exceeds maximum object size %E",
+                       exp, fndecl, bndrng[0], bndrng[1], maxobjsize);
+         return;
+       }
+    }
+
+  if (maxlen && !integer_all_onesp (maxlen))
+    {
+      /* Add one for the nul.  */
+      maxlen = const_binop (PLUS_EXPR, TREE_TYPE (maxlen), maxlen,
+                           size_one_node);
+
+      if (!bndrng[0])
+       {
+         /* Conservatively use the upper bound of the lengths for
+            both the lower and the upper bound of the operation.  */
+         bndrng[0] = maxlen;
+         bndrng[1] = maxlen;
+         bound = void_type_node;
+       }
+      else if (maxlen)
+       {
+         /* Replace the bound on the operation with the upper bound
+            of the length of the string if the latter is smaller.  */
+         if (tree_int_cst_lt (maxlen, bndrng[0]))
+           bndrng[0] = maxlen;
+         else if (tree_int_cst_lt (maxlen, bndrng[1]))
+           bndrng[1] = maxlen;
+       }
+    }
+
+  /* Iterate over the built-in function's formal arguments and check
+     each const char* against the actual argument.  If the actual
+     argument is declared attribute non-string issue a warning unless
+     the argument's maximum length is bounded.  */
+  function_args_iterator it;
+  function_args_iter_init (&it, TREE_TYPE (fndecl));
+
+  for (unsigned argno = 0; ; ++argno, function_args_iter_next (&it))
+    {
+      /* Avoid iterating past the declared argument in a call
+        to function declared without a prototype.  */
+      if (argno >= nargs)
+       break;
+
+      tree argtype = function_args_iter_cond (&it);
+      if (!argtype)
+       break;
+
+      if (TREE_CODE (argtype) != POINTER_TYPE)
+       continue;
+
+      argtype = TREE_TYPE (argtype);
+
+      if (TREE_CODE (argtype) != INTEGER_TYPE
+         || !TYPE_READONLY (argtype))
+       continue;
+
+      argtype = TYPE_MAIN_VARIANT (argtype);
+      if (argtype != char_type_node)
+       continue;
+
+      tree callarg = CALL_EXPR_ARG (exp, argno);
+      if (TREE_CODE (callarg) == ADDR_EXPR)
+       callarg = TREE_OPERAND (callarg, 0);
+
+      /* See if the destination is declared with attribute "nonstring".  */
+      tree decl = get_attr_nonstring_decl (callarg);
+      if (!decl)
+       continue;
+
+      /* The maximum number of array elements accessed.  */
+      offset_int wibnd = 0;
+
+      if (argno && fncode == BUILT_IN_STRNCAT)
+       {
+         /* See if the bound in strncat is derived from the length
+            of the strlen of the destination (as it's expected to be).
+            If so, reset BOUND and FNCODE to trigger a warning.  */
+         tree dstarg = CALL_EXPR_ARG (exp, 0);
+         if (is_strlen_related_p (dstarg, bound))
+           {
+             /* The bound applies to the destination, not to the source,
+                so reset these to trigger a warning without mentioning
+                the bound.  */
+             bound = NULL;
+             fncode = 0;
+           }
+         else if (bndrng[1])
+           /* Use the upper bound of the range for strncat.  */
+           wibnd = wi::to_offset (bndrng[1]);
+       }
+      else if (bndrng[0])
+       /* Use the lower bound of the range for functions other than
+          strncat.  */
+       wibnd = wi::to_offset (bndrng[0]);
+
+      /* Determine the size of the argument array if it is one.  */
+      offset_int asize = wibnd;
+      bool known_size = false;
+      tree type = TREE_TYPE (decl);
+
+      /* Determine the array size.  For arrays of unknown bound and
+        pointers reset BOUND to trigger the appropriate warning.  */
+      if (TREE_CODE (type) == ARRAY_TYPE)
+       {
+         if (tree arrbnd = TYPE_DOMAIN (type))
+           {
+             if ((arrbnd = TYPE_MAX_VALUE (arrbnd)))
+               {
+                 asize = wi::to_offset (arrbnd) + 1;
+                 known_size = true;
+               }
+           }
+         else if (bound == void_type_node)
+           bound = NULL_TREE;
+       }
+      else if (bound == void_type_node)
+       bound = NULL_TREE;
+
+      /* In a call to strncat with a bound in a range whose lower but
+        not upper bound is less than the array size, reset ASIZE to
+        be the same as the bound and the other variable to trigger
+        the apprpriate warning below.  */
+      if (fncode == BUILT_IN_STRNCAT
+         && bndrng[0] != bndrng[1]
+         && wi::ltu_p (wi::to_offset (bndrng[0]), asize)
+         && (!known_size
+             || wi::ltu_p (asize, wibnd)))
+       {
+         asize = wibnd;
+         bound = NULL_TREE;
+         fncode = 0;
+       }
+
+      bool warned = false;
+
+      auto_diagnostic_group d;
+      if (wi::ltu_p (asize, wibnd))
+       {
+         if (bndrng[0] == bndrng[1])
+           warned = warning_at (loc, OPT_Wstringop_overflow_,
+                                "%qD argument %i declared attribute "
+                                "%<nonstring%> is smaller than the specified "
+                                "bound %wu",
+                                fndecl, argno + 1, wibnd.to_uhwi ());
+         else if (wi::ltu_p (asize, wi::to_offset (bndrng[0])))
+           warned = warning_at (loc, OPT_Wstringop_overflow_,
+                                "%qD argument %i declared attribute "
+                                "%<nonstring%> is smaller than "
+                                "the specified bound [%E, %E]",
+                                fndecl, argno + 1, bndrng[0], bndrng[1]);
+         else
+           warned = warning_at (loc, OPT_Wstringop_overflow_,
+                                "%qD argument %i declared attribute "
+                                "%<nonstring%> may be smaller than "
+                                "the specified bound [%E, %E]",
+                                fndecl, argno + 1, bndrng[0], bndrng[1]);
+       }
+      else if (fncode == BUILT_IN_STRNCAT)
+       ; /* Avoid warning for calls to strncat() when the bound
+            is equal to the size of the non-string argument.  */
+      else if (!bound)
+       warned = warning_at (loc, OPT_Wstringop_overflow_,
+                            "%qD argument %i declared attribute %<nonstring%>",
+                            fndecl, argno + 1);
+
+      if (warned)
+       inform (DECL_SOURCE_LOCATION (decl),
+               "argument %qD declared here", decl);
+    }
+}
+
 /* Issue an error if CALL_EXPR was flagged as requiring
    tall-call optimization.  */
 
@@ -1521,6 +1865,310 @@ maybe_complain_about_tail_call (tree call_expr, const char *reason)
   error_at (EXPR_LOCATION (call_expr), "cannot tail-call: %s", reason);
 }
 
+/* Used to define rdwr_map below.  */
+struct rdwr_access_hash: int_hash<int, -1> { };
+
+/* A mapping between argument number corresponding to attribute access
+   mode (read_only, write_only, or read_write) and operands.  */
+typedef hash_map<rdwr_access_hash, attr_access> rdwr_map;
+
+/* Initialize a mapping for a call to function FNDECL declared with
+   attribute access.  Each attribute positional operand inserts one
+   entry into the mapping with the operand number as the key.  */
+
+static void
+init_attr_rdwr_indices (rdwr_map *rwm, tree fntype)
+{
+  if (!fntype)
+    return;
+
+  for (tree access = TYPE_ATTRIBUTES (fntype);
+       (access = lookup_attribute ("access", access));
+       access = TREE_CHAIN (access))
+    {
+      /* The TREE_VALUE of an attribute is a TREE_LIST whose TREE_VALUE
+        is the attribute argument's value.  */
+      tree mode = TREE_VALUE (access);
+      gcc_assert (TREE_CODE (mode) == TREE_LIST);
+      mode = TREE_VALUE (mode);
+      gcc_assert (TREE_CODE (mode) == STRING_CST);
+
+      const char *modestr = TREE_STRING_POINTER (mode);
+      for (const char *m = modestr; *m; )
+       {
+         attr_access acc = { };
+
+         switch (*m)
+           {
+           case 'r': acc.mode = acc.read_only; break;
+           case 'w': acc.mode = acc.write_only; break;
+           default: acc.mode = acc.read_write; break;
+           }
+
+         char *end;
+         acc.ptrarg = strtoul (++m, &end, 10);
+         m = end;
+         if (*m == ',')
+           {
+             acc.sizarg = strtoul (++m, &end, 10);
+             m = end;
+           }
+         else
+           acc.sizarg = UINT_MAX;
+
+         acc.ptr = NULL_TREE;
+         acc.size = NULL_TREE;
+
+         /* Unconditionally add an entry for the required pointer
+            operand of the attribute, and one for the optional size
+            operand when it's specified.  */
+         rwm->put (acc.ptrarg, acc);
+         if (acc.sizarg != UINT_MAX)
+           rwm->put (acc.sizarg, acc);
+       }
+    }
+}
+
+/* Returns the type of the argument ARGNO to function with type FNTYPE
+   or null when the typoe cannot be determined or no such argument exists.  */
+
+static tree
+fntype_argno_type (tree fntype, unsigned argno)
+{
+  if (!prototype_p (fntype))
+    return NULL_TREE;
+
+  tree argtype;
+  function_args_iterator it;
+  FOREACH_FUNCTION_ARGS (fntype, argtype, it)
+    if (argno-- == 0)
+      return argtype;
+
+  return NULL_TREE;
+}
+
+/* Helper to append the "rdwr" attribute specification described
+   by ACCESS to the array ATTRSTR with size STRSIZE.  Used in
+   diagnostics.  */
+
+static inline void
+append_attrname (const std::pair<int, attr_access> &access,
+                char *attrstr, size_t strsize)
+{
+  /* Append the relevant attribute to the string.  This (deliberately)
+     appends the attribute pointer operand even when none was specified.  */
+  size_t len = strlen (attrstr);
+
+  const char *atname
+    = (access.second.mode == attr_access::read_only
+       ? "read_only"
+       : (access.second.mode == attr_access::write_only
+         ? "write_only" : "read_write"));
+
+  const char *sep = len ? ", " : "";
+
+  if (access.second.sizarg == UINT_MAX)
+    snprintf (attrstr + len, strsize - len,
+             "%s%s (%i)", sep, atname,
+             access.second.ptrarg + 1);
+  else
+    snprintf (attrstr + len, strsize - len,
+             "%s%s (%i, %i)", sep, atname,
+             access.second.ptrarg + 1, access.second.sizarg + 1);
+}
+
+/* Iterate over attribute access read-only, read-write, and write-only
+   arguments and diagnose past-the-end accesses and related problems
+   in the function call EXP.  */
+
+static void
+maybe_warn_rdwr_sizes (rdwr_map *rwm, tree exp)
+{
+  tree fndecl = NULL_TREE;
+  tree fntype = NULL_TREE;
+  if (tree fnaddr = CALL_EXPR_FN (exp))
+    {
+      if (TREE_CODE (fnaddr) == ADDR_EXPR)
+       {
+         fndecl = TREE_OPERAND (fnaddr, 0);
+         fntype = TREE_TYPE (fndecl);
+       }
+      else
+       fntype = TREE_TYPE (TREE_TYPE (fnaddr));
+    }
+
+  if (!fntype)
+    return;
+
+  /* A string describing the attributes that the warnings issued by this
+     function apply to.  Used to print one informational note per function
+     call, rather than one per warning.  That reduces clutter.  */
+  char attrstr[80];
+  attrstr[0] = 0;
+
+  for (rdwr_map::iterator it = rwm->begin (); it != rwm->end (); ++it)
+    {
+      std::pair<int, attr_access> access = *it;
+
+      /* Get the function call arguments corresponding to the attribute's
+        positional arguments.  When both arguments have been specified
+        there will be two entries in *RWM, one for each.  They are
+        cross-referenced by their respective argument numbers in
+        ACCESS.PTRARG and ACCESS.SIZARG.  */
+      const int ptridx = access.second.ptrarg;
+      const int sizidx = access.second.sizarg;
+
+      gcc_assert (ptridx != -1);
+      gcc_assert (access.first == ptridx || access.first == sizidx);
+
+      /* The pointer is set to null for the entry corresponding to
+        the size argument.  Skip it.  It's handled when the entry
+        corresponding to the pointer argument comes up.  */
+      if (!access.second.ptr)
+       continue;
+
+      tree argtype = fntype_argno_type (fntype, ptridx);
+      argtype = TREE_TYPE (argtype);
+
+      tree size;
+      if (sizidx == -1)
+       {
+         /* If only the pointer attribute operand was specified
+            and not size, set SIZE to the size of one element of
+            the pointed to type to detect smaller objects (null
+            pointers are diagnosed in this case only if
+            the pointer is also declared with attribute nonnull.  */
+         size = size_one_node;
+       }
+      else
+       size = rwm->get (sizidx)->size;
+
+      tree ptr = access.second.ptr;
+      tree sizrng[2] = { size_zero_node, build_all_ones_cst (sizetype) };
+      if (get_size_range (size, sizrng, true)
+         && tree_int_cst_sgn (sizrng[0]) < 0
+         && tree_int_cst_sgn (sizrng[1]) < 0)
+       {
+         /* Warn about negative sizes.  */
+         bool warned = false;
+         location_t loc = EXPR_LOCATION (exp);
+         if (tree_int_cst_equal (sizrng[0], sizrng[1]))
+           warned = warning_at (loc, OPT_Wstringop_overflow_,
+                                "%Kargument %i value %E is negative",
+                                exp, sizidx + 1, size);
+         else
+           warned = warning_at (loc, OPT_Wstringop_overflow_,
+                                "%Kargument %i range [%E, %E] is negative",
+                                exp, sizidx + 1, sizrng[0], sizrng[1]);
+         if (warned)
+           {
+             append_attrname (access, attrstr, sizeof attrstr);
+             /* Avoid warning again for the same attribute.  */
+             continue;
+           }
+       }
+
+      if (tree_int_cst_sgn (sizrng[0]) >= 0)
+       {
+         if (COMPLETE_TYPE_P (argtype))
+           {
+             /* Multiple SIZE by the size of the type the pointer
+                argument points to.  If it's incomplete the size
+                is used as is.  */
+             size = NULL_TREE;
+             if (tree argsize = TYPE_SIZE_UNIT (argtype))
+               if (TREE_CODE (argsize) == INTEGER_CST)
+                 {
+                   const int prec = TYPE_PRECISION (sizetype);
+                   wide_int minsize = wi::to_wide (sizrng[0], prec);
+                   minsize *= wi::to_wide (argsize, prec);
+                   size = wide_int_to_tree (sizetype, minsize);
+                 }
+           }
+       }
+      else
+       size = NULL_TREE;
+
+      if (sizidx >= 0
+         && integer_zerop (ptr)
+         && tree_int_cst_sgn (sizrng[0]) > 0)
+       {
+         /* Warn about null pointers with positive sizes.  This is
+            different from also declaring the pointer argument with
+            attribute nonnull when the function accepts null pointers
+            only when the corresponding size is zero.  */
+         bool warned = false;
+         location_t loc = EXPR_LOCATION (exp);
+         if (tree_int_cst_equal (sizrng[0], sizrng[1]))
+           warned = warning_at (loc, OPT_Wnonnull,
+                                "%Kargument %i is null but the corresponding "
+                                "size argument %i value is %E",
+                                exp, ptridx + 1, sizidx + 1, size);
+         else
+           warned = warning_at (loc, OPT_Wnonnull,
+                                "%Kargument %i is null but the corresponding "
+                                "size argument %i range is [%E, %E]",
+                                exp, ptridx + 1, sizidx + 1,
+                                sizrng[0], sizrng[1]);
+         if (warned)
+           {
+             append_attrname (access, attrstr, sizeof attrstr);
+             /* Avoid warning again for the same attribute.  */
+             continue;
+           }
+       }
+
+      tree objsize = compute_objsize (ptr, 0);
+
+      tree srcsize;
+      if (access.second.mode == attr_access::write_only)
+       {
+         /* For a write-only argument there is no source.  */
+         srcsize = NULL_TREE;
+       }
+      else
+       {
+         /* For read-only and read-write attributes also set the source
+            size.  */
+         srcsize = objsize;
+         if (access.second.mode == attr_access::read_only)
+           {
+             /* For a read-only attribute there is no destination so
+                clear OBJSIZE.  This emits "reading N bytes" kind of
+                diagnostics instead of the "writing N bytes" kind.  */
+             objsize = NULL_TREE;
+           }
+       }
+
+      /* Clear the no-warning bit in case it was set in a prior
+        iteration so that accesses via different arguments are
+        diagnosed.  */
+      TREE_NO_WARNING (exp) = false;
+      check_access (exp, NULL_TREE, NULL_TREE, size, /*maxread=*/ NULL_TREE,
+                   srcsize, objsize);
+
+      if (TREE_NO_WARNING (exp))
+       /* If check_access issued a warning above, append the relevant
+          attribute to the string.  */
+       append_attrname (access, attrstr, sizeof attrstr);
+    }
+
+  if (!*attrstr)
+    return;
+
+  if (fndecl)
+    inform (DECL_SOURCE_LOCATION (fndecl),
+           "in a call to function %qD declared with attribute %qs",
+           fndecl, attrstr);
+  else
+    inform (EXPR_LOCATION (fndecl),
+           "in a call with type %qT and attribute %qs",
+           fntype, attrstr);
+
+  /* Set the bit in case if was cleared and not set above.  */
+  TREE_NO_WARNING (exp) = true;
+}
+
 /* Fill in ARGS_SIZE and ARGS array based on the parameters found in
    CALL_EXPR EXP.
 
@@ -1560,7 +2208,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
                                 tree fndecl, tree fntype,
                                 cumulative_args_t args_so_far,
                                 int reg_parm_stack_space,
-                                rtx *old_stack_level, int *old_pending_adj,
+                                rtx *old_stack_level,
+                                poly_int64_pod *old_pending_adj,
                                 int *must_preallocate, int *ecf_flags,
                                 bool *may_tailcall, bool call_from_thunk_p)
 {
@@ -1582,7 +2231,7 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
 
   i = num_actuals - 1;
   {
-    int j = i, ptr_arg = -1;
+    int j = i;
     call_expr_arg_iterator iter;
     tree arg;
     bitmap slots = NULL;
@@ -1591,79 +2240,12 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
       {
        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--;
-         }
       }
     argpos = 0;
     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 (CALL_WITH_BOUNDS_P (exp)
-                && pass_by_reference (NULL, TYPE_MODE (argtype), argtype,
-                                      argpos < n_named_args))
-         {
-           if (slots)
-             BITMAP_FREE (slots);
-           ptr_arg = j;
-         }
-       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
@@ -1686,14 +2268,13 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
 
   bitmap_obstack_release (NULL);
 
-  /* Extract attribute alloc_size and if set, store the indices of
-     the corresponding arguments in ALLOC_IDX, and then the actual
-     argument(s) at those indices in ALLOC_ARGS.  */
+  /* Extract attribute alloc_size from the type of the called expression
+     (which could be a function or a function pointer) and if set, store
+     the indices of the corresponding arguments in ALLOC_IDX, and then
+     the actual argument(s) at those indices in ALLOC_ARGS.  */
   int alloc_idx[2] = { -1, -1 };
-  if (tree alloc_size
-      = (fndecl ? lookup_attribute ("alloc_size",
-                                   TYPE_ATTRIBUTES (TREE_TYPE (fndecl)))
-        : NULL_TREE))
+  if (tree alloc_size = lookup_attribute ("alloc_size",
+                                         TYPE_ATTRIBUTES (fntype)))
     {
       tree args = TREE_VALUE (alloc_size);
       alloc_idx[0] = TREE_INT_CST_LOW (TREE_VALUE (args)) - 1;
@@ -1704,12 +2285,16 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
   /* Array for up to the two attribute alloc_size arguments.  */
   tree alloc_args[] = { NULL_TREE, NULL_TREE };
 
+  /* Map of attribute read_only, write_only, or read_write specifications
+     for function arguments.  */
+  rdwr_map rdwr_idx;
+  init_attr_rdwr_indices (&rdwr_idx, fntype);
+
   /* 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;
-      machine_mode mode;
 
       /* Replace erroneous argument with constant zero.  */
       if (type == error_mark_node || !COMPLETE_TYPE_P (type))
@@ -1718,8 +2303,7 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
       /* If TYPE is a transparent union or record, pass things the way
         we would pass the first field of the union or record.  We have
         already verified that the modes are the same.  */
-      if ((TREE_CODE (type) == UNION_TYPE || TREE_CODE (type) == RECORD_TYPE)
-          && TYPE_TRANSPARENT_AGGR (type))
+      if (RECORD_OR_UNION_TYPE_P (type) && TYPE_TRANSPARENT_AGGR (type))
        type = TREE_TYPE (first_field (type));
 
       /* Decide where to pass this arg.
@@ -1737,15 +2321,13 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
         with those made by function.c.  */
 
       /* See if this argument should be passed by invisible reference.  */
-      if (pass_by_reference (args_so_far_pnt, TYPE_MODE (type),
-                            type, argpos < n_named_args))
+      function_arg_info arg (type, argpos < n_named_args);
+      if (pass_by_reference (args_so_far_pnt, arg))
        {
          bool callee_copies;
          tree base = NULL_TREE;
 
-         callee_copies
-           = reference_callee_copied (args_so_far_pnt, TYPE_MODE (type),
-                                      type, argpos < n_named_args);
+         callee_copies = reference_callee_copied (args_so_far_pnt, arg);
 
          /* If we're compiling a thunk, pass through invisible references
             instead of making a copy.  */
@@ -1784,7 +2366,7 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
                  *may_tailcall = false;
                  maybe_complain_about_tail_call (exp,
                                                  "a callee-copied argument is"
-                                                 " stored in the current "
+                                                 " stored in the current"
                                                  " function's frame");
                }
 
@@ -1824,6 +2406,8 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
                  copy = allocate_dynamic_stack_space (size_rtx,
                                                       TYPE_ALIGN (type),
                                                       TYPE_ALIGN (type),
+                                                      max_int_size_in_bytes
+                                                      (type),
                                                       true);
                  copy = gen_rtx_MEM (BLKmode, copy);
                  set_mem_attributes (copy, type, 1);
@@ -1853,40 +2437,38 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
                                              "argument must be passed"
                                              " by copying");
            }
+         arg.pass_by_reference = true;
        }
 
       unsignedp = TYPE_UNSIGNED (type);
-      mode = promote_function_mode (type, TYPE_MODE (type), &unsignedp,
-                                   fndecl ? TREE_TYPE (fndecl) : fntype, 0);
+      arg.type = type;
+      arg.mode
+       = promote_function_mode (type, TYPE_MODE (type), &unsignedp,
+                                fndecl ? TREE_TYPE (fndecl) : fntype, 0);
 
       args[i].unsignedp = unsignedp;
-      args[i].mode = mode;
+      args[i].mode = arg.mode;
 
-      args[i].reg = targetm.calls.function_arg (args_so_far, mode, type,
-                                               argpos < n_named_args);
+      targetm.calls.warn_parameter_passing_abi (args_so_far, type);
+
+      args[i].reg = targetm.calls.function_arg (args_so_far, arg);
 
       if (args[i].reg && CONST_INT_P (args[i].reg))
-       {
-         args[i].special_slot = args[i].reg;
-         args[i].reg = NULL;
-       }
+       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.  */
       if (targetm.calls.function_incoming_arg != targetm.calls.function_arg)
        args[i].tail_call_reg
-         = targetm.calls.function_incoming_arg (args_so_far, mode, type,
-                                                argpos < n_named_args);
+         = targetm.calls.function_incoming_arg (args_so_far, arg);
       else
        args[i].tail_call_reg = args[i].reg;
 
       if (args[i].reg)
-       args[i].partial
-         = targetm.calls.arg_partial_bytes (args_so_far, mode, type,
-                                            argpos < n_named_args);
+       args[i].partial = targetm.calls.arg_partial_bytes (args_so_far, arg);
 
-      args[i].pass_on_stack = targetm.calls.must_pass_in_stack (mode, type);
+      args[i].pass_on_stack = targetm.calls.must_pass_in_stack (arg);
 
       /* If FUNCTION_ARG returned a (parallel [(expr_list (nil) ...) ...]),
         it means that we are to pass this arg in the register(s) designated
@@ -1904,14 +2486,11 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
          || (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.  */
-      else if (args[i].reg == 0 || args[i].partial != 0
+      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,
+       locate_and_pad_parm (arg.mode, type,
 #ifdef STACK_PARMS_IN_REG_PARM_AREA
                             1,
 #else
@@ -1925,7 +2504,7 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
        /* The argument is passed entirely in registers.  See at which
           end it should be padded.  */
        args[i].locate.where_pad =
-         BLOCK_REG_PADDING (mode, type,
+         BLOCK_REG_PADDING (arg.mode, type,
                             int_size_in_bytes (type) <= UNITS_PER_WORD);
 #endif
 
@@ -1938,8 +2517,12 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
       /* Increment ARGS_SO_FAR, which has info about which arg-registers
         have been used, etc.  */
 
-      targetm.calls.function_arg_advance (args_so_far, TYPE_MODE (type),
-                                         type, argpos < n_named_args);
+      /* ??? Traditionally we've passed TYPE_MODE here, instead of the
+        promoted_mode used for function_arg above.  However, the
+        corresponding handling of incoming arguments in function.c
+        does pass the promoted mode.  */
+      arg.mode = TYPE_MODE (type);
+      targetm.calls.function_arg_advance (args_so_far, arg);
 
       /* Store argument values for functions decorated with attribute
         alloc_size.  */
@@ -1947,6 +2530,22 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
        alloc_args[0] = args[i].tree_value;
       else if (argpos == alloc_idx[1])
        alloc_args[1] = args[i].tree_value;
+
+      /* Save the actual argument that corresponds to the access attribute
+        operand for later processing.  */
+      if (attr_access *access = rdwr_idx.get (argpos))
+       {
+         if (POINTER_TYPE_P (type))
+           {
+             access->ptr = args[i].tree_value;
+             gcc_assert (access->size == NULL_TREE);
+           }
+         else
+           {
+             access->size = args[i].tree_value;
+             gcc_assert (access->ptr == NULL_TREE);
+           }
+       }
     }
 
   if (alloc_args[0])
@@ -1955,6 +2554,13 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
         alloc_size.  */
       maybe_warn_alloc_args_overflow (fndecl, exp, alloc_args, alloc_idx);
     }
+
+  /* Detect passing non-string arguments to functions expecting
+     nul-terminated strings.  */
+  maybe_warn_nonstring_arg (fndecl, exp);
+
+  /* Check read_only, write_only, and read_write arguments.  */
+  maybe_warn_rdwr_sizes (&rdwr_idx, exp);
 }
 
 /* Update ARGS_SIZE to contain the total size for the argument block.
@@ -1963,14 +2569,14 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
    REG_PARM_STACK_SPACE holds the number of bytes of stack space reserved
    for arguments passed in registers.  */
 
-static int
+static poly_int64
 compute_argument_block_size (int reg_parm_stack_space,
                             struct args_size *args_size,
                             tree fndecl ATTRIBUTE_UNUSED,
                             tree fntype ATTRIBUTE_UNUSED,
                             int preferred_stack_boundary ATTRIBUTE_UNUSED)
 {
-  int unadjusted_args_size = args_size->constant;
+  poly_int64 unadjusted_args_size = args_size->constant;
 
   /* For accumulate outgoing args mode we don't need to align, since the frame
      will be already aligned.  Align to STACK_BOUNDARY in order to prevent
@@ -1993,7 +2599,8 @@ compute_argument_block_size (int reg_parm_stack_space,
          /* We don't handle this case yet.  To handle it correctly we have
             to add the delta, round and subtract the delta.
             Currently no machine description requires this support.  */
-         gcc_assert (!(stack_pointer_delta & (preferred_stack_boundary - 1)));
+         gcc_assert (multiple_p (stack_pointer_delta,
+                                 preferred_stack_boundary));
          args_size->var = round_up (args_size->var, preferred_stack_boundary);
        }
 
@@ -2016,15 +2623,13 @@ compute_argument_block_size (int reg_parm_stack_space,
       preferred_stack_boundary /= BITS_PER_UNIT;
       if (preferred_stack_boundary < 1)
        preferred_stack_boundary = 1;
-      args_size->constant = (((args_size->constant
-                              + stack_pointer_delta
-                              + preferred_stack_boundary - 1)
-                             / preferred_stack_boundary
-                             * preferred_stack_boundary)
+      args_size->constant = (aligned_upper_bound (args_size->constant
+                                                 + stack_pointer_delta,
+                                                 preferred_stack_boundary)
                             - stack_pointer_delta);
 
-      args_size->constant = MAX (args_size->constant,
-                                reg_parm_stack_space);
+      args_size->constant = upper_bound (args_size->constant,
+                                        reg_parm_stack_space);
 
       if (! OUTGOING_REG_PARM_STACK_SPACE ((!fndecl ? fntype : TREE_TYPE (fndecl))))
        args_size->constant -= reg_parm_stack_space;
@@ -2129,7 +2734,7 @@ finalize_must_preallocate (int must_preallocate, int num_actuals,
   if (! must_preallocate)
     {
       int partial_seen = 0;
-      int copy_to_evaluate_size = 0;
+      poly_int64 copy_to_evaluate_size = 0;
       int i;
 
       for (i = 0; i < num_actuals && ! must_preallocate; i++)
@@ -2138,12 +2743,6 @@ finalize_must_preallocate (int must_preallocate, int num_actuals,
            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
@@ -2154,8 +2753,8 @@ finalize_must_preallocate (int must_preallocate, int num_actuals,
              += int_size_in_bytes (TREE_TYPE (args[i].tree_value));
        }
 
-      if (copy_to_evaluate_size * 2 >= args_size->constant
-         && args_size->constant > 0)
+      if (maybe_ne (args_size->constant, 0)
+         && maybe_ge (copy_to_evaluate_size * 2, args_size->constant))
        must_preallocate = 1;
     }
   return must_preallocate;
@@ -2175,10 +2774,14 @@ compute_argument_addresses (struct arg_data *args, rtx argblock, int num_actuals
   if (argblock)
     {
       rtx arg_reg = argblock;
-      int i, arg_offset = 0;
+      int i;
+      poly_int64 arg_offset = 0;
 
       if (GET_CODE (argblock) == PLUS)
-       arg_reg = XEXP (argblock, 0), arg_offset = INTVAL (XEXP (argblock, 1));
+       {
+         arg_reg = XEXP (argblock, 0);
+         arg_offset = rtx_to_poly_int64 (XEXP (argblock, 1));
+       }
 
       for (i = 0; i < num_actuals; i++)
        {
@@ -2186,7 +2789,7 @@ compute_argument_addresses (struct arg_data *args, rtx argblock, int num_actuals
          rtx slot_offset = ARGS_SIZE_RTX (args[i].locate.slot_offset);
          rtx addr;
          unsigned int align, boundary;
-         unsigned int units_on_stack = 0;
+         poly_uint64 units_on_stack = 0;
          machine_mode partial_mode = VOIDmode;
 
          /* Skip this parm if it will not be passed on the stack.  */
@@ -2195,15 +2798,10 @@ compute_argument_addresses (struct arg_data *args, rtx argblock, int num_actuals
              && args[i].partial == 0)
            continue;
 
-         /* Pointer Bounds are never passed on the stack.  */
-         if (POINTER_BOUNDS_P (args[i].tree_value))
+         if (TYPE_EMPTY_P (TREE_TYPE (args[i].tree_value)))
            continue;
 
-         if (CONST_INT_P (offset))
-           addr = plus_constant (Pmode, arg_reg, INTVAL (offset));
-         else
-           addr = gen_rtx_PLUS (Pmode, arg_reg, offset);
-
+         addr = simplify_gen_binary (PLUS, Pmode, arg_reg, offset);
          addr = plus_constant (Pmode, addr, arg_offset);
 
          if (args[i].partial != 0)
@@ -2211,8 +2809,8 @@ compute_argument_addresses (struct arg_data *args, rtx argblock, int num_actuals
              /* Only part of the parameter is being passed on the stack.
                 Generate a simple memory reference of the correct size.  */
              units_on_stack = args[i].locate.size.constant;
-             partial_mode = mode_for_size (units_on_stack * BITS_PER_UNIT,
-                                           MODE_INT, 1);
+             poly_uint64 bits_on_stack = units_on_stack * BITS_PER_UNIT;
+             partial_mode = int_mode_for_size (bits_on_stack, 1).else_blk ();
              args[i].stack = gen_rtx_MEM (partial_mode, addr);
              set_mem_size (args[i].stack, units_on_stack);
            }
@@ -2224,20 +2822,20 @@ compute_argument_addresses (struct arg_data *args, rtx argblock, int num_actuals
            }
          align = BITS_PER_UNIT;
          boundary = args[i].locate.boundary;
-         if (args[i].locate.where_pad != downward)
+         poly_int64 offset_val;
+         if (args[i].locate.where_pad != PAD_DOWNWARD)
            align = boundary;
-         else if (CONST_INT_P (offset))
+         else if (poly_int_rtx_p (offset, &offset_val))
            {
-             align = INTVAL (offset) * BITS_PER_UNIT | boundary;
-             align = least_bit_hwi (align);
+             align = least_bit_hwi (boundary);
+             unsigned int offset_align
+               = known_alignment (offset_val) * BITS_PER_UNIT;
+             if (offset_align != 0)
+               align = MIN (align, offset_align);
            }
          set_mem_align (args[i].stack, align);
 
-         if (CONST_INT_P (slot_offset))
-           addr = plus_constant (Pmode, arg_reg, INTVAL (slot_offset));
-         else
-           addr = gen_rtx_PLUS (Pmode, arg_reg, slot_offset);
-
+         addr = simplify_gen_binary (PLUS, Pmode, arg_reg, slot_offset);
          addr = plus_constant (Pmode, addr, arg_offset);
 
          if (args[i].partial != 0)
@@ -2298,6 +2896,17 @@ rtx_for_function_call (tree fndecl, tree addr)
   return funexp;
 }
 
+/* Return the static chain for this function, if any.  */
+
+rtx
+rtx_for_static_chain (const_tree fndecl_or_type, bool incoming_p)
+{
+  if (DECL_P (fndecl_or_type) && !DECL_STATIC_CHAIN (fndecl_or_type))
+    return NULL;
+
+  return targetm.calls.static_chain (fndecl_or_type, incoming_p);
+}
+
 /* Internal state for internal_arg_pointer_based_exp and its helpers.  */
 static struct
 {
@@ -2376,12 +2985,13 @@ internal_arg_pointer_based_exp (const_rtx rtl, bool toplevel)
   if (REG_P (rtl) && HARD_REGISTER_P (rtl))
     return NULL_RTX;
 
-  if (GET_CODE (rtl) == PLUS && CONST_INT_P (XEXP (rtl, 1)))
+  poly_int64 offset;
+  if (GET_CODE (rtl) == PLUS && poly_int_rtx_p (XEXP (rtl, 1), &offset))
     {
       rtx val = internal_arg_pointer_based_exp (XEXP (rtl, 0), toplevel);
       if (val == NULL_RTX || val == pc_rtx)
        return val;
-      return plus_constant (Pmode, val, INTVAL (XEXP (rtl, 1)));
+      return plus_constant (Pmode, val, offset);
     }
 
   /* When called at the topmost level, scan pseudo assignments in between the
@@ -2412,45 +3022,53 @@ internal_arg_pointer_based_exp (const_rtx rtl, bool toplevel)
   return NULL_RTX;
 }
 
-/* Return true if and only if SIZE storage units (usually bytes)
-   starting from address ADDR overlap with already clobbered argument
-   area.  This function is used to determine if we should give up a
-   sibcall.  */
+/* Return true if SIZE bytes starting from address ADDR might overlap an
+   already-clobbered argument area.  This function is used to determine
+   if we should give up a sibcall.  */
 
 static bool
-mem_overlaps_already_clobbered_arg_p (rtx addr, unsigned HOST_WIDE_INT size)
+mem_might_overlap_already_clobbered_arg_p (rtx addr, poly_uint64 size)
 {
-  HOST_WIDE_INT i;
+  poly_int64 i;
+  unsigned HOST_WIDE_INT start, end;
   rtx val;
 
-  if (bitmap_empty_p (stored_args_map))
+  if (bitmap_empty_p (stored_args_map)
+      && stored_args_watermark == HOST_WIDE_INT_M1U)
     return false;
   val = internal_arg_pointer_based_exp (addr, true);
   if (val == NULL_RTX)
     return false;
-  else if (val == pc_rtx)
+  else if (!poly_int_rtx_p (val, &i))
     return true;
-  else
-    i = INTVAL (val);
+
+  if (known_eq (size, 0U))
+    return false;
 
   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;
+  /* We can ignore any references to the function's pretend args,
+     which at this point would manifest as negative values of I.  */
+  if (known_le (i, 0) && known_le (size, poly_uint64 (-i)))
+    return false;
 
-      for (k = 0; k < size; k++)
-       if (i + k < SBITMAP_SIZE (stored_args_map)
-           && bitmap_bit_p (stored_args_map, i + k))
-         return true;
-    }
+  start = maybe_lt (i, 0) ? 0 : constant_lower_bound (i);
+  if (!(i + size).is_constant (&end))
+    end = HOST_WIDE_INT_M1U;
+
+  if (end > stored_args_watermark)
+    return true;
+
+  end = MIN (end, SBITMAP_SIZE (stored_args_map));
+  for (unsigned HOST_WIDE_INT k = start; k < end; ++k)
+    if (bitmap_bit_p (stored_args_map, k))
+      return true;
 
   return false;
 }
@@ -2480,8 +3098,12 @@ load_register_parameters (struct arg_data *args, int num_actuals,
        {
          int partial = args[i].partial;
          int nregs;
-         int size = 0;
+         poly_int64 size = 0;
+         HOST_WIDE_INT const_size = 0;
          rtx_insn *before_arg = get_last_insn ();
+         tree type = TREE_TYPE (args[i].tree_value);
+         if (RECORD_OR_UNION_TYPE_P (type) && TYPE_TRANSPARENT_AGGR (type))
+           type = TREE_TYPE (first_field (type));
          /* Set non-negative if we must move a word at a time, even if
             just one word (e.g, partial == 4 && mode == DFmode).  Set
             to -1 if we just use a normal move insn.  This value can be
@@ -2494,10 +3116,14 @@ load_register_parameters (struct arg_data *args, int num_actuals,
              gcc_assert (partial % UNITS_PER_WORD == 0);
              nregs = partial / UNITS_PER_WORD;
            }
-         else if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode)
+         else if (TYPE_MODE (type) == BLKmode)
            {
-             size = int_size_in_bytes (TREE_TYPE (args[i].tree_value));
-             nregs = (size + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD;
+             /* Variable-sized parameters should be described by a
+                PARALLEL instead.  */
+             const_size = int_size_in_bytes (type);
+             gcc_assert (const_size >= 0);
+             nregs = (const_size + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD;
+             size = const_size;
            }
          else
            size = GET_MODE_SIZE (args[i].mode);
@@ -2519,21 +3145,27 @@ load_register_parameters (struct arg_data *args, int num_actuals,
              /* Handle case where we have a value that needs shifting
                 up to the msb.  eg. a QImode value and we're padding
                 upward on a BYTES_BIG_ENDIAN machine.  */
-             if (size < UNITS_PER_WORD
-                 && (args[i].locate.where_pad
-                     == (BYTES_BIG_ENDIAN ? upward : downward)))
+             if (args[i].locate.where_pad
+                 == (BYTES_BIG_ENDIAN ? PAD_UPWARD : PAD_DOWNWARD))
                {
-                 rtx x;
-                 int shift = (UNITS_PER_WORD - size) * BITS_PER_UNIT;
-
-                 /* Assigning REG here rather than a temp makes CALL_FUSAGE
-                    report the whole reg as used.  Strictly speaking, the
-                    call only uses SIZE bytes at the msb end, but it doesn't
-                    seem worth generating rtl to say that.  */
-                 reg = gen_rtx_REG (word_mode, REGNO (reg));
-                 x = expand_shift (LSHIFT_EXPR, word_mode, reg, shift, reg, 1);
-                 if (x != reg)
-                   emit_move_insn (reg, x);
+                 gcc_checking_assert (ordered_p (size, UNITS_PER_WORD));
+                 if (maybe_lt (size, UNITS_PER_WORD))
+                   {
+                     rtx x;
+                     poly_int64 shift
+                       = (UNITS_PER_WORD - size) * BITS_PER_UNIT;
+
+                     /* Assigning REG here rather than a temp makes
+                        CALL_FUSAGE report the whole reg as used.
+                        Strictly speaking, the call only uses SIZE
+                        bytes at the msb end, but it doesn't seem worth
+                        generating rtl to say that.  */
+                     reg = gen_rtx_REG (word_mode, REGNO (reg));
+                     x = expand_shift (LSHIFT_EXPR, word_mode,
+                                       reg, shift, reg, 1);
+                     if (x != reg)
+                       emit_move_insn (reg, x);
+                   }
                }
 #endif
            }
@@ -2548,17 +3180,20 @@ load_register_parameters (struct arg_data *args, int num_actuals,
 
          else if (partial == 0 || args[i].pass_on_stack)
            {
+             /* SIZE and CONST_SIZE are 0 for partial arguments and
+                the size of a BLKmode type otherwise.  */
+             gcc_checking_assert (known_eq (size, const_size));
              rtx mem = validize_mem (copy_rtx (args[i].value));
 
              /* Check for overlap with already clobbered argument area,
                 providing that this has non-zero size.  */
              if (is_sibcall
-                 && size != 0
-                 && (mem_overlaps_already_clobbered_arg_p
-                     (XEXP (args[i].value, 0), size)))
+                 && const_size != 0
+                 && (mem_might_overlap_already_clobbered_arg_p
+                     (XEXP (args[i].value, 0), const_size)))
                *sibcall_failure = 1;
 
-             if (size % UNITS_PER_WORD == 0
+             if (const_size % UNITS_PER_WORD == 0
                  || MEM_ALIGN (mem) % BITS_PER_WORD == 0)
                move_block_to_reg (REGNO (reg), mem, nregs, args[i].mode);
              else
@@ -2568,7 +3203,7 @@ load_register_parameters (struct arg_data *args, int num_actuals,
                                       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;
+                 unsigned int bitsize = const_size * BITS_PER_UNIT - bitoff;
                  rtx x = extract_bit_field (mem, bitsize, bitoff, 1, dest,
                                             word_mode, word_mode, false,
                                             NULL);
@@ -2580,16 +3215,16 @@ load_register_parameters (struct arg_data *args, int num_actuals,
                }
 
              /* Handle a BLKmode that needs shifting.  */
-             if (nregs == 1 && size < UNITS_PER_WORD
+             if (nregs == 1 && const_size < UNITS_PER_WORD
 #ifdef BLOCK_REG_PADDING
-                 && args[i].locate.where_pad == downward
+                 && args[i].locate.where_pad == PAD_DOWNWARD
 #else
                  && BYTES_BIG_ENDIAN
 #endif
                  )
                {
                  rtx dest = gen_rtx_REG (word_mode, REGNO (reg));
-                 int shift = (UNITS_PER_WORD - size) * BITS_PER_UNIT;
+                 int shift = (UNITS_PER_WORD - const_size) * BITS_PER_UNIT;
                  enum tree_code dir = (BYTES_BIG_ENDIAN
                                        ? RSHIFT_EXPR : LSHIFT_EXPR);
                  rtx x;
@@ -2612,8 +3247,7 @@ load_register_parameters (struct arg_data *args, int num_actuals,
          if (GET_CODE (reg) == PARALLEL)
            use_group_regs (call_fusage, reg);
          else if (nregs == -1)
-           use_reg_mode (call_fusage, reg,
-                         TYPE_MODE (TREE_TYPE (args[i].tree_value)));
+           use_reg_mode (call_fusage, reg, TYPE_MODE (type));
          else if (nregs > 0)
            use_regs (call_fusage, REGNO (reg), nregs);
        }
@@ -2623,27 +3257,32 @@ load_register_parameters (struct arg_data *args, int num_actuals,
 /* We need to pop PENDING_STACK_ADJUST bytes.  But, if the arguments
    wouldn't fill up an even multiple of PREFERRED_UNIT_STACK_BOUNDARY
    bytes, then we would need to push some additional bytes to pad the
-   arguments.  So, we compute an adjust to the stack pointer for an
+   arguments.  So, we try to compute an adjust to the stack pointer for an
    amount that will leave the stack under-aligned by UNADJUSTED_ARGS_SIZE
    bytes.  Then, when the arguments are pushed the stack will be perfectly
-   aligned.  ARGS_SIZE->CONSTANT is set to the number of bytes that should
-   be popped after the call.  Returns the adjustment.  */
+   aligned.
 
-static int
-combine_pending_stack_adjustment_and_call (int unadjusted_args_size,
+   Return true if this optimization is possible, storing the adjustment
+   in ADJUSTMENT_OUT and setting ARGS_SIZE->CONSTANT to the number of
+   bytes that should be popped after the call.  */
+
+static bool
+combine_pending_stack_adjustment_and_call (poly_int64_pod *adjustment_out,
+                                          poly_int64 unadjusted_args_size,
                                           struct args_size *args_size,
                                           unsigned int preferred_unit_stack_boundary)
 {
   /* The number of bytes to pop so that the stack will be
      under-aligned by UNADJUSTED_ARGS_SIZE bytes.  */
-  HOST_WIDE_INT adjustment;
+  poly_int64 adjustment;
   /* The alignment of the stack after the arguments are pushed, if we
      just pushed the arguments without adjust the stack here.  */
   unsigned HOST_WIDE_INT unadjusted_alignment;
 
-  unadjusted_alignment
-    = ((stack_pointer_delta + unadjusted_args_size)
-       % preferred_unit_stack_boundary);
+  if (!known_misalignment (stack_pointer_delta + unadjusted_args_size,
+                          preferred_unit_stack_boundary,
+                          &unadjusted_alignment))
+    return false;
 
   /* We want to get rid of as many of the PENDING_STACK_ADJUST bytes
      as possible -- leaving just enough left to cancel out the
@@ -2652,15 +3291,24 @@ combine_pending_stack_adjustment_and_call (int unadjusted_args_size,
      -UNADJUSTED_ALIGNMENT modulo the PREFERRED_UNIT_STACK_BOUNDARY.  */
 
   /* Begin by trying to pop all the bytes.  */
-  unadjusted_alignment
-    = (unadjusted_alignment
-       - (pending_stack_adjust % preferred_unit_stack_boundary));
+  unsigned HOST_WIDE_INT tmp_misalignment;
+  if (!known_misalignment (pending_stack_adjust,
+                          preferred_unit_stack_boundary,
+                          &tmp_misalignment))
+    return false;
+  unadjusted_alignment -= tmp_misalignment;
   adjustment = pending_stack_adjust;
   /* Push enough additional bytes that the stack will be aligned
      after the arguments are pushed.  */
   if (preferred_unit_stack_boundary > 1 && unadjusted_alignment)
     adjustment -= preferred_unit_stack_boundary - unadjusted_alignment;
 
+  /* We need to know whether the adjusted argument size
+     (UNADJUSTED_ARGS_SIZE - ADJUSTMENT) constitutes an allocation
+     or a deallocation.  */
+  if (!ordered_p (adjustment, unadjusted_args_size))
+    return false;
+
   /* Now, sets ARGS_SIZE->CONSTANT so that we pop the right number of
      bytes after the call.  The right number is the entire
      PENDING_STACK_ADJUST less our ADJUSTMENT plus the amount required
@@ -2668,7 +3316,8 @@ combine_pending_stack_adjustment_and_call (int unadjusted_args_size,
   args_size->constant
     = pending_stack_adjust - adjustment + unadjusted_args_size;
 
-  return adjustment;
+  *adjustment_out = adjustment;
+  return true;
 }
 
 /* Scan X expression if it does not dereference any argument slots
@@ -2694,8 +3343,8 @@ check_sibcall_argument_overlap_1 (rtx x)
     return 0;
 
   if (code == MEM)
-    return mem_overlaps_already_clobbered_arg_p (XEXP (x, 0),
-                                                GET_MODE_SIZE (GET_MODE (x)));
+    return (mem_might_overlap_already_clobbered_arg_p
+           (XEXP (x, 0), GET_MODE_SIZE (GET_MODE (x))));
 
   /* Scan all subexpressions.  */
   fmt = GET_RTX_FORMAT (code);
@@ -2727,7 +3376,8 @@ static int
 check_sibcall_argument_overlap (rtx_insn *insn, struct arg_data *arg,
                                int mark_stored_args_map)
 {
-  int low, high;
+  poly_uint64 low, high;
+  unsigned HOST_WIDE_INT const_low, const_high;
 
   if (insn == NULL_RTX)
     insn = get_insns ();
@@ -2745,9 +3395,14 @@ check_sibcall_argument_overlap (rtx_insn *insn, struct arg_data *arg,
        low = -arg->locate.slot_offset.constant - arg->locate.size.constant;
       else
        low = arg->locate.slot_offset.constant;
+      high = low + arg->locate.size.constant;
 
-      for (high = low + arg->locate.size.constant; low < high; low++)
-       bitmap_set_bit (stored_args_map, low);
+      const_low = constant_lower_bound (low);
+      if (high.is_constant (&const_high))
+       for (unsigned HOST_WIDE_INT i = const_low; i < const_high; ++i)
+         bitmap_set_bit (stored_args_map, i);
+      else
+       stored_args_watermark = MIN (stored_args_watermark, const_low);
     }
   return insn != NULL_RTX;
 }
@@ -2759,18 +3414,19 @@ check_sibcall_argument_overlap (rtx_insn *insn, struct arg_data *arg,
 bool
 shift_return_value (machine_mode mode, bool left_p, rtx value)
 {
-  HOST_WIDE_INT shift;
-
   gcc_assert (REG_P (value) && HARD_REGISTER_P (value));
-  shift = GET_MODE_BITSIZE (GET_MODE (value)) - GET_MODE_BITSIZE (mode);
-  if (shift == 0)
+  machine_mode value_mode = GET_MODE (value);
+  poly_int64 shift = GET_MODE_BITSIZE (value_mode) - GET_MODE_BITSIZE (mode);
+
+  if (known_eq (shift, 0))
     return false;
 
   /* Use ashr rather than lshr for right shifts.  This is for the benefit
      of the MIPS port, which requires SImode values to be sign-extended
      when stored in 64-bit registers.  */
-  if (!force_expand_binop (GET_MODE (value), left_p ? ashl_optab : ashr_optab,
-                          value, GEN_INT (shift), value, 1, OPTAB_WIDEN))
+  if (!force_expand_binop (value_mode, left_p ? ashl_optab : ashr_optab,
+                          value, gen_int_shift_amount (value_mode, shift),
+                          value, 1, OPTAB_WIDEN))
     gcc_unreachable ();
   return true;
 }
@@ -2833,7 +3489,7 @@ can_implement_as_sibling_call_p (tree exp,
     }
 
 #ifdef REG_PARM_STACK_SPACE
-  /* If outgoing reg parm stack space changes, we can not do sibcall.  */
+  /* If outgoing reg parm stack space changes, we cannot do sibcall.  */
   if (OUTGOING_REG_PARM_STACK_SPACE (funtype)
       != OUTGOING_REG_PARM_STACK_SPACE (TREE_TYPE (current_function_decl))
       || (reg_parm_stack_space != REG_PARM_STACK_SPACE (current_function_decl)))
@@ -2888,7 +3544,8 @@ can_implement_as_sibling_call_p (tree exp,
      function, we cannot change it into a sibling call.
      crtl->args.pretend_args_size is not part of the
      stack allocated by our caller.  */
-  if (args_size.constant > (crtl->args.size - crtl->args.pretend_args_size))
+  if (maybe_gt (args_size.constant,
+               crtl->args.size - crtl->args.pretend_args_size))
     {
       maybe_complain_about_tail_call (exp,
                                      "callee required more stack slots"
@@ -2898,10 +3555,12 @@ can_implement_as_sibling_call_p (tree exp,
 
   /* If the callee pops its own arguments, then it must pop exactly
      the same number of arguments as the current function.  */
-  if (targetm.calls.return_pops_args (fndecl, funtype, args_size.constant)
-      != targetm.calls.return_pops_args (current_function_decl,
-                                        TREE_TYPE (current_function_decl),
-                                        crtl->args.size))
+  if (maybe_ne (targetm.calls.return_pops_args (fndecl, funtype,
+                                               args_size.constant),
+               targetm.calls.return_pops_args (current_function_decl,
+                                               TREE_TYPE
+                                               (current_function_decl),
+                                               crtl->args.size)))
     {
       maybe_complain_about_tail_call (exp,
                                      "inconsistent number of"
@@ -2920,6 +3579,19 @@ can_implement_as_sibling_call_p (tree exp,
   return true;
 }
 
+/* Update stack alignment when the parameter is passed in the stack
+   since the outgoing parameter requires extra alignment on the calling
+   function side. */
+
+static void
+update_stack_alignment_for_call (struct locate_and_pad_arg_data *locate)
+{
+  if (crtl->stack_alignment_needed < locate->boundary)
+    crtl->stack_alignment_needed = locate->boundary;
+  if (crtl->preferred_stack_boundary < locate->boundary)
+    crtl->preferred_stack_boundary = locate->boundary;
+}
+
 /* Generate all the code for a CALL_EXPR exp
    and return an rtx for its value.
    Store the value in TARGET (specified as an rtx) if convenient.
@@ -2954,8 +3626,6 @@ expand_call (tree exp, rtx target, int ignore)
   /* 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;
@@ -2968,7 +3638,7 @@ expand_call (tree exp, rtx target, int ignore)
   /* Size of aggregate value wanted, or zero if none wanted
      or if we are using the non-reentrant PCC calling convention
      or expecting the value in registers.  */
-  HOST_WIDE_INT struct_value_size = 0;
+  poly_int64 struct_value_size = 0;
   /* Nonzero if called function returns an aggregate in memory PCC style,
      by returning the address of where to find it.  */
   int pcc_struct_value = 0;
@@ -2991,7 +3661,7 @@ expand_call (tree exp, rtx target, int ignore)
   struct args_size args_size;
   struct args_size adjusted_args_size;
   /* Size of arguments before any adjustments (such as rounding).  */
-  int unadjusted_args_size;
+  poly_int64 unadjusted_args_size;
   /* Data on reg parms scanned so far.  */
   CUMULATIVE_ARGS args_so_far_v;
   cumulative_args_t args_so_far;
@@ -3024,22 +3694,23 @@ expand_call (tree exp, rtx target, int ignore)
   rtx save_area = 0;           /* Place that it is saved */
 #endif
 
-  int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
+  unsigned int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
   char *initial_stack_usage_map = stack_usage_map;
+  unsigned HOST_WIDE_INT initial_stack_usage_watermark = stack_usage_watermark;
   char *stack_usage_map_buf = NULL;
 
-  int old_stack_allocated;
+  poly_int64 old_stack_allocated;
 
   /* State variables to track stack modifications.  */
   rtx old_stack_level = 0;
   int old_stack_arg_under_construction = 0;
-  int old_pending_adj = 0;
+  poly_int64 old_pending_adj = 0;
   int old_inhibit_defer_pop = inhibit_defer_pop;
 
   /* Some stack pointer alterations we make are performed via
      allocate_dynamic_stack_space. This modifies the stack_pointer_delta,
      which we then also need to save/restore along the way.  */
-  int old_stack_pointer_delta = 0;
+  poly_int64 old_stack_pointer_delta = 0;
 
   rtx call_fusage;
   tree addr = CALL_EXPR_FN (exp);
@@ -3129,16 +3800,22 @@ expand_call (tree exp, rtx target, int ignore)
       }
 #else /* not PCC_STATIC_STRUCT_RETURN */
       {
-       struct_value_size = int_size_in_bytes (rettype);
+       if (!poly_int_tree_p (TYPE_SIZE_UNIT (rettype), &struct_value_size))
+         struct_value_size = -1;
 
        /* Even if it is semantically safe to use the target as the return
           slot, it may be not sufficiently aligned for the return type.  */
        if (CALL_EXPR_RETURN_SLOT_OPT (exp)
            && target
            && MEM_P (target)
-           && !(MEM_ALIGN (target) < TYPE_ALIGN (rettype)
-                && SLOW_UNALIGNED_ACCESS (TYPE_MODE (rettype),
-                                          MEM_ALIGN (target))))
+           /* If rettype is addressable, we may not create a temporary.
+              If target is properly aligned at runtime and the compiler
+              just doesn't know about it, it will work fine, otherwise it
+              will be UB.  */
+           && (TREE_ADDRESSABLE (rettype)
+               || !(MEM_ALIGN (target) < TYPE_ALIGN (rettype)
+                    && targetm.slow_unaligned_access (TYPE_MODE (rettype),
+                                                      MEM_ALIGN (target)))))
          structure_value_addr = XEXP (target, 0);
        else
          {
@@ -3216,7 +3893,7 @@ expand_call (tree exp, rtx target, int ignore)
 
       structure_value_addr_value =
        make_tree (build_pointer_type (TREE_TYPE (funtype)), temp);
-      structure_value_addr_parm = CALL_WITH_BOUNDS_P (exp) ? 2 : 1;
+      structure_value_addr_parm = 1;
     }
 
   /* Count the arguments and set NUM_ACTUALS.  */
@@ -3303,20 +3980,42 @@ expand_call (tree exp, rtx target, int ignore)
          || reg_mentioned_p (virtual_outgoing_args_rtx,
                              structure_value_addr))
       && (args_size.var
-         || (!ACCUMULATE_OUTGOING_ARGS && args_size.constant)))
+         || (!ACCUMULATE_OUTGOING_ARGS
+             && maybe_ne (args_size.constant, 0))))
     structure_value_addr = copy_to_reg (structure_value_addr);
 
   /* Tail calls can make things harder to debug, and we've traditionally
      pushed these optimizations into -O2.  Don't try if we're already
      expanding a call, as that means we're an argument.  Don't try if
      there's cleanups, as we know there's code to follow the call.  */
-
   if (currently_expanding_call++ != 0
-      || !flag_optimize_sibling_calls
+      || (!flag_optimize_sibling_calls && !CALL_FROM_THUNK_P (exp))
       || args_size.var
       || dbg_cnt (tail_call) == false)
     try_tail_call = 0;
 
+  /* Workaround buggy C/C++ wrappers around Fortran routines with
+     character(len=constant) arguments if the hidden string length arguments
+     are passed on the stack; if the callers forget to pass those arguments,
+     attempting to tail call in such routines leads to stack corruption.
+     Avoid tail calls in functions where at least one such hidden string
+     length argument is passed (partially or fully) on the stack in the
+     caller and the callee needs to pass any arguments on the stack.
+     See PR90329.  */
+  if (try_tail_call && maybe_ne (args_size.constant, 0))
+    for (tree arg = DECL_ARGUMENTS (current_function_decl);
+        arg; arg = DECL_CHAIN (arg))
+      if (DECL_HIDDEN_STRING_LENGTH (arg) && DECL_INCOMING_RTL (arg))
+       {
+         subrtx_iterator::array_type array;
+         FOR_EACH_SUBRTX (iter, array, DECL_INCOMING_RTL (arg), NONCONST)
+           if (MEM_P (*iter))
+             {
+               try_tail_call = 0;
+               break;
+             }
+       }
+
   /* If the user has marked the function as requiring tail-call
      optimization, attempt it.  */
   if (must_tail_call)
@@ -3357,8 +4056,7 @@ expand_call (tree exp, rtx target, int ignore)
              || ((caller_mode != caller_promoted_mode
                   || callee_mode != callee_promoted_mode)
                  && (caller_unsignedp != callee_unsignedp
-                     || GET_MODE_BITSIZE (caller_mode)
-                        < GET_MODE_BITSIZE (callee_mode)))))
+                     || partial_subreg_p (caller_mode, callee_mode)))))
        {
          try_tail_call = 0;
          maybe_complain_about_tail_call (exp,
@@ -3371,6 +4069,12 @@ expand_call (tree exp, rtx target, int ignore)
   /* Ensure current function's preferred stack boundary is at least
      what we need.  Stack alignment may also increase preferred stack
      boundary.  */
+  for (i = 0; i < num_actuals; i++)
+    if (reg_parm_stack_space > 0
+       || args[i].reg == 0
+       || args[i].partial != 0
+       || args[i].pass_on_stack)
+      update_stack_alignment_for_call (&args[i].locate);
   if (crtl->preferred_stack_boundary < preferred_stack_boundary)
     crtl->preferred_stack_boundary = preferred_stack_boundary;
   else
@@ -3378,6 +4082,9 @@ expand_call (tree exp, rtx target, int ignore)
 
   preferred_unit_stack_boundary = preferred_stack_boundary / BITS_PER_UNIT;
 
+  if (flag_callgraph_info)
+    record_final_call (fndecl, EXPR_LOCATION (exp));
+
   /* 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.  */
@@ -3420,10 +4127,10 @@ expand_call (tree exp, rtx target, int ignore)
         call sequence.
         Also do the adjustments before a throwing call, otherwise
         exception handling can fail; PR 19225. */
-      if (pending_stack_adjust >= 32
-         || (pending_stack_adjust > 0
+      if (maybe_ge (pending_stack_adjust, 32)
+         || (maybe_ne (pending_stack_adjust, 0)
              && (flags & ECF_MAY_BE_ALLOCA))
-         || (pending_stack_adjust > 0
+         || (maybe_ne (pending_stack_adjust, 0)
              && flag_exceptions && !(flags & ECF_NOTHROW))
          || pass == 0)
        do_pending_stack_adjust ();
@@ -3469,8 +4176,10 @@ expand_call (tree exp, rtx target, int ignore)
            argblock
              = plus_constant (Pmode, argblock, -crtl->args.pretend_args_size);
 
-         stored_args_map = sbitmap_alloc (args_size.constant);
+         HOST_WIDE_INT map_size = constant_lower_bound (args_size.constant);
+         stored_args_map = sbitmap_alloc (map_size);
          bitmap_clear (stored_args_map);
+         stored_args_watermark = HOST_WIDE_INT_M1U;
        }
 
       /* If we have no actual push instructions, or shouldn't use them,
@@ -3500,14 +4209,14 @@ expand_call (tree exp, rtx target, int ignore)
             in the area reserved for register arguments, which may be part of
             the stack frame.  */
 
-         int needed = adjusted_args_size.constant;
+         poly_int64 needed = adjusted_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 > crtl->outgoing_args_size)
-           crtl->outgoing_args_size = needed;
+         crtl->outgoing_args_size = upper_bound (crtl->outgoing_args_size,
+                                                 needed);
 
          if (must_preallocate)
            {
@@ -3533,12 +4242,16 @@ expand_call (tree exp, rtx target, int ignore)
                  if (! OUTGOING_REG_PARM_STACK_SPACE ((!fndecl ? fntype : TREE_TYPE (fndecl))))
                    needed += reg_parm_stack_space;
 
+                 poly_int64 limit = needed;
                  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);
+                   limit += 1;
+
+                 /* For polynomial sizes, this is the maximum possible
+                    size needed for arguments with a constant size
+                    and offset.  */
+                 HOST_WIDE_INT const_limit = constant_lower_bound (limit);
+                 highest_outgoing_arg_in_use
+                   = MAX (initial_highest_arg_in_use, const_limit);
 
                  free (stack_usage_map_buf);
                  stack_usage_map_buf = XNEWVEC (char, highest_outgoing_arg_in_use);
@@ -3563,23 +4276,25 @@ expand_call (tree exp, rtx target, int ignore)
                }
              else
                {
-                 if (inhibit_defer_pop == 0)
+                 /* Try to reuse some or all of the pending_stack_adjust
+                    to get this space.  */
+                 if (inhibit_defer_pop == 0
+                     && (combine_pending_stack_adjustment_and_call
+                         (&needed,
+                          unadjusted_args_size,
+                          &adjusted_args_size,
+                          preferred_unit_stack_boundary)))
                    {
-                     /* 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,
-                           &adjusted_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)
+                     /* Checked by
+                        combine_pending_stack_adjustment_and_call.  */
+                     gcc_checking_assert (ordered_p (needed, 0));
+                     if (maybe_lt (needed, 0))
                        {
                          /* We're releasing stack space.  */
                          /* ??? We can avoid any adjustment at all if we're
@@ -3596,11 +4311,12 @@ expand_call (tree exp, rtx target, int ignore)
 
                  /* Special case this because overhead of `push_block' in
                     this case is non-trivial.  */
-                 if (needed == 0)
+                 if (known_eq (needed, 0))
                    argblock = virtual_outgoing_args_rtx;
                  else
                    {
-                     argblock = push_block (GEN_INT (needed), 0, 0);
+                     rtx needed_rtx = gen_int_mode (needed, Pmode);
+                     argblock = push_block (needed_rtx, 0, 0);
                      if (ARGS_GROW_DOWNWARD)
                        argblock = plus_constant (Pmode, argblock, needed);
                    }
@@ -3626,10 +4342,11 @@ expand_call (tree exp, rtx target, int ignore)
          if (stack_arg_under_construction)
            {
              rtx push_size
-               = GEN_INT (adjusted_args_size.constant
-                          + (OUTGOING_REG_PARM_STACK_SPACE ((!fndecl ? fntype
-                                                                     : TREE_TYPE (fndecl))) ? 0
-                             : reg_parm_stack_space));
+               = (gen_int_mode
+                  (adjusted_args_size.constant
+                   + (OUTGOING_REG_PARM_STACK_SPACE (!fndecl ? fntype
+                                                     : TREE_TYPE (fndecl))
+                      ? 0 : reg_parm_stack_space), Pmode));
              if (old_stack_level == 0)
                {
                  emit_stack_save (SAVE_BLOCK, &old_stack_level);
@@ -3648,12 +4365,13 @@ expand_call (tree exp, rtx target, int ignore)
                  stack_usage_map_buf = XCNEWVEC (char, highest_outgoing_arg_in_use);
                  stack_usage_map = stack_usage_map_buf;
                  highest_outgoing_arg_in_use = 0;
+                 stack_usage_watermark = HOST_WIDE_INT_M1U;
                }
              /* We can pass TRUE as the 4th argument because we just
                 saved the stack pointer and will restore it right after
                 the call.  */
-             allocate_dynamic_stack_space (push_size, 0,
-                                           BIGGEST_ALIGNMENT, true);
+             allocate_dynamic_stack_space (push_size, 0, BIGGEST_ALIGNMENT,
+                                           -1, true);
            }
 
          /* If argument evaluation might modify the stack pointer,
@@ -3683,24 +4401,23 @@ expand_call (tree exp, rtx target, int ignore)
 
       /* Perform stack alignment before the first push (the last arg).  */
       if (argblock == 0
-          && adjusted_args_size.constant > reg_parm_stack_space
-         && adjusted_args_size.constant != unadjusted_args_size)
+         && maybe_gt (adjusted_args_size.constant, reg_parm_stack_space)
+         && maybe_ne (adjusted_args_size.constant, unadjusted_args_size))
        {
          /* When the stack adjustment is pending, we get better code
             by combining the adjustments.  */
-         if (pending_stack_adjust
-             && ! inhibit_defer_pop)
-           {
-             pending_stack_adjust
-               = (combine_pending_stack_adjustment_and_call
-                  (unadjusted_args_size,
-                   &adjusted_args_size,
-                   preferred_unit_stack_boundary));
-             do_pending_stack_adjust ();
-           }
+         if (maybe_ne (pending_stack_adjust, 0)
+             && ! inhibit_defer_pop
+             && (combine_pending_stack_adjustment_and_call
+                 (&pending_stack_adjust,
+                  unadjusted_args_size,
+                  &adjusted_args_size,
+                  preferred_unit_stack_boundary)))
+           do_pending_stack_adjust ();
          else if (argblock == 0)
-           anti_adjust_stack (GEN_INT (adjusted_args_size.constant
-                                       - unadjusted_args_size));
+           anti_adjust_stack (gen_int_mode (adjusted_args_size.constant
+                                            - unadjusted_args_size,
+                                            Pmode));
        }
       /* Now that the stack is properly aligned, pops can't safely
         be deferred during the evaluation of the arguments.  */
@@ -3714,9 +4431,10 @@ expand_call (tree exp, rtx target, int ignore)
          && pass
          && adjusted_args_size.var == 0)
        {
-         int pushed = adjusted_args_size.constant + pending_stack_adjust;
-         if (pushed > current_function_pushed_stack_size)
-           current_function_pushed_stack_size = pushed;
+         poly_int64 pushed = (adjusted_args_size.constant
+                              + pending_stack_adjust);
+         current_function_pushed_stack_size
+           = upper_bound (current_function_pushed_stack_size, pushed);
        }
 
       funexp = rtx_for_function_call (fndecl, addr);
@@ -3742,16 +4460,13 @@ expand_call (tree exp, rtx target, int ignore)
 
       for (i = 0; i < num_actuals; i++)
        {
-         /* 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)
+         if (args[i].reg == 0 || args[i].pass_on_stack)
            {
              rtx_insn *before_arg = get_last_insn ();
 
              /* We don't allow passing huge (> 2^30 B) arguments
                 by value.  It would cause an overflow later on.  */
-             if (adjusted_args_size.constant
+             if (constant_lower_bound (adjusted_args_size.constant)
                  >= (1 << (HOST_BITS_PER_INT - 2)))
                {
                  sorry ("passing too large argument on stack");
@@ -3819,28 +4534,15 @@ expand_call (tree exp, rtx target, int ignore)
 
       /* 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));
-           }
+           valreg = hard_function_value (build_pointer_type (rettype),
+                                         fndecl, NULL, (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));
-           }
+           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
@@ -3856,17 +4558,6 @@ expand_call (tree exp, rtx target, int ignore)
            }
        }
 
-      /* 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.  */
@@ -3905,14 +4596,11 @@ expand_call (tree exp, rtx target, int ignore)
       /* Set up next argument register.  For sibling calls on machines
         with register windows this should be the incoming register.  */
       if (pass == 0)
-       next_arg_reg = targetm.calls.function_incoming_arg (args_so_far,
-                                                           VOIDmode,
-                                                           void_type_node,
-                                                           true);
+       next_arg_reg = targetm.calls.function_incoming_arg
+         (args_so_far, function_arg_info::end_marker ());
       else
-       next_arg_reg = targetm.calls.function_arg (args_so_far,
-                                                  VOIDmode, void_type_node,
-                                                  true);
+       next_arg_reg = targetm.calls.function_arg
+         (args_so_far, function_arg_info::end_marker ());
 
       if (pass == 1 && (return_flags & ERF_RETURNS_ARG))
        {
@@ -3934,7 +4622,8 @@ expand_call (tree exp, rtx target, int ignore)
 
       /* Stack must be properly aligned now.  */
       gcc_assert (!pass
-                 || !(stack_pointer_delta % preferred_unit_stack_boundary));
+                 || multiple_p (stack_pointer_delta,
+                                preferred_unit_stack_boundary));
 
       /* Generate the actual call instruction.  */
       emit_call_1 (funexp, exp, fndecl, funtype, unadjusted_args_size,
@@ -3990,7 +4679,7 @@ expand_call (tree exp, rtx target, int ignore)
 
          emit_move_insn (temp, valreg);
 
-         /* The return value from a malloc-like function can not alias
+         /* The return value from a malloc-like function cannot alias
             anything else.  */
          last = get_last_insn ();
          add_reg_note (last, REG_NOALIAS, temp);
@@ -4131,7 +4820,6 @@ expand_call (tree exp, rtx target, int ignore)
        {
          tree type = rettype;
          int unsignedp = TYPE_UNSIGNED (type);
-         int offset = 0;
          machine_mode pmode;
 
          /* Ensure we promote as expected, and get the new unsignedness.  */
@@ -4139,18 +4827,8 @@ expand_call (tree exp, rtx target, int ignore)
                                         funtype, 1);
          gcc_assert (GET_MODE (target) == pmode);
 
-         if ((WORDS_BIG_ENDIAN || BYTES_BIG_ENDIAN)
-             && (GET_MODE_SIZE (GET_MODE (target))
-                 > GET_MODE_SIZE (TYPE_MODE (type))))
-           {
-             offset = GET_MODE_SIZE (GET_MODE (target))
-               - GET_MODE_SIZE (TYPE_MODE (type));
-             if (! BYTES_BIG_ENDIAN)
-               offset = (offset / UNITS_PER_WORD) * UNITS_PER_WORD;
-             else if (! WORDS_BIG_ENDIAN)
-               offset %= UNITS_PER_WORD;
-           }
-
+         poly_uint64 offset = subreg_lowpart_offset (TYPE_MODE (type),
+                                                     GET_MODE (target));
          target = gen_rtx_SUBREG (TYPE_MODE (type), target, offset);
          SUBREG_PROMOTED_VAR_P (target) = 1;
          SUBREG_PROMOTED_SET (target, unsignedp);
@@ -4173,6 +4851,7 @@ expand_call (tree exp, rtx target, int ignore)
          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;
+         stack_usage_watermark = initial_stack_usage_watermark;
          sibcall_failure = 1;
        }
       else if (ACCUMULATE_OUTGOING_ARGS && pass)
@@ -4197,12 +4876,14 @@ expand_call (tree exp, rtx target, int ignore)
                  emit_move_insn (stack_area, args[i].save_area);
                else
                  emit_block_move (stack_area, args[i].save_area,
-                                  GEN_INT (args[i].locate.size.constant),
+                                  (gen_int_mode
+                                   (args[i].locate.size.constant, Pmode)),
                                   BLOCK_OP_CALL_PARM);
              }
 
          highest_outgoing_arg_in_use = initial_highest_arg_in_use;
          stack_usage_map = initial_stack_usage_map;
+         stack_usage_watermark = initial_stack_usage_watermark;
        }
 
       /* If this was alloca, record the new stack level.  */
@@ -4245,8 +4926,9 @@ expand_call (tree exp, rtx target, int ignore)
 
          /* Verify that we've deallocated all the stack we used.  */
          gcc_assert ((flags & ECF_NORETURN)
-                     || (old_stack_allocated
-                         == stack_pointer_delta - pending_stack_adjust));
+                     || known_eq (old_stack_allocated,
+                                  stack_pointer_delta
+                                  - pending_stack_adjust));
        }
 
       /* If something prevents making this a sibling call,
@@ -4277,10 +4959,6 @@ expand_call (tree exp, rtx target, int ignore)
 
   free (stack_usage_map_buf);
   free (args);
-
-  /* Join result with returned bounds so caller may use them if needed.  */
-  target = chkp_join_splitted_slot (target, valbnd);
-
   return target;
 }
 
@@ -4365,14 +5043,21 @@ split_complex_types (tree types)
   return types;
 }
 \f
-/* Output a library call to function FUN (a SYMBOL_REF rtx).
-   The RETVAL parameter specifies whether return value needs to be saved, other
-   parameters are documented in the emit_library_call function below.  */
+/* Output a library call to function ORGFUN (a SYMBOL_REF rtx)
+   for a value of mode OUTMODE,
+   with NARGS different arguments, passed as ARGS.
+   Store the return value if RETVAL is nonzero: store it in VALUE if
+   VALUE is nonnull, otherwise pick a convenient location.  In either
+   case return the location of the stored value.
 
-static rtx
+   FN_TYPE should be LCT_NORMAL for `normal' calls, LCT_CONST for
+   `const' calls, LCT_PURE for `pure' calls, or another LCT_ value for
+   other types of library calls.  */
+
+rtx
 emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
                           enum libcall_type fn_type,
-                          machine_mode outmode, int nargs, va_list p)
+                          machine_mode outmode, int nargs, rtx_mode_t *args)
 {
   /* Total size in bytes of all the stack-parms scanned so far.  */
   struct args_size args_size;
@@ -4403,10 +5088,10 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
   rtx mem_value = 0;
   rtx valreg;
   int pcc_struct_value = 0;
-  int struct_value_size = 0;
+  poly_int64 struct_value_size = 0;
   int flags;
   int reg_parm_stack_space = 0;
-  int needed;
+  poly_int64 needed;
   rtx_insn *before_call;
   bool have_push_fusage;
   tree tfom;                   /* type_for_mode (outmode, 0) */
@@ -4419,8 +5104,9 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
 #endif
 
   /* Size of the stack reserved for parameter registers.  */
-  int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
+  unsigned int initial_highest_arg_in_use = highest_outgoing_arg_in_use;
   char *initial_stack_usage_map = stack_usage_map;
+  unsigned HOST_WIDE_INT initial_stack_usage_watermark = stack_usage_watermark;
   char *stack_usage_map_buf = NULL;
 
   rtx struct_value = targetm.calls.struct_value_rtx (0, 0);
@@ -4531,10 +5217,9 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
       argvec[count].mode = Pmode;
       argvec[count].partial = 0;
 
-      argvec[count].reg = targetm.calls.function_arg (args_so_far,
-                                                     Pmode, NULL_TREE, true);
-      gcc_assert (targetm.calls.arg_partial_bytes (args_so_far, Pmode,
-                                                  NULL_TREE, 1) == 0);
+      function_arg_info ptr_arg (Pmode, /*named=*/true);
+      argvec[count].reg = targetm.calls.function_arg (args_so_far, ptr_arg);
+      gcc_assert (targetm.calls.arg_partial_bytes (args_so_far, ptr_arg) == 0);
 
       locate_and_pad_parm (Pmode, NULL_TREE,
 #ifdef STACK_PARMS_IN_REG_PARM_AREA
@@ -4549,32 +5234,33 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
          || reg_parm_stack_space > 0)
        args_size.constant += argvec[count].locate.size.constant;
 
-      targetm.calls.function_arg_advance (args_so_far, Pmode, (tree) 0, true);
+      targetm.calls.function_arg_advance (args_so_far, ptr_arg);
 
       count++;
     }
 
-  for (; count < nargs; count++)
+  for (unsigned int i = 0; count < nargs; i++, count++)
     {
-      rtx val = va_arg (p, rtx);
-      machine_mode mode = (machine_mode) va_arg (p, int);
+      rtx val = args[i].first;
+      function_arg_info arg (args[i].second, /*named=*/true);
       int unsigned_p = 0;
 
       /* We cannot convert the arg value to the mode the library wants here;
         must do it earlier where we know the signedness of the arg.  */
-      gcc_assert (mode != BLKmode
-                 && (GET_MODE (val) == mode || GET_MODE (val) == VOIDmode));
+      gcc_assert (arg.mode != BLKmode
+                 && (GET_MODE (val) == arg.mode
+                     || GET_MODE (val) == VOIDmode));
 
       /* Make sure it is a reasonable operand for a move or push insn.  */
       if (!REG_P (val) && !MEM_P (val)
-         && !(CONSTANT_P (val) && targetm.legitimate_constant_p (mode, val)))
+         && !(CONSTANT_P (val)
+              && targetm.legitimate_constant_p (arg.mode, val)))
        val = force_operand (val, NULL_RTX);
 
-      if (pass_by_reference (&args_so_far_v, mode, NULL_TREE, 1))
+      if (pass_by_reference (&args_so_far_v, arg))
        {
          rtx slot;
-         int must_copy
-           = !reference_callee_copied (&args_so_far_v, mode, NULL_TREE, 1);
+         int must_copy = !reference_callee_copied (&args_so_far_v, arg);
 
          /* If this was a CONST function, it is now PURE since it now
             reads memory.  */
@@ -4593,7 +5279,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
            }
          else
            {
-             slot = assign_temp (lang_hooks.types.type_for_mode (mode, 0),
+             slot = assign_temp (lang_hooks.types.type_for_mode (arg.mode, 0),
                                  1, 1);
              emit_move_insn (slot, val);
            }
@@ -4607,24 +5293,26 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
                                                              slot),
                                             call_fusage);
 
-         mode = Pmode;
+         arg.mode = Pmode;
+         arg.pass_by_reference = true;
          val = force_operand (XEXP (slot, 0), NULL_RTX);
        }
 
-      mode = promote_function_mode (NULL_TREE, mode, &unsigned_p, NULL_TREE, 0);
-      argvec[count].mode = mode;
-      argvec[count].value = convert_modes (mode, GET_MODE (val), val, unsigned_p);
-      argvec[count].reg = targetm.calls.function_arg (args_so_far, mode,
-                                                     NULL_TREE, true);
+      arg.mode = promote_function_mode (NULL_TREE, arg.mode, &unsigned_p,
+                                       NULL_TREE, 0);
+      argvec[count].mode = arg.mode;
+      argvec[count].value = convert_modes (arg.mode, GET_MODE (val), val,
+                                          unsigned_p);
+      argvec[count].reg = targetm.calls.function_arg (args_so_far, arg);
 
       argvec[count].partial
-       = targetm.calls.arg_partial_bytes (args_so_far, mode, NULL_TREE, 1);
+       = targetm.calls.arg_partial_bytes (args_so_far, arg);
 
       if (argvec[count].reg == 0
          || argvec[count].partial != 0
          || reg_parm_stack_space > 0)
        {
-         locate_and_pad_parm (mode, NULL_TREE,
+         locate_and_pad_parm (arg.mode, NULL_TREE,
 #ifdef STACK_PARMS_IN_REG_PARM_AREA
                               1,
 #else
@@ -4640,39 +5328,44 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
        /* The argument is passed entirely in registers.  See at which
           end it should be padded.  */
        argvec[count].locate.where_pad =
-         BLOCK_REG_PADDING (mode, NULL_TREE,
-                            GET_MODE_SIZE (mode) <= UNITS_PER_WORD);
+         BLOCK_REG_PADDING (arg.mode, NULL_TREE,
+                            known_le (GET_MODE_SIZE (arg.mode),
+                                      UNITS_PER_WORD));
 #endif
 
-      targetm.calls.function_arg_advance (args_so_far, mode, (tree) 0, true);
+      targetm.calls.function_arg_advance (args_so_far, arg);
     }
 
+  for (int i = 0; i < nargs; i++)
+    if (reg_parm_stack_space > 0
+       || argvec[i].reg == 0
+       || argvec[i].partial != 0)
+      update_stack_alignment_for_call (&argvec[i].locate);
+
   /* If this machine requires an external definition for library
      functions, write one out.  */
   assemble_external_libcall (fun);
 
   original_args_size = args_size;
-  args_size.constant = (((args_size.constant
-                         + stack_pointer_delta
-                         + STACK_BYTES - 1)
-                         / STACK_BYTES
-                         * STACK_BYTES)
-                        - stack_pointer_delta);
+  args_size.constant = (aligned_upper_bound (args_size.constant
+                                            + stack_pointer_delta,
+                                            STACK_BYTES)
+                       - stack_pointer_delta);
 
-  args_size.constant = MAX (args_size.constant,
-                           reg_parm_stack_space);
+  args_size.constant = upper_bound (args_size.constant,
+                                   reg_parm_stack_space);
 
   if (! OUTGOING_REG_PARM_STACK_SPACE ((!fndecl ? fntype : TREE_TYPE (fndecl))))
     args_size.constant -= reg_parm_stack_space;
 
-  if (args_size.constant > crtl->outgoing_args_size)
-    crtl->outgoing_args_size = args_size.constant;
+  crtl->outgoing_args_size = upper_bound (crtl->outgoing_args_size,
+                                         args_size.constant);
 
   if (flag_stack_usage_info && !ACCUMULATE_OUTGOING_ARGS)
     {
-      int pushed = args_size.constant + pending_stack_adjust;
-      if (pushed > current_function_pushed_stack_size)
-       current_function_pushed_stack_size = pushed;
+      poly_int64 pushed = args_size.constant + pending_stack_adjust;
+      current_function_pushed_stack_size
+       = upper_bound (current_function_pushed_stack_size, pushed);
     }
 
   if (ACCUMULATE_OUTGOING_ARGS)
@@ -4697,11 +5390,15 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
       if (! OUTGOING_REG_PARM_STACK_SPACE ((!fndecl ? fntype : TREE_TYPE (fndecl))))
        needed += reg_parm_stack_space;
 
+      poly_int64 limit = needed;
       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);
+       limit += 1;
+
+      /* For polynomial sizes, this is the maximum possible size needed
+        for arguments with a constant size and offset.  */
+      HOST_WIDE_INT const_limit = constant_lower_bound (limit);
+      highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use,
+                                        const_limit);
 
       stack_usage_map_buf = XNEWVEC (char, highest_outgoing_arg_in_use);
       stack_usage_map = stack_usage_map_buf;
@@ -4729,14 +5426,15 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
   else
     {
       if (!PUSH_ARGS)
-       argblock = push_block (GEN_INT (args_size.constant), 0, 0);
+       argblock = push_block (gen_int_mode (args_size.constant, Pmode), 0, 0);
     }
 
   /* We push args individually in reverse order, perform stack alignment
      before the first push (the last arg).  */
   if (argblock == 0)
-    anti_adjust_stack (GEN_INT (args_size.constant
-                               - original_args_size.constant));
+    anti_adjust_stack (gen_int_mode (args_size.constant
+                                    - original_args_size.constant,
+                                    Pmode));
 
   argnum = nargs - 1;
 
@@ -4776,7 +5474,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
       rtx reg = argvec[argnum].reg;
       int partial = argvec[argnum].partial;
       unsigned int parm_align = argvec[argnum].locate.boundary;
-      int lower_bound = 0, upper_bound = 0, i;
+      poly_int64 lower_bound = 0, upper_bound = 0;
 
       if (! (reg != 0 && partial == 0))
        {
@@ -4800,21 +5498,14 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
                  upper_bound = lower_bound + argvec[argnum].locate.size.constant;
                }
 
-             i = lower_bound;
-             /* Don't worry about things in the fixed argument area;
-                it has already been saved.  */
-             if (i < reg_parm_stack_space)
-               i = reg_parm_stack_space;
-             while (i < upper_bound && stack_usage_map[i] == 0)
-               i++;
-
-             if (i < upper_bound)
+             if (stack_region_maybe_used_p (lower_bound, upper_bound,
+                                            reg_parm_stack_space))
                {
                  /* We need to make a save area.  */
-                 unsigned int size
+                 poly_uint64 size
                    = argvec[argnum].locate.size.constant * BITS_PER_UNIT;
                  machine_mode save_mode
-                   = mode_for_size (size, MODE_INT, 1);
+                   = int_mode_for_size (size, 1).else_blk ();
                  rtx adr
                    = plus_constant (Pmode, argblock,
                                     argvec[argnum].locate.offset.constant);
@@ -4831,7 +5522,9 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
                      emit_block_move (validize_mem
                                         (copy_rtx (argvec[argnum].save_area)),
                                       stack_area,
-                                      GEN_INT (argvec[argnum].locate.size.constant),
+                                      (gen_int_mode
+                                       (argvec[argnum].locate.size.constant,
+                                        Pmode)),
                                       BLOCK_OP_CALL_PARM);
                    }
                  else
@@ -4845,14 +5538,14 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
 
          emit_push_insn (val, mode, NULL_TREE, NULL_RTX, parm_align,
                          partial, reg, 0, argblock,
-                         GEN_INT (argvec[argnum].locate.offset.constant),
+                         (gen_int_mode
+                          (argvec[argnum].locate.offset.constant, Pmode)),
                          reg_parm_stack_space,
                          ARGS_SIZE_RTX (argvec[argnum].locate.alignment_pad), false);
 
          /* Now mark the segment we just used.  */
          if (ACCUMULATE_OUTGOING_ARGS)
-           for (i = lower_bound; i < upper_bound; i++)
-             stack_usage_map[i] = 1;
+           mark_stack_region_used (lower_bound, upper_bound);
 
          NO_DEFER_POP;
 
@@ -4894,9 +5587,6 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
       rtx val = argvec[argnum].value;
       rtx reg = argvec[argnum].reg;
       int partial = argvec[argnum].partial;
-#ifdef BLOCK_REG_PADDING
-      int size = 0;
-#endif
       
       /* Handle calls that pass values in multiple non-contiguous
         locations.  The PA64 has examples of this for library calls.  */
@@ -4906,19 +5596,19 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
         {
          emit_move_insn (reg, val);
 #ifdef BLOCK_REG_PADDING
-         size = GET_MODE_SIZE (argvec[argnum].mode);
+         poly_int64 size = GET_MODE_SIZE (argvec[argnum].mode);
 
          /* Copied from load_register_parameters.  */
 
          /* Handle case where we have a value that needs shifting
             up to the msb.  eg. a QImode value and we're padding
             upward on a BYTES_BIG_ENDIAN machine.  */
-         if (size < UNITS_PER_WORD
+         if (known_lt (size, UNITS_PER_WORD)
              && (argvec[argnum].locate.where_pad
-                 == (BYTES_BIG_ENDIAN ? upward : downward)))
+                 == (BYTES_BIG_ENDIAN ? PAD_UPWARD : PAD_DOWNWARD)))
            {
              rtx x;
-             int shift = (UNITS_PER_WORD - size) * BITS_PER_UNIT;
+             poly_int64 shift = (UNITS_PER_WORD - size) * BITS_PER_UNIT;
 
              /* Assigning REG here rather than a temp makes CALL_FUSAGE
                 report the whole reg as used.  Strictly speaking, the
@@ -4974,11 +5664,14 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
            ? hard_libcall_value (outmode, orgfun) : NULL_RTX);
 
   /* Stack must be properly aligned now.  */
-  gcc_assert (!(stack_pointer_delta
-               & (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT - 1)));
+  gcc_assert (multiple_p (stack_pointer_delta,
+                         PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT));
 
   before_call = get_last_insn ();
 
+  if (flag_callgraph_info)
+    record_final_call (SYMBOL_REF_DECL (orgfun), UNKNOWN_LOCATION);
+
   /* We pass the old value of inhibit_defer_pop + 1 to emit_call_1, which
      will set inhibit_defer_pop to that value.  */
   /* The return type is needed to decide how many bytes the function pops.
@@ -4992,7 +5685,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
               original_args_size.constant, args_size.constant,
               struct_value_size,
               targetm.calls.function_arg (args_so_far,
-                                          VOIDmode, void_type_node, true),
+                                          function_arg_info::end_marker ()),
               valreg,
               old_inhibit_defer_pop + 1, call_fusage, flags, args_so_far);
 
@@ -5113,7 +5806,8 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
              emit_block_move (stack_area,
                               validize_mem
                                 (copy_rtx (argvec[count].save_area)),
-                              GEN_INT (argvec[count].locate.size.constant),
+                              (gen_int_mode
+                               (argvec[count].locate.size.constant, Pmode)),
                               BLOCK_OP_CALL_PARM);
            else
              emit_move_insn (stack_area, argvec[count].save_area);
@@ -5121,6 +5815,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
 
       highest_outgoing_arg_in_use = initial_highest_arg_in_use;
       stack_usage_map = initial_stack_usage_map;
+      stack_usage_watermark = initial_stack_usage_watermark;
     }
 
   free (stack_usage_map_buf);
@@ -5129,112 +5824,6 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
 
 }
 \f
-/* Output a library call to function FUN (a SYMBOL_REF rtx)
-   (emitting the queue unless NO_QUEUE is nonzero),
-   for a value of mode OUTMODE,
-   with NARGS different arguments, passed as alternating rtx values
-   and machine_modes to convert them to.
-
-   FN_TYPE should be LCT_NORMAL for `normal' calls, LCT_CONST for
-   `const' calls, LCT_PURE for `pure' calls, or other LCT_ value for
-   other types of library calls.  */
-
-void
-emit_library_call (rtx orgfun, enum libcall_type fn_type,
-                  machine_mode outmode, int nargs, ...)
-{
-  va_list p;
-
-  va_start (p, nargs);
-  emit_library_call_value_1 (0, orgfun, NULL_RTX, fn_type, outmode, nargs, p);
-  va_end (p);
-}
-\f
-/* Like emit_library_call except that an extra argument, VALUE,
-   comes second and says where to store the result.
-   (If VALUE is zero, this function chooses a convenient way
-   to return the value.
-
-   This function returns an rtx for where the value is to be found.
-   If VALUE is nonzero, VALUE is returned.  */
-
-rtx
-emit_library_call_value (rtx orgfun, rtx value,
-                        enum libcall_type fn_type,
-                        machine_mode outmode, int nargs, ...)
-{
-  rtx result;
-  va_list p;
-
-  va_start (p, nargs);
-  result = emit_library_call_value_1 (1, orgfun, value, fn_type, outmode,
-                                     nargs, p);
-  va_end (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.
@@ -5262,8 +5851,8 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
   tree pval = arg->tree_value;
   rtx reg = 0;
   int partial = 0;
-  int used = 0;
-  int i, lower_bound = 0, upper_bound = 0;
+  poly_int64 used = 0;
+  poly_int64 lower_bound = 0, upper_bound = 0;
   int sibcall_failure = 0;
 
   if (TREE_CODE (pval) == ERROR_MARK)
@@ -5284,7 +5873,10 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
              /* 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;
+               {
+                 rtx offset = XEXP (XEXP (arg->stack_slot, 0), 1);
+                 upper_bound = -rtx_to_poly_int64 (offset) + 1;
+               }
              else
                upper_bound = 0;
 
@@ -5293,26 +5885,23 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
          else
            {
              if (GET_CODE (XEXP (arg->stack_slot, 0)) == PLUS)
-               lower_bound = INTVAL (XEXP (XEXP (arg->stack_slot, 0), 1));
+               {
+                 rtx offset = XEXP (XEXP (arg->stack_slot, 0), 1);
+                 lower_bound = rtx_to_poly_int64 (offset);
+               }
              else
                lower_bound = 0;
 
              upper_bound = lower_bound + arg->locate.size.constant;
            }
 
-         i = lower_bound;
-         /* Don't worry about things in the fixed argument area;
-            it has already been saved.  */
-         if (i < reg_parm_stack_space)
-           i = reg_parm_stack_space;
-         while (i < upper_bound && stack_usage_map[i] == 0)
-           i++;
-
-         if (i < upper_bound)
+         if (stack_region_maybe_used_p (lower_bound, upper_bound,
+                                        reg_parm_stack_space))
            {
              /* We need to make a save area.  */
-             unsigned int size = arg->locate.size.constant * BITS_PER_UNIT;
-             machine_mode save_mode = mode_for_size (size, MODE_INT, 1);
+             poly_uint64 size = arg->locate.size.constant * BITS_PER_UNIT;
+             machine_mode save_mode
+               = int_mode_for_size (size, 1).else_blk ();
              rtx adr = memory_address (save_mode, XEXP (arg->stack_slot, 0));
              rtx stack_area = gen_rtx_MEM (save_mode, adr);
 
@@ -5323,7 +5912,8 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
                  preserve_temp_slots (arg->save_area);
                  emit_block_move (validize_mem (copy_rtx (arg->save_area)),
                                   stack_area,
-                                  GEN_INT (arg->locate.size.constant),
+                                  (gen_int_mode
+                                   (arg->locate.size.constant, Pmode)),
                                   BLOCK_OP_CALL_PARM);
                }
              else
@@ -5400,8 +5990,8 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
   /* Check for overlap with already clobbered argument area.  */
   if ((flags & ECF_SIBCALL)
       && MEM_P (arg->value)
-      && mem_overlaps_already_clobbered_arg_p (XEXP (arg->value, 0),
-                                              arg->locate.size.constant))
+      && mem_might_overlap_already_clobbered_arg_p (XEXP (arg->value, 0),
+                                                   arg->locate.size.constant))
     sibcall_failure = 1;
 
   /* Don't allow anything left on stack from computation
@@ -5414,7 +6004,6 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
     ;
   else if (arg->mode != BLKmode)
     {
-      int size;
       unsigned int parm_align;
 
       /* Argument is a scalar, not entirely passed in registers.
@@ -5427,7 +6016,9 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
         Note that in C the default argument promotions
         will prevent such mismatches.  */
 
-      size = GET_MODE_SIZE (arg->mode);
+      poly_int64 size = (TYPE_EMPTY_P (TREE_TYPE (pval))
+                        ? 0 : GET_MODE_SIZE (arg->mode));
+
       /* Compute how much space the push instruction will push.
         On many machines, pushing a byte will advance the stack
         pointer by a halfword.  */
@@ -5438,29 +6029,32 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 
       /* Compute how much space the argument should get:
         round up to a multiple of the alignment for arguments.  */
-      if (none != FUNCTION_ARG_PADDING (arg->mode, TREE_TYPE (pval)))
-       used = (((size + PARM_BOUNDARY / BITS_PER_UNIT - 1)
-                / (PARM_BOUNDARY / BITS_PER_UNIT))
-               * (PARM_BOUNDARY / BITS_PER_UNIT));
+      if (targetm.calls.function_arg_padding (arg->mode, TREE_TYPE (pval))
+         != PAD_NONE)
+       /* At the moment we don't (need to) support ABIs for which the
+          padding isn't known at compile time.  In principle it should
+          be easy to add though.  */
+       used = force_align_up (size, PARM_BOUNDARY / BITS_PER_UNIT);
 
       /* Compute the alignment of the pushed argument.  */
       parm_align = arg->locate.boundary;
-      if (FUNCTION_ARG_PADDING (arg->mode, TREE_TYPE (pval)) == downward)
+      if (targetm.calls.function_arg_padding (arg->mode, TREE_TYPE (pval))
+         == PAD_DOWNWARD)
        {
-         int pad = used - size;
-         if (pad)
-           {
-             unsigned int pad_align = least_bit_hwi (pad) * BITS_PER_UNIT;
-             parm_align = MIN (parm_align, pad_align);
-           }
+         poly_int64 pad = used - size;
+         unsigned int pad_align = known_alignment (pad) * BITS_PER_UNIT;
+         if (pad_align != 0)
+           parm_align = MIN (parm_align, pad_align);
        }
 
       /* 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.  */
-      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), true))
+      if (maybe_ne (used, 0)
+         && !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), true))
        sibcall_failure = 1;
 
       /* Unless this is a partially-in-register argument, the argument is now
@@ -5473,7 +6067,7 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
       /* BLKmode, at least partly to be pushed.  */
 
       unsigned int parm_align;
-      int excess;
+      poly_int64 excess;
       rtx size_rtx;
 
       /* Pushing a nonscalar.
@@ -5493,9 +6087,9 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
          /* PUSH_ROUNDING has no effect on us, because emit_push_insn
             for BLKmode is careful to avoid it.  */
          excess = (arg->locate.size.constant
-                   - int_size_in_bytes (TREE_TYPE (pval))
+                   - arg_int_size_in_bytes (TREE_TYPE (pval))
                    + partial);
-         size_rtx = expand_expr (size_in_bytes (TREE_TYPE (pval)),
+         size_rtx = expand_expr (arg_size_in_bytes (TREE_TYPE (pval)),
                                  NULL_RTX, TYPE_MODE (sizetype),
                                  EXPAND_NORMAL);
        }
@@ -5504,14 +6098,17 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 
       /* When an argument is padded down, the block is aligned to
         PARM_BOUNDARY, but the actual argument isn't.  */
-      if (FUNCTION_ARG_PADDING (arg->mode, TREE_TYPE (pval)) == downward)
+      if (targetm.calls.function_arg_padding (arg->mode, TREE_TYPE (pval))
+         == PAD_DOWNWARD)
        {
          if (arg->locate.size.var)
            parm_align = BITS_PER_UNIT;
-         else if (excess)
+         else
            {
-             unsigned int excess_align = least_bit_hwi (excess) * BITS_PER_UNIT;
-             parm_align = MIN (parm_align, excess_align);
+             unsigned int excess_align
+               = known_alignment (excess) * BITS_PER_UNIT;
+             if (excess_align != 0)
+               parm_align = MIN (parm_align, excess_align);
            }
        }
 
@@ -5520,17 +6117,11 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
          /* emit_push_insn might not work properly if arg->value and
             argblock + arg->locate.offset areas overlap.  */
          rtx x = arg->value;
-         int i = 0;
+         poly_int64 i = 0;
 
-         if (XEXP (x, 0) == crtl->args.internal_arg_pointer
-             || (GET_CODE (XEXP (x, 0)) == PLUS
-                 && XEXP (XEXP (x, 0), 0) ==
-                    crtl->args.internal_arg_pointer
-                 && CONST_INT_P (XEXP (XEXP (x, 0), 1))))
+         if (strip_offset (XEXP (x, 0), &i)
+             == crtl->args.internal_arg_pointer)
            {
-             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)
@@ -5540,40 +6131,37 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
 
              /* expand_call should ensure this.  */
              gcc_assert (!arg->locate.offset.var
-                         && arg->locate.size.var == 0
-                         && CONST_INT_P (size_rtx));
+                         && arg->locate.size.var == 0);
+             poly_int64 size_val = rtx_to_poly_int64 (size_rtx);
 
-             if (arg->locate.offset.constant > i)
-               {
-                 if (arg->locate.offset.constant < i + INTVAL (size_rtx))
-                   sibcall_failure = 1;
-               }
-             else if (arg->locate.offset.constant < i)
-               {
-                 /* Use arg->locate.size.constant instead of size_rtx
-                    because we only care about the part of the argument
-                    on the stack.  */
-                 if (i < (arg->locate.offset.constant
-                          + arg->locate.size.constant))
-                   sibcall_failure = 1;
-               }
-             else
+             if (known_eq (arg->locate.offset.constant, i))
                {
                  /* Even though they appear to be at the same location,
                     if part of the outgoing argument is in registers,
                     they aren't really at the same location.  Check for
                     this by making sure that the incoming size is the
                     same as the outgoing size.  */
-                 if (arg->locate.size.constant != INTVAL (size_rtx))
+                 if (maybe_ne (arg->locate.size.constant, size_val))
                    sibcall_failure = 1;
                }
+             else if (maybe_in_range_p (arg->locate.offset.constant,
+                                        i, size_val))
+               sibcall_failure = 1;
+             /* Use arg->locate.size.constant instead of size_rtx
+                because we only care about the part of the argument
+                on the stack.  */
+             else if (maybe_in_range_p (i, arg->locate.offset.constant,
+                                        arg->locate.size.constant))
+               sibcall_failure = 1;
            }
        }
 
-      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), false);
+      if (!CONST_INT_P (size_rtx) || INTVAL (size_rtx) != 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), false);
 
       /* Unless this is a partially-in-register argument, the argument is now
         in the stack.
@@ -5598,8 +6186,7 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
   /* Mark all slots this store used.  */
   if (ACCUMULATE_OUTGOING_ARGS && !(flags & ECF_SIBCALL)
       && argblock && ! variable_size && arg->stack)
-    for (i = lower_bound; i < upper_bound; i++)
-      stack_usage_map[i] = 1;
+    mark_stack_region_used (lower_bound, upper_bound);
 
   /* Once we have pushed something, pops can't safely
      be deferred during the rest of the arguments.  */
@@ -5611,22 +6198,21 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
   return sibcall_failure;
 }
 
-/* Nonzero if we do not know how to pass TYPE solely in registers.  */
+/* Nonzero if we do not know how to pass ARG solely in registers.  */
 
 bool
-must_pass_in_stack_var_size (machine_mode mode ATTRIBUTE_UNUSED,
-                            const_tree type)
+must_pass_in_stack_var_size (const function_arg_info &arg)
 {
-  if (!type)
+  if (!arg.type)
     return false;
 
   /* If the type has variable size...  */
-  if (TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
+  if (!poly_int_tree_p (TYPE_SIZE (arg.type)))
     return true;
 
   /* If the type is marked as addressable (it is required
      to be constructed into the stack)...  */
-  if (TREE_ADDRESSABLE (type))
+  if (TREE_ADDRESSABLE (arg.type))
     return true;
 
   return false;
@@ -5637,30 +6223,58 @@ must_pass_in_stack_var_size (machine_mode mode ATTRIBUTE_UNUSED,
 /* ??? Should be able to merge these two by examining BLOCK_REG_PADDING.  */
 
 bool
-must_pass_in_stack_var_size_or_pad (machine_mode mode, const_tree type)
+must_pass_in_stack_var_size_or_pad (const function_arg_info &arg)
 {
-  if (!type)
+  if (!arg.type)
     return false;
 
   /* If the type has variable size...  */
-  if (TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
+  if (TREE_CODE (TYPE_SIZE (arg.type)) != INTEGER_CST)
     return true;
 
   /* If the type is marked as addressable (it is required
      to be constructed into the stack)...  */
-  if (TREE_ADDRESSABLE (type))
+  if (TREE_ADDRESSABLE (arg.type))
     return true;
 
+  if (TYPE_EMPTY_P (arg.type))
+    return false;
+
   /* If the padding and mode of the type is such that a copy into
      a register would put it into the wrong part of the register.  */
-  if (mode == BLKmode
-      && int_size_in_bytes (type) % (PARM_BOUNDARY / BITS_PER_UNIT)
-      && (FUNCTION_ARG_PADDING (mode, type)
-         == (BYTES_BIG_ENDIAN ? upward : downward)))
+  if (arg.mode == BLKmode
+      && int_size_in_bytes (arg.type) % (PARM_BOUNDARY / BITS_PER_UNIT)
+      && (targetm.calls.function_arg_padding (arg.mode, arg.type)
+         == (BYTES_BIG_ENDIAN ? PAD_UPWARD : PAD_DOWNWARD)))
     return true;
 
   return false;
 }
 
+/* Return true if TYPE must be passed on the stack when passed to
+   the "..." arguments of a function.  */
+
+bool
+must_pass_va_arg_in_stack (tree type)
+{
+  function_arg_info arg (type, /*named=*/false);
+  return targetm.calls.must_pass_in_stack (arg);
+}
+
+/* Return true if FIELD is the C++17 empty base field that should
+   be ignored for ABI calling convention decisions in order to
+   maintain ABI compatibility between C++14 and earlier, which doesn't
+   add this FIELD to classes with empty bases, and C++17 and later
+   which does.  */
+
+bool
+cxx17_empty_base_field_p (const_tree field)
+{
+  return (DECL_FIELD_ABI_IGNORED (field)
+         && DECL_ARTIFICIAL (field)
+         && RECORD_OR_UNION_TYPE_P (TREE_TYPE (field))
+         && !lookup_attribute ("no_unique_address", DECL_ATTRIBUTES (field)));
+}
+
 /* Tell the garbage collector about GTY markers in this source file.  */
 #include "gt-calls.h"