[comp.arch] RISC data alignment

gjo@calmasd.GE.COM (Glenn Olander) (01/23/88)

Please forgive a possibly neophyte-type question, but is it true that
there may be an inherent incompatibility between RISC and conventional
machines?  In particular, I believe that many RISC machines require data
to be aligned on a natural boundary, e.g. longwords must be referenced
on a 4-byte boundary.  This requires compilers to make accomodations to
ensure that such alignment always occurs, even if it means padding a
data structure which contains mixed types of data, for example a C
structure with a mixture of shorts, longs, and doubles.

If this is true, then it would seem to also be true that a C structure
could have different lengths, depending on whether it was compiled 
on a RISC or non-RISC machine.  Further, it would seem that
if that C structure were written out to a file, it could only be read
properly by a machine of the same type as that which wrote it.

Does such incompatibilty truly exist?  If I create a file on a Sun/4
will I be able to read it on a Sun/3?

Glenn Olander
GE Calma
gjo%calmasd.ge.com@sdcsvax.ucsd.edu

schwartz@gondor.cs.psu.edu (Scott E. Schwartz) (01/24/88)

In article <2635@calmasd.GE.COM> gjo@calmasd.UUCP (Glenn Olander) writes:
>machines?  In particular, I believe that many RISC machines require data
>to be aligned on a natural boundary, e.g. longwords must be referenced
>on a 4-byte boundary.  

This is true of sparc.

>If this is true, then it would seem to also be true that a C structure
>could have different lengths, depending on whether it was compiled 
>on a RISC or non-RISC machine.  Further, it would seem that
>if that C structure were written out to a file, it could only be read
>properly by a machine of the same type as that which wrote it.

This is exactly correct.

>Does such incompatibilty truly exist?  If I create a file on a Sun/4
>will I be able to read it on a Sun/3?

From "Porting Software to SPARC", Sun part no. 800-1596-10, Nov '87, p4:

	Because of the three considerations above, members of a given
	structure may have different offsets on SPARC machines than
	on the MC680x0, and the structure as a whole may have a different
	size.  Even though data representations identical on the MC680x0
	and the SPARC, binary files where raw structures have been
	written out may not be portable between processors.
	

-- Scott Schwartz            schwartz@gondor.cs.psu.edu

mash@mips.UUCP (01/24/88)

In article <2635@calmasd.GE.COM> gjo@calmasd.UUCP (Glenn Olander) writes:
>Please forgive a possibly neophyte-type question, but is it true that
>there may be an inherent incompatibility between RISC and conventional
>machines?  In particular, I believe that many RISC machines require data
>to be aligned on a natural boundary, e.g. longwords must be referenced
>on a 4-byte boundary.  This requires compilers to make accomodations to
>ensure that such alignment always occurs, even if it means padding a
>data structure which contains mixed types of data....

>If this is true, then it would seem to also be true that a C structure
>could have different lengths, depending on whether it was compiled 
>on a RISC or non-RISC machine.  Further, it would seem that
>if that C structure were written out to a file, it could only be read
>properly by a machine of the same type as that which wrote it.

>Does such incompatibilty truly exist?  If I create a file on a Sun/4
>will I be able to read it on a Sun/3?

This issue is not inherent to RISC versus non-RISC machines.

Here are the implementation choices:

Machine:
	M1: require alignment for every object 
		SPARC, 78000, most other RISCs
		WE32xxx, Clipper
	M1A: alignment required, except practical unaligned code 
		MIPS R2000 (odd-case - see later)
	M2: no alignment required
		IBM 370, VAX, MC68020, NSC32xxx, Intel 80X86
	M3: alignment required for some, but not others
		MC68000 (16-bit on 16-bit boundary, 32 on 16-bit also)

C language choices:
	C1: align every piece of data on its natural boundary, padding
		as necessary, including padding the size of a structure
		to the maximum alignment requirement of anything found in
		that structure [this makes arrays of structures work].
	C2: Align some things, but not others
	(there might have been a C3: align nothing, but I couldn't think
	of a C compiler that does it)

It's fairly clear that:
1) C1 (align everything) is the safest choice.
2) All of the M1 machines want C1 alignment

Here's the matrix, with some examples:

	M1		M2		M3
C1	All M1s		VAX 4.3BSD	
	R2000		68020-?		68010-?

C2	-		Sun-68020	Sun-68010

Some vendors (Convergent, at least, unless memory fails me), implemented C1
even on 68010s that didn't require it.  Sun aligned 32s on 16s on 68000s
and stayed that way on 68020s, but Sun-4s indeed require 32 on 32.
Thus, structures exist that do not have the same mapping between Sun-3s
and Sun-4s.
Fortunately, most C compilers chose C1 alignment, and most structures
were often padded by hand by people who knew that unaligned things cost
cycles on almost any machine.

Case M1A is a little odd: the MIPS R2000 requires alignment of things
on natural boundaries; however, "unaligned" load/store instruction pairs
are provided, which can be used either from assembler, or (soon) with
compiler switches to make the compielr use them when alignment is in doubt.
Thus, loading something where you're not sure takes the 2 cycles that
are required on most systems for something that is truly unaligned,
and storing is likewise. Besides string-manipulation, and some COBOL-related
things, a major motivation of of this is:

The more serious issue here, actually, is there is a class of program
that is excruciatingly difficult to port for pure M1-class machines.
Specifically, some large FORTRAN programs (as old CAD systems), often in the
half-million-line-plus category contain COMMON+EQUIVALENCE combinations
THAT CANNOT EVER BE ALIGNED UNTIL THE CODE IS HEAVILY REWORKED.
In particular, IBM-derived programs with INTEGER*2 in them can be nasty.
For some reason, some owners have such code (thru which the armies have
marched thru the years) have proved to be less than excited about porting it...
-- 
-john mashey	DISCLAIMER: <generic disclaimer, I speak for me only, etc>
UUCP: 	{ames,decwrl,prls,pyramid}!mips!mash  OR  mash@mips.com
DDD:  	408-991-0253 or 408-720-1700, x253
USPS: 	MIPS Computer Systems, 930 E. Arques, Sunnyvale, CA 94086

guy@gorodish.Sun.COM (Guy Harris) (01/24/88)

> >If this is true, then it would seem to also be true that a C structure
> >could have different lengths, depending on whether it was compiled 
> >on a RISC or non-RISC machine.

True, but not necessarily for reasons having to do with RISC vs. non-RISC:

	1) I know of one CISC that requires 4-byte alignment of 4-byte
	   quantities, and 2-byte alignment of 2-byte quantities: the WE32100.

	2) While the VAX does not impose any alignment restrictions, I think
	   most, if not all, VAX implementations run faster if 4-byte
	   quantities are aligned on 4-byte boundaries and 2-byte quantities
	   are aligned on 2-byte boundaries.

As such, both the VAX UNIX C compiler and the WE32K C compiler, and probably
the VAX/VMS C compiler, align 4-byte quantities in structures on 4-byte
boundaries and 2-byte quantities in structures on 2-byte boundaries.  The
structure as a whole is aligned on the boundary required by its most strictly
aligned member.  These are the same rules used by the SPARC C compiler;
however, on the SPARC *8*-byte quantities (e.g., double-precision floating
point numbers) must be aligned on *8*-byte boundaries.  These restrictions are
not imposed by e.g. the WE32K nor the VAX, so they only align them on 4-byte
boundaries.

However, there are machines with different alignment restrictions, and C
compilers with different alignment rules:

	1) The MC68010 requires 2-byte quantities to be aligned on 2-byte
	   boundaries, but does not require 4-byte quantities to be aligned on
	   4-byte boundaries.  Most of the C compilers for UNIX 68K
	   implementations put 4-byte quantites only on 2-byte boundaries, and
	   always align structures on 2-byte boundaries even if no member
	   requires this alignment.  These rules are often propagated to the
	   68020, which imposes no alignment restrictions.

	2) The CCI Power 6/32 C compiler, last time I dealt with it, always
	   aligns structures on at least 4-byte boundaries.

> >Further, it would seem that if that C structure were written out to a file,
> >it could only be read properly by a machine of the same type as that which
> >wrote it.
> 
> This is exactly correct.

And not only that, it would still be true even if all C implementations imposed
the exact same alignment rules!  VAXes, National Semiconductor 32Ks, and Intel
80*86es address the bytes within a 2-byte or 4-byte quantity from bottom to
top; the least significant byte is byte 0.  These architectures are called
"little-endian".  IBM 360/370s, Motorola 68Ks, AT&T WE32Ks (except for the
WE32000), SPARCs, and CCI Power 6/32s address them from top to bottom; the
*most* significant byte is byte 0.  These architectures are called
"big-endian".  The WE32000, and, if I remember correctly, the MIPS chips, can
select which byte order to use, although I think all WE32000 implementations
use the "big-endian" byte order.

Tapes, disks, and networks are usually byte-serial.  They generally do not
record (in the case of tapes and disks) or transmit (in the case of networks)
2-byte or 4-byte quantities in parallel.

This means that a sequence of *bytes* will, when copied via tape or disk or
transmitted over a network, from a big-endian to a little-endian machine,
appear the same.  If you put the character string "hi mom" on the tape, disk,
or wire, and send it to a machine with the opposite byte sex, that machine will
see "hi mom" (assuming, of course, that the hardware and/or software on both
ends uses the same character set).

However, if you put the number 127 on the tape, disk, or wire as a 4-byte
integer, and send it between two machines with different byte sexes, the number
will appear to be 2130706432 on the other machine.  A machine will generally
write a 4-byte integer on tape or disk or send it over the wire by putting the
byte with address 0, then the byte with address 1, then 2, then 3.  This means
that a little-endian machine will put out a byte with the value 127, and then 3
bytes with the value 0.  A big-endian machine will put out 3 bytes with the
value and then a byte with the value 127.  A machine with the opposite byte sex
will put the 127 in the *most*-significant byte of the integer and put the
zeroes in the lower three bytes.

Furthermore, floating-point formats differ in ways other than their byte order.
Most of the architectures listed above use the IEEE floating-point format
(either directly or in their floating-point coprocessors); however, neither the
IBM 360/370 nor the VAX do, and I don't think the Power 6/32 does either.

And, on top of that, the size of the C data types are not guaranteed to be the
same.  "int" is generally 4 bytes on the 360/370, VAX, the NS32K, WE32K, SPARC,
and MIPS architectures.  It may be 2 or 4 bytes on the 80*86 and Motorola 68K
architectures, depending on the implementation.  It may be *8* bytes on a
supercomputer.  It may be *3* bytes on a 24-bit machine.  On top of this,
there's not even a guarantee that a byte is 8 bits, or that an "int" is 16 or
32 bits; there exists at least two C implementations on 32-bit machines, one of
which even runs UNIX.

In short, the statement made by Scott Schwartz in the summary line:

	you had better use XDR or something similar

is 10,000% true, as is the statement in the original article:

	Further, it would seem that if that C structure were written out to a
	file, it could only be read properly by a machine of the same type as
	that which wrote it.

There are exceptions to this statement: a structure written out on an Intel
386-based machine *might* be readable directly on a NS32K-based machine, for
instance - althought I don't know that their alignment rules or floating-point
formats are the same (both are, I think, IEEE, but I don't know that the byte
order in *floating*-point numbers is the same).

These exceptions are rare, and as indicated I don't even know which of them
really exist.  If you want to write data to a file or put it out on the network
so that some other machine of a different type can read it, *don't* just dump a
raw structure; use the Sun XDR library, or roll your own routines that put
things out in a standard byte order with a standard floating point format,
standard alignment, standard data sizes, etc., etc..

And as for the particular question:

> >Does such incompatibilty truly exist?  If I create a file on a Sun/4
> >will I be able to read it on a Sun/3?

As Mr. Schwartz has already pointed out, the answer is "yes".  The Sun-3 uses
the MC68020 chip, and uses the alignment rules that most 68K UNIX C
implementations use:  structures are always aligned on at least a 2-byte
boundary, and most quantities are only aligned on 2-byte boundaries.  The Sun-4
uses the SPARC chip, and uses the rules listed above for that chip: structures
may be aligned on 1-byte boundaries if they contain nothing requiring a
stricter alignment, 4-byte quantities are aligned on 4-byte boundaries, and
8-byte quantities are aligned on 8-byte boundaries.
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com

csg@pyramid.pyramid.com (Carl S. Gutekunst) (01/24/88)

In article <2635@calmasd.GE.COM> gjo@calmasd.UUCP (Glenn Olander) writes:
>I believe that many RISC machines require data to be aligned on a natural
>boundary, e.g. longwords must be referenced on a 4-byte boundary.

While this is true of practically all RISC and RISC-like designs, it is also
true of nearly all other architectures, too. The number of architectures that
allow totally random positioning of objects is quite small, and I daresay that
C programmers have gotten totally spoiled by them. (I did not say installed
base! Number of *architectures.* That means the VAX counts as 1.)

>...then it would seem to also be true that a C structure could have different
>lengths, depending on whether it was compiled on a RISC or non-RISC machine.
>...if that C structure were written out to a file, it could only be read
>properly by a machine of the same type as that which wrote it.

C structures compiled on *any* two different architectures are likely to be
different lengths.  Think of all the things that can be different besides the
alignment: 16 or 32 bit ints, size of floats, format of floats. Then add byte
order if you really want to confuse things.

>If I create a file on a Sun/4 will I be able to read it on a Sun/3?

Under the terms you described, no. Binary data files are simply not portable.
So use ASCII text files.

<csg>

ok@quintus.UUCP (Richard A. O'Keefe) (01/25/88)

In article <2635@calmasd.GE.COM>, gjo@calmasd.GE.COM (Glenn Olander)
asks about data alignment in RISCs and is worried that
> If this is true, then it would seem to also be true that a C structure
> could have different lengths, depending on whether it was compiled 
> on a RISC or non-RISC machine.  Further, it would seem that
> if that C structure were written out to a file, it could only be read
> properly by a machine of the same type as that which wrote it.

Yes, both of these problems exist.  But 68010s require 16-bit alignment
for 16-bit and 32-bit data, whereas 68020s do not, so the problem may
exist even within a processor family.  (The same is true of IBM's
mainframes:  the old models had stricter alignment requirements.)
Even on a single machine, there is no reason to expect two compilers
to lay structures out the same way.  There is even one machine where
the byte sex is determined at powerup (if I've read the manuals
properly), so a data file might written by one program might not be
readable by another, even though both used the same compiler and
the same #include file.  Then of course there are floating-point
differences...

This isn't really a C problem, either.  The problem exists in any
language having records.

Also, even on machines which don't require 32-bit alignment, there
may be a substantial speed advantage in aligning the data anyway.
(We've seen figure of 30% for C-coded examples.)  So a compiler
may well align things for speed.

There are two solutions: write data files as text, or use XDR.

aglew@ccvaxa.UUCP (01/26/88)

	Further, it would seem that if that C structure were written out to a
	file, it could only be read properly by a machine of the same type as
	that which wrote it.

Many have affirmed the veracity of this statement. May I amplify it:
it sometimes isn't true between machines of the same type. A while back
I worked on a MULTIBUS 68000 system that did byte swapping to match the
68000's idea of data layout. Of course, it couldn't interchange disk
data with VME 68000 systems without byte swapping.

daveh@cbmvax.UUCP (Dave Haynie) (01/26/88)

in article <2635@calmasd.GE.COM>, gjo@calmasd.GE.COM (Glenn Olander) says:

> Please forgive a possibly neophyte-type question, but is it true that
> there may be an inherent incompatibility between RISC and conventional
> machines?  In particular, I believe that many RISC machines require data
> to be aligned on a natural boundary, e.g. longwords must be referenced
> on a 4-byte boundary.  This requires compilers to make accomodations to
> ensure that such alignment always occurs, even if it means padding a
> data structure which contains mixed types of data, for example a C
> structure with a mixture of shorts, longs, and doubles.

This is already going on in some degree with non-RISC machines.  For the 68000,
for instance, short and long words must be accessed on word boundaries.  The
68020 doesn't have this requirement, but it'll perform much faster if words
are on word boundaries and longwords are on 4-byte boundaries.  For at least
the Lattice compiler I use (V4.0, Amiga OS), the compiler is normally using
word alignment for non-byte objects, but has a switch to force longword
alignment.  

I'm sure all 32 bit processors get a speed advantage with longword aligned 
items, it's just a matter of whether it's a requirement or not.  With RISC
type machines, such alignment being a requirement would certainly be in
keeping with RISC's goal of hardware simplicity.

> If this is true, then it would seem to also be true that a C structure
> could have different lengths, depending on whether it was compiled 
> on a RISC or non-RISC machine.  

You'll already find these issues in going between non-RISC machines.  So you
should expect that they may show up in RISC vs. non-RISC.

> Further, it would seem that
> if that C structure were written out to a file, it could only be read
> properly by a machine of the same type as that which wrote it.

In many cases.  This is especially true when going between machines with
different CPU families.  The file I write directly on my Amiga won't be
directly readable on a VAX or an IBM PC, due to byte ordering as well as
word alignment.  It would probably be readable on a Macintosh or a Sun/3,
providing their compilers make similar assumptions on data alignments.

> Glenn Olander
> GE Calma
> gjo%calmasd.ge.com@sdcsvax.ucsd.edu
-- 
Dave Haynie  "The B2000 Guy"     Commodore-Amiga  "The Crew That Never Rests"
   {ihnp4|uunet|rutgers}!cbmvax!daveh      PLINK: D-DAVE H     BIX: hazy
		"I can't relax, 'cause I'm a Boinger!"

klein@ridge.UUCP (Doug Klein) (01/28/88)

In article <3195@cbmvax.UUCP>, daveh@cbmvax.UUCP (Dave Haynie) writes:
> in article <2635@calmasd.GE.COM>, gjo@calmasd.GE.COM (Glenn Olander) says:
> 
> > Please forgive a possibly neophyte-type question, but is it true that
> > there may be an inherent incompatibility between RISC and conventional
> > machines? 
> 
> > If this is true, then it would seem to also be true that a C structure
> > could have different lengths, depending on whether it was compiled 
> > on a RISC or non-RISC machine.  
> 

Having spent the last four years porting large applications to a data
aligned machine (Ridge), I would warn you of more than just data file
compatibility!  This is potentially the least of your problems. To 
mention a few of the gotcha's I have run into:

FORTRAN example 1)

Programmer decides he needs a "array copy" routine. For whatever reason
he decides that the fastest way to copy data is to use:

	subroutine copy(a,b,count)
	double precision a(1),b(1)
	integer count
	do i=1,count
		b(i) = a(1)
	enddo
	return
	end

Now he calls it from somewhere as:

	integer x(100),y(100)
	...
	call copy(x,y,100)

The problem is that x and y are *potentially* word aligned, but the 
subroutine 'copy' uses double loads and stores! BLAM - alignment trap!
The scary part of this is the the alignment of x and/or y can be 
effected in a part of the program *very* far away, e.g., a common
block thousands of lines away. The other scary part is it may take
many, many sample input sets to hit this case.

FORTRAN example 2)

I've seen older FORTRAN programs that try to do forms of stack 
manipulation by allocating a large array, (real buffer(100000) comes
to mind), and then poking arbitrary structures into arbitrary places
in these arrays - lots of potential alignment problems here! 
This happens frequently with EQUIVALENCE statements.
(In particular reference to the originator of this discussion, Calma's
DDM is a nightmare for this problem).


Fortunately, large C applications tend to have been developed with the
idea of portability, and *usually* have run into alignment issues 
early in their development life. Warning flags should fly, though,
when looking at code that runs only on VAXen or 68k boxes. 

Similar to FORTRAN example 2, a common practice in large C applications
is to malloc a big chunk of space, and then start putting arbitrary
structures in it. I've seen this in several of the large commercial
rendering codes, which do a lot of their own "stack" manipulation.
The workaround has usually been to put some "aligning" code into
the low-level routines, i.e., look at the actual address mod'ed with
4 (or 8, or whatever), and then adjust appropriately.

Casting something can cause the compiler to generate an instruction
with strict alignment restraints when the object being cast is not
aligned. (I haven't personally run into this many times).


Doug Klein

johnw@astroatc.UUCP (John F. Wardale) (01/29/88)

In article <354@ridge.UUCP> klein@ridge.UUCP (Doug Klein) writes:
>FORTRAN example 1)
>
>Programmer decides he needs a "array copy" routine. For whatever reason
>he decides that the fastest way to copy data is to use:
>
>	subroutine copy(a,b,count)
>	double precision a(1),b(1)
>   ...
>
>Now he calls it from somewhere as:
>
>	integer x(100),y(100)
>	...
>	call copy(x,y,100)

Not only is this UNDEFINED by the Fortarn standard, the "accepted"
assumetime in Fortran is sizeof(double) == 2*sizeof(int) == 2*sizeof(real)
So, this will (possibly/probably) copy *TWICE* the span of X !!!

The "Proper" way to do this is to have
CCOPY, ICOPY, RCOPY, DCOPY, ZCOPY for CHARACTER, INTEGER, REAL,
DOUBLEPRECISION, COMPLEX.

This is why Ada added Generics and all the "overlaoding" stuff!

> a common practice in large C applications
>is to malloc a big chunk of space, and then start putting arbitrary
>structures in it.   ....
>The workaround has usually been to put some "aligning" code into
>the low-level routines, 

The BSD 4.3 malloc grabs memory by the page (at least page
aligned) and manages pools of bocks for each power of 2 size.
Thus these WILL be sufficently aligned!!

I claim that any other maolloc where:
p = (struct anything)malloc(xxx);   or at least:
p = (struct anything)malloc(sizeof(struct anything);
can cause an error is a  *SYSTEM* bug in *MALLOC* !!!

-- 
					John Wardale
... {seismo | harvard | ihnp4} ! {uwvax | cs.wisc.edu} ! astroatc!johnw

To err is human, to really foul up world news requires the net!

gwu@clyde.ATT.COM (George Wu) (02/02/88)

Roy Smith posts:

> In article <2635@calmasd.GE.COM> gjo@calmasd.UUCP (Glenn Olander) writes:
> > it would seem that if that C structure were written out to a file, it could
> > only be read properly by a machine of the same type as that which wrote it.
> 
> 	Why is this a surprise?  Lots of things prevent you from writing a
> structure on one machine and reading it on another: byte (and bit) order,
> padding requirements, word length, floating point formats, etc.  If you
> want to be able to have your file read by another machine type, you
> shouldn't be writing binary structures.  Use ascii, or hton(), or XDR, or
> something like that.

     Okay, so what formats are used? I  know X-Windows has some such convention
to transmit binary data across the network, ie. a client and a server on
different machines. What format do they use? (Hopefully, Roy will expand
upon hton() and XDR. I think ASCII is self-explanatory.)

     I'm not sure what C-MU's Andrew system (consisting of Microvax-IIs,
Sun-3s, Sun-2s, and mostly IBM RT/PCs) uses across the network, but for
transportable files, a base 64 ASCII file is used. Any other bright ideas?

						George
						rutgers!clyde!gwu

aglew@ccvaxa.UUCP (02/04/88)

Talking about data structure alignment:
I have often wanted an optimizing compiler that could take
struct { long a; int b; long c; char d; }
which is usually mapped into bytes as AAAABBxxCCCCDxxx
(on a machine where misaligned accesses are penalized)
and make it into AAAACCCCBBDx.

Andy "Krazy" Glew. Gould CSD-Urbana.    1101 E. University, Urbana, IL 61801   
    aglew@gould.com     	- preferred, if you have nameserver
    aglew@gswd-vms.gould.com    - if you don't
    aglew@gswd-vms.arpa 	- if you use DoD hosttable
    aglew%mycroft@gswd-vms.arpa - domains are supposed to make things easier?
   
My opinions are my own, and are not the opinions of my employer, or any
other organisation. I indicate my company only so that the reader may
account for any possible bias I may have towards our products.

sjc@mips.COM (Steve "The" Correll) (02/05/88)

In article <28200092@ccvaxa>, aglew@ccvaxa.UUCP writes:
) I have often wanted an optimizing compiler that could take
) struct { long a; int b; long c; char d; }
) which is usually mapped into bytes as AAAABBxxCCCCDxxx
) (on a machine where misaligned accesses are penalized)
) and make it into AAAACCCCBBDx.

Note that you'd want to be able to select this optimization independently
of others, since it violates the Kernighan & Ritchie statement that the
addresses of fields increase from left to right, and a particular program
may or may not care about the violation.
-- 
...decwrl!mips!sjc						Steve Correll

franka@mmintl.UUCP (Frank Adams) (02/09/88)

In article <28200092@ccvaxa> aglew@ccvaxa.UUCP (Andy "Krazy" Glew) writes:
>I have often wanted an optimizing compiler that could take
>struct { long a; int b; long c; char d; }
>which is usually mapped into bytes as AAAABBxxCCCCDxxx
>(on a machine where misaligned accesses are penalized)
>and make it into AAAACCCCBBDx.

How about AAAABBDxCCCC?  This is what results if you follow the rule "put
each component into the first suitable place for it".  It has the important
property that structures with the first few components identically defined
will have those components in the same place.  This is important -- the C++
translator relies on it, for example.

Code that includes a size 1 (or size 0) array at the end of a structure to
represent a variable-sized array may still be broken, however.
-- 

Frank Adams                           ihnp4!philabs!pwa-b!mmintl!franka
Ashton-Tate          52 Oakland Ave North         E. Hartford, CT 06108

johng@ecrcvax.UUCP (John Gregor) (02/10/88)

In article <28200092@ccvaxa> aglew@ccvaxa.UUCP writes:
>
>I have often wanted an optimizing compiler that could take
>struct { long a; int b; long c; char d; }
>which is usually mapped into bytes as AAAABBxxCCCCDxxx
>(on a machine where misaligned accesses are penalized)
>and make it into AAAACCCCBBDx.
>
>Andy "Krazy" Glew. Gould CSD-Urbana.    1101 E. University, Urbana, IL 61801   

I would like that also, but don't call it a struct.  Rearranging the order
breaks lots of code and violates K&R p. 196.

	Within a structure, the objects declared have addresses
	which increase as their declarations are read left-to-right.

Call it a collection, or packed struct or something else.  If I had a PCish
type machine, I'd really like a feature like that.  I'm spoiled rotten by
virtual memory :-).

-- 
pqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpq
bdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbd

John Gregor                                     johng%ecrcvax.UUCP@germany.CSNET

jfc@athena.mit.edu (John F Carr) (02/18/88)

In article <496@ecrcvax.UUCP> johng@ecrcvax.UUCP (John Gregor) writes:
>In article <28200092@ccvaxa> aglew@ccvaxa.UUCP writes:

}}I have often wanted an optimizing compiler that could take
}}struct { long a; int b; long c; char d; }
}}which is usually mapped into bytes as AAAABBxxCCCCDxxx
}}(on a machine where misaligned accesses are penalized)
}}and make it into AAAACCCCBBDx.


}I would like that also, but don't call it a struct.  Rearranging the order
}breaks lots of code and violates K&R p. 196.
}
}	Within a structure, the objects declared have addresses
}	which increase as their declarations are read left-to-right.


Isn't code which depends on allignment and spacing risky anyway?  
If a program assumes that for

struct { int a;
         int b;
       } s;

 (&(s.a) - &(s.b)) == 1 it is making an unsupported assumption.  Can 
someone post an example of a program which is portable and depends on
the K&R rule quoted above?



  --John Carr   (jfc@ATHENA.MIT.EDU)

alverson@decwrl.dec.com (Robert Alverson) (02/18/88)

In article <3001@bloom-beacon.MIT.EDU> jfc@athena.mit.edu (John F Carr) writes:
>
> (&(s.a) - &(s.b)) == 1 it is making an unsupported assumption.  Can 
>someone post an example of a program which is portable and depends on
>the K&R rule quoted above?

How about this:

typedef struct VarStr_str { int len; char str[1]; } VarStr;

VarStr *MakStr(s) char *s; {
   VarStr *p = malloc(sizeof(VarStr) + strlen(s));
   strcpy(p->str, s);
   return p;
}


This is a commonly used technique to avoid another memory indirection
when accessing the variable data in the structure.

Bob

bader+@andrew.cmu.edu (Miles Bader) (02/19/88)

jfc@athena.mit.edu (John F Carr) writes:
> Isn't code which depends on allignment and spacing risky anyway?  
> If a program assumes that for
> 
> struct { int a;
>          int b;
>        } s;
> 
>  (&(s.a) - &(s.b)) == 1 it is making an unsupported assumption.  Can 
> someone post an example of a program which is portable and depends on
> the K&R rule quoted above?

The common case is that of extending the last element of a
structure...  Does anyone have code that will break if the elements
are ordered arbitrarily, BUT the last element is kept last?

-Miles

aglew@ccvaxa.UUCP (02/19/88)

..> K&R saying that struct fields must increase in address

What does ANSI C, X3J11, say about this?

mcm@rti.UUCP (Mike Mitchell) (02/19/88)

I have written device drivers where I created a structure to hold
the memory-mapped status registers.  Some of the hardware had very
strange memory alignments, and if the structure elements were changed,
the driver would break.  For example, a structure to reference a serial
port might look something like:

	struct ttport {
	    short t_tbaud;	/* xmit baud rate */
	    char  t_txt;	/* transmitted character */
	    short t_rbaud;	/* recv baud rate */
	    char  t_rec;	/* received character */
	} *csr = 0xFF8000;	/* address of device */

It would be more correct if a one byte pad were explicity put in after
the "char t_txt", but I think my point is still valid.  There are some
cases where the order of the structure elements cannot be changed.

-- 
Mike Mitchell	{decvax,seismo,ihnp4,philabs}!mcnc!rti!mcm  mcm@rti.rti.org

"There's laughter where I used to see a tear.		    w (919) 541-6098
 It's all done with mirrors, have no fear."		    h (919) 361-2048

hankd@pur-ee.UUCP (Hank Dietz) (02/19/88)

In article <286@bacchus.DEC.COM>, alverson@decwrl.dec.com (Robert Alverson) writes:
> In article <3001@bloom-beacon.MIT.EDU> jfc@athena.mit.edu (John F Carr) writes:
> >
> > (&(s.a) - &(s.b)) == 1 it is making an unsupported assumption.  Can 
> >someone post an example of a program which is portable and depends on
> >the K&R rule quoted above?
> 
> How about this:
> 
> typedef struct VarStr_str { int len; char str[1]; } VarStr;
>
> VarStr *MakStr(s) char *s; {
>    VarStr *p = malloc(sizeof(VarStr) + strlen(s));
>    strcpy(p->str, s);
>    return p;
> }

Portable?  Try again.  According to C, malloc does not exist
-- it is just another user function.  When you use malloc, you
take advantage of a hole in the C type system (i.e., the very
deliberate hole called type casting -- which you incidentally
forgot in your use of malloc).  I'm not saying the above hack
isn't useful, but that your "struct" doesn't work as a struct.

For example, C permits us to perform struct assignment, but
you can't use it for a struct like yours because it will NOT
COPY THE ELEMENTS WHICH WERE NOT DECLARED CORRECTLY -- i.e.,
it will copy only str[0] and not the whole string.  The same
is true of returning a struct or passing it by value.  And
sizeof is also wrong.  You are trying to justify the need to
maintain order of a struct's members by declaring something as
a struct even though it can't be treated as one.

Unfortunately, there is no way within C's type system to
declare the above, so a kludge is needed.  Compare your kludge
to the Refined C concept of a paramtype (a parameterized
struct type), which allows a variable-sized struct to be
declared and treated as a first-class data object.  (I can
supply references and/or papers on Refined C if you're
interested in how this works.)

Aside from the above argument, it is possible for a compiler
to determine when the order of members in a struct could be
significant, and to rearrange only when the order can be
changed without altering the program's meaning.  For example,
your struct only has to have the last field last... the order
of the other fields doesn't matter.  In code where the
programmer may have sloppily used the struct address as the
address of the first member, the compiler can constrain the
first member to remain first.  Of course, I'm not saying that
it is easy to do, but just that a compiler can do it.
(Minimizing space, or optimizing memory reference pattern, by
cerfully rearranging struct members is something I have taught
in fair detail in my graduate compilers courses for the past 4
or 5 years.)

In summary, let's not try to encourage kludges as principles
of language design.  If you must write code which depends on
the order, be aware that you are doing it outside of the
language's type system, and that compilers will tend to
generate lousy code for it because they have to make safe
assumptions about what undecipherable (to the compiler) things
you've done.

mac3n@babbage.acc.virginia.edu (Alex Colvin) (02/19/88)

Now that this discussion has moved from data alignment constraints in
processors to alignment specifications in C, perhaps there's another newsgroup
that's more appropriate.

aglew@ccvaxa.UUCP (02/20/88)

>It would be more correct if a one byte pad were explicity put in after
>the "char t_txt", but I think my point is still valid.  There are some
>cases where the order of the structure elements cannot be changed.
>
>Mike Mitchell	{decvax,seismo,ihnp4,philabs}!mcnc!rti!mcm  mcm@rti.rti.org

Agreed. I've written code that has to deal with hardware data structures
like PTEs. Rearranging data structures is an optimization I would like
to be able to turn on, but would definitely want to be able to turn off.

And, yes, if the compiler guys can make the compiler automatically discover
which those are, I'll use automatic generation - but until then I would
like automatic control.

barmar@think.COM (Barry Margolin) (02/20/88)

In article <2047@rti.UUCP> mcm@rti.UUCP (Mike Mitchell) writes:
>I have written device drivers where I created a structure to hold
>the memory-mapped status registers.  Some of the hardware had very
>strange memory alignments, and if the structure elements were changed,
>the driver would break.

Such uses are inherently non-portable, so all that matters is whether
the particular C compiler you are using provides a way to specify the
order.  Also, there is nothing in C that requires a particular
alignment scheme, so a C compiler that aligned shorts at double-word
boundaries would be within its rights, although it would screw up your
example (all that the C spec specifies is that the addresses increase,
but not by how much).

>	struct ttport {
>	    short t_tbaud;	/* xmit baud rate */
>	    char  t_txt;	/* transmitted character */
>	    short t_rbaud;	/* recv baud rate */
>	    char  t_rec;	/* received character */
>	} *csr = 0xFF8000;	/* address of device */

And that assignment definitely isn't portable.

Barry Margolin
Thinking Machines Corp.

barmar@think.com
uunet!think!barmar

ok@quintus.UUCP (Richard A. O'Keefe) (02/20/88)

In article <20003@bu-cs.BU.EDU>, bzs@bu-cs.BU.EDU (Barry Shein) writes:
: Does anyone have any problems with:
: foo()
:     {
: 	struct { double x; int i; } a;
: 
: 	a.x = 3.1;
: 	a.i = 12;
: 	printf("%g %d\n",a);
: }
: 
: as an example of a legal, portable program (printf isn't necessary,
: any function which takes multiple arguments possibly handed off as a
: struct)? 

Unless I have completely misread the dpANS, this will not be legal
ANSI C, and the reason for that is that it has never been portable.
K&R say that elements of a structure are given increasing addresses
(a.x is lower than a.i), but the order of arguments is NOT defined:
printf() could easily expect its arguments in the opposite order.  What
is more, some C compilers pass struct arguments on another stack; this
is common when "small" arguments are passed in registers.  The Orion C
compiler, for example, allocated arrays and structs in one stack, and
scalars in another.  (I got burned by this once; never again!)

amos@nsc.nsc.com (Amos Shapir NSTA) (02/21/88)

In article <2047@rti.UUCP> mcm@rti.UUCP (Mike Mitchell) writes:
>I have written device drivers where I created a structure to hold
>the memory-mapped status registers.  Some of the hardware had very
>strange memory alignments, and if the structure elements were changed,
>the driver would break.

This is not a very good example - the CPU reading the structure (i.e.
the device controller) is not the same as the one you compile for, and
therefore you cannot assume compatibility.

It is true, though, that C is generally a what-you-see-is-what-you-get
language, and most C programmers have come to expect - but this is more
of a side-effect than a feature.

-- 
	Amos Shapir				My other CPU is a NS32532
National Semiconductor 7C/266  1135 Kern st. Sunnyvale (408) 721-8161
amos@nsc.com till March 1, 88; Then back to amos%taux01@nsc.com 

aglew@ccvaxa.UUCP (02/21/88)

>Does anyone have any problems with:
>
>foo()
>{
>	struct {
>		double x;
>		int i;
>	} a;
>
>	a.x = 3.1;
>	a.i = 12;
>	printf("%g %d\n",a);
>}
>
>as an example of a legal, portable program (printf isn't necessary,
>any function which takes multiple arguments possibly handed off as a
>struct)? Or does the receiving function have to strictly be declared
>as receiving a like struct? (or is that just "a good idea" vs a true
>restriction in the language?)
>
>	-Barry Shein, Boston University

Well, if this is portable I am really going to be surprised.
Any language lawyers out there with a most recent X3J11?

In my out of date copy, C.3.2.2 says something like "each argument is
evaluated, and each formal parameter is assigned the value of the 
corresponding argument... If the number of arguments or their types
after conversion do not agree with those of the formal parameters, the
behavior is undefined".

Of course, printf is variadic, "...", so type checking doesn't apply.


Andy "Krazy" Glew. Gould CSD-Urbana.    1101 E. University, Urbana, IL 61801   
    aglew@gould.com     	- preferred, if you have nameserver
    aglew@gswd-vms.gould.com    - if you don't
    aglew@gswd-vms.arpa 	- if you use DoD hosttable
    aglew%mycroft@gswd-vms.arpa - domains are supposed to make things easier?
   
My opinions are my own, and are not the opinions of my employer, or any
other organisation. I indicate my company only so that the reader may
account for any possible bias I may have towards our products.

johng@ecrcvax.UUCP (John Gregor) (02/22/88)

In article <3001@bloom-beacon.MIT.EDU> jfc@athena.mit.edu (John F Carr) writes:
>In article <496@ecrcvax.UUCP> johng@ecrcvax.UUCP (John Gregor) writes:
>>In article <28200092@ccvaxa> aglew@ccvaxa.UUCP writes:
>}}I have often wanted an optimizing compiler that could take
>}}struct { long a; int b; long c; char d; }
>}}which is usually mapped into bytes as AAAABBxxCCCCDxxx
>}}(on a machine where misaligned accesses are penalized)
>}}and make it into AAAACCCCBBDx.
>}I would like that also, but don't call it a struct.  Rearranging the order
>}breaks lots of code and violates K&R p. 196.
>Isn't code which depends on allignment and spacing risky anyway?  
>If a program assumes that for
>struct { int a;
>         int b;
>       } s;
> (&(s.a) - &(s.b)) == 1 it is making an unsupported assumption.  Can 
>someone post an example of a program which is portable and depends on
>the K&R rule quoted above?
>  --John Carr   (jfc@ATHENA.MIT.EDU)

That's not a good example of the type of code that would break.  I use
the ordering property of structs to hide data.  If I have a struct which
has some house keeping at the end of the struct, I don't have to tell
the rest of the modules about it.  e.g.

struct private_foo_t { 
    int   a;
    short b;    /* would be reordered to AAAACCCCBB */
    int   c;
} fooba;

struct public_foo_t { 
    int   a;
    short b;    
} foobr;

By K&R I am guaranteed that: 

       (&(fooba.b) - &(fooba.a)) == (&(foobr.b) - &(foobr.a))

       /* i.e. the offsets are identical as long as the types 
       ** are the same.
       */

This would break if structs were reordered.  I believe that C++ uses this
feature of C to implement its data hiding also.  I do like the idea of an 
unordered aggregate type however.  I've had to reorder structs for memory
usage too many times and it's very machine dependent.

-- 
pqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpqpq
bdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbdbd

John Gregor                                     johng%ecrcvax.UUCP@germany.CSNET

aglew@ccvaxa.UUCP (02/25/88)

>That's not a good example of the type of code that would break.  I use
>the ordering property of structs to hide data.  If I have a struct which
>has some house keeping at the end of the struct, I don't have to tell
>the rest of the modules about it.  e.g.

But K&R don't guarantee that struct { int; short; int; } will have fields
in the same place as struct { int; short; } (or I'm sure that someone will
give me chapter and verse), so this code isn't portable anyway.