https://gcc.gnu.org/g:a5fabe7e20f4647bdfff5ed9d11d8526b11dae30

commit r17-5728-ga5fabe7e20f4647bdfff5ed9d11d8526b11dae30
Author: Ondřej Machota <[email protected]>
Date:   Wed Oct 28 22:04:43 2026 +0100

    tree-optimization: Remove unused allocations whose result is compared with 
NULL
    
    Hi,
    
    Thanks a lot for the review. Here are the changes in V2:
    
    - checks_return_value_of_removable_allocation_p has been rewritten. It now 
uses
      dyn_cast to gassign/gcond, and the two cases are combined.
    - The assignment case uses constant_boolean_node and
      gimple_assign_set_rhs_with_ops instead of gimple_assign_set_rhs_from_tree.
    
    Is there anything else that needs fixing or improvement? Could someone push
    this patch once it is OK for trunk? I don’t have write access.
    
    Bootstrapped and regression tested on x86_64-pc-linux-gnu with no 
regressions.
    
    Best regards,
    
    Ondřej Machota
    
    -- 8< --
    
    Jan Hubicka's earlier change ("Remove allocations which are used only
    for NULL pointer check and free", 7828dc0) taught tree-ssa-dce to
    remove an allocation/deallocation pair when the returned pointer is
    only used by a conditional comparing it against NULL. It does not handle
    the case where the result of the comparison is used in an assignment.
    
    With -O2 -fno-exceptions:
    
      int x (std::size_t n)
      {
        auto ptr = new (std::nothrow) int[n];
        delete[] ptr;
        return ptr != 0;
      }
    
      int y (std::size_t n)
      {
        auto ptr = new (std::nothrow) int[n];
        delete[] ptr
        return ptr != 0 ? 1 : 0;
      }
    
    y is optimized to "return 1", but x still calls operator new[] and
    operator delete[].
    
    Treat assignments of "ptr == NULL" and "ptr != NULL" like the conditional
    form: they no longer keep the allocation alive and are folded to a constant
    if the allocation is removed.
    
    Additionally, fix a typo in -fmalloc-dce=, which was incorrectly setting
    flag_malloc_dse instead of flag_malloc_dce, and include two new testcases
    to verify the flag's behavior.
    
    gcc/ChangeLog:
    
            * common.opt (fmalloc-dce): Use flag_malloc_dce instead of
            flag_malloc_dse.
            * tree-ssa-dce.cc (checks_return_value_of_removable_allocation_p):
            Also handle comparisons against NULL in assignments.
            (eliminate_unnecessary_stmts): Fold such comparisons to a constant
            when the allocation is removed.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/tree-ssa/dce-2.C: New test.
            * g++.dg/tree-ssa/dce-3.C: New test.
            * gcc.dg/tree-ssa/malloc-dce-1.c: New test.
            * gcc.dg/tree-ssa/malloc-dce-2.c: New test.
    
    Signed-off-by: Ondřej Machota <[email protected]>

Diff:
---
 gcc/common.opt                               |  2 +-
 gcc/testsuite/g++.dg/tree-ssa/dce-2.C        | 59 +++++++++++++++++++++++++
 gcc/testsuite/g++.dg/tree-ssa/dce-3.C        | 43 ++++++++++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/malloc-dce-1.c | 29 +++++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/malloc-dce-2.c | 29 +++++++++++++
 gcc/tree-ssa-dce.cc                          | 65 +++++++++++++++++++++++-----
 6 files changed, 216 insertions(+), 11 deletions(-)

diff --git a/gcc/common.opt b/gcc/common.opt
index 69a55b8d40d6..fb21a0c17350 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -2401,7 +2401,7 @@ Common Var(flag_malloc_dce,2) Init(2) Optimization
 Allow removal of malloc and free pairs when allocated block is unused.
 
 fmalloc-dce=
-Common Joined RejectNegative UInteger Var(flag_malloc_dse) Optimization 
IntegerRange(0, 2)
+Common Joined RejectNegative UInteger Var(flag_malloc_dce) Optimization 
IntegerRange(0, 2)
 
 fmem-report
 Common Var(mem_report)
diff --git a/gcc/testsuite/g++.dg/tree-ssa/dce-2.C 
b/gcc/testsuite/g++.dg/tree-ssa/dce-2.C
new file mode 100644
index 000000000000..6a072e0934cf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tree-ssa/dce-2.C
@@ -0,0 +1,59 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+#include <new>
+
+bool ne (unsigned s)
+{
+  int *p = new (std::nothrow) int[s];
+  delete[] p;
+  return p != 0;
+}
+
+bool eq (unsigned s)
+{
+  int *p = new (std::nothrow) int[s];
+  delete[] p;
+  return p == 0;
+}
+
+bool conv (unsigned s)
+{
+  int *p = new (std::nothrow) int[s];
+  delete[] p;
+  return p;
+}
+
+int ternary (unsigned s)
+{
+  int *p = new (std::nothrow) int[s];
+  delete[] p;
+  return p != 0 ? 1 : 0;
+}
+
+int copy (unsigned s)
+{
+  int *p = new (std::nothrow) int[s];
+  int c = p != 0;
+  delete[] p;
+  return c;
+}
+
+int result;
+void store (unsigned s)
+{
+  int *p = new (std::nothrow) int[s];
+  result = p != 0;
+  delete[] p;
+}
+
+int twice (unsigned s)
+{
+  int *p = new (std::nothrow) int[s];
+  int a = p != 0;
+  int b = p == 0;
+  delete[] p;
+  return a + b;
+}
+
+/* { dg-final { scan-tree-dump-not "operator new" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "operator delete" "optimized" } } */
diff --git a/gcc/testsuite/g++.dg/tree-ssa/dce-3.C 
b/gcc/testsuite/g++.dg/tree-ssa/dce-3.C
new file mode 100644
index 000000000000..4a4bf9272a2e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tree-ssa/dce-3.C
@@ -0,0 +1,43 @@
+/* Every allocation here must survive DCE.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized -Wno-mismatched-new-delete" } */
+#include <new>
+
+bool cmp_other (unsigned s, int *q)
+{
+  int *p = new (std::nothrow) int[s];
+  delete[] p;
+  return p == q;
+}
+
+int *sink;
+void escape (unsigned s)
+{
+  int *p = new (std::nothrow) int[s];
+  sink = p;
+  delete[] p;
+}
+
+void vol (unsigned s)
+{
+  char *p = new (std::nothrow) char[s + 1];
+  *(volatile char *) p = 4;
+  delete[] p;
+}
+
+bool direct (unsigned s)
+{
+  void *p = ::operator new (s, std::nothrow);
+  ::operator delete (p, std::nothrow);
+  return p != 0;
+}
+
+/* Mismatched new[] / delete.  */
+bool mismatch (unsigned s)
+{
+  int *p = new (std::nothrow) int[s];
+  delete p;
+  return p != 0;
+}
+
+/* { dg-final { scan-tree-dump-times "operator new" 5 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/malloc-dce-1.c 
b/gcc/testsuite/gcc.dg/tree-ssa/malloc-dce-1.c
new file mode 100644
index 000000000000..9f56f9af7443
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/malloc-dce-1.c
@@ -0,0 +1,29 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fmalloc-dce=1 -fdump-tree-optimized" } */
+#include <stdlib.h>
+
+void
+pair_only (size_t n)
+{
+  free (malloc (n)); /* removed */
+}
+
+int
+ne (size_t n)
+{
+  void *p = malloc (n);
+  int r = p != NULL;
+  free (p);
+  return r; /* malloc must stay */
+}
+
+int
+eq (size_t n)
+{
+  void *p = malloc (n);
+  int r = p == NULL;
+  free (p);
+  return r; /* malloc must stay */
+}
+
+/* { dg-final { scan-tree-dump-times "malloc \\(" 2 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/malloc-dce-2.c 
b/gcc/testsuite/gcc.dg/tree-ssa/malloc-dce-2.c
new file mode 100644
index 000000000000..f5e91bb8884f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/malloc-dce-2.c
@@ -0,0 +1,29 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fmalloc-dce=0 -fdump-tree-optimized" } */
+#include <stdlib.h>
+
+void
+pair_only (size_t n)
+{
+  free (malloc (n)); /* malloc must stay */
+}
+
+int
+ne (size_t n)
+{
+  void *p = malloc (n);
+  int r = p != NULL;
+  free (p);
+  return r; /* malloc must stay */
+}
+
+int
+eq (size_t n)
+{
+  void *p = malloc (n);
+  int r = p == NULL;
+  free (p);
+  return r; /* malloc must stay */
+}
+
+/* { dg-final { scan-tree-dump-times "malloc \\(" 3 "optimized" } } */
diff --git a/gcc/tree-ssa-dce.cc b/gcc/tree-ssa-dce.cc
index a5391290c4dc..9d5dce20d03c 100644
--- a/gcc/tree-ssa-dce.cc
+++ b/gcc/tree-ssa-dce.cc
@@ -341,22 +341,43 @@ is_removable_allocation_p (gcall *stmt, bool 
non_null_check)
     }
 }
 
-/* Return true if STMT is a conditional
+/* Return true if STMT is an assignment
+     foo = (ptr != NULL)
+     foo = (ptr == NULL)
+   or a conditional
      if (ptr != NULL)
+     if (ptr == NULL)
    where ptr was returned by a removable allocation function.  */
 
 static bool
 checks_return_value_of_removable_allocation_p (gimple *stmt)
 {
-  gcall *def_stmt;
-  return gimple_code (stmt) == GIMPLE_COND
-        && (gimple_cond_code (stmt) == EQ_EXPR
-            || gimple_cond_code (stmt) == NE_EXPR)
-        && integer_zerop (gimple_cond_rhs (stmt))
-        && TREE_CODE (gimple_cond_lhs (stmt)) == SSA_NAME
-        && (def_stmt = dyn_cast <gcall *>
-                        (SSA_NAME_DEF_STMT (gimple_cond_lhs (stmt))))
-        && is_removable_allocation_p (def_stmt, true);
+  enum tree_code code;
+  tree ptr, value;
+
+  if (gassign *assign = dyn_cast <gassign *> (stmt))
+    {
+      code = gimple_assign_rhs_code (assign);
+      ptr = gimple_assign_rhs1 (assign);
+      value = gimple_assign_rhs2 (assign);
+    }
+  else if (gcond *cond = dyn_cast <gcond *> (stmt))
+    {
+      code = gimple_cond_code (cond);
+      ptr = gimple_cond_lhs (cond);
+      value = gimple_cond_rhs (cond);
+    }
+  else
+    return false;
+
+  if (code != EQ_EXPR && code != NE_EXPR)
+    return false;
+
+  if (!integer_zerop (value) || TREE_CODE (ptr) != SSA_NAME)
+    return false;
+
+  gcall *def_stmt = dyn_cast <gcall *> (SSA_NAME_DEF_STMT (ptr));
+  return def_stmt && is_removable_allocation_p (def_stmt, true);
 }
 
 
@@ -1624,6 +1645,30 @@ eliminate_unnecessary_stmts (bool aggressive)
                    gimple_set_plf (stmt, STMT_NECESSARY, false);
                }
            }
+
+         /* Comparisons of the return value of an allocation against NULL
+            can be turned to a constant if the allocation itself is
+            unnecessary.  */
+         if (gimple_plf (stmt, STMT_NECESSARY) && is_gimple_assign (stmt)
+             && (gimple_assign_rhs_code (stmt) == EQ_EXPR
+                 || gimple_assign_rhs_code (stmt) == NE_EXPR)
+             && TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME)
+           {
+             tree rhs1 = gimple_assign_rhs1 (stmt);
+             gimple *def_stmt = SSA_NAME_DEF_STMT (rhs1);
+             if (!gimple_nop_p (def_stmt)
+                 && !gimple_plf (def_stmt, STMT_NECESSARY))
+               {
+                 gcc_checking_assert
+                   (checks_return_value_of_removable_allocation_p (stmt));
+                 tree lhs = gimple_assign_lhs (stmt);
+                 bool is_ne = gimple_assign_rhs_code (stmt) == NE_EXPR;
+                 tree cst = constant_boolean_node (is_ne, TREE_TYPE (lhs));
+                 gimple_assign_set_rhs_with_ops (&gsi, TREE_CODE (cst), cst);
+                 update_stmt (gsi_stmt (gsi));
+               }
+           }
+
          /* Conditional checking that return value of allocation is non-NULL
             can be turned to constant if the allocation itself
             is unnecessary.  */

Reply via email to