dasSpirv: fix vec*=scalar (OpVectorTimesScalar) + SPIR-V 1.4 entry-point interface#3238
Merged
Merged
Conversation
…int interface Two emitter bugs surfaced by the first mesh-shader-with-a-UBO (a GPU-tessellated teapot tutorial), both proven via spirv-val and guarded by new fixtures: 1. Compound assignment `vec *= scalar` emitted a component-wise OpFMul(vec, scalar), which is illegal (OpFMul requires both operands to equal the result type) and crashed the driver. The `*` binop already routed mixed shapes through emit_mul (OpVectorTimesScalar / OpMatrixTimes*); the `*=` path bypassed it and emitted bo.code directly. Route `*=` multiply through emit_mul too; same-shape still falls back to the component-wise op. (A runtime-scalar `vec*float` binop was fine; the `*=` form was wrong for both constant and runtime scalars.) 2. SPIR-V >= 1.4 requires the OpEntryPoint interface to list EVERY global the entry point references, not just Input/Output (the <= 1.3 rule). The emitter only pushed Input/Output vars, so a mesh/task shader reading a UBO produced a blob whose Uniform variable was missing from the interface (invalid; some drivers tolerated it, others would not). At entry-point emission, for version >= 1.4, append the remaining used globals (Uniform/StorageBuffer/PushConstant/UniformConstant/Workgroup), ordered by var-id (= emission order) so the blob stays byte-reproducible. Fixtures (tests/spirv): `vts` (vec *= const and runtime scalar -> two OpVectorTimesScalar, spirv-val clean) and `mesh_ubo` (a mesh shader reading a @uniform -> the Uniform var is in the OpEntryPoint interface, count 4, spirv-val clean). Both fail on the unfixed emitter. Full dasSpirv suite green (154/154: golden + census + spirv-val); no existing golden changed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes two SPIR-V emission correctness issues in dasSpirv that were surfaced by mesh-shader + UBO usage and validated via spirv-val, with new fixtures guarding regressions.
Changes:
- Fix compound-assignment multiply (
*=) so mixed-shape cases (e.g.,vec *= scalar) route throughemit_muland emit legalOpVectorTimesScalar/OpMatrixTimes*ops instead of an illegal shape-mismatchedOp*Mul. - For SPIR-V version ≥ 1.4, expand
OpEntryPointinterface to include all referenced globals (beyond Input/Output), appending any missing global variable IDs deterministically (sorted by var id). - Add new SPIR-V test fixtures (
vts,mesh_ubo) and wire them into the opcode census to ensure ongoing coverage.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
modules/dasSpirv/spirv/spirv_emit.das |
Fix *= lowering for mixed shapes via emit_mul; for SPIR-V ≥ 1.4, include all referenced globals in OpEntryPoint interface. |
tests/spirv/_spirv_common.das |
Add new fixtures vts_spv (vec*=scalar) and mesh_ubo_spv (mesh shader reads UBO) and expose *_words() helpers. |
tests/spirv/test_vecarith.das |
Add test asserting vec *= scalar lowers to exactly two OpVectorTimesScalar and validates cleanly. |
tests/spirv/test_mesh.das |
Add test asserting mesh shader UBO is included in OpEntryPoint interface (SPIR-V 1.4) and validates cleanly. |
tests/spirv/test_census.das |
Include the new fixtures in the opcode census union. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Two dasSpirv emitter bugs surfaced by the first mesh-shader-with-a-UBO (a GPU-tessellated teapot tutorial in dasVulkan), both proven via
spirv-valand guarded by new fixtures.1.
vec *= scalaremitted an illegalOpFMul(vec, scalar)A compound-assignment multiply with a vector LHS and a scalar RHS lowered to a component-wise
OpFMul— illegal, sinceOpFMulrequires both operands to equal the result type — which crashed the driver. The*binop already routed mixed shapes throughemit_mul(OpVectorTimesScalar/OpMatrixTimes*); the*=path bypassed it and emittedbo.codedirectly.Fix: route
*=multiply throughemit_multoo; same-shape (vec*=vec,scalar*=scalar) still falls back to the component-wise op. (A runtime-scalarvec*floatbinop was already fine; the*=form was wrong for both constant and runtime scalars.)2. SPIR-V ≥ 1.4 entry-point interface omitted UBOs
SPIR-V 1.4 requires the
OpEntryPointinterface to list every global the entry point references — not just Input/Output (the ≤ 1.3 rule). The emitter only pushed Input/Output vars, so a mesh/task shader reading a UBO produced a blob whoseUniformvariable was missing from the interface (invalid; some drivers tolerated it, others would not — and a strict validator/lavapipe rejects it).Fix: at entry-point emission, for version ≥ 1.4, append the remaining used globals (
Uniform/StorageBuffer/PushConstant/UniformConstant/Workgroup), ordered by var-id (= emission order) so the blob stays byte-reproducible.Tests
vts—vec *= 0.5(const) andvec *= x(runtime) → twoOpVectorTimesScalar, spirv-val clean.mesh_ubo— a mesh shader reading a@uniform→ theUniformvariable is listed in theOpEntryPointinterface (count 4: 3 outputs + the UBO), spirv-val clean.Both fail on the unfixed emitter. Full dasSpirv suite green (154/154: golden + census + spirv-val); no existing golden changed (nothing used
vec*=scalar, and the existing mesh fixtures have no descriptor globals to add).🤖 Generated with Claude Code