-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
translate-c: Translate clang packed struct C into Zig extern struct with align(1) #12745
translate-c: Translate clang packed struct C into Zig extern struct with align(1) #12745
Conversation
This fails with unused variable in debug:
|
02ebeba
to
fb09af8
Compare
Packed structs in the stage 2 compiler do not support pub const struct_foo = extern struct {
a: u8 align(1),
b: c_int align(1),
};
pub const struct_bar = extern struct {
a: u8 align(1),
b: c_int align(1),
c: c_short align(1),
d: c_long align(8),
}; |
Interesting. Would it be possible to support all packed structs this way (not having to deal with the "ABI-sized" restriction?) |
The ABI-sized restriction is related to Zig's packed structs which are fancy integers, not C's packed structs which are just differently aligned regular structs. This is confusing and why IMO Zig's packed structs should not be extern compatible. |
This comment was marked as outdated.
This comment was marked as outdated.
fb09af8
to
8c61a8e
Compare
As @ifreund and @Vexu both noticed, Zig I've also read the GCC docs again, to try and verify that interpreting C It appears Furthermore, the alignment requirement is the only thing stated about packing a field. This means it should be safe to translate to Zig I will update the commit with the new translation to (Also thanks Andrew for rebasing onto |
8c61a8e
to
25c05b6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good but needs tests. The layout option from the AST should also just be removed entirely since stage1 also supports align(1)
on extern structs.
25c05b6
to
e86c2e0
Compare
Rebased onto master... |
e86c2e0
to
4862630
Compare
Alright I've fixed conflicts, removed the stage1 checks and re-enabled the previously disabled tests. I also added several new tests for translation of packed unions. |
70cc493
to
e93e695
Compare
Superceeds PR ziglang#12735 (now supporting all packed structs in GNU C) Fixes issue ziglang#12733 This stops translating C packed struct as a Zig packed struct. Instead use a regular `extern struct` with `align(1)`. This is because (as @Vexu explained) Zig packed structs are really just integers (not structs). Alignment issue is more complicated. I think @ifreund was the first to notice it in his comment on PR ziglang#12735 Justification of my interpretion of the C(lang) behavior comes from a careful reading of the GCC docs for type & variable attributes: (clang emulates gnu's packed attribute here) The final line of the documentation for __attribute__ ((aligned)) [on types] says: > When used on a struct, or struct member, *the aligned attribute can only increase the alignment*; in order to decrease it, the packed attribute must be specified as well. This implies that GCC uses the `packed` attribute for alignment purposes in addition to eliminating padding. The documentation for __attribute__((packed)) [on types], states: > This attribute, attached to a struct, union, or C++ class type definition, specifies that each of its members (other than zero-width bit-fields) is placed to minimize the memory required. **This is equivalent to specifying the packed attribute on each of the members**. The key is resolving this indirection, and looking at the documentation for __attribute__((packed)) [on fields (wierdly under "variables" section)]: > The packed attribute specifies that a **structure member should have the smallest possible alignment** — one bit for a bit-field and one byte otherwise, unless a larger value is specified with the aligned attribute. The attribute does not apply to non-member objects. Furthermore, alignment is the only effect of the packed attribute mentioned in the GCC docs (for "common" architecture). Based on this, it seems safe to completely substitute C 'packed' with Zig 'align(1)'. Target-specific or undocumented behavior potentially changes this. Unfortunately, the current implementation of `translate-c` translates as `packed struct` without alignment info. Because Zig packed structs are really integers (as mentioned above), they are the wrong interpretation and we should be using 'extern struct'. Running `translate-c` on the following code: ```c struct foo { char a; int b; } __attribute__((packed)); struct bar { char a; int b; short c; __attribute__((aligned(8))) long d; } __attribute__((packed)); ``` Previously used a 'packed struct' (which was not FFI-safe on stage1). After applying this change, the translated structures have align(1) explicitly applied to all of their fields AS EXPECTED (unless explicitly overriden). This makes Zig behavior for `tranlsate-c` consistent with clang/GCC. Here is the newly produced (correct) output for the above example: ```zig pub const struct_foo = extern struct { a: u8 align(1), b: c_int align(1), }; pub const struct_bar = extern struct { a: u8 align(1), b: c_int align(1), c: c_short align(1), d: c_long align(8), }; ``` Also note for reference: Since the last stable release (0.9.1), there was a change in the language spec related to the alignment of packed structures. The docs for Zig 0.9.1 read: > Packed structs have 1-byte alignment. So the old behavior of translate-c (not specifying any alignment) was possibly correct back then. However the current docs read: > Packed structs have the same alignment as their backing integer Suggsestive both to the change to an integer-backed representation which is incompatible with C's notation.
Was missing test coverage before this (although it was suppored)
e93e695
to
a560af9
Compare
This is relevant to the fix and discussion in #12735 (and supersedes it).
@ifreund noticed that
align(1)
is missing fromtranslate-c
output. Hoewever that bug is present with both stage1 & stage2 and is technically independent from that other PR.Until PR #12735 is applied (or some other fix to #12733),packed struct
will only work with translate-c on stage1.I'm hoping this will help work towards gettingpacked struct
to supportextern
(but I don't really understand the details too much).EDIT: Zig's notion of packed struct is an integer e C's notion of packed struct is essentially an
extern struct
with every field set toalign(1)
(as @ifreund already noticed below).Relevant Mismatch with Zig Language Spec (things changed)
Since the last stable release (0.9.1), there was a change in the language spec related to the alignment of packed structures.
The docs for Zig 0.9.1 read:
So the old behavior of translate-c (not specifying any alignment) was possibly correct back then.
However the current docs read:
So now
translate-c
definitely needs to specifyalign(1)
for every field (unless the field has an explicitly specified alignment)Unfortunately, the current implementation of
translate-c
does not reflect this.EDIT: Also, packed structs are now interpreted simply as wrappers around integers (issue #10113). This means instead of translating
__attribute___(packed) struct
to be anpacked struct
it needs to be anextern struct
Running
translate-c
on the following code:does not apply any alignment attributes to any fields except d.
Justification for C behavior of
align(1)
This comes from a careful reading of the GCC docs for type & variable attributes.
The final line of the documentation for
__attribute__ ((aligned))
[on types] says:This implies that GCC uses the
packed
attribute for alignment purposes in addition to eliminating padding.The documentation for
__attribute__((packed))
[on types], states that "This is equivalent to specifying the packed attribute on each of the members."The key is resolving this indirection, and looking at the documentation for
__attribute__((packed))
[on fields]:(NOTE: Somewhat confusingly, field attributes are documented under "variables" in GCC...)
This means that a C structure with
__attribute__((packed)
is shorthand for__attribute__((packed))
on each member, which is equivalent to Zig'salign(1)
on each of its members.Fixed Behavior
Sometimes attribute((packed)) is used only for alignment purposes.
In particular, it can be used as a semi-portable way to do unaligned stores
The
stdlib.h
headers do this on aarch64-macos.12.The structures in the MacOS headers were being used chiefly for their alignment
properties, not for padding purposes (they only had one field).
After applying this change, the translated structures have align(1)
explicitly applied to all of their fields (unless explicitly overriden).
This makes Zig behavior for
tranlsate-c
consistent with clang/GCC.Here is the newly produced (correct) output for the above example:EDIT: Changed from
packed struct
toextern struct
.This is closely related with PR #12735
EDIT 1: Updated to reflect need to change from
packed struct
toextern struct
(see comment below).