This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: DATA_ALIGNMENT vs. DECL_USER_ALIGNMENT


Richard Kenner wrote:
> Consider a type P that is a pointer to type T where I say that type
> T has 4-byte alignment.  Now consider a function which is passed an
> object of type T.  If that function is not part of the current compilation,
> the specifcation of an alignment for T means that the compiler must ensure
> that any object of type T must be aligned to *at least* 4 bytes since the
> function is entitled to assume that.
> 
> Now consider a function that takes an object of type T that is *not* part
> of the current compilation.  When generating code to dereference P, it is
> not allowed to assume that the alignment of the object pointed to by P
> is *more* than four bytes.

This I understood already.  I think everyone sees this.

> This means that it is an error to change the alignment of the *type*
> in either direction from what the user specified.
> 
>     I have always expected that a user-specific alignment attribute can
>     only increase the alignment of a type or object, not decrease it.
>     After all, an object with alignment 8 *is* an object with alignment 4,
>     for pointer dereferences and arithmetic purposes.
> 
> Except in the case above, where you are *importing* an object that type
> and need to know what code to generate.  The reference to Ada is just that
> these things are discussed specifically in the Ada RM, but they really apply
> just as well to GNU C.

<lightbulb>
  Oh, I see why you mustn't increase alignment on types!  Thanks!
</lightbulb>

Naturally, an object cannot have a smaller alignment than the type
used to access it - that would lead to code generation errors.

That's why when I write:

	typedef int T __attribute__((aligned(16)));
	T x;

It is reasonably to expect to be able to access x through a pointer of
type T*, so I expect the *object* x to be allocated with alignment at
least 16 - because that's required for compatibility with the type.

If I now write:

	T y __attribute__((aligned(2)));

According to your rules I'd expect y to be allocated with alignment 2.
This implies that it is an error to access y through a pointer of type T*.

Consequently, &y must have type "T __attribute__((aligned(2)))", and
it would be a type error to assign it to a variable of type T*.

> It is certainly true in Ada that if you have
> 	for x'alignment use 4;
> then if you later ask for x'alignment, you must get 4 (bytes).  It's
> also true that x'address must be a multiple of 4.  But if the compiler
> were actually to tell the linker to align X to 8 bytes, there would be no
> way to distinguish that case from the case where it just happened to be
> aligned at that boundary.  So doing so would be conforming to the standard.

Somebody brought up an example where that might not be reasonable:
multiple object in a special linker section, and the programmer
expects them to be laid out as if in an array.  In practice an
object's size is usually a multiple of its alignment, so this isn't
usually a problem.

> So the issue with objects is not conformance, but what's "best".  To
> me, it seems that if the user goes to the trouble of specifying an
> alignment for an object, they are doing so for a reason and it should
> not be overriden unless there's a good reason to do that (e.g.,
> minimum alignment on a machine, like S390).

Ok, now I agree it is practical to want to reduce an object's
alignment for storage purposes.  (But isn't that why we have
__attribute__((packed))?)

Filling in some of the details: an object's alignment should inherit
from its type, including explicit type alignment attributes, though it
should be possible to override the type's alignment with an explicit
alignment attribute on the object.

The type of an object so declared includes the explicit object
alignment attribute, which overrides the original type alignment.

A pointer to a type of smaller alignment cannot be implicitly
converted to a pointer to the same type of larger alignment.

The only ugly part of this is that a user-specified alignment on a
type _increases_ the alignment, whereas a user-specified alignment on
an object will sometimes _decrease_ it.

This leads to the situation where I might reasonably have to write
code using a typedef to get usable code.  Here is a an example which
has well defined semantics according to your rules, but does not do
what some kinds of program require:

	#include "3rd_party.h"
	  -> defines "struct Foo { /* decls... */ };"
	...
	/* Force min 8-byte alignment so I can use 3 "tag" bits on pointers. */
	/* FIXME: This can reduce alignment and makes &my_foo
	   incompatible with the type "struct Foo *". */
	struct Foo my_foo __attribute__((aligned(8)));

	/* FIXME: It's an error to write do_something(&my_foo). */
	void do_something (struct Foo *);

The above is valid according to your rules and may actually be quite
useful, but in many programs it would not be what I'd want.  To get
what I'd want I'd have to write this instead:

	/* Force min 8-byte alignment so I can use 3 "tag" bits on pointers. */
	typedef struct Foo Foo_with_alignment __attribute__((aligned(8)));
	Foo_with_alignment my_foo;

Or this:

	/* I'm not sure if this works. */
	__typeof__(struct Foo __attribute__((aligned(8)))) my_foo;

Or this:

	struct Foo my_foo __attribute__((aligned(__alignof__(struct Foo) > 8
						 ?__alignof__(struct Foo):8)));

The essential quirk is this: whenever you take the address of an
object, if you have specified the object's alignment and it is less
than that of the original type, the resulting pointer is incompatible
with a pointer to the original type.  Hence the FIXME above.

That's not to say there's a problem with those semantics, but it is a
a bit counterintuitive that the second code sample does the right
thing for certain programs, despite appearing very similar to the first.

By the way, I've said that &my_foo is incompatible with struct Foo *.
GCC _doesn't_ complain about about such assignments, in fact it
doesn't even warn about them.

This means that the compiler will generated code that can fail with an
alignment fault on some architectures.  Specifically, this will fail
at run time on anything that traps on misalignment:

	short s = 1;
	int x __attribute__((aligned(2))) = 2;
	int * px = &x;

	int main() {
		return *px;
	}

Unfortunately I noticed that just "return x" will also do misaligned
accesses on every architecture I tried, including ARM2 where it will
return the wrong value.  I thought GCC was supposed to open code
accesses to low alignments properly?

-- Jamie


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]