diff --git a/mlir/include/mlir/Dialect/StandardOps/IR/Ops.td b/mlir/include/mlir/Dialect/StandardOps/IR/Ops.td --- a/mlir/include/mlir/Dialect/StandardOps/IR/Ops.td +++ b/mlir/include/mlir/Dialect/StandardOps/IR/Ops.td @@ -1761,6 +1761,43 @@ let summary = "base-2 logarithm of the specified value"; } +//===----------------------------------------------------------------------===// +// MaxFOp +//===----------------------------------------------------------------------===// + +def MaxFOp : FloatArithmeticOp<"maxf"> { + let summary = "floating point maximum operation"; + let description = [{ + Syntax: + + ``` + operation ::= ssa-id `=` `std.maxf` ssa-use `,` ssa-use `:` type + ``` + + The `maxf` operation takes two operands and returns one result, each of + these is required to be the same type. This type may be a floating point + scalar type, a vector whose element type is a floating point type, or a + floating point tensor. + + Example: + + ```mlir + // Scalar maximum. + %a = maxf %b, %c : f64 + + // SIMD pointwise vector maximum, e.g. for Intel SSE. + %f = maxf %g, %h : vector<4xf32> + + // Tensor pointwise maximum. + %x = maxf %y, %z : tensor<4x?xbf16> + ``` + + TODO: In the distant future, this will accept optional attributes for fast + math, contraction, rounding mode, and other controls. + }]; + let hasFolder = 1; +} + //===----------------------------------------------------------------------===// // MemRefCastOp //===----------------------------------------------------------------------===// @@ -1835,6 +1872,43 @@ }]; } +//===----------------------------------------------------------------------===// +// MinFOp +//===----------------------------------------------------------------------===// + +def MinFOp : FloatArithmeticOp<"minf"> { + let summary = "floating point minimum operation"; + let description = [{ + Syntax: + + ``` + operation ::= ssa-id `=` `std.minf` ssa-use `,` ssa-use `:` type + ``` + + The `minf` operation takes two operands and returns one result, each of + these is required to be the same type. This type may be a floating point + scalar type, a vector whose element type is a floating point type, or a + floating point tensor. + + Example: + + ```mlir + // Scalar minimum. + %a = minf %b, %c : f64 + + // SIMD pointwise vector minimum, e.g. for Intel SSE. + %f = minf %g, %h : vector<4xf32> + + // Tensor pointwise minimum. + %x = minf %y, %z : tensor<4x?xbf16> + ``` + + TODO: In the distant future, this will accept optional attributes for fast + math, contraction, rounding mode, and other controls. + }]; + let hasFolder = 1; +} + //===----------------------------------------------------------------------===// // MulFOp //===----------------------------------------------------------------------===// diff --git a/mlir/lib/Dialect/StandardOps/IR/Ops.cpp b/mlir/lib/Dialect/StandardOps/IR/Ops.cpp --- a/mlir/lib/Dialect/StandardOps/IR/Ops.cpp +++ b/mlir/lib/Dialect/StandardOps/IR/Ops.cpp @@ -1818,6 +1818,15 @@ return OpFoldResult(); } +//===----------------------------------------------------------------------===// +// MaxFOp +//===----------------------------------------------------------------------===// + +OpFoldResult MaxFOp::fold(ArrayRef operands) { + return constFoldBinaryOp( + operands, [](APFloat a, APFloat b) { return std::max(a, b); }); +} + //===----------------------------------------------------------------------===// // MemRefCastOp //===----------------------------------------------------------------------===// @@ -1896,6 +1905,15 @@ return impl::foldCastOp(*this); } +//===----------------------------------------------------------------------===// +// MinFOp +//===----------------------------------------------------------------------===// + +OpFoldResult MinFOp::fold(ArrayRef operands) { + return constFoldBinaryOp( + operands, [](APFloat a, APFloat b) { return std::min(a, b); }); +} + //===----------------------------------------------------------------------===// // MulFOp //===----------------------------------------------------------------------===// diff --git a/mlir/test/IR/core-ops.mlir b/mlir/test/IR/core-ops.mlir --- a/mlir/test/IR/core-ops.mlir +++ b/mlir/test/IR/core-ops.mlir @@ -554,6 +554,12 @@ // CHECK: = fptosi {{.*}} : f16 to i64 %162 = fptosi %half : f16 to i64 + // CHECK: %[[F6:.*]] = minf %[[F2]], %[[F2]] : f32 + %163 = minf %f2, %f2 : f32 + + // CHECK: %[[F6:.*]] = maxf %[[F2]], %[[F2]] : f32 + %164 = maxf %f2, %f2 : f32 + return } diff --git a/mlir/test/Transforms/constant-fold.mlir b/mlir/test/Transforms/constant-fold.mlir --- a/mlir/test/Transforms/constant-fold.mlir +++ b/mlir/test/Transforms/constant-fold.mlir @@ -273,6 +273,62 @@ // ----- +// CHECK-LABEL: func @simple_maxf +func @simple_maxf() -> f32 { + %0 = constant 4.5 : f32 + %1 = constant 1.5 : f32 + + // CHECK-NEXT: [[C:%.+]] = constant 4.5{{0*}}e+00 : f32 + %2 = maxf %0, %1 : f32 + + // CHECK-NEXT: return [[C]] + return %2 : f32 +} + +// ----- + +// CHECK-LABEL: func @maxf_splat_tensor +func @maxf_splat_tensor() -> tensor<4xf32> { + %0 = constant dense<4.5> : tensor<4xf32> + %1 = constant dense<1.5> : tensor<4xf32> + + // CHECK-NEXT: [[C:%.+]] = constant dense<4.5{{0*}}e+00> : tensor<4xf32> + %2 = maxf %0, %1 : tensor<4xf32> + + // CHECK-NEXT: return [[C]] + return %2 : tensor<4xf32> +} + +// ----- + +// CHECK-LABEL: func @simple_minf +func @simple_minf() -> f32 { + %0 = constant 4.5 : f32 + %1 = constant 1.5 : f32 + + // CHECK-NEXT: [[C:%.+]] = constant 1.5{{0*}}e+00 : f32 + %2 = minf %0, %1 : f32 + + // CHECK-NEXT: return [[C]] + return %2 : f32 +} + +// ----- + +// CHECK-LABEL: func @minf_splat_tensor +func @minf_splat_tensor() -> tensor<4xf32> { + %0 = constant dense<4.5> : tensor<4xf32> + %1 = constant dense<1.5> : tensor<4xf32> + + // CHECK-NEXT: [[C:%.+]] = constant dense<1.5{{0*}}e+00> : tensor<4xf32> + %2 = minf %0, %1 : tensor<4xf32> + + // CHECK-NEXT: return [[C]] + return %2 : tensor<4xf32> +} + +// ----- + // CHECK-LABEL: func @simple_mulf func @simple_mulf() -> f32 { %0 = constant 4.5 : f32