From: Martin Liska Date: Mon, 24 Aug 2020 11:21:10 +0000 (+0200) Subject: IPA symver: allow multiple symvers for a definition X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=363080bb8bd2cca81dd9e2e774910a8c8226f430;p=gcc.git IPA symver: allow multiple symvers for a definition gcc/ChangeLog: * cgraphunit.c (process_symver_attribute): Allow multiple symver attributes for one symbol. * doc/extend.texi: Document the change. gcc/testsuite/ChangeLog: * lib/target-supports-dg.exp: Add dg-require-symver. * lib/target-supports.exp: Likewise. * gcc.dg/ipa/symver1.c: New test. --- diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c index 0b1009d0dea..fa3aec79a48 100644 --- a/gcc/cgraphunit.c +++ b/gcc/cgraphunit.c @@ -720,80 +720,81 @@ process_symver_attribute (symtab_node *n) { tree value = lookup_attribute ("symver", DECL_ATTRIBUTES (n->decl)); - if (!value) - return; - if (lookup_attribute ("symver", TREE_CHAIN (value))) + for (; value != NULL; value = TREE_CHAIN (value)) { - error_at (DECL_SOURCE_LOCATION (n->decl), - "multiple versions for one symbol"); - return; - } - tree symver = get_identifier_with_length - (TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (value))), - TREE_STRING_LENGTH (TREE_VALUE (TREE_VALUE (value)))); - symtab_node *def = symtab_node::get_for_asmname (symver); + /* Starting from bintuils 2.35 gas supports: + # Assign foo to bar@V1 and baz@V2. + .symver foo, bar@V1 + .symver foo, baz@V2 + */ + + tree symver = get_identifier_with_length + (TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (value))), + TREE_STRING_LENGTH (TREE_VALUE (TREE_VALUE (value)))); + symtab_node *def = symtab_node::get_for_asmname (symver); + + if (def) + { + error_at (DECL_SOURCE_LOCATION (n->decl), + "duplicate definition of a symbol version"); + inform (DECL_SOURCE_LOCATION (def->decl), + "same version was previously defined here"); + return; + } + if (!n->definition) + { + error_at (DECL_SOURCE_LOCATION (n->decl), + "symbol needs to be defined to have a version"); + return; + } + if (DECL_COMMON (n->decl)) + { + error_at (DECL_SOURCE_LOCATION (n->decl), + "common symbol cannot be versioned"); + return; + } + if (DECL_COMDAT (n->decl)) + { + error_at (DECL_SOURCE_LOCATION (n->decl), + "comdat symbol cannot be versioned"); + return; + } + if (n->weakref) + { + error_at (DECL_SOURCE_LOCATION (n->decl), + "% cannot be versioned"); + return; + } + if (!TREE_PUBLIC (n->decl)) + { + error_at (DECL_SOURCE_LOCATION (n->decl), + "versioned symbol must be public"); + return; + } + if (DECL_VISIBILITY (n->decl) != VISIBILITY_DEFAULT) + { + error_at (DECL_SOURCE_LOCATION (n->decl), + "versioned symbol must have default visibility"); + return; + } - if (def) - { - error_at (DECL_SOURCE_LOCATION (n->decl), - "duplicate definition of a symbol version"); - inform (DECL_SOURCE_LOCATION (def->decl), - "same version was previously defined here"); - return; - } - if (!n->definition) - { - error_at (DECL_SOURCE_LOCATION (n->decl), - "symbol needs to be defined to have a version"); - return; - } - if (DECL_COMMON (n->decl)) - { - error_at (DECL_SOURCE_LOCATION (n->decl), - "common symbol cannot be versioned"); - return; - } - if (DECL_COMDAT (n->decl)) - { - error_at (DECL_SOURCE_LOCATION (n->decl), - "comdat symbol cannot be versioned"); - return; - } - if (n->weakref) - { - error_at (DECL_SOURCE_LOCATION (n->decl), - "% cannot be versioned"); - return; + /* Create new symbol table entry representing the version. */ + tree new_decl = copy_node (n->decl); + + DECL_INITIAL (new_decl) = NULL_TREE; + if (TREE_CODE (new_decl) == FUNCTION_DECL) + DECL_STRUCT_FUNCTION (new_decl) = NULL; + SET_DECL_ASSEMBLER_NAME (new_decl, symver); + TREE_PUBLIC (new_decl) = 1; + DECL_ATTRIBUTES (new_decl) = NULL; + + symtab_node *symver_node = symtab_node::get_create (new_decl); + symver_node->alias = true; + symver_node->definition = true; + symver_node->symver = true; + symver_node->create_reference (n, IPA_REF_ALIAS, NULL); + symver_node->analyzed = true; } - if (!TREE_PUBLIC (n->decl)) - { - error_at (DECL_SOURCE_LOCATION (n->decl), - "versioned symbol must be public"); - return; - } - if (DECL_VISIBILITY (n->decl) != VISIBILITY_DEFAULT) - { - error_at (DECL_SOURCE_LOCATION (n->decl), - "versioned symbol must have default visibility"); - return; - } - - /* Create new symbol table entry representing the version. */ - tree new_decl = copy_node (n->decl); - - DECL_INITIAL (new_decl) = NULL_TREE; - if (TREE_CODE (new_decl) == FUNCTION_DECL) - DECL_STRUCT_FUNCTION (new_decl) = NULL; - SET_DECL_ASSEMBLER_NAME (new_decl, symver); - TREE_PUBLIC (new_decl) = 1; - DECL_ATTRIBUTES (new_decl) = NULL; - - symtab_node *symver_node = symtab_node::get_create (new_decl); - symver_node->alias = true; - symver_node->definition = true; - symver_node->symver = true; - symver_node->create_reference (n, IPA_REF_ALIAS, NULL); - symver_node->analyzed = true; } /* Process attributes common for vars and functions. */ diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 2bb9b2f72f5..3b37aba5795 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -3742,13 +3742,19 @@ foo_v1 (void) Will produce a @code{.symver foo_v1, foo@@VERS_1} directive in the assembler output. -It's an error to define multiple version of a given symbol. In such case -an alias can be used. +One can also define multiple version for a given symbol. @smallexample -__attribute__ ((__symver__ ("foo@@VERS_2"))) -__attribute__ ((alias ("foo_v1"))) -int symver_foo_v1 (void); +__attribute__ ((__symver__ ("foo@@VERS_2"), ("foo@@VERS_3"))) +int symver_foo_v1 (void) +@{ +@} + +__attribute__ ((__symver__ ("bar@@VERS_2")))) +__attribute__ ((__symver__ ("bar@@VERS_3")))) +int symver_bar_v1 (void) +@{ +@} @end smallexample This example creates an alias of @code{foo_v1} with symbol name diff --git a/gcc/testsuite/gcc.dg/ipa/symver1.c b/gcc/testsuite/gcc.dg/ipa/symver1.c new file mode 100644 index 00000000000..645de7ea259 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/symver1.c @@ -0,0 +1,11 @@ +/* { dg-do compile } */ + +__attribute__ ((__symver__ ("foo@VER_2"))) +__attribute__ ((__symver__ ("foo@VER_3"))) +int foo() +{ + return 2; +} + +/* { dg-final { scan-assembler ".symver.*foo, foo@VER_2" } } */ +/* { dg-final { scan-assembler ".symver.*foo, foo@VER_3" } } */ diff --git a/gcc/testsuite/lib/target-supports-dg.exp b/gcc/testsuite/lib/target-supports-dg.exp index 5bb99f4e8f9..4a03eaae9ce 100644 --- a/gcc/testsuite/lib/target-supports-dg.exp +++ b/gcc/testsuite/lib/target-supports-dg.exp @@ -665,3 +665,13 @@ if { [info procs saved-dg-process-target] == [list] } { return [dg-process-target-1 $selector] } } + +# If this target does not support the "symver" attribute, skip this +# test. + +proc dg-require-symver { args } { + if { ![ check_symver_available ] } { + upvar dg-do-what dg-do-what + set dg-do-what [list [lindex ${dg-do-what} 0] "N" "P"] + } +} diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp index c24330a27ab..f3fc5b80aea 100644 --- a/gcc/testsuite/lib/target-supports.exp +++ b/gcc/testsuite/lib/target-supports.exp @@ -10445,3 +10445,15 @@ proc check_effective_target_large_initializer { } { return 1 } +# Returns 1 if the target toolchain supports extended +# syntax of .symver directive, 0 otherwise. + +proc check_symver_available { } { + return [check_no_compiler_messages symver_available object { + int foo(void) { return 0; } + int main (void) { + asm volatile (".symver foo,foo@VER_1, local"); + return 0; + } + }] +}