[net.lang.c] Yet another ALIGN idea.

flaps@utcs.UUCP (06/29/86)

How about an #include <align.h> which contains a single #define which gives the
alignment constant for this machine?

This of course would only be useful if put into the standard.

Which reminds me, is sizeof(char) always = 1 yet?  I mean, is it guaranteed?

ajr

gwyn@brl-smoke.ARPA (Doug Gwyn ) (06/30/86)

In article <1986Jun29.15:39:38.4762@utcs.uucp> flaps@utcs.uucp (Alan J Rosenthal) writes:
>Which reminds me, is sizeof(char) always = 1 yet?  I mean, is it guaranteed?

sizeof(char) is indeed supposed to be 1 as things now stand.
However, this may need to be reconsidered.  The meaning of
"char" is currently overloaded, which doesn't much matter
until one gets into multi-byte character representations.
I'm hoping to work up a complete discussion and a number
of specific proposals in a paper for X3J11 as soon as I can
find the time.

levy@ttrdc.UUCP (Daniel R. Levy) (07/06/86)

In article 38.4762@utcs.uucp>, flaps@utcs.UUCP writes:
>How about an #include <align.h> which contains a single #define which gives the
>alignment constant for this machine?
>This of course would only be useful if put into the standard.
>Which reminds me, is sizeof(char) always = 1 yet?  I mean, is it guaranteed?
>ajr

Say if ajr's suggestion were implemented and /usr/include/align.h contained
something like

#define ALIGNSIZE 4	/* long int */

Now, given an arbitrary char* pointer, how do we tell if it is aligned?
Now on a machine with four-byte pointers and unsigned longs, we could do:

	char *c;
	...
	if (((unsigned long)c % ALIGNSIZE) == 0) {
		/* c is aligned */
		...
	} else {
		/* c is not aligned */
		...
	}

But... what about a machine where alignments are "screwy," for example they
wanted doubles to be located at some fixed offset, say 2, from addresses which
are integral multiples of 4 or 8?  I can think of no such machine (indeed I
believe design of such would be an exercise in masochism) but where is it
engraved in stone what "aligned" really means (which we presume must imply
some guarantee of solid mapping between pointers and some other data type,
usually long... I used unsigned long in my example because it would work
on a machine with one's complement arithmetic and pointers with their
MSB set, whereas a bit-pattern copy to a long would produce a number which
could not be reliably tested for alignment--but again we hope that the
mapping instruction would be smarter than a bit-pattern copy).
-- 
 -------------------------------    Disclaimer:  The views contained herein are
|       dan levy | yvel nad      |  my own and are not at all those of my em-
|         an engihacker @        |  ployer or the administrator of any computer
| at&t computer systems division |  upon which I may hack.
|        skokie, illinois        |
 --------------------------------   Path: ..!{akgua,homxb,ihnp4,ltuxa,mvuxa,
						vax135}!ttrdc!ttrda!levy

flaps@utcs.UUCP (07/09/86)

In article <1038@ttrdc.UUCP> levy@ttrdc.UUCP (Daniel R. Levy) writes:
>Say if ajr's suggestion were implemented and /usr/include/align.h contained
>something like
>
>#define ALIGNSIZE 4	/* long int */
>
>Now, given an arbitrary char* pointer, how do we tell if it is aligned?

[provides the simple case]

>But... what about a machine where alignments are "screwy," for example they
>wanted doubles to be located at some fixed offset, say 2, from addresses which
>are integral multiples of 4 or 8?

#define OFFSET 2
#define ISALIGNED(p)((p)%4 == 2)


or of course in the usual case:

#define ALIGNSIZE 4
#define OFFSET 0
#define ISALIGNED(p)((p)%4 == 0)

Of course this could be expanded for any other information that a user needed
to know about the alignment.  And since it would all be in an include file, it
would not break any existing programs, and is not truly an addition to the
language.  I think it would be good to put it in the standard.

ajr

aglew@ccvaxa.UUCP (07/09/86)

An alignment constant would not necessarily work, because there are machines
that have unusual alignment restrictions. My favorite is `strictly aligned
power of two sized' - ie. an object of size N must be aligned on a boundary
of 2^ceil(lg(N)), if indexing is to be used to access fields of such an
object.

    Note that malloc(nitems*sizeof(obj)) is conservative for such a beast,
assuming sizeof rounds up correctly, but calloc() can be a bit more
intelligent.

This scheme is strictly size dependent, but screwier schemes are possible.

Since an alignment constant doesn't necessarily work, how about an
alignmentof(object) operator, analagous to sizeof()? At least that'll work
on all architectures that have alignments based on multiples.

Andy "Krazy" Glew. Gould CSD-Urbana.    USEnet:  ihnp4!uiucdcs!ccvaxa!aglew
1101 E. University, Urbana, IL 61801    ARPAnet: aglew@gswd-vms

rbutterworth@watmath.UUCP (Ray Butterworth) (07/11/86)

> Since an alignment constant doesn't necessarily work, how about an
> alignmentof(object) operator, analagous to sizeof()? At least that'll work
> on all architectures that have alignments based on multiples.

An "alignof(type)" operator is already required by X3J11 (although
they don't seem to have realized this yet) in order to implement
their va_arg(ap,type) macro.

chris@umcp-cs.UUCP (07/12/86)

In article <2600066@ccvaxa> aglew@ccvaxa.UUCP writes:
>Since an alignment constant doesn't necessarily work, how about an
>alignmentof(object) operator, analagous to sizeof()? At least that'll work
>on all architectures that have alignments based on multiples.

I think this can still be implemented with a common header file, e.g.,

	#define OFFSET_TO_ALIGN(ptr)	/*something*/
	#define	IS_ALIGNED(ptr)	(OFFSET_TO_ALIGN(ptr) == 0)

On a Vax, and purely for efficiency, /*something*/ is

	#define	OFFSET_TO_ALIGN(ptr)	((4 - (int) (ptr)) & 3)

On a Pyramid, the same `define' works; on a Sun, it can be reduced to

	#define	OFFSET_TO_ALIGN(ptr)	((int) (ptr) & 1)

On your hypothetical machine, /*something*/ may be

	#define	OFFSET_TO_ALIGN(ptr)	(_offset_to_align(sizeof *(ptr)))

where _offset_to_align is a library function.

(N.B.: That it *can* be done this way does not mean that it *must*
be done this way.)
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 1516)
UUCP:	seismo!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris@mimsy.umd.edu

gwyn@BRL.ARPA (07/12/86)

You say that an alignof(type) operator is required in order to
implement the va_arg(ap,type) macro.  Could you please demonstrate
this?  I don't believe it.

aglew@ccvaxa.UUCP (07/13/86)

... > Chris Torek responds to my alignof() proposal (the shorter name is
... > better):

>I think this can still be implemented with a common header file, e.g.,
>
>	#define OFFSET_TO_ALIGN(ptr)	/*something*/
>	#define	IS_ALIGNED(ptr)	(OFFSET_TO_ALIGN(ptr) == 0)
>
>On your hypothetical [strictly aligned, power of two sized]
>machine, /*something*/ may be
>
>	#define	OFFSET_TO_ALIGN(ptr)	(_offset_to_align(sizeof *(ptr)))
>
>where _offset_to_align is a library function.

He's right - I don't care if something gets stuck in the language or in a
header file - but his macros have some weaknesses. (1) They can only be 
used to determine if a pointer is correctly aligned, not to generate a 
correctly aligned pointer. Ie. given an arena from which your malloc()
draws stuff, do you run through every hole looking for IS_ALIGNED(p) where
p is what you're allocating now? And what if you have a block beginning on
a misaligned address - do you run through every byte in the block looking
for a place to start?

(2) On my hypothetical, strictly aligned, power of two sized, machine using
sizeof as the sole criterion can lead to some inefficiencies. For example,
a character string of length 2n+1 need not be allocated on a 4n boundary,
if you know that indexing is never used into the string, or if you are
willing to have indexing be slow (through explicit pointer calculations)
when it is. So you don't need to waste those 2n-1 bytes. However, since
the fields of a structure of size 2n+1 are very frequently indexed, then you
are willing to waste the memory.

---

The discussion is useful, though. We can see two needs:

[A] a predicate IS_ALIGNED(ptr,type) which returns true or false if
    the ptr is correctly aligned for an object of type.

    The predicate can handle any machine's alignment restrictions, including
    multiple plus an offset type constraints (not my idea). On most machines
    it can be implemented by a macro in a header, something like

    #define IS_ALIGNED(ptr,type) ( sizeof(type) == 1 ? 1 \
				 : sizeof(type) == 2 ? ptr & 1 \
				 : ... \
				 : !(ptr & ((1<<(ALIGN-1))-1)) )

    but Ooops! note that even on a VAX struct {char a,b;} doesn't HAVE
    to be aligned like this.

[B] Constants that can be used in writing nearly machine independent
    memory allocators.

    For a large class of machines this can most simply be done by providing
    the sizeof() the largest data type that has alignment restrictions, eg.
	
    #define ALIGN sizeof(double)

    However, that doesn't cover my favorite architecture, strictly aligned 
    power of two sized; but a macro generating the alignment constant can
    handle all architectures with alignment constraints based on multiples
    without offsets

    #define ALIGN(type)	(1<<ceil_log(sizeof(type))

    where ceil_log would have to be a function executable by the
    preprocessor.

Of course, you can say "why bother going that extra little step just for one
of Krazy Glew's hypothetical architectures..." Well (1) Even on conventional
machines that have a single upper bound on alignment restrictions,
ALIGN(type) might permit more efficient memory allocators, particularly if
the upper bound on alignments is large. It may only be a 64 bit double now,
but it is quite reasonable to see it becoming 128 bits extended precision
floating point numbers, or even 256 bits, in the near future.

(2) Such an architecture may not be hypothetical much longer... I hope,
and I have reasons for hoping.

(3) Such an architecture already exists! Surprised? I've heard of somebody
who modified a 68000 compiler to use shifting and oring instead of mutiplying
and adding to access fields of structures. Hai presto! a strictly aligned
power of two sized system. And it ran faster than conventional code. Since
memory keeps getting cheaper...

Andy "Krazy" Glew. Gould CSD-Urbana.    USEnet:  ihnp4!uiucdcs!ccvaxa!aglew
1101 E. University, Urbana, IL 61801    ARPAnet: aglew@gswd-vms

rbutterworth%watmath.waterloo.edu@CSNET-RELAY.ARPA (07/14/86)

>  From seismo!BRL.ARPA!gwyn  Sat Jul 12 05:07:56 1986
>  You say that an alignof(type) operator is required in order to
>  implement the va_arg(ap,type) macro.  Could you please demonstrate
>  this?  I don't believe it.

Suppose we have a machine where
sizeof(double)==8,  sizeof(long)=8,  sizeof(int)==4,
alignof(double)==8, alignof(long)=4  alignof(int)==4.

What does the macro va_arg(list,long) expand to?
The token "long" can't be concatenated to form a predefined token
such as "align_long" since it could just as easily have been "char *".
So the only information we can really determine about the argument 
is by using sizeof(long).  But this is simply 4 in this case.  We
still don't know where on the stack the argument will be.  If it is
a long (which we can't determine) it is at the next 4-byte boundary,
but if it is a double (which it could just as easily be, given only
its size) it is at the next 8-byte boundary.

How is va_arg supposed to differentiate between these two cases?
I didn't see anything in the standard restricting how things must
be passed in the stack (e.g. all arguments must be aligned the same
way (as in VAX BSD)).  If there isn't an alignof() operator, some
other built-in would be needed (e.g. newptr=align(oldptr,type)).

Another example would be the case of structures.  There is nothing
that says that "struct{char a; char b;}" has to be aligned the same
as "struct{double c;}", and I certainly don't see any way of making
va_arg determine the alignment without a built-in alignof() operator.

With a very strict reading of the standard one might decide that
the "..." arguments can only be of type int, unsigned int, or double.
That would certainly make my above examples invalid, but it would
also make <stdarg.h> much less useful.  If so, the standard could
make this much clearer by changing
    The parameter _^Ht_^Hy_^Hp_^He is a type name such that the type
    of a pointer to an object that has the specified type can be
    obtained simply by postfixing a * to the type name.
to
    The parameter _^Ht_^Hy_^Hp_^He must be "int", "unsigned int"
    or "double".
or better yet, by replacing va_arg by three macros, va_int, va_unsigned,
and va_double.  I really don't think this was what was intended though.

gwyn@BRL.ARPA (07/14/86)

Who says stack alignment has to be the same as machine-forced
alignment?  It's certainly not on some of the machines I use.

Since the parameter stack can have different alignment constraints,
one would actually need a stkalignof operator for <stdarg.h>, but
it would be of use only for this one purpose and therefore should
not be intruded upon the programmer's notice.

It's true that it would be easier to implement <stdarg.h> on some
machines if the alignof operator were available, but it's not
required.  Indeed, since the compiler can tell that variable-
parameter functions are involved, it may choose to use what we
old-timers call "dope vectors" to describe the variable args.

On some unusual architectures, actual library functions may have
to be used in the implementation of va_arg() (which itself is a
macro).

There was a motion last meeting to drop parmN, but it was defeated;
however, we decided to make "register" parmN an undefined situation.

Although I can't recall anything that requires all structs to
be aligned as stringently as the worst possible scalar data type,
implementations may elect to do so, or to make special arrangements
so that the size and alignment of very small structs are convenient
for <stdarg.h> (besides having to include padding so that arrays of
structs work out right).

The reference in the draft standard to int, unsigned int, and
double was with respect to possible result of parameter widening
promotions; it wasn't meant to rule out other parameter types.