Skip to content

Commit 3746b3d

Browse files
authored
Merge pull request #21741 from kj4tmp/langref-packed-structs
langref: improve packed struct memory layout description
2 parents ae38fc6 + bd38c41 commit 3746b3d

File tree

2 files changed

+44
-19
lines changed

2 files changed

+44
-19
lines changed

doc/langref.html.in

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -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,31 +2225,36 @@ 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
22442249
in a {#link|@bitCast#} or a {#link|@ptrCast#} to reinterpret memory.
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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
pub const GpioRegister = packed struct(u8) {
2+
GPIO0: bool,
3+
GPIO1: bool,
4+
GPIO2: bool,
5+
GPIO3: bool,
6+
reserved: u4 = 0,
7+
};
8+
9+
const gpio: *volatile GpioRegister = @ptrFromInt(0x0123);
10+
11+
pub 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

0 commit comments

Comments
 (0)