From patchwork Thu Jun 6 13:42:13 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bernd Schmidt X-Patchwork-Id: 249420 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "localhost", Issuer "www.qmailtoaster.com" (not verified)) by ozlabs.org (Postfix) with ESMTPS id 7853F2C0091 for ; Thu, 6 Jun 2013 23:42:37 +1000 (EST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :message-id:date:from:mime-version:to:cc:subject:content-type; q=dns; s=default; b=n0oEfhaF/wSHolepR8YbFqSXbixZOTJIMptC+/XGhpH cHZ6b81PQBA4KjoQ0XXmxa9STk7WTPpz3AhlYnpKbj17Bcar5Po0gP2Z6ChT5ao2 LABeqsqDTKmJHZTIEcKlzEEp5+0swzBxMNQd067TZgQnRgUDT6kZ8kUig5SYAaII = DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :message-id:date:from:mime-version:to:cc:subject:content-type; s=default; bh=wnQBdiFEgN5szQAz1pcbzDn1GRw=; b=LpyygBSimJclKuH9T j9FnaZLAKWxdkb5ntuvQBs4+hP9gtcJUlBLqKKOaa0TBDoTXiAHcCk31V+R8vRtK usBJiKx3mlggBu/rbwvwFEATYRMZlkbpDH2QvJ2gNm3nB9bjP87oCsNzvufaX/t7 DcR7nAAZXaL29eyvzgDJzp3YFg= Received: (qmail 17668 invoked by alias); 6 Jun 2013 13:42:29 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 17651 invoked by uid 89); 6 Jun 2013 13:42:25 -0000 X-Spam-SWARE-Status: No, score=-2.7 required=5.0 tests=AWL, BAYES_20, KHOP_RCVD_UNTRUST, RCVD_IN_HOSTKARMA_W, RCVD_IN_HOSTKARMA_WL, TW_TJ, TW_TM autolearn=no version=3.3.1 Received: from relay1.mentorg.com (HELO relay1.mentorg.com) (192.94.38.131) by sourceware.org (qpsmtpd/0.84/v0.84-167-ge50287c) with ESMTP; Thu, 06 Jun 2013 13:42:22 +0000 Received: from svr-orw-exc-10.mgc.mentorg.com ([147.34.98.58]) by relay1.mentorg.com with esmtp id 1UkaSK-0004wi-H3 from Bernd_Schmidt@mentor.com for gcc-patches@gcc.gnu.org; Thu, 06 Jun 2013 06:42:20 -0700 Received: from SVR-IES-FEM-01.mgc.mentorg.com ([137.202.0.104]) by SVR-ORW-EXC-10.mgc.mentorg.com with Microsoft SMTPSVC(6.0.3790.4675); Thu, 6 Jun 2013 06:42:20 -0700 Received: from [127.0.0.1] (137.202.0.76) by SVR-IES-FEM-01.mgc.mentorg.com (137.202.0.104) with Microsoft SMTP Server id 14.2.247.3; Thu, 6 Jun 2013 14:42:18 +0100 Message-ID: <51B091B5.2010207@codesourcery.com> Date: Thu, 6 Jun 2013 15:42:13 +0200 From: Bernd Schmidt User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:17.0) Gecko/20130522 Thunderbird/17.0.6 MIME-Version: 1.0 To: GCC Patches CC: Sandra Loosemore , Julian Brown Subject: Remove dead assignments to static local variables X-Virus-Found: No 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 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 + +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; }