Skip to content

[mlir][spirv] Add definition for OpEmitVertex and OpEndPrimitive #123759

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

Merged
merged 1 commit into from
Jan 22, 2025

Conversation

IgWod-IMG
Copy link
Contributor

Hey,

This is hopefully the first patch in the series of patches adding some missing SPIR-V ops to MLIR over the next weeks/months, starting with something simple: OpEmitVertex and OpEndPrimitive. Since the ops have no input and outputs, and the only condition is "This instruction must only be used when only one stream is present.", which I don't think can be validate at the instruction level in isolation, I set hasVerifier to 0. I hope I didn't miss anything, but I'm more than happy to address any comments.

Also, I don't have commit rights, so someone else needs to merge it, once approved.

Many thanks,
Igor

Copy link

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot
Copy link
Member

llvmbot commented Jan 21, 2025

@llvm/pr-subscribers-mlir-spirv

Author: Igor Wodiany (IgWod-IMG)

Changes

Hey,

This is hopefully the first patch in the series of patches adding some missing SPIR-V ops to MLIR over the next weeks/months, starting with something simple: OpEmitVertex and OpEndPrimitive. Since the ops have no input and outputs, and the only condition is "This instruction must only be used when only one stream is present.", which I don't think can be validate at the instruction level in isolation, I set hasVerifier to 0. I hope I didn't miss anything, but I'm more than happy to address any comments.

Also, I don't have commit rights, so someone else needs to merge it, once approved.

Many thanks,
Igor


Full diff: https://github.com/llvm/llvm-project/pull/123759.diff

5 Files Affected:

  • (modified) mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td (+6-4)
  • (modified) mlir/include/mlir/Dialect/SPIRV/IR/SPIRVOps.td (+1)
  • (added) mlir/include/mlir/Dialect/SPIRV/IR/SPIRVPrimitiveOps.td (+87)
  • (added) mlir/test/Dialect/SPIRV/IR/primitive-ops.mlir (+21)
  • (added) mlir/test/Target/SPIRV/primitive-ops.mlir (+14)
diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td
index 469a9a0ef01dd2..c84677d26a8b69 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td
@@ -4438,6 +4438,8 @@ def SPIRV_OC_OpBitFieldSExtract             : I32EnumAttrCase<"OpBitFieldSExtrac
 def SPIRV_OC_OpBitFieldUExtract             : I32EnumAttrCase<"OpBitFieldUExtract", 203>;
 def SPIRV_OC_OpBitReverse                   : I32EnumAttrCase<"OpBitReverse", 204>;
 def SPIRV_OC_OpBitCount                     : I32EnumAttrCase<"OpBitCount", 205>;
+def SPIRV_OC_OpEmitVertex                   : I32EnumAttrCase<"OpEmitVertex", 218>;
+def SPIRV_OC_OpEndPrimitive                 : I32EnumAttrCase<"OpEndPrimitive", 219>;
 def SPIRV_OC_OpControlBarrier               : I32EnumAttrCase<"OpControlBarrier", 224>;
 def SPIRV_OC_OpMemoryBarrier                : I32EnumAttrCase<"OpMemoryBarrier", 225>;
 def SPIRV_OC_OpAtomicExchange               : I32EnumAttrCase<"OpAtomicExchange", 229>;
@@ -4576,7 +4578,8 @@ def SPIRV_OpcodeAttr :
       SPIRV_OC_OpBitwiseOr, SPIRV_OC_OpBitwiseXor, SPIRV_OC_OpBitwiseAnd,
       SPIRV_OC_OpNot, SPIRV_OC_OpBitFieldInsert, SPIRV_OC_OpBitFieldSExtract,
       SPIRV_OC_OpBitFieldUExtract, SPIRV_OC_OpBitReverse, SPIRV_OC_OpBitCount,
-      SPIRV_OC_OpControlBarrier, SPIRV_OC_OpMemoryBarrier, SPIRV_OC_OpAtomicExchange,
+      SPIRV_OC_OpEmitVertex, SPIRV_OC_OpEndPrimitive, SPIRV_OC_OpControlBarrier,
+      SPIRV_OC_OpMemoryBarrier, SPIRV_OC_OpAtomicExchange,
       SPIRV_OC_OpAtomicCompareExchange, SPIRV_OC_OpAtomicCompareExchangeWeak,
       SPIRV_OC_OpAtomicIIncrement, SPIRV_OC_OpAtomicIDecrement,
       SPIRV_OC_OpAtomicIAdd, SPIRV_OC_OpAtomicISub, SPIRV_OC_OpAtomicSMin,
@@ -4609,9 +4612,8 @@ def SPIRV_OpcodeAttr :
       SPIRV_OC_OpCooperativeMatrixLengthKHR, SPIRV_OC_OpSubgroupBlockReadINTEL,
       SPIRV_OC_OpSubgroupBlockWriteINTEL, SPIRV_OC_OpAssumeTrueKHR,
       SPIRV_OC_OpAtomicFAddEXT, SPIRV_OC_OpConvertFToBF16INTEL,
-      SPIRV_OC_OpConvertBF16ToFINTEL,
-      SPIRV_OC_OpControlBarrierArriveINTEL, SPIRV_OC_OpControlBarrierWaitINTEL,
-      SPIRV_OC_OpGroupIMulKHR,
+      SPIRV_OC_OpConvertBF16ToFINTEL, SPIRV_OC_OpControlBarrierArriveINTEL,
+      SPIRV_OC_OpControlBarrierWaitINTEL, SPIRV_OC_OpGroupIMulKHR,
       SPIRV_OC_OpGroupFMulKHR
     ]>;
 
diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVOps.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVOps.td
index 9912f195ba11e6..ff1ca89f93b5ac 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVOps.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVOps.td
@@ -40,6 +40,7 @@ include "mlir/Dialect/SPIRV/IR/SPIRVMatrixOps.td"
 include "mlir/Dialect/SPIRV/IR/SPIRVMemoryOps.td"
 include "mlir/Dialect/SPIRV/IR/SPIRVMiscOps.td"
 include "mlir/Dialect/SPIRV/IR/SPIRVNonUniformOps.td"
+include "mlir/Dialect/SPIRV/IR/SPIRVPrimitiveOps.td"
 include "mlir/Dialect/SPIRV/IR/SPIRVCLOps.td"
 include "mlir/Dialect/SPIRV/IR/SPIRVStructureOps.td"
 include "mlir/Interfaces/SideEffectInterfaces.td"
diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVPrimitiveOps.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVPrimitiveOps.td
new file mode 100755
index 00000000000000..12afa49613fe25
--- /dev/null
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVPrimitiveOps.td
@@ -0,0 +1,87 @@
+//===-- SPIRVPrimitiveOps.td - MLIR SPIR-V Primitive Ops ------*- tablegen -*------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===------------------------------------------------------------------------------===//
+//
+// This file contains primitive ops for the SPIR-V dialect. It corresponds
+// to "3.52.19. Primitive Instructions" of the SPIR-V specification.
+//
+//===-----------------------------------------------------------------------------===//
+
+#ifndef MLIR_DIALECT_SPIRV_PRIMITIVE_OPS
+#define MLIR_DIALECT_SPIRV_PRIMITIVE_OPS
+
+include "mlir/Dialect/SPIRV/IR/SPIRVBase.td"
+
+// -----
+
+def SPIRV_EmitVertexOp : SPIRV_Op<"EmitVertex", []> {
+  let summary = [{
+    Emits the current values of all output variables to the current output
+    primitive. After execution, the values of all output variables are
+    undefined.
+  }];
+
+  let description = [{
+    This instruction must only be used when only one stream is present.
+
+    #### Example:
+
+    ```mlir
+    spirv.EmitVertex
+    ```
+  }];
+
+  let availability = [
+    MinVersion<SPIRV_V_1_0>,
+    MaxVersion<SPIRV_V_1_6>,
+    Extension<[]>,
+    Capability<[SPIRV_C_Geometry]>
+  ];
+
+  let arguments = (ins);
+
+  let results = (outs);
+
+  let hasVerifier = 0;
+
+  let assemblyFormat = "attr-dict";
+}
+
+// -----
+
+def SPIRV_EndPrimitiveOp : SPIRV_Op<"EndPrimitive", []> {
+  let summary = [{
+    Finish the current primitive and start a new one.  No vertex is emitted.
+  }];
+
+  let description = [{
+    This instruction must only be used when only one stream is present.
+
+    #### Example:
+
+    ```mlir
+    spirv.EndPrimitive
+    ```
+  }];
+
+  let availability = [
+    MinVersion<SPIRV_V_1_0>,
+    MaxVersion<SPIRV_V_1_6>,
+    Extension<[]>,
+    Capability<[SPIRV_C_Geometry]>
+  ];
+
+  let arguments = (ins);
+
+  let results = (outs);
+
+  let hasVerifier = 0;
+
+  let assemblyFormat = "attr-dict";
+}
+
+#endif // MLIR_DIALECT_SPIRV_PRIMITIVE_OPS
diff --git a/mlir/test/Dialect/SPIRV/IR/primitive-ops.mlir b/mlir/test/Dialect/SPIRV/IR/primitive-ops.mlir
new file mode 100644
index 00000000000000..5d8213da57e1e7
--- /dev/null
+++ b/mlir/test/Dialect/SPIRV/IR/primitive-ops.mlir
@@ -0,0 +1,21 @@
+// RUN: mlir-opt -split-input-file -verify-diagnostics %s | FileCheck %s
+
+//===----------------------------------------------------------------------===//
+// spirv.EmitVertex
+//===----------------------------------------------------------------------===//
+
+func.func @emit_vertex() {
+  // CHECK: spirv.EmitVertex
+  spirv.EmitVertex
+  spirv.Return
+}
+
+//===----------------------------------------------------------------------===//
+// spirv.EndPrimitive
+//===----------------------------------------------------------------------===//
+
+func.func @end_primitive() {
+  // CHECK: spirv.EndPrimitive
+  spirv.EndPrimitive
+  spirv.Return
+}
diff --git a/mlir/test/Target/SPIRV/primitive-ops.mlir b/mlir/test/Target/SPIRV/primitive-ops.mlir
new file mode 100644
index 00000000000000..5005511f022804
--- /dev/null
+++ b/mlir/test/Target/SPIRV/primitive-ops.mlir
@@ -0,0 +1,14 @@
+// RUN: mlir-translate -no-implicit-module -test-spirv-roundtrip %s | FileCheck %s
+
+spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader], []> {
+  spirv.func @emit_vertex() "None" {
+    // CHECK: spirv.EmitVertex
+    spirv.EmitVertex
+    spirv.Return
+  }
+  spirv.func @end_primitive() "None" {
+    // CHECK: spirv.EndPrimitive
+    spirv.EndPrimitive
+    spirv.Return
+  }
+}

@llvmbot
Copy link
Member

llvmbot commented Jan 21, 2025

@llvm/pr-subscribers-mlir

Author: Igor Wodiany (IgWod-IMG)

Changes

Hey,

This is hopefully the first patch in the series of patches adding some missing SPIR-V ops to MLIR over the next weeks/months, starting with something simple: OpEmitVertex and OpEndPrimitive. Since the ops have no input and outputs, and the only condition is "This instruction must only be used when only one stream is present.", which I don't think can be validate at the instruction level in isolation, I set hasVerifier to 0. I hope I didn't miss anything, but I'm more than happy to address any comments.

Also, I don't have commit rights, so someone else needs to merge it, once approved.

Many thanks,
Igor


Full diff: https://github.com/llvm/llvm-project/pull/123759.diff

5 Files Affected:

  • (modified) mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td (+6-4)
  • (modified) mlir/include/mlir/Dialect/SPIRV/IR/SPIRVOps.td (+1)
  • (added) mlir/include/mlir/Dialect/SPIRV/IR/SPIRVPrimitiveOps.td (+87)
  • (added) mlir/test/Dialect/SPIRV/IR/primitive-ops.mlir (+21)
  • (added) mlir/test/Target/SPIRV/primitive-ops.mlir (+14)
diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td
index 469a9a0ef01dd2..c84677d26a8b69 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td
@@ -4438,6 +4438,8 @@ def SPIRV_OC_OpBitFieldSExtract             : I32EnumAttrCase<"OpBitFieldSExtrac
 def SPIRV_OC_OpBitFieldUExtract             : I32EnumAttrCase<"OpBitFieldUExtract", 203>;
 def SPIRV_OC_OpBitReverse                   : I32EnumAttrCase<"OpBitReverse", 204>;
 def SPIRV_OC_OpBitCount                     : I32EnumAttrCase<"OpBitCount", 205>;
+def SPIRV_OC_OpEmitVertex                   : I32EnumAttrCase<"OpEmitVertex", 218>;
+def SPIRV_OC_OpEndPrimitive                 : I32EnumAttrCase<"OpEndPrimitive", 219>;
 def SPIRV_OC_OpControlBarrier               : I32EnumAttrCase<"OpControlBarrier", 224>;
 def SPIRV_OC_OpMemoryBarrier                : I32EnumAttrCase<"OpMemoryBarrier", 225>;
 def SPIRV_OC_OpAtomicExchange               : I32EnumAttrCase<"OpAtomicExchange", 229>;
@@ -4576,7 +4578,8 @@ def SPIRV_OpcodeAttr :
       SPIRV_OC_OpBitwiseOr, SPIRV_OC_OpBitwiseXor, SPIRV_OC_OpBitwiseAnd,
       SPIRV_OC_OpNot, SPIRV_OC_OpBitFieldInsert, SPIRV_OC_OpBitFieldSExtract,
       SPIRV_OC_OpBitFieldUExtract, SPIRV_OC_OpBitReverse, SPIRV_OC_OpBitCount,
-      SPIRV_OC_OpControlBarrier, SPIRV_OC_OpMemoryBarrier, SPIRV_OC_OpAtomicExchange,
+      SPIRV_OC_OpEmitVertex, SPIRV_OC_OpEndPrimitive, SPIRV_OC_OpControlBarrier,
+      SPIRV_OC_OpMemoryBarrier, SPIRV_OC_OpAtomicExchange,
       SPIRV_OC_OpAtomicCompareExchange, SPIRV_OC_OpAtomicCompareExchangeWeak,
       SPIRV_OC_OpAtomicIIncrement, SPIRV_OC_OpAtomicIDecrement,
       SPIRV_OC_OpAtomicIAdd, SPIRV_OC_OpAtomicISub, SPIRV_OC_OpAtomicSMin,
@@ -4609,9 +4612,8 @@ def SPIRV_OpcodeAttr :
       SPIRV_OC_OpCooperativeMatrixLengthKHR, SPIRV_OC_OpSubgroupBlockReadINTEL,
       SPIRV_OC_OpSubgroupBlockWriteINTEL, SPIRV_OC_OpAssumeTrueKHR,
       SPIRV_OC_OpAtomicFAddEXT, SPIRV_OC_OpConvertFToBF16INTEL,
-      SPIRV_OC_OpConvertBF16ToFINTEL,
-      SPIRV_OC_OpControlBarrierArriveINTEL, SPIRV_OC_OpControlBarrierWaitINTEL,
-      SPIRV_OC_OpGroupIMulKHR,
+      SPIRV_OC_OpConvertBF16ToFINTEL, SPIRV_OC_OpControlBarrierArriveINTEL,
+      SPIRV_OC_OpControlBarrierWaitINTEL, SPIRV_OC_OpGroupIMulKHR,
       SPIRV_OC_OpGroupFMulKHR
     ]>;
 
diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVOps.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVOps.td
index 9912f195ba11e6..ff1ca89f93b5ac 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVOps.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVOps.td
@@ -40,6 +40,7 @@ include "mlir/Dialect/SPIRV/IR/SPIRVMatrixOps.td"
 include "mlir/Dialect/SPIRV/IR/SPIRVMemoryOps.td"
 include "mlir/Dialect/SPIRV/IR/SPIRVMiscOps.td"
 include "mlir/Dialect/SPIRV/IR/SPIRVNonUniformOps.td"
+include "mlir/Dialect/SPIRV/IR/SPIRVPrimitiveOps.td"
 include "mlir/Dialect/SPIRV/IR/SPIRVCLOps.td"
 include "mlir/Dialect/SPIRV/IR/SPIRVStructureOps.td"
 include "mlir/Interfaces/SideEffectInterfaces.td"
diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVPrimitiveOps.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVPrimitiveOps.td
new file mode 100755
index 00000000000000..12afa49613fe25
--- /dev/null
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVPrimitiveOps.td
@@ -0,0 +1,87 @@
+//===-- SPIRVPrimitiveOps.td - MLIR SPIR-V Primitive Ops ------*- tablegen -*------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===------------------------------------------------------------------------------===//
+//
+// This file contains primitive ops for the SPIR-V dialect. It corresponds
+// to "3.52.19. Primitive Instructions" of the SPIR-V specification.
+//
+//===-----------------------------------------------------------------------------===//
+
+#ifndef MLIR_DIALECT_SPIRV_PRIMITIVE_OPS
+#define MLIR_DIALECT_SPIRV_PRIMITIVE_OPS
+
+include "mlir/Dialect/SPIRV/IR/SPIRVBase.td"
+
+// -----
+
+def SPIRV_EmitVertexOp : SPIRV_Op<"EmitVertex", []> {
+  let summary = [{
+    Emits the current values of all output variables to the current output
+    primitive. After execution, the values of all output variables are
+    undefined.
+  }];
+
+  let description = [{
+    This instruction must only be used when only one stream is present.
+
+    #### Example:
+
+    ```mlir
+    spirv.EmitVertex
+    ```
+  }];
+
+  let availability = [
+    MinVersion<SPIRV_V_1_0>,
+    MaxVersion<SPIRV_V_1_6>,
+    Extension<[]>,
+    Capability<[SPIRV_C_Geometry]>
+  ];
+
+  let arguments = (ins);
+
+  let results = (outs);
+
+  let hasVerifier = 0;
+
+  let assemblyFormat = "attr-dict";
+}
+
+// -----
+
+def SPIRV_EndPrimitiveOp : SPIRV_Op<"EndPrimitive", []> {
+  let summary = [{
+    Finish the current primitive and start a new one.  No vertex is emitted.
+  }];
+
+  let description = [{
+    This instruction must only be used when only one stream is present.
+
+    #### Example:
+
+    ```mlir
+    spirv.EndPrimitive
+    ```
+  }];
+
+  let availability = [
+    MinVersion<SPIRV_V_1_0>,
+    MaxVersion<SPIRV_V_1_6>,
+    Extension<[]>,
+    Capability<[SPIRV_C_Geometry]>
+  ];
+
+  let arguments = (ins);
+
+  let results = (outs);
+
+  let hasVerifier = 0;
+
+  let assemblyFormat = "attr-dict";
+}
+
+#endif // MLIR_DIALECT_SPIRV_PRIMITIVE_OPS
diff --git a/mlir/test/Dialect/SPIRV/IR/primitive-ops.mlir b/mlir/test/Dialect/SPIRV/IR/primitive-ops.mlir
new file mode 100644
index 00000000000000..5d8213da57e1e7
--- /dev/null
+++ b/mlir/test/Dialect/SPIRV/IR/primitive-ops.mlir
@@ -0,0 +1,21 @@
+// RUN: mlir-opt -split-input-file -verify-diagnostics %s | FileCheck %s
+
+//===----------------------------------------------------------------------===//
+// spirv.EmitVertex
+//===----------------------------------------------------------------------===//
+
+func.func @emit_vertex() {
+  // CHECK: spirv.EmitVertex
+  spirv.EmitVertex
+  spirv.Return
+}
+
+//===----------------------------------------------------------------------===//
+// spirv.EndPrimitive
+//===----------------------------------------------------------------------===//
+
+func.func @end_primitive() {
+  // CHECK: spirv.EndPrimitive
+  spirv.EndPrimitive
+  spirv.Return
+}
diff --git a/mlir/test/Target/SPIRV/primitive-ops.mlir b/mlir/test/Target/SPIRV/primitive-ops.mlir
new file mode 100644
index 00000000000000..5005511f022804
--- /dev/null
+++ b/mlir/test/Target/SPIRV/primitive-ops.mlir
@@ -0,0 +1,14 @@
+// RUN: mlir-translate -no-implicit-module -test-spirv-roundtrip %s | FileCheck %s
+
+spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader], []> {
+  spirv.func @emit_vertex() "None" {
+    // CHECK: spirv.EmitVertex
+    spirv.EmitVertex
+    spirv.Return
+  }
+  spirv.func @end_primitive() "None" {
+    // CHECK: spirv.EndPrimitive
+    spirv.EndPrimitive
+    spirv.Return
+  }
+}

Copy link
Member

@kuhar kuhar left a comment

Choose a reason for hiding this comment

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

Is the plan to allow non-compute shaders?

@IgWod-IMG
Copy link
Contributor Author

Hey @kuhar,

Thank you for you feedback!

Is the plan to allow non-compute shaders?

Yes and no, we use SPIR-V MLIR in the front-end of our research tool and we're interested in all sorts of shader, not only compute, so in that sense yes. However, we don't need intend to enable them in a runtime, e.g., provide further lowering and enabling it with mlir-cpu-runner, so in that sense no.

Don't these require the Geometry execution model and capability?

Good point. I believe it should specify Geometry capability as well:

spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader, Geometry], []> {

Do I need ExecutionMode as well? E.g.,

  spirv.ExecutionMode @main "Triangles"
  spirv.ExecutionMode @main "OutputTriangleStrip"
  spirv.ExecutionMode @main "OutputVertices", 12
  spirv.ExecutionMode @main "Invocations", 1

In that case, would it make sense to merge both functions together, since having EmitVertex and EndPrimitive separately does not make much sense? I am just not clear on the level of detail this tests needs.

Once I have a clarity on that I will push an update (that also addresses formatting issues).

Many thanks,
Igor

@kuhar
Copy link
Member

kuhar commented Jan 21, 2025

In that case, would it make sense to merge both functions together, since having EmitVertex and EndPrimitive separately does not make much sense? I am just not clear on the level of detail this tests needs.

What do you mean by 'merge both functions'?

Do I need ExecutionMode as well? E.g.,

I'm not sure, can you write a compute shader that uses these ops (say, in glsl/hlsl) and make it pass validation with spirv-val?

Also note that we have tests for op availability: https://github.com/llvm/llvm-project/blob/main/mlir/test/Dialect/SPIRV/IR/availability.mlir .

@IgWod-IMG
Copy link
Contributor Author

What do you mean by 'merge both functions'?

Sorry for the confusion. I meant merge both functions within the test. What I am proposing is to have test as below, instead of separating EmitVertex and EndPrimitive into two functions in the test:

spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Geometry], []> {
  spirv.func @emit_verted_end_primitive() "None" {
    // CHECK: spirv.EmitVertex
    spirv.EmitVertex
    // CHECK: spirv.EndPrimitive
    spirv.EndPrimitive
    spirv.Return
  }
}

This is still not a completely correct shader, though. There needs to be a store to an output so there is something for EmitVertex to emit. And so are the execution modes needed to tell the hardware how and what new primitives are emitted.

I'm not sure, can you write a compute shader that uses these ops (say, in glsl/hlsl) and make it pass validation with spirv-val?

So, you can't have a compute shader with those two ops. It must be geometry. I based my observation about the ExecutionMode on running a valid, although complex, shader through mlir-translate. But let's take a step back. Is the idea of the Target test to have a minimum viable shader that includes the new op and would correctly run? A simple example that I can probably further minimize would look, in SPIR-V, something like https://godbolt.org/z/6P9vK8YfY.

The direct translation of that example with mlir-translate is:

spirv.module Logical GLSL450 requires #spirv.vce<v1.3, [Geometry], []> {
  spirv.GlobalVariable @spirv_var_3 : !spirv.ptr<!spirv.struct<(vector<4xf32> [BuiltIn=0], f32 [BuiltIn=1], !spirv.array<1 x f32> [BuiltIn=3])>, Output>
  spirv.func @main() "None" {
    %spirv_var_3_addr = spirv.mlir.addressof @spirv_var_3 : !spirv.ptr<!spirv.struct<(vector<4xf32> [BuiltIn=0], f32 [BuiltIn=1], !spirv.array<1 x f32> [BuiltIn=3])>, Output>
    %cst0_si32 = spirv.Constant 0 : si32
    %0 = spirv.AccessChain %spirv_var_3_addr[%cst0_si32] : !spirv.ptr<!spirv.struct<(vector<4xf32> [BuiltIn=0], f32 [BuiltIn=1], !spirv.array<1 x f32> [BuiltIn=3])>, Output>, si32 -> !spirv.ptr<vector<4xf32>, Output>
    %cst_vec_4xf32 = spirv.Constant dense<[-1.000000e-01, 0.000000e+00, 0.000000e+00, 0.000000e+00]> : vector<4xf32>
    spirv.Store "Output" %0, %cst_vec_4xf32 : vector<4xf32>
    spirv.EmitVertex
    %spirv_var_3_addr_0 = spirv.mlir.addressof @spirv_var_3 : !spirv.ptr<!spirv.struct<(vector<4xf32> [BuiltIn=0], f32 [BuiltIn=1], !spirv.array<1 x f32> [BuiltIn=3])>, Output>
    %cst0_si32_1 = spirv.Constant 0 : si32
    %1 = spirv.AccessChain %spirv_var_3_addr_0[%cst0_si32_1] : !spirv.ptr<!spirv.struct<(vector<4xf32> [BuiltIn=0], f32 [BuiltIn=1], !spirv.array<1 x f32> [BuiltIn=3])>, Output>, si32 -> !spirv.ptr<vector<4xf32>, Output>
    %cst_vec_4xf32_2 = spirv.Constant dense<[1.000000e-01, 0.000000e+00, 0.000000e+00, 0.000000e+00]> : vector<4xf32>
    spirv.Store "Output" %1, %cst_vec_4xf32_2 : vector<4xf32>
    spirv.EmitVertex
    spirv.EndPrimitive
    spirv.Return
  }
  spirv.EntryPoint "Geometry" @main, @spirv_var_3
  spirv.ExecutionMode @main "InputPoints"
  spirv.ExecutionMode @main "Invocations", 1
  spirv.ExecutionMode @main "OutputLineStrip"
  spirv.ExecutionMode @main "OutputVertices", 2
}

Also note that we have tests for op availability: https://github.com/llvm/llvm-project/blob/main/mlir/test/Dialect/SPIRV/IR/availability.mlir .

I'll have a look at those tests.

@kuhar
Copy link
Member

kuhar commented Jan 21, 2025

What I am proposing is to have test as below, instead of separating EmitVertex and EndPrimitive into two functions in the test:

Sounds good. Not every op has to have it's own test module -- we can group and test similar ops together.

But let's take a step back. Is the idea of the Target test to have a minimum viable shader that includes the new op and would correctly run?

It doesn't need to run and produce any results, but it should compile and validate (with spirv-val). Ideally, our own verifiers should also catch issues like missing capabilities or wrong execution modes, but it's much harder to verify these non-local properties and we (in the SPIR-V dialect) don't guarantee exhaustive verification.

@IgWod-IMG
Copy link
Contributor Author

IgWod-IMG commented Jan 21, 2025

Oh! That makes perfect sense now! Thank you for bearing with me. I got confused about what and how SPIR-V and MLIR gets verified, and this made me misunderstand your comments and what's the goal here.

Anyway, I'm going to update the test so it produces a valid SPIR-V (I think my merged example is almost correct - it may only need an EntryPoint and ExecutionMode for it to serialize into a valid SPIR-V), look into the availability test, clean up and push an updated commit in the next day or two.

Again, thanks a lot for the help, it was a valuable learning experience.

@IgWod-IMG IgWod-IMG force-pushed the img_emit-vertex-end-primitive branch from 9936877 to bfd8c6f Compare January 22, 2025 14:17
@IgWod-IMG
Copy link
Contributor Author

@kuhar I've just pushed an updated commit. Hopefully, it all should be good now. I addressed the formatting issues and added availability tests. For the Target test I created a minimal geometry shader that serializes into SPIR-V and passes validation with spiv-val. Let me know if there is anything else you would like me to address.

Copy link
Member

@kuhar kuhar left a comment

Choose a reason for hiding this comment

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

LGTM % nits

@IgWod-IMG IgWod-IMG force-pushed the img_emit-vertex-end-primitive branch from bfd8c6f to 152e527 Compare January 22, 2025 16:47
@IgWod-IMG
Copy link
Contributor Author

Done! Please commit once ready, as I don't have committer rights. Thanks a lot!

@kuhar kuhar merged commit f78359c into llvm:main Jan 22, 2025
8 checks passed
Copy link

@IgWod-IMG Congratulations on having your first Pull Request (PR) merged into the LLVM Project!

Your changes will be combined with recent changes from other authors, then tested by our build bots. If there is a problem with a build, you may receive a report in an email or a comment on this PR.

Please check whether problems have been caused by your change specifically, as the builds can include changes from many authors. It is not uncommon for your change to be included in a build that fails due to someone else's changes, or infrastructure issues.

How to do this, and the rest of the post-merge process, is covered in detail here.

If your change does cause a problem, it may be reverted, or you can revert it yourself. This is a normal part of LLVM development. You can fix your changes and open a new PR to merge them again.

If you don't get any reports, no action is required from you. Your changes are working as expected, well done!

@IgWod-IMG IgWod-IMG deleted the img_emit-vertex-end-primitive branch January 23, 2025 10:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants