Fix excess precision handling of compound assignments (PR c/82071).
authorJoseph Myers <joseph@codesourcery.com>
Fri, 1 Sep 2017 16:29:49 +0000 (17:29 +0100)
committerJoseph Myers <jsm28@gcc.gnu.org>
Fri, 1 Sep 2017 16:29:49 +0000 (17:29 +0100)
PR c/82071 reports how compound assignment operators such as += handle
excess precision inconsistently with the same operation done with a
plain assignment and binary operator.

There were (at least) two problems with how compound assignments
handled excess precision.  The EXCESS_PRECISION_EXPR for an argument
with excess precision was removed too early, resulting in
build_binary_op being called with an rhs operand whose type reflected
the evaluation format, so not having sufficient information to achieve
the intended semantics in all cases, and then the code called
c_fully_fold on the results of build_binary_op without allowing for
the possibility of an EXCESS_PRECISION_EXPR as the result, so leading
to double rounding of the result (first to its semantic type, then to
the type of the LHS of the assignment) instead of the intended single
rounding.

This patch fixes those problems by keeping EXCESS_PRECISION_EXPRs
further through build_modify_expr (and build_atomic_assign which it
calls) and only removing them locally where appropriate.

Note that while this patch should achieve *consistency*, that's
consistency with the understanding of C99 semantics that I originally
intended to implement.  For the particular case in the testcase, C11
semantics (from N1531) differ from that understanding of C99
semantics, in that an implicit conversion of an integer to floating
point can have excess precision.  I intend to implement those C11
semantics separately (conditional on flag_isoc11) (which will also
mean that building conditional expressions can produce a result with
excess precision even when the arguments lack excess precision, where
previously it could not), and not to close the bug until that is also
done.

Tested for x86_64-pc-linux-gnu.

PR c/82071
gcc/c:
* c-typeck.c (build_atomic_assign): Handle argument with excess
precision.  Ensure any EXCESS_PRECISION_EXPR is present in
argument passed to build_binary_op and convert_for_assignment but
not for call to c_fully_fold.
(build_modify_expr): Do not remove EXCESS_PRECISION_EXPR early.
Ensure build_binary_op is called with argument with original
semantic type.  Avoid calling c_fully_fold with an
EXCESS_PRECISION_EXPR from build_binary_op.

gcc/testsuite:
* gcc.target/i386/excess-precision-7.c: New test.

From-SVN: r251603

gcc/c/ChangeLog
gcc/c/c-typeck.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.target/i386/excess-precision-7.c [new file with mode: 0644]

index 6f8c272397b85285e4fcbc6ab0ebe8bac3d1499b..2d25c4816c650c39d14ce11447ceefec275b6232 100644 (file)
@@ -1,3 +1,15 @@
+2017-09-01  Joseph Myers  <joseph@codesourcery.com>
+
+       PR c/82071
+       * c-typeck.c (build_atomic_assign): Handle argument with excess
+       precision.  Ensure any EXCESS_PRECISION_EXPR is present in
+       argument passed to build_binary_op and convert_for_assignment but
+       not for call to c_fully_fold.
+       (build_modify_expr): Do not remove EXCESS_PRECISION_EXPR early.
+       Ensure build_binary_op is called with argument with original
+       semantic type.  Avoid calling c_fully_fold with an
+       EXCESS_PRECISION_EXPR from build_binary_op.
+
 2017-09-01  Jakub Jelinek  <jakub@redhat.com>
 
        PR c/81887
index 135dd9d665cb099a17d942f1ba452a999db2974c..91996c95ed070c0b68f024a0df9aa054d2a84a93 100644 (file)
@@ -3919,7 +3919,9 @@ build_atomic_assign (location_t loc, tree lhs, enum tree_code modifycode,
   tree lhs_type = TREE_TYPE (lhs);
   tree lhs_addr = build_unary_op (loc, ADDR_EXPR, lhs, false);
   tree seq_cst = build_int_cst (integer_type_node, MEMMODEL_SEQ_CST);
-  tree rhs_type = TREE_TYPE (rhs);
+  tree rhs_semantic_type = TREE_TYPE (rhs);
+  tree nonatomic_rhs_semantic_type;
+  tree rhs_type;
 
   gcc_assert (TYPE_ATOMIC (lhs_type));
 
@@ -3933,6 +3935,15 @@ build_atomic_assign (location_t loc, tree lhs, enum tree_code modifycode,
      with a loop.  */
   compound_stmt = c_begin_compound_stmt (false);
 
+  /* Remove any excess precision (which is only present here in the
+     case of compound assignments).  */
+  if (TREE_CODE (rhs) == EXCESS_PRECISION_EXPR)
+    {
+      gcc_assert (modifycode != NOP_EXPR);
+      rhs = TREE_OPERAND (rhs, 0);
+    }
+  rhs_type = TREE_TYPE (rhs);
+
   /* Fold the RHS if it hasn't already been folded.  */
   if (modifycode != NOP_EXPR)
     rhs = c_fully_fold (rhs, false, NULL);
@@ -3941,6 +3952,8 @@ build_atomic_assign (location_t loc, tree lhs, enum tree_code modifycode,
      the VAL temp variable to hold the RHS.  */
   nonatomic_lhs_type = build_qualified_type (lhs_type, TYPE_UNQUALIFIED);
   nonatomic_rhs_type = build_qualified_type (rhs_type, TYPE_UNQUALIFIED);
+  nonatomic_rhs_semantic_type = build_qualified_type (rhs_semantic_type,
+                                                     TYPE_UNQUALIFIED);
   val = create_tmp_var_raw (nonatomic_rhs_type);
   TREE_ADDRESSABLE (val) = 1;
   TREE_NO_WARNING (val) = 1;
@@ -4100,8 +4113,17 @@ cas_loop:
   add_stmt (loop_label);
 
   /* newval = old + val;  */
+  if (rhs_type != rhs_semantic_type)
+    val = build1 (EXCESS_PRECISION_EXPR, nonatomic_rhs_semantic_type, val);
   rhs = build_binary_op (loc, modifycode, old, val, true);
-  rhs = c_fully_fold (rhs, false, NULL);
+  if (TREE_CODE (rhs) == EXCESS_PRECISION_EXPR)
+    {
+      tree eptype = TREE_TYPE (rhs);
+      rhs = c_fully_fold (TREE_OPERAND (rhs, 0), false, NULL);
+      rhs = build1 (EXCESS_PRECISION_EXPR, eptype, rhs);
+    }
+  else
+    rhs = c_fully_fold (rhs, false, NULL);
   rhs = convert_for_assignment (loc, UNKNOWN_LOCATION, nonatomic_lhs_type,
                                rhs, NULL_TREE, ic_assign, false, NULL_TREE,
                                NULL_TREE, 0);
@@ -5727,7 +5749,6 @@ build_modify_expr (location_t location, tree lhs, tree lhs_origtype,
   tree result;
   tree newrhs;
   tree rhseval = NULL_TREE;
-  tree rhs_semantic_type = NULL_TREE;
   tree lhstype = TREE_TYPE (lhs);
   tree olhstype = lhstype;
   bool npc;
@@ -5754,12 +5775,6 @@ build_modify_expr (location_t location, tree lhs, tree lhs_origtype,
 
   is_atomic_op = really_atomic_lvalue (lhs);
 
-  if (TREE_CODE (rhs) == EXCESS_PRECISION_EXPR)
-    {
-      rhs_semantic_type = TREE_TYPE (rhs);
-      rhs = TREE_OPERAND (rhs, 0);
-    }
-
   newrhs = rhs;
 
   if (TREE_CODE (lhs) == C_MAYBE_CONST_EXPR)
@@ -5794,8 +5809,14 @@ build_modify_expr (location_t location, tree lhs, tree lhs_origtype,
             that modify LHS.  */
          if (TREE_SIDE_EFFECTS (rhs))
            {
-             newrhs = save_expr (rhs);
+             if (TREE_CODE (rhs) == EXCESS_PRECISION_EXPR)
+               newrhs = save_expr (TREE_OPERAND (rhs, 0));
+             else
+               newrhs = save_expr (rhs);
              rhseval = newrhs;
+             if (TREE_CODE (rhs) == EXCESS_PRECISION_EXPR)
+               newrhs = build1 (EXCESS_PRECISION_EXPR, TREE_TYPE (rhs),
+                                newrhs);
            }
          newrhs = build_binary_op (location,
                                    modifycode, lhs, newrhs, true);
@@ -5810,7 +5831,10 @@ build_modify_expr (location_t location, tree lhs, tree lhs_origtype,
     {
       /* Check if we are modifying an Objective-C property reference;
         if so, we need to generate setter calls.  */
-      result = objc_maybe_build_modify_expr (lhs, newrhs);
+      if (TREE_CODE (newrhs) == EXCESS_PRECISION_EXPR)
+       result = objc_maybe_build_modify_expr (lhs, TREE_OPERAND (newrhs, 0));
+      else
+       result = objc_maybe_build_modify_expr (lhs, newrhs);
       if (result)
        goto return_result;
 
@@ -5887,6 +5911,12 @@ build_modify_expr (location_t location, tree lhs, tree lhs_origtype,
 
   if (!(is_atomic_op && modifycode != NOP_EXPR))
     {
+      tree rhs_semantic_type = NULL_TREE;
+      if (TREE_CODE (newrhs) == EXCESS_PRECISION_EXPR)
+       {
+         rhs_semantic_type = TREE_TYPE (newrhs);
+         newrhs = TREE_OPERAND (newrhs, 0);
+       }
       npc = null_pointer_constant_p (newrhs);
       newrhs = c_fully_fold (newrhs, false, NULL);
       if (rhs_semantic_type)
index e82751438d65691971f65aba6903679652b9a29a..20f579e391da6d0c3901aa3a7574dfb7d5b066c9 100644 (file)
@@ -1,3 +1,8 @@
+2017-09-01  Joseph Myers  <joseph@codesourcery.com>
+
+       PR c/82071
+       * gcc.target/i386/excess-precision-7.c: New test.
+
 2017-09-01  Andreas Krebbel  <krebbel@linux.vnet.ibm.com>
 
        PR target/82012
diff --git a/gcc/testsuite/gcc.target/i386/excess-precision-7.c b/gcc/testsuite/gcc.target/i386/excess-precision-7.c
new file mode 100644 (file)
index 0000000..0cdd932
--- /dev/null
@@ -0,0 +1,41 @@
+/* Excess precision tests.  Test C99 semantics for conversions from
+   integers to floating point: no excess precision for either explicit
+   or implicit conversions.  */
+/* { dg-do run } */
+/* { dg-options "-std=c99 -mfpmath=387 -fexcess-precision=standard" } */
+
+extern void abort (void);
+extern void exit (int);
+
+int
+main (void)
+{
+  float f = 1.0f;
+  int i;
+
+  i = 0x10001234;
+  if ((float) i != 0x10001240)
+    abort ();
+
+  i = 0x10001234;
+  i += f;
+  if (i != 0x10001241)
+    abort ();
+
+  i = 0x10001234;
+  i += 1.0f;
+  if (i != 0x10001241)
+    abort ();
+
+  i = 0x10001234;
+  i = i + f;
+  if (i != 0x10001241)
+    abort ();
+
+  i = 0x10001234;
+  i = i + 1.0f;
+  if (i != 0x10001241)
+    abort ();
+
+  exit (0);
+}