commit ce5d3fe1bf7934dd551b7bf091f113f396e15d64
Author: Bernd Schmidt <bernds@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.
@@ -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) \
@@ -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;
}
@@ -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;
@@ -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;
new file mode 100644
@@ -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" } } */
new file mode 100644
@@ -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" } } */
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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;
+}
+
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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);
+}
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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;
+}
+
new file mode 100644
@@ -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;
+}
+
new file mode 100644
@@ -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" } } */
new file mode 100644
@@ -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" } } */
new file mode 100644
@@ -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" } } */
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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);
+}
+
new file mode 100644
@@ -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;
+}
+
@@ -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;
@@ -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;
}