Discussion:
Platforms with sub natural alignment requirements and alignof(T)
(too old to reply)
Jörn Heusipp
2017-09-30 12:08:18 UTC
Permalink
Hello.



This post is related to the recent "Alignment of a union less than its
members?" thread, however, I independently ran into a very similar issue
in the last days and want to tackle it from a slightly different angle.



All examples will be based on standard Linux x86 32bit (which has System
V i386 ABI). I specifically do not want to discuss Windows Win32 (x86)
ABI or any x86-64 ABI right now (which do behave differently).
I tested on Ubuntu 16.04, GCC 5.4.0, Clang 3.8.0, also verified on more
recent versions via gcc.godbolt.org (passing -x c for C language and of
course always -m32, as well as appropriate -std= (i.e. c11 or
c++11,c++14,c++17).

In the given ABI, a double is 8 bytes in size and is aligned to 4 bytes
inside a struct. I.e. struct foo { char x; double y; }; has sizeof(foo)
== 12 and alignof(foo) == 4. However, alignof(double) == 8 on GCC and
Clang.


From a rough glance, it appears the standard says alignof(T) should
return the alignment requirement for an object of type T.

Consider the following code:

// translation unit 1:
struct foox {
char x1;
double y;
char x2;
double z;
};
static_assert(sizeof(foox) == 24, "");
void baz(double * a);
void bar() {
foox a;
baz(&a.y);
baz(&a.z);
}
// translation unit 2:
#ifdef NDEBUG
#undef NDEBUG
#endif
#ifndef _DEBUG
#define _DEBUG
#endif
#include <cassert>
#include <cstdint>
void baz(double * a) {
// will fail exactly once when called via bar()
assert(reinterpret_cast<std::uintptr_t>(a) % alignof(double) == 0);
}

One assert will fail in current implementations. I do consider that
outcome neither correct nor useful.


The standards for C11 and C++17 say:

C: §6.2.8 Alignment of objects p1:
Complete object types have alignment requirements which place
restrictions on the addresses at which objects of that type may be
allocated. An alignment is an implementation-defined integer value
representing the number of bytes between successive addresses at which a
given object can be allocated. An object type imposes an alignment
requirement on every object of that type: stricter alignment can be
requested using the _Alignas keyword.

C++: §6.11 [basic.align] p1:
Object types have alignment requirements (6.9.1, 6.9.2) which place
restrictions on the addresses at which an object of that type may be
allocated. An alignment is an implementation-defined integer value
representing the number of bytes between successive addresses at which a
given object can be allocated. An object type imposes an alignment
requirement on every object of that type; stricter alignment can be
requested using the alignment specifier (10.6.2).

additional related paragraphs:

C++: §3.8 [basic.life] p1:
The lifetime of an object or reference is a runtime property of the
object or reference. An object is said to have non-vacuous
initialization if it is of a class or aggregate type and it or one of
its subobjects is initialized by a constructor other than a trivial
default constructor. [ Note: initialization by a trivial copy/move
constructor is non-vacuous initialization. — end note ] The lifetime of
an object of type T begins when:
C++: §3.8 [basic.life] p1.1:
storage with the proper alignment and size for type T is obtained,
[...]

C++: §5.3.6 [expr.alignof] p1:
An alignof expression yields the alignment requirement of its operand
type. The operand shall be a type-id representing a complete object
type, or an array thereof, or a reference to one of those types.


To me, this sound like really similar language which probably intended
giving the same answer for C11 _Alignof(T) and C++11/14/17 alignof(T).
However:
gcc C11: _Alignof(double) == 4
clang C11: _Alignof(double) == 8
tcc C : __alignof__(double) == 4 /* tcc has no C11 support, it
implements a non-standard __alignof__(T) though */
g++ C++: alignof(double) == 8
clang C++: alignof(double) == 8

struct foo {
char x;
double y;
}; // sizeof(foo)==12 && alignof(foo)==4

As far as I understand standard-speak, "double" is an "object type" (see
§1.8 [intro.object] 1.) and in the case of struct foo, an object (as a
subobject) of type double will get allocated whenever struct foo is
allocated. The standard explicitly says "every object", so this is a
contradiction with what happens in practice.
If my interpretation of "allocation" is flawed here, please explain and
point me to the relevant definition that even defines "allocated" (or
"allocation"). I can only find "dynamic allocation" and object creation
being defined.


alignof(T) is supposed to return the alignment *requirement* for
allocations of type T. However, the actual *requirement* is 4. A
function taking a pointer to double argument in a translation unit
different from the one allocating the storage cannot know whether it had
been allocated via operator new or as part of a structure. It thus must
assume the alignment could be as low as only 4 bytes, and thus it must
handle also that case. This means that type double has an alignment
*requirement* of 4 and not 8, which implies that alignof(double)
currently returns the wrong value (in particular, the standard does not
say something like "the alignment requirement or some greater alignment"
anywhere in the definition of alignof(T), and also does not say
something like "every object, except those mandated otherwise by the
implementation" in the definition of alignment requirement).
If alignof(T) is actually intended to only be useful for aligning
dynamic allocation (in which case any multiple of 4 would do), what does
the result of alignof(T) even mean semantically? As far as I can see, it
would be merely a (possibly performance-motivated) hint, but it is
clearly not specified as such. Short of being the natural alignment, I
cannot even come up with any reason to even consider returning 8 for
alignof(double).
(In my opinion, this argument is semantically the most convincing of all
the different arguments that I bring up in this post, so let me state it
a second time: The actual REQUIERED alignment for any object of type
double (no matter where it came from) can only ever be as big as 4, as a
direct consequence of the targeted ABI. alignof(double) returns 8 as the
specified required alignment. 4 byte aligned doubles *MUST* work, thus
stating 8 as *required* alignment is clearly wrong.)


Other related aspects:

C++ §3.11 [basic.align] p2:
A fundamental alignment is represented by an alignment less than or
equal to the greatest alignment supported by the implementation in all
contexts, which is equal to alignof(std::max_align_t) (18.2). The
alignment required for a type might be different when it is used as the
type of a complete object and when it is used as the type of a subobject.
[ Example:
struct B { long double d; };
struct D : virtual B { char c; };
When D is the type of a complete object, it will have a subobject of
type B, so it must be aligned appropriately for a long double. If D
appears as a subobject of another object that also has B as a virtual
base class, the B subobject might be part of a different subobject,
reducing the alignment requirements on the D subobject.
— end example ]
The result of the alignof operator reflects the alignment requirement of
the type in the complete-object case.

C §6.2.8 Alignment of objects p2:
A fundamental alignment is represented by an alignment less than or
equal to the greatest alignment supported by the implementation in all
contexts, which is equal to _Alignof (max_align_t).

I do not think that paragraph is at all relevant to the discussion here.
What I think is intended to be specified here in C++ can only ever be
relevant if virtual inheritance is involved, i.e. if some part B of the
object D is removed from the actual object because it is virtually
inherited from another object that already has a virtual subobject of
type B. This in particular does not apply to a structure containing only
a char x and a double y, as used in my examples. In other words, this
paragraph in the C++ standard (I think) can only apply to class
subobjects as opposed to member subobjects (which is very likely the
reason why C11 does not talk about subobjects here (because there are no
class subobjects in C), and omits that example).


C++ §7.6.2 [dcl.align] p5:
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 appertaining to that entity were omitted.

struct foo3 {
char x;
alignas(4) double y;
};

This is ambiguous. Does the "required" in the quoted paragraph mean the
actual practically required alignment (which is 4) or the arbitrarily
invented "required" alignment as returned by alignof(double) (which
currently is 8). In the latter case, foo3 would be ill-formed, however
the programmer-intended to-be-obtained layout would be precisely
identical to the layout obtained by not specifying alignas() at all. In
the former case, the semantics of the word "required" (or "requirement")
in the standard would be ambiguous (§7.6.2 [dcl.align] p5 versus §6.11
[basic.align] p1).


And,

struct foo {
char x;
double y;
}; // sizeof(foo)==12
struct foo2 {
char x;
alignas(double) double y; // equivalent to alignas(alignof(double))
// by specification
}; // sizeof(foo2)==16

is also rather surprising and unintuitive.


And additionally,

struct foo2_c {
char x;
_Alignas(double) double y;
}; // sizeof(struct foo2_c)==12

struct foo2_c has a different layout in C (according to GCC) than struct
foo2 has in C++. This easily breaks code in practice.


Another point: In the current situation I cannot use alignof(T) to
determine how many bits of additional information I can store in the
least significant bits of a pointer to double once I have casted it to
std::uintptr_t. I even cannot do that for ANY type because in general
the required precondition does not hold if it is violated for even only
some types. I am pretty sure that people do that in practice, and I can
only imagine that this problem has not been noticed earlier because
32bit x86 tends to only get used seldomly nowadays, and because the type
in question is rarely a double (or long long), or because in most cases
the pointer has better alignment either by chance or because it does not
point to a double inside of a struct.


Semantically, the alignment requirement of a type is a property of that
type.
struct foo {
char x;
double y;
}; // sizeof(foo)==12
foo f; // alignof(f.y) == 4 (f.y has type double (and in particular
// no other explicit properties in addition compared to any
// other plain type double), however alignof(double) == 8
IMHO, this (philosophically) breaks the type system, which is a quite
severe defect in a statically-typed language. In particular, this can
totally confuse templated code that does type computation, as the actual
alignment information is lost on the first operation that takes the type
out of the context of being a struct member.



Unless the intended outcome of the standard (for whatever reason) was to
have C++ and C disagree about the semantics of _Alignof/alignof (which I
really hope was not the intention), at least 2 compilers give the wrong
answer here. Additionally, I do not think the standard (and ABI) allow
for 2 different compilers to return different results on the same
platform ABI, so in C11 mode, either GCC and tcc are wrong (I think they
are not), or Clang is wrong. In C++11/14/17 mode, I think both, GCC and
Clang, are wrong.


The wrong result of alignof(double) also has practical implications if I
have a function that takes a pointer to an (over-sized) array of double
as an argument and wants to align a pointer into that buffer on 16 or 32
bytes (which is useful for SIMD access).
static_assert(alignof(double)>=8, "") suggests that I could align any
pointer to array of double to an alignment of 16 or 32. However, I
cannot do that in practice because it might have only been 4 byte
aligned in the first place, which implies aligning it to 16 bytes has
undefined behavior because I might be pointing inside half of an double
object then. The fact that this is not possible is fine, as this aspect
is basically a direct consequence of the ABI and cannot be changed.
However, I think alignof(double) should allow me to *detect* that (in
particular, there is currently *NO* other means to do that). (I am aware
that I can still use other means to obtain a pointer to an array of
double aligned to 16 or 32 bytes, but that is not the point here)).


The original code that ran into this problem is trying to detect at
compile-time if it can align a pointer to any array of T to some given
alignment without invoking undefined behavior. That is a reasonable
question to ask, and alignof(double) should be able to answer that, but
it cannot because it currently returns an arbitrary and meaningless
value in practice.



Questions:


Could someone please explain why alignof(double)==8 in GCC C++ and Clang
C/C++ would be correct (in which case you should also explain why every
single example outlined here is either already flawed in its premise or
sensible with alignof(double)==8), or confirm that this is in fact the
wrong result?

What are the intended use cases for alignof(T)? In the current
situation, I cannot use it to deduce structure alignment or padding. I
cannot use it to make assumptions about the alignment of a pointer. I
cannot use it to store bits into the least significant bits of a
pointer. I even cannot use it to dynamically allocate minimally aligned
storage because the result of alignof(T) is bigger than it provably (see
above) needs to be in practice. Even if I want to align allocated
storage for possibly performance-motivated reasons more than
fundamentally required, I could always just use std::lcm(sizeof(double),
alignof(double)) even if alignof(double) would be 4, and get at least
naturally aligned storage (some kind of alignof_fast(double) would be
more elegant and semantically clear though).

Should struct foo { char x; double y; } and struct foo2 { char x;
alignas(double) double y; } have identical layout? In practice, they
currently have not.

How do I determine how many bits I can store into the least significant
bits of a std::uintptr_t obtained from casting a pointer do double to a
std::uintptr_t?

Why do/did compiler implementors even consider returning 8 for
alignof(double)? The standard does not hint at that being any kind of
sensible value here, and the ABI clearly allows for only 4 byte aligned
doubles. The C++ standard or an implementation can impose additional
requirements on alignment compared to the x86 ISA as much as they want
to (and they do (the x86 ISA for example has no requirements on aligning
short or int), rendering mis-aligned pointers to these types undefined
behavior). However, trying to impose additional alignment requirements
(inconsistently) on top of the targeted ABI (as opposed to the ISA) does
not seem useful or reasonable to me (and falls short with respect to
consistency in various situations as outlined in this post).



unsigned long long and signed long long are equally affected.
I get the correct answer (4) for long double though, which is
understandable as 16 would be kind of weird for a 12 bytes sized type,
and 12 is forbidden by specification (the alignment requirement must be
a power-of-2).

Similar reasoning can be applied to union instead of struct.


Please note that any reasoning or answer should better be valid with
even only considering C and not any C++ specific constructs or language,
because otherwise C _Alignof and C++ alignof will be inconsistent.


There are already several related bug reports (mostly for C instead of C++):
GCC (C): https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52023 , in
particular comment https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52023#c6
GCC (C/C++): https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69763
GCC (C++): https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69560
Clang (C): https://bugs.llvm.org/show_bug.cgi?id=26547


Depending on the outcome of this discussion, I intend to file additional
or augment existing compiler bug reports and/or language defect reports
as the current situation is quite frankly a complete mess (I do think
the standard might already be fine though, and even as it stands does
not allow alignof(double)==8 on a target platform ABI that aligns double
to 4 inside of structs; it could be more explicit and clear though).
If someone else wants to do that, feel free to do so, but please notify
me about that in order to avoid duplicate work.



Regards,
Jörn
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Chris Hallock
2017-10-01 00:00:11 UTC
Permalink
Post by Jörn Heusipp
Hello.
This post is related to the recent "Alignment of a union less than its
members?" thread, however, I independently ran into a very similar issue
in the last days and want to tackle it from a slightly different angle.
All examples will be based on standard Linux x86 32bit (which has System
V i386 ABI). I specifically do not want to discuss Windows Win32 (x86)
ABI or any x86-64 ABI right now (which do behave differently).
I tested on Ubuntu 16.04, GCC 5.4.0, Clang 3.8.0, also verified on more
recent versions via gcc.godbolt.org (passing -x c for C language and of
course always -m32, as well as appropriate -std= (i.e. c11 or
c++11,c++14,c++17).
In the given ABI, a double is 8 bytes in size and is aligned to 4 bytes
inside a struct. I.e. struct foo { char x; double y; }; has sizeof(foo)
== 12 and alignof(foo) == 4. However, alignof(double) == 8 on GCC and
Clang.
From a rough glance, it appears the standard says alignof(T) should
return the alignment requirement for an object of type T.
struct foox {
char x1;
double y;
char x2;
double z;
};
static_assert(sizeof(foox) == 24, "");
void baz(double * a);
void bar() {
foox a;
baz(&a.y);
baz(&a.z);
}
#ifdef NDEBUG
#undef NDEBUG
#endif
#ifndef _DEBUG
#define _DEBUG
#endif
#include <cassert>
#include <cstdint>
void baz(double * a) {
// will fail exactly once when called via bar()
assert(reinterpret_cast<std::uintptr_t>(a) % alignof(double) == 0);
}
One assert will fail in current implementations. I do consider that
outcome neither correct nor useful.
Complete object types have alignment requirements which place
restrictions on the addresses at which objects of that type may be
allocated. An alignment is an implementation-defined integer value
representing the number of bytes between successive addresses at which a
given object can be allocated. An object type imposes an alignment
requirement on every object of that type: stricter alignment can be
requested using the _Alignas keyword.
Object types have alignment requirements (6.9.1, 6.9.2) which place
restrictions on the addresses at which an object of that type may be
allocated. An alignment is an implementation-defined integer value
representing the number of bytes between successive addresses at which a
given object can be allocated. An object type imposes an alignment
requirement on every object of that type; stricter alignment can be
requested using the alignment specifier (10.6.2).
The lifetime of an object or reference is a runtime property of the
object or reference. An object is said to have non-vacuous
initialization if it is of a class or aggregate type and it or one of
its subobjects is initialized by a constructor other than a trivial
default constructor. [ Note: initialization by a trivial copy/move
constructor is non-vacuous initialization. — end note ] The lifetime of
storage with the proper alignment and size for type T is obtained,
[...]
An alignof expression yields the alignment requirement of its operand
type. The operand shall be a type-id representing a complete object
type, or an array thereof, or a reference to one of those types.
To me, this sound like really similar language which probably intended
giving the same answer for C11 _Alignof(T) and C++11/14/17 alignof(T).
gcc C11: _Alignof(double) == 4
clang C11: _Alignof(double) == 8
tcc C : __alignof__(double) == 4 /* tcc has no C11 support, it
implements a non-standard __alignof__(T) though */
g++ C++: alignof(double) == 8
clang C++: alignof(double) == 8
struct foo {
char x;
double y;
}; // sizeof(foo)==12 && alignof(foo)==4
As far as I understand standard-speak, "double" is an "object type" (see
§1.8 [intro.object] 1.) and in the case of struct foo, an object (as a
subobject) of type double will get allocated whenever struct foo is
allocated. The standard explicitly says "every object", so this is a
contradiction with what happens in practice.
If my interpretation of "allocation" is flawed here, please explain and
point me to the relevant definition that even defines "allocated" (or
"allocation"). I can only find "dynamic allocation" and object creation
being defined.
alignof(T) is supposed to return the alignment *requirement* for
allocations of type T. However, the actual *requirement* is 4. A
function taking a pointer to double argument in a translation unit
different from the one allocating the storage cannot know whether it had
been allocated via operator new or as part of a structure. It thus must
assume the alignment could be as low as only 4 bytes, and thus it must
handle also that case. This means that type double has an alignment
*requirement* of 4 and not 8, which implies that alignof(double)
currently returns the wrong value (in particular, the standard does not
say something like "the alignment requirement or some greater alignment"
anywhere in the definition of alignof(T), and also does not say
something like "every object, except those mandated otherwise by the
implementation" in the definition of alignment requirement).
If alignof(T) is actually intended to only be useful for aligning
dynamic allocation (in which case any multiple of 4 would do), what does
the result of alignof(T) even mean semantically? As far as I can see, it
would be merely a (possibly performance-motivated) hint, but it is
clearly not specified as such. Short of being the natural alignment, I
cannot even come up with any reason to even consider returning 8 for
alignof(double).
(In my opinion, this argument is semantically the most convincing of all
the different arguments that I bring up in this post, so let me state it
a second time: The actual REQUIERED alignment for any object of type
double (no matter where it came from) can only ever be as big as 4, as a
direct consequence of the targeted ABI. alignof(double) returns 8 as the
specified required alignment. 4 byte aligned doubles *MUST* work, thus
stating 8 as *required* alignment is clearly wrong.)
A fundamental alignment is represented by an alignment less than or
equal to the greatest alignment supported by the implementation in all
contexts, which is equal to alignof(std::max_align_t) (18.2). The
alignment required for a type might be different when it is used as the
type of a complete object and when it is used as the type of a subobject.
struct B { long double d; };
struct D : virtual B { char c; };
When D is the type of a complete object, it will have a subobject of
type B, so it must be aligned appropriately for a long double. If D
appears as a subobject of another object that also has B as a virtual
base class, the B subobject might be part of a different subobject,
reducing the alignment requirements on the D subobject.
— end example ]
The result of the alignof operator reflects the alignment requirement of
the type in the complete-object case.
A fundamental alignment is represented by an alignment less than or
equal to the greatest alignment supported by the implementation in all
contexts, which is equal to _Alignof (max_align_t).
I do not think that paragraph is at all relevant to the discussion here.
What I think is intended to be specified here in C++ can only ever be
relevant if virtual inheritance is involved, i.e. if some part B of the
object D is removed from the actual object because it is virtually
inherited from another object that already has a virtual subobject of
type B. This in particular does not apply to a structure containing only
a char x and a double y, as used in my examples. In other words, this
paragraph in the C++ standard (I think) can only apply to class
subobjects as opposed to member subobjects (which is very likely the
reason why C11 does not talk about subobjects here (because there are no
class subobjects in C), and omits that example).
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 appertaining to that entity were omitted.
struct foo3 {
char x;
alignas(4) double y;
};
This is ambiguous. Does the "required" in the quoted paragraph mean the
actual practically required alignment (which is 4) or the arbitrarily
invented "required" alignment as returned by alignof(double) (which
currently is 8). In the latter case, foo3 would be ill-formed, however
the programmer-intended to-be-obtained layout would be precisely
identical to the layout obtained by not specifying alignas() at all. In
the former case, the semantics of the word "required" (or "requirement")
in the standard would be ambiguous (§7.6.2 [dcl.align] p5 versus §6.11
[basic.align] p1).
And,
struct foo {
char x;
double y;
}; // sizeof(foo)==12
struct foo2 {
char x;
alignas(double) double y; // equivalent to
alignas(alignof(double))
// by specification
}; // sizeof(foo2)==16
is also rather surprising and unintuitive.
And additionally,
struct foo2_c {
char x;
_Alignas(double) double y;
}; // sizeof(struct foo2_c)==12
struct foo2_c has a different layout in C (according to GCC) than struct
foo2 has in C++. This easily breaks code in practice.
Another point: In the current situation I cannot use alignof(T) to
determine how many bits of additional information I can store in the
least significant bits of a pointer to double once I have casted it to
std::uintptr_t. I even cannot do that for ANY type because in general
the required precondition does not hold if it is violated for even only
some types. I am pretty sure that people do that in practice, and I can
only imagine that this problem has not been noticed earlier because
32bit x86 tends to only get used seldomly nowadays, and because the type
in question is rarely a double (or long long), or because in most cases
the pointer has better alignment either by chance or because it does not
point to a double inside of a struct.
Semantically, the alignment requirement of a type is a property of that
type.
struct foo {
char x;
double y;
}; // sizeof(foo)==12
foo f; // alignof(f.y) == 4 (f.y has type double (and in particular
// no other explicit properties in addition compared to any
// other plain type double), however alignof(double) == 8
IMHO, this (philosophically) breaks the type system, which is a quite
severe defect in a statically-typed language. In particular, this can
totally confuse templated code that does type computation, as the actual
alignment information is lost on the first operation that takes the type
out of the context of being a struct member.
Unless the intended outcome of the standard (for whatever reason) was to
have C++ and C disagree about the semantics of _Alignof/alignof (which I
really hope was not the intention), at least 2 compilers give the wrong
answer here. Additionally, I do not think the standard (and ABI) allow
for 2 different compilers to return different results on the same
platform ABI, so in C11 mode, either GCC and tcc are wrong (I think they
are not), or Clang is wrong. In C++11/14/17 mode, I think both, GCC and
Clang, are wrong.
The wrong result of alignof(double) also has practical implications if I
have a function that takes a pointer to an (over-sized) array of double
as an argument and wants to align a pointer into that buffer on 16 or 32
bytes (which is useful for SIMD access).
static_assert(alignof(double)>=8, "") suggests that I could align any
pointer to array of double to an alignment of 16 or 32. However, I
cannot do that in practice because it might have only been 4 byte
aligned in the first place, which implies aligning it to 16 bytes has
undefined behavior because I might be pointing inside half of an double
object then. The fact that this is not possible is fine, as this aspect
is basically a direct consequence of the ABI and cannot be changed.
However, I think alignof(double) should allow me to *detect* that (in
particular, there is currently *NO* other means to do that). (I am aware
that I can still use other means to obtain a pointer to an array of
double aligned to 16 or 32 bytes, but that is not the point here)).
The original code that ran into this problem is trying to detect at
compile-time if it can align a pointer to any array of T to some given
alignment without invoking undefined behavior. That is a reasonable
question to ask, and alignof(double) should be able to answer that, but
it cannot because it currently returns an arbitrary and meaningless
value in practice.
Could someone please explain why alignof(double)==8 in GCC C++ and Clang
C/C++ would be correct (in which case you should also explain why every
single example outlined here is either already flawed in its premise or
sensible with alignof(double)==8), or confirm that this is in fact the
wrong result?
What are the intended use cases for alignof(T)? In the current
situation, I cannot use it to deduce structure alignment or padding. I
cannot use it to make assumptions about the alignment of a pointer. I
cannot use it to store bits into the least significant bits of a
pointer. I even cannot use it to dynamically allocate minimally aligned
storage because the result of alignof(T) is bigger than it provably (see
above) needs to be in practice. Even if I want to align allocated
storage for possibly performance-motivated reasons more than
fundamentally required, I could always just use std::lcm(sizeof(double),
alignof(double)) even if alignof(double) would be 4, and get at least
naturally aligned storage (some kind of alignof_fast(double) would be
more elegant and semantically clear though).
Should struct foo { char x; double y; } and struct foo2 { char x;
alignas(double) double y; } have identical layout? In practice, they
currently have not.
How do I determine how many bits I can store into the least significant
bits of a std::uintptr_t obtained from casting a pointer do double to a
std::uintptr_t?
Why do/did compiler implementors even consider returning 8 for
alignof(double)? The standard does not hint at that being any kind of
sensible value here, and the ABI clearly allows for only 4 byte aligned
doubles. The C++ standard or an implementation can impose additional
requirements on alignment compared to the x86 ISA as much as they want
to (and they do (the x86 ISA for example has no requirements on aligning
short or int), rendering mis-aligned pointers to these types undefined
behavior). However, trying to impose additional alignment requirements
(inconsistently) on top of the targeted ABI (as opposed to the ISA) does
not seem useful or reasonable to me (and falls short with respect to
consistency in various situations as outlined in this post).
unsigned long long and signed long long are equally affected.
I get the correct answer (4) for long double though, which is
understandable as 16 would be kind of weird for a 12 bytes sized type,
and 12 is forbidden by specification (the alignment requirement must be
a power-of-2).
Similar reasoning can be applied to union instead of struct.
Please note that any reasoning or answer should better be valid with
even only considering C and not any C++ specific constructs or language,
because otherwise C _Alignof and C++ alignof will be inconsistent.
GCC (C): https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52023 , in
particular comment https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52023#c6
GCC (C/C++): https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69763
GCC (C++): https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69560
Clang (C): https://bugs.llvm.org/show_bug.cgi?id=26547
Depending on the outcome of this discussion, I intend to file additional
or augment existing compiler bug reports and/or language defect reports
as the current situation is quite frankly a complete mess (I do think
the standard might already be fine though, and even as it stands does
not allow alignof(double)==8 on a target platform ABI that aligns double
to 4 inside of structs; it could be more explicit and clear though).
If someone else wants to do that, feel free to do so, but please notify
me about that in order to avoid duplicate work.
Regards,
Jörn
It's reasonably clear that C11 requires every object, including
structure/union members, to respect the (singular) alignment requirement of
its type, and _Alignof must report that value, not some higher value (such
as a "preferred" value). GCC has already been fixed to conform and I would
guess that Clang will eventually follow suit.

C++ [basic.align]/2, however, is a troublesome break from C. The wording is
desirable for classes with virtual bases, but it goes way beyond that case
and effectively lets GCC/Clang report "preferred" alignments instead of
required alignments under C++. Maybe we should change [basic.align]/2 so
that it only applies to the virtual base class scenario, and if there's
significant demand for being able to query a "preferred" alignment, we
should add a type trait for that.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Jörn Heusipp
2017-10-01 13:06:29 UTC
Permalink
Post by Chris Hallock
It's reasonably clear that C11 requires every object, including
structure/union members, to respect the (singular) alignment requirement of
its type, and _Alignof must report that value, not some higher value (such
as a "preferred" value). GCC has already been fixed to conform and I would
guess that Clang will eventually follow suit.
I would hope so.
Post by Chris Hallock
C++ [basic.align]/2, however, is a troublesome break from C. The wording is
desirable for classes with virtual bases, but it goes way beyond that case
and effectively lets GCC/Clang report "preferred" alignments instead of
required alignments under C++. Maybe we should change [basic.align]/2 so
that it only applies to the virtual base class scenario, and if there's
significant demand for being able to query a "preferred" alignment, we
should add a type trait for that.
I do not think that C++ [basic.align]/2 lets implementations completely
ignore the definition of alignment requirement in C++ [basic.align]/1.
The only thing that is somewhat obvious here, is that the given
paragraph is interpreted differently and inconsistently by various
people, which I think at the very least warrants some clarification in
the standard wording.
Even iff C++ [basic.align]/2 lets compilers return a preferred alignment
without contradicting other paragraphs, it is still not clear to me why
that would be a reasonable trade-off for implementations, given the
amount of weird inconsistencies (and incompatibilities with C) that it
introduces.

I would also prefer having an explicit (and different from alignof(T))
way to determine the preferred alignment in the standard. The current
wording does not suggest at all that alignof(T) would return a preferred
alignment (in fact it suggests in order to obtain better than required
alignment, I might have to use alignas or std::align).


Jörn
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Chris Hallock
2017-10-01 18:40:38 UTC
Permalink
Post by Jörn Heusipp
I do not think that C++ [basic.align]/2 lets implementations completely
ignore the definition of alignment requirement in C++ [basic.align]/1.
The only thing that is somewhat obvious here, is that the given
paragraph is interpreted differently and inconsistently by various
people, which I think at the very least warrants some clarification in
the standard wording.
Even iff C++ [basic.align]/2 lets compilers return a preferred alignment
without contradicting other paragraphs, it is still not clear to me why
that would be a reasonable trade-off for implementations, given the
amount of weird inconsistencies (and incompatibilities with C) that it
introduces.
I think it's clear that ¶2 overrides the implication within ¶1 (or any
other part of the Standard) that there's always exactly 1 alignment
requirement associated with a type. In particular, the example within ¶2
makes it clear that the intent of ¶2 is to, at minimum, loosen that
restriction for virtual inheritance scenarios. That is all very well and
good. The problem, however, is that ¶2 doesn't say "base class subobjects
that virtually inherit other classes", it says "subobjects", which, per
[intro.object]/2 <http://eel.is/c++draft/intro.object#2.sentence-2>, also
includes member subobjects and base class subobjects that don't virtually
inherit other classes and even array elements (!).
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Jörn Heusipp
2017-10-02 14:53:31 UTC
Permalink
Post by Jörn Heusipp
I do not think that C++ [basic.align]/2 lets implementations completely
ignore the definition of alignment requirement in C++ [basic.align]/1.
The only thing that is somewhat obvious here, is that the given
paragraph is interpreted differently and inconsistently by various
people, which I think at the very least warrants some clarification in
the standard wording.
Even iff C++ [basic.align]/2 lets compilers return a preferred alignment
without contradicting other paragraphs, it is still not clear to me why
that would be a reasonable trade-off for implementations, given the
amount of weird inconsistencies (and incompatibilities with C) that it
introduces.
I think it's clear that ¶2 overrides the implication within ¶1 (or any
other part of the Standard) that there's always exactly 1 alignment
requirement associated with a type.
I would not say that it is clear, however I can understand it could be
read that way.

Committee seems to think it is clear though
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#1879.
In particular, the example within ¶2
makes it clear that the intent of ¶2 is to, at minimum, loosen that
restriction for virtual inheritance scenarios. That is all very well and
good. The problem, however, is that ¶2 doesn't say "base class subobjects
that virtually inherit other classes", it says "subobjects", which, per
[intro.object]/2 <http://eel.is/c++draft/intro.object#2.sentence-2>, also
includes member subobjects and base class subobjects that don't virtually
inherit other classes and even array elements (!).
Agreed, some wording like that would make things clearer.
A note about C compatibility would also be good to have there.


Jörn
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-10-01 09:13:18 UTC
Permalink
Post by Jörn Heusipp
(In my opinion, this argument is semantically the most convincing of all
the different arguments that I bring up in this post, so let me state it
a second time: The actual REQUIERED alignment for any object of type
double (no matter where it came from) can only ever be as big as 4, as a
direct consequence of the targeted ABI. alignof(double) returns 8 as the
specified required alignment. 4 byte aligned doubles *MUST* work, thus
stating 8 as *required* alignment is clearly wrong.)
1- and 2-byte aligned doubles also work.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Jörn Heusipp
2017-10-01 13:08:10 UTC
Permalink
Post by Thiago Macieira
Post by Jörn Heusipp
(In my opinion, this argument is semantically the most convincing of all
the different arguments that I bring up in this post, so let me state it
a second time: The actual REQUIERED alignment for any object of type
double (no matter where it came from) can only ever be as big as 4, as a
direct consequence of the targeted ABI. alignof(double) returns 8 as the
specified required alignment. 4 byte aligned doubles *MUST* work, thus
stating 8 as *required* alignment is clearly wrong.)
1- and 2-byte aligned doubles also work.
They are not demanded to by either the standard or the ABI though, which
is the whole point here. 4 byte alignment is required for the ABI to
work, thus stating a requirement of 8 via alignof() is wrong.

The "implementation" from the perspective of the standard is limited by
what the ABI which it implements demands. It can support 1 or 2 byte
aligned double if it wants to (in which case alignof(double) better be 1
or 2, because §6.11 [basic.align] p1 specifies the alignment requirement
as applying to *every object* of that type. So, if an implementation
wants 1 or 2 byte aligned doubles to work, it has to state so by giving
the matching answer in alingnof(double)).

1 and 2 byte alignment double just happens to work out of pure luck
because the x86 ISA does not pose restrictions here (other ISAs do
though). From the perspective of the standard (and a given
implementation), they are undefined behavior because they violate the
alignment requirement for type double (which is obtainable by
alignof(double) as per specification, which is in turn why
alignof(double) must not be 8 if double can also be only 4 byte aligned).


Jörn
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Myriachan
2017-10-02 18:09:17 UTC
Permalink
Post by Thiago Macieira
Post by Jörn Heusipp
(In my opinion, this argument is semantically the most convincing of all
the different arguments that I bring up in this post, so let me state it
a second time: The actual REQUIERED alignment for any object of type
double (no matter where it came from) can only ever be as big as 4, as a
direct consequence of the targeted ABI. alignof(double) returns 8 as the
specified required alignment. 4 byte aligned doubles *MUST* work, thus
stating 8 as *required* alignment is clearly wrong.)
1- and 2-byte aligned doubles also work.
This isn't strictly true, even on x86. I recently dealt with a crash where
GCC's autovectorization assumed that the alignment of an array of floats
could only be 0, 4, 8, 12 mod 16, and the program crashed with a misaligned
movdqa address. In other words, the compiler is assuming that floats will
always be aligned.

I could not get GCC or Clang to autovectorize double math in a way that
would break with a double aligned to 4 or 12 mod 16 on x86-32, so the
compilers are aware of the possibility of doubles being half-misaligned as
in the struct case.

Melissa
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-10-02 18:33:47 UTC
Permalink
Post by Myriachan
Post by Thiago Macieira
1- and 2-byte aligned doubles also work.
This isn't strictly true, even on x86. I recently dealt with a crash where
GCC's autovectorization assumed that the alignment of an array of floats
could only be 0, 4, 8, 12 mod 16, and the program crashed with a misaligned
movdqa address. In other words, the compiler is assuming that floats will
always be aligned.
I don't doubt it. My point was that the processor can load from any address,
provided you don't use one of the very few instructions that require 16-byte
aligned addresses. MOVDQA and MOVAPS/MOVAPD are such. PALIGNR is another.

The existence of these instructions is also why the stack needs to be 16-byte
aligned nowadays.
Post by Myriachan
I could not get GCC or Clang to autovectorize double math in a way that
would break with a double aligned to 4 or 12 mod 16 on x86-32, so the
compilers are aware of the possibility of doubles being half-misaligned as
in the struct case.
There are three ways of doing vectorised code:

1) if the compiler knows it's aligned to 16-bytes, just use those instructions
directly. It'll crash if it isn't.

2) if the compilers doesn't know, use the unaligned equivalents (MOVDQU,
MOVUPS, etc.) and it'll work for any boundary. The cost is that straddling a
16-byte boundary may mean executing two loads and merging the result (depends
on the processor and whether it also crosses a cacheline).

3) align in the prologue by executing a handful of iterations without
vectorisation, then once it's aligned, go to strategy 1. This is useful if the
loop is long, in which case the cost of the prologue is amortised very quickly
by the gain in using the aligned instructions. But if the compiler makes the
assumption that the original alignment is a multiple of 4, then breaking that
will mean it never arrives at an alignment of 16.

Your case was probably #3. Which strategy the compiler chooses is very difficult
to determine, since it depends on the compiler, your optimisation level and
the target & tune CPU you've selected.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-10-01 09:20:20 UTC
Permalink
Post by Jörn Heusipp
How do I determine how many bits I can store into the least significant
bits of a std::uintptr_t obtained from casting a pointer do double to a
std::uintptr_t?
You read the ABI documentation. The standard does not guarantee anything about
the representation of pointer values.
Post by Jörn Heusipp
Why do/did compiler implementors even consider returning 8 for
alignof(double)?
It's more efficient. When aligned at 8 bytes, an 8-byte quantity cannot cross a
cacheline boundary. So every load and every store is atomic. If it did cross a
cacheline boundary (or in some processors, cross a 16-byte boundary), the
processor would execute two loads or two stores and merge the result for you.
That's slower and non-atomic.
Post by Jörn Heusipp
unsigned long long and signed long long are equally affected.
I get the correct answer (4) for long double though, which is
understandable as 16 would be kind of weird for a 12 bytes sized type,
and 12 is forbidden by specification (the alignment requirement must be
a power-of-2).
long double is actually 10-bytes wide, not 12. The ABI rounds it up to a
multiple of the machine register.
Post by Jörn Heusipp
Depending on the outcome of this discussion, I intend to file additional
or augment existing compiler bug reports and/or language defect reports
as the current situation is quite frankly a complete mess (I do think
the standard might already be fine though, and even as it stands does
not allow alignof(double)==8 on a target platform ABI that aligns double
to 4 inside of structs; it could be more explicit and clear though).
If someone else wants to do that, feel free to do so, but please notify
me about that in order to avoid duplicate work.
It's highly unlikely that you'll get anything to change.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Jörn Heusipp
2017-10-01 13:10:00 UTC
Permalink
Post by Thiago Macieira
Post by Jörn Heusipp
How do I determine how many bits I can store into the least significant
bits of a std::uintptr_t obtained from casting a pointer do double to a
std::uintptr_t?
You read the ABI documentation. The standard does not guarantee anything about
the representation of pointer values.
You are right, the exact representation is not specified directly.

I think some aspects are implied, however not the full deal to allow for
the way I tried to argue here. We can consider this one particular
argument (stuffing bits into the least significant bits of a uintptr_t)
to be refuted.

Additionally, this implies that I can only ever use std::align to align
a pointer to anything. This in turn implies some of the examples in my
original post about asserting the alignment of a runtime pointer must
also be rephrased in terms of std::align instead of
reinterpret_cast<std::uintptr_t>.

I.e.

// translation unit 1:
struct foox {
char x1;
double y;
char x2;
double z;
};
static_assert(sizeof(foox) == 24, "");
void baz(double * a);
void bar() {
foox a;
baz(&a.y);
baz(&a.z);
}
// translation unit 2:
#ifdef NDEBUG
#undef NDEBUG
#endif
#ifndef _DEBUG
#define _DEBUG
#endif
#include <cassert>
#include <cstdint>
void baz(double * a) {
// will fail exactly once when called via bar()
assert(reinterpret_cast<std::uintptr_t>(a) % alignof(double) == 0);
}

should in fact be

// translation unit 1:
struct foox {
char x1;
double y;
char x2;
double z;
};
static_assert(sizeof(foox) == 24, "");
void baz(double * a);
void bar() {
foox a;
baz(&a.y);
baz(&a.z);
}
// translation unit 2:
#ifdef NDEBUG
#undef NDEBUG
#endif
#ifndef _DEBUG
#define _DEBUG
#endif
#include <cassert>
#include <cstdint>
#include <memory>
template <typename T>
bool is_aligned(T * p) {
std::size_t space = sizeof(double);
void *ptr = reinterpret_cast<void*>(p);
return std::align(alignof(double), sizeof(double), ptr, space) !=
nullptr;
}
void baz(double * a) {
// will fail exactly once when called via bar()
assert(is_aligned(a));
}

Rephrasing the assertion does not change any further reasoning though.
Post by Thiago Macieira
Post by Jörn Heusipp
Why do/did compiler implementors even consider returning 8 for
alignof(double)?
It's more efficient. When aligned at 8 bytes, an 8-byte quantity cannot cross a
cacheline boundary. So every load and every store is atomic. If it did cross a
cacheline boundary (or in some processors, cross a 16-byte boundary), the
processor would execute two loads or two stores and merge the result for you.
That's slower and non-atomic.
§6.11 [basic.align] p1 does not talk about efficiency. It talks about
the alignment requirement for every object of type T. I can obtain 4
byte aligned object of type double that must work (as demanded by the
targeted ABI). How can the alignment requirement then be 8 for type
double? That's a contradiction. It just cannot be 8.
Post by Thiago Macieira
Post by Jörn Heusipp
unsigned long long and signed long long are equally affected.
I get the correct answer (4) for long double though, which is
understandable as 16 would be kind of weird for a 12 bytes sized type,
and 12 is forbidden by specification (the alignment requirement must be
a power-of-2).
long double is actually 10-bytes wide, not 12. The ABI rounds it up to a
multiple of the machine register.
Yes, good point, sorry for getting that fact wrong.
However, it does not matter for the reasoning I outlined about double.
In fact, I think it shows why the required alignment for long double is
4 instead of 2 even better than my original comment did. The ABI
*aligns* it to 4, which is why there needs to be padding added to the
size (the same way as done for structs).
Post by Thiago Macieira
Post by Jörn Heusipp
Depending on the outcome of this discussion, I intend to file additional
or augment existing compiler bug reports and/or language defect reports
as the current situation is quite frankly a complete mess (I do think
the standard might already be fine though, and even as it stands does
not allow alignof(double)==8 on a target platform ABI that aligns double
to 4 inside of structs; it could be more explicit and clear though).
If someone else wants to do that, feel free to do so, but please notify
me about that in order to avoid duplicate work.
It's highly unlikely that you'll get anything to change.
In case the standard allows for alignof(double)==8 on x86 Sys-V ABI (I
still highly doubt that), I have pointed out multiple
self-contradictions and/or ambiguities in the standard in my original
post. It should better be fixed even (or *in particular*) if
alignof(double)==8 is to be considered a correct result.
I do still prefer fixing the compilers though, as opposed to changing
the standard to match what current implementations do (which I still
think is neither useful in practice nor allowed according to current
standard wording).


Jörn
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-10-01 17:21:06 UTC
Permalink
Post by Jörn Heusipp
Post by Thiago Macieira
It's more efficient. When aligned at 8 bytes, an 8-byte quantity cannot
cross a cacheline boundary. So every load and every store is atomic. If
it did cross a cacheline boundary (or in some processors, cross a 16-byte
boundary), the processor would execute two loads or two stores and merge
the result for you. That's slower and non-atomic.
§6.11 [basic.align] p1 does not talk about efficiency. It talks about
the alignment requirement for every object of type T. I can obtain 4
byte aligned object of type double that must work (as demanded by the
targeted ABI). How can the alignment requirement then be 8 for type
double? That's a contradiction. It just cannot be 8.
You asked the reason. I answered. I didn't say the answer was related to the
standard.

Today, the alignment should be 8. But in the late 1980s when the ABI was
created, that wasn't the case. The problem described above of atomicity and
performance wasn't an issue, since the processor didn't support 8- or 10-byte
atomic loads anyway (and it was the coprocessor that did that). So the maximum
alignment in the ABI was 4.

Only much later, in the 2000s, was the issue revisited. At that point, 8-byte
alignment made much more sense. It was at that time the ABI changed to require
16-byte aligned stacks too. But the ABI couldn't break compatibility, so
structures kept the existing layout.

And it wasn't until C11 and C++11 that the standard had an alignment operator.
Until then, __alignof was a compiler extension and it could return anything
the compiler wanted. We standardised the operator, but I guess no one thought
about the consequences of the wording not matching what the compilers
traditionally returned in their extension.
Post by Jörn Heusipp
However, it does not matter for the reasoning I outlined about double.
In fact, I think it shows why the required alignment for long double is
4 instead of 2 even better than my original comment did. The ABI
*aligns* it to 4, which is why there needs to be padding added to the
size (the same way as done for structs).
Right. sizeof(T) is always a multiple of alignof(T). That's required so that
&ptr[1] is also aligned.
Post by Jörn Heusipp
In case the standard allows for alignof(double)==8 on x86 Sys-V ABI (I
still highly doubt that), I have pointed out multiple
self-contradictions and/or ambiguities in the standard in my original
post. It should better be fixed even (or *in particular*) if
alignof(double)==8 is to be considered a correct result.
I do still prefer fixing the compilers though, as opposed to changing
the standard to match what current implementations do (which I still
think is neither useful in practice nor allowed according to current
standard wording).
I personally think that C++ alignof should have the same wording as C11
_Alignof and should be the strict alignment and therefore should be 4. Whether
__alignof(double) returns something different is completely irrelevant.

But I don't think it will happen.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Jörn Heusipp
2017-10-01 18:37:36 UTC
Permalink
Post by Thiago Macieira
Post by Jörn Heusipp
Post by Thiago Macieira
It's more efficient. When aligned at 8 bytes, an 8-byte quantity cannot
cross a cacheline boundary. So every load and every store is atomic. If
it did cross a cacheline boundary (or in some processors, cross a 16-byte
boundary), the processor would execute two loads or two stores and merge
the result for you. That's slower and non-atomic.
§6.11 [basic.align] p1 does not talk about efficiency. It talks about
the alignment requirement for every object of type T. I can obtain 4
byte aligned object of type double that must work (as demanded by the
targeted ABI). How can the alignment requirement then be 8 for type
double? That's a contradiction. It just cannot be 8.
You asked the reason. I answered. I didn't say the answer was related to the
standard.
Fair enough.
Post by Thiago Macieira
Today, the alignment should be 8. But in the late 1980s when the ABI was
created, that wasn't the case. The problem described above of atomicity and
performance wasn't an issue, since the processor didn't support 8- or 10-byte
atomic loads anyway (and it was the coprocessor that did that). So the maximum
alignment in the ABI was 4.
Only much later, in the 2000s, was the issue revisited. At that point, 8-byte
alignment made much more sense. It was at that time the ABI changed to require
16-byte aligned stacks too. But the ABI couldn't break compatibility, so
structures kept the existing layout.
And it wasn't until C11 and C++11 that the standard had an alignment operator.
Until then, __alignof was a compiler extension and it could return anything
the compiler wanted. We standardised the operator, but I guess no one thought
about the consequences of the wording not matching what the compilers
traditionally returned in their extension.
And compilers presumably might have just implemented alignof in terms of
whatever __alignof with proprietary semantics they already had. That for
sure explains why alignof(double)==8 was "considered".
Post by Thiago Macieira
Post by Jörn Heusipp
In case the standard allows for alignof(double)==8 on x86 Sys-V ABI (I
still highly doubt that), I have pointed out multiple
self-contradictions and/or ambiguities in the standard in my original
post. It should better be fixed even (or *in particular*) if
alignof(double)==8 is to be considered a correct result.
I do still prefer fixing the compilers though, as opposed to changing
the standard to match what current implementations do (which I still
think is neither useful in practice nor allowed according to current
standard wording).
I personally think that C++ alignof should have the same wording as C11
_Alignof and should be the strict alignment and therefore should be 4. Whether
__alignof(double) returns something different is completely irrelevant.
That's exactly also my personal opinion.


Jörn
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Continue reading on narkive:
Loading...