[Mlir-commits] [mlir] [mlir][SPIR-V] Add GL NMin, NMax and NClamp ops (PR #203878)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Mon Jun 15 05:06:47 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir
Author: Arseniy Obolenskiy (aobolensk)
<details>
<summary>Changes</summary>
---
Full diff: https://github.com/llvm/llvm-project/pull/203878.diff
4 Files Affected:
- (modified) mlir/include/mlir/Dialect/SPIRV/IR/SPIRVGLOps.td (+77)
- (modified) mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp (+10)
- (modified) mlir/test/Dialect/SPIRV/IR/gl-ops.mlir (+56)
- (modified) mlir/test/Target/SPIRV/gl-ops.mlir (+11)
``````````diff
diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVGLOps.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVGLOps.td
index 83a3ff5c3adb0..6d95427aa3d5e 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVGLOps.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVGLOps.td
@@ -1033,6 +1033,83 @@ def SPIRV_GLSClampOp : SPIRV_GLTernaryArithmeticOp<"SClamp", 45, SPIRV_Integer>
// -----
+def SPIRV_GLNMaxOp : SPIRV_GLBinaryArithmeticOp<"NMax", 80, SPIRV_Float, [AlwaysSpeculatable]> {
+ let summary = "Return maximum of two floating-point operands, NaN-aware";
+
+ let description = [{
+ Result is y if x < y; otherwise result is x, where x and y are interpreted
+ as floating-point. NMax considers a NaN argument as missing, so if one
+ operand is a NaN the result is the other operand. If both operands are NaNs,
+ the result is a NaN.
+
+ The operands must all be a scalar or vector whose component type is
+ floating-point.
+
+ Result Type and the type of all operands must be the same type. Results are
+ computed per component.
+
+ #### Example:
+
+ ```mlir
+ %2 = spirv.GL.NMax %0, %1 : f32
+ %3 = spirv.GL.NMax %0, %1 : vector<3xf16>
+ ```
+ }];
+}
+
+// -----
+
+def SPIRV_GLNMinOp : SPIRV_GLBinaryArithmeticOp<"NMin", 79, SPIRV_Float, [AlwaysSpeculatable]> {
+ let summary = "Return minimum of two floating-point operands, NaN-aware";
+
+ let description = [{
+ Result is y if y < x; otherwise result is x, where x and y are interpreted
+ as floating-point. NMin considers a NaN argument as missing, so if one
+ operand is a NaN the result is the other operand. If both operands are NaNs,
+ the result is a NaN.
+
+ The operands must all be a scalar or vector whose component type is
+ floating-point.
+
+ Result Type and the type of all operands must be the same type. Results are
+ computed per component.
+
+ #### Example:
+
+ ```mlir
+ %2 = spirv.GL.NMin %0, %1 : f32
+ %3 = spirv.GL.NMin %0, %1 : vector<3xf16>
+ ```
+ }];
+}
+
+// -----
+
+def SPIRV_GLNClampOp : SPIRV_GLTernaryArithmeticOp<"NClamp", 81, SPIRV_Float> {
+ let summary = "Clamp x between min and max values, NaN-aware";
+
+ let description = [{
+ Result is min(max(x, minVal), maxVal). The resulting value is poison if
+ minVal > maxVal. The semantics used by min() and max() are those of NMin
+ and NMax, so NaN arguments are considered missing.
+
+ The operands must all be a scalar or vector whose component type is
+ floating-point.
+
+ Result Type and the type of all operands must be the same type. Results are
+ computed per component.
+
+ #### Example:
+
+ ```mlir
+ %2 = spirv.GL.NClamp %x, %min, %max : f32
+ %3 = spirv.GL.NClamp %x, %min, %max : vector<3xf16>
+ ```
+ }];
+}
+
+// -----
+
def SPIRV_GLFmaOp : SPIRV_GLTernaryArithmeticOp<"Fma", 50, SPIRV_Float, [AlwaysSpeculatable]> {
let summary = "Computes a * b + c.";
diff --git a/mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp b/mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp
index 119cdcf0fe4fa..ba6ac602c2453 100644
--- a/mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp
+++ b/mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp
@@ -1244,6 +1244,16 @@ ParseResult spirv::GLSClampOp::parse(OpAsmParser &parser,
}
void spirv::GLSClampOp::print(OpAsmPrinter &p) { printOneResultOp(*this, p); }
+//===----------------------------------------------------------------------===//
+// spirv.GLNClampOp
+//===----------------------------------------------------------------------===//
+
+ParseResult spirv::GLNClampOp::parse(OpAsmParser &parser,
+ OperationState &result) {
+ return parseOneResultSameOperandTypeOp(parser, result);
+}
+void spirv::GLNClampOp::print(OpAsmPrinter &p) { printOneResultOp(*this, p); }
+
//===----------------------------------------------------------------------===//
// spirv.GLFmaOp
//===----------------------------------------------------------------------===//
diff --git a/mlir/test/Dialect/SPIRV/IR/gl-ops.mlir b/mlir/test/Dialect/SPIRV/IR/gl-ops.mlir
index 88593d54a6783..da29f71d660d7 100644
--- a/mlir/test/Dialect/SPIRV/IR/gl-ops.mlir
+++ b/mlir/test/Dialect/SPIRV/IR/gl-ops.mlir
@@ -1214,3 +1214,59 @@ func.func @length_vec_out(%arg0 : vector<3xf32>) -> () {
%0 = spirv.GL.Length %arg0 : vector<3xf32> -> vector<3xf32>
return
}
+
+// -----
+
+//===----------------------------------------------------------------------===//
+// spirv.GL.NMax
+//===----------------------------------------------------------------------===//
+
+func.func @nmax(%arg0 : f32, %arg1 : f32) {
+ // CHECK: spirv.GL.NMax {{%.*}}, {{%.*}} : f32
+ %0 = spirv.GL.NMax %arg0, %arg1 : f32
+ return
+}
+
+func.func @nmaxvec(%arg0 : vector<3xf16>, %arg1 : vector<3xf16>) {
+ // CHECK: spirv.GL.NMax {{%.*}}, {{%.*}} : vector<3xf16>
+ %0 = spirv.GL.NMax %arg0, %arg1 : vector<3xf16>
+ return
+}
+
+// -----
+
+//===----------------------------------------------------------------------===//
+// spirv.GL.NMin
+//===----------------------------------------------------------------------===//
+
+func.func @nmin(%arg0 : f32, %arg1 : f32) {
+ // CHECK: spirv.GL.NMin {{%.*}}, {{%.*}} : f32
+ %0 = spirv.GL.NMin %arg0, %arg1 : f32
+ return
+}
+
+func.func @nminvec(%arg0 : vector<3xf16>, %arg1 : vector<3xf16>) {
+ // CHECK: spirv.GL.NMin {{%.*}}, {{%.*}} : vector<3xf16>
+ %0 = spirv.GL.NMin %arg0, %arg1 : vector<3xf16>
+ return
+}
+
+// -----
+
+//===----------------------------------------------------------------------===//
+// spirv.GL.NClamp
+//===----------------------------------------------------------------------===//
+
+func.func @nclamp(%arg0 : f32, %min : f32, %max : f32) -> () {
+ // CHECK: spirv.GL.NClamp {{%[^,]*}}, {{%[^,]*}}, {{%[^,]*}} : f32
+ %2 = spirv.GL.NClamp %arg0, %min, %max : f32
+ return
+}
+
+// -----
+
+func.func @nclamp(%arg0 : vector<3xf32>, %min : vector<3xf32>, %max : vector<3xf32>) -> () {
+ // CHECK: spirv.GL.NClamp {{%[^,]*}}, {{%[^,]*}}, {{%[^,]*}} : vector<3xf32>
+ %2 = spirv.GL.NClamp %arg0, %min, %max : vector<3xf32>
+ return
+}
diff --git a/mlir/test/Target/SPIRV/gl-ops.mlir b/mlir/test/Target/SPIRV/gl-ops.mlir
index f4dc4051818bf..cdd33f04065c1 100644
--- a/mlir/test/Target/SPIRV/gl-ops.mlir
+++ b/mlir/test/Target/SPIRV/gl-ops.mlir
@@ -72,6 +72,11 @@ spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader, Linkage], []> {
%5 = spirv.GL.SMin %arg2, %arg3 : i32
// CHECK: {{%.*}} = spirv.GL.UMin {{%.*}}, {{%.*}} : i32
%6 = spirv.GL.UMin %arg2, %arg3 : i32
+
+ // CHECK: {{%.*}} = spirv.GL.NMax {{%.*}}, {{%.*}} : f32
+ %7 = spirv.GL.NMax %arg0, %arg1 : f32
+ // CHECK: {{%.*}} = spirv.GL.NMin {{%.*}}, {{%.*}} : f32
+ %8 = spirv.GL.NMin %arg0, %arg1 : f32
spirv.Return
}
@@ -81,6 +86,12 @@ spirv.module Logical GLSL450 requires #spirv.vce<v1.0, [Shader, Linkage], []> {
spirv.Return
}
+ spirv.func @nclamp(%arg0 : f32, %arg1 : f32, %arg2 : f32) "None" {
+ // CHECK: spirv.GL.NClamp {{%[^,]*}}, {{%[^,]*}}, {{%[^,]*}} : f32
+ %13 = spirv.GL.NClamp %arg0, %arg1, %arg2 : f32
+ spirv.Return
+ }
+
spirv.func @uclamp(%arg0 : ui32, %arg1 : ui32, %arg2 : ui32) "None" {
// CHECK: spirv.GL.UClamp {{%[^,]*}}, {{%[^,]*}}, {{%[^,]*}} : i32
%13 = spirv.GL.UClamp %arg0, %arg1, %arg2 : ui32
``````````
</details>
https://github.com/llvm/llvm-project/pull/203878
More information about the Mlir-commits
mailing list