translate-c: packed struct implies align(1) on every field
Superceeds PR #12735 (now supporting all packed structs in GNU C) Fixes issue #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 #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.
This commit is contained in:
@@ -250,20 +250,18 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
|
||||
\\}
|
||||
, "");
|
||||
|
||||
if (@import("builtin").zig_backend == .stage1) {
|
||||
cases.add("struct initializer - packed",
|
||||
\\#define _NO_CRT_STDIO_INLINE 1
|
||||
\\#include <stdint.h>
|
||||
\\#include <stdlib.h>
|
||||
\\struct s {uint8_t x,y;
|
||||
\\ uint32_t z;} __attribute__((packed)) s0 = {1, 2};
|
||||
\\int main() {
|
||||
\\ /* sizeof nor offsetof currently supported */
|
||||
\\ if (((intptr_t)&s0.z - (intptr_t)&s0.x) != 2) abort();
|
||||
\\ return 0;
|
||||
\\}
|
||||
, "");
|
||||
}
|
||||
cases.add("struct initializer - packed",
|
||||
\\#define _NO_CRT_STDIO_INLINE 1
|
||||
\\#include <stdint.h>
|
||||
\\#include <stdlib.h>
|
||||
\\struct s {uint8_t x,y;
|
||||
\\ uint32_t z;} __attribute__((packed)) s0 = {1, 2};
|
||||
\\int main() {
|
||||
\\ /* sizeof nor offsetof currently supported */
|
||||
\\ if (((intptr_t)&s0.z - (intptr_t)&s0.x) != 2) abort();
|
||||
\\ return 0;
|
||||
\\}
|
||||
, "");
|
||||
|
||||
cases.add("cast signed array index to unsigned",
|
||||
\\#include <stdlib.h>
|
||||
|
||||
Reference in New Issue
Block a user