Open Quuxplusone opened 4 years ago
Bugzilla Link | PR47674 |
Status | NEW |
Importance | P normal |
Reported by | Mateusz Zych (mte.zych@gmail.com) |
Reported on | 2020-09-28 17:37:06 -0700 |
Last modified on | 2020-09-29 10:41:02 -0700 |
Version | 10.0 |
Hardware | All Linux |
CC | blitzrakete@gmail.com, dgregor@apple.com, erik.pilkington@gmail.com, llvm-bugs@lists.llvm.org, richard-llvm@metafoo.co.uk |
Fixed by commit(s) | |
Attachments | |
Blocks | |
Blocked by | |
See also | https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97222, PR40810 |
Since Clang aims to be compatible with GCC,
I want to mention that GCC also has this issue and I've already reported it:
- https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97222
Also, I hope this will allow GCC and Clang developers to figure out
how to resolve this issue, since (according to Andrew Pinski)
solving it requires addition to an Itanium ABI, used by both GCC and Clang.
Thanks, Mateusz
Thanks for the detailed writeup!
Your analysis is basically correct, but I would add that ICC's behavior here is
unsound (it only appears to "implement[] this behavior correctly" in simple
cases), whereas the GCC/Clang behavior is sound but surprising. For GCC, ICC,
and Clang, identity<fp> and identity<float> are the same type. Therefore it's
not possible for identity<fp>::type and identity<float>::type to have different
alignments, because they're the same type. So, for an example such as this:
std::cout << alignof(typename identity<fp>::type) << std::endl;
std::cout << alignof(typename identity<float>::type) << std::endl;
under GCC and Clang, both lines print out 4, whereas under ICC, they either
both print out 4 or both print out 16 depending on which one happens to appear
first in the program (and in general you can encounter ODR violations when
using ICC despite there being nothing wrong at the source level).
Fundamentally, the 'aligned' attribute is a GCC extension, so the GCC folks get
to define how it works. And they chose that instead of it resulting in a
different type that's *almost* like the original type (for example, treating it
as a type qualifier), it results in the same type, but that in some contexts
that same type behaves differently. That's a semantic disaster, but it's what
we live with. And in particular, the only way for template instantiation to be
sound in the presence of this semantic disaster is for it to ignore all such
attributes on template type arguments.
I think there are two things we could reasonably do here:
1) Implement the warning that modern versions of GCC produce when such a type
is used as a template argument, pointing out that the attribute that modifies
the type is ignored, and
2) Add alternative forms of the functionality question that behave like real
type qualifiers (as suggested). Note that we can't reasonably change the
meaning of __attribute__((aligned(N))) to do this; it'd need to be distinct
syntax.
The fact that our own intrin.h headers don't work reliably due to this issue
seems like a pretty good argument that we should do (2), for at least the
may_alias attribute.
Hi Richard,
Wow, I already was thinking that ICC behavior is contradictory,
because of this "single type, but two different alignments" issue,
but your argument makes it clear that ICC is not implementing correctly
typedefs with attribute aligned applied on them.
Compiler Explorer links with your examples:
ICC -> https://godbolt.org/z/z91axs
Clang -> https://godbolt.org/z/4fP8Tb
I agree that ICC's lack of consistency is even worse than GCC and Clang
discarding the aligned attribute, since it leads to consistency issues.
Regarding the steps to improve current situation,
I agree that issuing a warning about ignored attributes would be good idea,
since it would make developers aware of what is actually happening.
Also, I understand that due to backward compatibility changing current
semantics of the aligned and may_alias attributes is not possible.
This is fine, as long as x86 SIMD headers could be updated in such a way,
that x86 SIMD types could be passed as template arguments safely.
Note that, some x86 SIMD types use both may_alias and aligned attributes:
- /usr/lib/gcc/x86_64-linux-gnu/9/include/xmmintrin.h
typedef float __m128 __attribute__ ((__vector_size__(16),
__may_alias__));
/* Unaligned version of the same type. */
typedef float __m128_u __attribute__ ((__vector_size__(16),
__may_alias__,
__aligned__(1)));
Since there is no standard mechanism, which could replicate the behavior of
the may_alias attribute, I agree that a new way of controlling aliasing rules
for a particular type might be necessary.
Unfortunately, we might also need a new way of specifying alignment,
since the alignas specifier from C++11 will not help here,
because it does not allow decreasing alignment requirements:
"The combined effect of all alignment-specifiers in a declaration
shall not specify an alignment that is less strict than
the alignment that would be required for the entity being declared
if all alignment-specifiers were omitted (including those
in other declarations)."
On the other hand, the GCC's aligned attribute does explicitly allow it:
"When used as part of a typedef, the aligned attribute can both
increase and decrease alignment, and specifying the packed attribute
generates a warning."
However, introducing new aliasing and alignment specifiers
would increase complexity of Clang and GCC, so maybe we could to avoid it?
Since all x86 SIMD types have attribute vector_size applied on them,
and the vector_size attribute always introduces a brand new type,
then could we simply change x86 SIMD header files to use structs?
Obviously such change would break code using C++ type_traits to detect
whether an x86 SIMD type is a class type (via std::is_class meta-function),
but this shouldn't be a huge problem is practice, right?
What do you think?
Thank you, Mateusz Zych