From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 32154 invoked by alias); 21 Apr 2003 19:44:59 -0000 Mailing-List: contact gcc-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Archive: List-Post: List-Help: Sender: gcc-owner@gcc.gnu.org Received: (qmail 32142 invoked from network); 21 Apr 2003 19:44:59 -0000 Received: from unknown (HELO mail.jlokier.co.uk) (81.29.64.88) by sources.redhat.com with SMTP; 21 Apr 2003 19:44:59 -0000 Received: from mail.jlokier.co.uk (localhost [127.0.0.1]) by mail.jlokier.co.uk (8.12.8/8.12.8) with ESMTP id h3LJiwjF016846; Mon, 21 Apr 2003 20:44:58 +0100 Received: (from jamie@localhost) by mail.jlokier.co.uk (8.12.8/8.12.8/Submit) id h3LJiwx9016844; Mon, 21 Apr 2003 20:44:58 +0100 Date: Mon, 21 Apr 2003 20:33:00 -0000 From: Jamie Lokier To: Richard Kenner Cc: gcc-patches@gcc.gnu.org, gcc@gcc.gnu.org Subject: Re: DATA_ALIGNMENT vs. DECL_USER_ALIGNMENT Message-ID: <20030421194457.GA16255@mail.jlokier.co.uk> References: <10304211744.AA00185@vlsi1.ultra.nyu.edu> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <10304211744.AA00185@vlsi1.ultra.nyu.edu> User-Agent: Mutt/1.4.1i X-SW-Source: 2003-04/txt/msg01005.txt.bz2 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. Oh, I see why you mustn't increase alignment on types! Thanks! 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