Skip to content

dasSpirv: fix vec*=scalar (OpVectorTimesScalar) + SPIR-V 1.4 entry-point interface#3238

Merged
borisbat merged 1 commit into
masterfrom
bbatkin/dasspirv-vecscalar-iface
Jun 21, 2026
Merged

dasSpirv: fix vec*=scalar (OpVectorTimesScalar) + SPIR-V 1.4 entry-point interface#3238
borisbat merged 1 commit into
masterfrom
bbatkin/dasspirv-vecscalar-iface

Conversation

@borisbat

Copy link
Copy Markdown
Collaborator

Two dasSpirv emitter bugs surfaced by the first mesh-shader-with-a-UBO (a GPU-tessellated teapot tutorial in dasVulkan), both proven via spirv-val and guarded by new fixtures.

1. vec *= scalar emitted an illegal OpFMul(vec, scalar)

A compound-assignment multiply with a vector LHS and a scalar RHS lowered to a component-wise OpFMul — illegal, since OpFMul requires both operands to equal the result type — which crashed the driver. The * binop already routed mixed shapes through emit_mul (OpVectorTimesScalar / OpMatrixTimes*); the *= path bypassed it and emitted bo.code directly.

Fix: route *= multiply through emit_mul too; same-shape (vec*=vec, scalar*=scalar) still falls back to the component-wise op. (A runtime-scalar vec*float binop 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 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 — 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

  • vtsvec *= 0.5 (const) and vec *= x (runtime) → two OpVectorTimesScalar, spirv-val clean.
  • mesh_ubo — a mesh shader reading a @uniform → the Uniform variable is listed in the OpEntryPoint interface (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

…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>
Copilot AI review requested due to automatic review settings June 21, 2026 04:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 through emit_mul and emit legal OpVectorTimesScalar / OpMatrixTimes* ops instead of an illegal shape-mismatched Op*Mul.
  • For SPIR-V version ≥ 1.4, expand OpEntryPoint interface 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.

@borisbat borisbat merged commit 8095236 into master Jun 21, 2026
34 checks passed
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.

2 participants