Skip to content
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

spirv: rework generic global #19337

Merged
merged 11 commits into from
Mar 19, 2024
Merged

spirv: rework generic global #19337

merged 11 commits into from
Mar 19, 2024

Conversation

Snektron
Copy link
Collaborator

@Snektron Snektron commented Mar 17, 2024

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 the Private storage class. OpenCL does not support Private, 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 the extinst.zig.grammar.json extended instruction set. This instruction does not have any runtime support of course, and instead any values that are referenced from an InvocationGlobal 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 the Function 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:

  • One pass to merge modules into the same ID range
  • One pass to remove type and constant declarations that are the same
  • One pass to link exported and imported functions together
  • Maybe one pass to compress result-IDs.

For module parsing and assembling, we will also need to know
all of the SPIR-V extensions and their instructions. This commit
updates the generator to generate those. Because there are
multiple instruction sets that each have a separate list of Opcodes,
no separate enum is generated for these opcodes. Additionally, the
previous mechanism for runtime instruction information, `Opcode`'s
`fn operands()`, has been removed in favor for
`InstructionSet.core.instructions()`.

Any mapping from operand to instruction is to be done at runtime.
Using a runtime populated hashmap should also be more efficient
than the previous mechanism using `stringToEnum`.
We need this "unstable" version to get the Zig identifiers.
This may be removed again in the future...
@Snektron Snektron merged commit 7057bff into ziglang:master Mar 19, 2024
10 checks passed
@Snektron Snektron deleted the spirv-globals branch March 19, 2024 08:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant