https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991

Jakub Jelinek <jakub at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |jakub at gcc dot gnu.org

--- Comment #27 from Jakub Jelinek <jakub at gcc dot gnu.org> ---
The #c26 tests in a single testcase:

#ifdef _WIN32
# ifdef _MSC_VER
#  define PACK(typeDec) __pragma( pack(push, 1) ) typeDec __pragma( pack(pop) )
# else
#  define PACK(typeDec) typeDec __attribute__((__packed__,ms_struct))
# endif
#else
# define PACK(typeDec) typeDec __attribute__((__packed__))
#endif

#ifdef _MSC_VER
# define ALIGN(typeDec, n) __declspec(align(n)) typeDec
#else
# define ALIGN(typeDec, n) typeDec __attribute__((aligned(n)))
#endif

#define assert_cc(expr) extern char c[(expr) ? 1 : -1]
#define offsetof(x, y) __builtin_offsetof (x, y)

PACK(struct test_sp1 {
    int a;
    short b;
    int c;
    char d;
});

assert_cc(sizeof(struct test_sp1) == 11);
assert_cc(offsetof(struct test_sp1, a) == 0);
assert_cc(offsetof(struct test_sp1, b) == 4);
assert_cc(offsetof(struct test_sp1, c) == 6);
assert_cc(offsetof(struct test_sp1, d) == 10);

PACK(struct test_sp3 {
    int a;
    ALIGN(short b, 8);
    int c;
    char d;
});

assert_cc(sizeof(struct test_sp3) == 16);
assert_cc(offsetof(struct test_sp3, a) == 0);
assert_cc(offsetof(struct test_sp3, b) == 8);
assert_cc(offsetof(struct test_sp3, c) == 10);
assert_cc(offsetof(struct test_sp3, d) == 14);

struct test_s4 {
    int a;
    short b;
    int c:15;
    char d;
};

#ifdef _WIN32
assert_cc(sizeof(struct test_s4) == 16);
assert_cc(offsetof(struct test_s4, a) == 0);
assert_cc(offsetof(struct test_s4, b) == 4);
assert_cc(offsetof(struct test_s4, d) == 12);
#else
assert_cc(sizeof(struct test_s4) == 12);
assert_cc(offsetof(struct test_s4, a) == 0);
assert_cc(offsetof(struct test_s4, b) == 4);
assert_cc(offsetof(struct test_s4, d) == 8);
#endif

but rather with r114364 which revamped the ms bitfield layout completely.
See https://gcc.gnu.org/ml/gcc-patches/2006-04/msg01064.html for more info.
The above testcase indeed passes with -D_WIN32 -mms-bitfields when built with
GCC 4.1 or earlier.

Ignoring the type_align bump in place_field for DECL_PACKED fields sounds
reasonable, just the formatting is wrong (needs a space in between DECL_PACKED
and (field)).

In the second hunk, the formatting is also wrong (e.g. a single statement body
of else shouldn't be wrapped with {}s around it), but more importantly it is
unclear why that would be the right thing, e.g. why is ignoring
maximum_field_alignment the right thing etc.

So, for the first two hunks in #c26 I'd go with:
--- gcc/stor-layout.c.jj        2018-02-22 14:35:33.135216198 +0100
+++ gcc/stor-layout.c   2018-02-27 15:14:03.271387867 +0100
@@ -1038,7 +1038,7 @@ update_alignment_for_field (record_layou
         the type, except that for zero-size bitfields this only
         applies if there was an immediately prior, nonzero-size
         bitfield.  (That's the way it is, experimentally.) */
-      if ((!is_bitfield && !DECL_PACKED (field))
+      if (!is_bitfield
          || ((DECL_SIZE (field) == NULL_TREE
               || !integer_zerop (DECL_SIZE (field)))
              ? !DECL_PACKED (field)
@@ -1047,7 +1047,10 @@ update_alignment_for_field (record_layou
                 && ! integer_zerop (DECL_SIZE (rli->prev_field)))))
        {
          unsigned int type_align = TYPE_ALIGN (type);
-         type_align = MAX (type_align, desired_align);
+         if (!is_bitfield && DECL_PACKED (field))
+           type_align = desired_align;
+         else
+           type_align = MAX (type_align, desired_align);
          if (maximum_field_alignment != 0)
            type_align = MIN (type_align, maximum_field_alignment);
          rli->record_align = MAX (rli->record_align, type_align);
@@ -1555,7 +1558,8 @@ place_field (record_layout_info rli, tre
            }

          /* Now align (conventionally) for the new type.  */
-         type_align = TYPE_ALIGN (TREE_TYPE (field));
+         if (! DECL_PACKED (field))
+           type_align = TYPE_ALIGN (TREE_TYPE (field));

          if (maximum_field_alignment != 0)
            type_align = MIN (type_align, maximum_field_alignment);

instead.

The #c26 third hunk is I presume instead something that was probably broken
only in GCC 4.6 with r184409 aka PR52238 fix.  That 
if (targetm.ms_bitfield_layout_p (rli->t))
  rli->prev_field = NULL;
in that patch looks wrong to me, you lose all the special handling of
rli->prev_field that way.

Before trying to deal with that, I think it is important to figure out what VC
does for the case when a bitfield is followed by another bitfield with the same
underlying type size, but higher alignment.

E.g. how is:
struct S {
  int a : 2;
  __declspec(align(8)) int b : 2;
  int c : 28;
  __declspec(align(16)) int d : 2;
  int e : 30;
} s;
int a = sizeof (struct S);
void f1 (int x) { s.a = x; }
void f2 (int x) { s.b = x; }
void f3 (int x) { s.c = x; }
void f4 (int x) { s.d = x; }
void f5 (int x) { s.e = x; }
compiled by VC?  Godbolt.org seems to be down right now, checking some clang
version with -fms-extensions -mms-bitfields shows that the differences in type
alignments don't break the bitfield packs, a, b, c are all packed into a single
32-bit word at offset 0, then d and e are packed into another 32-bit word at
offset 16 (so the alignment is honored for the first bitfield in the pack).

If that is what VC does, then one way to deal with this would be to replace:
  if (known_align < desired_align)
with
  if (known_align < desired_align
      && (!targetm.ms_bitfield_layout_p (rli->t)
          || rli->prev_field == NULL))
drop the
      if (targetm.ms_bitfield_layout_p (rli->t))
        rli->prev_field = NULL;
and deal with the further alignment (basically duplicate the if (known_align <
desired_align body)) later.

Reply via email to