PR testsuite/83131 - c-c++/common/attr-nonstring-3 failure for strcmp tests on PowerPC
authorMartin Sebor <msebor@redhat.com>
Wed, 20 Dec 2017 16:56:20 +0000 (16:56 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Wed, 20 Dec 2017 16:56:20 +0000 (09:56 -0700)
gcc/ChangeLog:

PR testsuite/83131
* builtins.c (expand_builtin_strlen): Use get_callee_fndecl.
(expand_builtin_strcmp): Call maybe_warn_nonstring_arg.
(expand_builtin_strncmp): Same.

gcc/testsuite/ChangeLog:

PR testsuite/83131
* c-c++-common/attr-nonstring-4.c: New test.

From-SVN: r255898

gcc/ChangeLog
gcc/builtins.c
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/attr-nonstring-4.c [new file with mode: 0644]

index 6e98187f0161aaa05603d41411cbd66fbc0cbf3e..2f23a2b88600911f631c0db7ea080180485d3413 100644 (file)
@@ -1,3 +1,10 @@
+2017-12-20  Martin Sebor  <msebor@redhat.com>
+
+       PR testsuite/83131
+       * builtins.c (expand_builtin_strlen): Use get_callee_fndecl.
+       (expand_builtin_strcmp): Call maybe_warn_nonstring_arg. 
+       (expand_builtin_strncmp): Same.
+
 2017-12-20  Alexandre Oliva <aoliva@redhat.com>
 
        PR bootstrap/83396
index 277bbe8db269d69791251a66d49ecff8e0d9e43a..f8b853c6ecd03466a771b06831ada7feaf6e2f54 100644 (file)
@@ -2829,102 +2829,100 @@ expand_builtin_strlen (tree exp, rtx target,
 {
   if (!validate_arglist (exp, POINTER_TYPE, VOID_TYPE))
     return NULL_RTX;
-  else
-    {
-      struct expand_operand ops[4];
-      rtx pat;
-      tree len;
-      tree src = CALL_EXPR_ARG (exp, 0);
-      rtx src_reg;
-      rtx_insn *before_strlen;
-      machine_mode insn_mode;
-      enum insn_code icode = CODE_FOR_nothing;
-      unsigned int align;
 
-      /* If the length can be computed at compile-time, return it.  */
-      len = c_strlen (src, 0);
-      if (len)
-       return expand_expr (len, target, target_mode, EXPAND_NORMAL);
-
-      /* If the length can be computed at compile-time and is constant
-        integer, but there are side-effects in src, evaluate
-        src for side-effects, then return len.
-        E.g. x = strlen (i++ ? "xfoo" + 1 : "bar");
-        can be optimized into: i++; x = 3;  */
-      len = c_strlen (src, 1);
-      if (len && TREE_CODE (len) == INTEGER_CST)
-       {
-         expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL);
-         return expand_expr (len, target, target_mode, EXPAND_NORMAL);
-       }
+  struct expand_operand ops[4];
+  rtx pat;
+  tree len;
+  tree src = CALL_EXPR_ARG (exp, 0);
+  rtx src_reg;
+  rtx_insn *before_strlen;
+  machine_mode insn_mode;
+  enum insn_code icode = CODE_FOR_nothing;
+  unsigned int align;
 
-      align = get_pointer_alignment (src) / BITS_PER_UNIT;
+  /* If the length can be computed at compile-time, return it.  */
+  len = c_strlen (src, 0);
+  if (len)
+    return expand_expr (len, target, target_mode, EXPAND_NORMAL);
 
-      /* If SRC is not a pointer type, don't do this operation inline.  */
-      if (align == 0)
-       return NULL_RTX;
+  /* If the length can be computed at compile-time and is constant
+     integer, but there are side-effects in src, evaluate
+     src for side-effects, then return len.
+     E.g. x = strlen (i++ ? "xfoo" + 1 : "bar");
+     can be optimized into: i++; x = 3;  */
+  len = c_strlen (src, 1);
+  if (len && TREE_CODE (len) == INTEGER_CST)
+    {
+      expand_expr (src, const0_rtx, VOIDmode, EXPAND_NORMAL);
+      return expand_expr (len, target, target_mode, EXPAND_NORMAL);
+    }
 
-      /* Bail out if we can't compute strlen in the right mode.  */
-      FOR_EACH_MODE_FROM (insn_mode, target_mode)
-       {
-         icode = optab_handler (strlen_optab, insn_mode);
-         if (icode != CODE_FOR_nothing)
-           break;
-       }
-      if (insn_mode == VOIDmode)
-       return NULL_RTX;
+  align = get_pointer_alignment (src) / BITS_PER_UNIT;
+
+  /* If SRC is not a pointer type, don't do this operation inline.  */
+  if (align == 0)
+    return NULL_RTX;
 
-      /* Make a place to hold the source address.  We will not expand
-        the actual source until we are sure that the expansion will
-        not fail -- there are trees that cannot be expanded twice.  */
-      src_reg = gen_reg_rtx (Pmode);
+  /* Bail out if we can't compute strlen in the right mode.  */
+  FOR_EACH_MODE_FROM (insn_mode, target_mode)
+    {
+      icode = optab_handler (strlen_optab, insn_mode);
+      if (icode != CODE_FOR_nothing)
+       break;
+    }
+  if (insn_mode == VOIDmode)
+    return NULL_RTX;
 
-      /* Mark the beginning of the strlen sequence so we can emit the
-        source operand later.  */
-      before_strlen = get_last_insn ();
+  /* Make a place to hold the source address.  We will not expand
+     the actual source until we are sure that the expansion will
+     not fail -- there are trees that cannot be expanded twice.  */
+  src_reg = gen_reg_rtx (Pmode);
 
-      create_output_operand (&ops[0], target, insn_mode);
-      create_fixed_operand (&ops[1], gen_rtx_MEM (BLKmode, src_reg));
-      create_integer_operand (&ops[2], 0);
-      create_integer_operand (&ops[3], align);
-      if (!maybe_expand_insn (icode, 4, ops))
-       return NULL_RTX;
+  /* Mark the beginning of the strlen sequence so we can emit the
+     source operand later.  */
+  before_strlen = get_last_insn ();
 
-      /* Check to see if the argument was declared attribute nonstring
-        and if so, issue a warning since at this point it's not known
-        to be nul-terminated.  */
-      maybe_warn_nonstring_arg (TREE_OPERAND (CALL_EXPR_FN (exp), 0), exp);
+  create_output_operand (&ops[0], target, insn_mode);
+  create_fixed_operand (&ops[1], gen_rtx_MEM (BLKmode, src_reg));
+  create_integer_operand (&ops[2], 0);
+  create_integer_operand (&ops[3], align);
+  if (!maybe_expand_insn (icode, 4, ops))
+    return NULL_RTX;
 
-      /* Now that we are assured of success, expand the source.  */
-      start_sequence ();
-      pat = expand_expr (src, src_reg, Pmode, EXPAND_NORMAL);
-      if (pat != src_reg)
-       {
+  /* Check to see if the argument was declared attribute nonstring
+     and if so, issue a warning since at this point it's not known
+     to be nul-terminated.  */
+  maybe_warn_nonstring_arg (get_callee_fndecl (exp), exp);
+
+  /* Now that we are assured of success, expand the source.  */
+  start_sequence ();
+  pat = expand_expr (src, src_reg, Pmode, EXPAND_NORMAL);
+  if (pat != src_reg)
+    {
 #ifdef POINTERS_EXTEND_UNSIGNED
-         if (GET_MODE (pat) != Pmode)
-           pat = convert_to_mode (Pmode, pat,
-                                  POINTERS_EXTEND_UNSIGNED);
+      if (GET_MODE (pat) != Pmode)
+       pat = convert_to_mode (Pmode, pat,
+                              POINTERS_EXTEND_UNSIGNED);
 #endif
-         emit_move_insn (src_reg, pat);
-       }
-      pat = get_insns ();
-      end_sequence ();
+      emit_move_insn (src_reg, pat);
+    }
+  pat = get_insns ();
+  end_sequence ();
 
-      if (before_strlen)
-       emit_insn_after (pat, before_strlen);
-      else
-       emit_insn_before (pat, get_insns ());
+  if (before_strlen)
+    emit_insn_after (pat, before_strlen);
+  else
+    emit_insn_before (pat, get_insns ());
 
-      /* Return the value in the proper mode for this function.  */
-      if (GET_MODE (ops[0].value) == target_mode)
-       target = ops[0].value;
-      else if (target != 0)
-       convert_move (target, ops[0].value, 0);
-      else
-       target = convert_to_mode (target_mode, ops[0].value, 0);
+  /* Return the value in the proper mode for this function.  */
+  if (GET_MODE (ops[0].value) == target_mode)
+    target = ops[0].value;
+  else if (target != 0)
+    convert_move (target, ops[0].value, 0);
+  else
+    target = convert_to_mode (target_mode, ops[0].value, 0);
 
-      return target;
-    }
+  return target;
 }
 
 /* Callback routine for store_by_pieces.  Read GET_MODE_BITSIZE (MODE)
@@ -4485,102 +4483,104 @@ expand_builtin_strcmp (tree exp, ATTRIBUTE_UNUSED rtx target)
 
   insn_code cmpstr_icode = direct_optab_handler (cmpstr_optab, SImode);
   insn_code cmpstrn_icode = direct_optab_handler (cmpstrn_optab, SImode);
-  if (cmpstr_icode != CODE_FOR_nothing || cmpstrn_icode != CODE_FOR_nothing)
-    {
-      rtx arg1_rtx, arg2_rtx;
-      tree fndecl, fn;
-      tree arg1 = CALL_EXPR_ARG (exp, 0);
-      tree arg2 = CALL_EXPR_ARG (exp, 1);
-      rtx result = NULL_RTX;
+  if (cmpstr_icode == CODE_FOR_nothing && cmpstrn_icode == CODE_FOR_nothing)
+    return NULL_RTX;
 
-      unsigned int arg1_align = get_pointer_alignment (arg1) / BITS_PER_UNIT;
-      unsigned int arg2_align = get_pointer_alignment (arg2) / BITS_PER_UNIT;
+  tree arg1 = CALL_EXPR_ARG (exp, 0);
+  tree arg2 = CALL_EXPR_ARG (exp, 1);
 
-      /* If we don't have POINTER_TYPE, call the function.  */
-      if (arg1_align == 0 || arg2_align == 0)
-       return NULL_RTX;
+  unsigned int arg1_align = get_pointer_alignment (arg1) / BITS_PER_UNIT;
+  unsigned int arg2_align = get_pointer_alignment (arg2) / BITS_PER_UNIT;
 
-      /* Stabilize the arguments in case gen_cmpstr(n)si fail.  */
-      arg1 = builtin_save_expr (arg1);
-      arg2 = builtin_save_expr (arg2);
+  /* If we don't have POINTER_TYPE, call the function.  */
+  if (arg1_align == 0 || arg2_align == 0)
+    return NULL_RTX;
 
-      arg1_rtx = get_memory_rtx (arg1, NULL);
-      arg2_rtx = get_memory_rtx (arg2, NULL);
+  /* Stabilize the arguments in case gen_cmpstr(n)si fail.  */
+  arg1 = builtin_save_expr (arg1);
+  arg2 = builtin_save_expr (arg2);
 
-      /* Try to call cmpstrsi.  */
-      if (cmpstr_icode != CODE_FOR_nothing)
-       result = expand_cmpstr (cmpstr_icode, target, arg1_rtx, arg2_rtx,
-                               MIN (arg1_align, arg2_align));
+  rtx arg1_rtx = get_memory_rtx (arg1, NULL);
+  rtx arg2_rtx = get_memory_rtx (arg2, NULL);
 
-      /* Try to determine at least one length and call cmpstrnsi.  */
-      if (!result && cmpstrn_icode != CODE_FOR_nothing)
-       {
-         tree len;
-         rtx arg3_rtx;
-
-         tree len1 = c_strlen (arg1, 1);
-         tree len2 = c_strlen (arg2, 1);
-
-         if (len1)
-           len1 = size_binop (PLUS_EXPR, ssize_int (1), len1);
-         if (len2)
-           len2 = size_binop (PLUS_EXPR, ssize_int (1), len2);
-
-         /* If we don't have a constant length for the first, use the length
-            of the second, if we know it.  We don't require a constant for
-            this case; some cost analysis could be done if both are available
-            but neither is constant.  For now, assume they're equally cheap,
-            unless one has side effects.  If both strings have constant lengths,
-            use the smaller.  */
-
-         if (!len1)
-           len = len2;
-         else if (!len2)
-           len = len1;
-         else if (TREE_SIDE_EFFECTS (len1))
-           len = len2;
-         else if (TREE_SIDE_EFFECTS (len2))
-           len = len1;
-         else if (TREE_CODE (len1) != INTEGER_CST)
-           len = len2;
-         else if (TREE_CODE (len2) != INTEGER_CST)
-           len = len1;
-         else if (tree_int_cst_lt (len1, len2))
-           len = len1;
-         else
-           len = len2;
+  rtx result = NULL_RTX;
+  /* Try to call cmpstrsi.  */
+  if (cmpstr_icode != CODE_FOR_nothing)
+    result = expand_cmpstr (cmpstr_icode, target, arg1_rtx, arg2_rtx,
+                           MIN (arg1_align, arg2_align));
 
-         /* If both arguments have side effects, we cannot optimize.  */
-         if (len && !TREE_SIDE_EFFECTS (len))
-           {
-             arg3_rtx = expand_normal (len);
-             result = expand_cmpstrn_or_cmpmem
-               (cmpstrn_icode, target, arg1_rtx, arg2_rtx, TREE_TYPE (len),
-                arg3_rtx, MIN (arg1_align, arg2_align));
-           }
-       }
+  /* Try to determine at least one length and call cmpstrnsi.  */
+  if (!result && cmpstrn_icode != CODE_FOR_nothing)
+    {
+      tree len;
+      rtx arg3_rtx;
+
+      tree len1 = c_strlen (arg1, 1);
+      tree len2 = c_strlen (arg2, 1);
+
+      if (len1)
+       len1 = size_binop (PLUS_EXPR, ssize_int (1), len1);
+      if (len2)
+       len2 = size_binop (PLUS_EXPR, ssize_int (1), len2);
+
+      /* If we don't have a constant length for the first, use the length
+        of the second, if we know it.  We don't require a constant for
+        this case; some cost analysis could be done if both are available
+        but neither is constant.  For now, assume they're equally cheap,
+        unless one has side effects.  If both strings have constant lengths,
+        use the smaller.  */
+
+      if (!len1)
+       len = len2;
+      else if (!len2)
+       len = len1;
+      else if (TREE_SIDE_EFFECTS (len1))
+       len = len2;
+      else if (TREE_SIDE_EFFECTS (len2))
+       len = len1;
+      else if (TREE_CODE (len1) != INTEGER_CST)
+       len = len2;
+      else if (TREE_CODE (len2) != INTEGER_CST)
+       len = len1;
+      else if (tree_int_cst_lt (len1, len2))
+       len = len1;
+      else
+       len = len2;
 
-      if (result)
+      /* If both arguments have side effects, we cannot optimize.  */
+      if (len && !TREE_SIDE_EFFECTS (len))
        {
-         /* Return the value in the proper mode for this function.  */
-         machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
-         if (GET_MODE (result) == mode)
-           return result;
-         if (target == 0)
-           return convert_to_mode (mode, result, 0);
-         convert_move (target, result, 0);
-         return target;
+         arg3_rtx = expand_normal (len);
+         result = expand_cmpstrn_or_cmpmem
+           (cmpstrn_icode, target, arg1_rtx, arg2_rtx, TREE_TYPE (len),
+            arg3_rtx, MIN (arg1_align, arg2_align));
        }
+    }
 
-      /* Expand the library call ourselves using a stabilized argument
-        list to avoid re-evaluating the function's arguments twice.  */
-      fndecl = get_callee_fndecl (exp);
-      fn = build_call_nofold_loc (EXPR_LOCATION (exp), fndecl, 2, arg1, arg2);
-      gcc_assert (TREE_CODE (fn) == CALL_EXPR);
-      CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
-      return expand_call (fn, target, target == const0_rtx);
+  /* Check to see if the argument was declared attribute nonstring
+     and if so, issue a warning since at this point it's not known
+     to be nul-terminated.  */
+  tree fndecl = get_callee_fndecl (exp);
+  maybe_warn_nonstring_arg (fndecl, exp);
+
+  if (result)
+    {
+      /* Return the value in the proper mode for this function.  */
+      machine_mode mode = TYPE_MODE (TREE_TYPE (exp));
+      if (GET_MODE (result) == mode)
+       return result;
+      if (target == 0)
+       return convert_to_mode (mode, result, 0);
+      convert_move (target, result, 0);
+      return target;
     }
-  return NULL_RTX;
+
+  /* Expand the library call ourselves using a stabilized argument
+     list to avoid re-evaluating the function's arguments twice.  */
+  tree fn = build_call_nofold_loc (EXPR_LOCATION (exp), fndecl, 2, arg1, arg2);
+  gcc_assert (TREE_CODE (fn) == CALL_EXPR);
+  CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
+  return expand_call (fn, target, target == const0_rtx);
 }
 
 /* Expand expression EXP, which is a call to the strncmp builtin. Return
@@ -4591,8 +4591,6 @@ static rtx
 expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx target,
                        ATTRIBUTE_UNUSED machine_mode mode)
 {
-  location_t loc ATTRIBUTE_UNUSED = EXPR_LOCATION (exp);
-
   if (!validate_arglist (exp,
                         POINTER_TYPE, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
     return NULL_RTX;
@@ -4601,88 +4599,92 @@ expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx target,
      lengths, and it doesn't have side effects, then emit cmpstrnsi
      using length MIN(strlen(string)+1, arg3).  */
   insn_code cmpstrn_icode = direct_optab_handler (cmpstrn_optab, SImode);
-  if (cmpstrn_icode != CODE_FOR_nothing)
-  {
-    tree len, len1, len2, len3;
-    rtx arg1_rtx, arg2_rtx, arg3_rtx;
-    rtx result;
-    tree fndecl, fn;
-    tree arg1 = CALL_EXPR_ARG (exp, 0);
-    tree arg2 = CALL_EXPR_ARG (exp, 1);
-    tree arg3 = CALL_EXPR_ARG (exp, 2);
-
-    unsigned int arg1_align = get_pointer_alignment (arg1) / BITS_PER_UNIT;
-    unsigned int arg2_align = get_pointer_alignment (arg2) / BITS_PER_UNIT;
-
-    len1 = c_strlen (arg1, 1);
-    len2 = c_strlen (arg2, 1);
-
-    if (len1)
-      len1 = size_binop_loc (loc, PLUS_EXPR, ssize_int (1), len1);
-    if (len2)
-      len2 = size_binop_loc (loc, PLUS_EXPR, ssize_int (1), len2);
-
-    len3 = fold_convert_loc (loc, sizetype, arg3);
-
-    /* If we don't have a constant length for the first, use the length
-       of the second, if we know it.  If neither string is constant length,
-       use the given length argument.  We don't require a constant for
-       this case; some cost analysis could be done if both are available
-       but neither is constant.  For now, assume they're equally cheap,
-       unless one has side effects.  If both strings have constant lengths,
-       use the smaller.  */
-
-    if (!len1 && !len2)
-      len = len3;
-    else if (!len1)
-      len = len2;
-    else if (!len2)
-      len = len1;
-    else if (TREE_SIDE_EFFECTS (len1))
-      len = len2;
-    else if (TREE_SIDE_EFFECTS (len2))
-      len = len1;
-    else if (TREE_CODE (len1) != INTEGER_CST)
-      len = len2;
-    else if (TREE_CODE (len2) != INTEGER_CST)
-      len = len1;
-    else if (tree_int_cst_lt (len1, len2))
-      len = len1;
-    else
-      len = len2;
-
-    /* If we are not using the given length, we must incorporate it here.
-       The actual new length parameter will be MIN(len,arg3) in this case.  */
-    if (len != len3)
-      len = fold_build2_loc (loc, MIN_EXPR, TREE_TYPE (len), len, len3);
-    arg1_rtx = get_memory_rtx (arg1, len);
-    arg2_rtx = get_memory_rtx (arg2, len);
-    arg3_rtx = expand_normal (len);
-    result = expand_cmpstrn_or_cmpmem (cmpstrn_icode, target, arg1_rtx,
-                                      arg2_rtx, TREE_TYPE (len), arg3_rtx,
-                                      MIN (arg1_align, arg2_align));
-    if (result)
-      {
-       /* Return the value in the proper mode for this function.  */
-       mode = TYPE_MODE (TREE_TYPE (exp));
-       if (GET_MODE (result) == mode)
-         return result;
-       if (target == 0)
-         return convert_to_mode (mode, result, 0);
-       convert_move (target, result, 0);
-       return target;
-      }
+  if (cmpstrn_icode == CODE_FOR_nothing)
+    return NULL_RTX;
 
-    /* Expand the library call ourselves using a stabilized argument
-       list to avoid re-evaluating the function's arguments twice.  */
-    fndecl = get_callee_fndecl (exp);
-    fn = build_call_nofold_loc (EXPR_LOCATION (exp), fndecl, 3,
-                               arg1, arg2, len);
-    gcc_assert (TREE_CODE (fn) == CALL_EXPR);
-    CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
-    return expand_call (fn, target, target == const0_rtx);
-  }
-  return NULL_RTX;
+  tree len;
+
+  tree arg1 = CALL_EXPR_ARG (exp, 0);
+  tree arg2 = CALL_EXPR_ARG (exp, 1);
+  tree arg3 = CALL_EXPR_ARG (exp, 2);
+
+  unsigned int arg1_align = get_pointer_alignment (arg1) / BITS_PER_UNIT;
+  unsigned int arg2_align = get_pointer_alignment (arg2) / BITS_PER_UNIT;
+
+  tree len1 = c_strlen (arg1, 1);
+  tree len2 = c_strlen (arg2, 1);
+
+  location_t loc = EXPR_LOCATION (exp);
+
+  if (len1)
+    len1 = size_binop_loc (loc, PLUS_EXPR, ssize_int (1), len1);
+  if (len2)
+    len2 = size_binop_loc (loc, PLUS_EXPR, ssize_int (1), len2);
+
+  tree len3 = fold_convert_loc (loc, sizetype, arg3);
+
+  /* If we don't have a constant length for the first, use the length
+     of the second, if we know it.  If neither string is constant length,
+     use the given length argument.  We don't require a constant for
+     this case; some cost analysis could be done if both are available
+     but neither is constant.  For now, assume they're equally cheap,
+     unless one has side effects.  If both strings have constant lengths,
+     use the smaller.  */
+
+  if (!len1 && !len2)
+    len = len3;
+  else if (!len1)
+    len = len2;
+  else if (!len2)
+    len = len1;
+  else if (TREE_SIDE_EFFECTS (len1))
+    len = len2;
+  else if (TREE_SIDE_EFFECTS (len2))
+    len = len1;
+  else if (TREE_CODE (len1) != INTEGER_CST)
+    len = len2;
+  else if (TREE_CODE (len2) != INTEGER_CST)
+    len = len1;
+  else if (tree_int_cst_lt (len1, len2))
+    len = len1;
+  else
+    len = len2;
+
+  /* If we are not using the given length, we must incorporate it here.
+     The actual new length parameter will be MIN(len,arg3) in this case.  */
+  if (len != len3)
+    len = fold_build2_loc (loc, MIN_EXPR, TREE_TYPE (len), len, len3);
+  rtx arg1_rtx = get_memory_rtx (arg1, len);
+  rtx arg2_rtx = get_memory_rtx (arg2, len);
+  rtx arg3_rtx = expand_normal (len);
+  rtx result = expand_cmpstrn_or_cmpmem (cmpstrn_icode, target, arg1_rtx,
+                                        arg2_rtx, TREE_TYPE (len), arg3_rtx,
+                                        MIN (arg1_align, arg2_align));
+
+  /* Check to see if the argument was declared attribute nonstring
+     and if so, issue a warning since at this point it's not known
+     to be nul-terminated.  */
+  tree fndecl = get_callee_fndecl (exp);
+  maybe_warn_nonstring_arg (fndecl, exp);
+
+  if (result)
+    {
+      /* Return the value in the proper mode for this function.  */
+      mode = TYPE_MODE (TREE_TYPE (exp));
+      if (GET_MODE (result) == mode)
+       return result;
+      if (target == 0)
+       return convert_to_mode (mode, result, 0);
+      convert_move (target, result, 0);
+      return target;
+    }
+
+  /* Expand the library call ourselves using a stabilized argument
+     list to avoid re-evaluating the function's arguments twice.  */
+  tree fn = build_call_nofold_loc (loc, fndecl, 3, arg1, arg2, len);
+  gcc_assert (TREE_CODE (fn) == CALL_EXPR);
+  CALL_EXPR_TAILCALL (fn) = CALL_EXPR_TAILCALL (exp);
+  return expand_call (fn, target, target == const0_rtx);
 }
 
 /* Expand a call to __builtin_saveregs, generating the result in TARGET,
index 76f1b795c638217e69a8ad4a44352d1f925a0d0d..3eb1f534cf32922f4f6585686101da3e6911a0c9 100644 (file)
@@ -1,3 +1,8 @@
+2017-12-20  Martin Sebor  <msebor@redhat.com>
+
+       PR testsuite/83131
+       * c-c++-common/attr-nonstring-4.c: New test.
+
 2017-12-20  Richard Sandiford  <richard.sandiford@linaro.org>
 
        * gcc.dg/plugin/poly-int-tests.h (test_nonpoly_multiple_p): New
diff --git a/gcc/testsuite/c-c++-common/attr-nonstring-4.c b/gcc/testsuite/c-c++-common/attr-nonstring-4.c
new file mode 100644 (file)
index 0000000..0571e46
--- /dev/null
@@ -0,0 +1,79 @@
+/* PR middle-end/83131 - c-c++/common/attr-nonstring-3 failure for strcmp
+   tests on PowerPC
+   { dg-do compile }
+   { dg-options "-O2 -Wstringop-overflow -ftrack-macro-expansion=0" }  */
+
+#if __cplusplus
+extern "C" {
+#endif
+
+typedef __SIZE_TYPE__ size_t;
+
+extern int strcmp (const char*, const char*);
+extern int strncmp (const char*, const char*, size_t);
+
+#if __cplusplus
+}   /* extern "C" */
+#endif
+
+extern char arx[] __attribute__ ((nonstring));
+extern char ar5[5] __attribute__ ((nonstring));
+extern char str[];
+
+enum { N = sizeof ar5 };
+enum { X = sizeof ar5 + 1 };
+
+
+int warn_strcmp_cst_1 (void)
+{
+  return strcmp ("bar", arx);       /* { dg-warning "argument 2 declared attribute .nonstring." } */
+}
+
+int warn_strcmp_cst_2 (void)
+{
+  return strcmp (arx, "foo");       /* { dg-warning "argument 1 declared attribute .nonstring." } */
+}
+
+
+int warn_strncmp_cst_1 (void)
+{
+  return strncmp ("bar", ar5, X);   /* { dg-warning "argument 2 declared attribute .nonstring." } */
+}
+
+int warn_strncmp_cst_2 (void)
+{
+  return strncmp (ar5, "foo", X);   /* { dg-warning "argument 1 declared attribute .nonstring." } */
+}
+
+
+int nowarn_strncmp_cst_1 (void)
+{
+  return strncmp ("bar", ar5, N);
+}
+
+int nowarn_strncmp_cst_2 (void)
+{
+  return strncmp (ar5, "foo", N);
+}
+
+
+int warn_strncmp_var_1 (void)
+{
+  return strncmp (str, ar5, X);     /* { dg-warning "argument 2 declared attribute .nonstring." } */
+}
+
+int warn_strncmp_var_2 (void)
+{
+  return strncmp (ar5, str, X);     /* { dg-warning "argument 1 declared attribute .nonstring." } */
+}
+
+
+int nowarn_strncmp_var_1 (void)
+{
+  return strncmp (str, ar5, N);
+}
+
+int nowarn_strncmp_var_2 (void)
+{
+  return strncmp (ar5, str, N);
+}