On Wed, May 21, 2025 at 10:18:40AM +0200, Paolo Bonzini wrote:
> One common thing that device emulation does is manipulate bitmasks, for 
> example
> to check whether two bitmaps have common bits.  One example in the pl011 crate
> is the checks for pending interrupts, where an interrupt cause corresponds to
> at least one interrupt source from a fixed set.
> 
> Unfortunately, this is one case where Rust *can* provide some kind of
> abstraction but it does so with a rather Perl-ish There Is More Way To
> Do It.  It is not something where a crate like "bilge" helps, because
> it only covers the packing of bits in a structure; operations like "are
> all bits of Y set in X" almost never make sense for bit-packed structs;
> you need something else, there are several crates that do it and of course
> we're going to roll our own.
> 
> In particular I examined three:
> 
> - bitmask (https://docs.rs/bitmask/0.5.0/bitmask/) does not support const
>   at all.  This is a showstopper because one of the ugly things in the
>   current pl011 code is the ugliness of code that defines interrupt masks
>   at compile time:
> 
>     pub const E: Self = Self(Self::OE.0 | Self::BE.0 | Self::PE.0 | 
> Self::FE.0);
> 
>   or even worse:
> 
>     const IRQMASK: [u32; 6] = [
>       Interrupt::E.0 | Interrupt::MS.0 | Interrupt::RT.0 | Interrupt::TX.0 | 
> Interrupt::RX.0,
>       ...
>     }
> 
>   You would have to use roughly the same code---"bitmask" only helps with
>   defining the struct.
> 
> - bitmask_enum (https://docs.rs/bitmask-enum/2.2.5/bitmask_enum/) does not
>   have a good separation of "valid" and "invalid" bits, so for example "!x"
>   will invert all 16 bits if you choose u16 as the representation -- even if
>   you only defined 10 bits.  This makes it easier to introduce subtle bugs
>   in comparisons.
> 
> - bitflags (https://docs.rs/bitflags/2.6.0/bitflags/) is generally the most
>   used such crate and is the one that I took most inspiration from with
>   respect to the syntax.  It's a pretty sophisticated implementation,
>   with a lot of bells and whistles such as an implementation of "Iter"
>   that returns the bits one at a time.
> 
> The main thing that all of them lack, however, is a way to simplify the
> ugly definitions like the above.  "bitflags" includes const methods that
> perform AND/OR/XOR of masks (these are necessary because Rust operator
> overloading does not support const yet, and therefore overloaded operators
> cannot be used in the definition of a "static" variable), but they become
> even more verbose and unmanageable, like
> 
>   
> Interrupt::E.union(Interrupt::MS).union(Interrupt::RT).union(Interrupt::TX).union(Interrupt::RX)
> 
> This was the main reason to create "bits", which allows something like
> 
>   bits!(Interrupt: E | MS | RT | TX | RX)
> 
> and expands it 1) add "Interrupt::" in front of all identifiers 2) convert
> operators to the wordy const functions like "union".  It supports boolean
> operators "&", "|", "^", "!" and parentheses, with a relatively simple
> recursive descent parser that's implemented in qemu_api_macros.
> 
> Signed-off-by: Paolo Bonzini <pbonz...@redhat.com>
> ---
>  rust/Cargo.lock                  |   7 +
>  rust/Cargo.toml                  |   1 +
>  rust/bits/Cargo.toml             |  19 ++
>  rust/bits/meson.build            |  12 +
>  rust/bits/src/lib.rs             | 441 +++++++++++++++++++++++++++++++
>  rust/meson.build                 |   1 +
>  rust/qemu-api-macros/src/bits.rs | 227 ++++++++++++++++
>  rust/qemu-api-macros/src/lib.rs  |  12 +
>  8 files changed, 720 insertions(+)
>  create mode 100644 rust/bits/Cargo.toml
>  create mode 100644 rust/bits/meson.build
>  create mode 100644 rust/bits/src/lib.rs
>  create mode 100644 rust/qemu-api-macros/src/bits.rs

> diff --git a/rust/bits/src/lib.rs b/rust/bits/src/lib.rs
> new file mode 100644
> index 00000000000..d80a6263f1e
> --- /dev/null
> +++ b/rust/bits/src/lib.rs
> @@ -0,0 +1,441 @@

This (and other new .rs files) needs SPDX-License-Identifier

> +/// # Definition entry point
> +///
> +/// Define a struct with a single field of type $type.  Include public 
> constants
> +/// for each element listed in braces.
> +///
> +/// The unnamed element at the end, if present, can be used to enlarge the 
> set
> +/// of valid bits.  Bits that are valid but not listed are treated normally 
> for
> +/// the purpose of arithmetic operations, and are printed with their 
> hexadecimal
> +/// value.
> +///
> +/// The struct implements the following traits: [`BitAnd`](std::ops::BitAnd),
> +/// [`BitOr`](std::ops::BitOr), [`BitXor`](std::ops::BitXor),
> +/// [`Not`](std::ops::Not), [`Sub`](std::ops::Sub); 
> [`Debug`](std::fmt::Debug),
> +/// [`Display`](std::fmt::Display), [`Binary`](std::fmt::Binary),
> +/// [`Octal`](std::fmt::Octal), [`LowerHex`](std::fmt::LowerHex),
> +/// [`UpperHex`](std::fmt::UpperHex); [`From`]`<type>`/[`Into`]`<type>` where
> +/// type is the type specified in the definition.

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|


Reply via email to