From patchwork Sun Dec 12 16:19:23 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John David Anglin X-Patchwork-Id: 75263 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]) by ozlabs.org (Postfix) with SMTP id EEEACB708B for ; Mon, 13 Dec 2010 03:19:40 +1100 (EST) Received: (qmail 26323 invoked by alias); 12 Dec 2010 16:19:38 -0000 Received: (qmail 26313 invoked by uid 22791); 12 Dec 2010 16:19:34 -0000 X-SWARE-Spam-Status: No, hits=-1.9 required=5.0 tests=AWL, BAYES_00, TW_TM, T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from hiauly1.hia.nrc.ca (HELO hiauly1.hia.nrc.ca) (132.246.10.84) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Sun, 12 Dec 2010 16:19:26 +0000 Received: by hiauly1.hia.nrc.ca (Postfix, from userid 1000) id BABAA4DF2; Sun, 12 Dec 2010 11:19:23 -0500 (EST) Date: Sun, 12 Dec 2010 11:19:23 -0500 From: John David Anglin To: Jan Hubicka Cc: Richard Henderson , sje@cup.hp.com, gcc-patches@gcc.gnu.org, mark@codesourcery.com, law@redhat.com Subject: Re: HPPA constructor merge patch, PR middle-end/45388 Message-ID: <20101212161922.GA7838@hiauly1.hia.nrc.ca> Reply-To: John David Anglin References: <20100928120156.GC16774@atrey.karlin.mff.cuni.cz> <20100928152320.C373E502C@hiauly1.hia.nrc.ca> <20100928153628.GA22494@kam.mff.cuni.cz> <4CA20EEA.9040105@redhat.com> <20100928163015.GD6708@atrey.karlin.mff.cuni.cz> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <20100928163015.GD6708@atrey.karlin.mff.cuni.cz> User-Agent: Mutt/1.5.16 (2007-06-09) 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 The attached change reverts commit r163443. This commit broke constructor/destructor handling on targets using the collect2 _GLOBAL_* mechanism (e.g., 32-bit hppa*-*-hpux*). It has now been nearly four months since the commit was made and no progress has been made in finding a fix along the lines discussed below. I believe the change should be reverted to provide an incentive to fix the issue. With the change in place, it causes approximately 155 g++, 74 obj-c++ and 5 libgomp fails. Thus, it is time consuming to watch for new regressions in test runs. The reversion is clean. Except for the changes to ipa.c, the commit can be directly reverted. For ipa.c, there were some subsequent changes to the new code but no interaction between old and new. At this time, the change can be easily restored if a solution to the collect2 problem is found. The reversion has been tested on hppa2.0w-hp-hpux11.11. It fixes all the fails introduced in r163443. There is one regression: gcc.dg/ipa/ctor-empty-1.c. This is expected. See http://gcc.gnu.org/ml/gcc-testresults/2010-12/msg01017.html for test results. Ok to commit? On Tue, 28 Sep 2010, Jan Hubicka wrote: > > On 09/28/2010 08:36 AM, Jan Hubicka wrote: > > > This is bit difficult to arrange with LTO. We produce at compile time the consturctor > > > function with magic names, then at LTO time we want to produce single constructor function > > > calling them all. We would need to guess on what name is the magic name (by same logic > > > as what collect does) and rename function back. > > > I can definitly implement it, but it seems more hackish than the collect2 side change. > > > > An alternative is that at compile time we emit > > > > _Z41__static_initialization_and_destruction_0ii > > > > to the intermediate code as the constructor, and > > > > GLOBAL__I__ZN2c12f6Ev calls > > > > to the object code calling _Z41. However, we don't emit > > GLOBAL to the intermediate code at all. Thus when LTO > > replaces the object files there's no trace of the original > > GLOBAL function at all, and thus collect2 does not see it. > > LTO will simply see _Z41 and call that function directly. > > > > This is not entirely different from the case in which we > > have .ctor support -- it's not like we read in the piece > > of the object code that contains the .ctor data. Just > > consider the GLOBAL function object file data. > > > > This should be doable with a flag on the decl for GLOBAL > > that indicates that it should not be serialized. > > Or we can just produce those collected global constructors after > serialization. I will check... > > Honza > > > > > > r~ Dave Index: tree-pass.h =================================================================== --- tree-pass.h (revision 167709) +++ tree-pass.h (working copy) @@ -465,7 +465,6 @@ extern struct ipa_opt_pass_d pass_ipa_lto_wpa_fixup; extern struct ipa_opt_pass_d pass_ipa_lto_finish_out; extern struct ipa_opt_pass_d pass_ipa_profile; -extern struct ipa_opt_pass_d pass_ipa_cdtor_merge; extern struct gimple_opt_pass pass_all_optimizations; extern struct gimple_opt_pass pass_cleanup_cfg_post_optimizing; Index: cgraphunit.c =================================================================== --- cgraphunit.c (revision 167709) +++ cgraphunit.c (working copy) @@ -147,9 +147,174 @@ FILE *cgraph_dump_file; +/* A vector of FUNCTION_DECLs declared as static constructors. */ +static GTY (()) VEC(tree, gc) *static_ctors; +/* A vector of FUNCTION_DECLs declared as static destructors. */ +static GTY (()) VEC(tree, gc) *static_dtors; + /* Used for vtable lookup in thunk adjusting. */ static GTY (()) tree vtable_entry_type; +/* When target does not have ctors and dtors, we call all constructor + and destructor by special initialization/destruction function + recognized by collect2. + + When we are going to build this function, collect all constructors and + destructors and turn them into normal functions. */ + +static void +record_cdtor_fn (tree fndecl) +{ + struct cgraph_node *node; + if (targetm.have_ctors_dtors + || (!DECL_STATIC_CONSTRUCTOR (fndecl) + && !DECL_STATIC_DESTRUCTOR (fndecl))) + return; + + if (DECL_STATIC_CONSTRUCTOR (fndecl)) + { + VEC_safe_push (tree, gc, static_ctors, fndecl); + DECL_STATIC_CONSTRUCTOR (fndecl) = 0; + } + if (DECL_STATIC_DESTRUCTOR (fndecl)) + { + VEC_safe_push (tree, gc, static_dtors, fndecl); + DECL_STATIC_DESTRUCTOR (fndecl) = 0; + } + node = cgraph_node (fndecl); + node->local.disregard_inline_limits = 1; + cgraph_mark_reachable_node (node); +} + +/* Define global constructors/destructor functions for the CDTORS, of + which they are LEN. The CDTORS are sorted by initialization + priority. If CTOR_P is true, these are constructors; otherwise, + they are destructors. */ + +static void +build_cdtor (bool ctor_p, tree *cdtors, size_t len) +{ + size_t i; + + i = 0; + while (i < len) + { + tree body; + tree fn; + priority_type priority; + + priority = 0; + body = NULL_TREE; + /* Find the next batch of constructors/destructors with the same + initialization priority. */ + do + { + priority_type p; + tree call; + fn = cdtors[i]; + p = ctor_p ? DECL_INIT_PRIORITY (fn) : DECL_FINI_PRIORITY (fn); + if (!body) + priority = p; + else if (p != priority) + break; + call = build_call_expr (fn, 0); + append_to_statement_list (call, &body); + ++i; + } + while (i < len); + gcc_assert (body != NULL_TREE); + /* Generate a function to call all the function of like + priority. */ + cgraph_build_static_cdtor (ctor_p ? 'I' : 'D', body, priority); + } +} + +/* Comparison function for qsort. P1 and P2 are actually of type + "tree *" and point to static constructors. DECL_INIT_PRIORITY is + used to determine the sort order. */ + +static int +compare_ctor (const void *p1, const void *p2) +{ + tree f1; + tree f2; + int priority1; + int priority2; + + f1 = *(const tree *)p1; + f2 = *(const tree *)p2; + priority1 = DECL_INIT_PRIORITY (f1); + priority2 = DECL_INIT_PRIORITY (f2); + + if (priority1 < priority2) + return -1; + else if (priority1 > priority2) + return 1; + else + /* Ensure a stable sort. */ + return (const tree *)p1 - (const tree *)p2; +} + +/* Comparison function for qsort. P1 and P2 are actually of type + "tree *" and point to static destructors. DECL_FINI_PRIORITY is + used to determine the sort order. */ + +static int +compare_dtor (const void *p1, const void *p2) +{ + tree f1; + tree f2; + int priority1; + int priority2; + + f1 = *(const tree *)p1; + f2 = *(const tree *)p2; + priority1 = DECL_FINI_PRIORITY (f1); + priority2 = DECL_FINI_PRIORITY (f2); + + if (priority1 < priority2) + return -1; + else if (priority1 > priority2) + return 1; + else + /* Ensure a stable sort. */ + return (const tree *)p1 - (const tree *)p2; +} + +/* Generate functions to call static constructors and destructors + for targets that do not support .ctors/.dtors sections. These + functions have magic names which are detected by collect2. */ + +static void +cgraph_build_cdtor_fns (void) +{ + if (!VEC_empty (tree, static_ctors)) + { + gcc_assert (!targetm.have_ctors_dtors); + qsort (VEC_address (tree, static_ctors), + VEC_length (tree, static_ctors), + sizeof (tree), + compare_ctor); + build_cdtor (/*ctor_p=*/true, + VEC_address (tree, static_ctors), + VEC_length (tree, static_ctors)); + VEC_truncate (tree, static_ctors, 0); + } + + if (!VEC_empty (tree, static_dtors)) + { + gcc_assert (!targetm.have_ctors_dtors); + qsort (VEC_address (tree, static_dtors), + VEC_length (tree, static_dtors), + sizeof (tree), + compare_dtor); + build_cdtor (/*ctor_p=*/false, + VEC_address (tree, static_dtors), + VEC_length (tree, static_dtors)); + VEC_truncate (tree, static_dtors, 0); + } +} + /* Determine if function DECL is needed. That is, visible to something either outside this translation unit, something magic in the system configury. */ @@ -353,6 +518,7 @@ node->local.finalized = true; node->lowered = DECL_STRUCT_FUNCTION (decl)->cfg != NULL; node->finalized_by_frontend = true; + record_cdtor_fn (node->decl); if (cgraph_decide_is_function_needed (node, decl)) cgraph_mark_needed_node (node); @@ -1008,6 +1174,10 @@ /* Emit size functions we didn't inline. */ finalize_size_functions (); + /* Call functions declared with the "constructor" or "destructor" + attribute. */ + cgraph_build_cdtor_fns (); + /* Mark alias targets necessary and emit diagnostics. */ finish_aliases_1 (); @@ -1861,7 +2031,79 @@ #endif } + +/* Generate and emit a static constructor or destructor. WHICH must + be one of 'I' (for a constructor) or 'D' (for a destructor). BODY + is a STATEMENT_LIST containing GENERIC statements. PRIORITY is the + initialization priority for this constructor or destructor. */ + void +cgraph_build_static_cdtor (char which, tree body, int priority) +{ + static int counter = 0; + char which_buf[16]; + tree decl, name, resdecl; + + /* The priority is encoded in the constructor or destructor name. + collect2 will sort the names and arrange that they are called at + program startup. */ + sprintf (which_buf, "%c_%.5d_%d", which, priority, counter++); + name = get_file_function_name (which_buf); + + decl = build_decl (input_location, FUNCTION_DECL, name, + build_function_type_list (void_type_node, NULL_TREE)); + current_function_decl = decl; + + resdecl = build_decl (input_location, + RESULT_DECL, NULL_TREE, void_type_node); + DECL_ARTIFICIAL (resdecl) = 1; + DECL_RESULT (decl) = resdecl; + DECL_CONTEXT (resdecl) = decl; + + allocate_struct_function (decl, false); + + TREE_STATIC (decl) = 1; + TREE_USED (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; + DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl) = 1; + DECL_SAVED_TREE (decl) = body; + if (!targetm.have_ctors_dtors) + { + TREE_PUBLIC (decl) = 1; + DECL_PRESERVE_P (decl) = 1; + } + DECL_UNINLINABLE (decl) = 1; + + DECL_INITIAL (decl) = make_node (BLOCK); + TREE_USED (DECL_INITIAL (decl)) = 1; + + DECL_SOURCE_LOCATION (decl) = input_location; + cfun->function_end_locus = input_location; + + switch (which) + { + case 'I': + DECL_STATIC_CONSTRUCTOR (decl) = 1; + decl_init_priority_insert (decl, priority); + break; + case 'D': + DECL_STATIC_DESTRUCTOR (decl) = 1; + decl_fini_priority_insert (decl, priority); + break; + default: + gcc_unreachable (); + } + + gimplify_function_tree (decl); + + cgraph_add_new_function (decl, false); + cgraph_mark_needed_node (cgraph_node (decl)); + + set_cfun (NULL); + current_function_decl = NULL; +} + +void init_cgraph (void) { if (!cgraph_dump_file) Index: ipa.c =================================================================== --- ipa.c (revision 167709) +++ ipa.c (working copy) @@ -29,8 +29,6 @@ #include "ggc.h" #include "flags.h" #include "pointer-set.h" -#include "target.h" -#include "tree-iterator.h" /* Fill array order with all nodes with output flag set in the reverse topological order. */ @@ -1492,296 +1490,3 @@ NULL, /* function_transform */ NULL /* variable_transform */ }; - -/* Generate and emit a static constructor or destructor. WHICH must - be one of 'I' (for a constructor) or 'D' (for a destructor). BODY - is a STATEMENT_LIST containing GENERIC statements. PRIORITY is the - initialization priority for this constructor or destructor. */ - -void -cgraph_build_static_cdtor (char which, tree body, int priority) -{ - static int counter = 0; - char which_buf[16]; - tree decl, name, resdecl; - - /* The priority is encoded in the constructor or destructor name. - collect2 will sort the names and arrange that they are called at - program startup. */ - sprintf (which_buf, "%c_%.5d_%d", which, priority, counter++); - name = get_file_function_name (which_buf); - - decl = build_decl (input_location, FUNCTION_DECL, name, - build_function_type_list (void_type_node, NULL_TREE)); - current_function_decl = decl; - - resdecl = build_decl (input_location, - RESULT_DECL, NULL_TREE, void_type_node); - DECL_ARTIFICIAL (resdecl) = 1; - DECL_RESULT (decl) = resdecl; - DECL_CONTEXT (resdecl) = decl; - - allocate_struct_function (decl, false); - - TREE_STATIC (decl) = 1; - TREE_USED (decl) = 1; - DECL_ARTIFICIAL (decl) = 1; - DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl) = 1; - DECL_SAVED_TREE (decl) = body; - if (!targetm.have_ctors_dtors) - { - TREE_PUBLIC (decl) = 1; - DECL_PRESERVE_P (decl) = 1; - } - DECL_UNINLINABLE (decl) = 1; - - DECL_INITIAL (decl) = make_node (BLOCK); - TREE_USED (DECL_INITIAL (decl)) = 1; - - DECL_SOURCE_LOCATION (decl) = input_location; - cfun->function_end_locus = input_location; - - switch (which) - { - case 'I': - DECL_STATIC_CONSTRUCTOR (decl) = 1; - decl_init_priority_insert (decl, priority); - break; - case 'D': - DECL_STATIC_DESTRUCTOR (decl) = 1; - decl_fini_priority_insert (decl, priority); - break; - default: - gcc_unreachable (); - } - - gimplify_function_tree (decl); - - cgraph_add_new_function (decl, false); - - set_cfun (NULL); - current_function_decl = NULL; -} - - -/* A vector of FUNCTION_DECLs declared as static constructors. */ -static VEC(tree, heap) *static_ctors; -/* A vector of FUNCTION_DECLs declared as static destructors. */ -static VEC(tree, heap) *static_dtors; - -/* When target does not have ctors and dtors, we call all constructor - and destructor by special initialization/destruction function - recognized by collect2. - - When we are going to build this function, collect all constructors and - destructors and turn them into normal functions. */ - -static void -record_cdtor_fn (struct cgraph_node *node) -{ - if (DECL_STATIC_CONSTRUCTOR (node->decl)) - VEC_safe_push (tree, heap, static_ctors, node->decl); - if (DECL_STATIC_DESTRUCTOR (node->decl)) - VEC_safe_push (tree, heap, static_dtors, node->decl); - node = cgraph_node (node->decl); - node->local.disregard_inline_limits = 1; -} - -/* Define global constructors/destructor functions for the CDTORS, of - which they are LEN. The CDTORS are sorted by initialization - priority. If CTOR_P is true, these are constructors; otherwise, - they are destructors. */ - -static void -build_cdtor (bool ctor_p, VEC (tree, heap) *cdtors) -{ - size_t i,j; - size_t len = VEC_length (tree, cdtors); - - i = 0; - while (i < len) - { - tree body; - tree fn; - priority_type priority; - - priority = 0; - body = NULL_TREE; - j = i; - do - { - priority_type p; - fn = VEC_index (tree, cdtors, j); - p = ctor_p ? DECL_INIT_PRIORITY (fn) : DECL_FINI_PRIORITY (fn); - if (j == i) - priority = p; - else if (p != priority) - break; - j++; - } - while (j < len); - - /* When there is only one cdtor and target supports them, do nothing. */ - if (j == i + 1 - && targetm.have_ctors_dtors) - { - i++; - continue; - } - /* Find the next batch of constructors/destructors with the same - initialization priority. */ - for (;i < j; i++) - { - tree call; - fn = VEC_index (tree, cdtors, i); - call = build_call_expr (fn, 0); - if (ctor_p) - DECL_STATIC_CONSTRUCTOR (fn) = 0; - else - DECL_STATIC_DESTRUCTOR (fn) = 0; - /* We do not want to optimize away pure/const calls here. - When optimizing, these should be already removed, when not - optimizing, we want user to be able to breakpoint in them. */ - TREE_SIDE_EFFECTS (call) = 1; - append_to_statement_list (call, &body); - } - gcc_assert (body != NULL_TREE); - /* Generate a function to call all the function of like - priority. */ - cgraph_build_static_cdtor (ctor_p ? 'I' : 'D', body, priority); - } -} - -/* Comparison function for qsort. P1 and P2 are actually of type - "tree *" and point to static constructors. DECL_INIT_PRIORITY is - used to determine the sort order. */ - -static int -compare_ctor (const void *p1, const void *p2) -{ - tree f1; - tree f2; - int priority1; - int priority2; - - f1 = *(const tree *)p1; - f2 = *(const tree *)p2; - priority1 = DECL_INIT_PRIORITY (f1); - priority2 = DECL_INIT_PRIORITY (f2); - - if (priority1 < priority2) - return -1; - else if (priority1 > priority2) - return 1; - else - /* Ensure a stable sort. Constructors are executed in backwarding - order to make LTO initialize braries first. */ - return DECL_UID (f2) - DECL_UID (f1); -} - -/* Comparison function for qsort. P1 and P2 are actually of type - "tree *" and point to static destructors. DECL_FINI_PRIORITY is - used to determine the sort order. */ - -static int -compare_dtor (const void *p1, const void *p2) -{ - tree f1; - tree f2; - int priority1; - int priority2; - - f1 = *(const tree *)p1; - f2 = *(const tree *)p2; - priority1 = DECL_FINI_PRIORITY (f1); - priority2 = DECL_FINI_PRIORITY (f2); - - if (priority1 < priority2) - return -1; - else if (priority1 > priority2) - return 1; - else - /* Ensure a stable sort. */ - return DECL_UID (f1) - DECL_UID (f2); -} - -/* Generate functions to call static constructors and destructors - for targets that do not support .ctors/.dtors sections. These - functions have magic names which are detected by collect2. */ - -static void -build_cdtor_fns (void) -{ - if (!VEC_empty (tree, static_ctors)) - { - gcc_assert (!targetm.have_ctors_dtors || in_lto_p); - VEC_qsort (tree, static_ctors, compare_ctor); - build_cdtor (/*ctor_p=*/true, static_ctors); - } - - if (!VEC_empty (tree, static_dtors)) - { - gcc_assert (!targetm.have_ctors_dtors || in_lto_p); - VEC_qsort (tree, static_dtors, compare_dtor); - build_cdtor (/*ctor_p=*/false, static_dtors); - } -} - -/* Look for constructors and destructors and produce function calling them. - This is needed for targets not supporting ctors or dtors, but we perform the - transformation also at linktime to merge possibly numberous - constructors/destructors into single function to improve code locality and - reduce size. */ - -static unsigned int -ipa_cdtor_merge (void) -{ - struct cgraph_node *node; - for (node = cgraph_nodes; node; node = node->next) - if (node->analyzed - && (DECL_STATIC_CONSTRUCTOR (node->decl) - || DECL_STATIC_DESTRUCTOR (node->decl))) - record_cdtor_fn (node); - build_cdtor_fns (); - VEC_free (tree, heap, static_ctors); - VEC_free (tree, heap, static_dtors); - return 0; -} - -/* Perform the pass when we have no ctors/dtors support - or at LTO time to merge multiple constructors into single - function. */ - -static bool -gate_ipa_cdtor_merge (void) -{ - return !targetm.have_ctors_dtors || (optimize && in_lto_p); -} - -struct ipa_opt_pass_d pass_ipa_cdtor_merge = -{ - { - IPA_PASS, - "cdtor", /* name */ - gate_ipa_cdtor_merge, /* gate */ - ipa_cdtor_merge, /* execute */ - NULL, /* sub */ - NULL, /* next */ - 0, /* static_pass_number */ - TV_CGRAPHOPT, /* tv_id */ - 0, /* properties_required */ - 0, /* properties_provided */ - 0, /* properties_destroyed */ - 0, /* todo_flags_start */ - 0 /* todo_flags_finish */ - }, - NULL, /* generate_summary */ - NULL, /* write_summary */ - NULL, /* read_summary */ - NULL, /* write_optimization_summary */ - NULL, /* read_optimization_summary */ - NULL, /* stmt_fixup */ - 0, /* TODOs */ - NULL, /* function_transform */ - NULL /* variable_transform */ -}; Index: ipa-prop.c =================================================================== --- ipa-prop.c (revision 167709) +++ ipa-prop.c (working copy) @@ -1575,12 +1575,11 @@ struct cgraph_node *node, VEC (cgraph_edge_p, heap) **new_edges) { - struct ipa_edge_args *top; + struct ipa_edge_args *top = IPA_EDGE_REF (cs); struct cgraph_edge *ie, *next_ie, *new_direct_edge; bool res = false; ipa_check_create_edge_args (); - top = IPA_EDGE_REF (cs); for (ie = node->indirect_calls; ie; ie = next_ie) { Index: passes.c =================================================================== --- passes.c (revision 167709) +++ passes.c (working copy) @@ -794,7 +794,6 @@ NEXT_PASS (pass_ipa_whole_program_visibility); NEXT_PASS (pass_ipa_profile); NEXT_PASS (pass_ipa_cp); - NEXT_PASS (pass_ipa_cdtor_merge); NEXT_PASS (pass_ipa_inline); NEXT_PASS (pass_ipa_pure_const); NEXT_PASS (pass_ipa_reference);