This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.
Index Nav: | [Date Index] [Subject Index] [Author Index] [Thread Index] | |
---|---|---|
Message Nav: | [Date Prev] [Date Next] | [Thread Prev] [Thread Next] |
Other format: | [Raw text] |
This patch makes us generate smaller code for cleaning up C++ objects with static storage duration. These functions must be destroyed when the program terminates. In the old C++ ABI, we called atexit to register these functions, or called them from .dtors. In either case, we had no way to pass a parameter to the function. So, we generated a cleanup function per object to be cleaned up; that function then called the appropriate destructor, passing it the address of the object as the "this" pointer. The new ABI gave us __cxa_atexit, which provides a more efficient alternative. In particular, because __cxa_atexit allows us to register both a function and a pointer argument to the function, we can register the destructor and the "this" pointer directly. That avoids generating any additional functions, which makes the program smaller. (I suppose it may also make it marginally faster, in that there's less code to load, and we avoid a bit of indirection when the program exits, but the primary impact is space.) I don't know how much of an impact this makes on most programs, but it was low-hanging fruit, so ... Tested on x86_64-unknown-linux-gnu, applied on the mainline. -- Mark Mitchell CodeSourcery mark@codesourcery.com (650) 331-3385 x713 2007-05-31 Mark Mitchell <mark@codesourcery.com> * decl.c (get_atexit_fn_ptr_type): New function. (get_atexit_node): Use it. (start_cleanup_fn): Likewise. (register_dtor_fn): Use the object's destructor, instead of a separate cleanup function, where possible. * cp-tree.h (CPTI_ATEXIT_FN_PTR_TYPE): New enumerator. (atexit_fn_ptr_type_node): New macro. * decl2.c (build_cleanup): Use build_address. 2007-05-31 Mark Mitchell <mark@codesourcery.com> * g++.dg/init/cleanup3.C: New test. Index: gcc/cp/decl.c =================================================================== --- gcc/cp/decl.c (revision 125150) +++ gcc/cp/decl.c (working copy) @@ -5413,6 +5413,33 @@ declare_global_var (tree name, tree type return decl; } +/* Returns the type for the argument to "__cxa_atexit" (or "atexit", + if "__cxa_atexit" is not being used) corresponding to the function + to be called when the program exits. */ + +static tree +get_atexit_fn_ptr_type (void) +{ + tree arg_types; + tree fn_type; + + if (!atexit_fn_ptr_type_node) + { + if (flag_use_cxa_atexit + && !targetm.cxx.use_atexit_for_cxa_atexit ()) + /* The parameter to "__cxa_atexit" is "void (*)(void *)". */ + arg_types = tree_cons (NULL_TREE, ptr_type_node, void_list_node); + else + /* The parameter to "atexit" is "void (*)(void)". */ + arg_types = void_list_node; + + fn_type = build_function_type (void_type_node, arg_types); + atexit_fn_ptr_type_node = build_pointer_type (fn_type); + } + + return atexit_fn_ptr_type_node; +} + /* Returns a pointer to the `atexit' function. Note that if FLAG_USE_CXA_ATEXIT is nonzero, then this will actually be the new `__cxa_atexit' function specified in the IA64 C++ ABI. */ @@ -5442,9 +5469,7 @@ get_atexit_node (void) use_aeabi_atexit = targetm.cxx.use_aeabi_atexit (); /* First, build the pointer-to-function type for the first argument. */ - arg_types = tree_cons (NULL_TREE, ptr_type_node, void_list_node); - fn_type = build_function_type (void_type_node, arg_types); - fn_ptr_type = build_pointer_type (fn_type); + fn_ptr_type = get_atexit_fn_ptr_type (); /* Then, build the rest of the argument types. */ arg_types = tree_cons (NULL_TREE, ptr_type_node, void_list_node); if (use_aeabi_atexit) @@ -5473,8 +5498,7 @@ get_atexit_node (void) We build up the argument types and then then function type itself. */ - fn_type = build_function_type (void_type_node, void_list_node); - fn_ptr_type = build_pointer_type (fn_type); + fn_ptr_type = get_atexit_fn_ptr_type (); arg_types = tree_cons (NULL_TREE, fn_ptr_type, void_list_node); /* Build the final atexit type. */ fn_type = build_function_type (integer_type_node, arg_types); @@ -5515,7 +5539,6 @@ static tree start_cleanup_fn (void) { char name[32]; - tree parmtypes; tree fntype; tree fndecl; bool use_cxa_atexit = flag_use_cxa_atexit @@ -5526,19 +5549,10 @@ start_cleanup_fn (void) /* No need to mangle this. */ push_lang_context (lang_name_c); - /* Build the parameter-types. */ - parmtypes = void_list_node; - /* Functions passed to __cxa_atexit take an additional parameter. - We'll just ignore it. After we implement the new calling - convention for destructors, we can eliminate the use of - additional cleanup functions entirely in the -fnew-abi case. */ - if (use_cxa_atexit) - parmtypes = tree_cons (NULL_TREE, ptr_type_node, parmtypes); - /* Build the function type itself. */ - fntype = build_function_type (void_type_node, parmtypes); /* Build the name of the function. */ sprintf (name, "__tcf_%d", start_cleanup_cnt++); /* Build the function declaration. */ + fntype = TREE_TYPE (get_atexit_fn_ptr_type ()); fndecl = build_lang_decl (FUNCTION_DECL, get_identifier (name), fntype); /* It's a function with internal linkage, generated by the compiler. */ @@ -5590,50 +5604,96 @@ register_dtor_fn (tree decl) tree compound_stmt; tree args; tree fcall; + tree type; + bool use_dtor; - if (TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE (decl))) + type = TREE_TYPE (decl); + if (TYPE_HAS_TRIVIAL_DESTRUCTOR (type)) return void_zero_node; - /* Call build_cleanup before we enter the anonymous function so that - any access checks will be done relative to the current scope, - rather than the scope of the anonymous function. */ - build_cleanup (decl); - - /* Now start the function. */ - cleanup = start_cleanup_fn (); - - /* Now, recompute the cleanup. It may contain SAVE_EXPRs that refer - to the original function, rather than the anonymous one. That - will make the back end think that nested functions are in use, - which causes confusion. */ - - push_deferring_access_checks (dk_no_check); - fcall = build_cleanup (decl); - pop_deferring_access_checks (); - - /* Create the body of the anonymous function. */ - compound_stmt = begin_compound_stmt (BCS_FN_BODY); - finish_expr_stmt (fcall); - finish_compound_stmt (compound_stmt); - end_cleanup_fn (); + /* If we're using "__cxa_atexit" (or "__aeabi_atexit"), and DECL is + a class object, we can just pass the destructor to + "__cxa_atexit"; we don't have to build a temporary function to do + the cleanup. */ + use_dtor = (flag_use_cxa_atexit + && !targetm.cxx.use_atexit_for_cxa_atexit () + && CLASS_TYPE_P (type)); + if (use_dtor) + { + int idx; + + /* Find the destructor. */ + idx = lookup_fnfields_1 (type, complete_dtor_identifier); + gcc_assert (idx >= 0); + cleanup = VEC_index (tree, CLASSTYPE_METHOD_VEC (type), idx); + /* Make sure it is accessible. */ + perform_or_defer_access_check (TYPE_BINFO (type), cleanup, cleanup); + } + else + { + /* Call build_cleanup before we enter the anonymous function so + that any access checks will be done relative to the current + scope, rather than the scope of the anonymous function. */ + build_cleanup (decl); + + /* Now start the function. */ + cleanup = start_cleanup_fn (); + + /* Now, recompute the cleanup. It may contain SAVE_EXPRs that refer + to the original function, rather than the anonymous one. That + will make the back end think that nested functions are in use, + which causes confusion. */ + push_deferring_access_checks (dk_no_check); + fcall = build_cleanup (decl); + pop_deferring_access_checks (); + + /* Create the body of the anonymous function. */ + compound_stmt = begin_compound_stmt (BCS_FN_BODY); + finish_expr_stmt (fcall); + finish_compound_stmt (compound_stmt); + end_cleanup_fn (); + } /* Call atexit with the cleanup function. */ - cxx_mark_addressable (cleanup); mark_used (cleanup); - cleanup = build_unary_op (ADDR_EXPR, cleanup, 0); + cleanup = build_address (cleanup); if (flag_use_cxa_atexit && !targetm.cxx.use_atexit_for_cxa_atexit ()) { + tree addr; + + if (use_dtor) + { + /* We must convert CLEANUP to the type that "__cxa_atexit" + expects. */ + cleanup = build_nop (get_atexit_fn_ptr_type (), cleanup); + /* "__cxa_atexit" will pass the address of DECL to the + cleanup function. */ + mark_used (decl); + addr = build_address (decl); + /* The declared type of the parameter to "__cxa_atexit" is + "void *". For plain "T*", we could just let the + machinery in build_function_call convert it -- but if the + type is "cv-qualified T *", then we need to convert it + before passing it in, to avoid spurious errors. */ + addr = build_nop (ptr_type_node, addr); + } + else + /* Since the cleanup functions we build ignore the address + they're given, there's no reason to pass the actual address + in, and, in general, it's cheaper to pass NULL than any + other value. */ + addr = null_pointer_node; args = tree_cons (NULL_TREE, build_unary_op (ADDR_EXPR, get_dso_handle_node (), 0), NULL_TREE); if (targetm.cxx.use_aeabi_atexit ()) { args = tree_cons (NULL_TREE, cleanup, args); - args = tree_cons (NULL_TREE, null_pointer_node, args); + args = tree_cons (NULL_TREE, addr, args); } else { - args = tree_cons (NULL_TREE, null_pointer_node, args); + args = tree_cons (NULL_TREE, addr, args); args = tree_cons (NULL_TREE, cleanup, args); } } Index: gcc/cp/cp-tree.h =================================================================== --- gcc/cp/cp-tree.h (revision 125150) +++ gcc/cp/cp-tree.h (working copy) @@ -614,6 +614,7 @@ enum cp_tree_index CPTI_JCLASS, CPTI_TERMINATE, CPTI_CALL_UNEXPECTED, + CPTI_ATEXIT_FN_PTR_TYPE, CPTI_ATEXIT, CPTI_DSO_HANDLE, CPTI_DCAST, @@ -703,6 +704,10 @@ extern GTY(()) tree cp_global_trees[CPTI /* The declaration for "__cxa_call_unexpected". */ #define call_unexpected_node cp_global_trees[CPTI_CALL_UNEXPECTED] +/* The type of the function-pointer argument to "__cxa_atexit" (or + "std::atexit", if "__cxa_atexit" is not being used). */ +#define atexit_fn_ptr_type_node cp_global_trees[CPTI_ATEXIT_FN_PTR_TYPE] + /* A pointer to `std::atexit'. */ #define atexit_node cp_global_trees[CPTI_ATEXIT] Index: gcc/cp/decl2.c =================================================================== --- gcc/cp/decl2.c (revision 125150) +++ gcc/cp/decl2.c (working copy) @@ -2190,10 +2190,7 @@ build_cleanup (tree decl) if (TREE_CODE (type) == ARRAY_TYPE) temp = decl; else - { - cxx_mark_addressable (decl); - temp = build1 (ADDR_EXPR, build_pointer_type (type), decl); - } + temp = build_address (decl); temp = build_delete (TREE_TYPE (temp), temp, sfk_complete_destructor, LOOKUP_NORMAL|LOOKUP_NONVIRTUAL|LOOKUP_DESTRUCTOR, 0); Index: gcc/testsuite/g++.dg/init/cleanup3.C =================================================================== --- gcc/testsuite/g++.dg/init/cleanup3.C (revision 0) +++ gcc/testsuite/g++.dg/init/cleanup3.C (revision 0) @@ -0,0 +1,22 @@ +// Check that on targets with "__cxa_atexit" we use destructors, +// rather than cleanup functions, to destroy objects with static +// storage duration. + +// { dg-require-effective-target "cxa_atexit" } +// Cleanup functions generated by G++ have the "_tcf" prefix. +// { dg-final { scan-assembler-not "_tcf" } } + +struct S { + ~S(); +}; + +struct T { + S s; +}; + +S s; +T t; + +void f() { + static S s; +}
Index Nav: | [Date Index] [Subject Index] [Author Index] [Thread Index] | |
---|---|---|
Message Nav: | [Date Prev] [Date Next] | [Thread Prev] [Thread Next] |