Padding involved

Discussion in 'C Programming' started by anish singh, Mar 7, 2014.

  1. Change made in source and recompiled/executed:

    % cat foo.c
    #include <stdio.h>
    #define alignof(t) __alignof__(t)
    struct twodouble { double x; double y; };
    int main(void) {
    printf("alignof(double) = %zu\n", alignof(double));
    printf("alignof(double[2]) = %zu\n", alignof(double[2]));
    printf("alignof(double complex) = %zu\n", alignof(double _Complex));
    printf("alignof(struct twodouble) = %zu\n", alignof(struct twodouble));
    }
    % gcc -std=c99 -pedantic -W -Wall foo.c
    % ./a.out
    alignof(double) = 8
    alignof(double[2]) = 8
    alignof(double complex) = 8
    alignof(struct twodouble) = 4
    My GCC doesn't support _Alignof(), but __alignof__ should be the same.

    It's possible that, for a related reason, GCC doesn't provide the
    expected alignment for struct twodouble. Unfortunately, I can't upgrade
    it right now to see if the result has changed in more recent versions.

    S
     
    Stephen Sprunk, Mar 31, 2014
    #61
    1. Advertisements

  2. Interesting. I get the same output on my 32-bit system after replacing
    __alignof__ by _Alignof and compiling with -std=c11.

    It doesn't seem to me to make sense for struct twodouble to have a
    weaker alignment requirement than double, but I'm not sure it violates
    the standard. I don't see a specific statement that the alignment of a
    struct must be at least the alignment of any of its members, and as long
    as the generated code works (this is on an x86, which permits arbitrary
    alignments with some performance penalty), it's likely conforming.
     
    Keith Thompson, Mar 31, 2014
    #62
    1. Advertisements

  3. Even stranger, gcc's value for _Offsetof seems to be inconsistent with
    the alignment it actually uses:

    #include <stdio.h>
    #include <stddef.h>

    #define ALIGNOF(type) (offsetof(struct { char c; type t; }, t))

    struct twodouble { double x; double y; };

    int main(void) {
    printf("_Alignof(double) = %zu\n", _Alignof(double));
    printf("ALIGNOF(double) = %zu\n", ALIGNOF(double));
    printf("_Alignof(struct twodouble) = %zu\n", _Alignof(struct twodouble));
    printf("ALIGNOF(struct twodouble) = %zu\n", ALIGNOF(struct
    twodouble));
    }

    The output (on a 32-bit x86 system with gcc 4.7.0):

    _Alignof(double) = 8
    ALIGNOF(double) = 4
    _Alignof(struct twodouble) = 4
    ALIGNOF(struct twodouble) = 4

    So gcc claims (via the result of the _Offsetof operator) that double has
    an 8-byte alignment, but it only imposes a 4-byte alignment when it's
    a struct member. That looks like a bug to me.
     
    Keith Thompson, Mar 31, 2014
    #63
  4. anish singh

    Kaz Kylheku Guest

    Bug #52023:

    http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52023

    Reported by Paul Eggert in January 2012.

    "On x86, alignof (double) should be 4, since that's the alignment
    requirement used for 'double' when laying out structures. But with
    GCC alignof incorrectly returns 8."

    Fixed for gcc 4.9, in December 2013.
     
    Kaz Kylheku, Mar 31, 2014
    #64
  5. Very interesting; thanks.

    Does(Did) GCC actually use 4- or 8-byte alignment for doubles when _not_
    a member of a structure? For arrays of double?

    S
     
    Stephen Sprunk, Mar 31, 2014
    #65
  6. AIUI, such additions appear because that was the intent (or at least
    known practice) all along but someone discovered it wasn't actually
    stated and asked for clarification.
    I wouldn't expect array dimension to affect alignment, and IMHO most
    people would assume an object would have the same alignment as an array
    of one object (and vice versa). It seems worth saying so explicitly,
    though, based on the debate here.
    That seems sufficient, but wouldn't it be simpler for them to just say
    so explicitly if that was the intent?

    S
     
    Stephen Sprunk, Mar 31, 2014
    #66
  7. anish singh

    James Kuyper Guest

    Yes, and I wouldn't object to adding such text, at least as a footnote.

    However, realize that there's a wide variety of other interesting
    mathematical results that can be derived from those same facts. For
    instance, in C2011, the requirement was added that alignment
    requirements had to be powers of 2, which matches what essentially all
    known implementations of C actually do, or have ever actually done.
    However, prior to that change, a conforming implementation could have
    had alignment requirements for short and long that were 3 bytes and 5
    bytes, respectively. From C99, it could be proven, using the same set of
    facts mentioned above, that any struct or union containing both a short
    and a long must have a alignment requirement when using that
    implementation, which is an integer multiple of both 3 and 5, and
    therefore must be a multiple of 15.

    Should the C99 standard have explicitly said something that would lead
    to that conclusion more directly, perhaps by expressing the relationship
    in terms of LCM (least common multiple)? I know from personal experience
    that very few people were aware of that fact. More than a few people
    argued with me about this when I brought it up. I think this was mainly
    because most of them had the "power of 2" rule already engraved in their
    brains, long before it was officially adopted as a requirement. When a
    and b are non-negative integer powers of two, LCM(a,b) == MAX(a,b),
    which is what most people actually used when thinking about such issues.

    How do you decide which technically redundant facts need to be
    explicitly spelled out, and which ones should be left for readers to
    derive for themselves?
     
    James Kuyper, Mar 31, 2014
    #67
  8. anish singh

    Phil Carmody Guest

    If gcc has gone all anti-FPU, and pro VFP, then I'm surprised that the
    alignment isn't 8 bytes, as at least in several generations of the
    vector instructions non-aligned loads and stores instructions were a
    performance hit. Optimising for packing in structs, but for speed
    elsewhere does seem to make a little bit of sense in a twisted-x86
    kinda way. However, aligning anything at a finer granularity than
    the _Alignof is just plain wrong. (So this is now a bug no matter
    what Richard Henderson says on that bugzilla.)

    Do the VFP-related switches change the behaviour?

    Seems like Keith's at least in part actively on the case:
    http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54188

    Phil
     
    Phil Carmody, Apr 1, 2014
    #68
  9. anish singh

    Phil Carmody Guest

    Don't say that - there are probably corners of N-bit la-la-land
    (for N itself not a power of 2) for which it hasn't always held!
    I can't remember what some old 24-bit DSPs did when they were
    compiled to pack strings (3 chars per word), as the whole concept
    of doing dodgy stuff that required knowledge of alignment was so
    taboo that nobody ever seemed to do it. You were either dealing
    with words, or you were dealing with packed strings, there was
    never a sane context where you'd need to deal with either.
    I'd say this can be derived from basic principles, as it
    seems rather obvious (including the LCM aspect you mention,
    but my background is pure mathematics).

    Phil
     
    Phil Carmody, Apr 1, 2014
    #69
  10. Not that I know of.

    FWIW, GCC can auto-vectorize array accesses regardless of the array's
    alignment; it uses non-vector ops for any initial unaligned elements,
    then switches to vector ops for the main loop, and then switches back to
    non-vector ops for any remainder at the end. That's the only portable
    way to do it. If you want a guarantee that your entire array is
    properly aligned and sized for vector ops, then use GCC's explicit
    vector types.

    S
     
    Stephen Sprunk, Apr 3, 2014
    #70
  11. Could one have addressed that by stating the struct's alignment must be
    a positive integer multiple of each* member's alignment?

    (* Or should that be "every"? Neither sounds exactly correct.)

    Once you require power-of-two alignments, though, it suffices to say
    that the struct's alignment must be at least as strict as all of its
    members.
    Good point, though the argument for LCM would be clearer if your example
    alignments weren't relatively prime, i.e. if GCF(a,b) != 1.

    Now, of course, LCM(a,b) == MAX(a,b) and GCF(a,b) == MIN(a,b).
    I suppose that's a judgment call, but I found it surprising that when
    searching N1570 for "alignment" and then "struct", I found absolutely
    nothing useful about how structs needed to be aligned. That seems to
    indicate they erred on the side of not enough redundancy, but I can
    appreciate that redundancy introduces a risk of inconsistency.

    S
     
    Stephen Sprunk, Apr 4, 2014
    #71
  12. anish singh

    James Kuyper Guest

    I think "every" would be correct.
    Which is why I had to hark back to an earlier version of the standard to
    come up with an example of something that is less obvious.
    I suppose it would be clearer what the general rule is if a and b have
    some factors in common, and each one has some factors not in common with
    the other; such as 4 and 6, with an LCM of 12.
    In standardese, that's handled by distinguishing between normative and
    non-normative text. The normative text should never say the same thing
    two different ways, because of the potential problems if the two ways
    turn out to be subtly in conflict with each other. However, you can put
    redundant statements in non-normative text, such as footnotes, and if
    they turn out to conflict with the normative text, the normative text is
    right, and the non-normative text is not. The accuracy of the standard
    itself isn't in danger, just that of the individual footnotes.
     
    James Kuyper, Apr 4, 2014
    #72
  13. anish singh

    Tim Rentsch Guest

    I see. How the comment is phrased makes it sound more like it's
    meant to be an argument than a restatement. So you might want
    to make that distinction more clear. But let's move on.
    In the absence of any requirement that constrains an alignment
    further, an implementation is free to choose the alignment of an
    array type to be whatever it wants, subject to certain base
    conditions (specifically the alignment of the element type must
    evenly divide the alignment of the array type, and the alignment
    of the array type must evenly divided the size of the type as a
    whole, ie, a single instance of that array type). So, unless
    there is such a requirement, the example program may be subject
    to undefined behavior (because the rule for converting pointer
    types mentions undefined behavior explicitly when there are
    alignment problems). I think it's easy to see that there is no
    such requirement in C99 -- simply check each occurrence of
    "alignment" in the text, plus a review of the rules for pointer
    conversion in section 6.3.2.3. Is there something you can point
    to that changes that? If you can't, then I think it's reasonable
    to say the point is settled and the above example program is not
    strictly conforming under a strict technical reading of the
    Standard.

    By the way, I agree with your (implied?) point that the example
    program is likely to work as expected on all currently existing
    implementations, that most C developers expect that it would, and
    that this result is what most of the Standards' authors would
    expect also, at least as an unconscious assumption. But I don't
    think that result is guaranteed by a strict c.s.c-type reading
    of C99 (or C90 either, but I haven't gone back to verify that).
     
    Tim Rentsch, Apr 14, 2014
    #73
  14. anish singh

    Tim Rentsch Guest

    This isn't exactly right. The alignments of non-bit-field struct
    members are implementation-defined, and the results of _Alignof
    in C11 are implementation-defined, but the alignments of types
    are not, and have never been, implementation-defined. Indeed if
    the alignment of types were implementation-defined, there would
    be no reason for the Stadard to say that the results of _Alignof
    are implementation-defined, which it does in 6.5.3.4 p5.
     
    Tim Rentsch, Apr 14, 2014
    #74
  15. anish singh

    Tim Rentsch Guest

    Here "each" is better. The reason is the alignments in the
    different cases are not the same but depend on the member.
    Compare, for example,

    "Every student worked on the class project."

    and

    "The professor will grade the homeworks of each student."

    In the first case we say "every student" because they were all
    working as a group. But in the second case it would be wrong to
    say "the homeworks of every student" because students do homework
    by themselves, not as a group, and are graded individually.
    Similarly the alignment of a struct will be a positive integer
    multiple of the alignment of each member - not the same alignment,
    or the same multiple, but in each case the one will be some
    multiple of the other. There isn't a single alignment for every
    member, only for each member.
     
    Tim Rentsch, Apr 14, 2014
    #75
  16. But "every" also implies that no-one was left out, such as being
    sick on those days.
    In this case, it doesn't imply that the professor graded "every"
    student, maybe the TA graded some of them.

    But even more, I have known professors that graded mulitple student
    projects as a group, such that everyone got the same grade.

    In one case I know (CS138) it was intended to force students
    to work together cooperatively. As many projects depend on the
    contribution of all members, especially programming projects,
    it makes some sense.
    -- glen
     
    glen herrmannsfeldt, Apr 14, 2014
    #76
  17. This is a minor point, and getting off-topic, but ...
    The phrase "every member's alignment" could imply that the members,
    as a group, have an alignment that they share. The phrase "each
    member's alignment" more clearly implies that each member has its
    own individual alignment, and that the struct's alignment is a
    positive integer multiple of the first member's alignment *and*
    of the second member's alignment *and* ....

    It's not a very strong implication either way, and it would take a
    perverse reading to interpret "every" in the way I suggested, but
    I find "each" clearer and more precise than "every" in this context.
     
    Keith Thompson, Apr 14, 2014
    #77
  18. anish singh

    Tim Rentsch Guest

    I almost forgot - the alignment of a struct is some positive
    integer multiple of each non-bit-field member's alignment.
     
    Tim Rentsch, Apr 17, 2014
    #78
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.