[comp.sys.amiga] Structure alignment in Manx C

rokicki@rocky.STANFORD.EDU (Tomas Rokicki) (02/14/88)

I just tracked down this bug that took me immense amounts of time
to find, and I discovered something about Manx C.  I found out that
one of my friends had also run into and wasted hours finding this
anomaly.  Specifically,  Manx C does not long-word align structures
containing long words!  (It also doesn't apparently long-word align
long words.)  The ramifications of this are two-fold:

- Programs compiled with Manx C and then run on a 68020 system will
run slower, as some (probably half) of their long word accesses will
need two memory accesses instead of one.

- You cannot statically allocate anything that needs to be on a long
word boundary; for instance, packets.  (Not that you should anyway, as
they should be MEMF_PUBLIC, but with the current release that doesn't
matter anyway.)  Anything that will be converted to a BPTR and back
will have problems.

Oh yeah, this is Manx 3.4b.

I can't think of any good way to fix this problem; I refuse to recompile
all my programs with 32-bit ints.  Why did Manx do this?  The only case
where it costs anything is in passing arguments to subroutines; all other
occurances would cost very little memory at all.  And subroutine arguments
could be handled as they are now, or perhaps modified.  I don't care.

What does Lattice do (4.0), anyone out there know?

-tom

dillon@CORY.BERKELEY.EDU (Matt Dillon) (02/14/88)

>I just tracked down this bug that took me immense amounts of time
>to find, and I discovered something about Manx C.  I found out that
>one of my friends had also run into and wasted hours finding this
>anomaly.  Specifically,  Manx C does not long-word align structures
>containing long words!  (It also doesn't apparently long-word align
>long words.)  The ramifications of this are two-fold:

	It might not be preferable, but can hardly be called a bug.  
Remember that warning a lonnngg time ago about declaring such items as
FileInfoBlocks auto?  You can't for the very reason that they require
longword alignment and since longwords don't, no assumption can be made
as to the alignment the compiler used.

	The only solution is to AllocMem() the structure, or to use 
such generally unportable features as the +A link option in Aztec's
linker (force a module to be aligned on a longword boundry).  In the 
latter case, only a partial fix is obtained.

	There are assembly level constructs that allow you to align
items however you wish but, of course, this requires inline assembly.

					-Matt

rokicki@rocky.STANFORD.EDU (Tomas Rokicki) (02/14/88)

dillon@CORY.BERKELEY.EDU (Matt Dillon) writes:
> > (Manx don't put longword entities on longword boundaries . . .)
> 	It might not be preferable, but can hardly be called a bug.  
> Remember that warning a lonnngg time ago about declaring such items as
> FileInfoBlocks auto?  You can't for the very reason that they require
> longword alignment and since longwords don't, no assumption can be made
> as to the alignment the compiler used.

Any compiler for a 68000/68020 machine, that does not do everything
reasonable to put longword structures on longword boundaries, is buggy.
Period.  The Amiga is most certainly a 68000/68020 machine.  I wonder
how much slower those 68020 machines run because all of the Manx
compiled commercial software (at least those with 16-bit ints) don't
have their longwords aligned correctly?

The costs are absolutely minimal, with the sole exception of passing
mixed longword and 16-bit arguments to functions, in which case the
costs are fair.  I can't imagine any excuse for such behavior.

-tom

dillon@CORY.BERKELEY.EDU (Matt Dillon) (02/15/88)

>>dillon@CORY.BERKELEY.EDU (Matt Dillon) writes:
>>> > (Manx don't put longword entities on longword boundaries . . .)
>>> 	It might not be preferable, but can hardly be called a bug.  
>>> Remember that warning a lonnngg time ago about declaring such items as
>>> FileInfoBlocks auto?  You can't for the very reason that they require
>>> longword alignment and since longwords don't, no assumption can be made
>>> as to the alignment the compiler used.
>>
>>Any compiler for a 68000/68020 machine, that does not do everything
>>reasonable to put longword structures on longword boundaries, is buggy.
>>Period.  The Amiga is most certainly a 68000/68020 machine.  I wonder
>>how much slower those 68020 machines run because all of the Manx
>>compiled commercial software (at least those with 16-bit ints) don't
>>have their longwords aligned correctly?

	I wouldn't mind seeing a compile time option to fix the problem,
but there are just as many reasons for NOT wanting them on longword boundries.

	You might want a more compact structure.

	C-A sure did, and...

	If you were to make longword alignment the default, your C compiler
would suddenly become totally incompatible with almost every Amiga structure
in existance.

	For example:  struct Menu, &menu.MenuName relative to &menu.NextMenu
is on a word boundry.  struct MenuItem, Node, Message, and thus IORequest,
IOStdReq, etc.....

					-Matt

rokicki@rocky.STANFORD.EDU (Tomas Rokicki) (02/15/88)

> 	You might want a more compact structure.
> 
> 	C-A sure did, and...
> 
> 	If you were to make longword alignment the default, your C compiler
> would suddenly become totally incompatible with almost every Amiga structure
> in existance.
> 
> 	For example:  struct Menu, &menu.MenuName relative to &menu.NextMenu
> is on a word boundry.  struct MenuItem, Node, Message, and thus IORequest,
> IOStdReq, etc.....
> 
> 					-Matt

Oh my, I didn't realize the problem was quite so deep seated!  Even the
Amiga software wasn't really designed with the 68020 in mind, then!  I
always order my structures so longwords stay on longword boundaries.  You
should lose at most three bytes per structure.  And the structures are
ingrained enough so they will not change . . .  You are absolutely correct,
though, about Amiga structures not being organized around a 32-bit word,
even at the lowest level (struct Node{struct Node *ln_Succ,*ln_Pred;
UBYTE ln_type;BYTE ln_Pri;char *ln_Name;};).  Ouch, ouch, double ouch.
Putting in a SHORT ln_Pad would have added 2 bytes to this 14-byte
structure; I think that would have been a worthwhile tradeoff.  No use
crying over spilt milk, I guess.

But if some new, later Amiga does have such changes that most code needs
recompiling to run (for instance, MEMF_PUBLIC enforced due to an MMU or
some such) then it might be worthwhile to investigate the cost of making
this change.

-tom

manx@well.UUCP (Jim Goodnow II) (02/15/88)

In article <1042@rocky.STANFORD.EDU> rokicki@rocky.STANFORD.EDU (Tomas Rokicki) writes:
>.....  Specifically,  Manx C does not long-word align structures
>containing long words!  (It also doesn't apparently long-word align
>long words.)  The ramifications of this are two-fold:
>
>- Programs compiled with Manx C and then run on a 68020 system will
>run slower, as some (probably half) of their long word accesses will
>need two memory accesses instead of one.
>
>- You cannot statically allocate anything that needs to be on a long
>word boundary; for instance, packets.  (Not that you should anyway, as
>they should be MEMF_PUBLIC, but with the current release that doesn't
>matter anyway.)  Anything that will be converted to a BPTR and back
>will have problems.
> ......
>-tom


First off, there is a definite penalty with long-word aligning data unless
all your data consists of longs. Worst case would be that every char or
short would generate an extra two bytes of filler to align the next item.
This would be true regardless of whether int is 32 bits. Second, the number
of programs running on 68020 systems is pretty much a minority at present.
There will be an option for alignment in a future version of the compiler
to rectify this. Third, data items allocated on the stack cannot be
guaranteed to be allocated on long word boundaries when passing some
arguments as shorts without making the function preamble more complicated.

Fourth, you can use malloc() or AllocMem() which both are guaranteed to
be aligned to a long word boundary.
Finally, I can find nothing in the dpANSI standard about alignment
requirements of static data objects. I might have missed it, I've only
been working with it for a couple of weeks.

To say it's a bug is a little strong, maybe a desired feature??? :-)

Jim Goodnow II

Disclaimer: I work for Manx, so I don't gotta give none...

cmcmanis%pepper@Sun.COM (Chuck McManis) (02/16/88)

Tom Rokicki wrote :
> I just tracked down this bug that took me immense amounts of time
> to find, and I discovered something about Manx C.  I found out that
> one of my friends had also run into and wasted hours finding this
> anomaly.  Specifically,  Manx C does not long-word align structures
> containing long words!  (It also doesn't apparently long-word align
> long words.) 
>

Correct-a-mundo! Since the 68000 can load longwords on odd word addresses
neither C compiler cares to put them on longword boundaries. It does
align them on word boundaries however. So you can very cruftily get around
the problem (in the case of the FileInfoBlock that Matt mentioned) like
so :

	short	mem[(sizeof(struct FileInfoBlock) >> 1)+1];
	struct FileInfoBlock *fi;

	fi = (struct FileInfoBlock *)(((ULONG)(mem) & 3) ? &mem[1] : &mem[0]);

Now, is that gross code or what?


--Chuck McManis
uucp: {anywhere}!sun!cmcmanis   BIX: cmcmanis  ARPAnet: cmcmanis@sun.com
These opinions are my own and no one elses, but you knew that didn't you.

dillon@CORY.BERKELEY.EDU (Matt Dillon) (02/16/88)

>	short	mem[(sizeof(struct FileInfoBlock) >> 1)+1];
>	struct FileInfoBlock *fi;
>
>	fi = (struct FileInfoBlock *)(((ULONG)(mem) & 3) ? &mem[1] : &mem[0]);
>
>Now, is that gross code or what?
>--Chuck McManis

	Definately, but it sure beats having to AllocMem() the block.

	It would be nice if one could have a salloc() call ... allocate memory
	off the stack.  Although Aztec (and lattice?) use a frame pointer, they
	apparaently also push and pop the registers using the stack pointer,
	so the call would have to be implemented like:

		ptr = salloc(bytes)	(allocates on a longword boundry)
		spop()			pops the allocated stack frame, must
					call before return statement / end of
					subroutine.

						-Matt

jesup@pawl20.pawl.rpi.edu (Randell E. Jesup) (02/17/88)

In article <8802160646.AA14996@cory.Berkeley.EDU> dillon@CORY.BERKELEY.EDU (Matt Dillon) writes:
>		ptr = salloc(bytes)	(allocates on a longword boundry)
>		spop()			pops the allocated stack frame, must
>					call before return statement / end of
>					subroutine.

	Actually, spop() isn't needed before an exit at all.  Lattice
(and I'm fairly sure Manx) use the unlink instruction, which will restore
the old stack pointer.  The only thing to watch out for is stack overflow.

     //	Randell Jesup			      Lunge Software Development
    //	Dedicated Amiga Programmer            13 Frear Ave, Troy, NY 12180
 \\//	beowulf!lunge!jesup@steinmetz.UUCP    (518) 272-2942
  \/    (uunet!steinmetz!beowulf!lunge!jesup) BIX: rjesup

dillon@CORY.BERKELEY.EDU (Matt Dillon) (02/18/88)

:In article <8802160646.AA14996@cory.Berkeley.EDU> dillon@CORY.BERKELEY.EDU (Matt Dillon) writes:
:>		ptr = salloc(bytes)	(allocates on a longword boundry)
:>		spop()			pops the allocated stack frame, must
:>					call before return statement / end of
:>					subroutine.
:
:	Actually, spop() isn't needed before an exit at all.  Lattice
:(and I'm fairly sure Manx) use the unlink instruction, which will restore
:the old stack pointer.  The only thing to watch out for is stack overflow.

	Yes, they do use LINK/UNLK, but they also use MOVEM REGS,-(SP) to
save registers, and before the RTS do a MOVEM (SP)+,REGS.  If the SP isn't
in the right place, a bogus set of registers will be restored.

					-Matt