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