Summary: Do not merge my patch. It needs more work.

I realized a problem with my patch. With -flto,
__attribute__((section)) is still broken (i.e. my patch has no effect
for LTO builds). I narrowed the problem to localize_node
(gcc/ipa-visibility.c, historically part of
function_and_variable_visibility) deliberately clearing the section
name [1]. If I delete the section-clearing code in localize_node, the
bug is fixed with -flto. However, that code probably exists for a
reason. =]

While trying to find the meaning of the code in localize_node, I
stumbled upon some discussion on copy_node and section names [2]. It
looks like I should carefully audit callers of copy_node, and I should
consider putting the section name copy logic in a caller instead.

[1] https://gcc.gnu.org/ml/gcc-patches/2014-06/msg00692.html
[2] https://gcc.gnu.org/ml/gcc-patches/2015-02/msg00965.html


On Tue, Nov 5, 2019 at 3:38 PM Strager Neds <strager....@gmail.com> wrote:
>
> Aside: This is my first time working in GCC's code base. I probably made
> some mistakes. Please point them out. =]
>
> When GCC encounters __attribute__((section("foo"))) on a function or
> variable declaration, it adds an entry in the symbol table for the
> declaration to remember its desired section. The symbol table is
> separate from the declaration's tree node.
>
> When instantiating a template, GCC copies the tree of the template
> recursively. GCC does *not* copy symbol table entries when copying
> function and variable declarations.
>
> Combined, these two details mean that section attributes on function and
> variable declarations in a template have no effect.
>
> Fix this issue by copying the section name (in the symbol table) when
> copying a tree node for template instantiation. This addresses PR
> c++/70435 and PR c++/88061.
>
> Known unknowns (these are questions I'm thinking aloud to myself):
>
> * For all targets which support the section attribute, are functions and
>   variables deduplicated (comdat) when using a custom section? It seems
>   to work with GNU ELF on Linux (i.e. I end up with only one copy), but
>   I'm unsure about other platforms. Richard Biener raised this concern
>   in PR c++/88061
> * Are there other callers of copy_node which do not want section
>   attributes to be copied? I did not audit callers of copy_node.
> * Did this patch break anything? I had trouble running GCC's full test
>   suite, so I have not compared test results with and without this
>   patch.
>
> 2019-11-05  Matthew Glazar <strager....@gmail.com>
>
> * gcc/tree.c (copy_node): Copy section name from source SYMTAB_NODE, not
> just init priority.
>
> diff --git 
> gcc/testsuite/g++.dg/ext/section-class-template-specialized-static-variable.C
> gcc/testsuite/g++.dg/ext/section-class-template-specialized-static-variable.C
> new file mode 100644
> index 00000000000..20f51fe665d
> --- /dev/null
> +++ 
> gcc/testsuite/g++.dg/ext/section-class-template-specialized-static-variable.C
> @@ -0,0 +1,29 @@
> +// PR c++/70435
> +// attribute((section)) should affect specialized static
> +// variables in class templates.
> +
> +// { dg-options "-std=c++17" }
> +// { dg-require-named-sections "" }
> +// { dg-final { scan-assembler-not {\.data.*my_var} } }
> +// { dg-final { scan-assembler {\.charsection.*my_var} } }
> +// { dg-final { scan-assembler {\.floatsection.*my_var} } }
> +
> +template<class>
> +struct s
> +{
> +  static int my_var;
> +};
> +
> +template<>
> +int s<char>::
> +my_var __attribute__((section(".charsection"))) = 1;
> +
> +template<>
> +int s<float>::
> +my_var __attribute__((section(".floatsection"))) = 2;
> +
> +int *
> +f(bool which)
> +{
> +  return which ? &s<char>::my_var : &s<float>::my_var;
> +}
> diff --git 
> gcc/testsuite/g++.dg/ext/section-class-template-static-inline-variable.C
> gcc/testsuite/g++.dg/ext/section-class-template-static-inline-variable.C
> new file mode 100644
> index 00000000000..e047c90c601
> --- /dev/null
> +++ gcc/testsuite/g++.dg/ext/section-class-template-static-inline-variable.C
> @@ -0,0 +1,20 @@
> +// PR c++/70435
> +// attribute((section)) should affect static inline variables in class
> +// templates.
> +
> +// { dg-options "-std=c++17" }
> +// { dg-require-named-sections "" }
> +// { dg-final { scan-assembler-not {\.data.*my_var} } }
> +// { dg-final { scan-assembler {\.testsection.*my_var} } }
> +
> +template<class>
> +struct s
> +{
> +  inline static int my_var __attribute__((section(".testsection"))) = 1;
> +};
> +
> +int *
> +f(bool which)
> +{
> +  return which ? &s<char>::my_var : &s<float>::my_var;
> +}
> diff --git gcc/testsuite/g++.dg/ext/section-class-template-static-variable.C
> gcc/testsuite/g++.dg/ext/section-class-template-static-variable.C
> new file mode 100644
> index 00000000000..ccf71e7c5df
> --- /dev/null
> +++ gcc/testsuite/g++.dg/ext/section-class-template-static-variable.C
> @@ -0,0 +1,22 @@
> +// attribute((section)) should affect static variables in class templates.
> +
> +// { dg-options "-std=c++17" }
> +// { dg-require-named-sections "" }
> +// { dg-final { scan-assembler-not {\.data.*my_var} } }
> +// { dg-final { scan-assembler {\.testsection.*my_var} } }
> +
> +template<class>
> +struct s
> +{
> +  static int my_var;
> +};
> +
> +template<class T>
> +int s<T>::
> +my_var __attribute__((section(".testsection"))) = 1;
> +
> +int *
> +f(bool which)
> +{
> +  return which ? &s<char>::my_var : &s<float>::my_var;
> +}
> diff --git 
> gcc/testsuite/g++.dg/ext/section-function-template-static-variable.C
> gcc/testsuite/g++.dg/ext/section-function-template-static-variable.C
> new file mode 100644
> index 00000000000..7685f255d82
> --- /dev/null
> +++ gcc/testsuite/g++.dg/ext/section-function-template-static-variable.C
> @@ -0,0 +1,21 @@
> +// PR c++/70435
> +// attribute((section)) should affect static variables in function templates.
> +
> +// { dg-do compile }
> +// { dg-require-named-sections "" }
> +// { dg-final { scan-assembler-not {\.data.*my_var} } }
> +// { dg-final { scan-assembler {\.testsection.*my_var} } }
> +
> +template<class>
> +int *
> +callee()
> +{
> +  static int my_var __attribute__((section(".testsection"))) = 1;
> +  return &my_var;
> +}
> +
> +int *
> +f(bool which)
> +{
> +  return which ? callee<char>() : callee<float>();
> +}
> diff --git gcc/testsuite/g++.dg/ext/section-function-template.C
> gcc/testsuite/g++.dg/ext/section-function-template.C
> new file mode 100644
> index 00000000000..49d367eeafc
> --- /dev/null
> +++ gcc/testsuite/g++.dg/ext/section-function-template.C
> @@ -0,0 +1,21 @@
> +// attribute((section)) should affect function templates.
> +
> +// { dg-do compile }
> +// { dg-require-named-sections "" }
> +// { dg-final { scan-assembler-not {\.text\..*callee} } }
> +// { dg-final { scan-assembler {\.testsection.*callee} } }
> +
> +template<class>
> +__attribute__((section(".testsection"))) void
> +callee()
> +{
> +}
> +
> +void
> +f(bool which)
> +{
> +  if (which)
> +    callee<int>();
> +  else
> +    callee<float>();
> +}
> diff --git gcc/testsuite/g++.dg/ext/section-multi-tu-template-main.C
> gcc/testsuite/g++.dg/ext/section-multi-tu-template-main.C
> new file mode 100644
> index 00000000000..0b4d5ee6b78
> --- /dev/null
> +++ gcc/testsuite/g++.dg/ext/section-multi-tu-template-main.C
> @@ -0,0 +1,38 @@
> +// attribute((section)) variables in templates should be
> +// shared across translation units.
> +
> +// { dg-do run }
> +// { dg-options "-std=c++17" }
> +// { dg-require-named-sections "" }
> +// { dg-additional-sources "section-multi-tu-template-other.C" }
> +
> +#include "section-multi-tu-template.h"
> +
> +int *
> +get_main_f_var_int()
> +{
> +  return f<int>();
> +}
> +
> +int *
> +get_main_s_var_int()
> +{
> +  return &s<int>::var;
> +}
> +
> +float *
> +get_main_s_var_float()
> +{
> +  return &s<float>::var;
> +}
> +
> +int main()
> +{
> +  if (get_main_f_var_int() != get_other_f_var_int())
> +    return 1;
> +  if (get_main_s_var_int() != get_other_s_var_int())
> +    return 2;
> +  if (get_main_s_var_float() != get_other_s_var_float())
> +    return 3;
> +  return 0;
> +}
> diff --git gcc/testsuite/g++.dg/ext/section-multi-tu-template-other.C
> gcc/testsuite/g++.dg/ext/section-multi-tu-template-other.C
> new file mode 100644
> index 00000000000..b15c1f7ee43
> --- /dev/null
> +++ gcc/testsuite/g++.dg/ext/section-multi-tu-template-other.C
> @@ -0,0 +1,24 @@
> +// This file is part of the section-multi-tu-template-main.C test.
> +
> +// { dg-options "-std=c++17" }
> +// { dg-require-named-sections "" }
> +
> +#include "section-multi-tu-template.h"
> +
> +int *
> +get_other_f_var_int()
> +{
> +  return f<int>();
> +}
> +
> +int *
> +get_other_s_var_int()
> +{
> +  return &s<int>::var;
> +}
> +
> +float *
> +get_other_s_var_float()
> +{
> +  return &s<float>::var;
> +}
> diff --git gcc/testsuite/g++.dg/ext/section-multi-tu-template.h
> gcc/testsuite/g++.dg/ext/section-multi-tu-template.h
> new file mode 100644
> index 00000000000..41a0ce311ac
> --- /dev/null
> +++ gcc/testsuite/g++.dg/ext/section-multi-tu-template.h
> @@ -0,0 +1,25 @@
> +// This file is part of the section-multi-tu-template-main.C test.
> +
> +template<class T>
> +struct s
> +{
> +  static inline T var __attribute__((section(".mysection"))) = 42;
> +};
> +
> +template<class T>
> +T *
> +f()
> +{
> +  static T var __attribute__((section(".mysection"))) = 42;
> +  return &var;
> +}
> +
> +// section-multi-tu-template-main.C
> +int *get_main_f_var_int();
> +int *get_main_s_var_int();
> +float *get_main_s_var_float();
> +
> +// section-multi-tu-template-other.C
> +int *get_other_f_var_int();
> +int *get_other_s_var_int();
> +float *get_other_s_var_float();
> diff --git gcc/testsuite/g++.dg/ext/section-variable-template.C
> gcc/testsuite/g++.dg/ext/section-variable-template.C
> new file mode 100644
> index 00000000000..32a83d264c9
> --- /dev/null
> +++ gcc/testsuite/g++.dg/ext/section-variable-template.C
> @@ -0,0 +1,16 @@
> +// PR c++/88061
> +// attribute((section)) should affect variable templates.
> +
> +// { dg-options "-std=c++17" }
> +// { dg-require-named-sections "" }
> +// { dg-final { scan-assembler-not {\.data.*my_var} } }
> +// { dg-final { scan-assembler {\.testsection.*my_var} } }
> +
> +template<class>
> +inline int my_var __attribute__((section(".testsection"))) = 1;
> +
> +int *
> +f(bool which)
> +{
> +  return which ? &my_var<char> : &my_var<float>;
> +}
> diff --git gcc/tree.c gcc/tree.c
> index 3299b344ea6..244f2a958db 100644
> --- gcc/tree.c
> +++ gcc/tree.c
> @@ -1215,17 +1215,26 @@ copy_node (tree node MEM_STAT_DECL)
>        if (VAR_P (node))
>      {
>        DECL_HAS_DEBUG_EXPR_P (t) = 0;
> -      t->decl_with_vis.symtab_node = NULL;
> -    }
> -      if (VAR_P (node) && DECL_HAS_INIT_PRIORITY_P (node))
> -    {
> -      SET_DECL_INIT_PRIORITY (t, DECL_INIT_PRIORITY (node));
> -      DECL_HAS_INIT_PRIORITY_P (t) = 1;
>      }
>        if (TREE_CODE (node) == FUNCTION_DECL)
>      {
>        DECL_STRUCT_FUNCTION (t) = NULL;
> +    }
> +
> +      /* Copy attributes of SYMTAB_NODE.  */
> +      if (VAR_OR_FUNCTION_DECL_P (node))
> +    {
>        t->decl_with_vis.symtab_node = NULL;
> +      if (VAR_P (node) && DECL_HAS_INIT_PRIORITY_P (node))
> +        {
> +          SET_DECL_INIT_PRIORITY (t, DECL_INIT_PRIORITY (node));
> +          DECL_HAS_INIT_PRIORITY_P (t) = 1;
> +        }
> +      if (struct symtab_node *snode = node->decl_with_vis.symtab_node)
> +        {
> +          if (const char *section_name = snode->get_section ())
> +        set_decl_section_name(t, section_name);
> +        }
>      }
>      }
>    else if (TREE_CODE_CLASS (code) == tcc_type)

Reply via email to