From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 48) id 775F4386F430; Sun, 27 Sep 2020 23:07:54 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 775F4386F430 From: "mte.zych at gmail dot com" To: gcc-bugs@gcc.gnu.org Subject: [Bug c++/97222] New: GCC discards attributes aligned and may_alias for typedefs passed as template arguments Date: Sun, 27 Sep 2020 23:07:54 +0000 X-Bugzilla-Reason: CC X-Bugzilla-Type: new X-Bugzilla-Watch-Reason: None X-Bugzilla-Product: gcc X-Bugzilla-Component: c++ X-Bugzilla-Version: 10.2.0 X-Bugzilla-Keywords: X-Bugzilla-Severity: normal X-Bugzilla-Who: mte.zych at gmail dot com X-Bugzilla-Status: UNCONFIRMED X-Bugzilla-Resolution: X-Bugzilla-Priority: P3 X-Bugzilla-Assigned-To: unassigned at gcc dot gnu.org X-Bugzilla-Target-Milestone: --- X-Bugzilla-Flags: X-Bugzilla-Changed-Fields: bug_id short_desc product version bug_status bug_severity priority component assigned_to reporter target_milestone Message-ID: Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable X-Bugzilla-URL: http://gcc.gnu.org/bugzilla/ Auto-Submitted: auto-generated MIME-Version: 1.0 X-BeenThere: gcc-bugs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-bugs mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 27 Sep 2020 23:07:54 -0000 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=3D97222 Bug ID: 97222 Summary: GCC discards attributes aligned and may_alias for typedefs passed as template arguments Product: gcc Version: 10.2.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: mte.zych at gmail dot com Target Milestone: --- Hello! GCC discards aligned attribute, applied on a typdef, when it's passed as a template argument. Compiler Expolorer: GCC -> https://godbolt.org/z/bj8v1T C++ Source Code: #include typedef float vec __attribute__((vector_size(8))); typedef float fp __attribute__((aligned(16))); template struct identity { typedef t type; }; int main () { std::cout << sizeof(typename identity::type) << std::endl; std::cout << sizeof(vec ) << std::endl; std::cout << alignof(typename identity::type) << std::endl; std::cout << alignof(fp ) << std::endl; } Program Output: 8 8 4 16 Compilation Log: warning: ignoring attributes on template argument 'fp' {aka 'float'} [-Wignored-attributes] 13 | std::cout << alignof(typename identity::type) << std::endl; | ^ The above program shows that alignment of the fp typedef changes, after it's been passed through the identity meta-function - it's 4-bytes instead of expected 16-bytes. What's interesting is not all type attributes are discarded - the type attribute vector_size is preserved after being passed through the identity meta-function. This behavior is required, since removal of the vector_size attribute would be a semantic change of the vec type, affecting even its size, because the vec type would represent a single float, instead of a vector of= 2 floats. The same could be said about the aligned attribute - discarding it is also a semantic change, since alignment is a fundamental property of a type, affecting among others code generation, that is, two types are not equivalent if they have different alignment. This is the reason why I argue that, passing a typedef as a template argume= nt should preserve its aligned attribute, instead of discarding it. Moreover, the Intel C++ compiler implements this behavior correctly. Compiler Expolorer: ICC -> https://godbolt.org/z/9vr9se Program Output: 8 8 16 16 The issue described above doesn't apply only to the aligned type attribute, but also to the may_alias type attribute. Compiler Expolorer: GCC -> https://godbolt.org/z/6EqsnP C++ Source Code: typedef int integer __attribute__((may_alias)); template struct identity { typedef t type; }; int main () { typename identity::type i; } Compilation Log: warning: ignoring attributes on template argument 'integer' {aka 'int'} [-Wignored-attributes] 7 | typename identity::type i; | ^ Again, discarding attribute may_alias is a semantic change, because aliasing rules can affect code generation. Why this issue is important? Well, because it prevents generic programming, via C++ templates, using x86 SIMD types. If we would look at definitions of x86 SIMD types, we will notice that they are essentially typedefs with attributes vector_size and may_alias app= lied on them: - immintrin.h typedef float __m256 __attribute__((__vector_size__(32), __may_alias__)); - emmintrin.h typedef long long __m128i __attribute__((__vector_size__(16), __may_alias__)); typedef double __m128d __attribute__((__vector_size__(16), __may_alias__)); - xmmintrin.h typedef float __m128 __attribute__((__vector_size__(16), __may_alias__));=20 Note that, the may_alias attributes is required and cannot be removed: - /usr/lib/gcc/x86_64-linux-gnu/10/include/immintrin.h /* The Intel API is flexible enough that we must allow aliasing with ot= her vector types, and their scalar components. */ Compiler Expolorer: GCC -> https://godbolt.org/z/vz4fWK C++ Source Code: #include template struct identity { typedef t type; }; int main () { typename identity<__m128>::type fvec4; } Compilation Log: warning: ignoring attributes on template argument '__m128' [-Wignored-attributes] 8 | typename identity<__m128>::type fvec4; | ^ What's the root cause of this problem? Well, the problem is in C++ a typedef is just an alias (a new name) for the= old type, that is, it does *not* introduce a new type. Implementing support for attributes vector_size, aligned and may_alias in C++ typedefs requires an opaque/strong typedef, introducing a brand new type and storing information about applied attribut= es. typedef float fp __attribute__((aligned(16))); Think about it - a typedef introducing the fp type has to create a new type, because even though both fp and float types represent floating point numbers identically, the fp type is *not* the float type, because these types have different alignment requirements. Note that, the Intel C++ Compiler does not introduce new types for typedefs, which have attributes aligned or may_alias applied on them. Compiler Explorer: ICC -> https://godbolt.org/z/MjdMqx C++ Source Code: #include typedef int vectorized_int __attribute__((vector_size(8))); typedef int aligned_int __attribute__((aligned(16))); typedef int aliasing_int __attribute__((may_alias)); int main () { std::cout << typeid( int).name() << std::endl; std::cout << typeid(vectorized_int).name() << std::endl; std::cout << typeid( aligned_int).name() << std::endl; std::cout << typeid( aliasing_int).name() << std::endl; } Program Output: i Dv2_i i i However, this behavior leads to a contradiction, in which there can exists a single type, which has 2 different alignment requirements. Compiler Explorer: ICC -> https://godbolt.org/z/4o9o3M C++ Source Code: template struct is_same { static const auto value =3D false; }; template struct is_same { static const auto value = =3D=20 true; }; typedef float fp __attribute__((aligned(16))); template struct check_same { static_assert(is_same::value , ""); static_assert( sizeof(first_type) =3D=3D sizeof(second_type), ""); static_assert(alignof(first_type) =3D=3D alignof(second_type), ""); }; int main () { check_same { }; check_same< fp, float> { }; } Compilation Log: error: static assertion failed static_assert(alignof(first_type) =3D=3D alignof(second_type), ""); ^ detected during instantiation of class "check_same [with first_type=3Dfp=3D{float}, second_type=3Dfloat]" To avoid these kind of issues, GCC could replicate the behavior of the vector_size attribute, that is, introduce a brand new type and store information about applied attributes. Thank you, Mateusz Zych PS. I want to thank Ivo Kabadshow from JSC for helping me with preparing th= ese code samples!=