[comp.lang.c] what C needs -- packed structures

devine@cookie.dec.com (Bob Devine) (01/08/88)

Path: decwrl!labrea!rutgers!cmcl2!brl-adm!brl-smoke!gwyn
Subject: Re: what C needs
 
I wrote:
>When writing code that requires control of alignment or location,
> [...I want a way to pack a structure]
 
Doug Gwyn wrote:
> If you're under the impression that a simple, efficient mechanism
> exists or could feasibly be made to exist for "rolling in" raw binary
> data obtained from another system, you're sadly mistaken.

  No, I didn't want a full-blown XDR or presentation layer type of
function.  What I described was smaller.  I stemmed from an experience
where two vendor's compilers where used on the same architecture but
with different layouts of data.  Frustrating!

  A different argument for providing packed/unpacked structures is
simply to allow the programmer to choose between access speed or
smaller data segment.  As an example of prior art, Microsoft C has
a compile time flag for this.  Granted, not all structures would
be packed the same way or even could be packed.  Some architectures
allow ints to be only integer aligned (pyramid, I believe) while
others all ints to be char aligned (vax).

  Consider this suggestion in light of the C philosophy or letting
the programmer decide.

nevin1@ihlpf.ATT.COM (00704A-Liber) (01/09/88)

In article <8801071842.AA04669@decwrl.dec.com> devine@cookie.dec.com (Bob Devine) writes:
.Path: decwrl!labrea!rutgers!cmcl2!brl-adm!brl-smoke!gwyn
.Subject: Re: what C needs
. 
.I wrote:
..When writing code that requires control of alignment or location,
.. [...I want a way to pack a structure]
. 
.  A different argument for providing packed/unpacked structures is
.simply to allow the programmer to choose between access speed or
.smaller data segment.  As an example of prior art, Microsoft C has
.a compile time flag for this.  Granted, not all structures would
.be packed the same way or even could be packed.  Some architectures
.allow ints to be only integer aligned (pyramid, I believe) while
.others all ints to be char aligned (vax).
.
.  Consider this suggestion in light of the C philosophy or letting
.the programmer decide.

I do not believe that this should be implemented as a feature of the language;
This is best left to defining library routines for packing/unpacking data.
-- 
 _ __			NEVIN J. LIBER	..!ihnp4!ihlpf!nevin1	(312) 510-6194
' )  )				"The secret compartment of my ring I fill
 /  / _ , __o  ____		 with an Underdog super-energy pill."
/  (_</_\/ <__/ / <_	These are solely MY opinions, not AT&T's, blah blah blah

dag@chinet.UUCP (Daniel A. Glasser) (01/09/88)

In article <8801071842.AA04669@decwrl.dec.com> devine@cookie.dec.com (Bob Devine) writes:
[lots of junk deleted]
>  A different argument for providing packed/unpacked structures is
>simply to allow the programmer to choose between access speed or
>smaller data segment.  As an example of prior art, Microsoft C has
>a compile time flag for this.  Granted, not all structures would
>be packed the same way or even could be packed.  Some architectures
>allow ints to be only integer aligned (pyramid, I believe) while
>others all ints to be char aligned (vax).
>
>  Consider this suggestion in light of the C philosophy or letting
>the programmer decide.

This is not so easy.  Yes, the VAX, 8088, and many other processors allow
access of data at any byte alignment, but many don't.  If you have a
packed type on a PDP-11 or 68000 you must use several instructions to
fetch a word or long or other multi-byte type any time you access any
structure through a pointer.

Take the following bit of C code:

	struct yerks {
		char	y_foo;
		int	*y_fie;
		char	y_poo;
	};

	struct yerks *w;
	int i;

	i = *w->y_fie;

The assignment, on a PDP-11, takes code to the effect of the following:

	mov	_w, r0
	clr	-(sp)
	movb	1(r0), (sp)
	movb	2(r0), 1(sp)
	mov	@(sp)+, _i

Now, assume that you are on a 68000 --

	movea.l	_w, a0
	moveq	$0, d0
	move.l	d0, -(sp)
	move.b	1(a0), (sp)
	move.b	2(a0), 1(sp)
	move.b	3(a0), 2(sp)
	move.b	4(a0), 3(sp)
	movea.l	(sp)+, a0
	move.w	(a0), _i;

Okay, that not seem too painful?

No, packed structures do not belong in the standard.  A particular
compiler can pack structures as an option, or by default, but that
is not going to be portable.  Period.  C is supposed to be an efficient
language.  If you really need packed structures, and want them portable,
code them directly with macros and unions.
-- 
					Daniel A. Glasser
					...!ihnp4!chinet!dag
					...!ihnp4!mwc!dag
					...!ihnp4!mwc!gorgon!dag
	One of those things that goes "BUMP!!! (ouch!)" in the night.

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

In article <2083@chinet.UUCP>, dag@chinet.UUCP (Daniel A. Glasser) writes:
> struct yerks { char y_foo; int *y_fie; char y_poo; };
> struct yerks *w;
> int i;
> i = *w->y_fie;
He claims that this assignment would turn into 9 M680x0 instructions.
Wrong.  It can be coded in three:
	movl	_w, a0
	movl	1(a0), a0
	movl	(a0), _i
The /370, and the 80386 can also handle misaligned data.  The R2000
can handle misaligned data at the cost of one extra instruction *if*
the compiler knows the data are misaligned, which in this case it
would.  None of this spoils Glasser's real point, namely that packing
is machine-dependent, and doesn't come free on any machine.
Even the machines that will handle his example in few instructions tend
to take lots more *cycles* to handle misaligned data.  (A loop like
	register int n = {something};
	register long *d = {something};
	register long *s = {something};
	while (--n >= 0) *d++ = *s++;
takes about 30% longer for misaligned than for aligned data on a 68020.)
Glasser says
> No, packed structures do not belong in the standard.
He's absolutely right.  We already *have* bitfields, after all.

gnu@hoptoad.uucp (John Gilmore) (01/11/88)

dag@chinet.UUCP (Daniel A. Glasser) wrote:
> Take the following bit of C code:
> 	struct yerks {
> 		char	y_foo;
> 		int	*y_fie;
> 		char	y_poo;
> 	};
> 	struct yerks *w;
> 	int i;
> 	i = *w->y_fie;
> The assignment, on a PDP-11, takes code to the effect of the following:
> 	[five instructions]
> Now, assume that you are on a 68000 --
>	[nine instructions -- not optimal either]
> Okay, that not seem too painful?
> No, packed structures do not belong in the standard...
>                                        C is supposed to be an efficient
> language.  If you really need packed structures, and want them portable,
> code them directly with macros and unions.

To me this looks like an argument *for* packed structures.  Is the
author proposing that if we want to access data defined by an outside
constraint, we should write assembly language routines?  I would
rather have the C compiler figure out the difference between the PDP-11
assembler sequence and the 68000 sequence, thank you.  Or how is he
proposing that we write "portable" definitions using macros and unions,
which are just as dependent as structs on compiler alignment, word size,
etc?

What ends up happening is that a C struct is defined which works on the
current compiler; this gradually gets hacked with #ifdef's to work on
various compilers as needed, but it still has to depend on individual
compilers' ideas of alignment constraints, since the C language allows
the compiler to add padding between members or bitfields with
impunity.  I had to write and maintain a lot of code like this which
accessed device registers on the Sun CPU board, or control block
layouts in memory that were shared with Ethernet chips or disk
controllers or whatever.  It was all heavily compiler dependent and I'm
sure it broke when ported to the Sun-4, but we didn't have any better
tools in C for this.

What we need is a portable way to tell the compiler, "don't add any
padding -- just lay down the bits like I tell you."  You could precede
a structure declaration with "#pragma packed", which would require the
structure definition to declare all its fields as bitfields (not
allowing the element sizes to default) and to declare each field either
signed or unsigned (not allowing the signedness to default).  The
resulting structure would contain exactly those bitfields, laid down
end-to-end in big-endian order, without regard to any alignment
considerations.  This would still not resolve floating point formats,
or ones' complement versus twos' complement signed integers, but the
rest of the "binary data description problem" (including the #ifdef
BIG_ENDIAN / LITTLE_ENDIAN hassle) would be solved.

This is too unformed an idea for this C standard, but if some people
implement it, like it, and it doesn't have hidden problems, maybe for
the next one.
-- 
{pyramid,ptsfa,amdahl,sun,ihnp4}!hoptoad!gnu			  gnu@toad.com
  I forsee a day when there are two kinds of C compilers: standard ones and 
  useful ones ... just like Pascal and Fortran.  Are we making progress yet?
	-- ASC:GUTHERY%slb-test.csnet

dag@chinet.UUCP (Daniel A. Glasser) (01/12/88)

In article <3816@hoptoad.uucp> gnu@hoptoad.uucp (John Gilmore) writes:
[Excerpts from an article I posted deleted]
>
>To me this looks like an argument *for* packed structures.  Is the
>author proposing that if we want to access data defined by an outside
>constraint, we should write assembly language routines?  I would
>rather have the C compiler figure out the difference between the PDP-11
>assembler sequence and the 68000 sequence, thank you.  Or how is he
>proposing that we write "portable" definitions using macros and unions,
>which are just as dependent as structs on compiler alignment, word size,
>etc?

Well, no, this was in oposition to packed structures being mandated
by the standard.  Different machine archetectures support different
packing schemes.  The requirements placed on the compiler code generation
when packed structures are being used may be easy on one machine and
very hard on another.  I am willing to be convinced otherwise, but I
would need to see a 68000 (not 68020) compiler that supports packed
structures before I would be ready to rewrite the compilers that I work
on to support these things.

I do have a few questions for those who want packed structures:

1) must the packed "keyword" be in the structure definition or just
with the declaration?  ie:

	packed struct rowdy {
		char	r_y;
		int	r_i;
		struct rowdy *r_next;
	};

	struct rowdy wow;

vs.

	struct rowdy {
		char	r_y;
		int	r_i;
		struct rowdy *r_next;
	};

	packed struct rowdy wow;
	struct rowdy why;

The second is more 'C' like, but really screws up what r_i and r_next
might mean in a given context, let alone what happens if you say "why = wow".

2) There is this "prior-art" thing.  Yes, some compilers give you switches
to select which way you want structures packed, but this is not a semantic
change to the language, and god have mercy on the soul of the programmer who
mixes modules compiled with different packing strategies.  Yes, Pascal also
provides packed types; C is not Pascal.  Have you ever encountered a C compiler
for an archetecture that does impose alignment restrictions on multi-byte
simple types (not just a performance penalty, ala 808[86]) that provides
packed structures?  I have not, and I've seen lots of compilers.

If you want to have packed structures in C, find a vendor who is willing
to provide them or write an implementation yourself, make sure the extension
is clean and does not violate too many C conventions and see that the rest
of the world gets to see this extension and it becomes popular.  Then
participate in the continuing ANSI C committee and see that this extension
makes it into the next standard.

ANSI does not disallow a compiler from providing extensions.
Extensions are just not portable, and should be disableable.

-- 
					Daniel A. Glasser
					...!ihnp4!chinet!dag
					...!ihnp4!mwc!dag
					...!ihnp4!mwc!gorgon!dag
	One of those things that goes "BUMP!!! (ouch!)" in the night.

dag@chinet.UUCP (Daniel A. Glasser) (01/13/88)

In article <519@cresswell.quintus.UUCP> ok@quintus.UUCP (Richard A. O'Keefe) writes:
+In article <2083@chinet.UUCP>, dag@chinet.UUCP (Daniel A. Glasser) writes:
++ struct yerks { char y_foo; int *y_fie; char y_poo; };
++ struct yerks *w;
++ int i;
++ i = *w->y_fie;
+He claims that this assignment would turn into 9 M680x0 instructions.
+Wrong.  It can be coded in three:
+	movl	_w, a0
+	movl	1(a0), a0
+	movl	(a0), _i
[rest of article omitted]

I specified M68000.  I know that the 68020 relaxes this restriction.
I also think that the T-11 (lsi-11 on a single 8085 style chip) relaxes
this restriction.  There are still lots of 68000 and 68010 chips in use
out here.  Richard's example causes an address error on the M68000.
-- 
					Daniel A. Glasser
					...!ihnp4!chinet!dag
					...!ihnp4!mwc!dag
					...!ihnp4!mwc!gorgon!dag
	One of those things that goes "BUMP!!! (ouch!)" in the night.

woods@tslanpar.UUCP (Greg A. Woods ) (01/26/88)

I'm sorry, but a compile time flag is far from what could solve problems with
imposing structure on external data.  Just how do you expect to compile a
module that must load data into a packed structure on one line, and then
perform an ioctl() call on the next to work?  (Most kernel's and device
drivers won't use packed structures!)  How are you to prevent an unknowing
programmer from doing such a thing?  The "-pack" option of the Microsoft
family of C compilers (yes it's still there in Xenix) was a quick and dirty
fix for Lattice's mistake of packing structures in the first place (it wastes
a lot of CPU time in may implementations).

The only possible solution could be the pseudo-functions: pack() and unpack()
ala Pascal.

I personally have had no problems with bit/byte wise manipulation of external
data (using bitfields and unsigned character arrays).
-- 
						Greg Woods.

UUCP: utgpu!woods, utgpu!{ontmoh, tmsoft!tslanpar}!woods, woods@tslanpar
VOICE: (416) 242-7572		LOCATION: Toronto, Ontario, Canada