-
Notifications
You must be signed in to change notification settings - Fork 78
Remove unsafe from unconditionally safe subgroup ops #306
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
Conversation
…ible, but not undefined behavior
…sible, but not undefined behavior
…t possible, but not undefined behavior
… not undefined behavior
|
Since I copied over the spirv spec for each function, it's actually surprisingly simple to just Ctrl+F for I do agree that undefined result should be considered safe. I'd love to see a |
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 cleaned up the docs a bit and added a safety section everywhere except cluster ops. I want to try to make them safe in a followup PR with some const assertions. Added it here anyway
Also discovered that subgroup_quad_broadcast is also safe, just the result may be undefined.
Ready to merge from my side
05a15a2 to
1590c70
Compare
|
I decided to add the custer ops safety into this PR. It needs to remain unsafe due to one precondition that we cannot statically verify, as group size is defined by the device: const {
assert!(CLUSTER_SIZE >= 1, "`ClusterSize` must be at least 1");
assert!(
CLUSTER_SIZE.is_power_of_two(),
"`ClusterSize` must be a power of 2"
);
// Cannot be verified with static assertions:
// `ClusterSize` must not be greater than the size of the group
} |
|
I'd love if someone else from the team has a quick read over this one. @LegNeato ? |
|
@Firestar99 it should be pretty simple to generate safe wrappers from the same macro if you think it's worth including. ( E: Scratch the idea for safe wrappers. This can definitely be enforced in a way that makes it unconditionally safe because any cluster size used by the spv module has to be known at compile time (it's a const generic) alongside the enabling capabilities (GroupNonUniform*). The module can't be loaded if the enabling capabilities aren't supported by the target device. This could be extended to subgroup size constraints too. But I don't know if the infrastructure is there for that. |
|
First, I'd merge this as is and move any further enhancements to a new PR.
I'm reading "size of the group" to be equivalent to the subgroupSize property of the device, which is device dependent obviously. So there's no way you can check this at compile time. (There's devices with varying subgroup size, so Vulkan 1.3 has min and max)
I don't know if you'd want to have a fix subgroup size defined in the shader. That said, I'm not experienced with how people use clustered ops, I haven't found a good use-case for them yet. |
Agreed
It's not that it'd be constant, it'd be "module requires subgroupSize >= X" |
|
https://registry.khronos.org/vulkan/specs/latest/man/html/SubgroupSize.html The SubgroupSize builtin is required to match the subgroupSize property (on the host), until 1.6 or with additional extensions / options. Intel and Apple can use smaller subgroups than reported (both in the shader and on the host). Intel supports subgroup_size_control, so it can be set to a known value. The number of active threads within a subgroup can be queried by a subgroup add op. While sound, this isn't zero cost. I would probably leave clustered ops as unsafe unless there's motivation. It is difficult to use subgroup ops without a known size (constant or spec constant), just like many algorithms may require a specific workgroup 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.
requesting changes to make sure folks see @charles-r-earp 's comment.
|
@LegNeato I'm a bit confused by you requesting changes. The discussion we've had were for a potential future extension on top of this PR to make clustered ops safe. This PR itself doesn't, it merely adds some compile-time const asserts for clustered ops and otherwise keeps them unsafe. |
|
@Firestar99 looked this over, seems reasonable to me (but this is not my area of expertise). |
| /// `id` must be a scalar of integer type, whose Signedness operand is 0. | ||
| /// | ||
| /// Before version 1.5, Id must come from a constant instruction. Starting with version 1.5, this restriction is lifted. However, behavior is undefined when Id is not dynamically uniform. | ||
| /// Before version 1.5, `id` must come from a constant instruction. Starting with version 1.5, this restriction is lifted. However, behavior is undefined when `id` is not dynamically uniform. |
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.
There is 1.5 mentioned in a few places. I suspect this means SPIR-V 1.5/Vulkan 1.2? Having a reference to the compilation target would be helpful in such cases.
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.
The text is a 1:1 copy from the spirv spec of said function:
https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#_non_uniform_instructions
so yea 1.5 refers to spirv 1.5
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 must say SPIR-V spec does not have good docs, not even close 🥲
And when there I at least have context of what I'm looking at, I think it would be useful to adjust messages in docs to explain what it refers to.
Moreover, when I compile to spirv-unknown-vulkan1.2 I don't really specify SPIR-V directly, so the gap between what I write in code and what I read in docs is even larger.
| /// Requires Capability `GroupNonUniformBallot`. | ||
| /// | ||
| /// # Safety | ||
| /// * `id` must not be dynamically uniform |
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 think there is a typo here and it should be this instead:
| /// * `id` must not be dynamically uniform | |
| /// * `id` must be dynamically uniform |
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.
Fixed in #443
Fixes #305. Here is what I decided to allow, and the justification for each.