diff --git a/mlir/lib/Dialect/Tosa/Transforms/TosaFolders.cpp b/mlir/lib/Dialect/Tosa/Transforms/TosaFolders.cpp index f338f5718712bf..fa11264cab7b1d 100644 --- a/mlir/lib/Dialect/Tosa/Transforms/TosaFolders.cpp +++ b/mlir/lib/Dialect/Tosa/Transforms/TosaFolders.cpp @@ -1090,6 +1090,174 @@ struct TosaFoldConstantErf } }; +struct TosaFoldConstantLog + : public TosaFoldConstantUnaryElementwise { + using TosaFoldConstantUnaryElementwise< + TosaFoldConstantLog, LogOp>::TosaFoldConstantUnaryElementwise; + + DenseElementsAttr computeFloat(DenseElementsAttr values, + PatternRewriter &rewriter, TosaOp op) const { + return applyElementWise( + values, + [](const APFloat &val, FloatType) { + auto res = APFloat(std::log(val.convertToFloat())); + bool lostPrecision; + res.convert(val.getSemantics(), APFloat::rmNearestTiesToEven, + &lostPrecision); + return res; + }, + cast(values.getElementType())); + } + + bool isSupportedElementType(Type type) const { + // convertToFloat uses F32, so we specify the supported types to make sure + // to properly handle F64 if needed in the future. + return type.isBF16() || type.isF16() || type.isF32(); + } +}; + +struct TosaFoldConstantBitwiseAnd + : public TosaFoldConstantBinary { + using TosaFoldConstantBinary::TosaFoldConstantBinary; + + DenseElementsAttr computeInteger(DenseElementsAttr lhsValues, + DenseElementsAttr rhsValues, + PatternRewriter &rewriter, + BitwiseAndOp op) const { + return applyElementWise( + lhsValues, rhsValues, op.getType(), + [](const APInt &lhs, const APInt &rhs) { return lhs & rhs; }); + } +}; + +struct TosaFoldConstantBitwiseOr + : public TosaFoldConstantBinary { + using TosaFoldConstantBinary::TosaFoldConstantBinary; + + DenseElementsAttr computeInteger(DenseElementsAttr lhsValues, + DenseElementsAttr rhsValues, + PatternRewriter &rewriter, + BitwiseOrOp op) const { + return applyElementWise( + lhsValues, rhsValues, op.getType(), + [](const APInt &lhs, const APInt &rhs) { return lhs | rhs; }); + } +}; + +struct TosaFoldConstantGreaterEqual + : public TosaFoldConstantBinary { + using TosaFoldConstantBinary::TosaFoldConstantBinary; + + DenseElementsAttr computeInteger(DenseElementsAttr lhsValues, + DenseElementsAttr rhsValues, + PatternRewriter &rewriter, + GreaterEqualOp op) const { + return applyElementWise( + lhsValues, rhsValues, op.getType(), + [](const APInt &first, const APInt &second) { + return APInt(1, first.sge(second)); + }); + } + + DenseElementsAttr computeFloat(DenseElementsAttr lhsValues, + DenseElementsAttr rhsValues, + PatternRewriter &rewriter, + GreaterEqualOp op) const { + return applyElementWise( + lhsValues, rhsValues, op.getType(), + [](const APFloat &first, const APFloat &second) { + return APInt(1, first >= second); + }); + } +}; + +struct TosaFoldConstantEqual + : public TosaFoldConstantBinary { + using TosaFoldConstantBinary::TosaFoldConstantBinary; + + DenseElementsAttr computeInteger(DenseElementsAttr lhsValues, + DenseElementsAttr rhsValues, + PatternRewriter &rewriter, + EqualOp op) const { + return applyElementWise( + lhsValues, rhsValues, op.getType(), + [](const APInt &first, const APInt &second) { + return APInt(1, first.eq(second)); + }); + } + + DenseElementsAttr computeFloat(DenseElementsAttr lhsValues, + DenseElementsAttr rhsValues, + PatternRewriter &rewriter, EqualOp op) const { + return applyElementWise( + lhsValues, rhsValues, op.getType(), + [](const APFloat &first, const APFloat &second) { + return APInt(1, first == second); + }); + } +}; + +struct TosaFoldConstantMinimum + : public TosaFoldConstantBinary { + using TosaFoldConstantBinary::TosaFoldConstantBinary; + + DenseElementsAttr computeInteger(DenseElementsAttr lhsValues, + DenseElementsAttr rhsValues, + PatternRewriter &rewriter, + MinimumOp op) const { + return applyElementWise( + lhsValues, rhsValues, op.getType(), + [](const APInt &first, const APInt &second) { + return first.slt(second) ? first : second; + }); + } + + DenseElementsAttr computeFloat(DenseElementsAttr lhsValues, + DenseElementsAttr rhsValues, + PatternRewriter &rewriter, + MinimumOp op) const { + return applyElementWise( + lhsValues, rhsValues, op.getType(), + [](const APFloat &first, const APFloat &second) { + return first < second ? first : second; + }); + } +}; + +struct TosaFoldConstantMaximum + : public TosaFoldConstantBinary { + using TosaFoldConstantBinary::TosaFoldConstantBinary; + + DenseElementsAttr computeInteger(DenseElementsAttr lhsValues, + DenseElementsAttr rhsValues, + PatternRewriter &rewriter, + MaximumOp op) const { + return applyElementWise( + lhsValues, rhsValues, op.getType(), + [](const APInt &first, const APInt &second) { + return first.sgt(second) ? first : second; + }); + } + + DenseElementsAttr computeFloat(DenseElementsAttr lhsValues, + DenseElementsAttr rhsValues, + PatternRewriter &rewriter, + MaximumOp op) const { + return applyElementWise( + lhsValues, rhsValues, op.getType(), + [](const APFloat &first, const APFloat &second) { + return first > second ? first : second; + }); + } +}; + } // namespace void mlir::tosa::populateTosaFoldConstantPatterns( @@ -1113,4 +1281,11 @@ void mlir::tosa::populateTosaFoldConstantPatterns( patterns.add(ctx, foldSplatOrSingleUseOnly); patterns.add(ctx, foldSplatOrSingleUseOnly); patterns.add(ctx, foldSplatOrSingleUseOnly); + patterns.add(ctx, foldSplatOrSingleUseOnly); + patterns.add(ctx, foldSplatOrSingleUseOnly); + patterns.add(ctx, foldSplatOrSingleUseOnly); + patterns.add(ctx, foldSplatOrSingleUseOnly); + patterns.add(ctx, foldSplatOrSingleUseOnly); + patterns.add(ctx, foldSplatOrSingleUseOnly); + patterns.add(ctx, foldSplatOrSingleUseOnly); } \ No newline at end of file diff --git a/mlir/test/Dialect/Tosa/constant-bitwise-and.mlir b/mlir/test/Dialect/Tosa/constant-bitwise-and.mlir new file mode 100644 index 00000000000000..89f1e2310ba640 --- /dev/null +++ b/mlir/test/Dialect/Tosa/constant-bitwise-and.mlir @@ -0,0 +1,74 @@ +// RUN: mlir-opt --split-input-file --tosa-layerwise-constant-fold %s | FileCheck %s + +// CHECK-LABEL: @bitwise_and_fold_single_valued +func.func @bitwise_and_fold_single_valued() -> tensor { + // CHECK: [[RES:]] ={{.*}}tosa.const{{.*}}-65536 + // CHECK-NOT: tosa.bitwise_and + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = dense<0xFFFFFFFF> : tensor} : () -> tensor + %1 = "tosa.const"() {value = dense<0xFFFF0000> : tensor} : () -> tensor + %2 = "tosa.bitwise_and"(%0, %1) : (tensor, tensor) -> tensor + return %2 : tensor +} + +// CHECK-LABEL: @bitwise_and_fold_splat +func.func @bitwise_and_fold_splat() -> tensor<12x7xi32> { + // CHECK: [[RES:]] ={{.*}}tosa.const{{.*}}65535 + // CHECK-NOT: tosa.bitwise_and + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = dense<0xFFFFFFFF> : tensor<12x7xi32>} : () -> tensor<12x7xi32> + %1 = "tosa.const"() {value = dense<0x0000FFFF> : tensor<12x7xi32>} : () -> tensor<12x7xi32> + %2 = "tosa.bitwise_and"(%0, %1) : (tensor<12x7xi32>, tensor<12x7xi32>) -> tensor<12x7xi32> + return %2 : tensor<12x7xi32> +} + +// CHECK-LABEL: @bitwise_and_no_fold +// The folding optimization works only intra-procedurally, so we won't be able +// to fold anything here +func.func @bitwise_and_no_fold(%arg0: tensor, %arg1: tensor) -> tensor { + // CHECK: tosa.bitwise_and + // CHECK-NEXT: return + %0 = "tosa.bitwise_and"(%arg0, %arg1) : (tensor, tensor) -> tensor + return %0 : tensor +} + +// CHECK-LABEL: @bitwise_and_fold +func.func @bitwise_and_fold() -> tensor<2x6xi32> { + // CHECK: [[RES:]] ={{.*}}tosa.const + // CHECK-SAME{LITERAL}: [[-1, -2, -3, -4, -5, -6], + // CHECK-SAME{LITERAL}: [1, 2, 3, 4, 5, 6]] + // CHECK-NOT: tosa.bitwise_and + // CHECK: return [[RES]] + %0 = "tosa.const"() { value = dense< + [[0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFD, + 0xFFFFFFFC, 0xFFFFFFFB, 0xFFFFFFFA], + [1, 2, 3, 4, 5, 6]]> + : tensor<2x6xi32> + } : () -> tensor<2x6xi32> + %1 = "tosa.const"() { value = dense< + [[0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF], + [0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF]]> + : tensor<2x6xi32> + } : () -> tensor<2x6xi32> + %2 = "tosa.bitwise_and"(%0, %1) : (tensor<2x6xi32>, tensor<2x6xi32>) -> tensor<2x6xi32> + return %2 : tensor<2x6xi32> +} + +// CHECK-LABEL: @bitwise_and_of_const_sparse +// Sparse tensors are currently not supported +func.func @bitwise_and_of_const_sparse() -> tensor<32xi8> { + // CHECK: tosa.const + // CHECK: tosa.bitwise_and + %0 = "tosa.const"() { value = sparse< + [[0], [3], [11], [17], [20], [23], [25], [30], [31]], + [0, 1, 2, 3, 4, 0xFF, 0xFE, 0xFD, 0xFC]> + : tensor<32xi8> } : () -> tensor<32xi8> + %1 = "tosa.const"() { value = sparse< + [[0], [3], [11], [17], [20], [23], [25], [30], [31]], + [0, 1, 2, 3, 4, 0xFF, 0xFE, 0xFD, 0xFC]> + : tensor<32xi8> } : () -> tensor<32xi8> + %2 = "tosa.bitwise_and"(%0, %1) : (tensor<32xi8>, tensor<32xi8>) -> tensor<32xi8> + return %2 : tensor<32xi8> +} diff --git a/mlir/test/Dialect/Tosa/constant-bitwise-or.mlir b/mlir/test/Dialect/Tosa/constant-bitwise-or.mlir new file mode 100644 index 00000000000000..147d0ecfa2557c --- /dev/null +++ b/mlir/test/Dialect/Tosa/constant-bitwise-or.mlir @@ -0,0 +1,73 @@ +// RUN: mlir-opt --split-input-file --tosa-layerwise-constant-fold %s | FileCheck %s + +// CHECK-LABEL: @bitwise_or_fold_single_valued +func.func @bitwise_or_fold_single_valued() -> tensor { + // CHECK: [[RES:]] ={{.*}}tosa.const{{.*}}-1 + // CHECK-NOT: tosa.bitwise_or + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = dense<0xFFFFFFFF> : tensor} : () -> tensor + %1 = "tosa.const"() {value = dense<0xFFFF0000> : tensor} : () -> tensor + %2 = "tosa.bitwise_or"(%0, %1) : (tensor, tensor) -> tensor + return %2 : tensor +} + +// CHECK-LABEL: @bitwise_or_fold_splat +func.func @bitwise_or_fold_splat() -> tensor<12x7xi32> { + // CHECK: [[RES:]] ={{.*}}tosa.const{{.*}}-1 + // CHECK-NOT: tosa.bitwise_or + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = dense<0xFFFFFFFF> : tensor<12x7xi32>} : () -> tensor<12x7xi32> + %1 = "tosa.const"() {value = dense<0x0000FFFF> : tensor<12x7xi32>} : () -> tensor<12x7xi32> + %2 = "tosa.bitwise_or"(%0, %1) : (tensor<12x7xi32>, tensor<12x7xi32>) -> tensor<12x7xi32> + return %2 : tensor<12x7xi32> +} + +// CHECK-LABEL: @bitwise_or_no_fold +// The folding optimization works only intra-procedurally, so we won't be able +// to fold anything here +func.func @bitwise_or_no_fold(%arg0: tensor, %arg1: tensor) -> tensor { + // CHECK: tosa.bitwise_or + // CHECK-NEXT: return + %0 = "tosa.bitwise_or"(%arg0, %arg1) : (tensor, tensor) -> tensor + return %0 : tensor +} + +// CHECK-LABEL: @bitwise_or_fold +func.func @bitwise_or_fold() -> tensor<2x6xi32> { + // CHECK: [[RES:]] ={{.*}}tosa.const + // CHECK-SAME{LITERAL}: [[-1, -1, -1, -1, -1, -1], + // CHECK-SAME{LITERAL}: [1, 3, 3, 5, 5, 7]] + // CHECK-NOT: tosa.bitwise_or + // CHECK: return [[RES]] + %0 = "tosa.const"() { value = dense< + [[0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFD, + 0xFFFFFFFC, 0xFFFFFFFB, 0xFFFFFFFA], + [1, 2, 3, 4, 5, 6]]> + : tensor<2x6xi32> + } : () -> tensor<2x6xi32> + %1 = "tosa.const"() { value = dense< + [[0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF], + [1, 1, 1, 1, 1, 1]]> + : tensor<2x6xi32> + } : () -> tensor<2x6xi32> + %2 = "tosa.bitwise_or"(%0, %1) : (tensor<2x6xi32>, tensor<2x6xi32>) -> tensor<2x6xi32> + return %2 : tensor<2x6xi32> +} + +// CHECK-LABEL: @bitwise_or_of_const_sparse +// Sparse tensors are currently not supported +func.func @bitwise_or_of_const_sparse() -> tensor<32xi8> { + // CHECK: tosa.const + // CHECK: tosa.bitwise_or + %0 = "tosa.const"() { value = sparse< + [[0], [3], [11], [17], [20], [23], [25], [30], [31]], + [0, 1, 2, 3, 4, 0xFF, 0xFE, 0xFD, 0xFC]> + : tensor<32xi8> } : () -> tensor<32xi8> + %1 = "tosa.const"() { value = sparse< + [[0], [3], [11], [17], [20], [23], [25], [30], [31]], + [0, 1, 2, 3, 4, 0xFF, 0xFE, 0xFD, 0xFC]> + : tensor<32xi8> } : () -> tensor<32xi8> + %2 = "tosa.bitwise_or"(%0, %1) : (tensor<32xi8>, tensor<32xi8>) -> tensor<32xi8> + return %2 : tensor<32xi8> +} diff --git a/mlir/test/Dialect/Tosa/constant-equal.mlir b/mlir/test/Dialect/Tosa/constant-equal.mlir new file mode 100644 index 00000000000000..71561dcda7aad7 --- /dev/null +++ b/mlir/test/Dialect/Tosa/constant-equal.mlir @@ -0,0 +1,97 @@ +// RUN: mlir-opt --split-input-file -verify-diagnostics --tosa-layerwise-constant-fold %s | FileCheck %s + +// Float comparisons + +// CHECK-LABEL: @equal_fold_float +func.func @equal_fold_float() -> tensor<4xi1> { + // CHECK: [[RES:]] = "tosa.const"() <{value = dense<[false, false, true, false]> + // CHECK-NOT: tosa.equal + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = + dense<[-17.4978, 4.9882, -0.0, -0.0]> : + tensor<4xf16> + } : () -> tensor<4xf16> + %1 = "tosa.const"() {value = + dense<[-132.7, -3.0, -0.0, 1.0]> : + tensor<4xf16> + } : () -> tensor<4xf16> + %2 = "tosa.equal"(%0, %1) : (tensor<4xf16>, tensor<4xf16>) -> tensor<4xi1> + return %2 : tensor<4xi1> +} + +// CHECK-LABEL: @equal_fold_float_infinity_nan +func.func @equal_fold_float_infinity_nan() -> tensor<6xi1> { + // CHECK: [[RES:]] = "tosa.const"() <{value = dense<[false, false, true, false, false, false]> + // CHECK-NOT: tosa.equal + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = + dense<[0x7F800000, 0xFF800000, 0x7F800000, 0xFF800000, 0x7FC00000, 0x7F800000]> : + tensor<6xf32> + } : () -> tensor<6xf32> + %1 = "tosa.const"() {value = + dense<[0xFF800000, 0x7F800000, 0x7F800000, 0x7F800000, 1.0, 0xFF800000]> : + tensor<6xf32> + } : () -> tensor<6xf32> + %2 = "tosa.equal"(%0, %1) : (tensor<6xf32>, tensor<6xf32>) -> tensor<6xi1> + return %2 : tensor<6xi1> +} + +// ----- +// Int comparison + +// CHECK-LABEL: @equal_fold_int +func.func @equal_fold_int() -> tensor<4xi1> { + // CHECK: [[RES:]] = "tosa.const"() <{value = dense<[true, false, true, false]> + // CHECK-NOT: tosa.equal + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = + dense<[-17, 4, 0, 0]> : + tensor<4xi32> + } : () -> tensor<4xi32> + %1 = "tosa.const"() {value = + dense<[-17, -3, 0, 5]> : + tensor<4xi32> + } : () -> tensor<4xi32> + %2 = "tosa.equal"(%0, %1) : (tensor<4xi32>, tensor<4xi32>) -> tensor<4xi1> + return %2 : tensor<4xi1> +} + +// ----- +// Broadcasted + +// CHECK-LABEL: @equal_fold_int_broadcast_simple +func.func @equal_fold_int_broadcast_simple() -> tensor<3xi1> { + // CHECK: [[RES:]] = "tosa.const"() <{value = dense<[true, false, false]> + // CHECK-NOT: tosa.equal + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = + dense<[-12, 4, 0]> : + tensor<3xi32> + } : () -> tensor<3xi32> + %1 = "tosa.const"() {value = + dense<-12> : + tensor<1xi32> + } : () -> tensor<1xi32> + %2 = "tosa.equal"(%0, %1) : (tensor<3xi32>, tensor<1xi32>) -> tensor<3xi1> + return %2 : tensor<3xi1> +} + +// CHECK-LABEL: @equal_fold_int_broadcast_complex +func.func @equal_fold_int_broadcast_complex() -> tensor<3x3xi1> { + // CHECK: [[RES:]] ={{.*}}tosa.const + // CHECK-SAME{LITERAL}: [[true, false, false] + // CHECK-SAME{LITERAL}: [false, true, false], + // CHECK-SAME{LITERAL}: [false, false, true]] + // CHECK-NOT: tosa.equal + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = + dense<[[-12], [1], [4]]> : + tensor<3x1xi32> + } : () -> tensor<3x1xi32> + %1 = "tosa.const"() {value = + dense<[[-12, 1, 4]]> : + tensor<1x3xi32> + } : () -> tensor<1x3xi32> + %2 = "tosa.equal"(%0, %1) : (tensor<3x1xi32>, tensor<1x3xi32>) -> tensor<3x3xi1> + return %2 : tensor<3x3xi1> +} diff --git a/mlir/test/Dialect/Tosa/constant-greater-equal.mlir b/mlir/test/Dialect/Tosa/constant-greater-equal.mlir new file mode 100644 index 00000000000000..114998bcd0579b --- /dev/null +++ b/mlir/test/Dialect/Tosa/constant-greater-equal.mlir @@ -0,0 +1,97 @@ +// RUN: mlir-opt --split-input-file -verify-diagnostics --tosa-layerwise-constant-fold %s | FileCheck %s + +// Float comparisons + +// CHECK-LABEL: @greater_equal_fold_float +func.func @greater_equal_fold_float() -> tensor<4xi1> { + // CHECK: [[RES:]] = "tosa.const"() <{value = dense<[true, true, false, false]> + // CHECK-NOT: tosa.greater_equal + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = + dense<[-17.4978, 4.9882, -1.0, -0.0]> : + tensor<4xf16> + } : () -> tensor<4xf16> + %1 = "tosa.const"() {value = + dense<[-132.7, -3.0, -0.0, 1.0]> : + tensor<4xf16> + } : () -> tensor<4xf16> + %2 = "tosa.greater_equal"(%0, %1) : (tensor<4xf16>, tensor<4xf16>) -> tensor<4xi1> + return %2 : tensor<4xi1> +} + +// CHECK-LABEL: @greater_equal_fold_float_infinity_nan +func.func @greater_equal_fold_float_infinity_nan() -> tensor<6xi1> { + // CHECK: [[RES:]] = "tosa.const"() <{value = dense<[true, false, true, false, false, true]> + // CHECK-NOT: tosa.greater_equal + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = + dense<[0x7F800000, 0xFF800000, 0x7F800000, 0xFF800000, 0x7FC00000, 0x7F800000]> : + tensor<6xf32> + } : () -> tensor<6xf32> + %1 = "tosa.const"() {value = + dense<[3.0, -3.0, -3.0, 3.0, 1.0, 0xFF800000]> : + tensor<6xf32> + } : () -> tensor<6xf32> + %2 = "tosa.greater_equal"(%0, %1) : (tensor<6xf32>, tensor<6xf32>) -> tensor<6xi1> + return %2 : tensor<6xi1> +} + +// ----- +// Int comparison + +// CHECK-LABEL: @greater_equal_fold_int +func.func @greater_equal_fold_int() -> tensor<4xi1> { + // CHECK: [[RES:]] = "tosa.const"() <{value = dense<[true, true, true, false]> + // CHECK-NOT: tosa.greater_equal + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = + dense<[-17, 4, 0, 0]> : + tensor<4xi32> + } : () -> tensor<4xi32> + %1 = "tosa.const"() {value = + dense<[-132, -3, 0, 5]> : + tensor<4xi32> + } : () -> tensor<4xi32> + %2 = "tosa.greater_equal"(%0, %1) : (tensor<4xi32>, tensor<4xi32>) -> tensor<4xi1> + return %2 : tensor<4xi1> +} + +// ----- +// Broadcasted + +// CHECK-LABEL: @greater_equal_fold_int_broadcast_simple +func.func @greater_equal_fold_int_broadcast_simple() -> tensor<3xi1> { + // CHECK: [[RES:]] = "tosa.const"() <{value = dense<[false, true, true]> + // CHECK-NOT: tosa.greater_equal + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = + dense<[-17, 4, 0]> : + tensor<3xi32> + } : () -> tensor<3xi32> + %1 = "tosa.const"() {value = + dense<-12> : + tensor<1xi32> + } : () -> tensor<1xi32> + %2 = "tosa.greater_equal"(%0, %1) : (tensor<3xi32>, tensor<1xi32>) -> tensor<3xi1> + return %2 : tensor<3xi1> +} + +// CHECK-LABEL: @greater_equal_fold_int_broadcast_complex +func.func @greater_equal_fold_int_broadcast_complex() -> tensor<3x3xi1> { + // CHECK: [[RES:]] ={{.*}}tosa.const + // CHECK-SAME{LITERAL}: [[false, false, false] + // CHECK-SAME{LITERAL}: [true, false, false], + // CHECK-SAME{LITERAL}: [true, true, true]] + // CHECK-NOT: tosa.greater_equal + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = + dense<[[-17], [1], [19]]> : + tensor<3x1xi32> + } : () -> tensor<3x1xi32> + %1 = "tosa.const"() {value = + dense<[[-12, 7, 4]]> : + tensor<1x3xi32> + } : () -> tensor<1x3xi32> + %2 = "tosa.greater_equal"(%0, %1) : (tensor<3x1xi32>, tensor<1x3xi32>) -> tensor<3x3xi1> + return %2 : tensor<3x3xi1> +} diff --git a/mlir/test/Dialect/Tosa/constant-log.mlir b/mlir/test/Dialect/Tosa/constant-log.mlir new file mode 100644 index 00000000000000..f4c9f838a1a9f2 --- /dev/null +++ b/mlir/test/Dialect/Tosa/constant-log.mlir @@ -0,0 +1,159 @@ +// RUN: mlir-opt --split-input-file --tosa-layerwise-constant-fold %s | FileCheck %s + +// CHECK-LABEL: @log_fold_single_valued +func.func @log_fold_single_valued() -> tensor { + // 0xFFC00000 is the value for NAN + // CHECK: [[RES:]] ={{.*}}tosa.const{{.*}}0xFFC00000{{.*}}tensor + // CHECK-NOT: tosa.log + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = dense<-1.0> : tensor} : () -> tensor + %1 = "tosa.log"(%0) : (tensor) -> tensor + return %1 : tensor +} + +// CHECK-LABEL: @log_int +func.func @log_int() -> tensor { + // CHECK: tosa.const{{.*}}12{{.*}}tensor + // CHECK: [[RES:]] ={{.*}}tosa.log + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = dense<12> : tensor} : () -> tensor + %1 = "tosa.log"(%0) : (tensor) -> tensor + return %1 : tensor +} + +// CHECK-LABEL: @log_fold_splat +func.func @log_fold_splat() -> tensor<12x7xf32> { + // CHECK: [[RES:]] ={{.*}}tosa.const{{.*}}-2.77258873 + // CHECK-NOT: tosa.log + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = dense<0.0625> : tensor<12x7xf32>} : () -> tensor<12x7xf32> + %1 = "tosa.log"(%0) : (tensor<12x7xf32>) -> tensor<12x7xf32> + return %1 : tensor<12x7xf32> +} + +// CHECK-LABEL: @log_fold_bf16 +func.func @log_fold_bf16() -> tensor<12x7xbf16> { + // CHECK: [[RES:]] ={{.*}}tosa.const{{.*}}-2.765630e+00 + // CHECK-NOT: tosa.log + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = dense<0.0625> : tensor<12x7xbf16>} : () -> tensor<12x7xbf16> + %1 = "tosa.log"(%0) : (tensor<12x7xbf16>) -> tensor<12x7xbf16> + return %1 : tensor<12x7xbf16> +} + +// CHECK-LABEL: @log_zero +func.func @log_zero() -> tensor { + // 0xFF800000 is the value for -Inf + // CHECK: [[RES:]] ={{.*}}tosa.const{{.*}}0xFF800000 + // CHECK-NOT: tosa.log + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = dense<0.0> : tensor} : () -> tensor + %1 = "tosa.log"(%0) : (tensor) -> tensor + return %1 : tensor +} + +// CHECK-LABEL: @log_neg_zero +func.func @log_neg_zero() -> tensor { + // 0xFF800000 is the value for -Inf + // CHECK: [[RES:]] ={{.*}}tosa.const{{.*}}0xFF800000 + // CHECK-NOT: tosa.log + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = dense<-0.0> : tensor} : () -> tensor + %1 = "tosa.log"(%0) : (tensor) -> tensor + return %1 : tensor +} + +// CHECK-LABEL: @log_nan +func.func @log_nan() -> tensor { + // 0x7FC00000 is the value for NAN + // CHECK: [[RES:]] ={{.*}}tosa.const{{.*}}0x7FC00000 + // CHECK-NOT: tosa.log + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = dense<0x7FC00000> : tensor} : () -> tensor + %1 = "tosa.log"(%0) : (tensor) -> tensor + return %1 : tensor +} + +// CHECK-LABEL: @log_infinity +func.func @log_infinity() -> tensor { + // CHECK: [[RES:]] ={{.*}}tosa.const{{.*}}0x7F800000 + // CHECK-NOT: tosa.log + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = dense<0x7F800000> : tensor} : () -> tensor + %1 = "tosa.log"(%0) : (tensor) -> tensor + return %1 : tensor +} + +// CHECK-LABEL: @log_neg_infinity +func.func @log_neg_infinity() -> tensor { + // 0xFFC00000 is the value for NAN + // CHECK: [[RES:]] ={{.*}}tosa.const{{.*}}0xFFC00000 + // CHECK-NOT: tosa.log + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = dense<0xFF800000> : tensor} : () -> tensor + %1 = "tosa.log"(%0) : (tensor) -> tensor + return %1 : tensor +} + +// CHECK-LABEL: @log_neg_value +func.func @log_neg_value() -> tensor { + // 0xFFC00000 is the value for NAN + // CHECK: [[RES:]] ={{.*}}tosa.const{{.*}}0xFFC00000 + // CHECK-NOT: tosa.log + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = dense<-4.0> : tensor} : () -> tensor + %1 = "tosa.log"(%0) : (tensor) -> tensor + return %1 : tensor +} + +// CHECK-LABEL: @log_no_fold +// The folding optimization works only intra-procedurally, so we won't be able +// to fold anything here +func.func @log_no_fold(%arg0: tensor) -> tensor { + // CHECK: tosa.log + // CHECK-NEXT: return + %0 = "tosa.log"(%arg0) : (tensor) -> tensor + return %0 : tensor +} + +// CHECK-LABEL: @log_fold_f16 +func.func @log_fold_f16() -> tensor<12x7xf16> { + // CHECK: [[RES:]] ={{.*}}tosa.const{{.*}}-2.773440e+00 + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = dense<6.250000e-02> : tensor<12x7xf16>} : () -> tensor<12x7xf16> + %1 = "tosa.log"(%0) : (tensor<12x7xf16>) -> tensor<12x7xf16> + return %1 : tensor<12x7xf16> +} + +// CHECK-LABEL: @log_fold +func.func @log_fold() -> tensor<4x6xf32> { + // CHECK: [[RES:]] ={{.*}}tosa.const + // CHECK-SAME{LITERAL}: [[-1.73840833, -2.43726015, -0.52357316, 0.38526243, 0xFFC00000, 1.07202876], + // CHECK-SAME{LITERAL}: [0xFFC00000, 0.359421164, 0.42415604, 0xFFC00000, 0xFFC00000, 0xFFC00000], + // CHECK-SAME{LITERAL}: [0xFFC00000, -2.57570696, -0.63922733, 0.121509403, -1.40813112, -0.364419162], + // CHECK-SAME{LITERAL}: [-0.707448959, 0xFFC00000, 0.592884779, -1.96825802, 0.438577473, 0xFFC00000]] + // CHECK-NOT: tosa.log + // CHECK: return [[RES]] + %0 = "tosa.const"() { value = dense<[ + [ 0.1758, 0.0874, 0.5924, 1.4700, -1.1424, 2.9213], + [-0.2078, 1.4325, 1.5283, -0.0121, -0.2306, -1.3377], + [-0.0804, 0.0761, 0.5277, 1.1292, 0.2446, 0.6946], + [ 0.4929, -0.6524, 1.8092, 0.1397, 1.5505, -1.0270]]> + : tensor<4x6xf32> + } : () -> tensor<4x6xf32> + %1 = "tosa.log"(%0) : (tensor<4x6xf32>) -> tensor<4x6xf32> + return %1 : tensor<4x6xf32> +} + +// CHECK-LABEL: @log_of_const_sparse +// Sparse tensors are currently not supported +func.func @log_of_const_sparse() -> tensor<32xbf16> { + // CHECK: tosa.const + // CHECK: tosa.log + %0 = "tosa.const"() { value = sparse< + [[0], [3], [11], [17], [20], [23], [25], [30], [31]], + [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]> + : tensor<32xbf16> } : () -> tensor<32xbf16> + %1 = "tosa.log"(%0) : (tensor<32xbf16>) -> tensor<32xbf16> + return %1 : tensor<32xbf16> +} diff --git a/mlir/test/Dialect/Tosa/constant-maximum.mlir b/mlir/test/Dialect/Tosa/constant-maximum.mlir new file mode 100644 index 00000000000000..bb149f7853dd3b --- /dev/null +++ b/mlir/test/Dialect/Tosa/constant-maximum.mlir @@ -0,0 +1,95 @@ +// RUN: mlir-opt --split-input-file -verify-diagnostics --tosa-layerwise-constant-fold %s | FileCheck %s + +// CHECK-LABEL: @maximum_fold_float +func.func @maximum_fold_float() -> tensor<4xf16> { + // CHECK: [[RES:]] = "tosa.const"() <{value = dense<[-1.750000e+01, 4.988280e+00, -0.000000e+00, 1.000000e+00]> + // CHECK-NOT: tosa.maximum + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = + dense<[-17.4978, 4.9882, -1.0, -0.0]> : + tensor<4xf16> + } : () -> tensor<4xf16> + %1 = "tosa.const"() {value = + dense<[-132.7, -3.0, -0.0, 1.0]> : + tensor<4xf16> + } : () -> tensor<4xf16> + %2 = "tosa.maximum"(%0, %1) : (tensor<4xf16>, tensor<4xf16>) -> tensor<4xf16> + return %2 : tensor<4xf16> +} + +// CHECK-LABEL: @maximum_fold_float_infinity_nan +func.func @maximum_fold_float_infinity_nan() -> tensor<6xf32> { + // 0x7F800000 is the value for Inf + // 0xFF800000 is the value for -Inf + // 0x7FC00000 is the value for NAN + // CHECK: [[RES:]] = "tosa.const"() <{value = dense<[0x7F800000, -3.000000e+00, 0x7F800000, 3.000000e+00, 1.000000e+00, 0x7F800000]> + // CHECK-NOT: tosa.maximum + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = + dense<[0x7F800000, -3.000000e+00, 0x7F800000, 3.000000e+00, 1.000000e+00, 0x7F800000]> : + tensor<6xf32> + } : () -> tensor<6xf32> + %1 = "tosa.const"() {value = + dense<[3.0, -3.0, -3.0, 3.0, 1.0, 0xFF800000]> : + tensor<6xf32> + } : () -> tensor<6xf32> + %2 = "tosa.maximum"(%0, %1) : (tensor<6xf32>, tensor<6xf32>) -> tensor<6xf32> + return %2 : tensor<6xf32> +} + +// ----- + +// CHECK-LABEL: @maximum_fold_int +func.func @maximum_fold_int() -> tensor<4xi32> { + // CHECK: [[RES:]] = "tosa.const"() <{value = dense<[-17, 4, 0, 5]> + // CHECK-NOT: tosa.maximum + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = + dense<[-17, 4, 0, 0]> : + tensor<4xi32> + } : () -> tensor<4xi32> + %1 = "tosa.const"() {value = + dense<[-132, -3, 0, 5]> : + tensor<4xi32> + } : () -> tensor<4xi32> + %2 = "tosa.maximum"(%0, %1) : (tensor<4xi32>, tensor<4xi32>) -> tensor<4xi32> + return %2 : tensor<4xi32> +} + +// ----- +// Broadcasted + +// CHECK-LABEL: @maximum_fold_int_broadcast_simple +func.func @maximum_fold_int_broadcast_simple() -> tensor<3xi32> { + // CHECK: [[RES:]] = "tosa.const"() <{value = dense<[-12, 4, 0]> + // CHECK-NOT: tosa.maximum + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = + dense<[-12, 4, 0]> : + tensor<3xi32> + } : () -> tensor<3xi32> + %1 = "tosa.const"() {value = + dense<-12> : + tensor<1xi32> + } : () -> tensor<1xi32> + %2 = "tosa.maximum"(%0, %1) : (tensor<3xi32>, tensor<1xi32>) -> tensor<3xi32> + return %2 : tensor<3xi32> +} + +// CHECK-LABEL: @maximum_fold_int_broadcast_complex +func.func @maximum_fold_int_broadcast_complex() -> tensor<3x3xi32> { + // CHECK: [[RES:]] ={{.*}}tosa.const + // CHECK-SAME{LITERAL}: [[-12, 7, 4], [1, 7, 4], [19, 19, 19]] + // CHECK-NOT: tosa.maximum + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = + dense<[[-17], [1], [19]]> : + tensor<3x1xi32> + } : () -> tensor<3x1xi32> + %1 = "tosa.const"() {value = + dense<[[-12, 7, 4]]> : + tensor<1x3xi32> + } : () -> tensor<1x3xi32> + %2 = "tosa.maximum"(%0, %1) : (tensor<3x1xi32>, tensor<1x3xi32>) -> tensor<3x3xi32> + return %2 : tensor<3x3xi32> +} diff --git a/mlir/test/Dialect/Tosa/constant-minimum.mlir b/mlir/test/Dialect/Tosa/constant-minimum.mlir new file mode 100644 index 00000000000000..56613be6e53846 --- /dev/null +++ b/mlir/test/Dialect/Tosa/constant-minimum.mlir @@ -0,0 +1,95 @@ +// RUN: mlir-opt --split-input-file -verify-diagnostics --tosa-layerwise-constant-fold %s | FileCheck %s + +// CHECK-LABEL: @minimum_fold_float +func.func @minimum_fold_float() -> tensor<4xf16> { + // CHECK: [[RES:]] = "tosa.const"() <{value = dense<[-1.327500e+02, -3.000000e+00, -1.000000e+00, -0.000000e+00]> + // CHECK-NOT: tosa.minimum + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = + dense<[-17.4978, 4.9882, -1.0, -0.0]> : + tensor<4xf16> + } : () -> tensor<4xf16> + %1 = "tosa.const"() {value = + dense<[-132.7, -3.0, -0.0, 1.0]> : + tensor<4xf16> + } : () -> tensor<4xf16> + %2 = "tosa.minimum"(%0, %1) : (tensor<4xf16>, tensor<4xf16>) -> tensor<4xf16> + return %2 : tensor<4xf16> +} + +// CHECK-LABEL: @minimum_fold_float_infinity_nan +func.func @minimum_fold_float_infinity_nan() -> tensor<6xf32> { + // 0x7F800000 is the value for Inf + // 0xFF800000 is the value for -Inf + // 0x7FC00000 is the value for NAN + // CHECK: [[RES:]] = "tosa.const"() <{value = dense<[3.000000e+00, 0xFF800000, -3.000000e+00, 0xFF800000, 1.000000e+00, 0xFF800000]> + // CHECK-NOT: tosa.minimum + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = + dense<[0x7F800000, 0xFF800000, 0x7F800000, 0xFF800000, 0x7FC00000, 0x7F800000]> : + tensor<6xf32> + } : () -> tensor<6xf32> + %1 = "tosa.const"() {value = + dense<[3.0, -3.0, -3.0, 3.0, 1.0, 0xFF800000]> : + tensor<6xf32> + } : () -> tensor<6xf32> + %2 = "tosa.minimum"(%0, %1) : (tensor<6xf32>, tensor<6xf32>) -> tensor<6xf32> + return %2 : tensor<6xf32> +} + +// ----- + +// CHECK-LABEL: @minimum_fold_int +func.func @minimum_fold_int() -> tensor<4xi32> { + // CHECK: [[RES:]] = "tosa.const"() <{value = dense<[-132, -3, 0, 0]> + // CHECK-NOT: tosa.minimum + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = + dense<[-17, 4, 0, 0]> : + tensor<4xi32> + } : () -> tensor<4xi32> + %1 = "tosa.const"() {value = + dense<[-132, -3, 0, 5]> : + tensor<4xi32> + } : () -> tensor<4xi32> + %2 = "tosa.minimum"(%0, %1) : (tensor<4xi32>, tensor<4xi32>) -> tensor<4xi32> + return %2 : tensor<4xi32> +} + +// ----- +// Broadcasted + +// CHECK-LABEL: @minimum_fold_int_broadcast_simple +func.func @minimum_fold_int_broadcast_simple() -> tensor<3xi32> { + // CHECK: [[RES:]] = "tosa.const"() <{value = dense<[-17, -12, -12]> + // CHECK-NOT: tosa.minimum + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = + dense<[-17, 4, 0]> : + tensor<3xi32> + } : () -> tensor<3xi32> + %1 = "tosa.const"() {value = + dense<-12> : + tensor<1xi32> + } : () -> tensor<1xi32> + %2 = "tosa.minimum"(%0, %1) : (tensor<3xi32>, tensor<1xi32>) -> tensor<3xi32> + return %2 : tensor<3xi32> +} + +// CHECK-LABEL: @minimum_fold_int_broadcast_complex +func.func @minimum_fold_int_broadcast_complex() -> tensor<3x3xi32> { + // CHECK: [[RES:]] ={{.*}}tosa.const + // CHECK-SAME{LITERAL}: [[-17, -17, -17], [-12, 1, 1], [-12, 7, 4]] + // CHECK-NOT: tosa.minimum + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = + dense<[[-17], [1], [19]]> : + tensor<3x1xi32> + } : () -> tensor<3x1xi32> + %1 = "tosa.const"() {value = + dense<[[-12, 7, 4]]> : + tensor<1x3xi32> + } : () -> tensor<1x3xi32> + %2 = "tosa.minimum"(%0, %1) : (tensor<3x1xi32>, tensor<1x3xi32>) -> tensor<3x3xi32> + return %2 : tensor<3x3xi32> +}