]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/calls.c
PR fortran/95090 - ICE: identifier overflow
[thirdparty/gcc.git] / gcc / calls.c
index 6eefeec17d9455a0ccd9094f52b02466c17d4eda..8041388c1d202199240223210edafa6749ed5a88 100644 (file)
@@ -1,5 +1,5 @@
 /* Convert function calls to rtl insns, for GNU C compiler.
-   Copyright (C) 1989-2019 Free Software Foundation, Inc.
+   Copyright (C) 1989-2020 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -52,6 +52,8 @@ along with GCC; see the file COPYING3.  If not see
 #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"
@@ -584,18 +586,8 @@ special_function_p (const_tree fndecl, int flags)
 {
   tree name_decl = DECL_NAME (fndecl);
 
-  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;
@@ -911,7 +903,7 @@ pass_by_reference (CUMULATIVE_ARGS *ca, function_arg_info arg)
        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)
@@ -1258,6 +1250,9 @@ alloc_max_size (void)
 bool
 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.  */
@@ -1614,6 +1609,10 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
            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;
              }
@@ -1639,6 +1638,10 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
        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;
          }
@@ -1862,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.
 
@@ -1978,6 +2285,11 @@ 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++)
     {
@@ -1991,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.
@@ -2219,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])
@@ -2231,6 +2558,9 @@ initialize_argument_information (int num_actuals ATTRIBUTE_UNUSED,
   /* 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.
@@ -2771,6 +3101,9 @@ load_register_parameters (struct arg_data *args, int num_actuals,
          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
@@ -2783,11 +3116,11 @@ 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)
            {
              /* Variable-sized parameters should be described by a
                 PARALLEL instead.  */
-             const_size = int_size_in_bytes (TREE_TYPE (args[i].tree_value));
+             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;
@@ -2914,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);
        }
@@ -3750,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.  */
@@ -5334,6 +5669,9 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
 
   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.
@@ -5869,7 +6207,7 @@ must_pass_in_stack_var_size (const function_arg_info &arg)
     return false;
 
   /* If the type has variable size...  */
-  if (TREE_CODE (TYPE_SIZE (arg.type)) != INTEGER_CST)
+  if (!poly_int_tree_p (TYPE_SIZE (arg.type)))
     return true;
 
   /* If the type is marked as addressable (it is required
@@ -5923,5 +6261,20 @@ must_pass_va_arg_in_stack (tree type)
   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"