Version 1.1 of acs/ac-00091.txt

Unformatted version of acs/ac-00091.txt version 1.1
Other versions for file acs/ac-00091.txt

!standard 13.03(23)          04-02-05 AC95-00091/01
!class amendment 03-12-12
!status received no action 03-12-12
!status received 03-12-12
!subject Some gripe about Alignment
!summary
!appendix

From: Michael F. Yoder
Sent: Friday, December 12, 2003 12:09 PM

This is as good a place as any to insert my own (minor) "Annex J" gripe.

I've wished for some time that "at mod" clauses would be taken out of
the annex, but not with their current semantics.

The clause "at mod n" is interpreted to mean that the record type has
alignment n.  This is a redundant feature, and deserves to be called
obsolescent.

However, up until Ada 95 was defined, I always thought the "at mod n"
clause specified that the record type had alignment that was a
*multiple* of n, rather than specifying n exactly.  This is a genuinely
useful feature.  It can be emulated by adding a zero-sized dummy field
with alignment n, but this is ugly.  (And, in light of the debate about
zero-sized objects and access values, the field might end up taking up
space after all.)

The case that comes to mind where I last wanted this looked something
like this:

    type R is record at mod 2;
             Data: T; -- a generic private type
     end record;
    type AR is access R;

The point was to insure that the addresses were multiples of 2 so that
the low-order bit of values of type AR could be used in atomic-swap
algorithms as a lock bit.  The "multiple of 2" semantics is needed so
that the type T can have any alignment; the actual alignment of type R
will be the least common multiple of 2 and the alignment of T.

****************************************************************

From: Robert Dewar
Sent: Friday, December 12, 2003  3:30 PM

Sorry, I am confused,

for X'Alignment use 2;

precisely means that objects of X have an alignment that is a multiple
(any multiple) of two bytes.

Perhaps I am missing something here???

****************************************************************

From: Robert A. Duff
Sent: Friday, December 12, 2003  3:56 PM

That was my initial reaction.  But, yes, I think you're missing
something (as I was, initially).

    for X'Alignment use 2;

means two things:

    - If the compiler allocates an object of type X, it must choose an
      address that is a multiple of 2.

    - If the compiler generates code to refer to an object of type X,
      it can assume that X'Address is a multiple of 2, but it cannot
      assume more.

The second point is only interesting for objects whose allocation is not
under control of the compiler.  For example, an object imported from
another language.  When loading an object of type X (or components
thereof) into a register, the compiler must ensure that the code won't
crash when the address is not a multiple of 4, if the object was
allocated by C code or whatever.

Another example is user-defined storage pools.  If the pool returns
address 1_000_006, the generated code has to work.  (Presuming the
compiler didn't reject the Alignment clause, of course, which I suspect
it would do if the target machine can't easily handle that alignment for
that type.)

What Mike Yoder is asking for is a way to say "Please make the alignment
at least 2, but 4 or 8 is OK, too."  It would imply that the compiler
can assume 4, if it likes, and then a user-defined storage pool would
have to use 4, not 2.

****************************************************************

From: Robert A. Duff
Sent: Friday, December 12, 2003  4:10 PM

> Sorry, I am confused,

I tried to alleviate that confusion in my previous message. ;-)

But now I'd like to say:
I'm surprised by Mike's interpretation of Ada 83.  I believe I'm
responsible for writing the Ada 95 alignment rules, and I honestly
thought I was transcribing *exactly* the Ada 83 rules, except that
the syntax changed so it wasn't restricted to record types, but was
allowed for all kinds of types.  I truly believed that "for T'Alignment
use ..." meant exactly the same thing (for record types) as the "at mod"
syntax (which was only allowed for record types).

Does anybody have an opinion as to what the Ada 83 syntax meant?

I (now) agree with Mike that "This is a genuinely useful feature."
I wouldn't restrict it to records, either.  I would call it,
"for T'Min_Alignment use...".

****************************************************************

From: Robert Dewar
Sent: Friday, December 12, 2003  5:36 PM

>     - If the compiler generates code to refer to an object of type X,
>       it can assume that X'Address is a multiple of 2, but it cannot
>       assume more.

OK, I got it. Well my reaction is this is may be a layer of hard to
understand complication to an already complex notion (alignment) that
many users have a lot of difficulties with.

I think it is certainly out of the question to modify the semantics of
the existing syntactic forms (which definitely mean the same thing, and
indeed provide a two-way guarantee).

But let's get into positive mode and make a suggestion.

In GNAT, we have an attribute Object_Size that gives the size to be
allocated for objects of the type (as opposed to the Value_Size which
is the size of the type, used in unchecked allocation and packing).

So, by analogy

    for X'Object_Alignment use bla;

This means that objects of this type that are allocated by the compiler
should get this alignment where possible unless overridden.

Value_Alignment then refers to the guaranteed alignment that the
compiler can assume for all values of the type.

Example:

    type R is array (1 .. 8) of Character;
    for R'Value_Alignment use 1;
    for R'Object_Alignment use 8;

Now the compiler can handle any alignment for values of this type
(e.g. values that come from unchecked conversion of arbitrary
addresses), but if it allocates a value of this type, they are
allocated on an 8-byte boundary.

A normal definition of 'Alignment would set both alignment
values.

By the way is the ARG considering adopting Value_Size and
Object_Size? We find these very useful in the GNAT environment.

****************************************************************

From: Robert Dewar
Sent: Friday, December 12, 2003  5:44 PM

> I'm surprised by Mike's interpretation of Ada 83.

I think there is no authority at all for Mike to consider that
the alignment clause in Ada 83 meant anything different. As one
of the people most involved in understanding and testing the
chapter 13 requirements I can tell you that I have never heard
this interpretation before, and certainly Ada 83 compilers
used the value in the alignment clause to align data.

****************************************************************

From: Michael F. Yoder
Sent: Friday, December 12, 2003  6:21 PM

> I think there is no authority at all for Mike to consider that
> the alignment clause in Ada 83 meant anything different. [snip]

This is amusing, because I derived it from what has come to be called
Dewar's rule, namely that the language doesn't require silly things; it
was my belief (while I was an Ada 83 implementer) that the alternative
interpretation was obviously wrong.  The alignment of a record must (in
most implementations) be a multiple of the LCM of its components'
alignments, and those aren't always known at compile time (if, e.g.,
they are of generic private types).

But let's follow through with this logic.  Suppose I do this:

    generic
          type T is private;
     package G is
           type R is record
               C: T;
           end record;
           for R'Alignment use 2;
     end package G;

If I now instantiate G with an actual type whose alignment is 4, is it
an illegal instantiation?  Or does the type R in the instance become
alignment 4?  Or is there another choice?

****************************************************************

From: Robert Dewar
Sent: Friday, December 12, 2003  6:57 PM

Michael F. Yoder wrote:

>>>>> However, up until Ada 95 was defined, I always thought the "at mod
>>>>> n" clause specified that the record type had alignment that was a
>>>>> *multiple* of n, rather than specifying n exactly.  This is a
>>>>> genuinely useful feature.

The importance of this is that both interpretations are compatible with
the above language. That's why I needed Bob's intervention to understand
your point. Merely saying the above is not sufficient, because of course
when you say

    for X'Alignment use 4;

You precisely mean that the alignment of objects of type X must be
divisible by 4 (it can of course also be divisible by 16 or 4096).

I still an not clear what you expect here.

If you are wondering about a compiler that does the following:

    type X is new Long_Long_Integer;   -- 8 bytes, default alignment 8
    for X'Alignment use 4;

    but the compiler still, allocates all objects of type X on a boundary
    of 8.

Well that of course is 100% compatible with the RM.

I think to make your point clear, you need to write a program which
would be illegal under your interpretation and legal under the
interpretation you think is implied in Ada 95 (since I don't know
how they differ, I don't know what I mean here), or vice versa.

> This is amusing, because I derived it from what has come to be called
> Dewar's rule, namely that the language doesn't require silly things; it
> was my belief (while I was an Ada 83 implementer) that the alternative
> interpretation was obviously wrong.  The alignment of a record must (in
> most implementations) be a multiple of the LCM of its components'
> alignments, and those aren't always known at compile time (if, e.g.,
> they are of generic private types).

No, it is perfectly fine for the compiler to allow the default
alignment to be overridden. Let's consider this

	procedure k is
    	   type r is new integer range 1 .. 10;
	   for r'alignment use 4;

	   type s is record
	      r1 : r;
	   end record;

	   for s'alignment use 2;
	begin
	   null;
	end k;

Now the compiler does not have to accept this (and nothing in the
Ada 83 or Ada 95 RM requires this to be accepted), but on the other
hand, it is perfectly fine for the compiler to accept it.

GNAT takes the view that it will not allow this in the default
case, but if the record s is repped:

	procedure k is
    	   type r is new integer range 1 .. 10;
	   for r'alignment use 4;

	   type s is record
	      r1 : r;
	   end record;

	   for s use record
	     r1 at 0 range 0 .. 31;
	   end record;

	   for s'alignment use 2;
	begin
	   null;
	end k;

Now GNAT will accept this version of the program.
>
> But let's follow through with this logic.  Suppose I do this:
>
>    generic
>          type T is private;
>     package G is
>           type R is record
>               C: T;
>           end record;
>           for R'Alignment use 2;
>     end package G;
>
> If I now instantiate G with an actual type whose alignment is 4, is it
> an illegal instantiation?  Or does the type R in the instance become
> alignment 4?  Or is there another choice?

The compiler may or may not allow this. There is nothing in either the
Ada 83 or Ada 95 RM that either prohibits or requires the compiler
to accept such an instantiation.

So, Mike, the challenge remains. Please show a program which shows
the difference in terms of required behavior. I can't see one and
it seems to me that compilers are free to behave either way, so
making some surgery in the language in the direction you suggest
has precisely zero effect on the set of programs required to be
accepted, and on the set of programs required to be rejected.

Remember that you can never make statements about the choice of
default alignments. It is fine for instance for compilers to
make the default alignment depend on whether the variable name
contains the letter Z. Bizarre, but conforming.

I think what may be happening is that you are thinking something
like: an alignment clause does not change the default alignment
of allocated objects. But such a statement is meaningless since
the default alignment is non-deterministic.

However, take a look at my proposal. This I think captures what
you have in mind???

****************************************************************

From: Michael F. Yoder
Sent: Saturday, December 13, 2003 10:45 AM

>I (now) agree with Mike that "This is a genuinely useful feature."
>I wouldn't restrict it to records, either.  I would call it,
>"for T'Min_Alignment use...".

This idea is superior to what I implicitly suggested.  (Namely, to
resurrect "at mod" clauses with what I thought were the Ada 83
semantics.)  So, I retract my gripe, and am content to see them left in
Annex J.  Instead, I'll briefly plug Bob's suggestion: to the best of my
recollection I've *always* wished I had the "min alignment" semantics
when giving alignments, and I've never wanted to specify alignment exactly.

****************************************************************

From: Robert Dewar
Sent: Saturday, December 13, 2003 11:44 AM

Once again, this is confused, since the current Alignment attribute is
precisely a minimum alignment attribute. Try formalizing this proposal
and I bet it does not work!

****************************************************************

From: Michael F. Yoder
Sent: Sunday, December 14, 2003 11:45 AM

You are confusing alignment of objects with the value of the 'Alignment
attribute, unless you are asserting that if I specify

    for T'Alignment use 4;

it is possible for T'Alignment to become something other than 4.

Yes, T'Alignment specifies the min alignment of objects of type T.  That
isn't what is at issue here.  What is wanted is the ability to say
"T'Alignment must be a multiple of n, but I don't need it to be exactly
n."  This is most useful when T is a composite type with components of
generic formal types whose alignments are unknown at compile time, but
it can also be useful for maintenance reasons.  One might wish
R'Alignment to be the LCM of 2 and whatever alignments its components
happen to have; this is easily done by saying

   for R'Min_Alignment use 2;

and this will remain the right rep spec when highly aligned components
are added or deleted during maintenance.

****************************************************************

From: Robert Dewar
Sent: Sunday, December 14, 2003 12:24 PM

> Yes, T'Alignment specifies the min alignment of objects of type T.  That
> isn't what is at issue here.  What is wanted is the ability to say
> "T'Alignment must be a multiple of n, but I don't need it to be exactly
> n."

But specifying an alignment of n *MEANS* that the address is a multiple
of N. The idea of alignment being *exactly* N is odd. Now I think what
you really mean is that you want the compiler to be able to use a larger
alignment for all its allocations etc. Well that's fine, it is free to
do this if it wants right now.

> This is most useful when T is a composite type with components of
> generic formal types whose alignments are unknown at compile time, but
> it can also be useful for maintenance reasons.  One might wish
> R'Alignment to be the LCM of 2 and whatever alignments its components
> happen to have; this is easily done by saying
>
>   for R'Min_Alignment use 2;
>
> and this will remain the right rep spec when highly aligned components
> are added or deleted during maintenance.

But your Min_Alignment has EXACTLY the same semantics in terms of
programs that are required to be accepted and required to be rejected
as the current Alignment semantics.

Please show a program where you could tell the difference!

The program you showed before was one that might or might not be
accepted by the compiler with either possible semantics, and it is
my contention that there are no programs whose legality depends on
the difference.

A compiler is allowed, but not required, to use higher alignments
for all allocated objects of the type in either case.

****************************************************************

From: Tucker Taft
Sent: Sunday, December 14, 2003  6:02 PM

As Bob explained, the only place where it would matter
is if the object is allocated outside of the control
of the compiler, and it was not aligned any more than
the specified alignment.  E.g., for an imported object,
or an address to access conversion.  The RM says the
compiler may not presume it is more aligned than T'Alignment,
and hence cannot use instructions that presume more
alignment than that on objects that might have been allocated
outside of its control.  For example, if you have a user-defined
storage pool, and the allocation routine is passed the alignment,
then it doesn't have to allocate storage any more aligned than
specified.

A rather kludgey workaround for this is as follows:

   type Underlying is record
       X : Whatever;
       Y : Whoever;
   end record;

   type T is new Underlying;
   for T'Alignment use Integer'Max(Underlying'Alignment, 2);

This will give T the max of the "natural" alignment
of Underlying and 2, which would accomplish your goal if you
can be certain that the natural alignment is always
a power of 2.  If your machine was truly bizarre, then
you would need some kind of static least-common-multiple
function, but I think the number of machines that have
non-power-of-2 alignments can be counted on the fingers
of zero hands.  (Note that I am aware of machines whose
word size is not a multiple of 2, and I am aware of machines
that have 3-word floating point types, but none that have
alignment requirements in storage units that are not
a power of 2.)

****************************************************************

From: Robert Dewar
Sent: Monday, December 15, 2003 12:47 AM

There is nothing to stop the compiler from allocating things
more aligned.

> A rather kludgey workaround for this is as follows:
>
>    type Underlying is record
>        X : Whatever;
>        Y : Whoever;
>    end record;
>
>    type T is new Underlying;
>    for T'Alignment use Integer'Max(Underlying'Alignment, 2);

But there is no requirement that the compiler give any particular
alignment for Underlying, so this does not achieve any guaranteed
effect.

You are trying to talk about the "natural" (or I suppose) default
alignment of things, but the RM has nothing to say about that.

I would like to see this formalized, I think you are getting
confused between formal language semantics and some informal
pragmatic notion of what you expect.

****************************************************************

From: Michael F. Yoder
Sent: Monday, December 15, 2003 10:09 PM

> But specifying an alignment of n *MEANS* that the address is a
> multiple of N. The idea of alignment being *exactly* N is odd. Now I
> think what
> you really mean is that you want the compiler to be able to use a larger
> alignment for all its allocations etc. Well that's fine, it is free to
> do this if it wants right now.

This is simply incorrect.  The idea of alignment being exactly n is odd
for objects, but not for types.  For types, T'Alignment being exactly n
means precisely that T'Alignment = n.  This generally means two things:
first, that objects must be allocated at an address that's a multiple of
n.  Second, that the compiler can't assume a higher alignment than this
when generating code for, e.g., parameters of type T.

What I want isn't that the compiler is *allowed* to use a larger
alignment, I want it to be *required* to use a larger alignment when
needed.  Specifically, I want the least alignment that is a multiple of
my specified minimum alignment *and* the alignment of any component
types.  (Said multiple may be equal to n, but it may not be.)

Here's a contrived example to illustrate the point.  Suppose the storage
unit is 8 bits.

   type TM is mod 2**32;
   for TM'Size use 32;
   for TM'Alignment use 4;

   type R is record
       Modulus: TM;
   end record;
   for R'Size use 32;
   for R'Alignment use 2;

I would expect a compiler to reject the declaration of R as illegal,
since an allocator could create an object of type R that was at an
address congruent to 2 modulo 4, and this would mean its component
Modulus wasn't aligned modulo 4.

If the alignment clause were "for R'Min_Alignment use 2;" the
declaration would be fine, and R'Alignment would become 4.

The problem from a maintenance point of view is that using exact
specification of alignments (rather than the min alignment semantics)
requires that the programmer who writes the alignment clause for R be
aware of the alignments of all component types, and to do a LCM.  Here,
since TM is a static type, it's possible to write

   for R'Alignment use Integer'Max (2, TM'Alignment);

But this isn't always available.  And if TM were a generic formal type
there would be no way to express what's wanted.

****************************************************************

From: Robert A. Duff
Sent: Monday, December 15, 2003 12:09 PM

> There is nothing to stop the compiler from allocating things
> more aligned.

But the issue is when the compiler is not allocating -- user-written
code is, or a foreign language.

Let me add some chapter-and-verse to Mike Yoder's example:

>    type TM is mod 2**32;
>    for TM'Size use 32;
>    for TM'Alignment use 4;
>
>    type R is record
>        Modulus: TM;
>    end record;
>    for R'Size use 32;
>    for R'Alignment use 2;

    type A is access all R;
    for A'Storage_Pool use My_Pool;

    procedure P(X: in out A) is
    begin
        X.all.Modulus := X.all.Modulus + 1; -- (*)
    end P;

    P(new R'(Modulus => 123));
      -- Calls Allocate(My_Pool, Alignment => 2, ...)

RM-13.1(17) requires that R'Alignment = 2.  The compiler is not allowed
to return 4.

13.11(16) requires the compiler to pass 2 as the Alignment parameter to
the Allocate procedure.  The compiler is not allowed to pass 4.

The (user-written) Allocate procedure may then return an address that is
divisible by 2 but not by 4.  The code at (*) is then *required* to work
when X.Modulus'Address is not divisible by 4; it cannot use load/store
instructions that will fail in that case.

The compiler could instead make "for R'Alignment use 2;" illegal.
But it cannot accept this representation clause, and then pass
Alignment => 4 to Allocate.

The same issue arises with imported objects:

    Imp: aliased R;
    pragma Import(C, Imp);

    P(Imp'Access);

If the C compiler allocates Imp at an address not divisible by 4,
then the code at (*) is required to work (unless the Ada compiler
rejects the "for R'Alignment use 2;").

****************************************************************

From: Robert Dewar
Sent: Tuesday, December 16, 2003  9:55 AM

OK, so the issue really revolves around the argument
given to the storage pool. In fact what I would like
to see here is that the alignment for objects can be
greater than the alignment for types. This has a
perfect analogy for sizes.

The alignment for a type gives the guarantee that all
objects of the type have at least this alignment, just
as the size for a type gives the guarantee that all
objects of this type have at least this size.

Just as we expect the compiler to give larger sizes
for objects for the sake of efficiency, we can expect
a compiler to give larger alignments to objects for
efficiency purposes.

Now in Ada 95, there is no control over object sizes
(though in GNAT we have added 'Object_Size), and so
currently there would be no control over object
alignments. A feature can certainly be added.

A note here is that I had not particularly concentrated
on storage pools since we have not seen that feature
used at all (except for our special debug storage pool).
I would be interested in hearing of real applications
using this feature. One problem that stands in the way
of general use is that there is no convenient way to
specify a default storage_pool usage, and it is not
easy to come up with one.

One thing that is an important principle to me is that
if a user does not specify an alignment for a type, then
they have no right to assume anything about alignment of
objects of the type.

So why do users specify alignments at all? That's a
question that is useful to answer in understanding the
best approach here.

Most often it is to ensure some compatibility between
different data in some kind of unchecked conversion
situation. Here most certainly the issue is minimum
guarantees.

Sometimes it is for time efficiency purposes, but that's
almost always unreasonable, since to me, the compiler
should be doing that choice, and should know FAR more
than the user can about alignment requirements of the
target machine.

Sometimes it is for space efficiency purposes, where
we dealign things to allow more packing. That's a more
murky area, and one where indeed the issue is about
allocated object alignment.

****************************************************************

From: Robert A. Duff
Sent: Tuesday, December 16, 2003  1:42 PM

Robert wrote:

> A note here is that I had not particularly concentrated
> on storage pools since we have not seen that feature
> used at all (except for our special debug storage pool).
> I would be interested in hearing of real applications
> using this feature.

We use storage pools heavily in our current project at SofCheck.

I know you think ARG members aren't "real" Ada programmers.  ;-)
But this is a real Ada program, depite having been written in
part by ARG members.

We have a coding convention: every access type must have either a
Storage_Pool clause, or "for T'Storage_Size use 0;".  Or a comment
explaining why the coding convention is not followed.

The reason for this convention is that the program is a long-running
server sort of thing, where storage leaks and other memory management
issues are very important, so we want to have explicit control of
storage management, visible in the source code.

I've seen embedded systems where the goal was to segregate different
types of data into different pools.  E.g., have enough space for up
to 100 radar-tracked planes, and up to 500 gizmos, and up to 200 mumbles.
It would be more memory-efficient (on average) to put all those things
in the global heap, but it makes analysis much harder.  If the
requirements say we have to be able to deal with 500 gizmos,
no matter what, then we don't want 3000 mumbles using up the memory
reserved for gizmos.

>... One problem that stands in the way
> of general use is that there is no convenient way to
> specify a default storage_pool usage, and it is not
> easy to come up with one.

I'm not sure what you mean.  Do you mean a way for the user to say "all
access types should use My_Pool, unless otherwise specified"?
Given the convention mentioned above, I would like a way to say
that the default is "for T'Storage_Size use 0;", so I can get
a warning when an allocator exists for an access type that wasn't
intended for allocation.  If you use class-wide types, that happens a
lot.

Another problem with the feature is that there's no way to specify the
pool dynamically, at the site of the allocator -- it has to be
determined at the access type.  We have some kludgery to get around that
limitation.

> So why do users specify alignments at all?

I don't know.

There are exactly two Alignment clauses in our project.  They are both
inside one of our Storage_Pool packages.  The package allocates hunks of
memory, and Allocate allocates small objects within those.  It wants the
hunks aligned to the maximum possible alignment, so that Allocate can
easily and efficiently produce an aligned address.

In a couple of cases, I wanted very large alignments, but I was unable
to use Alignment clauses, because the compilers being used didn't
support alignments above 4 or 8 or something.  Example: page-align
data structures, so I can use operating system services (on Unix and
Windows) to make them read-only and whatnot.  I ended up going outside
the language to get the alignments I wanted.

I think Mike Yoder wanted to specify "Alignment use 2" because he wanted
to steal the low-order bit of addresses for some sort of flag.

****************************************************************


Questions? Ask the ACAA Technical Agent