-
Notifications
You must be signed in to change notification settings - Fork 1.9k
CPUID (2/3): Bit fields #3105
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CPUID (2/3): Bit fields #3105
Conversation
c24176f
to
e376cce
Compare
54e5418
to
a065ec4
Compare
72dd9ea
to
f20e071
Compare
f20e071
to
40d95cf
Compare
1bb6631
to
db04ba7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR needs a better motivation on why these new crates are needed. We already use the bitflags
crate. We should outline why this is different and what differences does this bring.
It also seems that the commitmsg is empty. We should add these details in the commit message as well.
Since there are 2 separate crates being added, we can split the commit in 2 and have 2 commits, each one for each crate.
I've added a short justification.
Added commit message.
Added a point about this, that these should be used as 1 crate. |
db04ba7
to
7fb8c52
Compare
The current failures in CI are a result of the currently not explicitly allowed license |
7fb8c52
to
a7f8999
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've gone through just bit-fields-macros and highlighted all the allow
directives that are outdated/can easily have the underlying lint fixed.
I still think that #[warn(pedantic, restriction)]
just generates too much noise, resulting in out of date and excessive allow
s, and desensitizes us to actually fixing lints.
I feel we should instead have a look at the list of clippy lints that are actually useful without high false-positives and explicitly enable that subset.
8e117e5
to
cdc436d
Compare
cdc436d
to
59411d9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These also apply to the non-zero impls.
59411d9
to
25fb11d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, I think from a functionality point of view, there are no more issues. The main things I looked at during my review were:
- undefined behavior caused by unsafe code
- being able to construct Bit(Range)s using nonsense const generic parameters (e.g. a Bit<10>(u8)).
I am approving based on this PR now correctly providing all the required functionality. However, there are some design points I would like to see revisited in follow up PRs (or justified by the usage of the functionality in the final product):
- Currently, the proc-macro generates Bit/BitMut implementations on each BitField (a few hundred lines of generated code for bitfield). These can instead be provided once per type by just implementing a
HasBit<const N: u8>
marker-trait onu8
,u16
,... (since Bit/BitMut are only used as marker traits for the function that allows arbitrary bit access to a bit-fields underlying value. - The HashSet/HashMap (de)serialization functions generated are not currently used by any implementation. If this stays this way, they should be removed (again, multiple hundred lines of code generated with each macro invocation). If they are needed, I would like them to be as specialized as possible (e.g. all the
T: Display
andK: From<u64>
bounds should be either justified through usage, or specialized to the types for which the implementations are actually needed). - The previous point regarding "remove that is not used" extends to a lot of other functions too (e.g. the multiple aliases for reading a bit(range), etc)
- The parser code can be cleaned up significantly. The grammar here is LL(1), and we should be using an LL(1) parser to process it. Potentially, We can use the
syn
crate (on which we depend already anyway) for simplifiying a lot of code, as it provides methods to, for example, parse attributes and other bits of valid rust syntax that show up in the bitfields grammar.
And lastly, I personally do not see the advantage of the Bit/BitRange structs over simply having the proc-macro generate getters and setters for each bit field bit/range. This is the approach taken by crosvm's bitfields crate. Using the complexity of that crate (which apart from not allow "holes" in defined Bitfields is at feature parity with this bit-fields crate) as a baseline for complexity, we should be able to cut down roughly 4000 lines of code by doing this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the time being, I think the functionality can be merged like it is in the feature branch. did not find any major functional bugs.
However, I do think some refinements need to happen for the functionality to be merged in main:
- One of my biggest concern is the size of the code that gets generated right now for each bitfield-enabled structure. The size is right now approximately 28K. This is worrying since this is only for one structure (i.e one
Leaf
and the CPUID ends up having a big number of those). So, what I would like to see is the generated code getting trimmed down to exactly the minimum we require for CPUID. I left some comments regarding some of the things that I saw we can get rid of (NonZero types for e.g) - I am in line with Patrick on modifying the current approach of using
non-mut
/mut
functionality to one that uses getters/setters cause in the end this is what we need for CPUID; This will make the code more straightforward and will again rid us of some duplication (like implementing read methods on both non-mut and mut code). - I think we should move this functionality all in one crate because it is weird for bit-fields-macros to generate code that calls into bit-fields when the crate dependency is actually the other way around. I am talkign about this: https://github.com/firecracker-microvm/firecracker/pull/3105/files#diff-db7421964bc6c93ef265a89cff62f1c7ea9e95748ee3c90755b492ed516878bbR142.
I will not approve for the moment since I also want to wait for @bchalios's input.
This functionality cannot be in 1 crate, crates which export proc-macros cannot export any other items. |
A bitflags like crate to support efficient implementation of CPUID functionality. Signed-off-by: Jonathan Woollett-Light <[email protected]>
25fb11d
to
7e93f57
Compare
I saw crosvm has all in 1 crate, maybe it s worth taking a look since they also used proc-macros. |
They are using 2 nested crates, the 1st is at https://github.com/dgreid/crosvm/tree/main/bit_field and the 2nd is at https://github.com/dgreid/crosvm/tree/main/bit_field/bit_field_derive. We could also do this, it shouldn't make any difference, I think its just a question of style. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reapproving after force push
82a806b
into
firecracker-microvm:feature/cpu-templates
#3104 must be merged first
Reason for This PR
Working with the large number of values smaller than
u8
required for CPUID quickly becomes untenable. With thousands of constants denoting offsets.bitflags does not support fields which are required for CPUID. To use bitflags here would require high hundreds (possibly 1000+) of additional loosely associated constants to reference these fields within the
bitflag
structures. This would form a large and difficult to read implementation.bitfield does not support exotically sized bit ranges (e.g. bits 2-7). To use this would require heavy adaption.
Description of Changes
Introduces
bit-fields
andbit-fields-macros
crates to support handling these values. These function as one crate1 inacting a similar role tobitflags
except with additional support for fields within bytes used for unsigned integer values.Linting
bit-fields
andbit-fields-macros
raise the bar in safety adhering to much stricter linting. This is defined with:Beginning from the strictest possible linting
#![warn(clippy::pedantic, clippy::restriction)]
then disabling some of the lints withinclippy::restriction
to make it reasonable.Testing & Coverage
Coverage = ~97% or ~75% with grcov or ~93% with cargo-llvm-cov
Both crates feature extensive testing. Due to #3206 when running tests within the container and in CI coverage is greatly reduced to ~40% (recent-coverage.zip).
When running with doc tests:
The coverage is reported as ~75%. Although there does appear to be an anomaly in the html coverage report, this is that
index.html
reports a coverage of62.37%
inbit_range.rs
whilebit_range.rs.html
reports97.22%
.The current grcov coverage does not cover proc-macros.
@pb8o informed me of https://crates.io/crates/cargo-llvm-cov:
Produces a report which notes ~93% line coverage.
On
bit-fields
:On
bit-fields-macros
:This path may warrant further discussion, although may well be outside the scope of this PR.
License Acceptance
By submitting this pull request, I confirm that my contribution is made under
the terms of the Apache 2.0 license.
PR Checklist
git commit -s
).unsafe
code is properly documented.CHANGELOG.md
.Footnotes
A crates cannot export both procedural-macros and non-procedural-macros, so it requires 2 crates acting as 1, like
serde
andserde-derive
↩