Skip to content

[mlir][spirv] Add definition for GL Length #144041

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 4 commits into from
Jun 16, 2025

Conversation

IgWod-IMG
Copy link
Contributor

No description provided.

@llvmbot
Copy link
Member

llvmbot commented Jun 13, 2025

@llvm/pr-subscribers-mlir-spirv

@llvm/pr-subscribers-mlir

Author: Igor Wodiany (IgWod-IMG)

Changes

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

3 Files Affected:

  • (modified) mlir/include/mlir/Dialect/SPIRV/IR/SPIRVGLOps.td (+40)
  • (modified) mlir/test/Dialect/SPIRV/IR/gl-ops.mlir (+66)
  • (modified) mlir/test/Target/SPIRV/gl-ops.mlir (+4)
diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVGLOps.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVGLOps.td
index 2ec61758ba8ef..8c4da9b2dce18 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVGLOps.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVGLOps.td
@@ -1160,6 +1160,46 @@ def SPIRV_GLFMixOp :
 
 // -----
 
+def SPIRV_GLLengthOp : SPIRV_GLOp<"Length", 66, [
+    Pure,
+    TypesMatchWith<"result type must match operand element type",
+                  "operand", "result",
+                  "::mlir::getElementTypeOrSelf($_self)">
+  ]> {
+  let summary = "Return the length of a vector x";
+
+  let description = [{
+    Result is the length of vector x, i.e., sqrt(x[0]**2 + x[1]**2 + ...).
+
+    The operand x must be a scalar or vector whose component type is floating-point.
+
+    Result Type must be a scalar of the same type as the component type of x.
+
+    #### Example:
+
+    ```mlir
+    %2 = spirv.GL.Length %0 : vector<3xf32> -> f32
+    %3 = spirv.GL.Length %1 : f32 -> f32
+    ```
+  }];
+
+  let arguments = (ins
+    SPIRV_ScalarOrVectorOf<SPIRV_Float>:$operand
+  );
+
+  let results = (outs
+    SPIRV_Float:$result
+  );
+
+  let assemblyFormat = [{
+    $operand attr-dict `:` type($operand) `->` type($result)
+  }];
+
+  let hasVerifier = 0;
+}
+
+// -----
+
 def SPIRV_GLDistanceOp : SPIRV_GLOp<"Distance", 67, [
     Pure,
     AllTypesMatch<["p0", "p1"]>,
diff --git a/mlir/test/Dialect/SPIRV/IR/gl-ops.mlir b/mlir/test/Dialect/SPIRV/IR/gl-ops.mlir
index 2b75767feaf92..f49be21fcf783 100644
--- a/mlir/test/Dialect/SPIRV/IR/gl-ops.mlir
+++ b/mlir/test/Dialect/SPIRV/IR/gl-ops.mlir
@@ -983,3 +983,69 @@ func.func @unpack_half_2x16_scalar_out(%arg0 : i32) -> () {
   %0 = spirv.GL.UnpackHalf2x16 %arg0 : i32 -> f32
   return
 }
+
+// -----
+
+//===----------------------------------------------------------------------===//
+// spirv.GL.Length
+//===----------------------------------------------------------------------===//
+
+func.func @length(%arg0 : f32) -> () {
+  // CHECK: spirv.GL.Length {{%.*}} : f32 -> f32
+  %0 = spirv.GL.Length %arg0 : f32 -> f32
+  return
+}
+
+func.func @lengthvec(%arg0 : vector<3xf32>) -> () {
+  // CHECK: spirv.GL.Length {{%.*}} : vector<3xf32> -> f32
+  %0 = spirv.GL.Length %arg0 : vector<3xf32> -> f32
+  return
+}
+
+// -----
+
+func.func @length_i32_in(%arg0 : i32) -> () {
+  // expected-error @+1 {{op operand #0 must be 16/32/64-bit float or vector of 16/32/64-bit float values of length 2/3/4/8/16, but got 'i32'}}
+  %0 = spirv.GL.Length %arg0 : i32 -> f32
+  return
+}
+
+// -----
+
+func.func @length_f16_in(%arg0 : f16) -> () {
+  // expected-error @+1 {{op failed to verify that result type must match operand element type}}
+  %0 = spirv.GL.Length %arg0 : f16 -> f32
+  return
+}
+
+// -----
+
+func.func @length_i32vec_in(%arg0 : vector<3xi32>) -> () {
+  // expected-error @+1 {{op operand #0 must be 16/32/64-bit float or vector of 16/32/64-bit float values of length 2/3/4/8/16, but got 'vector<3xi32>'}}
+  %0 = spirv.GL.Length %arg0 : vector<3xi32> -> f32
+  return
+}
+
+// -----
+
+func.func @length_f16vec_in(%arg0 : vector<3xf16>) -> () {
+  // expected-error @+1 {{op failed to verify that result type must match operand element type}}
+  %0 = spirv.GL.Length %arg0 : vector<3xf16> -> f32
+  return
+}
+
+// -----
+
+func.func @length_i32_out(%arg0 : vector<3xf32>) -> () {
+  // expected-error @+1 {{op result #0 must be 16/32/64-bit float, but got 'i32'}}
+  %0 = spirv.GL.Length %arg0 : vector<3xf32> -> i32
+  return
+}
+
+// -----
+
+func.func @length_vec_out(%arg0 : vector<3xf32>) -> () {
+  // expected-error @+1 {{op result #0 must be 16/32/64-bit float, but got 'vector<3xf32>'}}
+  %0 = spirv.GL.Length %arg0 : vector<3xf32> -> vector<3xf32>
+  return
+}
diff --git a/mlir/test/Target/SPIRV/gl-ops.mlir b/mlir/test/Target/SPIRV/gl-ops.mlir
index eacf36bfba9ce..832f7ea2fe314 100644
--- a/mlir/test/Target/SPIRV/gl-ops.mlir
+++ b/mlir/test/Target/SPIRV/gl-ops.mlir
@@ -128,6 +128,10 @@ spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader], []> {
     %8 = spirv.GL.FindSMsb %arg3 : vector<3xi32>
     // CHECK: {{%.*}} = spirv.GL.FindUMsb {{%.*}} : vector<3xi32>
     %9 = spirv.GL.FindUMsb %arg3 : vector<3xi32>
+    // CHECK: {{%.*}} = spirv.GL.Length {{%.*}} : f32 -> f32
+    %10 = spirv.GL.Length %arg0 : f32 -> f32
+    // CHECK: {{%.*}} = spirv.GL.Length {{%.*}} : vector<3xf32> -> f32
+    %11 = spirv.GL.Length %arg1 : vector<3xf32> -> f32
     spirv.Return
   }
 

@IgWod-IMG
Copy link
Contributor Author

I have added the canonicalizer. I've considered whether I should put it in the GL canonicalizer, but since in here we transform GL op into GL op, the op availability should not be an issues.

@IgWod-IMG
Copy link
Contributor Author

Build failures are unrelated to this change. The problem has been already reported in #144179.

@kuhar
Copy link
Member

kuhar commented Jun 16, 2025

I have added the canonicalizer. I've considered whether I should put it in the GL canonicalizer, but since in here we transform GL op into GL op, the op availability should not be an issues.

I looked at this a little bit more closely and I think gl canonicalization is separated out mostly to better organize the code, similar how we have group op definitions in .td files (unlike most dialect that put all ops together). I don't see any logic that decides availability, unless I missed something. One caveat is that gl canon patterns don't run with the generic --canonicalize pass AFAICT and need to be added to the pass pipeline. spriv.GL.Length is probably not the most common op (do we plan to emit it anywhere?), so having the canonicalization be opt-in sounds fine to me.

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.

I'm going to approve but I think it'd make more sense for the canon pattern to be with other gl patterns.

@IgWod-IMG
Copy link
Contributor Author

The comment in the header file:

/// These patterns cannot be run in default canonicalization because GL ops
implies to me that GLSL canonicalization is separate because of the availability reasons, or at least that's my interpretation. In this case that wouldn't be an issue.

Having said that, I agree it does make sense to put it with other GL ops -- it is only useful if GL ops are available anyway and it will make the code cleaner. I'll update this PR.

@kuhar
Copy link
Member

kuhar commented Jun 16, 2025

implies to me that GLSL canonicalization is separate because of the availability reasons, or at least that's my interpretation. In this case that wouldn't be an issue.

Oh, I understand what you mean by availability now. Right, we wouldn't want to canonicalize spriv.select to spirv.GL.clamp when we compile for open cl spirv. I think it's fine either way then.

@kuhar
Copy link
Member

kuhar commented Jun 16, 2025

So these are the buckets of spirv canon patterns:

  1. generic to generic ==> canonicalize
  2. generic to gl ==> gl canonicalize
  3. generic to cl ==> cl canonicalize
  4. gl to gl ==> either canonicalize or gl canonicalize
  5. similar for cl to cl
  6. gl to generic ==> either canonicalize or gl canonicalize
  7. similar for cl to generic

@IgWod-IMG
Copy link
Contributor Author

I'm glad we're on the same page now :) I think it makes more sense to have all GL canonicalization in one place both from code organization perspective and from user interface -- I think it'd be a bit confusing if some GL canonicalization is run with default canonicalization and some with GL specific canonicalization.

  1. gl to gl ==> either canonicalize or gl canonicalize
  2. similar for cl to cl

I guess from now on we should be consistent and in both cases run those pattern as a part of gl/cl canonicalization.

@IgWod-IMG IgWod-IMG merged commit 22d9ea1 into llvm:main Jun 16, 2025
7 checks passed
@IgWod-IMG IgWod-IMG deleted the img_length branch June 16, 2025 16:41
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