diff --git a/mlir/include/mlir/IR/OperationSupport.h b/mlir/include/mlir/IR/OperationSupport.h index f2bae58c80b3a2..7e43418f6f156e 100644 --- a/mlir/include/mlir/IR/OperationSupport.h +++ b/mlir/include/mlir/IR/OperationSupport.h @@ -1096,6 +1096,10 @@ class OpPrintingFlags { /// elements. OpPrintingFlags &elideLargeElementsAttrs(int64_t largeElementLimit = 16); + /// Enables breaking attributes on individual lines when there are more than + /// the given number of attributes on an operation. + OpPrintingFlags& newlineAfterAttribute(int64_t attributeLimit = 2); + /// Enable or disable printing of debug information (based on `enable`). If /// 'prettyForm' is set to true, debug information is printed in a more /// readable 'pretty' form. Note: The IR generated with 'prettyForm' is not @@ -1126,6 +1130,9 @@ class OpPrintingFlags { /// Return the size limit for printing large ElementsAttr. std::optional getLargeElementsAttrLimit() const; + /// Return the size limit for printing newlines after attributes. + std::optional getNewlineAfterAttrLimit() const; + /// Return if debug information should be printed. bool shouldPrintDebugInfo() const; @@ -1152,6 +1159,10 @@ class OpPrintingFlags { /// the upper limit. std::optional elementsAttrElementLimit; + /// Print newlines after each attribute when an operation has more than + /// the given number of attributes. + std::optional newlineAfterAttr; + /// Print debug information. bool printDebugInfoFlag : 1; bool printDebugInfoPrettyFormFlag : 1; diff --git a/mlir/lib/Dialect/Tosa/Transforms/TosaFolders.cpp b/mlir/lib/Dialect/Tosa/Transforms/TosaFolders.cpp index f338f5718712bf..1b9ab22706d297 100644 --- a/mlir/lib/Dialect/Tosa/Transforms/TosaFolders.cpp +++ b/mlir/lib/Dialect/Tosa/Transforms/TosaFolders.cpp @@ -1026,6 +1026,8 @@ struct TosaFoldConstantGreater : public TosaFoldConstantBinary( lhsValues, rhsValues, op.getType(), [](const APFloat &first, const APFloat &second) { + if (first.isNaN() || second.isNaN()) + return APInt(1, false); return APInt(1, first > second); }); } @@ -1090,6 +1092,277 @@ 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) { + if (first.isNaN() || second.isNaN()) + return APInt(1, false); + 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) { + if (first.isNaN() || second.isNaN()) + return first.isNaN() ? first : 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) { + if (first.isNaN() || second.isNaN()) + return first.isNaN() ? first : second; + return first > second ? first : second; + }); + } +}; + +template +DenseElementsAttr padType(ShapedType inputType, ElementsAttr inputValues, + DenseElementsAttr paddings, + std::optional padConstValue, + ShapedType outputType, BaseType zero) { + BaseType padConst(zero); + if (padConstValue.has_value()) + padConst = padConstValue.value().getSplatValue(); + + auto values = inputValues.getValues(); + auto paddingVals = paddings.getValues(); + + auto outputShape = outputType.getShape(); + auto inputShape = inputType.getShape(); + + // Implements the logic from + // https://www.mlplatform.org/tosa/tosa_spec.html#_pad + SmallVector outputValues(outputType.getNumElements(), padConst); + for (size_t outIndex = 0, e = outputValues.size(); outIndex < e; ++outIndex) { + auto indexInTarget = offsetToIndex(outputShape, outIndex); + + bool isPad = + llvm::any_of(llvm::enumerate(indexInTarget), [&](const auto &dimInfo) { + auto index = dimInfo.index(); + auto i = dimInfo.value() - paddingVals[index * 2]; + return static_cast(i < 0 || i >= inputShape[index]); + }); + + auto inputIndexOffset = indexToOffset(outputShape, indexInTarget); + outputValues[outIndex] = isPad ? padConst : values[inputIndexOffset]; + } + return DenseElementsAttr::get(outputType, + llvm::ArrayRef(outputValues)); +} + +DenseElementsAttr pad(ShapedType inputType, ElementsAttr inputValues, + DenseElementsAttr paddings, + std::optional padConstValue, + ShapedType outputType) { + + auto baseType = inputType.getElementType(); + + // Handle integer types with APInt + if (auto intType = dyn_cast(baseType)) + return padType(inputType, inputValues, paddings, padConstValue, + outputType, + APInt(baseType.getIntOrFloatBitWidth(), 0)); + + assert(isa(baseType) && "Unknown element type."); + FloatType fpType = cast(baseType); + + // Handle FP types with APFloat + APFloat zero(fpType.getFloatSemantics(), APInt::getZero(fpType.getWidth())); + return padType(inputType, inputValues, paddings, padConstValue, + outputType, zero); +} + +struct TosaFoldConstantPad : public TosaFoldConstantBase { + using TosaFoldConstantBase::TosaFoldConstantBase; + + LogicalResult matchAndRewrite(tosa::PadOp op, + PatternRewriter &rewriter) const override { + auto outputType = cast(op.getType()); + // TOSA doesn't support quantized types. + if (!outputType.getElementType().isIntOrIndexOrFloat()) + return failure(); + + auto input = op.getInput1(); + ElementsAttr inputValues; + if (!matchPattern(input, m_Constant(&inputValues))) + return failure(); + + // Only fold op with multiple users if foldSplatOrSingleUseOnly == true. + if (!llvm::hasSingleElement(input.getDefiningOp()->getUsers()) && + foldSplatOrSingleUseOnly) + return failure(); + + std::optional padConstValue; + if (op.getPadConst()) { + DenseElementsAttr attr; + if (!matchPattern(op.getPadConst(), m_Constant(&attr))) + return failure(); + padConstValue = attr; + } + + DenseElementsAttr paddings; + if (!matchPattern(op.getPadding(), m_Constant(&paddings))) + return failure(); + + auto resultAttr = + pad(input.getType(), inputValues, paddings, padConstValue, outputType); + rewriter.replaceOpWithNewOp(op, outputType, resultAttr); + + return success(); + } +}; + } // namespace void mlir::tosa::populateTosaFoldConstantPatterns( @@ -1113,4 +1386,12 @@ 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); + patterns.add(ctx, foldSplatOrSingleUseOnly); } \ No newline at end of file diff --git a/mlir/lib/IR/AsmPrinter.cpp b/mlir/lib/IR/AsmPrinter.cpp index ef0bf75a9cd674..906dda75403d8c 100644 --- a/mlir/lib/IR/AsmPrinter.cpp +++ b/mlir/lib/IR/AsmPrinter.cpp @@ -140,6 +140,11 @@ struct AsmPrinterOptions { llvm::cl::desc("Elide ElementsAttrs with \"...\" that have " "more elements than the given upper limit")}; + llvm::cl::opt newlineAfterAttr{ + "mlir-newline-after-attr", + llvm::cl::desc("Break attributes on ops into multiple lines with more " + "than the given upper limit")}; + llvm::cl::opt printDebugInfoOpt{ "mlir-print-debuginfo", llvm::cl::init(false), llvm::cl::desc("Print debug info in MLIR output")}; @@ -191,6 +196,8 @@ OpPrintingFlags::OpPrintingFlags() return; if (clOptions->elideElementsAttrIfLarger.getNumOccurrences()) elementsAttrElementLimit = clOptions->elideElementsAttrIfLarger; + if (clOptions->newlineAfterAttr.getNumOccurrences()) + newlineAfterAttr = clOptions->newlineAfterAttr; printDebugInfoFlag = clOptions->printDebugInfoOpt; printDebugInfoPrettyFormFlag = clOptions->printPrettyDebugInfoOpt; printGenericOpFormFlag = clOptions->printGenericOpFormOpt; @@ -209,6 +216,14 @@ OpPrintingFlags::elideLargeElementsAttrs(int64_t largeElementLimit) { return *this; } +/// Enables breaking attributes on individual lines when there are more than +/// the given number of attributes on an operation. +OpPrintingFlags & +OpPrintingFlags::newlineAfterAttribute(int64_t attributeLimit) { + newlineAfterAttr = attributeLimit; + return *this; +} + /// Enable printing of debug information. If 'prettyForm' is set to true, /// debug information is printed in a more readable 'pretty' form. OpPrintingFlags &OpPrintingFlags::enableDebugInfo(bool enable, @@ -262,6 +277,11 @@ std::optional OpPrintingFlags::getLargeElementsAttrLimit() const { return elementsAttrElementLimit; } +/// Return the size limit for printing newlines after attributes. +std::optional OpPrintingFlags::getNewlineAfterAttrLimit() const { + return newlineAfterAttr; +} + /// Return if debug information should be printed. bool OpPrintingFlags::shouldPrintDebugInfo() const { return printDebugInfoFlag; @@ -346,6 +366,12 @@ class AsmPrinter::Impl { llvm::interleaveComma(c, os, eachFn); } + template + inline void interleave(const Container &c, UnaryFunctor eachFn, + StringRef separator) const { + llvm::interleave(c, os, eachFn, separator); + } + /// This enum describes the different kinds of elision for the type of an /// attribute when printing it. enum class AttrTypeElision { @@ -396,7 +422,7 @@ class AsmPrinter::Impl { protected: void printOptionalAttrDict(ArrayRef attrs, ArrayRef elidedAttrs = {}, - bool withKeyword = false); + unsigned currentIndent = 0, bool withKeyword = false); void printNamedAttribute(NamedAttribute attr); void printTrailingLocation(Location loc, bool allowAlias = true); void printLocationInternal(LocationAttr loc, bool pretty = false, @@ -1916,7 +1942,7 @@ void AsmPrinter::Impl::printLocationInternal(LocationAttr loc, bool pretty, os << '>'; } os << '['; - interleave( + llvm::interleave( loc.getLocations(), [&](Location loc) { printLocationInternal(loc, pretty); }, [&]() { os << ", "; }); @@ -2546,6 +2572,7 @@ void AsmPrinter::Impl::printTypeImpl(Type type) { void AsmPrinter::Impl::printOptionalAttrDict(ArrayRef attrs, ArrayRef elidedAttrs, + unsigned currentIndent, bool withKeyword) { // If there are no attributes, then there is nothing to be done. if (attrs.empty()) @@ -2556,11 +2583,30 @@ void AsmPrinter::Impl::printOptionalAttrDict(ArrayRef attrs, // Print the 'attributes' keyword if necessary. if (withKeyword) os << " attributes"; + os << " {"; + + SmallString<16> separator = StringRef(", "); + if (printerFlags.getNewlineAfterAttrLimit() && + attrs.size() > *printerFlags.getNewlineAfterAttrLimit()) { + + // Increase indent to match the visually match the "{ " below. + //currentIndent += 2; + + separator.clear(); + separator.reserve(currentIndent + 2); + separator.append(",\n"); + for (size_t i = 0; i < currentIndent; ++i) + separator.push_back(' '); + + // Already put the first attribute on its own line. + os << "\n"; + os.indent(currentIndent); + } // Otherwise, print them all out in braces. - os << " {"; - interleaveComma(filteredAttrs, - [&](NamedAttribute attr) { printNamedAttribute(attr); }); + interleave( + filteredAttrs, [&](NamedAttribute attr) { printNamedAttribute(attr); }, + separator); os << '}'; }; @@ -2980,12 +3026,12 @@ class OperationPrinter : public AsmPrinter::Impl, private OpAsmPrinter { /// Print an optional attribute dictionary with a given set of elided values. void printOptionalAttrDict(ArrayRef attrs, ArrayRef elidedAttrs = {}) override { - Impl::printOptionalAttrDict(attrs, elidedAttrs); + Impl::printOptionalAttrDict(attrs, elidedAttrs, currentIndent + indentWidth); } void printOptionalAttrDictWithKeyword( ArrayRef attrs, ArrayRef elidedAttrs = {}) override { - Impl::printOptionalAttrDict(attrs, elidedAttrs, + Impl::printOptionalAttrDict(attrs, elidedAttrs, currentIndent + indentWidth, /*withKeyword=*/true); } 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..e647702e04a2be --- /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, false]> + // CHECK-NOT: tosa.greater_equal + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = + dense<[0x7F800000, 0xFF800000, 0x7F800000, 0xFF800000, 0x7FC00000, 0xFF800000]> : + tensor<6xf32> + } : () -> tensor<6xf32> + %1 = "tosa.const"() {value = + dense<[3.0, -3.0, -3.0, 3.0, 1.0, 0x7FC00000]> : + 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-greater-fold.mlir b/mlir/test/Dialect/Tosa/constant-greater-fold.mlir index f585f763459cdc..fdb8e652801e32 100644 --- a/mlir/test/Dialect/Tosa/constant-greater-fold.mlir +++ b/mlir/test/Dialect/Tosa/constant-greater-fold.mlir @@ -21,15 +21,15 @@ func.func @greater_fold_float() -> tensor<4xi1> { // CHECK-LABEL: @greater_fold_float_infinity_nan func.func @greater_fold_float_infinity_nan() -> tensor<6xi1> { - // CHECK: [[RES:]] = "tosa.const"() <{value = dense<[true, false, true, false, false, true]> + // CHECK: [[RES:]] = "tosa.const"() <{value = dense<[true, false, true, false, false, false]> // CHECK-NOT: tosa.greater // CHECK: return [[RES]] %0 = "tosa.const"() {value = - dense<[0x7F800000, 0xFF800000, 0x7F800000, 0xFF800000, 0x7FC00000, 0x7F800000]> : + dense<[0x7F800000, 0xFF800000, 0x7F800000, 0xFF800000, 0x7FC00000, 0xFF800000]> : tensor<6xf32> } : () -> tensor<6xf32> %1 = "tosa.const"() {value = - dense<[3.0, -3.0, -3.0, 3.0, 1.0, 0xFF800000]> : + dense<[3.0, -3.0, -3.0, 3.0, 1.0, 0x7FC00000]> : tensor<6xf32> } : () -> tensor<6xf32> %2 = "tosa.greater"(%0, %1) : (tensor<6xf32>, tensor<6xf32>) -> tensor<6xi1> 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..9a7db35c72ed6d --- /dev/null +++ b/mlir/test/Dialect/Tosa/constant-maximum.mlir @@ -0,0 +1,96 @@ +// 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> { + // Any comparison with NAN results in NAN + // 0x7FC00000 is the value for NAN + // 0x7F800000 is the value for Inf + // 0xFF800000 is the value for -Inf + // CHECK: [[RES:]] = "tosa.const"() <{value = dense<[0x7F800000, -3.000000e+00, 0x7F800000, 3.000000e+00, 0x7FC00000, 0x7FC00000]> + // CHECK-NOT: tosa.maximum + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = + dense<[0x7F800000, 0xFF800000, 0x7F800000, 0xFF800000, 0x7FC00000, 0xFF800000]> : + tensor<6xf32> + } : () -> tensor<6xf32> + %1 = "tosa.const"() {value = + dense<[3.0, -3.0, -3.0, 3.0, 1.0, 0x7FC00000]> : + 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..b90b403d07ecf1 --- /dev/null +++ b/mlir/test/Dialect/Tosa/constant-minimum.mlir @@ -0,0 +1,96 @@ +// 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> { + // Any comparison with NAN results in NAN + // 0x7FC00000 is the value for NAN + // 0x7F800000 is the value for Inf + // 0xFF800000 is the value for -Inf + // CHECK: [[RES:]] = "tosa.const"() <{value = dense<[3.000000e+00, 0xFF800000, -3.000000e+00, 0xFF800000, 0x7FC00000, 0x7FC00000]> + // CHECK-NOT: tosa.minimum + // CHECK: return [[RES]] + %0 = "tosa.const"() {value = + dense<[0x7F800000, 0xFF800000, 0x7F800000, 0xFF800000, 0x7FC00000, 0xFF800000]> : + tensor<6xf32> + } : () -> tensor<6xf32> + %1 = "tosa.const"() {value = + dense<[3.0, -3.0, -3.0, 3.0, 1.0, 0x7FC00000]> : + 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> +} diff --git a/mlir/test/Dialect/Tosa/constant-pad-multi-user.mlir b/mlir/test/Dialect/Tosa/constant-pad-multi-user.mlir new file mode 100644 index 00000000000000..69d975483c4c5b --- /dev/null +++ b/mlir/test/Dialect/Tosa/constant-pad-multi-user.mlir @@ -0,0 +1,13 @@ +// RUN: mlir-opt --split-input-file --tosa-layerwise-constant-fold="fold-splat-or-single-use-only=0" %s | FileCheck %s + +// CHECK-LABEL: @pad_int32_multi_user +func.func @pad_int32_multi_user() -> (tensor<2x2xi32>, tensor<5x5xi32>) { + // CHECK: "tosa.const"() <{value = dense<{{\[\[}}1, 1, 1, 1, 1], [1, 2, 2, 1, 1], [1, 2, 2, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1]]> + // CHECK: "tosa.const"() <{value = dense<2> : tensor<2x2xi32>}> + // CHECK-NOT: "tosa.pad" + %0 = "tosa.const"() {value = dense<2> : tensor<2x2xi32>} : () -> tensor<2x2xi32> + %5 = "tosa.const"() {value = dense<[[1, 2], [1, 2]]> : tensor<2x2xi64>} : () -> tensor<2x2xi64> + %6 = "tosa.const"() {value = dense<1> : tensor} : () -> tensor + %1 = "tosa.pad"(%0, %5, %6) : (tensor<2x2xi32>, tensor<2x2xi64>, tensor) -> tensor<5x5xi32> + return %0, %1 : tensor<2x2xi32>, tensor<5x5xi32> +} \ No newline at end of file diff --git a/mlir/test/Dialect/Tosa/constant-pad.mlir b/mlir/test/Dialect/Tosa/constant-pad.mlir new file mode 100644 index 00000000000000..93a457ea16779d --- /dev/null +++ b/mlir/test/Dialect/Tosa/constant-pad.mlir @@ -0,0 +1,121 @@ +// RUN: mlir-opt --split-input-file --tosa-layerwise-constant-fold %s | FileCheck %s + +// CHECK-LABEL: @pad_bool +func.func @pad_bool() -> (tensor<5x5xi1>) { + // CHECK: "tosa.const"() <{value = dense<{{\[\[}}false, false, false, false, false], + // CHECK-SAME: [false, true, true, false, false], [false, true, true, false, false], + // CHECK-SAME: [false, false, false, false, false], [false, false, false, false, false]]> + %0 = "tosa.const"() {value = dense : tensor<2x2xi1>} : () -> tensor<2x2xi1> + %1 = "tosa.const"() {value = dense<[[1, 2], [1, 2]]> : tensor<2x2xi64>} : () -> tensor<2x2xi64> + %2 = "tosa.const"() {value = dense : tensor} : () -> tensor + %3 = "tosa.pad"(%0, %1, %2) : (tensor<2x2xi1>, tensor<2x2xi64>, tensor) -> tensor<5x5xi1> + return %3 : tensor<5x5xi1> +} + +// CHECK-LABEL: @pad_int8 +func.func @pad_int8() -> (tensor<5x5xi8>) { + // CHECK: "tosa.const"() <{value = dense<{{\[\[}}1, 1, 1, 1, 1], [1, 2, 2, 1, 1], [1, 2, 2, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1]]> + %0 = "tosa.const"() {value = dense<2> : tensor<2x2xi8>} : () -> tensor<2x2xi8> + %1 = "tosa.const"() {value = dense<[[1, 2], [1, 2]]> : tensor<2x2xi64>} : () -> tensor<2x2xi64> + %2 = "tosa.const"() {value = dense<1> : tensor} : () -> tensor + %3 = "tosa.pad"(%0, %1, %2) : (tensor<2x2xi8>, tensor<2x2xi64>, tensor) -> tensor<5x5xi8> + return %3 : tensor<5x5xi8> +} + +// CHECK-LABEL: @pad_int32 +func.func @pad_int32() -> (tensor<5x5xi32>) { + // CHECK: "tosa.const"() <{value = dense<{{\[\[}}1, 1, 1, 1, 1], [1, 2, 2, 1, 1], [1, 2, 2, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1]]> + %0 = "tosa.const"() {value = dense<2> : tensor<2x2xi32>} : () -> tensor<2x2xi32> + %1 = "tosa.const"() {value = dense<[[1, 2], [1, 2]]> : tensor<2x2xi64>} : () -> tensor<2x2xi64> + %2 = "tosa.const"() {value = dense<1> : tensor} : () -> tensor + %3 = "tosa.pad"(%0, %1, %2) : (tensor<2x2xi32>, tensor<2x2xi64>, tensor) -> tensor<5x5xi32> + return %3 : tensor<5x5xi32> +} + +// CHECK-LABEL: @pad_int32_default_value +func.func @pad_int32_default_value() -> (tensor<5x5xi32>) { + // CHECK: "tosa.const"() <{value = dense<{{\[\[}}0, 0, 0, 0, 0], [0, 2, 2, 0, 0], [0, 2, 2, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]> + %0 = "tosa.const"() {value = dense<2> : tensor<2x2xi32>} : () -> tensor<2x2xi32> + %1 = "tosa.const"() {value = dense<[[1, 2], [1, 2]]> : tensor<2x2xi64>} : () -> tensor<2x2xi64> + %2 = "tosa.pad"(%0, %1) : (tensor<2x2xi32>, tensor<2x2xi64>) -> tensor<5x5xi32> + return %2 : tensor<5x5xi32> +} + +// CHECK-LABEL: @pad_bfloat16 +func.func @pad_bfloat16() -> (tensor<5x5xbf16>) { + // CHECK: "tosa.const"() + // CHECK-SAME: {{\[\[}}2.000000e+00, 2.000000e+00, 2.000000e+00, 2.000000e+00, 2.000000e+00], + // CHECK-SAME: [2.000000e+00, 1.000000e+00, 1.000000e+00, 2.000000e+00, 2.000000e+00], + // CHECK-SAME: [2.000000e+00, 1.000000e+00, 1.000000e+00, 2.000000e+00, 2.000000e+00], + // CHECK-SAME: [2.000000e+00, 2.000000e+00, 2.000000e+00, 2.000000e+00, 2.000000e+00], + // CHECK-SAME: [2.000000e+00, 2.000000e+00, 2.000000e+00, 2.000000e+00, 2.000000e+00]]> + %0 = "tosa.const"() {value = dense<1.0> : tensor<2x2xbf16>} : () -> tensor<2x2xbf16> + %1 = "tosa.const"() {value = dense<[[1, 2], [1, 2]]> : tensor<2x2xi64>} : () -> tensor<2x2xi64> + %2 = "tosa.const"() {value = dense<2.0> : tensor} : () -> tensor + %3 = "tosa.pad"(%0, %1, %2) : (tensor<2x2xbf16>, tensor<2x2xi64>, tensor) -> tensor<5x5xbf16> + return %3 : tensor<5x5xbf16> +} + +// CHECK-LABEL: @pad_bfloat16_default_value +func.func @pad_bfloat16_default_value() -> (tensor<5x5xbf16>) { + // CHECK: "tosa.const"() + // CHECK-SAME: {{\[\[}}0.000000e+00, 0.000000e+00, 0.000000e+00, 0.000000e+00, 0.000000e+00], + // CHECK-SAME: [0.000000e+00, 1.000000e+00, 1.000000e+00, 0.000000e+00, 0.000000e+00], + // CHECK-SAME: [0.000000e+00, 1.000000e+00, 1.000000e+00, 0.000000e+00, 0.000000e+00], + // CHECK-SAME: [0.000000e+00, 0.000000e+00, 0.000000e+00, 0.000000e+00, 0.000000e+00], + // CHECK-SAME: [0.000000e+00, 0.000000e+00, 0.000000e+00, 0.000000e+00, 0.000000e+00]]> + %0 = "tosa.const"() {value = dense<1.0> : tensor<2x2xbf16>} : () -> tensor<2x2xbf16> + %1 = "tosa.const"() {value = dense<[[1, 2], [1, 2]]> : tensor<2x2xi64>} : () -> tensor<2x2xi64> + %2 = "tosa.pad"(%0, %1) : (tensor<2x2xbf16>, tensor<2x2xi64>) -> tensor<5x5xbf16> + return %2 : tensor<5x5xbf16> +} + +// CHECK-LABEL: @pad_f32_3d +func.func @pad_f32_3d() -> (tensor<3x3x4xf32>) { + // CHECK: "tosa.const"() + // CHECK-SAME: {{\[\[}}[1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00], + // CHECK-SAME: [1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00], + // CHECK-SAME: [1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00]], + // CHECK-SAME: {{\[\[}}1.000000e+00, 2.000000e+00, 2.000000e+00, 1.000000e+00], + // CHECK-SAME: [1.000000e+00, 2.000000e+00, 2.000000e+00, 1.000000e+00], + // CHECK-SAME: [1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00]], + // CHECK-SAME: {{\[\[}}1.000000e+00, 2.000000e+00, 2.000000e+00, 1.000000e+00], + // CHECK-SAME: [1.000000e+00, 2.000000e+00, 2.000000e+00, 1.000000e+00], + // CHECK-SAME: [1.000000e+00, 1.000000e+00, 1.000000e+00, 1.000000e+00]]]> + %0 = "tosa.const"() {value = dense<2.0> : tensor<2x2x2xf32>} : () -> tensor<2x2x2xf32> + %1 = "tosa.const"() {value = dense<[[1, 0], [0, 1], [1, 1]]> : tensor<3x2xi64>} : () -> tensor<3x2xi64> + %2 = "tosa.const"() {value = dense<1.0> : tensor} : () -> tensor + %3 = "tosa.pad"(%0, %1, %2) : (tensor<2x2x2xf32>, tensor<3x2xi64>, tensor) -> tensor<3x3x4xf32> + return %3 : tensor<3x3x4xf32> +} + +// CHECK-LABEL: @pad_f32_3d_default_value +func.func @pad_f32_3d_default_value() -> (tensor<3x3x4xf32>) { + // CHECK: "tosa.const"() + // CHECK-SAME: {{\[\[}}[0.000000e+00, 0.000000e+00, 0.000000e+00, 0.000000e+00], + // CHECK-SAME: [0.000000e+00, 0.000000e+00, 0.000000e+00, 0.000000e+00], + // CHECK-SAME: [0.000000e+00, 0.000000e+00, 0.000000e+00, 0.000000e+00]], + // CHECK-SAME: {{\[\[}}0.000000e+00, 2.000000e+00, 2.000000e+00, 0.000000e+00], + // CHECK-SAME: [0.000000e+00, 2.000000e+00, 2.000000e+00, 0.000000e+00], + // CHECK-SAME: [0.000000e+00, 0.000000e+00, 0.000000e+00, 0.000000e+00]], + // CHECK-SAME: {{\[\[}}0.000000e+00, 2.000000e+00, 2.000000e+00, 0.000000e+00], + // CHECK-SAME: [0.000000e+00, 2.000000e+00, 2.000000e+00, 0.000000e+00], + // CHECK-SAME: [0.000000e+00, 0.000000e+00, 0.000000e+00, 0.000000e+00]]]> + %0 = "tosa.const"() {value = dense<2.0> : tensor<2x2x2xf32>} : () -> tensor<2x2x2xf32> + %1 = "tosa.const"() {value = dense<[[1, 0], [0, 1], [1, 1]]> : tensor<3x2xi64>} : () -> tensor<3x2xi64> + %2 = "tosa.pad"(%0, %1) : (tensor<2x2x2xf32>, tensor<3x2xi64>) -> tensor<3x3x4xf32> + return %2 : tensor<3x3x4xf32> +} + +// CHECK-LABEL: @pad_int32_multi_user +func.func @pad_int32_multi_user() -> (tensor<2x2xi32>, tensor<5x5xi32>) { + // CHECK: "tosa.const"() <{value = dense<2> : tensor<2x2xi32>}> + // CHECK: "tosa.const"() <{value = dense<{{\[\[}}1, 2], [1, 2]]> + // CHECK: "tosa.const"() <{value = dense<1> : tensor}> + // CHECK: "tosa.pad" + %0 = "tosa.const"() {value = dense<2> : tensor<2x2xi32>} : () -> tensor<2x2xi32> + %5 = "tosa.const"() {value = dense<[[1, 2], [1, 2]]> : tensor<2x2xi64>} : () -> tensor<2x2xi64> + %6 = "tosa.const"() {value = dense<1> : tensor} : () -> tensor + %1 = "tosa.pad"(%0, %5, %6) : (tensor<2x2xi32>, tensor<2x2xi64>, tensor) -> tensor<5x5xi32> + return %0, %1 : tensor<2x2xi32>, tensor<5x5xi32> +} \ No newline at end of file diff --git a/mlir/test/IR/mlir-newline-after-attr.mlir b/mlir/test/IR/mlir-newline-after-attr.mlir new file mode 100644 index 00000000000000..5657f6e5ad706e --- /dev/null +++ b/mlir/test/IR/mlir-newline-after-attr.mlir @@ -0,0 +1,10 @@ +// RUN: mlir-opt %s -mlir-newline-after-attr=2 | FileCheck %s +// Ensure that the printed version is still parseable. +// RUN: mlir-opt %s -mlir-newline-after-attr=2 | mlir-opt + +// CHECK: foo.dense_attr = +"test.op"() {foo.dense_attr = dense<1> : tensor<3xi32>} : () -> () + +// CHECK: foo.dense_attr = +// CHECK: foo.second_attr = +"test.op"() {foo.dense_attr = dense<1> : tensor<3xi32>, foo.second_attr = dense<2> : tensor<3xi32>} : () -> ()