Patch: Feature: MSVC-style bit field packing.

Donn Terry donn@interix.com
Tue May 4 08:13:00 GMT 1999


-- 

===================================================
Donn Terry                  mailto:donn@interix.com
Softway Systems, Inc.        http://www.interix.com
2850 McClelland Dr, Ste. 1800   Ft.Collins CO 80525
Tel: +1-970-204-9900           Fax: +1-970-204-9951
===================================================
Feature: MSVC-style bit field packing.

(Yes, I know, it's a feature and we're past the deadline, so Jeff, just
ignore it for now.  However, for others who could use it (Cygwin, e.g.), 
at least it's available.)

The patch below implements MSVC-style bit field packing.

It also provides compile line and attribute options to set the option.

Details:
	MSVC (and maybe other) compilers don't align bitfields to match
	any of the "normal" gcc modes.  In addition, there are good arguments
	on both sides of whether "gnu" or "native" packing should be provided
	(and what the default should be).  Rather than making a commitment...

	I chose the names "native" and "gnu" rather than "native" and
	"no-native" or some other variation both because it seemed more
	extensible and because it asserted what was being done directly.

	As a note: both the alpha and the i386 seem to already use
	PCC_BITFIELD_TYPE_MATTERS as 1, so I did not change that.  However,
	this is set up so that it would be a one line change in the
	the configuration files to make it track the setting of the
	"native" flag I introduce below.  (I don't see a reason to
	provide a bunch of options along these lines: the native mode
	and the gcc default seem to be the only two interesting ones.
	However, if there's more than one native mode, then there may
	be a need.) (No recommendation, just an observation.)

	1)  Add command line --native-struct and --gnu-struct, and
	    corresponding __attribute__ native_struct and gnu_struct.
	    Let the end-user choose which format he wants.  These are
	    exactly parallel to --packed/packed, except that they apply
	    ONLY to whole structs (__attribute__((packed)) can be applied
	    to a single field).
	    tree.h: add new bit native_flag to struct tree_type, 
	      TYPE_NATIVE macro to maintain it.
	    flags.h: add new flag_native_struct for global setting of
	      the mode.
	    c-common.c: add A_NATIVE and A_GCC_PACK to the __attribute__
	      enum, add the keywords in init_attributes, and the cases 
	      to decl_attributes.  These set/clear TYPE_NATIVE
	    c-decl.c: set TYPE_NATIVE from flag_native_struct
	    toplev.c: parse the new keywords (add to table) and initialize
	      flag_native_struct to DEFAULT_NATIVE_STRUCT (0 if not defined)
	      so that the compiler's default can be controlled from the
	      configuration.

	2)  Add a new packing mode, enabled by GROUP_BITFIELDS_BY_ALIGN
	    which aligns bitfields so that fields with underlying type
	    of the same alignment are packed together (but a new alignment
	    starts a new group of bitfields).  (This is an abstraction
	    of the MSVC semantics.)

	3)  C++ was not honoring the command line --packed-struct option
	    (just ignoring it).  It also needed to propigate native type.

	    * cp/decl.c:xref_tag(): set the NATIVE and PACKED bits for the
	      type in the analogous place that in c-decl.c.  (I don't
	      see a reason not to honor the command line option if the
	      attribute works, but if there is one....)
	    * cp/pt.c: instantiate_class_template().  Propigate the 
	      native property the same way packed is.

	4)  (The flags to turn this on for Interix were already present,
	    but needed a tweak.)

	I have a testcase, which I have informally attached.

	Part of the testcase should be compiled with the native
	compiler, part with gcc, and the two linked together and
	run to check the results.  I'll provide a testsuite patch
	if someone will recommend a way to set up that mixed
	compilation in the testsuite environment (or is that asking
	too much?)  The test case is set up to use both the attribute
	and command line forms easily.  As it appears below, it's
	set up for command line.


ChangeLog gcc

Mon Apr 26 12:25:41 1999 Donn Terry (donn@interix.com)

	* flags.h (flag_native_struct): New boolean.
	* tree.h (TYPE_NATIVE): New macro.
	(tree_type): Add native_flag.
	* c-common.c (attrs): Add A_NATIVE, A_GCC_PACK to enum.
	(init_attributes): Set them up.
	(decl_attributes): Parse them, setting flag_native_struct.
	* c-decl.c (start_struct): Propigate TYPE_NATIVE.
	* stor-layout.c (layout_record): Honor GROUP_BITFIELDS_BY_ALIGN.
	* toplev.c (lang_independent_options): Add native-struct and
	gcc-struct flags.
	(flag_native_struct): Initialize.
	* config/i386/i386-interix.h (PCC_BITFIELD_TYPE_TEST): remove.
	* config/alpha/alpha-interix.h (PCC_BITFIELD_TYPE_TEST): remove.

ChangeLog gcc/cp

Mon Apr 26 12:25:41 1999 Donn Terry (donn@interix.com)

	* decl.c (xref_tag): Init TYPE_PACKED and TYPE_NATIVE from globals.
	* pt.c (instantiate_class_template): Propigate TYPE_NATIVE.


diff -drupP egcs.source.pack.reference/gcc/c-common.c egcs.source/gcc/c-common.c
--- egcs.source.pack.reference/gcc/c-common.c	Mon Apr 26 12:25:41 1999
+++ egcs.source/gcc/c-common.c	Mon Apr 26 20:58:50 1999
@@ -54,7 +54,9 @@ int skip_evaluation;
 enum attrs {A_PACKED, A_NOCOMMON, A_COMMON, A_NORETURN, A_CONST, A_T_UNION,
 	    A_NO_CHECK_MEMORY_USAGE, A_NO_INSTRUMENT_FUNCTION,
 	    A_CONSTRUCTOR, A_DESTRUCTOR, A_MODE, A_SECTION, A_ALIGNED,
-	    A_UNUSED, A_FORMAT, A_FORMAT_ARG, A_WEAK, A_ALIAS};
+	    A_UNUSED, A_FORMAT, A_FORMAT_ARG, A_WEAK, A_ALIAS,
+	    A_NATIVE, A_GCC_PACK,
+	    };
 
 enum format_type { printf_format_type, scanf_format_type,
 		   strftime_format_type };
@@ -376,6 +378,8 @@ static void
 init_attributes ()
 {
   add_attribute (A_PACKED, "packed", 0, 0, 0);
+  add_attribute (A_NATIVE, "native_struct", 0, 0, 0);
+  add_attribute (A_GCC_PACK, "gcc_struct", 0, 0, 0);
   add_attribute (A_NOCOMMON, "nocommon", 0, 0, 1);
   add_attribute (A_COMMON, "common", 0, 0, 1);
   add_attribute (A_NORETURN, "noreturn", 0, 0, 1);
@@ -499,6 +503,24 @@ decl_attributes (node, attributes, prefi
 	    DECL_PACKED (decl) = 1;
 	  /* We can't set DECL_PACKED for a VAR_DECL, because the bit is
 	     used for DECL_REGISTER.  It wouldn't mean anything anyway.  */
+	  else
+	    warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
+	  break;
+
+	case A_NATIVE:
+	  /* It only applies to the whole struct */
+	  if (is_type)
+	    TYPE_NATIVE (type) = 1;
+	  /* it doesn't mean anything to variables */
+	  else
+	    warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
+	  break;
+
+	case A_GCC_PACK:
+	  /* It only applies to the whole struct */
+	  if (is_type)
+	    TYPE_NATIVE (type) = 0;
+	  /* it doesn't mean anything to variables */
 	  else
 	    warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
 	  break;
diff -drupP egcs.source.pack.reference/gcc/c-decl.c egcs.source/gcc/c-decl.c
--- egcs.source.pack.reference/gcc/c-decl.c	Mon Apr 26 12:25:42 1999
+++ egcs.source/gcc/c-decl.c	Tue Apr 27 12:10:43 1999
@@ -5743,6 +5743,7 @@ start_struct (code, name)
     {
       C_TYPE_BEING_DEFINED (ref) = 1;
       TYPE_PACKED (ref) = flag_pack_struct;
+      TYPE_NATIVE (ref) = flag_native_struct;
       if (TYPE_FIELDS (ref))
 	error ((code == UNION_TYPE ? "redefinition of `union %s'"
 		: "redefinition of `struct %s'"),
@@ -5757,6 +5758,7 @@ start_struct (code, name)
   pushtag (name, ref);
   C_TYPE_BEING_DEFINED (ref) = 1;
   TYPE_PACKED (ref) = flag_pack_struct;
+  TYPE_NATIVE (ref) = flag_native_struct;
   return ref;
 }
 
diff -drupP egcs.source.pack.reference/gcc/config/alpha/alpha-interix.h egcs.source/gcc/config/alpha/alpha-interix.h
--- egcs.source.pack.reference/gcc/config/alpha/alpha-interix.h	Mon Apr 26 12:23:27 1999
+++ egcs.source/gcc/config/alpha/alpha-interix.h	Tue Apr 27 14:20:48 1999
@@ -206,7 +206,6 @@ while (0)
 #define HOST_PTR_AS_INT unsigned long
 
 #define PCC_BITFIELD_TYPE_MATTERS 1
-#define PCC_BITFIELD_TYPE_TEST TYPE_NATIVE(rec)
 #define GROUP_BITFIELDS_BY_ALIGN TYPE_NATIVE(rec)
 
 /* DWARF2 Unwinding doesn't work with exception handling yet. */
diff -drupP egcs.source.pack.reference/gcc/config/i386/i386-interix.h egcs.source/gcc/config/i386/i386-interix.h
--- egcs.source.pack.reference/gcc/config/i386/i386-interix.h	Mon Apr 26 12:23:37 1999
+++ egcs.source/gcc/config/i386/i386-interix.h	Tue Apr 27 13:24:53 1999
@@ -412,7 +412,6 @@ while (0)
 #define HOST_PTR_AS_INT unsigned long
 
 #define PCC_BITFIELD_TYPE_MATTERS 1
-#define PCC_BITFIELD_TYPE_TEST TYPE_NATIVE(rec)
 #define GROUP_BITFIELDS_BY_ALIGN TYPE_NATIVE(rec)
 
 /* The following two flags are usually "off" for i386, because some non-gnu
diff -drupP egcs.source.pack.reference/gcc/flags.h egcs.source/gcc/flags.h
--- egcs.source.pack.reference/gcc/flags.h	Mon Apr 26 12:25:54 1999
+++ egcs.source/gcc/flags.h	Mon Apr 26 20:58:51 1999
@@ -436,6 +436,9 @@ extern int flag_gnu_linker;
 /* Tag all structures with __attribute__(packed) */
 extern int flag_pack_struct;
 
+/* Tag all structures with __attribute__(native_struct) */
+extern int flag_native_struct;
+
 /* This flag is only tested if alias checking is enabled.
    0 if pointer arguments may alias each other.  True in C.
    1 if pointer arguments may not alias each other but may alias
diff -drupP egcs.source.pack.reference/gcc/stor-layout.c egcs.source/gcc/stor-layout.c
--- egcs.source.pack.reference/gcc/stor-layout.c	Mon Apr 26 12:26:06 1999
+++ egcs.source/gcc/stor-layout.c	Tue Apr 27 13:27:55 1999
@@ -333,6 +333,26 @@ layout_decl (decl, known_align)
    The return value is a list of static members of the record.
    They still need to be laid out.  */
 
+/* To repreise: PCC_BITFIELD_TYPE_MATTERS is a flag (which must be a valid
+   expression) to force alignment to follow a different set of alignment
+   rules (more like "other compilers", whatever that means).
+
+   In some systems, it can be a dynamic test for the "native" flag applied
+   to a structure.  In others it may not be.
+
+   It may also be just part of the solution.  The flag GROUP_BITFIELDS_BY_ALIGN
+   causes structures to be aligned according to additional rules (beyond
+   those of PCC...) (that coincide with Windows alignment). */
+
+/* G_B_F_A means that a change in the required alignment of a sequence
+   of bitfields causes the next one to be aligned to whatever alignment
+   it needs.  Otherwise with P_B_T_M, bitfields are forced to align only
+   by zero-sized fields.  With neither, gcc default alignment (fairly
+   tightly packed), prevails.  So noone goes astray, sanity check. */
+#if defined(GROUP_BITFIELDS_BY_ALIGN) && !defined(PCC_BITFIELD_TYPE_MATTERS)
+You must define PCC_BITFIELD_TYPE_MATTERS to use GROUP_BITFIELDS_BY_ALIGN
+#endif
+
 static tree
 layout_record (rec)
      tree rec;
@@ -351,6 +371,7 @@ layout_record (rec)
   /* Once we start using VAR_SIZE, this is the maximum alignment
      that we know VAR_SIZE has.  */
   register int var_align = BITS_PER_UNIT;
+  int last_align = -1;
 
 #ifdef STRUCTURE_SIZE_BOUNDARY
   /* Packed structures don't need to have minimum size.  */
@@ -415,6 +436,14 @@ layout_record (rec)
 	    record_align = MAX ((int)record_align, desired_align);
 	  else if (! DECL_PACKED (field))
 	    desired_align = TYPE_ALIGN (TREE_TYPE (field));
+#ifdef GROUP_BITFIELDS_BY_ALIGN
+	  if (GROUP_BITFIELDS_BY_ALIGN
+	      && last_align != TYPE_ALIGN(TREE_TYPE(field)))
+	    {
+	      desired_align = MAX(last_align,(int)TYPE_ALIGN (TREE_TYPE (field)));
+	      last_align = TYPE_ALIGN (TREE_TYPE (field));
+	    }
+#endif
 	  /* A named bit field of declared type `int'
 	     forces the entire structure to have `int' alignment.  */
 	  if (DECL_NAME (field) != 0)
@@ -429,7 +458,10 @@ layout_record (rec)
 	    }
 	}
       else
-	record_align = MAX ((int)record_align, desired_align);
+	{
+	  last_align = -1;
+	  record_align = MAX ((int)record_align, desired_align);
+	}
 #endif
 
       /* Does this field automatically have alignment it needs
diff -drupP egcs.source.pack.reference/gcc/toplev.c egcs.source/gcc/toplev.c
--- egcs.source.pack.reference/gcc/toplev.c	Mon Apr 26 12:26:07 1999
+++ egcs.source/gcc/toplev.c	Tue Apr 27 13:21:41 1999
@@ -718,9 +718,15 @@ int flag_gnu_linker = 0;
 int flag_gnu_linker = 1;
 #endif
 
-/* Tag all structures with __attribute__(packed) */
+/* Tag all structures with __attribute__(packed) off */
 int flag_pack_struct = 0;
 
+/* Tag all structures with __attribute__(native_struct) off */
+#ifndef DEFAULT_NATIVE_STRUCT
+#define DEFAULT_NATIVE_STRUCT 0
+#endif
+int flag_native_struct = DEFAULT_NATIVE_STRUCT;
+
 /* Emit code to check for stack overflow; also may cause large objects
    to be allocated dynamically.  */
 int flag_stack_check;
@@ -944,6 +950,10 @@ lang_independent_options f_options[] =
    "Do the full regmove optimization pass"},
   {"pack-struct", &flag_pack_struct, 1,
    "Pack structure members together without holes" },
+  {"native-struct", &flag_native_struct, 1,
+   "Pack structure members consistent with some other (native) compiler" },
+  {"gcc-struct", &flag_native_struct, 0,
+   "Pack structure members using gcc default rules" },
   {"stack-check", &flag_stack_check, 1,
    "Insert stack checking code into the program" },
   {"argument-alias", &flag_argument_noalias, 0,
diff -drupP egcs.source.pack.reference/gcc/tree.h egcs.source/gcc/tree.h
--- egcs.source.pack.reference/gcc/tree.h	Mon Apr 26 12:26:07 1999
+++ egcs.source/gcc/tree.h	Mon Apr 26 20:58:51 1999
@@ -875,10 +875,15 @@ struct tree_block
    the same way that the first union alternative would be passed.  */
 #define TYPE_TRANSPARENT_UNION(NODE) (TYPE_CHECK (NODE)->type.transparent_union_flag)
 
-/* Indicated that objects of this type should be laid out in as
+/* Indicates that objects of this type should be laid out in as
    compact a way as possible.  */
 #define TYPE_PACKED(NODE) (TYPE_CHECK (NODE)->type.packed_flag)
 
+/* Indicates that objects of this type should be layed out as the
+   native compiler does; if a compile line option (or default state)
+   turns this on, then turning it OFF should result in gnu alignment. */
+#define TYPE_NATIVE(NODE) ((NODE)->type.native_flag)
+
 struct tree_type
 {
   char common[sizeof (struct tree_common)];
@@ -900,6 +905,7 @@ struct tree_type
   unsigned needs_constructing_flag : 1;
   unsigned transparent_union_flag : 1;
   unsigned packed_flag : 1;
+  unsigned native_flag : 1;
   unsigned restrict_flag : 1;
 
   unsigned lang_flag_0 : 1;
@@ -909,7 +915,7 @@ struct tree_type
   unsigned lang_flag_4 : 1;
   unsigned lang_flag_5 : 1;
   unsigned lang_flag_6 : 1;
-  /* room for 3 more bits */
+  /* room for 2 more bits */
 
   unsigned int align;
   union tree_node *pointer_to;
diff -drupP egcs.source.pack.reference/gcc/cp/decl.c egcs.source/gcc/cp/decl.c
--- egcs.source.pack.reference/gcc/cp/decl.c	Mon Apr 26 12:24:09 1999
+++ egcs.source/gcc/cp/decl.c	Mon Apr 26 20:58:51 1999
@@ -12657,6 +12657,9 @@ xref_tag (code_type_node, name, globaliz
 	  /* Class types don't nest the way enums do.  */
 	  class_binding_level = (struct binding_level *)0;
 #endif
+	  TYPE_PACKED (ref) = flag_pack_struct;
+	  TYPE_NATIVE (ref) = flag_native_struct;
+
 	  pushtag (name, ref, globalize);
 	  class_binding_level = old_b;
 	}
diff -drupP egcs.source.pack.reference/gcc/cp/pt.c egcs.source/gcc/cp/pt.c
--- egcs.source.pack.reference/gcc/cp/pt.c	Mon Apr 26 12:24:12 1999
+++ egcs.source/gcc/cp/pt.c	Mon Apr 26 20:58:51 1999
@@ -4882,6 +4882,7 @@ instantiate_class_template (type)
   TYPE_USES_VIRTUAL_BASECLASSES (type)
     = TYPE_USES_VIRTUAL_BASECLASSES (pattern);
   TYPE_PACKED (type) = TYPE_PACKED (pattern);
+  TYPE_NATIVE (type) = TYPE_NATIVE (pattern);
   TYPE_ALIGN (type) = TYPE_ALIGN (pattern);
   TYPE_FOR_JAVA (type) = TYPE_FOR_JAVA (pattern); /* For libjava's JArray<T> */
   if (ANON_UNION_TYPE_P (pattern))

-----------------TEST CASE bittest.h --------------------------------------
struct a_type {
   int a;
   unsigned char b;
   unsigned c:7;
   int d;
   unsigned e:3;
   unsigned f:9;
   unsigned char g:7;
   int h;
   unsigned int i:6;
   unsigned int :0;
   unsigned int j:6;
} NATIVE ;

struct b_type {
   int a;
   unsigned char b;
   unsigned int c:7;
   int d;
   unsigned int e:3;
   unsigned int f:9;
   unsigned char g:7;
   int h;
   unsigned char i:6;
   unsigned char :0;
   unsigned char j:6;

} NATIVE ;

struct c_type {
   int a;
   unsigned char b;
   unsigned short c:7;
   int d;
   unsigned short e:3;
   unsigned short f:9;
   unsigned char g:7;
   short h;
   unsigned short i:6;
   unsigned short :0;
   unsigned short j:6;
} NATIVE ;

struct d_type {
   int a:3;
   int b:4;
   int c:4;
   int d:6;
   int e:5;
   int f:5;
   int g:5;
} NATIVE ;

-----------------TEST CASE bittest_nat.c --------------------------------------
#define NATIVE   /* as nothing */
#include "bittest.h"
#include <stddef.h>
#define poffset(a,b) printf("   %s: %d\n", #b, offsetof(struct a,b));

struct a_type a;
struct b_type b;
struct c_type c;
struct d_type d;

int a_offsets[]={offsetof(struct a_type,b),
             offsetof(struct a_type,d),
             offsetof(struct a_type,h),
	     sizeof(struct a_type)};

int b_offsets[]={offsetof(struct b_type,b),
             offsetof(struct b_type,d),
             offsetof(struct b_type,h),
	     sizeof(struct b_type)};

int c_offsets[]={offsetof(struct c_type,b),
             offsetof(struct c_type,d),
             offsetof(struct c_type,h),
	     sizeof(struct c_type)};

main()
{

    a.a=1;
    a.b=2;
    a.c=3;
    a.d=4;
    a.e=5;
    a.f=6;
    a.g=7;
    a.h=8;
    a.i=9;
    a.j=10;

    b.a=1;
    b.b=2;
    b.c=3;
    b.d=4;
    b.e=5;
    b.f=6;
    b.g=7;
    b.h=8;
    b.i=9;
    b.j=10;

    c.a=1;
    c.b=2;
    c.c=3;
    c.d=4;
    c.e=5;
    c.f=6;
    c.g=7;
    c.h=8;
    c.i=9;
    c.j=10;

    d.a=1;
    d.b=2;
    d.c=3;
    d.d=4;
    d.e=5;
    d.f=6;
    d.g=7;

    check_results();
}
-----------------TEST CASE bittest_gcc.c --------------------------------------
#define NATIVE 
//#define NATIVE __attribute__((__native_struct__))
#include "bittest.h"
#include <stddef.h>
#define check(s,f,v) if (s.f != v) { \
        printf(#s"."#f " was %d not " #v "\n", s.f); exit_code = 1;}
#define ckoff(a) for (i=0; i<3; i++) { \
		if (a##_offsets[i] != my_##a##_offsets[i]) {\
		    printf(#a" offset #%d did not match: nat: %d v gcc: %d\n", \
			i, a##_offsets[i], my_##a##_offsets[i]); \
			exit_code = 1; } }

extern int a_offsets[];
extern int b_offsets[];
extern int c_offsets[];

int my_a_offsets[]={offsetof(struct a_type,b),
             offsetof(struct a_type,d),
             offsetof(struct a_type,h),
	     sizeof(struct a_type)};

int my_b_offsets[]={offsetof(struct b_type,b),
             offsetof(struct b_type,d),
             offsetof(struct b_type,h),
	     sizeof(struct b_type)};

int my_c_offsets[]={offsetof(struct c_type,b),
             offsetof(struct c_type,d),
             offsetof(struct c_type,h),
	     sizeof(struct c_type)};

extern struct a_type a;
extern struct b_type b;
extern struct c_type c;
extern struct d_type d;

check_results()
{
    int i;
    int exit_code = 0;

    ckoff(a);
    ckoff(b);
    ckoff(c);

    check(a,a,1);
    check(a,b,2);
    check(a,c,3);
    check(a,d,4);
    check(a,e,5);
    check(a,f,6);
    check(a,g,7);
    check(a,h,8);
    check(a,i,9);
    check(a,j,10);

    check(b,a,1);
    check(b,b,2);
    check(b,c,3);
    check(b,d,4);
    check(b,e,5);
    check(b,f,6);
    check(b,g,7);
    check(b,h,8);
    check(b,i,9);
    check(b,j,10);

    check(c,a,1);
    check(c,b,2);
    check(c,c,3);
    check(c,d,4);
    check(c,e,5);
    check(c,f,6);
    check(c,g,7);
    check(c,h,8);
    check(c,i,9);
    check(c,j,10);

    check(d,a,1);
    check(d,b,2);
    check(d,c,3);
    check(d,d,4);
    check(d,e,5);
    check(d,f,6);
    check(d,g,7);

    exit(exit_code);
}
-----------------TEST CASE driver script --------------------------------------
cc -c bittest_nat.c
# be sure to point gcc at the right gcc.

#This should pass and say nothing.
gcc -c -fnative-struct bittest_gcc.c 
echo NATIVE:
gcc bittest_nat.o bittest_gcc.o
a.out

#...This should fail and list errors.
gcc -c -fgcc-struct bittest_gcc.c 
echo GNU:
gcc bittest_nat.o bittest_gcc.o
a.out


More information about the Gcc-patches mailing list