Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR reworks how "generic" globals are handled in Zig. Previously, these just emitted
CrossWorkgroup
globals, which are stored in VRAM and have the same address for every thread. This seems unexpected to me, and globals should rather be private to a particular thread, like they are in GLSL through thePrivate
storage class. OpenCL does not supportPrivate
, though. This PR implements the beginnings of a SPIR-V linker that handles this.Invocation globals, which are generated from pointers in the generic address space, are now emitted in codegen as a custom
InvocationGlobal
instruction from theextinst.zig.grammar.json
extended instruction set. This instruction does not have any runtime support of course, and instead any values that are referenced from anInvocationGlobal
instruction are lowered to function parameters by the linker. Additionally, the linker generates new entry points and calls initializers for these functions. This means that "invocation globals" are lowered to theFunction
address space.I took this approach because emitting a single struct and passing this around generates a giant struct for the behavior tests, and this grinds OpenCL runtimes to a halt. The current implementation only passes the globals that are actually required, and performs quite a bit of operations to figure that out.
Additionally, there is an operation that removes unused instructions. This is mostly for removing stuff that isn't required anymore, but also to work around a problem where the initializer generated for the test functions has as many function parameters as there are tests in the behavior.zig suite, and SPIR-V only allows for 255. I have a feeling that this might be a problem in the future, but for now, I feel this is a suitable workaround. These globals should be relatively spare in actual programs, and in the future, constant globals should be emitted in the UniformConstant address space anyway.
This initial linker implementation is pass-based: There is one pass that lowers the invocation globals, and one pass that prunes unused instructions. This allows relatively easy separation of concerns and opens a straight forward roadmap for the remaining linker operations, at the cost of multiple passes through the input. These modules are usually at most a few MB (for the time being, behavior.zig will be much larger than any actual programs), so I feel like it is fine to adopt a pass-based architecture.
This PR also adds a bunch of generated code for the SPIR-V extensions. This is required to properly parse modules, and automatically extract result-IDs from operations. This saves a lot of manual code writing, so I feel like this is fine.
To actually link multiple modules together, the linker still needs the following passes: