c++: Avoid adding implicit attributes during apply_late_template_attributes [PR101180]

Message ID 20211119100624.GN2646553@tucnak
State Committed
Headers
Series c++: Avoid adding implicit attributes during apply_late_template_attributes [PR101180] |

Commit Message

Jakub Jelinek Nov. 19, 2021, 10:06 a.m. UTC
  Hi!

decl_attributes and its caller cplus_decl_attributes sometimes add
implicit attributes, e.g. optimize attribute if #pragma GCC optimize
is active, target attribute if #pragma GCC target is active, or
e.g. omp declare target attribute if in between #pragma omp declare target
and #pragma omp end declare target.

For templates that seems highly undesirable to me though, they should
get those implicit attributes from the spot the templates were parsed
(and they do get that), then tsubst through copy_node copies those
attributes, but then apply_late_template_attributes can or does add
a new set from the spot where they are instantiated, which can be pretty
random point of first use of the template.

Consider e.g.
#pragma GCC push_options
#pragma GCC target "avx"
template <int N>
inline void foo ()
{
}
#pragma GCC pop_options
#pragma GCC push_options
#pragma GCC target "crc32"
void
bar ()
{
  foo<0> ();
}
#pragma GCC pop_options
testcase where the intention is that foo has avx target attribute
and bar has crc32 target attribute, but we end up with
__attribute__((target ("crc32"), target ("avx")))
on foo<0> (and due to yet another bug actually don't enable avx
in foo<0>).  In this particular case it is a regression caused
by r12-299-ga0fdff3cf33f7284 which apparently calls
cplus_decl_attributes even if attributes != NULL but late_attrs
is NULL, before those changes we didn't call it in those cases.
But, if there is at least one unrelated dependent attribute this
would happen already in older releases.

The following patch fixes that by temporarily overriding the variables
that control the addition of the implicit attributes.

Shall we also change the function so that it doesn't call
cplus_decl_attributes if late_attrs is NULL, or was that change
intentional?

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2021-11-19  Jakub Jelinek  <jakub@redhat.com>

	PR c++/101180
	* pt.c (apply_late_template_attributes): Temporarily override
	current_optimize_pragma, optimization_current_node,
	current_target_pragma and scope_chain->omp_declare_target_attribute,
	so that cplus_decl_attributes doesn't add implicit attributes.

	* g++.target/i386/pr101180.C: New test.


	Jakub
  

Comments

Jason Merrill Nov. 19, 2021, 3:40 p.m. UTC | #1
On 11/19/21 05:06, Jakub Jelinek wrote:
> Hi!
> 
> decl_attributes and its caller cplus_decl_attributes sometimes add
> implicit attributes, e.g. optimize attribute if #pragma GCC optimize
> is active, target attribute if #pragma GCC target is active, or
> e.g. omp declare target attribute if in between #pragma omp declare target
> and #pragma omp end declare target.
> 
> For templates that seems highly undesirable to me though, they should
> get those implicit attributes from the spot the templates were parsed
> (and they do get that), then tsubst through copy_node copies those
> attributes, but then apply_late_template_attributes can or does add
> a new set from the spot where they are instantiated, which can be pretty
> random point of first use of the template.
> 
> Consider e.g.
> #pragma GCC push_options
> #pragma GCC target "avx"
> template <int N>
> inline void foo ()
> {
> }
> #pragma GCC pop_options
> #pragma GCC push_options
> #pragma GCC target "crc32"
> void
> bar ()
> {
>    foo<0> ();
> }
> #pragma GCC pop_options
> testcase where the intention is that foo has avx target attribute
> and bar has crc32 target attribute, but we end up with
> __attribute__((target ("crc32"), target ("avx")))
> on foo<0> (and due to yet another bug actually don't enable avx
> in foo<0>).  In this particular case it is a regression caused
> by r12-299-ga0fdff3cf33f7284 which apparently calls
> cplus_decl_attributes even if attributes != NULL but late_attrs
> is NULL, before those changes we didn't call it in those cases.
> But, if there is at least one unrelated dependent attribute this
> would happen already in older releases.
> 
> The following patch fixes that by temporarily overriding the variables
> that control the addition of the implicit attributes.

OK.

> Shall we also change the function so that it doesn't call
> cplus_decl_attributes if late_attrs is NULL [...]?

Please.

> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
> 
> 2021-11-19  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR c++/101180
> 	* pt.c (apply_late_template_attributes): Temporarily override
> 	current_optimize_pragma, optimization_current_node,
> 	current_target_pragma and scope_chain->omp_declare_target_attribute,
> 	so that cplus_decl_attributes doesn't add implicit attributes.
> 
> 	* g++.target/i386/pr101180.C: New test.
> 
> --- gcc/cp/pt.c.jj	2021-11-18 12:33:22.025628027 +0100
> +++ gcc/cp/pt.c	2021-11-18 19:08:52.800539490 +0100
> @@ -11722,6 +11722,17 @@ apply_late_template_attributes (tree *de
>   	q = &TREE_CHAIN (*q);
>       }
>   
> +  /* cplus_decl_attributes can add some attributes implicitly.  For templates,
> +     those attributes should have been added already when those templates were
> +     parsed, and shouldn't be added based on from which context they are
> +     first time instantiated.  */
> +  auto o1 = make_temp_override (current_optimize_pragma, NULL_TREE);
> +  auto o2 = make_temp_override (optimization_current_node,
> +				optimization_default_node);
> +  auto o3 = make_temp_override (current_target_pragma, NULL_TREE);
> +  auto o4 = make_temp_override (scope_chain->omp_declare_target_attribute,
> +				NULL);
> +
>     cplus_decl_attributes (decl_p, late_attrs, attr_flags);
>   
>     return true;
> --- gcc/testsuite/g++.target/i386/pr101180.C.jj	2021-11-18 19:11:50.512009888 +0100
> +++ gcc/testsuite/g++.target/i386/pr101180.C	2021-11-18 19:11:33.066258216 +0100
> @@ -0,0 +1,25 @@
> +// PR c++/101180
> +// { dg-do compile { target c++11 } }
> +
> +#pragma GCC target "avx"
> +template <typename> struct A {};
> +#pragma GCC push_options
> +#pragma GCC target "avx,avx2,bmi,bmi2,fma,f16c"
> +template <typename T> using B = A<T>;
> +template <typename> struct C;
> +template <> struct C<float> {
> +  __attribute__((always_inline)) float operator()(long) { return .0f; }
> +};
> +long d;
> +template <typename T> void e(B<T>) {
> +  T{C<T>()(d)};
> +}
> +template <typename T, typename FromT> void f(T d, FromT) {
> +  e(d);
> +}
> +int g;
> +void h() {
> +  A<float> i;
> +  f(i, g);
> +}
> +#pragma GCC pop_options
> 
> 	Jakub
>
  

Patch

--- gcc/cp/pt.c.jj	2021-11-18 12:33:22.025628027 +0100
+++ gcc/cp/pt.c	2021-11-18 19:08:52.800539490 +0100
@@ -11722,6 +11722,17 @@  apply_late_template_attributes (tree *de
 	q = &TREE_CHAIN (*q);
     }
 
+  /* cplus_decl_attributes can add some attributes implicitly.  For templates,
+     those attributes should have been added already when those templates were
+     parsed, and shouldn't be added based on from which context they are
+     first time instantiated.  */
+  auto o1 = make_temp_override (current_optimize_pragma, NULL_TREE);
+  auto o2 = make_temp_override (optimization_current_node,
+				optimization_default_node);
+  auto o3 = make_temp_override (current_target_pragma, NULL_TREE);
+  auto o4 = make_temp_override (scope_chain->omp_declare_target_attribute,
+				NULL);
+
   cplus_decl_attributes (decl_p, late_attrs, attr_flags);
 
   return true;
--- gcc/testsuite/g++.target/i386/pr101180.C.jj	2021-11-18 19:11:50.512009888 +0100
+++ gcc/testsuite/g++.target/i386/pr101180.C	2021-11-18 19:11:33.066258216 +0100
@@ -0,0 +1,25 @@ 
+// PR c++/101180
+// { dg-do compile { target c++11 } }
+
+#pragma GCC target "avx"
+template <typename> struct A {};
+#pragma GCC push_options
+#pragma GCC target "avx,avx2,bmi,bmi2,fma,f16c"
+template <typename T> using B = A<T>;
+template <typename> struct C;
+template <> struct C<float> {
+  __attribute__((always_inline)) float operator()(long) { return .0f; }
+};
+long d;
+template <typename T> void e(B<T>) {
+  T{C<T>()(d)};
+}
+template <typename T, typename FromT> void f(T d, FromT) {
+  e(d);
+}
+int g;
+void h() {
+  A<float> i;
+  f(i, g);
+}
+#pragma GCC pop_options