init.c (build_new_1): Do not check for arithmetic overflow if inner array size is 1.
authorFlorian Weimer <fweimer@redhat.com>
Wed, 7 Nov 2012 09:45:57 +0000 (10:45 +0100)
committerFlorian Weimer <fw@gcc.gnu.org>
Wed, 7 Nov 2012 09:45:57 +0000 (10:45 +0100)
* init.c (build_new_1): Do not check for arithmetic overflow if
inner array size is 1.

* g++.dg/init/new40.C: New.

From-SVN: r193287

gcc/cp/ChangeLog
gcc/cp/init.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/init/new40.C [new file with mode: 0644]

index 5e73e1b..c623b5e 100644 (file)
@@ -1,3 +1,8 @@
+2012-11-07  Florian Weimer  <fweimer@redhat.com>
+
+       * init.c (build_new_1): Do not check for arithmetic overflow if
+       inner array size is 1.
+
 2012-11-05  Sriraman Tallam  <tmsriram@google.com>
 
        * class.c (add_method): Change assembler names of function versions.
index 013b01e..c842aac 100644 (file)
@@ -2185,6 +2185,8 @@ build_new_1 (VEC(tree,gc) **placement, tree type, tree nelts,
   bool outer_nelts_from_type = false;
   double_int inner_nelts_count = double_int_one;
   tree alloc_call, alloc_expr;
+  /* Size of the inner array elements. */
+  double_int inner_size;
   /* The address returned by the call to "operator new".  This node is
      a VAR_DECL and is therefore reusable.  */
   tree alloc_node;
@@ -2346,8 +2348,6 @@ build_new_1 (VEC(tree,gc) **placement, tree type, tree nelts,
       double_int max_size
        = double_int_one.llshift (TYPE_PRECISION (sizetype) - 1,
                                  HOST_BITS_PER_DOUBLE_INT);
-      /* Size of the inner array elements. */
-      double_int inner_size;
       /* Maximum number of outer elements which can be allocated. */
       double_int max_outer_nelts;
       tree max_outer_nelts_tree;
@@ -2451,7 +2451,13 @@ build_new_1 (VEC(tree,gc) **placement, tree type, tree nelts,
          if (array_p && TYPE_VEC_NEW_USES_COOKIE (elt_type))
            size = size_binop (PLUS_EXPR, size, cookie_size);
          else
-           cookie_size = NULL_TREE;
+           {
+             cookie_size = NULL_TREE;
+             /* No size arithmetic necessary, so the size check is
+                not needed. */
+             if (outer_nelts_check != NULL && inner_size.is_one ())
+               outer_nelts_check = NULL_TREE;
+           }
          /* Perform the overflow check.  */
          if (outer_nelts_check != NULL_TREE)
             size = fold_build3 (COND_EXPR, sizetype, outer_nelts_check,
@@ -2487,7 +2493,13 @@ build_new_1 (VEC(tree,gc) **placement, tree type, tree nelts,
          /* Use a global operator new.  */
          /* See if a cookie might be required.  */
          if (!(array_p && TYPE_VEC_NEW_USES_COOKIE (elt_type)))
-           cookie_size = NULL_TREE;
+           {
+             cookie_size = NULL_TREE;
+             /* No size arithmetic necessary, so the size check is
+                not needed. */
+             if (outer_nelts_check != NULL && inner_size.is_one ())
+               outer_nelts_check = NULL_TREE;
+           }
 
          alloc_call = build_operator_new_call (fnname, placement,
                                                &size, &cookie_size,
index 8c5d5f3..5f10e41 100644 (file)
@@ -1,3 +1,7 @@
+2012-11-07  Florian Weimer  <fweimer@redhat.com>
+
+       * g++.dg/init/new40.C: New.
+
 2012-11-07  Jakub Jelinek  <jakub@redhat.com>
 
        PR debug/54693
diff --git a/gcc/testsuite/g++.dg/init/new40.C b/gcc/testsuite/g++.dg/init/new40.C
new file mode 100644 (file)
index 0000000..4b283a4
--- /dev/null
@@ -0,0 +1,112 @@
+// Testcase for overflow handling in operator new[].
+// Optimization of unnecessary overflow checks.
+// { dg-do run }
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdexcept>
+
+static size_t magic_allocation_size
+  = 1 + (size_t (1) << (sizeof (size_t) * 8 - 1));
+
+struct exc : std::bad_alloc {
+};
+
+static size_t expected_size;
+
+struct pod_with_new {
+  char ch;
+  void *operator new[] (size_t sz)
+  {
+    if (sz != expected_size)
+      abort ();
+    throw exc ();
+  }
+};
+
+struct with_new {
+  char ch;
+  with_new () { }
+  ~with_new () { }
+  void *operator new[] (size_t sz)
+  {
+    if (sz != size_t (-1))
+      abort ();
+    throw exc ();
+  }
+};
+
+struct non_pod {
+  char ch;
+  non_pod () { }
+  ~non_pod () { }
+};
+
+void *
+operator new (size_t sz) _GLIBCXX_THROW (std::bad_alloc)
+{
+  if (sz != expected_size)
+    abort ();
+  throw exc ();
+}
+
+int
+main ()
+{
+  if (sizeof (pod_with_new) == 1)
+    expected_size = magic_allocation_size;
+  else
+    expected_size = -1;
+
+  try {
+    new pod_with_new[magic_allocation_size];
+    abort ();
+  } catch (exc &) {
+  }
+
+  if (sizeof (with_new) == 1)
+    expected_size = magic_allocation_size;
+  else
+    expected_size = -1;
+
+  try {
+    new with_new[magic_allocation_size];
+    abort ();
+  } catch (exc &) {
+  }
+
+  expected_size = magic_allocation_size;
+  try {
+    new char[magic_allocation_size];
+    abort ();
+  } catch (exc &) {
+  }
+
+  expected_size = -1;
+
+  try {
+    new pod_with_new[magic_allocation_size][2];
+    abort ();
+  } catch (exc &) {
+  }
+
+  try {
+    new with_new[magic_allocation_size][2];
+    abort ();
+  } catch (exc &) {
+  }
+
+  try {
+    new char[magic_allocation_size][2];
+    abort ();
+  } catch (exc &) {
+  }
+
+  try {
+    new non_pod[magic_allocation_size];
+    abort ();
+  } catch (exc &) {
+  }
+
+  return 0;
+}