From: Niall Douglas Date: Sun, 25 Jul 2004 22:52:22 +0000 (+0000) Subject: re PR c++/9283 (__attribute__((visibility ("hidden"))) not supported for class/struct) X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=d7afec4b079f40ba7857ae1c898ed537b5c14313;p=gcc.git re PR c++/9283 (__attribute__((visibility ("hidden"))) not supported for class/struct) PR c++/9283 PR c++/15000 * c-common.c (c_common_attribute_table): Allow handle_visibility_attribute to be called for types. (handle_visibility_attribute) When given a type, set the visibility bits on the TYPE_NAME. When given a decl, don't set no_add_attrs so that we can check later whether the attribute was present. Added warning if attribute applied to non class type. * c-decl.c (diagnose_mismatched_decls): Updated rules for merging decls and checking that they are consistent. * common.opt: Added -fvisibility. * c.opt, c-opts.c: Added -fvisibility-inlines-hidden. * c-pragma.h, c-pragma.c: Added handle_pragma_visibility(). * flags.h, tree.h: Added assorted support defines for overall patch * opts.c: Added parsing support for -fvisibility. * tree.c (build_decl): Set visibility for all decls to be whatever is in force at that time. * varasm.c (default_binds_local_p_1): Reworked logic determining when to make a symbol locally bound. * doc/invoke.texi: Added documentation for -fvisibility and -fvisibility-inlines-hidden. PR c++/15000 PR c++/9283 * class.c (check_field_decls): Apply hidden visibility if -fvisibility-inlines-hidden and inlined unless otherwise specified (build_vtable): Set vtable visibility to class visibility. (check_field_decls): Default static member visibility to class visibility. (check_methods): Default method visibility to class visibility. * cp-tree.h: Added CLASSTYPE_VISIBILITY and CLASSTYPE_VISIBILITY_SPECIFIED macro. * decl.c (duplicate_decls): New logic for merging definition decls with declaration decls. Added ignore & warning when non default applied to global operator new or delete. * method.c, optimize.c, rtti.c: Added setting of VISIBILITY_SPECIFIED wherever VISIBILITY was changed * rtti.c (get_tinfo_decl): Set typeinfo visibility to class visibility. (tinfo_base_init): Set typeinfo name visibility to class visibility. PR c++/9283 PR c++/15000 * gcc.dg/visibility-9.c, gcc.dg/visibility-a.c: New tests. * g++.dg/ext/visibility/: New directory. * g++.dg/ext/visibility-1.C, g++.dg/ext/visibility-2.C g++.dg/ext/visibility-3.C, g++.dg/ext/visibility-4.C, g++.dg/ext/visibility-5.C, g++.dg/ext/visibility-6.C, g++.dg/ext/visibility-7.C: Move to g++.dg/ext/visibility/. * g++.dg/ext/visibility/fvisibility.C, g++.dg/ext/visibility/fvisibility-inlines-hidden.C, g++.dg/ext/visibility/fvisibility-override1.C g++.dg/ext/visibility/fvisibility-override2.C g++.dg/ext/visibility/memfuncts.C g++.dg/ext/visibility/noPLT.C g++.dg/ext/visibility/pragma.C g++.dg/ext/visibility/pragma-override1.C g++.dg/ext/visibility/pragma-override2.C g++.dg/ext/visibility/staticmemfuncts.C g++.dg/ext/visibility/virtual.C: New tests. Co-Authored-By: Brian Ryner From-SVN: r85167 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 6815008cab5..6c69579d8cc 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,28 @@ +2004-07-26 Niall Douglas + Brian Ryner + + PR c++/9283 + PR c++/15000 + * c-common.c (c_common_attribute_table): Allow + handle_visibility_attribute to be called for types. + (handle_visibility_attribute) When given a type, set the visibility + bits on the TYPE_NAME. When given a decl, don't set no_add_attrs + so that we can check later whether the attribute was present. Added + warning if attribute applied to non class type. + * c-decl.c (diagnose_mismatched_decls): Updated rules for merging + decls and checking that they are consistent. + * common.opt: Added -fvisibility. + * c.opt, c-opts.c: Added -fvisibility-inlines-hidden. + * c-pragma.h, c-pragma.c: Added handle_pragma_visibility(). + * flags.h, tree.h: Added assorted support defines for overall patch + * opts.c: Added parsing support for -fvisibility. + * tree.c (build_decl): Set visibility for all decls to be whatever + is in force at that time. + * varasm.c (default_binds_local_p_1): Reworked logic determining + when to make a symbol locally bound. + * doc/invoke.texi: Added documentation for -fvisibility and + -fvisibility-inlines-hidden. + 2004-07-25 Bernardo Innocenti * basic-block.h (reorder_block_def): Rename to reorder_block_def_p. diff --git a/gcc/c-common.c b/gcc/c-common.c index ddd8e8766b0..2fe696d676c 100644 --- a/gcc/c-common.c +++ b/gcc/c-common.c @@ -616,7 +616,7 @@ const struct attribute_spec c_common_attribute_table[] = handle_deprecated_attribute }, { "vector_size", 1, 1, false, true, false, handle_vector_size_attribute }, - { "visibility", 1, 1, true, false, false, + { "visibility", 1, 1, false, false, false, handle_visibility_attribute }, { "tls_model", 1, 1, true, false, false, handle_tls_model_attribute }, @@ -4563,7 +4563,16 @@ handle_visibility_attribute (tree *node, tree name, tree args, *no_add_attrs = true; - if (decl_function_context (decl) != 0 || ! TREE_PUBLIC (decl)) + if (TYPE_P (*node)) + { + if (TREE_CODE (*node) != RECORD_TYPE && TREE_CODE (*node) != UNION_TYPE) + { + warning ("`%s' attribute ignored on non-class types", + IDENTIFIER_POINTER (name)); + return NULL_TREE; + } + } + else if (decl_function_context (decl) != 0 || ! TREE_PUBLIC (decl)) { warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); return NULL_TREE; @@ -4574,6 +4583,14 @@ handle_visibility_attribute (tree *node, tree name, tree args, error ("visibility arg not a string"); return NULL_TREE; } + + /* If this is a type, set the visibility on the type decl. */ + if (TYPE_P (decl)) + { + decl = TYPE_NAME (decl); + if (! decl) + return NULL_TREE; + } if (strcmp (TREE_STRING_POINTER (id), "default") == 0) DECL_VISIBILITY (decl) = VISIBILITY_DEFAULT; @@ -4585,6 +4602,14 @@ handle_visibility_attribute (tree *node, tree name, tree args, DECL_VISIBILITY (decl) = VISIBILITY_PROTECTED; else error ("visibility arg must be one of \"default\", \"hidden\", \"protected\" or \"internal\""); + DECL_VISIBILITY_SPECIFIED (decl) = 1; + + /* For decls only, go ahead and attach the attribute to the node as well. + This is needed so we can determine whether we have VISIBILITY_DEFAULT + because the visibility was not specified, or because it was explicitly + overridden from the class visibility. */ + if (DECL_P (*node)) + *no_add_attrs = false; return NULL_TREE; } diff --git a/gcc/c-decl.c b/gcc/c-decl.c index 47cfd5cc24f..e4c022ff25e 100644 --- a/gcc/c-decl.c +++ b/gcc/c-decl.c @@ -1367,9 +1367,8 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl, } /* warnings */ - /* All decls must agree on a non-default visibility. */ - if (DECL_VISIBILITY (newdecl) != VISIBILITY_DEFAULT - && DECL_VISIBILITY (olddecl) != VISIBILITY_DEFAULT + /* All decls must agree on a visibility. */ + if (DECL_VISIBILITY_SPECIFIED (newdecl) && DECL_VISIBILITY_SPECIFIED (olddecl) && DECL_VISIBILITY (newdecl) != DECL_VISIBILITY (olddecl)) { warning ("%Jredeclaration of '%D' with different visibility " @@ -1570,9 +1569,12 @@ merge_decls (tree newdecl, tree olddecl, tree newtype, tree oldtype) Currently, it can only be defined in the prototype. */ COPY_DECL_ASSEMBLER_NAME (olddecl, newdecl); - /* If either declaration has a nondefault visibility, use it. */ - if (DECL_VISIBILITY (olddecl) != VISIBILITY_DEFAULT) - DECL_VISIBILITY (newdecl) = DECL_VISIBILITY (olddecl); + /* Use visibility of whichever declaration had it specified */ + if (DECL_VISIBILITY_SPECIFIED (olddecl)) + { + DECL_VISIBILITY (newdecl) = DECL_VISIBILITY (olddecl); + DECL_VISIBILITY_SPECIFIED (newdecl) = 1; + } if (TREE_CODE (newdecl) == FUNCTION_DECL) { diff --git a/gcc/c-opts.c b/gcc/c-opts.c index 2b4ab9f76dc..faa6023b1b9 100644 --- a/gcc/c-opts.c +++ b/gcc/c-opts.c @@ -764,6 +764,10 @@ c_common_handle_option (size_t scode, const char *arg, int value) case OPT_fuse_cxa_atexit: flag_use_cxa_atexit = value; break; + + case OPT_fvisibility_inlines_hidden: + visibility_options.inlines_hidden = value; + break; case OPT_fweak: flag_weak = value; diff --git a/gcc/c-pragma.c b/gcc/c-pragma.c index 563e2d7abdc..0bf2f1231bc 100644 --- a/gcc/c-pragma.c +++ b/gcc/c-pragma.c @@ -566,6 +566,86 @@ maybe_apply_renaming_pragma (tree decl, tree asmname) return 0; } + +#ifdef HANDLE_PRAGMA_VISIBILITY +static void handle_pragma_visibility (cpp_reader *); + +/* Sets the default visibility for symbols to something other than that + specified on the command line. */ +static void +handle_pragma_visibility (cpp_reader *dummy ATTRIBUTE_UNUSED) +{ /* Form is #pragma GCC visibility push(hidden)|pop */ + static int visstack [16], visidx; + tree x; + enum cpp_ttype token; + enum { bad, push, pop } action = bad; + + token = c_lex (&x); + if (token == CPP_NAME) + { + const char *op = IDENTIFIER_POINTER (x); + if (!strcmp (op, "push")) + action = push; + else if (!strcmp (op, "pop")) + action = pop; + } + if (bad == action) + GCC_BAD ("#pragma GCC visibility must be followed by push or pop"); + else + { + if (pop == action) + { + if (!visidx) + { + GCC_BAD ("No matching push for '#pragma GCC visibility pop'"); + } + else + { + default_visibility = visstack[--visidx]; + visibility_options.inpragma = (visidx>0); + } + } + else + { + if (c_lex (&x) != CPP_OPEN_PAREN) + GCC_BAD ("missing '(' after '#pragma GCC visibility push' - ignored"); + token = c_lex (&x); + if (token != CPP_NAME) + { + GCC_BAD ("malformed #pragma GCC visibility push"); + } + else if (visidx >= 16) + { + GCC_BAD ("No more than sixteen #pragma GCC visibility pushes allowed at once"); + } + else + { + const char *str = IDENTIFIER_POINTER (x); + visstack[visidx++] = default_visibility; + if (!strcmp (str, "default")) + default_visibility = VISIBILITY_DEFAULT; + else if (!strcmp (str, "internal")) + default_visibility = VISIBILITY_INTERNAL; + else if (!strcmp (str, "hidden")) + default_visibility = VISIBILITY_HIDDEN; + else if (!strcmp (str, "protected")) + default_visibility = VISIBILITY_PROTECTED; + else + { + GCC_BAD ("#pragma GCC visibility push() must specify default, internal, hidden or protected"); + } + visibility_options.inpragma = 1; + } + if (c_lex (&x) != CPP_CLOSE_PAREN) + GCC_BAD ("missing '(' after '#pragma GCC visibility push' - ignored"); + } + } + if (c_lex (&x) != CPP_EOF) + warning ("junk at end of '#pragma GCC visibility'"); +} + +#endif + /* Front-end wrapper for pragma registration to avoid dragging cpplib.h in almost everywhere. */ void @@ -585,6 +665,9 @@ init_pragma (void) #ifdef HANDLE_PRAGMA_WEAK c_register_pragma (0, "weak", handle_pragma_weak); #endif +#ifdef HANDLE_PRAGMA_VISIBILITY + c_register_pragma ("GCC", "visibility", handle_pragma_visibility); +#endif c_register_pragma (0, "redefine_extname", handle_pragma_redefine_extname); c_register_pragma (0, "extern_prefix", handle_pragma_extern_prefix); diff --git a/gcc/c-pragma.h b/gcc/c-pragma.h index 6bb10f3af70..64c83ff0c5a 100644 --- a/gcc/c-pragma.h +++ b/gcc/c-pragma.h @@ -44,6 +44,11 @@ extern struct cpp_reader* parse_in; #define HANDLE_PRAGMA_PACK 1 #endif /* HANDLE_PRAGMA_PACK_PUSH_POP */ +/* It's safe to always leave visibility pragma enabled as if + visibility is not supported on the host OS platform the + statements are ignored. */ +#define HANDLE_PRAGMA_VISIBILITY 1 + extern void init_pragma (void); /* Front-end wrapper for pragma registration to avoid dragging diff --git a/gcc/c.opt b/gcc/c.opt index 32e600b637d..2f85c8cb35f 100644 --- a/gcc/c.opt +++ b/gcc/c.opt @@ -683,6 +683,10 @@ fuse-cxa-atexit C++ ObjC++ Use __cxa_atexit to register destructors +fvisibility-inlines-hidden +C++ +Marks all inlined methods as having hidden visibility + fvtable-gc C++ ObjC++ Discard unused virtual functions diff --git a/gcc/common.opt b/gcc/common.opt index fd3df7c7da3..24310f19322 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -913,6 +913,11 @@ fverbose-asm Common Report Var(flag_verbose_asm) Add extra commentary to assembler output +fvisibility= +Common Joined RejectNegative +-fvisibility=[default|internal|hidden|protected] Set the default symbol visibility + + fvpt Common Report Var(flag_value_profile_transformations) Use expression value profiles in optimizations diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index bbcd038f2ac..182f373a26a 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,25 @@ +2004-07-26 Niall Douglas + Brian Ryner + + PR c++/15000 + PR c++/9283 + * class.c (check_field_decls): Apply hidden visibility if + -fvisibility-inlines-hidden and inlined unless otherwise specified + (build_vtable): Set vtable visibility to class visibility. + (check_field_decls): Default static member visibility to class + visibility. + (check_methods): Default method visibility to class visibility. + * cp-tree.h: Added CLASSTYPE_VISIBILITY and + CLASSTYPE_VISIBILITY_SPECIFIED macro. + * decl.c (duplicate_decls): New logic for merging definition decls + with declaration decls. Added ignore & warning when non default + applied to global operator new or delete. + * method.c, optimize.c, rtti.c: Added setting of VISIBILITY_SPECIFIED + wherever VISIBILITY was changed + * rtti.c (get_tinfo_decl): Set typeinfo visibility to class + visibility. + (tinfo_base_init): Set typeinfo name visibility to class visibility. + 2004-07-25 Bernardo Innocenti * decl.c: Rename all identifiers named `class' to `cl'. diff --git a/gcc/cp/class.c b/gcc/cp/class.c index 6c0988c0909..f30d519fe8f 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -607,6 +607,10 @@ build_vtable (tree class_type, tree name, tree vtable_type) DECL_ALIGN (decl) = MAX (TYPE_ALIGN (double_type_node), DECL_ALIGN (decl)); + /* The vtable's visibility is the class visibility. There is no way + to override the visibility for just the vtable. */ + DECL_VISIBILITY (decl) = CLASSTYPE_VISIBILITY (class_type); + DECL_VISIBILITY_SPECIFIED (decl) = CLASSTYPE_VISIBILITY_SPECIFIED (class_type); import_export_vtable (decl, class_type, 0); return decl; @@ -2914,7 +2918,25 @@ check_field_decls (tree t, tree *access_decls, continue; if (TREE_CODE (x) == CONST_DECL || TREE_CODE (x) == VAR_DECL) - continue; + { + /* Apply the class's visibility attribute to static members + which do not have a visibility attribute. */ + if (! lookup_attribute ("visibility", DECL_ATTRIBUTES (x))) + { + if (visibility_options.inlines_hidden && DECL_INLINE (x)) + { + DECL_VISIBILITY (x) = VISIBILITY_HIDDEN; + DECL_VISIBILITY_SPECIFIED (x) = 1; + } + else + { + DECL_VISIBILITY (x) = CLASSTYPE_VISIBILITY (current_class_type); + DECL_VISIBILITY_SPECIFIED (x) = CLASSTYPE_VISIBILITY_SPECIFIED (current_class_type); + } + } + + continue; + } /* Now it can only be a FIELD_DECL. */ @@ -3669,6 +3691,22 @@ check_methods (tree t) check_for_override (x, t); if (DECL_PURE_VIRTUAL_P (x) && ! DECL_VINDEX (x)) cp_error_at ("initializer specified for non-virtual method `%D'", x); + + /* Apply the class's visibility attribute to methods which do + not have a visibility attribute. */ + if (! lookup_attribute ("visibility", DECL_ATTRIBUTES (x))) + { + if (visibility_options.inlines_hidden && DECL_INLINE (x)) + { + DECL_VISIBILITY (x) = VISIBILITY_HIDDEN; + DECL_VISIBILITY_SPECIFIED (x) = 1; + } + else + { + DECL_VISIBILITY (x) = CLASSTYPE_VISIBILITY (current_class_type); + DECL_VISIBILITY_SPECIFIED (x) = CLASSTYPE_VISIBILITY_SPECIFIED (current_class_type); + } + } /* The name of the field is the original field name Save this in auxiliary field for later overloading. */ @@ -7763,3 +7801,4 @@ cp_fold_obj_type_ref (tree ref, tree known_type) return build_address (fndecl); } + diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index bcbf793e27a..e7eed5e4437 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -931,7 +931,12 @@ enum languages { lang_c, lang_cplusplus, lang_java }; #define PUBLICLY_UNIQUELY_DERIVED_P(PARENT, TYPE) \ (lookup_base ((TYPE), (PARENT), ba_not_special | ba_quiet, NULL) \ != NULL_TREE) - + +/* Gives the visibility specification for a class type. */ +#define CLASSTYPE_VISIBILITY(TYPE) DECL_VISIBILITY (TYPE_NAME (TYPE)) +#define CLASSTYPE_VISIBILITY_SPECIFIED(TYPE) DECL_VISIBILITY_SPECIFIED (TYPE_NAME (TYPE)) + + /* This is a few header flags for 'struct lang_type'. Actually, all but the first are used only for lang_type_class; they are put in this structure to save space. */ diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 17b391bd49b..2a9e61fdd39 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -1866,17 +1866,34 @@ duplicate_decls (tree newdecl, tree olddecl) DECL_COMMON (newdecl) = DECL_COMMON (olddecl); COPY_DECL_ASSEMBLER_NAME (olddecl, newdecl); - /* If either declaration has a nondefault visibility, use it. */ - if (DECL_VISIBILITY (olddecl) != VISIBILITY_DEFAULT) + /* Warn about conflicting visibility specifications. */ + if (DECL_VISIBILITY_SPECIFIED (olddecl) && DECL_VISIBILITY_SPECIFIED (newdecl) + && DECL_VISIBILITY (newdecl) != DECL_VISIBILITY (olddecl)) + { + warning ("%J'%D': visibility attribute ignored because it", + newdecl, newdecl); + warning ("%Jconflicts with previous declaration here", olddecl); + } + /* Choose the declaration which specified visibility. */ + if (DECL_VISIBILITY_SPECIFIED (olddecl)) { - if (DECL_VISIBILITY (newdecl) != VISIBILITY_DEFAULT - && DECL_VISIBILITY (newdecl) != DECL_VISIBILITY (olddecl)) - { - warning ("%J'%D': visibility attribute ignored because it", - newdecl, newdecl); - warning ("%Jconflicts with previous declaration here", olddecl); - } DECL_VISIBILITY (newdecl) = DECL_VISIBILITY (olddecl); + DECL_VISIBILITY_SPECIFIED (newdecl) = 1; + } + /* If it's a definition of a global operator new or operator + delete, it must be default visibility. */ + if (NEW_DELETE_OPNAME_P (DECL_NAME (newdecl)) && DECL_INITIAL (newdecl) != NULL_TREE) + { + if (!DECL_FUNCTION_MEMBER_P (newdecl) && VISIBILITY_DEFAULT != DECL_VISIBILITY (newdecl)) + { + warning ("%J`%D': ignoring non-default symbol", + newdecl, newdecl); + warning ("%Jvisibility on global operator new or delete", newdecl); + DECL_VISIBILITY (olddecl) = VISIBILITY_DEFAULT; + DECL_VISIBILITY_SPECIFIED (olddecl) = 1; + DECL_VISIBILITY (newdecl) = VISIBILITY_DEFAULT; + DECL_VISIBILITY_SPECIFIED (newdecl) = 1; + } } if (TREE_CODE (newdecl) == FUNCTION_DECL) diff --git a/gcc/cp/method.c b/gcc/cp/method.c index d2d52bbf0b9..07fbacf266a 100644 --- a/gcc/cp/method.c +++ b/gcc/cp/method.c @@ -368,6 +368,7 @@ use_thunk (tree thunk_fndecl, bool emit_p) rewrite. */ TREE_PUBLIC (thunk_fndecl) = TREE_PUBLIC (function); DECL_VISIBILITY (thunk_fndecl) = DECL_VISIBILITY (function); + DECL_VISIBILITY_SPECIFIED (thunk_fndecl) = DECL_VISIBILITY_SPECIFIED (function); if (flag_weak && TREE_PUBLIC (thunk_fndecl)) comdat_linkage (thunk_fndecl); diff --git a/gcc/cp/optimize.c b/gcc/cp/optimize.c index 8a3ebe6ebbe..000d6cc4c27 100644 --- a/gcc/cp/optimize.c +++ b/gcc/cp/optimize.c @@ -112,6 +112,7 @@ maybe_clone_body (tree fn) DECL_NOT_REALLY_EXTERN (clone) = DECL_NOT_REALLY_EXTERN (fn); TREE_PUBLIC (clone) = TREE_PUBLIC (fn); DECL_VISIBILITY (clone) = DECL_VISIBILITY (fn); + DECL_VISIBILITY_SPECIFIED (clone) = DECL_VISIBILITY_SPECIFIED (fn); /* Adjust the parameter names and locations. */ parm = DECL_ARGUMENTS (fn); diff --git a/gcc/cp/rtti.c b/gcc/cp/rtti.c index b1ae3dda80f..7332a9a6914 100644 --- a/gcc/cp/rtti.c +++ b/gcc/cp/rtti.c @@ -360,7 +360,11 @@ get_tinfo_decl (tree type) pushdecl_top_level_and_finish (d, NULL_TREE); if (CLASS_TYPE_P (type)) - CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d; + { + CLASSTYPE_TYPEINFO_VAR (TYPE_MAIN_VARIANT (type)) = d; + DECL_VISIBILITY (d) = CLASSTYPE_VISIBILITY (type); + DECL_VISIBILITY_SPECIFIED (d) = CLASSTYPE_VISIBILITY_SPECIFIED (type); + } /* Remember the type it is for. */ TREE_TYPE (name) = type; @@ -758,6 +762,11 @@ tinfo_base_init (tree desc, tree target) TREE_STATIC (name_decl) = 1; DECL_EXTERNAL (name_decl) = 0; TREE_PUBLIC (name_decl) = 1; + if (CLASS_TYPE_P (target)) + { + DECL_VISIBILITY (name_decl) = CLASSTYPE_VISIBILITY (target); + DECL_VISIBILITY_SPECIFIED (name_decl) = CLASSTYPE_VISIBILITY_SPECIFIED (target); + } import_export_tinfo (name_decl, target, typeinfo_in_lib_p (target)); /* External name of the string containing the type's name has a special name. */ diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index efd20a97eed..2dc69698307 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -182,7 +182,8 @@ in the following sections. -fno-optional-diags -fpermissive @gol -frepo -fno-rtti -fstats -ftemplate-depth-@var{n} @gol -fuse-cxa-atexit -fno-weak -nostdinc++ @gol --fno-default-inline -Wabi -Wctor-dtor-privacy @gol +-fno-default-inline -fvisibility-inlines-hidden @gol +-Wabi -Wctor-dtor-privacy @gol -Wnon-virtual-dtor -Wreorder @gol -Weffc++ -Wno-deprecated @gol -Wno-non-template-friend -Wold-style-cast @gol @@ -696,7 +697,8 @@ See S/390 and zSeries Options. -fargument-alias -fargument-noalias @gol -fargument-noalias-global -fleading-underscore @gol -ftls-model=@var{model} @gol --ftrapv -fwrapv -fbounds-check} +-ftrapv -fwrapv -fbounds-check @gol +-fvisibility} @end table @menu @@ -1457,6 +1459,20 @@ This option is required for fully standards-compliant handling of static destructors, but will only work if your C library supports @code{__cxa_atexit}. +@item -fvisibility-inlines-hidden +@opindex fvisibility-inlines-hidden +Causes all inlined methods to be marked with +@code{__attribute__ ((visibility ("hidden")))} so that they do not +appear in the export table of a DSO and do not require a PLT indirection +when used within the DSO. Enabling this option can have a dramatic effect +on load and link times of a DSO as it massively reduces the size of the +dynamic export table when the library makes heavy use of templates. While +it can cause bloating through duplication of code within each DSO where +it is used, often the wastage is less than the considerable space occupied +by a long symbol name in the export table which is typical when using +templates and namespaces. For even more savings, combine with the +@code{-fvisibility=hidden} switch. + @item -fno-weak @opindex fno-weak Do not use weak symbol support, even if it is provided by the linker. @@ -11630,6 +11646,54 @@ The @var{model} argument should be one of @code{global-dynamic}, The default without @option{-fpic} is @code{initial-exec}; with @option{-fpic} the default is @code{global-dynamic}. + +@item -fvisibility=@var{default|internal|hidden|protected} +@opindex fvisibility +Set the default ELF image symbol visibility to the specified option - all +symbols will be marked with this unless overrided within the code. +Using this feature can very substantially improve linking and +load times of shared object libraries, produce more optimised +code, provide near-perfect API export and prevent symbol clashes. +It is @strong{strongly} recommended that you use this in any shared objects +you distribute. + +Despite the nomenclature, @code{default} always means public ie; +available to be linked against from outside the shared object. +@code{protected} and @code{internal} are pretty useless in real-world +usage so the only other commonly used option will be @code{hidden}. +The default if -fvisibility isn't specified is @code{default} ie; make every +symbol public - this causes the same behaviour as previous versions of +GCC. + +A good explanation of the benefits offered by ensuring ELF +symbols have the correct visibility is given by ``How To Write +Shared Libraries'' by Ulrich Drepper (which can be found at +@w{@uref{http://people.redhat.com/~drepper/}}) - however a superior +solution made possible by this option to marking things hidden when +the default is public is to make the default hidden and mark things +public. This is the norm with DLL's on Windows and with @option{-fvisibility=hidden} +and @code{__attribute__ ((visibility("default")))} instead of +@code{__declspec(dllexport)} you get almost identical semantics with +identical syntax. This is a great boon to those working with +cross-platform projects. + +For those adding visibility support to existing code, you may find +@samp{#pragma GCC visibility} of use. This works by you enclosing +the declarations you wish to set visibility for with (for example) +@samp{#pragma GCC visibility push(hidden)} and +@samp{#pragma GCC visibility pop}. These can be nested up to sixteen +times. Bear in mind that symbol visibility should be viewed @strong{as +part of the API interface contract} and thus all new code should +always specify visibility when it is not the default ie; declarations +only for use within the local DSO should @strong{always} be marked explicitly +as hidden as so to avoid PLT indirection overheads - making this +abundantly clear also aids readability and self-documentation of the code. +Note that due to ISO C++ specification requirements, operator new and +operator delete must always be of default visibility. + +An overview of these techniques, their benefits and how to use them +is at @w{@uref{http://www.nedprod.com/programs/gccvisibility.html}}. + @end table @c man end diff --git a/gcc/flags.h b/gcc/flags.h index 1b4a10a7346..542a53fea38 100644 --- a/gcc/flags.h +++ b/gcc/flags.h @@ -59,6 +59,30 @@ extern enum debug_info_level debug_info_level; debugging information. */ extern bool use_gnu_debug_info_extensions; +/* Enumerate visibility settings. */ +#ifndef SYMBOL_VISIBILITY_DEFINED +#define SYMBOL_VISIBILITY_DEFINED +enum symbol_visibility +{ + VISIBILITY_DEFAULT, + VISIBILITY_INTERNAL, + VISIBILITY_HIDDEN, + VISIBILITY_PROTECTED +}; +#endif + +/* The default visibility for all symbols (unless overridden). */ +extern enum symbol_visibility default_visibility; + +struct visibility_flags +{ + unsigned inpragma : 1; /* True when in #pragma GCC visibility. */ + unsigned inlines_hidden : 1; /* True when -finlineshidden in effect. */ +}; + +/* Global visibility options. */ +extern struct visibility_flags visibility_options; + /* Nonzero means do optimizations. -opt. */ extern int optimize; diff --git a/gcc/opts.c b/gcc/opts.c index cb9b5f93603..01297b0ee53 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -76,6 +76,12 @@ enum debug_info_level debug_info_level = DINFO_LEVEL_NONE; write_symbols is set to DBX_DEBUG, XCOFF_DEBUG, or DWARF_DEBUG. */ bool use_gnu_debug_info_extensions; +/* The default visibility for all symbols (unless overridden) */ +enum symbol_visibility default_visibility = VISIBILITY_DEFAULT; + +/* Global visibility options. */ +struct visibility_flags visibility_options; + /* Columns of --help display. */ static unsigned int columns = 80; @@ -826,6 +832,21 @@ common_handle_option (size_t scode, const char *arg, int value) flag_profile_values_set = true; break; + case OPT_fvisibility_: + { + if (!strcmp(arg, "default")) + default_visibility = VISIBILITY_DEFAULT; + else if (!strcmp(arg, "internal")) + default_visibility = VISIBILITY_INTERNAL; + else if (!strcmp(arg, "hidden")) + default_visibility = VISIBILITY_HIDDEN; + else if (!strcmp(arg, "protected")) + default_visibility = VISIBILITY_PROTECTED; + else + error ("unrecognised visibility value \"%s\"", arg); + } + break; + case OPT_fvpt: flag_value_profile_transformations_set = value; break; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 2d51e4aed09..4be1b7e9d1d 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,26 @@ +2004-07-26 Niall Douglas + Brian Ryner + + PR c++/9283 + PR c++/15000 + * gcc.dg/visibility-9.c, gcc.dg/visibility-a.c: New tests. + * g++.dg/ext/visibility/: New directory. + * g++.dg/ext/visibility-1.C, g++.dg/ext/visibility-2.C + g++.dg/ext/visibility-3.C, g++.dg/ext/visibility-4.C, + g++.dg/ext/visibility-5.C, g++.dg/ext/visibility-6.C, + g++.dg/ext/visibility-7.C: Move to g++.dg/ext/visibility/. + * g++.dg/ext/visibility/fvisibility.C, + g++.dg/ext/visibility/fvisibility-inlines-hidden.C, + g++.dg/ext/visibility/fvisibility-override1.C + g++.dg/ext/visibility/fvisibility-override2.C + g++.dg/ext/visibility/memfuncts.C + g++.dg/ext/visibility/noPLT.C + g++.dg/ext/visibility/pragma.C + g++.dg/ext/visibility/pragma-override1.C + g++.dg/ext/visibility/pragma-override2.C + g++.dg/ext/visibility/staticmemfuncts.C + g++.dg/ext/visibility/virtual.C: New tests. + 2004-07-25 Joseph S. Myers PR c/15360 diff --git a/gcc/testsuite/g++.dg/ext/visibility-1.C b/gcc/testsuite/g++.dg/ext/visibility-1.C deleted file mode 100644 index d579eb27206..00000000000 --- a/gcc/testsuite/g++.dg/ext/visibility-1.C +++ /dev/null @@ -1,8 +0,0 @@ -/* Test visibility attribute on function definition. */ -/* { dg-do compile { target *86-*-linux* } } */ -/* { dg-final { scan-assembler "\\.hidden.*_Z3foov" } } */ - -void -__attribute__((visibility ("hidden"))) -foo() -{ } diff --git a/gcc/testsuite/g++.dg/ext/visibility-2.C b/gcc/testsuite/g++.dg/ext/visibility-2.C deleted file mode 100644 index 89e853c4dc5..00000000000 --- a/gcc/testsuite/g++.dg/ext/visibility-2.C +++ /dev/null @@ -1,7 +0,0 @@ -/* Test that visibility attribute on declaration extends to definition. */ -/* { dg-do compile { target *86-*-linux* } } */ -/* { dg-final { scan-assembler "\\.hidden.*_Z3foov" } } */ - -void __attribute__((visibility ("hidden"))) foo(); - -void foo() { } diff --git a/gcc/testsuite/g++.dg/ext/visibility-3.C b/gcc/testsuite/g++.dg/ext/visibility-3.C deleted file mode 100644 index d0cc8912efb..00000000000 --- a/gcc/testsuite/g++.dg/ext/visibility-3.C +++ /dev/null @@ -1,7 +0,0 @@ -/* Test visibility attribute on forward declaration of global variable */ -/* { dg-do compile { target *86-*-linux* } } */ -/* { dg-final { scan-assembler "\\.hidden.*xyzzy" } } */ - -int -__attribute__((visibility ("hidden"))) -xyzzy = 5; diff --git a/gcc/testsuite/g++.dg/ext/visibility-4.C b/gcc/testsuite/g++.dg/ext/visibility-4.C deleted file mode 100644 index d217bc9ec5d..00000000000 --- a/gcc/testsuite/g++.dg/ext/visibility-4.C +++ /dev/null @@ -1,8 +0,0 @@ -/* Test visibility attribute on forward declaration of global variable */ -/* { dg-do compile { target *86-*-linux* } } */ -/* { dg-final { scan-assembler "\\.hidden.*xyzzy" } } */ - -extern int __attribute__ ((visibility ("hidden"))) -xyzzy; - -int xyzzy = 5; diff --git a/gcc/testsuite/g++.dg/ext/visibility-5.C b/gcc/testsuite/g++.dg/ext/visibility-5.C deleted file mode 100644 index 9cdc8021e47..00000000000 --- a/gcc/testsuite/g++.dg/ext/visibility-5.C +++ /dev/null @@ -1,11 +0,0 @@ -/* Test visibility attribute on definition of a function that has - already had a forward declaration. */ -/* { dg-do compile { target *86-*-linux* } } */ -/* { dg-final { scan-assembler "\\.hidden.*_Z3foov" } } */ - -void foo(); - -void - __attribute__((visibility ("hidden"))) -foo() -{ } diff --git a/gcc/testsuite/g++.dg/ext/visibility-6.C b/gcc/testsuite/g++.dg/ext/visibility-6.C deleted file mode 100644 index 6e8f0ce1135..00000000000 --- a/gcc/testsuite/g++.dg/ext/visibility-6.C +++ /dev/null @@ -1,10 +0,0 @@ -/* Test visibility attribute on definition of global variable that has - already had a forward declaration. */ -/* { dg-do compile { target *86-*-linux* } } */ -/* { dg-final { scan-assembler "\\.hidden.*xyzzy" } } */ - -extern int xyzzy; - -int -__attribute__((visibility ("hidden"))) -xyzzy = 5; diff --git a/gcc/testsuite/g++.dg/ext/visibility-7.C b/gcc/testsuite/g++.dg/ext/visibility-7.C deleted file mode 100644 index 40acb72463f..00000000000 --- a/gcc/testsuite/g++.dg/ext/visibility-7.C +++ /dev/null @@ -1,11 +0,0 @@ -/* Test warning from conflicting visibility specifications. */ -/* { dg-do compile { target *86-*-linux* } } */ -/* { dg-final { scan-assembler "\\.hidden.*xyzzy" } } */ - -extern int -__attribute__((visibility ("hidden"))) -xyzzy; /* { dg-warning "previous declaration here" "" } */ - -int -__attribute__((visibility ("protected"))) -xyzzy = 5; /* { dg-warning "visibility attribute ignored" "" } */ diff --git a/gcc/testsuite/g++.dg/ext/visibility/fvisibility-inlines-hidden.C b/gcc/testsuite/g++.dg/ext/visibility/fvisibility-inlines-hidden.C new file mode 100644 index 00000000000..4b610229b46 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/visibility/fvisibility-inlines-hidden.C @@ -0,0 +1,18 @@ +/* Test that -fvisibility-inlines-hidden affects class members. */ +/* { dg-do compile } */ +/* { dg-require-visibility "" } */ +/* { dg-options "-fvisibility-inlines-hidden" } */ +/* { dg-final { scan-assembler "\\.hidden.*Foo.methodEv" } } */ + +class Foo +{ +public: + void method() { } +}; + +int main(void) +{ + Foo f; + f.method(); + return 0; +} diff --git a/gcc/testsuite/g++.dg/ext/visibility/fvisibility-override1.C b/gcc/testsuite/g++.dg/ext/visibility/fvisibility-override1.C new file mode 100644 index 00000000000..67d5ef09f60 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/visibility/fvisibility-override1.C @@ -0,0 +1,12 @@ +/* Test that -fvisibility does not override class member specific settings. */ +/* { dg-do compile } */ +/* { dg-require-visibility "" } */ +/* { dg-options "-fvisibility=hidden" } */ +/* { dg-final { scan-assembler "\\.internal.*Foo.methodEv" } } */ + +class __attribute__ ((visibility ("internal"))) Foo +{ + void method(); +}; + +void Foo::method() { } diff --git a/gcc/testsuite/g++.dg/ext/visibility/fvisibility-override2.C b/gcc/testsuite/g++.dg/ext/visibility/fvisibility-override2.C new file mode 100644 index 00000000000..a0a2df5e81b --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/visibility/fvisibility-override2.C @@ -0,0 +1,12 @@ +/* Test that -fvisibility does not override class member specific settings. */ +/* { dg-do compile } */ +/* { dg-require-visibility "" } */ +/* { dg-options "-fvisibility=hidden" } */ +/* { dg-final { scan-assembler "\\.internal.*Foo.methodEv" } } */ + +class Foo +{ + __attribute__ ((visibility ("internal"))) void method(); +}; + +void Foo::method() { } diff --git a/gcc/testsuite/g++.dg/ext/visibility/fvisibility.C b/gcc/testsuite/g++.dg/ext/visibility/fvisibility.C new file mode 100644 index 00000000000..fd2c7e2fc3e --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/visibility/fvisibility.C @@ -0,0 +1,12 @@ +/* Test that -fvisibility affects class members. */ +/* { dg-do compile } */ +/* { dg-require-visibility "" } */ +/* { dg-options "-fvisibility=hidden" } */ +/* { dg-final { scan-assembler "\\.hidden.*Foo.methodEv" } } */ + +class Foo +{ + void method(); +}; + +void Foo::method() { } diff --git a/gcc/testsuite/g++.dg/ext/visibility/memfuncts.C b/gcc/testsuite/g++.dg/ext/visibility/memfuncts.C new file mode 100644 index 00000000000..19a5c9d506b --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/visibility/memfuncts.C @@ -0,0 +1,11 @@ +/* Test that setting visibility for class member functions works. */ +/* { dg-do compile } */ +/* { dg-require-visibility "" } */ +/* { dg-final { scan-assembler "\\.hidden.*Foo.methodEv" } } */ + +class __attribute__ ((visibility ("hidden"))) Foo +{ + void method(); +}; + +void Foo::method() { } diff --git a/gcc/testsuite/g++.dg/ext/visibility/noPLT.C b/gcc/testsuite/g++.dg/ext/visibility/noPLT.C new file mode 100644 index 00000000000..0ad981f7e61 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/visibility/noPLT.C @@ -0,0 +1,20 @@ +/* Test that -fvisibility=hidden prevents PLT. */ +/* { dg-do compile } */ +/* { dg-require-visibility "" } */ +/* { dg-options "-fPIC -fvisibility=hidden" } */ +/* { dg-final { scan-assembler-not "methodEv@PLT" } } */ + +class Foo +{ +public: + void method(); +}; + +void Foo::method() { } + +int main(void) +{ + Foo f; + f.method(); + return 0; +} diff --git a/gcc/testsuite/g++.dg/ext/visibility/pragma-override1.C b/gcc/testsuite/g++.dg/ext/visibility/pragma-override1.C new file mode 100644 index 00000000000..a2c93ebb752 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/visibility/pragma-override1.C @@ -0,0 +1,13 @@ +/* Test that #pragma GCC visibility does not override class member specific settings. */ +/* { dg-do compile } */ +/* { dg-require-visibility "" } */ +/* { dg-final { scan-assembler "\\.internal.*Foo.methodEv" } } */ + +#pragma GCC visibility push(hidden) +class __attribute__ ((visibility ("internal"))) Foo +{ + void method(); +}; +#pragma GCC visibility pop + +void Foo::method() { } diff --git a/gcc/testsuite/g++.dg/ext/visibility/pragma-override2.C b/gcc/testsuite/g++.dg/ext/visibility/pragma-override2.C new file mode 100644 index 00000000000..a4bb42c728a --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/visibility/pragma-override2.C @@ -0,0 +1,13 @@ +/* Test that #pragma GCC visibility does not override class member specific settings. */ +/* { dg-do compile } */ +/* { dg-require-visibility "" } */ +/* { dg-final { scan-assembler "\\.internal.*Foo.methodEv" } } */ + +#pragma GCC visibility push(hidden) +class Foo +{ + __attribute__ ((visibility ("internal"))) void method(); +}; +#pragma GCC visibility pop + +void Foo::method() { } diff --git a/gcc/testsuite/g++.dg/ext/visibility/pragma.C b/gcc/testsuite/g++.dg/ext/visibility/pragma.C new file mode 100644 index 00000000000..860b2284e15 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/visibility/pragma.C @@ -0,0 +1,13 @@ +/* Test that #pragma GCC visibility affects class members. */ +/* { dg-do compile } */ +/* { dg-require-visibility "" } */ +/* { dg-final { scan-assembler "\\.hidden.*Foo.methodEv" } } */ + +#pragma GCC visibility push(hidden) +class Foo +{ + void method(); +}; +#pragma GCC visibility pop + +void Foo::method() { } diff --git a/gcc/testsuite/g++.dg/ext/visibility/staticmemfuncts.C b/gcc/testsuite/g++.dg/ext/visibility/staticmemfuncts.C new file mode 100644 index 00000000000..b49cbd5bab9 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/visibility/staticmemfuncts.C @@ -0,0 +1,11 @@ +/* Test that setting visibility for static class member functions works. */ +/* { dg-do compile } */ +/* { dg-require-visibility "" } */ +/* { dg-final { scan-assembler "\\.hidden.*Foo.methodEv" } } */ + +class __attribute__ ((visibility ("hidden"))) Foo +{ + static void method(); +}; + +void Foo::method() { } diff --git a/gcc/testsuite/g++.dg/ext/visibility/virtual.C b/gcc/testsuite/g++.dg/ext/visibility/virtual.C new file mode 100644 index 00000000000..604c552bcb6 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/visibility/virtual.C @@ -0,0 +1,11 @@ +/* Test that setting visibility for class affects virtual table. */ +/* { dg-do compile } */ +/* { dg-require-visibility "" } */ +/* { dg-final { scan-assembler "\\.hidden.*ZTV3Foo" } } */ + +class __attribute__ ((visibility ("hidden"))) Foo +{ + virtual void method(); +}; + +void Foo::method() { } diff --git a/gcc/testsuite/g++.dg/ext/visibility/visibility-1.C b/gcc/testsuite/g++.dg/ext/visibility/visibility-1.C new file mode 100644 index 00000000000..d579eb27206 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/visibility/visibility-1.C @@ -0,0 +1,8 @@ +/* Test visibility attribute on function definition. */ +/* { dg-do compile { target *86-*-linux* } } */ +/* { dg-final { scan-assembler "\\.hidden.*_Z3foov" } } */ + +void +__attribute__((visibility ("hidden"))) +foo() +{ } diff --git a/gcc/testsuite/g++.dg/ext/visibility/visibility-2.C b/gcc/testsuite/g++.dg/ext/visibility/visibility-2.C new file mode 100644 index 00000000000..89e853c4dc5 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/visibility/visibility-2.C @@ -0,0 +1,7 @@ +/* Test that visibility attribute on declaration extends to definition. */ +/* { dg-do compile { target *86-*-linux* } } */ +/* { dg-final { scan-assembler "\\.hidden.*_Z3foov" } } */ + +void __attribute__((visibility ("hidden"))) foo(); + +void foo() { } diff --git a/gcc/testsuite/g++.dg/ext/visibility/visibility-3.C b/gcc/testsuite/g++.dg/ext/visibility/visibility-3.C new file mode 100644 index 00000000000..d0cc8912efb --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/visibility/visibility-3.C @@ -0,0 +1,7 @@ +/* Test visibility attribute on forward declaration of global variable */ +/* { dg-do compile { target *86-*-linux* } } */ +/* { dg-final { scan-assembler "\\.hidden.*xyzzy" } } */ + +int +__attribute__((visibility ("hidden"))) +xyzzy = 5; diff --git a/gcc/testsuite/g++.dg/ext/visibility/visibility-4.C b/gcc/testsuite/g++.dg/ext/visibility/visibility-4.C new file mode 100644 index 00000000000..d217bc9ec5d --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/visibility/visibility-4.C @@ -0,0 +1,8 @@ +/* Test visibility attribute on forward declaration of global variable */ +/* { dg-do compile { target *86-*-linux* } } */ +/* { dg-final { scan-assembler "\\.hidden.*xyzzy" } } */ + +extern int __attribute__ ((visibility ("hidden"))) +xyzzy; + +int xyzzy = 5; diff --git a/gcc/testsuite/g++.dg/ext/visibility/visibility-5.C b/gcc/testsuite/g++.dg/ext/visibility/visibility-5.C new file mode 100644 index 00000000000..9cdc8021e47 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/visibility/visibility-5.C @@ -0,0 +1,11 @@ +/* Test visibility attribute on definition of a function that has + already had a forward declaration. */ +/* { dg-do compile { target *86-*-linux* } } */ +/* { dg-final { scan-assembler "\\.hidden.*_Z3foov" } } */ + +void foo(); + +void + __attribute__((visibility ("hidden"))) +foo() +{ } diff --git a/gcc/testsuite/g++.dg/ext/visibility/visibility-6.C b/gcc/testsuite/g++.dg/ext/visibility/visibility-6.C new file mode 100644 index 00000000000..6e8f0ce1135 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/visibility/visibility-6.C @@ -0,0 +1,10 @@ +/* Test visibility attribute on definition of global variable that has + already had a forward declaration. */ +/* { dg-do compile { target *86-*-linux* } } */ +/* { dg-final { scan-assembler "\\.hidden.*xyzzy" } } */ + +extern int xyzzy; + +int +__attribute__((visibility ("hidden"))) +xyzzy = 5; diff --git a/gcc/testsuite/g++.dg/ext/visibility/visibility-7.C b/gcc/testsuite/g++.dg/ext/visibility/visibility-7.C new file mode 100644 index 00000000000..40acb72463f --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/visibility/visibility-7.C @@ -0,0 +1,11 @@ +/* Test warning from conflicting visibility specifications. */ +/* { dg-do compile { target *86-*-linux* } } */ +/* { dg-final { scan-assembler "\\.hidden.*xyzzy" } } */ + +extern int +__attribute__((visibility ("hidden"))) +xyzzy; /* { dg-warning "previous declaration here" "" } */ + +int +__attribute__((visibility ("protected"))) +xyzzy = 5; /* { dg-warning "visibility attribute ignored" "" } */ diff --git a/gcc/testsuite/gcc.dg/visibility-9.c b/gcc/testsuite/gcc.dg/visibility-9.c new file mode 100644 index 00000000000..ac6493ef323 --- /dev/null +++ b/gcc/testsuite/gcc.dg/visibility-9.c @@ -0,0 +1,9 @@ +/* Test that -fvisibility works. */ +/* { dg-do compile } */ +/* { dg-require-visibility "" } */ +/* { dg-options "-fvisibility=hidden" } */ +/* { dg-final { scan-assembler "\\.hidden.*foo" } } */ + +void foo(); + +void foo() { } diff --git a/gcc/testsuite/gcc.dg/visibility-a.c b/gcc/testsuite/gcc.dg/visibility-a.c new file mode 100644 index 00000000000..17492342f15 --- /dev/null +++ b/gcc/testsuite/gcc.dg/visibility-a.c @@ -0,0 +1,10 @@ +/* Test that #pragma GCC visibility works. */ +/* { dg-do compile } */ +/* { dg-require-visibility "" } */ +/* { dg-final { scan-assembler "\\.hidden.*foo" } } */ + +#pragma GCC visibility push(hidden) +void foo(); +#pragma GCC visibility pop + +void foo() { } diff --git a/gcc/tree.c b/gcc/tree.c index bba53fd5727..46479be7d39 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -2700,6 +2700,11 @@ build_decl_stat (enum tree_code code, tree name, tree type MEM_STAT_DECL) layout_decl (t, 0); else if (code == FUNCTION_DECL) DECL_MODE (t) = FUNCTION_MODE; + + /* Set default visibility to whatever the user supplied with + visibility_specified depending on #pragma GCC visibility. */ + DECL_VISIBILITY (t) = default_visibility; + DECL_VISIBILITY_SPECIFIED (t) = visibility_options.inpragma; return t; } diff --git a/gcc/tree.h b/gcc/tree.h index f9567c33b73..692716e0cc2 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -2008,6 +2008,10 @@ struct tree_binfo GTY (()) /* Value of the decls's visibility attribute */ #define DECL_VISIBILITY(NODE) (DECL_CHECK (NODE)->decl.visibility) +/* Nonzero means that the decl had its visibility specified rather than + being inferred. */ +#define DECL_VISIBILITY_SPECIFIED(NODE) (DECL_CHECK (NODE)->decl.visibility_specified) + /* In a FUNCTION_DECL, nonzero if the function cannot be inlined. */ #define DECL_UNINLINABLE(NODE) (FUNCTION_DECL_CHECK (NODE)->decl.uninlinable) @@ -2162,7 +2166,8 @@ struct tree_binfo GTY (()) FUNCTION_DECL_CHECK (DECL)->decl.possibly_inlined /* Enumerate visibility settings. */ - +#ifndef SYMBOL_VISIBILITY_DEFINED +#define SYMBOL_VISIBILITY_DEFINED enum symbol_visibility { VISIBILITY_DEFAULT, @@ -2170,6 +2175,7 @@ enum symbol_visibility VISIBILITY_HIDDEN, VISIBILITY_PROTECTED }; +#endif struct function; union alias_var_def; @@ -2214,6 +2220,7 @@ struct tree_decl GTY(()) unsigned declared_inline_flag : 1; unsigned seen_in_bind_expr : 1; ENUM_BITFIELD(symbol_visibility) visibility : 2; + unsigned visibility_specified : 1; unsigned lang_flag_0 : 1; unsigned lang_flag_1 : 1; diff --git a/gcc/varasm.c b/gcc/varasm.c index 95ac3775136..b73c6542231 100644 --- a/gcc/varasm.c +++ b/gcc/varasm.c @@ -4956,8 +4956,8 @@ default_binds_local_p_1 (tree exp, int shlib) /* Static variables are always local. */ else if (! TREE_PUBLIC (exp)) local_p = true; - /* A variable is local if the user tells us so. */ - else if (DECL_VISIBILITY (exp) != VISIBILITY_DEFAULT) + /* A variable is local if the user explicitly tells us so. */ + else if (DECL_VISIBILITY_SPECIFIED (exp) && DECL_VISIBILITY (exp) != VISIBILITY_DEFAULT) local_p = true; /* Otherwise, variables defined outside this object may not be local. */ else if (DECL_EXTERNAL (exp)) @@ -4965,6 +4965,9 @@ default_binds_local_p_1 (tree exp, int shlib) /* Linkonce and weak data are never local. */ else if (DECL_ONE_ONLY (exp) || DECL_WEAK (exp)) local_p = false; + /* If none of the above and visibility is not default, make local. */ + else if (DECL_VISIBILITY (exp) != VISIBILITY_DEFAULT) + local_p = true; /* If PIC, then assume that any global name can be overridden by symbols resolved from other modules. */ else if (shlib)