There's a well-known benchmark which uselessly likes to declare local
variables as static. There exist at least two implementations to demote
these to normal register variables. See the discussion thread here:
  http://gcc.gnu.org/ml/gcc-patches/2008-07/msg00982.html
These days, however, we can skip most of the work as pointed out by
Andrew Pinski:
  http://gcc.gnu.org/ml/gcc-patches/2008-07/msg01035.html
PRE usually manages to eliminate all references to the static variable
except a final assignment. The only thing that's left to do is to
enhance DSE to recognize these as dead. So, the following patch is a
cut-down version of CodeSourcery's approach, originally written by
Nathan Froyd, modified to do exactly that.

Bootstrapped and tested on x86_64-linux, all languages except Ada. OK?


Bernd
commit ce5d3fe1bf7934dd551b7bf091f113f396e15d64
Author: Bernd Schmidt <ber...@codesourcery.com>
Date:   Wed Jun 5 15:04:56 2013 +0200

    Extend DSE to remove static local variables that are only set.
    
    Based on an earlier patch by Nathan Froyd and Andrew Stubbs.
    
    	gcc/
    	* cgraph.c (cgraph_node): Set ever_was_nested in the node and
    	its parent when creating a new node.
    	* cgraph.h (struct cgraph_node): New field ever_was_nested.
    	* tree-ssa-dse.c: Include "hashtab.h".
    	(struct rls_decl_info, struct rls_stmt_info): New.
    	(static_variables, defuse_statements, n_statics): New static
    	variables.
    	(rls_hash_decl_info, rls_eq_decl_info, rls_free_decl_info,
    	rls_hash_use_info, rls_eq_use_info, rls_free_use_info, rls_init,
    	rls_done, note_var_ref, mark_used, remove_local_statics,
    	find_static_nonvolatile_declarations, maybe_remove_stmt): New static
    	functions.
    	(tree_ssa_dse): Call remove_local_statics if appropriate.
    	* Makefile.in (tree-ssa-dse.o): Update dependencies.
    
    	gcc/cp/
    	* decl2.c (mark_used): Mark _DECLs as DECL_NONLOCAL if appropriate.
    
    	gcc/testsuite/
    	* g++.dg/remove-local-statics-1.C: New test.
    	* g++.dg/remove-local-statics-2.C: New test.
    	* gcc.dg/remove-local-statics-1.c: New file.
    	* gcc.dg/remove-local-statics-2.c: New file.
    	* gcc.dg/remove-local-statics-3.c: New file.
    	* gcc.dg/remove-local-statics-4.c: New file.
    	* gcc.dg/remove-local-statics-5.c: New file.
    	* gcc.dg/remove-local-statics-6.c: New file.
    	* gcc.dg/remove-local-statics-7.c: New file.
    	* gcc.dg/remove-local-statics-8.c: New file.
    	* gcc.dg/remove-local-statics-9.c: New file.
    	* gcc.dg/remove-local-statics-10.c: New file.
    	* gcc.dg/remove-local-statics-11.c: New file.
    	* gcc.dg/remove-local-statics-12.c: New file.
    	* gcc.dg/remove-local-statics-13.c: New test.
    	* gcc.dg/remove-local-statics-14.c: New test.
    	* gcc.dg/remove-local-statics-15.c: New test.
    	* gcc.dg/remove-local-statics-16.c: New test.
    	* gcc.dg/remove-local-statics-17.c: New test.
    	* gcc.dg/remove-local-statics-18.c: New test.
    	* gcc.dg/tree-ssa/ssa-dse-6.c: Ensure the local variables aren't
    	optimized away.

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index e95dd63..3acd9fb 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -2306,7 +2306,7 @@ tree-outof-ssa.o : tree-outof-ssa.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \
 tree-ssa-dse.o : tree-ssa-dse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
    $(TM_H) $(GGC_H) $(TREE_H) $(TM_P_H) $(BASIC_BLOCK_H) \
    $(TREE_FLOW_H) $(TREE_PASS_H) domwalk.h $(FLAGS_H) \
-   $(GIMPLE_PRETTY_PRINT_H) langhooks.h
+   $(GIMPLE_PRETTY_PRINT_H) $(HASHTAB_H) langhooks.h
 tree-ssa-forwprop.o : tree-ssa-forwprop.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
    $(TM_H) $(TREE_H) $(TM_P_H) $(BASIC_BLOCK_H) $(CFGLOOP_H) \
    $(TREE_FLOW_H) $(TREE_PASS_H) $(DIAGNOSTIC_H) \
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 445282a..9343e4c 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -531,8 +531,10 @@ cgraph_create_node (tree decl)
   if (DECL_CONTEXT (decl) && TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL)
     {
       node->origin = cgraph_get_create_node (DECL_CONTEXT (decl));
+      node->origin->ever_was_nested = 1;
       node->next_nested = node->origin->nested;
       node->origin->nested = node;
+      node->ever_was_nested = 1;
     }
   return node;
 }
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 276e568..a667f74 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -303,6 +303,8 @@ struct GTY(()) cgraph_node {
   /* Set once the function has been instantiated and its callee
      lists created.  */
   unsigned process : 1;
+  /* Set if the function is a nested function or has nested functions.  */
+  unsigned ever_was_nested : 1;
   /* How commonly executed the node is.  Initialized during branch
      probabilities pass.  */
   ENUM_BITFIELD (node_frequency) frequency : 2;
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 5e7dbcd..8b346cd 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -4515,6 +4515,15 @@ mark_used (tree decl, tsubst_flags_t complain)
 
   /* Set TREE_USED for the benefit of -Wunused.  */
   TREE_USED (decl) = 1;
+  if (current_function_decl != NULL_TREE
+      && (TREE_CODE (decl) == VAR_DECL
+	  || TREE_CODE (decl) == PARM_DECL
+	  || TREE_CODE (decl) == FUNCTION_DECL))
+    {
+      tree context = decl_function_context (decl);
+      if (context != NULL_TREE && context != current_function_decl)
+	DECL_NONLOCAL (decl) = 1;
+    }
   if (DECL_CLONED_FUNCTION_P (decl))
     TREE_USED (DECL_CLONED_FUNCTION (decl)) = 1;
 
diff --git a/gcc/testsuite/g++.dg/remove-local-statics-1.C b/gcc/testsuite/g++.dg/remove-local-statics-1.C
new file mode 100644
index 0000000..c017892
--- /dev/null
+++ b/gcc/testsuite/g++.dg/remove-local-statics-1.C
@@ -0,0 +1,24 @@
+/* Verify that we do not eliminate a static variable in
+   main::Local::Foo.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-dse2-details  -fdump-tree-dse1-details" } */
+
+int
+main (void)
+{
+   static int thestatic = 0;
+   struct Local {
+     __attribute__((__noinline__))
+     static void Foo () { thestatic = 1; }
+   };
+
+   thestatic = 2;
+   Local::Foo();
+
+   return thestatic++;
+}
+/* { dg-final { scan-tree-dump-times "static variables to consider" 0 "dse1" } } */
+/* { dg-final { scan-tree-dump-times "static variables to consider" 0 "dse2" } } */
+/* { dg-final { cleanup-tree-dump "dse1" } } */
+/* { dg-final { cleanup-tree-dump "dse2" } } */
diff --git a/gcc/testsuite/g++.dg/remove-local-statics-2.C b/gcc/testsuite/g++.dg/remove-local-statics-2.C
new file mode 100644
index 0000000..cb89f4d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/remove-local-statics-2.C
@@ -0,0 +1,24 @@
+/* Verify that we do not eliminate a static variable in
+   main due to its use in Local::Foo.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-dse2-details  -fdump-tree-dse1-details" } */
+
+int
+main (void)
+{
+   static int thestatic = 0;
+   struct Local {
+     __attribute__((__noinline__))
+     static int Foo () { return thestatic; }
+   };
+
+   thestatic = 2;
+   thestatic = Local::Foo();
+
+   return thestatic++;
+}
+/* { dg-final { scan-tree-dump-times "static variables to consider" 0 "dse1" } } */
+/* { dg-final { scan-tree-dump-times "static variables to consider" 0 "dse2" } } */
+/* { dg-final { cleanup-tree-dump "dse1" } } */
+/* { dg-final { cleanup-tree-dump "dse2" } } */
diff --git a/gcc/testsuite/gcc.dg/remove-local-statics-1.c b/gcc/testsuite/gcc.dg/remove-local-statics-1.c
new file mode 100644
index 0000000..e49409a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/remove-local-statics-1.c
@@ -0,0 +1,16 @@
+/* Verify that we eliminate a static local variable where its uses
+   are dominated by a def.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { scan-assembler-not "thestatic" } } */
+
+int
+test1 (int x)
+{
+  static int thestatic;
+
+  thestatic = x;
+
+  return thestatic + x;
+}
diff --git a/gcc/testsuite/gcc.dg/remove-local-statics-10.c b/gcc/testsuite/gcc.dg/remove-local-statics-10.c
new file mode 100644
index 0000000..e8038fe
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/remove-local-statics-10.c
@@ -0,0 +1,32 @@
+/* Verify that we do not eliminate a static local variable when it is
+   live on return from a function call that recursively calls the
+   function in which the variable is defined.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { scan-assembler "thestatic" } } */
+
+int
+test2 (int x)
+{
+  if (x < 0)
+    return 0;
+  else
+    return test1 (x - 1);
+}
+
+int
+test1 (int x)
+{
+  static int thestatic;
+  int y;
+
+  thestatic = x;
+
+  y = test2 (x - 1);
+
+  y += thestatic;
+
+  return y + x;
+}
+  
diff --git a/gcc/testsuite/gcc.dg/remove-local-statics-11.c b/gcc/testsuite/gcc.dg/remove-local-statics-11.c
new file mode 100644
index 0000000..f9fbdb5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/remove-local-statics-11.c
@@ -0,0 +1,16 @@
+/* Verify that we do not eliminate a static local variable when its
+   address is taken.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { scan-assembler "thestatic" } } */
+
+int *
+test1 (int x)
+{
+  static int thestatic;
+
+  thestatic = x;
+
+  return &thestatic + x;
+}
diff --git a/gcc/testsuite/gcc.dg/remove-local-statics-12.c b/gcc/testsuite/gcc.dg/remove-local-statics-12.c
new file mode 100644
index 0000000..9e57fff
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/remove-local-statics-12.c
@@ -0,0 +1,20 @@
+/* Verify that we do not eliminate a static variable when it is declared
+   in a function that has nested functions.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { scan-assembler "thestatic" } } */
+
+int test1 (int x)
+{
+  static int thestatic;
+
+  int nested_test1 (int x)
+  {
+    return x + thestatic;
+  }
+
+  thestatic = x;
+
+  return thestatic + x + nested_test1 (x);
+}
diff --git a/gcc/testsuite/gcc.dg/remove-local-statics-13.c b/gcc/testsuite/gcc.dg/remove-local-statics-13.c
new file mode 100644
index 0000000..3f8be1b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/remove-local-statics-13.c
@@ -0,0 +1,24 @@
+/* We used to ICE on this test, because the call to BAR appeared to
+   define both static variables in FOO.  Verify that we no longer do
+   this.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { scan-assembler "static1" } } */
+/* { dg-final { scan-assembler-not "static2" } } */
+
+int foo(int i) {
+  static int static1 = 0;
+  static int static2;
+
+  if (static2 = bar(i))
+    static1 = 1;
+  static2 = static1 + 30;
+
+  return static1 + static2;
+}
+
+int bar(int i) {
+  if (i) { foo(i-1); return 0; }
+  return 1;
+}
diff --git a/gcc/testsuite/gcc.dg/remove-local-statics-14.c b/gcc/testsuite/gcc.dg/remove-local-statics-14.c
new file mode 100644
index 0000000..f93160c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/remove-local-statics-14.c
@@ -0,0 +1,29 @@
+/* Verify that we do eliminate a static local variable whose last use is
+   in a statement containing a call expression.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { scan-assembler-not "thestatic" } } */
+
+int
+test2 (int x)
+{
+  if (x < 0)
+    return 0;
+  else
+    return test1 (x - 1);
+}
+
+__attribute__((noinline,noclone)) int
+test1 (int x)
+{
+  static int thestatic;
+  int y;
+
+  thestatic = x;
+
+  y = test2 (thestatic - 1);
+
+  return y + x;
+}
+  
diff --git a/gcc/testsuite/gcc.dg/remove-local-statics-14b.c b/gcc/testsuite/gcc.dg/remove-local-statics-14b.c
new file mode 100644
index 0000000..61587f4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/remove-local-statics-14b.c
@@ -0,0 +1,29 @@
+/* Verify that we do not eliminate a static local variable if the function
+   containing it is inlined.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { scan-assembler "thestatic" } } */
+
+int
+test2 (int x)
+{
+  if (x < 0)
+    return 0;
+  else
+    return test1 (x - 1);
+}
+
+inline int
+test1 (int x)
+{
+  static int thestatic;
+  int y;
+
+  thestatic = x;
+
+  y = test2 (thestatic - 1);
+
+  return y + x;
+}
+  
diff --git a/gcc/testsuite/gcc.dg/remove-local-statics-15.c b/gcc/testsuite/gcc.dg/remove-local-statics-15.c
new file mode 100644
index 0000000..87e1956
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/remove-local-statics-15.c
@@ -0,0 +1,19 @@
+/* Verify that we do not consider an array variable for local static
+   elimination.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-dse2-details  -fdump-tree-dse1-details" } */
+
+int foo (void)
+{
+  static int a[1];
+
+  a[0] = 0;
+
+  return a[0];
+}
+
+/* { dg-final { scan-tree-dump-times "static variables to consider" 0 "dse1" } } */
+/* { dg-final { scan-tree-dump-times "static variables to consider" 0 "dse2" } } */
+/* { dg-final { cleanup-tree-dump "dse1" } } */
+/* { dg-final { cleanup-tree-dump "dse2" } } */
diff --git a/gcc/testsuite/gcc.dg/remove-local-statics-16.c b/gcc/testsuite/gcc.dg/remove-local-statics-16.c
new file mode 100644
index 0000000..c4fa24d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/remove-local-statics-16.c
@@ -0,0 +1,22 @@
+/* Verify that we do not consider an structure variable for local static
+   elimination.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-dse2-details  -fdump-tree-dse1-details" } */
+
+int foo (void)
+{
+  static struct {
+    int x;
+    int y;
+  } a;
+
+  a.x = 0;
+
+  return a.y;
+}
+
+/* { dg-final { scan-tree-dump-times "static variables to consider" 0 "dse1" } } */
+/* { dg-final { scan-tree-dump-times "static variables to consider" 0 "dse2" } } */
+/* { dg-final { cleanup-tree-dump "dse1" } } */
+/* { dg-final { cleanup-tree-dump "dse2" } } */
diff --git a/gcc/testsuite/gcc.dg/remove-local-statics-17.c b/gcc/testsuite/gcc.dg/remove-local-statics-17.c
new file mode 100644
index 0000000..d4e9b39
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/remove-local-statics-17.c
@@ -0,0 +1,20 @@
+/* Verify that we do not eliminate a static variable that is "defined"
+   by an asm that clobbers memory.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-dse2-details  -fdump-tree-dse1-details" } */
+
+int foo (void)
+{
+  static int thestatic = 0;
+
+  __asm__ __volatile__ ("" : : : "memory");
+
+  thestatic++;
+
+  return thestatic;
+}
+/* { dg-final { scan-tree-dump-times "static variables to consider" 0 "dse1" } } */
+/* { dg-final { scan-tree-dump-times "static variables to consider" 0 "dse2" } } */
+/* { dg-final { cleanup-tree-dump "dse1" } } */
+/* { dg-final { cleanup-tree-dump "dse2" } } */
diff --git a/gcc/testsuite/gcc.dg/remove-local-statics-18.c b/gcc/testsuite/gcc.dg/remove-local-statics-18.c
new file mode 100644
index 0000000..56a46e1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/remove-local-statics-18.c
@@ -0,0 +1,47 @@
+/* Verify that we do not eliminate the static variable `x' when `func'
+   is inlined and its return value become dead.  */
+
+/* { dg-do run } */
+/* { dg-output "" } */
+/* { dg-options "-O2" } */
+
+void abort(void);
+
+static int guard;
+static int func(int y)
+{
+  static int x;
+  if (guard == 0)
+    {
+      x = y;
+      guard = 1;
+    }
+  return x + y;
+}
+
+int __attribute__((noinline)) call1(int a)
+{
+  func(a);
+  return 0;
+}
+
+int __attribute__((noinline)) call2(int a)
+{
+  return func(a);
+}
+
+int global1 = 3;
+int global2 = 5;
+
+extern int printf (const char *, ...);
+
+int main()
+{
+  call1 (global1);
+  printf ("call2: %d\n", call2(global2));
+#if 0
+  if (call2 (global2) != 8)
+    abort ();
+#endif
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/remove-local-statics-2.c b/gcc/testsuite/gcc.dg/remove-local-statics-2.c
new file mode 100644
index 0000000..762c2c0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/remove-local-statics-2.c
@@ -0,0 +1,19 @@
+/* Verify that we do not eliminate a static local variable when its uses
+   are not dominated by a def.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { scan-assembler "first_time" } } */
+
+int
+test1 (int x)
+{
+  static int first_time;
+
+  if (x == 1)
+    first_time = 1;
+  else if (x > 0)
+    first_time = 2;
+
+  return first_time + x;
+}
diff --git a/gcc/testsuite/gcc.dg/remove-local-statics-3.c b/gcc/testsuite/gcc.dg/remove-local-statics-3.c
new file mode 100644
index 0000000..be7dd26
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/remove-local-statics-3.c
@@ -0,0 +1,16 @@
+/* Verify that we do not eliminate a static local variable whose uses
+   are dominated by a def when the variable is volatile.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { scan-assembler "thestatic" } } */
+
+int
+test1 (int x)
+{
+  static volatile int thestatic;
+
+  thestatic = x;
+
+  return thestatic + x;
+}
diff --git a/gcc/testsuite/gcc.dg/remove-local-statics-4.c b/gcc/testsuite/gcc.dg/remove-local-statics-4.c
new file mode 100644
index 0000000..c3b9230
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/remove-local-statics-4.c
@@ -0,0 +1,15 @@
+/* Verify that we don't eliminate a global static variable.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { scan-assembler "global_static" } } */
+
+static int global_static;
+
+int
+test1 (int x)
+{
+  global_static = x;
+
+  return global_static + x;
+}
diff --git a/gcc/testsuite/gcc.dg/remove-local-statics-5.c b/gcc/testsuite/gcc.dg/remove-local-statics-5.c
new file mode 100644
index 0000000..81e0c51
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/remove-local-statics-5.c
@@ -0,0 +1,24 @@
+/* Verify that we do not eliminate a static local variable whose uses
+   are dominated by a def when the function calls setjmp.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { scan-assembler "thestatic" } } */
+
+#include <setjmp.h>
+
+int
+foo (int x)
+{
+  static int thestatic;
+  int retval;
+  jmp_buf env;
+
+  thestatic = x;
+
+  retval = thestatic + x;
+
+  setjmp (env);
+
+  return retval;
+}
diff --git a/gcc/testsuite/gcc.dg/remove-local-statics-6.c b/gcc/testsuite/gcc.dg/remove-local-statics-6.c
new file mode 100644
index 0000000..b04759f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/remove-local-statics-6.c
@@ -0,0 +1,16 @@
+/* Verify that we do not eliminate a static local variable whose uses
+   are dominated by a def when the variable is addressed.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { scan-assembler "thestatic" } } */
+
+int *
+test1 (int x)
+{
+  static int thestatic;
+
+  thestatic = x;
+
+  return &thestatic + x;
+}
diff --git a/gcc/testsuite/gcc.dg/remove-local-statics-7.c b/gcc/testsuite/gcc.dg/remove-local-statics-7.c
new file mode 100644
index 0000000..1177bdd
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/remove-local-statics-7.c
@@ -0,0 +1,19 @@
+/* Verify that we eliminate a static local variable where it is defined
+   along all paths leading to a use.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { scan-assembler-not "thestatic" } } */
+
+int
+test1 (int x)
+{
+  static int thestatic;
+
+  if (x < 0)
+    thestatic = x;
+  else
+    thestatic = -x;
+
+  return thestatic + x;
+}
diff --git a/gcc/testsuite/gcc.dg/remove-local-statics-8.c b/gcc/testsuite/gcc.dg/remove-local-statics-8.c
new file mode 100644
index 0000000..e1b0825
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/remove-local-statics-8.c
@@ -0,0 +1,33 @@
+/* Verify that we eliminate a static local variable when it is dead on
+   return from a function call that recursively calls the function in
+   which the variable is defined.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { scan-assembler-not "thestatic" } } */
+
+int test1 (int);
+int test2 (int);
+
+int
+test2 (int x)
+{
+  if (x < 0)
+    return 0;
+  else
+    return test1 (x - 1);
+}
+
+int
+test1 (int x)
+{
+  static int thestatic;
+  int y;
+
+  thestatic = x;
+
+  y = thestatic;
+
+  return y + x + test1 (x - 1) + test2 (x - 1);
+}
+  
diff --git a/gcc/testsuite/gcc.dg/remove-local-statics-9.c b/gcc/testsuite/gcc.dg/remove-local-statics-9.c
new file mode 100644
index 0000000..58cd325
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/remove-local-statics-9.c
@@ -0,0 +1,32 @@
+/* Verify that we eliminate a static local variable when it is live
+   on return from a function call that does not recursively call the
+   function in which the variable is defined.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { scan-assembler-not "thestatic" } } */
+
+static int
+test2 (int x)
+{
+  if (x < 0)
+    return 0;
+  else
+    return x + test2 (x - 1);
+}
+
+int
+test1 (int x)
+{
+  static int thestatic;
+  int y;
+
+  thestatic = x;
+
+  y = test2 (x - 1);
+
+  y += thestatic;
+
+  return y + x;
+}
+  
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-6.c b/gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-6.c
index 3d02006..fae3392 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-6.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-6.c
@@ -1,9 +1,12 @@
 /* { dg-do compile } */
 /* { dg-options "-O2 -fdump-tree-dse1" } */
 
+int *x1, *x2;
 int foo11 (int c)
 {
   static int local1, local2;
+  x1 = &local1;
+  x2 = &local2;
   local1 = 0;
   local2 += c;
   local1 = 2;
diff --git a/gcc/tree-ssa-dse.c b/gcc/tree-ssa-dse.c
index ad99ea9..5c4a32a 100644
--- a/gcc/tree-ssa-dse.c
+++ b/gcc/tree-ssa-dse.c
@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tm.h"
 #include "ggc.h"
 #include "tree.h"
+#include "hashtab.h"
 #include "tm_p.h"
 #include "basic-block.h"
 #include "gimple-pretty-print.h"
@@ -301,6 +302,310 @@ dse_enter_block (struct dom_walk_data *walk_data ATTRIBUTE_UNUSED,
     }
 }
 
+/* Sub-pass to remove unused assignments to local static variables that
+   are never read.  */
+
+/* Describe a potential candidate variable for the optimization.  */
+struct rls_decl_info
+{
+  /* The variable declaration.  */
+  tree var;
+
+  /* Whether we can optimize this variable.  */
+  bool optimizable_p;
+};
+
+/* Filled with 'struct rls_decl_info'; keyed off VAR.  */
+static htab_t static_variables;
+
+/* Describe a statement assigning to one of our candidate variables.  */
+struct rls_stmt_info
+{
+  /* Information about the variable.  */
+  struct rls_decl_info *info;
+
+  /* The statement in which we found a def or a use of the variable.  */
+  gimple stmt;
+};
+
+/* Filled with 'struct rls_stmt_info'; keyed off STMT.  */
+static htab_t defuse_statements;
+
+/* The number of static variables we found.  */
+static int n_statics;
+
+/* Parameters for the 'static_variables' hash table.  */
+
+static hashval_t
+rls_hash_decl_info (const void *x)
+{
+  return htab_hash_pointer
+    ((const void *) ((const struct rls_decl_info *) x)->var);
+}
+
+static int
+rls_eq_decl_info (const void *x, const void *y)
+{
+  const struct rls_decl_info *a = (const struct rls_decl_info *) x;
+  const struct rls_decl_info *b = (const struct rls_decl_info *) y;
+
+  return a->var == b->var;
+}
+
+static void
+rls_free_decl_info (void *info)
+{
+  free (info);
+}
+
+/* Parameters for the 'defuse_statements' hash table.  */
+
+static hashval_t
+rls_hash_use_info (const void *x)
+{
+  return htab_hash_pointer
+    ((const void *) ((const struct rls_stmt_info *) x)->stmt);
+}
+
+static int
+rls_eq_use_info (const void *x, const void *y)
+{
+  const struct rls_stmt_info *a = (const struct rls_stmt_info *) x;
+  const struct rls_stmt_info *b = (const struct rls_stmt_info *) y;
+
+  return a->stmt == b->stmt;
+}
+
+static void
+rls_free_use_info (void *info)
+{
+  struct rls_stmt_info *stmt_info = (struct rls_stmt_info *) info;
+
+  free (stmt_info);
+}
+
+/* Initialize data structures and statistics.  */
+
+static void
+rls_init (void)
+{
+  /* We expect relatively few static variables, hence the small
+     initial size for the hash table.  */
+  static_variables = htab_create (8, rls_hash_decl_info,
+                                  rls_eq_decl_info, rls_free_decl_info);
+
+  /* We expect quite a few statements.  */
+  defuse_statements = htab_create (128, rls_hash_use_info,
+                                   rls_eq_use_info, rls_free_use_info);
+
+  n_statics = 0;
+}
+
+/* Free data structures.  */
+
+static void
+rls_done (void)
+{
+  htab_delete (static_variables);
+  htab_delete (defuse_statements);
+}
+
+
+/* Doing the initial work to find static variables.  */
+
+/* Examine VAR, known to be a VAR_DECL, and determine whether it is a
+   static variable we could potentially optimize.  If so, stick in it in
+   the 'static_variables' hashtable.
+
+   STMT is the statement in which a definition or use of VAR occurs.
+   USE_P indicates whether VAR is used or defined in STMT.  Enter STMT
+   into 'defuse_statements' as well for use during dataflow
+   analysis.  */
+
+static void
+note_var_ref (tree var, gimple stmt, bool use_p)
+{
+  if (TREE_CODE (var) == VAR_DECL
+      /* We cannot optimize statics that were defined in another
+	 function.  The static may be non-optimizable in its original
+	 function, but optimizable when said function is inlined due to
+	 DCE of its uses.  (e.g. the only use was in a return statement
+	 and the function is inlined in a void context.)  */
+      && DECL_CONTEXT (var) == current_function_decl
+      /* We cannot optimize away a static used in multiple functions (as
+	 might happen in C++).  */
+      && !DECL_NONLOCAL(var)
+      && TREE_STATIC (var)
+      /* We cannot optimize away aggregate statics, as we would have to
+	 prove that definitions of every field of the aggregate dominate
+	 uses.  */
+      && !AGGREGATE_TYPE_P (TREE_TYPE (var))
+      /* GCC doesn't normally treat vectors as aggregates; we need to,
+	 though, since a user could use intrinsics to read/write
+	 particular fields of the vector, thereby treating it as an
+	 array.  */
+      && TREE_CODE (TREE_TYPE (var)) != VECTOR_TYPE
+      && !TREE_ADDRESSABLE (var)
+      && !TREE_THIS_VOLATILE (var))
+    {
+      struct rls_decl_info dummy;
+      void **slot;
+      struct rls_decl_info *info;
+
+      dummy.var = var;
+      slot = htab_find_slot (static_variables, &dummy, INSERT);
+      info = (struct rls_decl_info *)*slot;
+      if (info == NULL)
+        {
+          /* Found a use or a def of a new declaration.  */
+          info = XNEW (struct rls_decl_info);
+
+          info->var = var;
+          info->optimizable_p = !use_p;
+	  if (!use_p)
+	    n_statics++;
+          *slot = (void *) info;
+        }
+      if (use_p)
+	{
+	  if (info->optimizable_p)
+	    n_statics--;
+	  info->optimizable_p = false;
+	  return;
+	}
+
+      /* Enter the statement into DEFUSE_STATEMENTS.  */
+      {
+        struct rls_stmt_info dummy;
+        struct rls_stmt_info *stmt_info;
+
+        dummy.stmt = stmt;
+        slot = htab_find_slot (defuse_statements, &dummy, INSERT);
+
+        /* We should never insert the same statement into the
+           hashtable twice.  */
+        gcc_assert (*slot == NULL);
+
+        stmt_info = XNEW (struct rls_stmt_info);
+        stmt_info->info = info;
+        stmt_info->stmt = stmt;
+        if (dump_file)
+          {
+            fprintf (dump_file, "entering def ");
+            print_gimple_stmt (dump_file, stmt, 0, TDF_DETAILS | TDF_VOPS);
+          }
+        *slot = (void *) stmt_info;
+      }
+    }
+}
+
+/* Helper functions for walk_stmt_load_store_ops.  Used to detect uses
+   of static variables outside of assignments.  */
+
+static bool
+mark_used (gimple stmt ATTRIBUTE_UNUSED, tree t, void *data ATTRIBUTE_UNUSED)
+{
+  note_var_ref (t, stmt, true);
+  return true;
+}
+
+/* Grovel through all the statements in the program, looking for
+   SSA_NAMEs whose SSA_NAME_VAR is a VAR_DECL.  We look at both use and
+   def SSA_NAMEs.  */
+
+static void
+find_static_nonvolatile_declarations (void)
+{
+  basic_block bb;
+
+  FOR_EACH_BB (bb)
+    {
+      gimple_stmt_iterator i;
+
+      for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i))
+        {
+	  gimple stmt = gsi_stmt (i);
+
+	  if (gimple_code (stmt) == GIMPLE_ASM
+	      && gimple_asm_clobbers_memory_p (stmt))
+	    {
+	      /* Abort this optimization if an asm with a memory clobber is
+		 seen; it must be assumed to also read memory.  */
+	      n_statics = 0;
+	      return;
+	    }
+	  /* Static variables usually only occur in plain assignments
+	     that copy to or from a temporary.  */
+	  if (gimple_assign_single_p (stmt))
+	    {
+	      tree lhs = gimple_assign_lhs (stmt);
+	      tree rhs = gimple_assign_rhs1 (stmt);
+	      note_var_ref (lhs, stmt, false);
+	      note_var_ref (rhs, stmt, true);
+	      continue;
+	    }
+
+	  /* If they occur anywhere else, such as in function arguments,
+	     they must not be optimized away.  */
+	  walk_stmt_load_store_ops (stmt, NULL, mark_used, mark_used);
+        }
+    }
+}
+
+/* Traverse the 'defuse_statements' hash table.  For every use,
+   determine if the associated variable is defined along all paths
+   leading to said use.  Remove the associated variable from
+   'static_variables' if it is not.  */
+
+static int
+maybe_remove_stmt (void **slot, void *data ATTRIBUTE_UNUSED)
+{
+  struct rls_stmt_info *info = (struct rls_stmt_info *) *slot;
+
+  if (info->info->optimizable_p)
+    {
+      gimple_stmt_iterator bsi = gsi_for_stmt (info->stmt);
+
+      if (dump_file)
+	{
+	  fprintf (dump_file, "removing stmt ");
+	  print_gimple_stmt (dump_file, info->stmt, 0, 0);
+	  fprintf (dump_file, "\n");
+	}
+      reset_debug_uses (info->stmt);
+      unlink_stmt_vdef (info->stmt);
+      gsi_remove (&bsi, true);
+      release_defs (info->stmt);
+    }
+  return 1;
+}
+
+/* Remove local static variables that are only assigned to.  Return
+   end-of-pass TODO flags.  */
+static unsigned int
+remove_local_statics (void)
+{
+  rls_init ();
+
+  find_static_nonvolatile_declarations ();
+
+  /* Can we optimize anything?  */
+  if (n_statics != 0)
+    {
+      htab_traverse (defuse_statements, maybe_remove_stmt, NULL);
+      if (dump_file)
+        fprintf (dump_file, "removed %d static variables\n",
+                 n_statics);
+    }
+
+  rls_done ();
+
+  if (n_statics > 0)
+    return TODO_rebuild_alias | TODO_update_ssa;
+  else
+    return 0;
+}
+
 /* Main entry point.  */
 
 static unsigned int
@@ -350,6 +655,12 @@ tree_ssa_dse (void)
     
   /* For now, just wipe the post-dominator information.  */
   free_dominance_info (CDI_POST_DOMINATORS);
+
+  if (!cfun->calls_setjmp
+      && !cgraph_get_node (current_function_decl)->ever_was_nested
+      && !cgraph_function_possibly_inlined_p (current_function_decl))
+    return remove_local_statics ();
+
   return 0;
 }
 

Reply via email to