https://gcc.gnu.org/g:804e9d55d9e54cffd32d8dd065bf3c785b1edbf4

commit r15-6566-g804e9d55d9e54cffd32d8dd065bf3c785b1edbf4
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Mon Jan 6 10:12:00 2025 +0100

    tree-ssa-dce: Punt on allocations with too large constant sizes [PR118224]
    
    As suggested by Richi in the PR, the following patch will fail to DCE
    allocation calls if they have constant size which is too large (over
    PTRDIFF_MAX), or for the case of calloc, if either of the arguments
    is too large (in that case in theory the call could succeed if the other
    argument is variable zero but who cares) or if both are constant and
    their product overflows or is above PTRDIFF_MAX.
    
    This will make some pedantic conformance tests happy, though if one
    hides the size one will still need to use -fno-malloc-dce or obfuscate even
    the malloc etc. uses.  If the size is constant and too large, it isn't worth
    trying to optimize it.
    
    2025-01-06  Jakub Jelinek  <ja...@redhat.com>
    
            PR tree-optimization/118224
            * tree-ssa-dce.cc (is_removable_allocation_p): Don't return true
            for allocations with constant size argument larger than PTRDIFF_MAX
            or for calloc with one of the arguments constant larger than
            PTRDIFF_MAX or their product known constant above PTRDIFF_MAX.
            Fix comment typos, furhter -> further and then -> than.
            * lto-section-in.cc (lto_free_function_in_decl_state_for_node):
            Fix comment typo, furhter -> further.
    
            * gcc.dg/pr118224.c: New test.
            * c-c++-common/ubsan/vla-1.c (bar): Use noipa attribute instead
            of noinline, noclone.

Diff:
---
 gcc/lto-section-in.cc                    |  2 +-
 gcc/testsuite/c-c++-common/ubsan/vla-1.c |  2 +-
 gcc/testsuite/gcc.dg/pr118224.c          | 31 ++++++++++++
 gcc/tree-ssa-dce.cc                      | 81 ++++++++++++++++++++++++++++----
 4 files changed, 105 insertions(+), 11 deletions(-)

diff --git a/gcc/lto-section-in.cc b/gcc/lto-section-in.cc
index 0068aadb142c..1dd9520137a1 100644
--- a/gcc/lto-section-in.cc
+++ b/gcc/lto-section-in.cc
@@ -428,7 +428,7 @@ lto_free_function_in_decl_state (struct lto_in_decl_state 
*state)
   ggc_free (state);
 }
 
-/* Free decl_states associated with NODE.  This makes it possible to furhter
+/* Free decl_states associated with NODE.  This makes it possible to further
    release trees needed by the NODE's body.  */
 
 void
diff --git a/gcc/testsuite/c-c++-common/ubsan/vla-1.c 
b/gcc/testsuite/c-c++-common/ubsan/vla-1.c
index c97465edae19..b29d904dea39 100644
--- a/gcc/testsuite/c-c++-common/ubsan/vla-1.c
+++ b/gcc/testsuite/c-c++-common/ubsan/vla-1.c
@@ -6,7 +6,7 @@ int x = -1;
 double di = -3.2;
 V v = -6;
 
-static int __attribute__ ((noinline, noclone))
+static int __attribute__ ((noipa))
 bar (void)
 {
   return -4;
diff --git a/gcc/testsuite/gcc.dg/pr118224.c b/gcc/testsuite/gcc.dg/pr118224.c
new file mode 100644
index 000000000000..683f4dc8f631
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr118224.c
@@ -0,0 +1,31 @@
+/* PR tree-optimization/118224 */
+/* { dg-do run } */
+/* { dg-options "-O2 -w" } */
+
+#include <stdlib.h>
+
+void
+foo (__SIZE_TYPE__ s)
+{
+  if (__builtin_calloc (s, ~(__SIZE_TYPE__) 0))
+    __builtin_abort ();
+  if (__builtin_calloc (~(__SIZE_TYPE__) 0, s))
+    __builtin_abort ();
+}
+
+int
+main ()
+{
+  if (__builtin_malloc (~(__SIZE_TYPE__) 0))
+    __builtin_abort ();
+#ifdef __GLIBC_PREREQ
+#if __GLIBC_PREREQ (2, 16)
+  /* aligned_alloc was added in glibc 2.16 */
+  if (__builtin_aligned_alloc (32, ~(__SIZE_TYPE__) 0))
+    __builtin_abort ();
+#endif
+#endif
+  if (__builtin_calloc ((~(__SIZE_TYPE__) 0) / 2, 3))
+    __builtin_abort ();
+  foo (1);
+}
diff --git a/gcc/tree-ssa-dce.cc b/gcc/tree-ssa-dce.cc
index 11f7e7472efa..adc794103317 100644
--- a/gcc/tree-ssa-dce.cc
+++ b/gcc/tree-ssa-dce.cc
@@ -243,38 +243,101 @@ mark_operand_necessary (tree op)
 
 /* Return true if STMT is a call to allocation function that can be
    optimized out if the memory block is never used for anything else
-   then NULL pointer check or free.
-   If NON_NULL_CHECK is false, we can furhter assume that return value
-   is never checked to be non-NULL. */
+   than NULL pointer check or free.
+   If NON_NULL_CHECK is false, we can further assume that return value
+   is never checked to be non-NULL.
+   Don't return true if it is called with constant size (or sizes for calloc)
+   and the size is excessively large (larger than PTRDIFF_MAX, for calloc
+   either argument larger than PTRDIFF_MAX or both constant and their product
+   larger than PTRDIFF_MAX).  */
 
 static bool
 is_removable_allocation_p (gcall *stmt, bool non_null_check)
 {
-  tree callee = gimple_call_fndecl (stmt);
+  int arg = -1;
+  tree callee = gimple_call_fndecl (stmt), a1, a2;
   if (callee != NULL_TREE
       && fndecl_built_in_p (callee, BUILT_IN_NORMAL))
     switch (DECL_FUNCTION_CODE (callee))
       {
       case BUILT_IN_MALLOC:
+       arg = 1;
+       goto do_malloc;
       case BUILT_IN_ALIGNED_ALLOC:
+       arg = 2;
+       goto do_malloc;
       case BUILT_IN_CALLOC:
+       arg = 3;
+       goto do_malloc;
       CASE_BUILT_IN_ALLOCA:
+       arg = 1;
+       goto do_malloc;
       case BUILT_IN_STRDUP:
       case BUILT_IN_STRNDUP:
-       return non_null_check ? flag_malloc_dce > 1 : flag_malloc_dce;
+       arg = 0;
+       /* FALLTHRU */
+      do_malloc:
+       if (non_null_check)
+         {
+           if (flag_malloc_dce <= 1)
+             return false;
+         }
+       else if (!flag_malloc_dce)
+         return false;
+       break;
 
       case BUILT_IN_GOMP_ALLOC:
-       return true;
+       arg = 2;
+       break;
 
       default:;
       }
 
-  if (callee != NULL_TREE
+  if (arg == -1
+      && callee != NULL_TREE
       && flag_allocation_dce
       && gimple_call_from_new_or_delete (stmt)
       && DECL_IS_REPLACEABLE_OPERATOR_NEW_P (callee))
-    return true;
-  return false;
+    arg = 1;
+
+  switch (arg)
+    {
+    case -1:
+      return false;
+    case 0:
+      return true;
+    case 1:
+    case 2:
+      if (gimple_call_num_args (stmt) < (unsigned) arg)
+       return false;
+      a1 = gimple_call_arg (stmt, arg - 1);
+      if (tree_fits_uhwi_p (a1)
+         && (tree_to_uhwi (a1)
+             > tree_to_uhwi (TYPE_MAX_VALUE (ptrdiff_type_node))))
+       return false;
+      return true;
+    case 3:
+      if (gimple_call_num_args (stmt) < 2)
+       return false;
+      a1 = gimple_call_arg (stmt, 0);
+      a2 = gimple_call_arg (stmt, 1);
+      if (tree_fits_uhwi_p (a1)
+         && (tree_to_uhwi (a1)
+             > tree_to_uhwi (TYPE_MAX_VALUE (ptrdiff_type_node))))
+       return false;
+      if (tree_fits_uhwi_p (a2)
+         && (tree_to_uhwi (a2)
+             > tree_to_uhwi (TYPE_MAX_VALUE (ptrdiff_type_node))))
+       return false;
+      if (TREE_CODE (a1) == INTEGER_CST
+         && TREE_CODE (a2) == INTEGER_CST
+         && (wi::to_widest (a1) + wi::to_widest (a2)
+             > tree_to_uhwi (TYPE_MAX_VALUE (ptrdiff_type_node))))
+       return false;
+      return true;
+    default:
+      gcc_unreachable ();
+    }
 }
 
 /* Return true if STMT is a conditional

Reply via email to