mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-04 00:03:54 -06:00
* tcg/optimize: optimize TSTNE using smask and zmask
* target/i386: fix exceptions for 0 * Inf + QNaN * rust: cleanups to the configuration and the warnings * rust: add developer docs -----BEGIN PGP SIGNATURE----- iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmep0nsUHHBib256aW5p QHJlZGhhdC5jb20ACgkQv/vSX3jHroN0aggAo8mKY4c7LGLF+5xGM1W/ZLxaBrMN 4/LGMV3UJJq+OIEb+e7ThtfyHzcAsYXdO2SIMJ6ffW58ukmwM1SycA8WUjG3JMya m05dVnI//D/G7iqYgyNlsiTbqazdr3P/Ha0ty10l9K9dL3SjB3Nm3xLD4XnD3tzM U+0yXrn1ngMZ3Y1knTuWwjoH7JT5QftwGCVZ5aeq0KlSAvWb8M8jupYFunLBcZ0z LkFSWXbxhw9HRkI+Lp6c2IjIBDEd7267tEfnXf+d29ykVnrdyIWgyYw08/Un72i+ C5hNEUMiwcD7preTYX5YqDcxSASG1zWNFzEWGhTphMWf1O60f2PIXMWX5g== =0KKa -----END PGP SIGNATURE----- Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging * tcg/optimize: optimize TSTNE using smask and zmask * target/i386: fix exceptions for 0 * Inf + QNaN * rust: cleanups to the configuration and the warnings * rust: add developer docs # -----BEGIN PGP SIGNATURE----- # # iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmep0nsUHHBib256aW5p # QHJlZGhhdC5jb20ACgkQv/vSX3jHroN0aggAo8mKY4c7LGLF+5xGM1W/ZLxaBrMN # 4/LGMV3UJJq+OIEb+e7ThtfyHzcAsYXdO2SIMJ6ffW58ukmwM1SycA8WUjG3JMya # m05dVnI//D/G7iqYgyNlsiTbqazdr3P/Ha0ty10l9K9dL3SjB3Nm3xLD4XnD3tzM # U+0yXrn1ngMZ3Y1knTuWwjoH7JT5QftwGCVZ5aeq0KlSAvWb8M8jupYFunLBcZ0z # LkFSWXbxhw9HRkI+Lp6c2IjIBDEd7267tEfnXf+d29ykVnrdyIWgyYw08/Un72i+ # C5hNEUMiwcD7preTYX5YqDcxSASG1zWNFzEWGhTphMWf1O60f2PIXMWX5g== # =0KKa # -----END PGP SIGNATURE----- # gpg: Signature made Mon 10 Feb 2025 05:18:35 EST # gpg: using RSA key F13338574B662389866C7682BFFBD25F78C7AE83 # gpg: issuer "pbonzini@redhat.com" # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [full] # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" [full] # Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1 # Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83 * tag 'for-upstream' of https://gitlab.com/bonzini/qemu: rust: restrict missing_const_for_fn to qemu_api crate rust: pl011: use default set of lints tcg/optimize: optimize TSTNE using smask and zmask tests/tcg/x86_64/fma: Test some x86 fused-multiply-add cases target/i386: Do not raise Invalid for 0 * Inf + QNaN rust: add clippy configuration file rust: add docs rust: include rust_version in Cargo.toml rust: remove unnecessary Cargo.toml metadata Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
commit
9c72ba3af2
18 changed files with 581 additions and 68 deletions
|
@ -17,3 +17,4 @@ Notes about how to interact with the community and how and where to submit patch
|
|||
stable-process
|
||||
submitting-a-pull-request
|
||||
secure-coding-practices
|
||||
rust
|
||||
|
|
430
docs/devel/rust.rst
Normal file
430
docs/devel/rust.rst
Normal file
|
@ -0,0 +1,430 @@
|
|||
.. |msrv| replace:: 1.63.0
|
||||
|
||||
Rust in QEMU
|
||||
============
|
||||
|
||||
Rust in QEMU is a project to enable using the Rust programming language
|
||||
to add new functionality to QEMU.
|
||||
|
||||
Right now, the focus is on making it possible to write devices that inherit
|
||||
from ``SysBusDevice`` in `*safe*`__ Rust. Later, it may become possible
|
||||
to write other kinds of devices (e.g. PCI devices that can do DMA),
|
||||
complete boards, or backends (e.g. block device formats).
|
||||
|
||||
__ https://doc.rust-lang.org/nomicon/meet-safe-and-unsafe.html
|
||||
|
||||
Building the Rust in QEMU code
|
||||
------------------------------
|
||||
|
||||
The Rust in QEMU code is included in the emulators via Meson. Meson
|
||||
invokes rustc directly, building static libraries that are then linked
|
||||
together with the C code. This is completely automatic when you run
|
||||
``make`` or ``ninja``.
|
||||
|
||||
However, QEMU's build system also tries to be easy to use for people who
|
||||
are accustomed to the more "normal" Cargo-based development workflow.
|
||||
In particular:
|
||||
|
||||
* the set of warnings and lints that are used to build QEMU always
|
||||
comes from the ``rust/Cargo.toml`` workspace file
|
||||
|
||||
* it is also possible to use ``cargo`` for common Rust-specific coding
|
||||
tasks, in particular to invoke ``clippy``, ``rustfmt`` and ``rustdoc``.
|
||||
|
||||
To this end, QEMU includes a ``build.rs`` build script that picks up
|
||||
generated sources from QEMU's build directory and puts it in Cargo's
|
||||
output directory (typically ``rust/target/``). A vanilla invocation
|
||||
of Cargo will complain that it cannot find the generated sources,
|
||||
which can be fixed in different ways:
|
||||
|
||||
* by using special shorthand targets in the QEMU build directory::
|
||||
|
||||
make clippy
|
||||
make rustfmt
|
||||
make rustdoc
|
||||
|
||||
* by invoking ``cargo`` through the Meson `development environment`__
|
||||
feature::
|
||||
|
||||
pyvenv/bin/meson devenv -w ../rust cargo clippy --tests
|
||||
pyvenv/bin/meson devenv -w ../rust cargo fmt
|
||||
|
||||
If you are going to use ``cargo`` repeatedly, ``pyvenv/bin/meson devenv``
|
||||
will enter a shell where commands like ``cargo clippy`` just work.
|
||||
|
||||
__ https://mesonbuild.com/Commands.html#devenv
|
||||
|
||||
* by pointing the ``MESON_BUILD_ROOT`` to the top of your QEMU build
|
||||
tree. This third method is useful if you are using ``rust-analyzer``;
|
||||
you can set the environment variable through the
|
||||
``rust-analyzer.cargo.extraEnv`` setting.
|
||||
|
||||
As shown above, you can use the ``--tests`` option as usual to operate on test
|
||||
code. Note however that you cannot *build* or run tests via ``cargo``, because
|
||||
they need support C code from QEMU that Cargo does not know about. Tests can
|
||||
be run via ``meson test`` or ``make``::
|
||||
|
||||
make check-rust
|
||||
|
||||
Building Rust code with ``--enable-modules`` is not supported yet.
|
||||
|
||||
Supported tools
|
||||
'''''''''''''''
|
||||
|
||||
QEMU supports rustc version 1.63.0 and newer. Notably, the following features
|
||||
are missing:
|
||||
|
||||
* ``core::ffi`` (1.64.0). Use ``std::os::raw`` and ``std::ffi`` instead.
|
||||
|
||||
* ``cast_mut()``/``cast_const()`` (1.65.0). Use ``as`` instead.
|
||||
|
||||
* "let ... else" (1.65.0). Use ``if let`` instead. This is currently patched
|
||||
in QEMU's vendored copy of the bilge crate.
|
||||
|
||||
* Generic Associated Types (1.65.0)
|
||||
|
||||
* ``CStr::from_bytes_with_nul()`` as a ``const`` function (1.72.0).
|
||||
|
||||
* "Return position ``impl Trait`` in Traits" (1.75.0, blocker for including
|
||||
the pinned-init create).
|
||||
|
||||
* ``MaybeUninit::zeroed()`` as a ``const`` function (1.75.0). QEMU's
|
||||
``Zeroable`` trait can be implemented without ``MaybeUninit::zeroed()``,
|
||||
so this would be just a cleanup.
|
||||
|
||||
* ``c"" literals`` (stable in 1.77.0). QEMU provides a ``c_str!()`` macro
|
||||
to define ``CStr`` constants easily
|
||||
|
||||
* ``offset_of!`` (stable in 1.77.0). QEMU uses ``offset_of!()`` heavily; it
|
||||
provides a replacement in the ``qemu_api`` crate, but it does not support
|
||||
lifetime parameters and therefore ``&'a Something`` fields in the struct
|
||||
may have to be replaced by ``NonNull<Something>``. *Nested* ``offset_of!``
|
||||
was only stabilized in Rust 1.82.0, but it is not used.
|
||||
|
||||
* inline const expression (stable in 1.79.0), currently worked around with
|
||||
associated constants in the ``FnCall`` trait.
|
||||
|
||||
* associated constants have to be explicitly marked ``'static`` (`changed in
|
||||
1.81.0`__)
|
||||
|
||||
* ``&raw`` (stable in 1.82.0). Use ``addr_of!`` and ``addr_of_mut!`` instead,
|
||||
though hopefully the need for raw pointers will go down over time.
|
||||
|
||||
* ``new_uninit`` (stable in 1.82.0). This is used internally by the ``pinned_init``
|
||||
crate, which is planned for inclusion in QEMU, but it can be easily patched
|
||||
out.
|
||||
|
||||
* referencing statics in constants (stable in 1.83.0). For now use a const
|
||||
function; this is an important limitation for QEMU's migration stream
|
||||
architecture (VMState). Right now, VMState lacks type safety because
|
||||
it is hard to place the ``VMStateField`` definitions in traits.
|
||||
|
||||
* associated const equality would be nice to have for some users of
|
||||
``callbacks::FnCall``, but is still experimental. ``ASSERT_IS_SOME``
|
||||
replaces it.
|
||||
|
||||
__ https://github.com/rust-lang/rust/pull/125258
|
||||
|
||||
It is expected that QEMU will advance its minimum supported version of
|
||||
rustc to 1.77.0 as soon as possible; as of January 2025, blockers
|
||||
for that right now are Debian bookworm and 32-bit MIPS processors.
|
||||
This unfortunately means that references to statics in constants will
|
||||
remain an issue.
|
||||
|
||||
QEMU also supports version 0.60.x of bindgen, which is missing option
|
||||
``--generate-cstr``. This option requires version 0.66.x and will
|
||||
be adopted as soon as supporting these older versions is not necessary
|
||||
anymore.
|
||||
|
||||
Writing Rust code in QEMU
|
||||
-------------------------
|
||||
|
||||
Right now QEMU includes three crates:
|
||||
|
||||
* ``qemu_api`` for bindings to C code and useful functionality
|
||||
|
||||
* ``qemu_api_macros`` defines several procedural macros that are useful when
|
||||
writing C code
|
||||
|
||||
* ``pl011`` (under ``rust/hw/char/pl011``) is the sample device that is being
|
||||
used to further develop ``qemu_api`` and ``qemu_api_macros``. It is a functional
|
||||
replacement for the ``hw/char/pl011.c`` file.
|
||||
|
||||
This section explains how to work with them.
|
||||
|
||||
Status
|
||||
''''''
|
||||
|
||||
Modules of ``qemu_api`` can be defined as:
|
||||
|
||||
- *complete*: ready for use in new devices; if applicable, the API supports the
|
||||
full functionality available in C
|
||||
|
||||
- *stable*: ready for production use, the API is safe and should not undergo
|
||||
major changes
|
||||
|
||||
- *proof of concept*: the API is subject to change but allows working with safe
|
||||
Rust
|
||||
|
||||
- *initial*: the API is in its initial stages; it requires large amount of
|
||||
unsafe code; it might have soundness or type-safety issues
|
||||
|
||||
The status of the modules is as follows:
|
||||
|
||||
================ ======================
|
||||
module status
|
||||
================ ======================
|
||||
``assertions`` stable
|
||||
``bitops`` complete
|
||||
``callbacks`` complete
|
||||
``cell`` stable
|
||||
``c_str`` complete
|
||||
``irq`` complete
|
||||
``module`` complete
|
||||
``offset_of`` stable
|
||||
``qdev`` stable
|
||||
``qom`` stable
|
||||
``sysbus`` stable
|
||||
``vmstate`` proof of concept
|
||||
``zeroable`` stable
|
||||
================ ======================
|
||||
|
||||
.. note::
|
||||
API stability is not a promise, if anything because the C APIs are not a stable
|
||||
interface either. Also, ``unsafe`` interfaces may be replaced by safe interfaces
|
||||
later.
|
||||
|
||||
Common pitfalls
|
||||
'''''''''''''''
|
||||
|
||||
Rust has very strict rules with respect to how you get an exclusive (``&mut``)
|
||||
reference; failure to respect those rules is a source of undefined behavior.
|
||||
In particular, even if a value is loaded from a raw mutable pointer (``*mut``),
|
||||
it *cannot* be casted to ``&mut`` unless the value was stored to the ``*mut``
|
||||
from a mutable reference. Furthermore, it is undefined behavior if any
|
||||
shared reference was created between the store to the ``*mut`` and the load::
|
||||
|
||||
let mut p: u32 = 42;
|
||||
let p_mut = &mut p; // 1
|
||||
let p_raw = p_mut as *mut u32; // 2
|
||||
|
||||
// p_raw keeps the mutable reference "alive"
|
||||
|
||||
let p_shared = &p; // 3
|
||||
println!("access from &u32: {}", *p_shared);
|
||||
|
||||
// Bring back the mutable reference, its lifetime overlaps
|
||||
// with that of a shared reference.
|
||||
let p_mut = unsafe { &mut *p_raw }; // 4
|
||||
println!("access from &mut 32: {}", *p_mut);
|
||||
|
||||
println!("access from &u32: {}", *p_shared); // 5
|
||||
|
||||
These rules can be tested with `MIRI`__, for example.
|
||||
|
||||
__ https://github.com/rust-lang/miri
|
||||
|
||||
Almost all Rust code in QEMU will involve QOM objects, and pointers to these
|
||||
objects are *shared*, for example because they are part of the QOM composition
|
||||
tree. This creates exactly the above scenario:
|
||||
|
||||
1. a QOM object is created
|
||||
|
||||
2. a ``*mut`` is created, for example as the opaque value for a ``MemoryRegion``
|
||||
|
||||
3. the QOM object is placed in the composition tree
|
||||
|
||||
4. a memory access dereferences the opaque value to a ``&mut``
|
||||
|
||||
5. but the shared reference is still present in the composition tree
|
||||
|
||||
Because of this, QOM objects should almost always use ``&self`` instead
|
||||
of ``&mut self``; access to internal fields must use *interior mutability*
|
||||
to go from a shared reference to a ``&mut``.
|
||||
|
||||
Whenever C code provides you with an opaque ``void *``, avoid converting it
|
||||
to a Rust mutable reference, and use a shared reference instead. Rust code
|
||||
will then have to use QEMU's ``BqlRefCell`` and ``BqlCell`` type, which
|
||||
enforce that locking rules for the "Big QEMU Lock" are respected. These cell
|
||||
types are also known to the ``vmstate`` crate, which is able to "look inside"
|
||||
them when building an in-memory representation of a ``struct``s layout.
|
||||
Note that the same is not true of a ``RefCell`` or ``Mutex``.
|
||||
|
||||
In the future, similar cell types might also be provided for ``AioContext``-based
|
||||
locking as well.
|
||||
|
||||
Writing bindings to C code
|
||||
''''''''''''''''''''''''''
|
||||
|
||||
Here are some things to keep in mind when working on the ``qemu_api`` crate.
|
||||
|
||||
**Look at existing code**
|
||||
Very often, similar idioms in C code correspond to similar tricks in
|
||||
Rust bindings. If the C code uses ``offsetof``, look at qdev properties
|
||||
or ``vmstate``. If the C code has a complex const struct, look at
|
||||
``MemoryRegion``. Reuse existing patterns for handling lifetimes;
|
||||
for example use ``&T`` for QOM objects that do not need a reference
|
||||
count (including those that can be embedded in other objects) and
|
||||
``Owned<T>`` for those that need it.
|
||||
|
||||
**Use the type system**
|
||||
Bindings often will need access information that is specific to a type
|
||||
(either a builtin one or a user-defined one) in order to pass it to C
|
||||
functions. Put them in a trait and access it through generic parameters.
|
||||
The ``vmstate`` module has examples of how to retrieve type information
|
||||
for the fields of a Rust ``struct``.
|
||||
|
||||
**Prefer unsafe traits to unsafe functions**
|
||||
Unsafe traits are much easier to prove correct than unsafe functions.
|
||||
They are an excellent place to store metadata that can later be accessed
|
||||
by generic functions. C code usually places metadata in global variables;
|
||||
in Rust, they can be stored in traits and then turned into ``static``
|
||||
variables. Often, unsafe traits can be generated by procedural macros.
|
||||
|
||||
**Document limitations due to old Rust versions**
|
||||
If you need to settle for an inferior solution because of the currently
|
||||
supported set of Rust versions, document it in the source and in this
|
||||
file. This ensures that it can be fixed when the minimum supported
|
||||
version is bumped.
|
||||
|
||||
**Keep locking in mind**.
|
||||
When marking a type ``Sync``, be careful of whether it needs the big
|
||||
QEMU lock. Use ``BqlCell`` and ``BqlRefCell`` for interior data,
|
||||
or assert ``bql_locked()``.
|
||||
|
||||
**Don't be afraid of complexity, but document and isolate it**
|
||||
It's okay to be tricky; device code is written more often than bindings
|
||||
code and it's important that it is idiomatic. However, you should strive
|
||||
to isolate any tricks in a place (for example a ``struct``, a trait
|
||||
or a macro) where it can be documented and tested. If needed, include
|
||||
toy versions of the code in the documentation.
|
||||
|
||||
Writing procedural macros
|
||||
'''''''''''''''''''''''''
|
||||
|
||||
By conventions, procedural macros are split in two functions, one
|
||||
returning ``Result<proc_macro2::TokenStream, MacroError>` with the body of
|
||||
the procedural macro, and the second returning ``proc_macro::TokenStream``
|
||||
which is the actual procedural macro. The former's name is the same as
|
||||
the latter with the ``_or_error`` suffix. The code for the latter is more
|
||||
or less fixed; it follows the following template, which is fixed apart
|
||||
from the type after ``as`` in the invocation of ``parse_macro_input!``::
|
||||
|
||||
#[proc_macro_derive(Object)]
|
||||
pub fn derive_object(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let expanded = derive_object_or_error(input).unwrap_or_else(Into::into);
|
||||
|
||||
TokenStream::from(expanded)
|
||||
}
|
||||
|
||||
The ``qemu_api_macros`` crate has utility functions to examine a
|
||||
``DeriveInput`` and perform common checks (e.g. looking for a struct
|
||||
with named fields). These functions return ``Result<..., MacroError>``
|
||||
and can be used easily in the procedural macro function::
|
||||
|
||||
fn derive_object_or_error(input: DeriveInput) ->
|
||||
Result<proc_macro2::TokenStream, MacroError>
|
||||
{
|
||||
is_c_repr(&input, "#[derive(Object)]")?;
|
||||
|
||||
let name = &input.ident;
|
||||
let parent = &get_fields(&input, "#[derive(Object)]")?[0].ident;
|
||||
...
|
||||
}
|
||||
|
||||
Use procedural macros with care. They are mostly useful for two purposes:
|
||||
|
||||
* Performing consistency checks; for example ``#[derive(Object)]`` checks
|
||||
that the structure has ``#[repr[C])`` and that the type of the first field
|
||||
is consistent with the ``ObjectType`` declaration.
|
||||
|
||||
* Extracting information from Rust source code into traits, typically based
|
||||
on types and attributes. For example, ``#[derive(TryInto)]`` builds an
|
||||
implementation of ``TryFrom``, and it uses the ``#[repr(...)]`` attribute
|
||||
as the ``TryFrom`` source and error types.
|
||||
|
||||
Procedural macros can be hard to debug and test; if the code generation
|
||||
exceeds a few lines of code, it may be worthwhile to delegate work to
|
||||
"regular" declarative (``macro_rules!``) macros and write unit tests for
|
||||
those instead.
|
||||
|
||||
|
||||
Coding style
|
||||
''''''''''''
|
||||
|
||||
Code should pass clippy and be formatted with rustfmt.
|
||||
|
||||
Right now, only the nightly version of ``rustfmt`` is supported. This
|
||||
might change in the future. While CI checks for correct formatting via
|
||||
``cargo fmt --check``, maintainers can fix this for you when applying patches.
|
||||
|
||||
It is expected that ``qemu_api`` provides full ``rustdoc`` documentation for
|
||||
bindings that are in their final shape or close.
|
||||
|
||||
Adding dependencies
|
||||
-------------------
|
||||
|
||||
Generally, the set of dependent crates is kept small. Think twice before
|
||||
adding a new external crate, especially if it comes with a large set of
|
||||
dependencies itself. Sometimes QEMU only needs a small subset of the
|
||||
functionality; see for example QEMU's ``assertions`` or ``c_str`` modules.
|
||||
|
||||
On top of this recommendation, adding external crates to QEMU is a
|
||||
slightly complicated process, mostly due to the need to teach Meson how
|
||||
to build them. While Meson has initial support for parsing ``Cargo.lock``
|
||||
files, it is still highly experimental and is therefore not used.
|
||||
|
||||
Therefore, external crates must be added as subprojects for Meson to
|
||||
learn how to build them, as well as to the relevant ``Cargo.toml`` files.
|
||||
The versions specified in ``rust/Cargo.lock`` must be the same as the
|
||||
subprojects; note that the ``rust/`` directory forms a Cargo `workspace`__,
|
||||
and therefore there is a single lock file for the whole build.
|
||||
|
||||
__ https://doc.rust-lang.org/cargo/reference/workspaces.html#virtual-workspace
|
||||
|
||||
Choose a version of the crate that works with QEMU's minimum supported
|
||||
Rust version (|msrv|).
|
||||
|
||||
Second, a new ``wrap`` file must be added to teach Meson how to download the
|
||||
crate. The wrap file must be named ``NAME-SEMVER-rs.wrap``, where ``NAME``
|
||||
is the name of the crate and ``SEMVER`` is the version up to and including the
|
||||
first non-zero number. For example, a crate with version ``0.2.3`` will use
|
||||
``0.2`` for its ``SEMVER``, while a crate with version ``1.0.84`` will use ``1``.
|
||||
|
||||
Third, the Meson rules to build the crate must be added at
|
||||
``subprojects/NAME-SEMVER-rs/meson.build``. Generally this includes:
|
||||
|
||||
* ``subproject`` and ``dependency`` lines for all dependent crates
|
||||
|
||||
* a ``static_library`` or ``rust.proc_macro`` line to perform the actual build
|
||||
|
||||
* ``declare_dependency`` and a ``meson.override_dependency`` lines to expose
|
||||
the result to QEMU and to other subprojects
|
||||
|
||||
Remember to add ``native: true`` to ``dependency``, ``static_library`` and
|
||||
``meson.override_dependency`` for dependencies of procedural macros.
|
||||
If a crate is needed in both procedural macros and QEMU binaries, everything
|
||||
apart from ``subproject`` must be duplicated to build both native and
|
||||
non-native versions of the crate.
|
||||
|
||||
It's important to specify the right compiler options. These include:
|
||||
|
||||
* the language edition (which can be found in the ``Cargo.toml`` file)
|
||||
|
||||
* the ``--cfg`` (which have to be "reverse engineered" from the ``build.rs``
|
||||
file of the crate).
|
||||
|
||||
* usually, a ``--cap-lints allow`` argument to hide warnings from rustc
|
||||
or clippy.
|
||||
|
||||
After every change to the ``meson.build`` file you have to update the patched
|
||||
version with ``meson subprojects update --reset ``NAME-SEMVER-rs``. This might
|
||||
be automated in the future.
|
||||
|
||||
Also, after every change to the ``meson.build`` file it is strongly suggested to
|
||||
do a dummy change to the ``.wrap`` file (for example adding a comment like
|
||||
``# version 2``), which will help Meson notice that the subproject is out of date.
|
||||
|
||||
As a last step, add the new subproject to ``scripts/archive-source.sh``,
|
||||
``scripts/make-release`` and ``subprojects/.gitignore``.
|
|
@ -126,7 +126,8 @@ static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b,
|
|||
float_raise(float_flag_invalid | float_flag_invalid_snan, s);
|
||||
}
|
||||
|
||||
if (infzero) {
|
||||
if (infzero &&
|
||||
!(s->float_infzeronan_rule & float_infzeronan_suppress_invalid)) {
|
||||
/* This is (0 * inf) + NaN or (inf * 0) + NaN */
|
||||
float_raise(float_flag_invalid | float_flag_invalid_imz, s);
|
||||
}
|
||||
|
@ -144,7 +145,7 @@ static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b,
|
|||
* Inf * 0 + NaN -- some implementations return the
|
||||
* default NaN here, and some return the input NaN.
|
||||
*/
|
||||
switch (s->float_infzeronan_rule) {
|
||||
switch (s->float_infzeronan_rule & ~float_infzeronan_suppress_invalid) {
|
||||
case float_infzeronan_dnan_never:
|
||||
break;
|
||||
case float_infzeronan_dnan_always:
|
||||
|
|
|
@ -280,11 +280,21 @@ typedef enum __attribute__((__packed__)) {
|
|||
/* No propagation rule specified */
|
||||
float_infzeronan_none = 0,
|
||||
/* Result is never the default NaN (so always the input NaN) */
|
||||
float_infzeronan_dnan_never,
|
||||
float_infzeronan_dnan_never = 1,
|
||||
/* Result is always the default NaN */
|
||||
float_infzeronan_dnan_always,
|
||||
float_infzeronan_dnan_always = 2,
|
||||
/* Result is the default NaN if the input NaN is quiet */
|
||||
float_infzeronan_dnan_if_qnan,
|
||||
float_infzeronan_dnan_if_qnan = 3,
|
||||
/*
|
||||
* Don't raise Invalid for 0 * Inf + NaN. Default is to raise.
|
||||
* IEEE 754-2008 section 7.2 makes it implementation defined whether
|
||||
* 0 * Inf + QNaN raises Invalid or not. Note that 0 * Inf + SNaN will
|
||||
* raise the Invalid flag for the SNaN anyway.
|
||||
*
|
||||
* This is a flag which can be ORed in with any of the above
|
||||
* DNaN behaviour options.
|
||||
*/
|
||||
float_infzeronan_suppress_invalid = (1 << 7),
|
||||
} FloatInfZeroNaNRule;
|
||||
|
||||
/*
|
||||
|
|
|
@ -52,7 +52,6 @@ empty_structs_with_brackets = "deny"
|
|||
ignored_unit_patterns = "deny"
|
||||
implicit_clone = "deny"
|
||||
macro_use_imports = "deny"
|
||||
missing_const_for_fn = "deny"
|
||||
missing_safety_doc = "deny"
|
||||
multiple_crate_versions = "deny"
|
||||
mut_mut = "deny"
|
||||
|
|
2
rust/clippy.toml
Normal file
2
rust/clippy.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
doc-valid-idents = ["PrimeCell", ".."]
|
||||
msrv = "1.63.0"
|
|
@ -4,14 +4,12 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
authors = ["Manos Pitsidianakis <manos.pitsidianakis@linaro.org>"]
|
||||
license = "GPL-2.0-or-later"
|
||||
readme = "README.md"
|
||||
homepage = "https://www.qemu.org"
|
||||
description = "pl011 device model for QEMU"
|
||||
repository = "https://gitlab.com/epilys/rust-for-qemu"
|
||||
resolver = "2"
|
||||
publish = false
|
||||
keywords = []
|
||||
categories = []
|
||||
rust-version = "1.63.0"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
# PL011 QEMU Device Model
|
||||
|
||||
This library implements a device model for the PrimeCell® UART (PL011)
|
||||
device in QEMU.
|
||||
|
||||
## Build static lib
|
||||
|
||||
Host build target must be explicitly specified:
|
||||
|
||||
```sh
|
||||
cargo build --target x86_64-unknown-linux-gnu
|
||||
```
|
||||
|
||||
Replace host target triplet if necessary.
|
||||
|
||||
## Generate Rust documentation
|
||||
|
||||
To generate docs for this crate, including private items:
|
||||
|
||||
```sh
|
||||
cargo doc --no-deps --document-private-items --target x86_64-unknown-linux-gnu
|
||||
```
|
||||
|
||||
To include direct dependencies like `bilge` (bitmaps for register types):
|
||||
|
||||
```sh
|
||||
cargo tree --depth 1 -e normal --prefix none \
|
||||
| cut -d' ' -f1 \
|
||||
| xargs printf -- '-p %s\n' \
|
||||
| xargs cargo doc --no-deps --document-private-items --target x86_64-unknown-linux-gnu
|
||||
```
|
|
@ -12,7 +12,6 @@ use qemu_api::{
|
|||
|
||||
use crate::device::{PL011Registers, PL011State};
|
||||
|
||||
#[allow(clippy::missing_const_for_fn)]
|
||||
extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool {
|
||||
let state = NonNull::new(opaque).unwrap().cast::<PL011State>();
|
||||
unsafe { state.as_ref().migrate_clock }
|
||||
|
|
|
@ -1,29 +1,18 @@
|
|||
// Copyright 2024, Linaro Limited
|
||||
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
//
|
||||
// PL011 QEMU Device Model
|
||||
//
|
||||
// This library implements a device model for the PrimeCell® UART (PL011)
|
||||
// device in QEMU.
|
||||
//
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
//! PL011 QEMU Device Model
|
||||
//!
|
||||
//! This library implements a device model for the PrimeCell® UART (PL011)
|
||||
//! device in QEMU.
|
||||
//!
|
||||
//! # Library crate
|
||||
//!
|
||||
//! See [`PL011State`](crate::device::PL011State) for the device model type and
|
||||
//! the [`registers`] module for register types.
|
||||
|
||||
#![deny(
|
||||
clippy::correctness,
|
||||
clippy::suspicious,
|
||||
clippy::complexity,
|
||||
clippy::perf,
|
||||
clippy::cargo,
|
||||
clippy::nursery,
|
||||
clippy::style
|
||||
)]
|
||||
#![allow(clippy::upper_case_acronyms)]
|
||||
#![allow(clippy::result_unit_err)]
|
||||
|
||||
use qemu_api::c_str;
|
||||
|
||||
|
|
|
@ -4,14 +4,12 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
authors = ["Manos Pitsidianakis <manos.pitsidianakis@linaro.org>"]
|
||||
license = "GPL-2.0-or-later"
|
||||
readme = "README.md"
|
||||
homepage = "https://www.qemu.org"
|
||||
description = "Rust bindings for QEMU - Utility macros"
|
||||
repository = "https://gitlab.com/qemu-project/qemu/"
|
||||
resolver = "2"
|
||||
publish = false
|
||||
keywords = []
|
||||
categories = []
|
||||
rust-version = "1.63.0"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
# `qemu-api-macros` - Utility macros for defining QEMU devices
|
|
@ -12,6 +12,7 @@ resolver = "2"
|
|||
publish = false
|
||||
keywords = []
|
||||
categories = []
|
||||
rust-version = "1.63.0"
|
||||
|
||||
[dependencies]
|
||||
qemu_api_macros = { path = "../qemu-api-macros" }
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#![cfg_attr(not(MESON), doc = include_str!("../README.md"))]
|
||||
#![deny(clippy::missing_const_for_fn)]
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub mod bindings;
|
||||
|
|
|
@ -178,8 +178,11 @@ void cpu_init_fp_statuses(CPUX86State *env)
|
|||
* "Fused-Multiply-ADD (FMA) Numeric Behavior" the NaN handling is
|
||||
* specified -- for 0 * inf + NaN the input NaN is selected, and if
|
||||
* there are multiple input NaNs they are selected in the order a, b, c.
|
||||
* We also do not raise Invalid for the 0 * inf + (Q)NaN case.
|
||||
*/
|
||||
set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->sse_status);
|
||||
set_float_infzeronan_rule(float_infzeronan_dnan_never |
|
||||
float_infzeronan_suppress_invalid,
|
||||
&env->sse_status);
|
||||
set_float_3nan_prop_rule(float_3nan_prop_abc, &env->sse_status);
|
||||
/* Default NaN: sign bit set, most significant frac bit set */
|
||||
set_float_default_nan_pattern(0b11000000, &env->fp_status);
|
||||
|
|
|
@ -766,6 +766,7 @@ static int do_constant_folding_cond1(OptContext *ctx, TCGOp *op, TCGArg dest,
|
|||
TCGArg *p1, TCGArg *p2, TCGArg *pcond)
|
||||
{
|
||||
TCGCond cond;
|
||||
TempOptInfo *i1;
|
||||
bool swap;
|
||||
int r;
|
||||
|
||||
|
@ -783,19 +784,21 @@ static int do_constant_folding_cond1(OptContext *ctx, TCGOp *op, TCGArg dest,
|
|||
return -1;
|
||||
}
|
||||
|
||||
i1 = arg_info(*p1);
|
||||
|
||||
/*
|
||||
* TSTNE x,x -> NE x,0
|
||||
* TSTNE x,-1 -> NE x,0
|
||||
* TSTNE x,i -> NE x,0 if i includes all nonzero bits of x
|
||||
*/
|
||||
if (args_are_copies(*p1, *p2) || arg_is_const_val(*p2, -1)) {
|
||||
if (args_are_copies(*p1, *p2) ||
|
||||
(arg_is_const(*p2) && (i1->z_mask & ~arg_info(*p2)->val) == 0)) {
|
||||
*p2 = arg_new_constant(ctx, 0);
|
||||
*pcond = tcg_tst_eqne_cond(cond);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* TSTNE x,sign -> LT x,0 */
|
||||
if (arg_is_const_val(*p2, (ctx->type == TCG_TYPE_I32
|
||||
? INT32_MIN : INT64_MIN))) {
|
||||
/* TSTNE x,i -> LT x,0 if i only includes sign bit copies */
|
||||
if (arg_is_const(*p2) && (arg_info(*p2)->val & ~i1->s_mask) == 0) {
|
||||
*p2 = arg_new_constant(ctx, 0);
|
||||
*pcond = tcg_tst_ltge_cond(cond);
|
||||
return -1;
|
||||
|
|
|
@ -18,6 +18,7 @@ X86_64_TESTS += adox
|
|||
X86_64_TESTS += test-1648
|
||||
X86_64_TESTS += test-2175
|
||||
X86_64_TESTS += cross-modifying-code
|
||||
X86_64_TESTS += fma
|
||||
TESTS=$(MULTIARCH_TESTS) $(X86_64_TESTS) test-x86_64
|
||||
else
|
||||
TESTS=$(MULTIARCH_TESTS)
|
||||
|
|
109
tests/tcg/x86_64/fma.c
Normal file
109
tests/tcg/x86_64/fma.c
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Test some fused multiply add corner cases.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
/*
|
||||
* Perform one "n * m + a" operation using the vfmadd insn and return
|
||||
* the result; on return *mxcsr_p is set to the bottom 6 bits of MXCSR
|
||||
* (the Flag bits). If ftz is true then we set MXCSR.FTZ while doing
|
||||
* the operation.
|
||||
* We print the operation and its results to stdout.
|
||||
*/
|
||||
static uint64_t do_fmadd(uint64_t n, uint64_t m, uint64_t a,
|
||||
bool ftz, uint32_t *mxcsr_p)
|
||||
{
|
||||
uint64_t r;
|
||||
uint32_t mxcsr = 0;
|
||||
uint32_t ftz_bit = ftz ? (1 << 15) : 0;
|
||||
uint32_t saved_mxcsr = 0;
|
||||
|
||||
asm volatile("stmxcsr %[saved_mxcsr]\n"
|
||||
"stmxcsr %[mxcsr]\n"
|
||||
"andl $0xffff7fc0, %[mxcsr]\n"
|
||||
"orl %[ftz_bit], %[mxcsr]\n"
|
||||
"ldmxcsr %[mxcsr]\n"
|
||||
"movq %[a], %%xmm0\n"
|
||||
"movq %[m], %%xmm1\n"
|
||||
"movq %[n], %%xmm2\n"
|
||||
/* xmm0 = xmm0 + xmm2 * xmm1 */
|
||||
"vfmadd231sd %%xmm1, %%xmm2, %%xmm0\n"
|
||||
"movq %%xmm0, %[r]\n"
|
||||
"stmxcsr %[mxcsr]\n"
|
||||
"ldmxcsr %[saved_mxcsr]\n"
|
||||
: [r] "=r" (r), [mxcsr] "=m" (mxcsr),
|
||||
[saved_mxcsr] "=m" (saved_mxcsr)
|
||||
: [n] "r" (n), [m] "r" (m), [a] "r" (a),
|
||||
[ftz_bit] "r" (ftz_bit)
|
||||
: "xmm0", "xmm1", "xmm2");
|
||||
*mxcsr_p = mxcsr & 0x3f;
|
||||
printf("vfmadd132sd 0x%" PRIx64 " 0x%" PRIx64 " 0x%" PRIx64
|
||||
" = 0x%" PRIx64 " MXCSR flags 0x%" PRIx32 "\n",
|
||||
n, m, a, r, *mxcsr_p);
|
||||
return r;
|
||||
}
|
||||
|
||||
typedef struct testdata {
|
||||
/* Input n, m, a */
|
||||
uint64_t n;
|
||||
uint64_t m;
|
||||
uint64_t a;
|
||||
bool ftz;
|
||||
/* Expected result */
|
||||
uint64_t expected_r;
|
||||
/* Expected low 6 bits of MXCSR (the Flag bits) */
|
||||
uint32_t expected_mxcsr;
|
||||
} testdata;
|
||||
|
||||
static testdata tests[] = {
|
||||
{ 0, 0x7ff0000000000000, 0x7ff000000000aaaa, false, /* 0 * Inf + SNaN */
|
||||
0x7ff800000000aaaa, 1 }, /* Should be QNaN and does raise Invalid */
|
||||
{ 0, 0x7ff0000000000000, 0x7ff800000000aaaa, false, /* 0 * Inf + QNaN */
|
||||
0x7ff800000000aaaa, 0 }, /* Should be QNaN and does *not* raise Invalid */
|
||||
/*
|
||||
* These inputs give a result which is tiny before rounding but which
|
||||
* becomes non-tiny after rounding. x86 is a "detect tininess after
|
||||
* rounding" architecture, so it should give a non-denormal result and
|
||||
* not set the Underflow flag (only the Precision flag for an inexact
|
||||
* result).
|
||||
*/
|
||||
{ 0x3fdfffffffffffff, 0x001fffffffffffff, 0x801fffffffffffff, false,
|
||||
0x8010000000000000, 0x20 },
|
||||
/*
|
||||
* Flushing of denormal outputs to zero should also happen after
|
||||
* rounding, so setting FTZ should not affect the result or the flags.
|
||||
* QEMU currently does not emulate this correctly because we do the
|
||||
* flush-to-zero check before rounding, so we incorrectly produce a
|
||||
* zero result and set Underflow as well as Precision.
|
||||
*/
|
||||
#ifdef ENABLE_FAILING_TESTS
|
||||
{ 0x3fdfffffffffffff, 0x001fffffffffffff, 0x801fffffffffffff, true,
|
||||
0x8010000000000000, 0x20 }, /* Enabling FTZ shouldn't change flags */
|
||||
#endif
|
||||
};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
bool passed = true;
|
||||
for (int i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||
uint32_t mxcsr;
|
||||
uint64_t r = do_fmadd(tests[i].n, tests[i].m, tests[i].a,
|
||||
tests[i].ftz, &mxcsr);
|
||||
if (r != tests[i].expected_r) {
|
||||
printf("expected result 0x%" PRIx64 "\n", tests[i].expected_r);
|
||||
passed = false;
|
||||
}
|
||||
if (mxcsr != tests[i].expected_mxcsr) {
|
||||
printf("expected MXCSR flags 0x%x\n", tests[i].expected_mxcsr);
|
||||
passed = false;
|
||||
}
|
||||
}
|
||||
return passed ? 0 : 1;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue