-
Notifications
You must be signed in to change notification settings - Fork 1k
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
[naga spv-out] Ensure loops generated by SPIRV backend are bounded #7080
Conversation
6d5a471
to
e7e914a
Compare
ee4b7ea
to
ea7babd
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.
We think there might be a nicer way to generate the temporary variables as we go, rather than making a separate pass over them beforehand.
ea7babd
to
d2c4df7
Compare
Indeed, the pass to generate a Function's Patch updated |
d2c4df7
to
1ee50df
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.
Looks great!
@jamienicol looks like there are some conflicts now |
If it is undefined behaviour for loops to be infinite, then, when encountering an infinite loop, downstream compilers are able to make certain optimizations that may be unsafe. For example, omitting bounds checks. To prevent this, we must ensure that any loops emitted by our backends are provably bounded. We already do this for both the MSL and HLSL backends. This patch makes us do so for SPIRV as well. The construct used is the same as for HLSL and MSL backends: use a vec2<u32> to emulate a 64-bit counter, which is incremented every iteration and breaks after 2^64 iterations. While the implementation is fairly verbose for the SPIRV backend, the logic is simple enough. The one point of note is that SPIRV requires `OpVariable` instructions with a `Function` storage class to be located at the start of the first block of the function. We therefore remember the IDs generated for each loop counter variable in a function whilst generating the function body's code. The instructions to declare these variables are then emitted in `Function::to_words()` prior to emitting the function's body. As this may negatively impact shader performance, this workaround can be disabled using the same mechanism as for other backends: eg calling Device::create_shader_module_trusted() and setting the ShaderRuntimeChecks::force_loop_bounding flag to false.
1ee50df
to
67c18e8
Compare
Connections
Partial fix for #6572 (can tick SPIR-V off, still GLSL to go)
Related to #6929: that updated the previous MSL workaround to use the 64bit counter approach, and added the workaround for HLSL
Description
If it is undefined behaviour for loops to be infinite, then, when encountering an infinite loop, downstream compilers are able to make certain optimizations that may be unsafe. For example, omitting bounds checks. To prevent this, we must ensure that any loops emitted by our backends are provably bounded. We already do this for both the MSL and HLSL backends. This patch makes us do so for SPIRV as well.
The construct used is the same as for HLSL and MSL backends: use a vec2 to emulate a 64-bit counter, which is incremented every iteration and breaks after 2^64 iterations.
While the implementation is fairly verbose for the SPIRV backend, the logic is simple enough. The one point of note is that SPIRV requires
OpVariable
instructions with aFunction
storage class to be located at the start of the first block of the function. We must therefore do an initial pass over the function to generate the IDs used for the loop counter variables, and ensure the correspondingOpVariable
instructions are emitted at the start of the function. Then finally during the main code-generation pass we can refer to these IDs.As this may negatively impact performance, this workaround can be disabled using the same mechanism as for other backends: eg calling Device::create_shader_module_trusted() and setting the ShaderRuntimeChecks::force_loop_bounding flag to false.
Testing
Inspected snapshot test changes. Ensured validation still passes
Checklist
cargo fmt
.cargo clippy
. If applicable, add:cargo xtask test
to run tests.CHANGELOG.md
. See simple instructions inside file. (also added the HLSL fix from [naga msl-out hlsl-out] Improve workaround for infinite loops causing undefined behaviour #6929)