authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-04-16 04:56:22-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-04-16 04:56:22-04:00
log3746b3d93ce895131ab1b2dea5391e5efa9fccf7
tree0e7df5a25d1706e050ef806ef97be69f0a4c553f
parentae38fc6a50dfcece964d7d19d9e35ac5dc583daa
parentbd38c417fc9ba367b3cfcd49d29b52412ef04251
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #21741 from kj4tmp/langref-packed-structs

langref: improve packed struct memory layout description

2 files changed, 44 insertions(+), 19 deletions(-)

doc/langref.html.in+25-19
......@@ -1649,6 +1649,7 @@ unwrapped == 1234{#endsyntax#}</pre>
16491649 <li>{#link|Floats#}</li>
16501650 <li>{#link|bool|Primitive Types#}</li>
16511651 <li>{#link|type|Primitive Types#}</li>
1652 <li>{#link|packed struct#}</li>
16521653 </ul>
16531654 </td>
16541655 <td>
......@@ -2224,20 +2225,24 @@ or
22242225
22252226 {#header_open|packed struct#}
22262227 <p>
2227 Unlike normal structs, {#syntax#}packed{#endsyntax#} structs have guaranteed in-memory layout:
2228 {#syntax#}packed{#endsyntax#} structs, like {#syntax#}enum{#endsyntax#}, are based on the concept
2229 of interpreting integers differently. All packed structs have a <strong>backing integer</strong>,
2230 which is implicitly determined by the total bit count of fields, or explicitly specified.
2231 Packed structs have well-defined memory layout - exactly the same ABI as their backing integer.
2232 </p>
2233 <p>
2234 Each field of a packed struct is interpreted as a logical sequence of bits, arranged from
2235 least to most significant. Allowed field types:
22282236 </p>
22292237 <ul>
2230 <li>Fields remain in the order declared, least to most significant.</li>
2231 <li>There is no padding between fields.</li>
2232 <li>Zig supports arbitrary width {#link|Integers#} and although normally, integers with fewer
2233 than 8 bits will still use 1 byte of memory, in packed structs, they use
2234 exactly their bit width.
2235 </li>
2236 <li>{#syntax#}bool{#endsyntax#} fields use exactly 1 bit.</li>
2238 <li>An {#link|integer|Integers#} field uses exactly as many bits as its
2239 bit width. For example, a {#syntax#}u5{#endsyntax#} will use 5 bits of
2240 the backing integer.</li>
2241 <li>A {#link|bool|Primitive Types#} field uses exactly 1 bit.</li>
22372242 <li>An {#link|enum#} field uses exactly the bit width of its integer tag type.</li>
22382243 <li>A {#link|packed union#} field uses exactly the bit width of the union field with
22392244 the largest bit width.</li>
2240 <li>Packed structs support equality operators.</li>
2245 <li>A {#syntax#}packed struct{#endsyntax#} field uses the bits of its backing integer.</li>
22412246 </ul>
22422247 <p>
22432248 This means that a {#syntax#}packed struct{#endsyntax#} can participate
......@@ -2245,10 +2250,11 @@ or
22452250 This even works at {#link|comptime#}:
22462251 </p>
22472252 {#code|test_packed_structs.zig#}
2248
22492253 <p>
2250 The backing integer is inferred from the fields' total bit width.
2251 Optionally, it can be explicitly provided and enforced at compile time:
2254 The backing integer can be inferred or explicitly provided. When
2255 inferred, it will be unsigned. When explicitly provided, its bit width
2256 will be enforced at compile time to exactly match the total bit width of
2257 the fields:
22522258 </p>
22532259 {#code|test_missized_packed_struct.zig#}
22542260
......@@ -2290,18 +2296,18 @@ or
22902296
22912297 <p>
22922298 Equating packed structs results in a comparison of the backing integer,
2293 and only works for the `==` and `!=` operators.
2299 and only works for the {#syntax#}=={#endsyntax#} and {#syntax#}!={#endsyntax#} {#link|Operators#}.
22942300 </p>
22952301 {#code|test_packed_struct_equality.zig#}
22962302
22972303 <p>
2298 Using packed structs with {#link|volatile#} is problematic, and may be a compile error in the future.
2299 For details on this subscribe to
2300 <a href="https://github.com/ziglang/zig/issues/1761">this issue</a>.
2301 TODO update these docs with a recommendation on how to use packed structs with MMIO
2302 (the use case for volatile packed structs) once this issue is resolved.
2303 Don't worry, there will be a good solution for this use case in zig.
2304 Field access and assignment can be understood as shorthand for bitshifts
2305 on the backing integer. These operations are not {#link|atomic|Atomics#},
2306 so beware using field access syntax when combined with memory-mapped
2307 input-output (MMIO). Instead of field access on {#link|volatile#} {#link|Pointers#},
2308 construct a fully-formed new value first, then write that value to the volatile pointer.
23042309 </p>
2310 {#code|packed_struct_mmio.zig#}
23052311 {#header_close#}
23062312
23072313 {#header_open|Struct Naming#}
doc/langref/packed_struct_mmio.zig created+19
......@@ -0,0 +1,19 @@
1pub const GpioRegister = packed struct(u8) {
2 GPIO0: bool,
3 GPIO1: bool,
4 GPIO2: bool,
5 GPIO3: bool,
6 reserved: u4 = 0,
7};
8
9const gpio: *volatile GpioRegister = @ptrFromInt(0x0123);
10
11pub fn writeToGpio(new_states: GpioRegister) void {
12 // Example of what not to do:
13 // BAD! gpio.GPIO0 = true; BAD!
14
15 // Instead, do this:
16 gpio.* = new_states;
17}
18
19// syntax