2012-10-31 Florian Weimer * init.c (build_new_1): Do not check for arithmetic overflow if inner array size is 1. * g++.dg/init/new40.C: New. --- gcc/cp/init.c (revision 193033) +++ gcc/cp/init.c (working copy) @@ -2176,6 +2176,8 @@ build_new_1 (VEC(tree,gc) **placement, t double_int inner_nelts_count = double_int_one; tree inner_nelts = NULL_TREE; 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; @@ -2318,8 +2320,6 @@ build_new_1 (VEC(tree,gc) **placement, t double_int max_size = double_int_lshift (double_int_one, TYPE_PRECISION (sizetype) - 1, HOST_BITS_PER_DOUBLE_INT, false); - /* 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; @@ -2438,7 +2438,15 @@ build_new_1 (VEC(tree,gc) **placement, t 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 + && double_int_one_p (inner_size) + && inner_nelts == NULL_TREE) + outer_nelts_check = NULL_TREE; + } /* Perform the overflow check. */ if (outer_nelts_check != NULL_TREE) size = fold_build3 (COND_EXPR, sizetype, outer_nelts_check, @@ -2474,7 +2482,15 @@ build_new_1 (VEC(tree,gc) **placement, t /* 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 + && double_int_one_p (inner_size) + && inner_nelts == NULL_TREE) + outer_nelts_check = NULL_TREE; + } alloc_call = build_operator_new_call (fnname, placement, &size, &cookie_size, --- gcc/testsuite/g++.dg/init/new40.C (revision 0) +++ gcc/testsuite/g++.dg/init/new40.C (working copy) @@ -0,0 +1,112 @@ +// Testcase for overflow handling in operator new[]. +// Optimization of unnecessary overflow checks. +// { dg-do run } + +#include +#include +#include + +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; +}