diff mbox series

[06/26] rust: add a bit operation module

Message ID 20241209123717.99077-7-pbonzini@redhat.com (mailing list archive)
State New
Headers show
Series rust: bundle of prerequisites for HPET implementation | expand

Commit Message

Paolo Bonzini Dec. 9, 2024, 12:36 p.m. UTC
The bindgen supports `static inline` function binding since v0.64.0 as
an experimental feature (`--wrap-static-fns`), and stabilizes it after
v0.70.0.

But the oldest version of bindgen supported by QEMU is v0.60.1, so
there's no way to generate the binding for deposit64() which is `static
inline` (in include/qemu/bitops.h).

Instead, implement it by hand in Rust and make it available for all
unsigned types through an IntegerExt trait. Since it only involves bit
operations, the Rust version of the code is almost identical to the
original C version, but it applies to more types than just u64.

Signed-off-by: Zhao Liu <zhao1.liu@intel.com>
Co-authored-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 rust/qemu-api/meson.build    |   1 +
 rust/qemu-api/src/bitops.rs  | 119 +++++++++++++++++++++++++++++++++++
 rust/qemu-api/src/lib.rs     |   1 +
 rust/qemu-api/src/prelude.rs |   2 +
 4 files changed, 123 insertions(+)
 create mode 100644 rust/qemu-api/src/bitops.rs

Comments

Zhao Liu Dec. 10, 2024, 8:13 a.m. UTC | #1
On Mon, Dec 09, 2024 at 01:36:57PM +0100, Paolo Bonzini wrote:
> Date: Mon,  9 Dec 2024 13:36:57 +0100
> From: Paolo Bonzini <pbonzini@redhat.com>
> Subject: [PATCH 06/26] rust: add a bit operation module
> X-Mailer: git-send-email 2.47.1
> 
> The bindgen supports `static inline` function binding since v0.64.0 as
> an experimental feature (`--wrap-static-fns`), and stabilizes it after
> v0.70.0.
> 
> But the oldest version of bindgen supported by QEMU is v0.60.1, so
> there's no way to generate the binding for deposit64() which is `static
> inline` (in include/qemu/bitops.h).
> 
> Instead, implement it by hand in Rust and make it available for all
> unsigned types through an IntegerExt trait. Since it only involves bit
> operations, the Rust version of the code is almost identical to the
> original C version, but it applies to more types than just u64.
> 
> Signed-off-by: Zhao Liu <zhao1.liu@intel.com>
> Co-authored-by: Paolo Bonzini <pbonzini@redhat.com>
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>  rust/qemu-api/meson.build    |   1 +
>  rust/qemu-api/src/bitops.rs  | 119 +++++++++++++++++++++++++++++++++++
>  rust/qemu-api/src/lib.rs     |   1 +
>  rust/qemu-api/src/prelude.rs |   2 +
>  4 files changed, 123 insertions(+)
>  create mode 100644 rust/qemu-api/src/bitops.rs
>
> diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
> index b927eb58c8e..adcee661150 100644
> --- a/rust/qemu-api/meson.build
> +++ b/rust/qemu-api/meson.build
> @@ -16,6 +16,7 @@ _qemu_api_rs = static_library(
>      [
>        'src/lib.rs',
>        'src/bindings.rs',
> +      'src/bitops.rs',
>        'src/cell.rs',
>        'src/c_str.rs',
>        'src/definitions.rs',
> diff --git a/rust/qemu-api/src/bitops.rs b/rust/qemu-api/src/bitops.rs
> new file mode 100644
> index 00000000000..5acd6642d1a
> --- /dev/null
> +++ b/rust/qemu-api/src/bitops.rs
> @@ -0,0 +1,119 @@
> +// Copyright (C) 2024 Intel Corporation.
> +// Author(s): Zhao Liu <zhai1.liu@intel.com>

You deserve to be the author!

> +// SPDX-License-Identifier: GPL-2.0-or-later
> +
> +//! This module provides bit operation extensions to integer types.
> +//! It is usually included via the `qemu_api` prelude.
> +
> +use std::ops::{
> +    Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign,
> +    Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign,
> +};
> +
> +/// Trait for extensions to integer types
> +pub trait IntegerExt:
> +    Add<Self, Output = Self> + AddAssign<Self> +
> +    BitAnd<Self, Output = Self> + BitAndAssign<Self> +
> +    BitOr<Self, Output = Self> + BitOrAssign<Self> +
> +    BitXor<Self, Output = Self> + BitXorAssign<Self> +
> +    Copy +
> +    Div<Self, Output = Self> + DivAssign<Self> +
> +    Eq +
> +    Mul<Self, Output = Self> + MulAssign<Self> +
> +    Not<Output = Self> + Ord + PartialOrd +
> +    Rem<Self, Output = Self> + RemAssign<Self> +
> +    Shl<Self, Output = Self> + ShlAssign<Self> +
> +    Shl<u32, Output = Self> + ShlAssign<u32> + // add more as needed

Ah, I used to define shift bits as usize. I can change the bit shift
type back to u32 in HPET.

> +    Shr<Self, Output = Self> + ShrAssign<Self> +
> +    Shr<u32, Output = Self> + ShrAssign<u32> // add more as needed
> +{
> +    const BITS: u32;
> +    const MAX: Self;
> +    const MIN: Self;
> +    const ONE: Self;
> +    const ZERO: Self;
> +
> +    #[inline]
> +    #[must_use]
> +    fn bit(start: u32) -> Self
> +    {
> +        assert!(start <= Self::BITS);
> +
> +        Self::ONE << start
> +    }

I think with this helper, I can add activating_bit() and deactivating_bit()
bindings, as they are also commonly used operations.

I will rename them to activate_bit and deactivate_bit, if no one has any
objections.

> +    #[inline]
> +    #[must_use]
> +    fn mask(start: u32, length: u32) -> Self
> +    {
> +        /* FIXME: Implement a more elegant check with error handling support? */

I think current design is elegant enough, and this FIXME is not needed.

> +        assert!(length > 0 && length <= Self::BITS - start);
> +
> +        (Self::MAX >> (Self::BITS - length)) << start
> +    }
> +
> +    #[inline]
> +    #[must_use]
> +    fn deposit<U: IntegerExt>(self, start: u32, length: u32,
> +                          fieldval: U) -> Self
> +        where Self: From<U>
> +    {
> +        debug_assert!(length <= U::BITS);

assert? as you've already replaced debug_assert with assert in BqlCell.

> +        let mask = Self::mask(start, length);
> +        (self & !mask) | ((Self::from(fieldval) << start) & mask)
> +    }
> +

Very useful for HPET! Thanks.

Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
Paolo Bonzini Dec. 10, 2024, 9:30 a.m. UTC | #2
On 12/10/24 09:13, Zhao Liu wrote:
>> diff --git a/rust/qemu-api/src/bitops.rs b/rust/qemu-api/src/bitops.rs
>> new file mode 100644
>> index 00000000000..5acd6642d1a
>> --- /dev/null
>> +++ b/rust/qemu-api/src/bitops.rs
>> @@ -0,0 +1,119 @@
>> +// Copyright (C) 2024 Intel Corporation.
>> +// Author(s): Zhao Liu <zhai1.liu@intel.com>
> 
> You deserve to be the author!

I'll add both of us.  At this stage even identifying a need is an 
important step.

>> +// SPDX-License-Identifier: GPL-2.0-or-later
>> +
>> +//! This module provides bit operation extensions to integer types.
>> +//! It is usually included via the `qemu_api` prelude.
>> +
>> +use std::ops::{
>> +    Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign,
>> +    Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign,
>> +};
>> +
>> +/// Trait for extensions to integer types
>> +pub trait IntegerExt:
>> +    Add<Self, Output = Self> + AddAssign<Self> +
>> +    BitAnd<Self, Output = Self> + BitAndAssign<Self> +
>> +    BitOr<Self, Output = Self> + BitOrAssign<Self> +
>> +    BitXor<Self, Output = Self> + BitXorAssign<Self> +
>> +    Copy +
>> +    Div<Self, Output = Self> + DivAssign<Self> +
>> +    Eq +
>> +    Mul<Self, Output = Self> + MulAssign<Self> +
>> +    Not<Output = Self> + Ord + PartialOrd +
>> +    Rem<Self, Output = Self> + RemAssign<Self> +
>> +    Shl<Self, Output = Self> + ShlAssign<Self> +
>> +    Shl<u32, Output = Self> + ShlAssign<u32> + // add more as needed
> 
> Ah, I used to define shift bits as usize. I can change the bit shift
> type back to u32 in HPET.

I used u32 because BITS is u32 in the standard library, so probably 
better to change it in HPET.

>> +    Shr<Self, Output = Self> + ShrAssign<Self> +
>> +    Shr<u32, Output = Self> + ShrAssign<u32> // add more as needed
>> +{
>> +    const BITS: u32;
>> +    const MAX: Self;
>> +    const MIN: Self;
>> +    const ONE: Self;
>> +    const ZERO: Self;
>> +
>> +    #[inline]
>> +    #[must_use]
>> +    fn bit(start: u32) -> Self
>> +    {
>> +        assert!(start <= Self::BITS);
>> +
>> +        Self::ONE << start
>> +    }
> 
> I think with this helper, I can add activating_bit() and deactivating_bit()
> bindings, as they are also commonly used operations.
> 
> I will rename them to activate_bit and deactivate_bit, if no one has any
> objections.

I think "-ing" is correct because it looks at a change between two 
values.  Or another possibility is putting the mask as "self":

     fn bits_activated(from: Self, to: Self) -> bool {
         (to & !from & self) != 0
     }

     fn bits_deactivated(from: Self, to: Self) -> bool {
         (from & !to & self) != 0
     }

Anyhow feel free to pick something you like and add it to IntegerExt.

>> +    #[inline]
>> +    #[must_use]
>> +    fn deposit<U: IntegerExt>(self, start: u32, length: u32,
>> +                          fieldval: U) -> Self
>> +        where Self: From<U>
>> +    {
>> +        debug_assert!(length <= U::BITS);
> 
> assert? as you've already replaced debug_assert with assert in BqlCell.

No, not this time :) Rust has checks for overflow in debug mode but not 
in release mode, so (independent of how Meson handles debug assertions) 
debug_assert is the right one here.

Paolo

>> +        let mask = Self::mask(start, length);
>> +        (self & !mask) | ((Self::from(fieldval) << start) & mask)
>> +    }
>> +
> 
> Very useful for HPET! Thanks.
> 
> Reviewed-by: Zhao Liu <zhao1.liu@intel.com>
> 
> 
>
diff mbox series

Patch

diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index b927eb58c8e..adcee661150 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -16,6 +16,7 @@  _qemu_api_rs = static_library(
     [
       'src/lib.rs',
       'src/bindings.rs',
+      'src/bitops.rs',
       'src/cell.rs',
       'src/c_str.rs',
       'src/definitions.rs',
diff --git a/rust/qemu-api/src/bitops.rs b/rust/qemu-api/src/bitops.rs
new file mode 100644
index 00000000000..5acd6642d1a
--- /dev/null
+++ b/rust/qemu-api/src/bitops.rs
@@ -0,0 +1,119 @@ 
+// Copyright (C) 2024 Intel Corporation.
+// Author(s): Zhao Liu <zhai1.liu@intel.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//! This module provides bit operation extensions to integer types.
+//! It is usually included via the `qemu_api` prelude.
+
+use std::ops::{
+    Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign,
+    Mul, MulAssign, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign,
+};
+
+/// Trait for extensions to integer types
+pub trait IntegerExt:
+    Add<Self, Output = Self> + AddAssign<Self> +
+    BitAnd<Self, Output = Self> + BitAndAssign<Self> +
+    BitOr<Self, Output = Self> + BitOrAssign<Self> +
+    BitXor<Self, Output = Self> + BitXorAssign<Self> +
+    Copy +
+    Div<Self, Output = Self> + DivAssign<Self> +
+    Eq +
+    Mul<Self, Output = Self> + MulAssign<Self> +
+    Not<Output = Self> + Ord + PartialOrd +
+    Rem<Self, Output = Self> + RemAssign<Self> +
+    Shl<Self, Output = Self> + ShlAssign<Self> +
+    Shl<u32, Output = Self> + ShlAssign<u32> + // add more as needed
+    Shr<Self, Output = Self> + ShrAssign<Self> +
+    Shr<u32, Output = Self> + ShrAssign<u32> // add more as needed
+{
+    const BITS: u32;
+    const MAX: Self;
+    const MIN: Self;
+    const ONE: Self;
+    const ZERO: Self;
+
+    #[inline]
+    #[must_use]
+    fn bit(start: u32) -> Self
+    {
+        assert!(start <= Self::BITS);
+
+        Self::ONE << start
+    }
+
+    #[inline]
+    #[must_use]
+    fn mask(start: u32, length: u32) -> Self
+    {
+        /* FIXME: Implement a more elegant check with error handling support? */
+        assert!(length > 0 && length <= Self::BITS - start);
+
+        (Self::MAX >> (Self::BITS - length)) << start
+    }
+
+    #[inline]
+    #[must_use]
+    fn deposit<U: IntegerExt>(self, start: u32, length: u32,
+                          fieldval: U) -> Self
+        where Self: From<U>
+    {
+        debug_assert!(length <= U::BITS);
+
+        let mask = Self::mask(start, length);
+        (self & !mask) | ((Self::from(fieldval) << start) & mask)
+    }
+
+    #[inline]
+    #[must_use]
+    fn extract(self, start: u32, length: u32) -> Self
+    {
+        let mask = Self::mask(start, length);
+        (self & mask) >> start
+    }
+}
+
+macro_rules! impl_num_ext {
+    ($type:ty) => {
+        impl IntegerExt for $type {
+            const BITS: u32 = <$type>::BITS;
+            const MAX: Self = <$type>::MAX;
+            const MIN: Self = <$type>::MIN;
+            const ONE: Self = 1;
+            const ZERO: Self = 0;
+        }
+    };
+}
+
+impl_num_ext!(u8);
+impl_num_ext!(u16);
+impl_num_ext!(u32);
+impl_num_ext!(u64);
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_deposit() {
+        assert_eq!(15u32.deposit(8, 8, 1u32), 256 + 15);
+        assert_eq!(15u32.deposit(8, 1, 255u8), 256 + 15);
+    }
+
+    #[test]
+    fn test_extract() {
+        assert_eq!(15u32.extract(2, 4), 3);
+    }
+
+    #[test]
+    fn test_bit() {
+        assert_eq!(u8::bit(7), 128);
+        assert_eq!(u32::bit(16), 0x10000);
+    }
+
+    #[test]
+    fn test_mask() {
+        assert_eq!(u8::mask(7, 1), 128);
+        assert_eq!(u32::mask(8, 8), 0xff00);
+    }
+}
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index 0efbef47441..9e007e16354 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -12,6 +12,7 @@ 
 #[rustfmt::skip]
 pub mod prelude;
 
+pub mod bitops;
 pub mod c_str;
 pub mod cell;
 pub mod definitions;
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
index dfaddbd062a..a39e228babf 100644
--- a/rust/qemu-api/src/prelude.rs
+++ b/rust/qemu-api/src/prelude.rs
@@ -2,5 +2,7 @@ 
 // Author(s): Paolo Bonzini <pbonzini@redhat.com>
 // SPDX-License-Identifier: GPL-2.0-or-later
 
+pub use crate::bitops::IntegerExt;
+
 pub use crate::cell::BqlCell;
 pub use crate::cell::BqlRefCell;