zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit 3746b3d93ce895131ab1b2dea5391e5efa9fccf7 (tree)
parent ae38fc6a50dfcece964d7d19d9e35ac5dc583daa
Author: Andrew Kelley <andrew@ziglang.org>
Date:   Wed, 16 Apr 2025 04:56:22 -0400

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

langref: improve packed struct memory layout description
Diffstat:
Mdoc/langref.html.in | 44+++++++++++++++++++++++++-------------------
Adoc/langref/packed_struct_mmio.zig | 19+++++++++++++++++++
2 files changed, 44 insertions(+), 19 deletions(-)

diff --git a/doc/langref.html.in b/doc/langref.html.in @@ -1649,6 +1649,7 @@ unwrapped == 1234{#endsyntax#}</pre> <li>{#link|Floats#}</li> <li>{#link|bool|Primitive Types#}</li> <li>{#link|type|Primitive Types#}</li> + <li>{#link|packed struct#}</li> </ul> </td> <td> @@ -2224,20 +2225,24 @@ or {#header_open|packed struct#} <p> - Unlike normal structs, {#syntax#}packed{#endsyntax#} structs have guaranteed in-memory layout: + {#syntax#}packed{#endsyntax#} structs, like {#syntax#}enum{#endsyntax#}, are based on the concept + of interpreting integers differently. All packed structs have a <strong>backing integer</strong>, + which is implicitly determined by the total bit count of fields, or explicitly specified. + Packed structs have well-defined memory layout - exactly the same ABI as their backing integer. + </p> + <p> + Each field of a packed struct is interpreted as a logical sequence of bits, arranged from + least to most significant. Allowed field types: </p> <ul> - <li>Fields remain in the order declared, least to most significant.</li> - <li>There is no padding between fields.</li> - <li>Zig supports arbitrary width {#link|Integers#} and although normally, integers with fewer - than 8 bits will still use 1 byte of memory, in packed structs, they use - exactly their bit width. - </li> - <li>{#syntax#}bool{#endsyntax#} fields use exactly 1 bit.</li> + <li>An {#link|integer|Integers#} field uses exactly as many bits as its + bit width. For example, a {#syntax#}u5{#endsyntax#} will use 5 bits of + the backing integer.</li> + <li>A {#link|bool|Primitive Types#} field uses exactly 1 bit.</li> <li>An {#link|enum#} field uses exactly the bit width of its integer tag type.</li> <li>A {#link|packed union#} field uses exactly the bit width of the union field with the largest bit width.</li> - <li>Packed structs support equality operators.</li> + <li>A {#syntax#}packed struct{#endsyntax#} field uses the bits of its backing integer.</li> </ul> <p> This means that a {#syntax#}packed struct{#endsyntax#} can participate @@ -2245,10 +2250,11 @@ or This even works at {#link|comptime#}: </p> {#code|test_packed_structs.zig#} - <p> - The backing integer is inferred from the fields' total bit width. - Optionally, it can be explicitly provided and enforced at compile time: + The backing integer can be inferred or explicitly provided. When + inferred, it will be unsigned. When explicitly provided, its bit width + will be enforced at compile time to exactly match the total bit width of + the fields: </p> {#code|test_missized_packed_struct.zig#} @@ -2290,18 +2296,18 @@ or <p> Equating packed structs results in a comparison of the backing integer, - and only works for the `==` and `!=` operators. + and only works for the {#syntax#}=={#endsyntax#} and {#syntax#}!={#endsyntax#} {#link|Operators#}. </p> {#code|test_packed_struct_equality.zig#} <p> - Using packed structs with {#link|volatile#} is problematic, and may be a compile error in the future. - For details on this subscribe to - <a href="https://github.com/ziglang/zig/issues/1761">this issue</a>. - TODO update these docs with a recommendation on how to use packed structs with MMIO - (the use case for volatile packed structs) once this issue is resolved. - Don't worry, there will be a good solution for this use case in zig. + Field access and assignment can be understood as shorthand for bitshifts + on the backing integer. These operations are not {#link|atomic|Atomics#}, + so beware using field access syntax when combined with memory-mapped + input-output (MMIO). Instead of field access on {#link|volatile#} {#link|Pointers#}, + construct a fully-formed new value first, then write that value to the volatile pointer. </p> + {#code|packed_struct_mmio.zig#} {#header_close#} {#header_open|Struct Naming#} diff --git a/doc/langref/packed_struct_mmio.zig b/doc/langref/packed_struct_mmio.zig @@ -0,0 +1,19 @@ +pub const GpioRegister = packed struct(u8) { + GPIO0: bool, + GPIO1: bool, + GPIO2: bool, + GPIO3: bool, + reserved: u4 = 0, +}; + +const gpio: *volatile GpioRegister = @ptrFromInt(0x0123); + +pub fn writeToGpio(new_states: GpioRegister) void { + // Example of what not to do: + // BAD! gpio.GPIO0 = true; BAD! + + // Instead, do this: + gpio.* = new_states; +} + +// syntax