-
Notifications
You must be signed in to change notification settings - Fork 83
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
Bitmask conversion trait #239
Conversation
6f9258d
to
a6c178a
Compare
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
I don't really see why this needs to be a generic trait. It feels like a degree of freedom that gives very little and will make understanding the API more complex than it needs to be. It seems like it mostly is this way to save a Concretely: is there a reason Bitmask can't just be an associated type on ToBitmask, which just is implemented once per vector with bitmask being the unsigned integer that fits all the bits? (at least for the current vector sizes) |
I also kind of still oppose using Is there a reason we want that? I also don't think it would be the end of the world if Simd<T, 256> had to use |
Well, we've had Regarding complexity, I'm not sure |
I did just consider, however, that we could offer two separate traits, one for conversion to integer bitmasks, and one to arrays. Then it would look something like |
I actually think that has a good chance of being less confusing, honestly. (I do still think that |
At the risk of regretting saying it, I suspect that Like, I pretty much always want to figure out stuff like the leading/trailing zeros/ones, in order to find the index of highest/lowest lane (in whatever produced the mask) for which the predicate that generated thet BitMask returned true/false... (And a dozen other things that just suck to do with [u8; N]). That said we already have many masks... (Actually, I wonder if those operations are just better on Mask<T, N>, and if they need to convert to bitmask internally to answer that question, they can, but aren't forced to) I guess much of this came up in the The Great Simd Mask Bikeshed Of 2021... |
If that's the specific operation you want, then you can cast the array to [u64; N] etc and run those operations. Another common thing would be to match on specific values, which you could use the byte array directly for. I think the point of providing it directly as a byte array is it's not restrictive about what you can do with it, for the same reason that we return integer bitmasks as integers at all and not just an opaque type. |
I would like to see |
In retrospect, it was pretty easy removing |
Of course mips failed again, not just endianness but completely reversed bit order... |
#[inline] | ||
#[must_use = "method returns a new array and does not mutate the original value"] | ||
pub fn to_bitmask(self) -> [u8; LaneCount::<LANES>::BITMASK_LEN] { | ||
pub unsafe fn to_bitmask_array<const N: usize>(self) -> [u8; N] { |
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.
if you assert, you get a safe function afaict.
pub unsafe fn to_bitmask_array<const N: usize>(self) -> [u8; N] { | |
pub fn to_bitmask_array<const N: usize>(self) -> [u8; N] { | |
assert_eq!(N, LaneCount::<LANES>::BITMASK_LEN, "invalid bitmask array size"); |
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'm not sure the assertion actually makes it safe--the intrinsic would still be (invalidly) lowered. It should cause a monomorphization error, but I didn't want to rely on that.
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.
having transmute_copy of a wrong-sized object in a code-path that's never executed is at least just as safe as calling unreachable_unchecked
-- perfectly safe imho.
pub fn this_is_safe() {
assert!(1 == 2);
unsafe { unreachable_unchecked() }
}
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.
to_bitmask_array
feels like a more confusing name than what we had before (i.e. giving a bitmask, but as bytes) since it may imply, since many masks are 1 byte per lane, somehow lane masks, but as a set of bytes...? I think the "bitmask" terminology doesn't adequately distinguish it from the "width = lane" mask, since those are also used in the same ways that "bitmasks" are used elsewhere in programming.
If we are going to have such a verbose method, maybe to_bitmask_as_bytes
.
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'm okay with that name too, or any other suggestions.
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'm not going to resolve this whole thread because of the naming issue, but the original concern about the function being safe has been addressed.
icr where I posted the link to the relevant code in llvm, but llvm arbitrarily decided that bit order always follows byte endianness. it might be nice to mention that in the code comments somewhere |
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 kind of want this, but I also kind of am starting to get the vibe of "did we lose the plot somewhere?" Insofar as whether this is an alternative or exception to the other PR, as I see it, there is no reason this PR cannot be rebased atop the other one, so I am inclined to say we should wave Jorge's through first, collect some user feedback, and work on this some more.
#[inline] | ||
#[must_use = "method returns a new array and does not mutate the original value"] | ||
pub fn to_bitmask(self) -> [u8; LaneCount::<LANES>::BITMASK_LEN] { | ||
pub unsafe fn to_bitmask_array<const N: usize>(self) -> [u8; N] { |
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.
to_bitmask_array
feels like a more confusing name than what we had before (i.e. giving a bitmask, but as bytes) since it may imply, since many masks are 1 byte per lane, somehow lane masks, but as a set of bytes...? I think the "bitmask" terminology doesn't adequately distinguish it from the "width = lane" mask, since those are also used in the same ways that "bitmasks" are used elsewhere in programming.
If we are going to have such a verbose method, maybe to_bitmask_as_bytes
.
My apologies, I would like to downscale all previous remarks in import and instead observe that this code induces an ICE when I issue the command |
I realized that I forgot the critical component of this--bitmasks now have nothing to do with the magic |
b7f93cf
to
af6956c
Compare
Actually, I realized I jumped the gun on that because of the limitations of our bitmask-layout masks, but let me explain what I mean:
The difference between this PR and #238 is this one doesn't expose that ugly implementation detail that we'd really like to remove ASAP. |
The commits got a little messed up since I removed a broken commit, but I was able to make all of the bitmask functions safe by making |
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.
other than maybe renaming ToBitmask
to ToIntBitmask
since it only works for a few specific lane counts due to us not having generic integers yet so people will wonder why it doesn't work on f32x4
or similar, this looks good to me!
maybe we need to add doc testing to our CI? |
Ah! That makes this all make more sense then, yes, if that is the "leading edge" of this. |
Mmm, I tried building this on a subtree pull into std and I can confirm it breaks, so we can't land this as-is. It is almost entirely lamenting about the way If you split out the |
@workingjubilee I removed bitmask arrays from this PR, to be handled in #246 |
195a676
to
20fa4b7
Compare
Another approach that fixes #223, as an alternative to #238.
This adds the
ToBitMask
trait, which is implemented on a vector for each bitmask type it supports. This includes all unsigned integers with enough bits to contain it. The byte array variant has been separated out for now into #246 and still requiresgeneric_const_exprs
, but the integer variants no longer require it and can make it to nightly.