diff --git a/mlir/include/mlir/Dialect/Tosa/IR/TosaOps.td b/mlir/include/mlir/Dialect/Tosa/IR/TosaOps.td index b14e892d459d4..2cddbee97dff2 100644 --- a/mlir/include/mlir/Dialect/Tosa/IR/TosaOps.td +++ b/mlir/include/mlir/Dialect/Tosa/IR/TosaOps.td @@ -1821,9 +1821,9 @@ def Tosa_ResizeOp : Tosa_InferShapedTypeOp<"resize"> { let arguments = (ins Tosa_Tensor4D:$input, - Tosa_IntArrayAttr4:$scale, - Tosa_IntArrayAttr2:$offset, - Tosa_IntArrayAttr2:$border, + Rank4TosaShape:$scale, + Rank2TosaShape:$offset, + Rank2TosaShape:$border, Tosa_ResizeTypeAttr:$mode ); @@ -1832,6 +1832,7 @@ def Tosa_ResizeOp : Tosa_InferShapedTypeOp<"resize"> { ); let hasFolder = 1; + let hasVerifier = 1; } //===----------------------------------------------------------------------===// diff --git a/mlir/include/mlir/Dialect/Tosa/Utils/ConversionUtils.h b/mlir/include/mlir/Dialect/Tosa/Utils/ConversionUtils.h index 498757e6ccffa..9fa779b1fa956 100644 --- a/mlir/include/mlir/Dialect/Tosa/Utils/ConversionUtils.h +++ b/mlir/include/mlir/Dialect/Tosa/Utils/ConversionUtils.h @@ -256,6 +256,9 @@ SmallVector convertFromMlirShape(ArrayRef shape); bool getConstShapeValue(Operation *op, llvm::SmallVector &result_shape); +// returns a small vector of int64_t values that attr contains +SmallVector convertFromIntAttr(const DenseElementsAttr &attr, + const int rank); } // namespace tosa } // namespace mlir diff --git a/mlir/lib/Conversion/TosaToLinalg/TosaToLinalg.cpp b/mlir/lib/Conversion/TosaToLinalg/TosaToLinalg.cpp index b84b2fddd047d..65e377044194c 100644 --- a/mlir/lib/Conversion/TosaToLinalg/TosaToLinalg.cpp +++ b/mlir/lib/Conversion/TosaToLinalg/TosaToLinalg.cpp @@ -1505,7 +1505,10 @@ class ResizeUnaryConverter : public OpConversionPattern { return success(); } - ArrayRef scale = operands.getScale(); + SmallVector scale; + if (!tosa::getConstShapeValue(op.getScale().getDefiningOp(), scale)) { + return failure(); + } // Collapse the unit width and height away. SmallVector reassociationMap(2); @@ -1611,8 +1614,9 @@ class MaterializeResizeBroadcast : public OpConversionPattern { resizeShape.push_back(channels); auto resizeTy = resultTy.clone(resizeShape); - auto resize = - builder.create(resizeTy, input, op->getAttrs()); + auto resize = builder.create(resizeTy, input, op.getScale(), + op.getOffset(), op.getBorder(), + op.getMode()); // Collapse an unit result dims. SmallVector reassociationMap(2); @@ -1733,9 +1737,14 @@ class GenericResizeConverter : public OpConversionPattern { Value inY = b.create(b.getI32Type(), y); Value inX = b.create(b.getI32Type(), x); - ArrayRef offset = operands.getOffset(); - ArrayRef border = operands.getBorder(); - ArrayRef scale = operands.getScale(); + SmallVector scale, offset, border; + if (!tosa::getConstShapeValue(op.getScale().getDefiningOp(), scale) || + !tosa::getConstShapeValue(op.getOffset().getDefiningOp(), offset) || + !tosa::getConstShapeValue(op.getBorder().getDefiningOp(), border)) { + return rewriter.notifyMatchFailure( + op, "tosa.resize scale/offset/border should have compile time " + "constant values."); + } Value yScaleN, yScaleD, xScaleN, xScaleD; yScaleN = b.create(b.getI32IntegerAttr(scale[0])); diff --git a/mlir/lib/Dialect/Tosa/IR/TosaCanonicalizations.cpp b/mlir/lib/Dialect/Tosa/IR/TosaCanonicalizations.cpp index b70b7cf30b1a2..22447e49fb3ea 100644 --- a/mlir/lib/Dialect/Tosa/IR/TosaCanonicalizations.cpp +++ b/mlir/lib/Dialect/Tosa/IR/TosaCanonicalizations.cpp @@ -1625,9 +1625,22 @@ OpFoldResult PadOp::fold(FoldAdaptor adaptor) { // Fold away cases where a tosa.resize operation returns a copy // of the input image. OpFoldResult ResizeOp::fold(FoldAdaptor adaptor) { - ArrayRef offset = getOffset(); - ArrayRef border = getBorder(); - ArrayRef scale = getScale(); + auto scaleAttr = + llvm::dyn_cast_if_present(adaptor.getScale()); + auto offsetAttr = + llvm::dyn_cast_if_present(adaptor.getOffset()); + auto borderAttr = + llvm::dyn_cast_if_present(adaptor.getBorder()); + if (!scaleAttr || !offsetAttr || !borderAttr) { + return {}; + } + + auto scale = tosa::convertFromIntAttr(scaleAttr, /* rank = */ 4); + auto offset = tosa::convertFromIntAttr(offsetAttr, /* rank = */ 2); + auto border = tosa::convertFromIntAttr(borderAttr, /* rank = */ 2); + if (scale.size() != 4 || offset.size() != 2 || border.size() != 2) { + return {}; + } // Check unit scaling. if (scale[0] != scale[1] || scale[2] != scale[3]) { diff --git a/mlir/lib/Dialect/Tosa/IR/TosaOps.cpp b/mlir/lib/Dialect/Tosa/IR/TosaOps.cpp index 8766d4b6252f8..b96ad6fa0f33f 100644 --- a/mlir/lib/Dialect/Tosa/IR/TosaOps.cpp +++ b/mlir/lib/Dialect/Tosa/IR/TosaOps.cpp @@ -1689,9 +1689,14 @@ LogicalResult tosa::ResizeOp::inferReturnTypeComponents( (inputWidth == ShapedType::kDynamic)) return failure(); - llvm::ArrayRef scaleInt = adaptor.getScale(); - llvm::ArrayRef offsetInt = adaptor.getOffset(); - llvm::ArrayRef borderInt = adaptor.getBorder(); + SmallVector scaleInt, offsetInt, borderInt; + if (!tosa::getConstShapeValue(adaptor.getScale().getDefiningOp(), scaleInt) || + !tosa::getConstShapeValue(adaptor.getOffset().getDefiningOp(), + offsetInt) || + !tosa::getConstShapeValue(adaptor.getBorder().getDefiningOp(), + borderInt)) { + return failure(); + } // Compute the output shape based on attributes: scale, offset, and border. outputShape[1] = @@ -1708,6 +1713,98 @@ LogicalResult tosa::ResizeOp::inferReturnTypeComponents( return success(); } +LogicalResult tosa::ResizeOp::verify() { + const Value input = getInput(); + const Value output = getOutput(); + const RankedTensorType inputType = + llvm::dyn_cast(input.getType()); + const RankedTensorType outputType = + llvm::dyn_cast(output.getType()); + + if (!inputType) + return emitOpError("expect a ranked input tensor"); + if (!outputType) + return emitOpError("expect a ranked output tensor"); + + const int64_t oh = outputType.getDimSize(1); + const int64_t ow = outputType.getDimSize(2); + const int64_t ih = inputType.getDimSize(1); + const int64_t iw = inputType.getDimSize(2); + + SmallVector scaleValues; + SmallVector offsetValues; + SmallVector borderValues; + if (!tosa::getConstShapeValue(getScale().getDefiningOp(), scaleValues) || + !tosa::getConstShapeValue(getOffset().getDefiningOp(), offsetValues) || + !tosa::getConstShapeValue(getBorder().getDefiningOp(), borderValues)) { + // Skip following checks if shape is not constant + return success(); + } + + if (llvm::any_of(scaleValues, [](int64_t s) { return s <= 0; })) + return emitOpError("expect all scale values to be > 0, got ") + << scaleValues; + + const int64_t scaleYN = scaleValues[0]; + const int64_t scaleYD = scaleValues[1]; + const int64_t scaleXN = scaleValues[2]; + const int64_t scaleXD = scaleValues[3]; + + const int64_t offsetY = offsetValues[0]; + const int64_t offsetX = offsetValues[1]; + + const int64_t borderY = borderValues[0]; + const int64_t borderX = borderValues[1]; + + auto idivCheck = [](const int64_t lhs, + const int64_t rhs) -> std::optional { + if (lhs % rhs != 0) + return std::nullopt; + return lhs / rhs; + }; + + // Don't check with input height that could be broadcast (ih != 1) + // since Linalg, a consumer of TOSA, expects broadcasting support + // in resize to be available. Taking the cautious approach for now, + // we can consider removing support for broadcasting later. + if (ih != ShapedType::kDynamic && ih != 1) { + const std::optional calculatedOutHeightMinusOne = + idivCheck((ih - 1) * scaleYN - offsetY + borderY, scaleYD); + if (!calculatedOutHeightMinusOne.has_value()) + return emitOpError("expected (input_height - 1) * scale_y_n - offset_y + " + "border_y ") + << "to be wholly divisible by scale_y_d, got ((" << ih + << " - 1) * " << scaleYN << " - " << offsetY << " + " << borderY + << ") / " << scaleYD; + const int64_t calculatedOutHeight = calculatedOutHeightMinusOne.value() + 1; + if (oh != ShapedType::kDynamic && calculatedOutHeight != oh) + return emitOpError("calculated output height did not match expected: ") + << "calculated=" << calculatedOutHeight << ", expected=" << oh; + } + + // Don't check with input width that could be broadcast (iw != 1) + // since Linalg, a consumer of TOSA, expects broadcasting support + // in resize to be available. Taking the cautious approach for now, + // we can consider removing support for broadcasting later. + if (iw != ShapedType::kDynamic && iw != 1) { + const int64_t scaledInWidth = (iw - 1) * scaleXN - offsetX + borderX; + const std::optional calculatedOutWidthMinusOne = + idivCheck(scaledInWidth, scaleXD); + if (!calculatedOutWidthMinusOne.has_value()) + return emitOpError("expected (input_width - 1) * scale_x_n - offset_x + " + "border_x ") + << "to be wholly divisible by scale_x_d, got ((" << iw + << " - 1) * " << scaleXN << " - " << offsetX << " + " << borderX + << ") / " << scaleXD; + const int64_t calculatedOutWidth = calculatedOutWidthMinusOne.value() + 1; + if (ow != ShapedType::kDynamic && calculatedOutWidth != ow) + return emitOpError("calculated output width did not match expected: ") + << "calculated=" << calculatedOutWidth << ", expected=" << ow; + } + + return success(); +} + LogicalResult tosa::ScatterOp::inferReturnTypeComponents( MLIRContext *context, ::std::optional location, ScatterOp::Adaptor adaptor, diff --git a/mlir/lib/Dialect/Tosa/Transforms/TosaValidation.cpp b/mlir/lib/Dialect/Tosa/Transforms/TosaValidation.cpp index 7f59ff70d3374..f4abe628d37d1 100644 --- a/mlir/lib/Dialect/Tosa/Transforms/TosaValidation.cpp +++ b/mlir/lib/Dialect/Tosa/Transforms/TosaValidation.cpp @@ -18,6 +18,7 @@ #include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/Dialect/Tosa/IR/TosaOps.h" +#include "mlir/Dialect/Tosa/Utils/ConversionUtils.h" #include "mlir/IR/Builders.h" #include "mlir/IR/BuiltinOps.h" #include "mlir/IR/Matchers.h" @@ -106,6 +107,9 @@ struct TosaValidation : public tosa::impl::TosaValidationBase { // check variable read/write data types against variable declarations LogicalResult applyVariableCheck(Operation *op); + // check error if conditions + LogicalResult applyErrorIfCheck(Operation *op); + private: void populateConstantOperandChecks() { constCheckers.emplace_back(checkConstantOperandPad); @@ -369,11 +373,14 @@ struct TosaValidation : public tosa::impl::TosaValidationBase { // Resize op: level check max scales bool levelCheckResize(Operation *op) { if (auto resize = dyn_cast(op)) { - auto scale = resize.getScale(); - int16_t scaleYN = scale[0]; - int16_t scaleYD = scale[1]; - int16_t scaleXN = scale[2]; - int16_t scaleXD = scale[3]; + SmallVector scale; + if (!tosa::getConstShapeValue(resize.getScale().getDefiningOp(), scale)) { + return false; + } + const int64_t scaleYN = scale[0]; + const int64_t scaleYD = scale[1]; + const int64_t scaleXN = scale[2]; + const int64_t scaleXD = scale[3]; if (!levelCheckScale(op, scaleYN / scaleYD, "scale_y_n/scale_y_d <= MAX_SCALE") || !levelCheckScale(op, scaleXN / scaleXD, @@ -505,6 +512,169 @@ LogicalResult TosaValidation::applyVariableCheck(Operation *op) { return success(); } +bool checkErrorIfResize(Operation *op) { + auto resize = dyn_cast(op); + if (!resize) + return true; + + const Value input = resize.getInput(); + const Value output = resize.getOutput(); + const RankedTensorType inputType = + llvm::dyn_cast(input.getType()); + const RankedTensorType outputType = + llvm::dyn_cast(output.getType()); + + if (!inputType || !outputType) { + op->emitOpError("expect ranked input/output tensor"); + return false; + } + + // Ensure the image size is supported by GPU APIs and that for integer + // implementations, position * stride does not overflow int32_t. + if (inputType.hasStaticShape() && outputType.hasStaticShape()) { + const SmallVector sizes = { + outputType.getDimSize(1), outputType.getDimSize(2), + inputType.getDimSize(1), inputType.getDimSize(2)}; + const int64_t *maxDim = llvm::max_element(sizes); + if (maxDim != sizes.end() && *maxDim >= 16384) { + op->emitOpError("expect input/output height/width dims to be < 16384, ") + << "got [OH, OW, IH, IW] = " << sizes; + return false; + } + } + + SmallVector scale; + if (!tosa::getConstShapeValue(resize.getScale().getDefiningOp(), scale)) { + return false; + } + + const int64_t scaleYN = scale[0]; + const int64_t scaleYD = scale[1]; + const int64_t scaleXN = scale[2]; + const int64_t scaleXD = scale[3]; + + // Ensure scale values don't overflow int32 accumulator + if (scaleYN > (1 << 11) || scaleXN > (1 << 11)) { + op->emitOpError("expect all scale numerator values to be <= (1 << 11), " + "got scale_y_n=") + << scaleYN << ", scale_x_n=" << scaleXN; + return false; + } + + if (scaleYD >= 16 * scaleYN || scaleXD >= 16 * scaleXN) { + op->emitOpError("expect a downscale ratio larger than 1/16, got y=") + << scaleYN << "/" << scaleYD << ", x=" << scaleXN << "/" << scaleXD; + return false; + } + + SmallVector offset; + SmallVector border; + if (!tosa::getConstShapeValue(resize.getOffset().getDefiningOp(), offset) || + !tosa::getConstShapeValue(resize.getBorder().getDefiningOp(), border)) { + return false; + } + + const int64_t offsetY = offset[0]; + const int64_t offsetX = offset[1]; + // Set a consistent lower limit of 1/16 downscale to simplify + // implementations + if (offsetY < -scaleYN || offsetY >= 16 * scaleYN) { + op->emitOpError( + "expect offsetY / scaleYNumerator to be in range [-1, 16), got ") + << offsetY << "/" << scaleYN; + return false; + } + if (offsetX < -scaleXN || offsetX >= 16 * scaleXN) { + op->emitOpError( + "expect offsetX / scaleXNumerator to be in range [-1, 16), got ") + << offsetX << "/" << scaleXN; + return false; + } + + const int64_t borderY = border[0]; + const int64_t borderX = border[1]; + if (borderY < -16 * scaleYN || borderY >= scaleYN) { + op->emitOpError( + "expect borderY / scaleYNumerator to be in range [-16, 1), got ") + << borderY << "/" << scaleYN; + return false; + } + if (borderX < -16 * scaleXN || borderX >= scaleXN) { + op->emitOpError( + "expect borderX / scaleXNumerator to be in range [-16, 1), got ") + << borderX << "/" << scaleXN; + return false; + } + + // The following section of code is mostly duplicated with ResizeOp::verify(). + // + // In TOSA specification, we do not support broadcast behavior. + // However, there is a rewrite pattern to materialize broadcast ResizeOp. + // It makes invalid TOSA ResizeOp into valid one. To avoid breaking + // existing code, we keep the rewrite pattern untouched. So, we need + // loose the checking in ResizeOp::verify() to support broadcast ResizeOp. + // + // Here is a strict checking to conform TOSA specification. + // FIXME: Remove the duplicated checkings when broadcast ResizeOp is removed. + auto idivCheck = [](const int64_t lhs, + const int64_t rhs) -> std::optional { + if (lhs % rhs != 0) + return std::nullopt; + return lhs / rhs; + }; + + const int64_t oh = outputType.getDimSize(1); + const int64_t ow = outputType.getDimSize(2); + const int64_t ih = inputType.getDimSize(1); + const int64_t iw = inputType.getDimSize(2); + + if (ih != ShapedType::kDynamic) { + const std::optional calculatedOutHeightMinusOne = + idivCheck((ih - 1) * scaleYN - offsetY + borderY, scaleYD); + if (!calculatedOutHeightMinusOne.has_value()) { + op->emitOpError("expected (input_height - 1) * scale_y_n - offset_y + " + "border_y ") + << "to be wholly divisible by scale_y_d, got ((" << ih << " - 1) * " + << scaleYN << " - " << offsetY << " + " << borderY << ") / " + << scaleYD; + return false; + } + const int64_t calculatedOutHeight = calculatedOutHeightMinusOne.value() + 1; + if (oh != ShapedType::kDynamic && calculatedOutHeight != oh) { + op->emitOpError("calculated output height did not match expected: ") + << "calculated=" << calculatedOutHeight << ", expected=" << oh; + return false; + } + } + + if (iw != ShapedType::kDynamic) { + const std::optional calculatedOutWidthMinusOne = + idivCheck((iw - 1) * scaleXN - offsetX + borderX, scaleXD); + if (!calculatedOutWidthMinusOne.has_value()) { + op->emitOpError("expected (input_width - 1) * scale_x_n - offset_x + " + "border_x ") + << "to be wholly divisible by scale_x_d, got ((" << iw << " - 1) * " + << scaleXN << " - " << offsetX << " + " << borderX << ") / " + << scaleXD; + return false; + } + const int64_t calculatedOutWidth = calculatedOutWidthMinusOne.value() + 1; + if (ow != ShapedType::kDynamic && calculatedOutWidth != ow) { + op->emitOpError("calculated output width did not match expected: ") + << "calculated=" << calculatedOutWidth << ", expected=" << ow; + return false; + } + } + + return true; +} + +LogicalResult TosaValidation::applyErrorIfCheck(Operation *op) { + if (!checkErrorIfResize(op)) + return failure(); + return success(); +} + bool TosaValidation::isValidElementType(Type type) { if (isa(type)) { if (!isEnabledProfile(TosaProfileEnum::MainInference)) @@ -568,6 +738,10 @@ void TosaValidation::runOnOperation() { // do variable type checks if (failed(applyVariableCheck(op))) signalPassFailure(); + + // do error if checks + if (StrictOperationSpecAlignment && failed(applyErrorIfCheck(op))) + signalPassFailure(); }); } } // namespace diff --git a/mlir/lib/Dialect/Tosa/Utils/ConversionUtils.cpp b/mlir/lib/Dialect/Tosa/Utils/ConversionUtils.cpp index 00df9f46baf72..936c5932ba5a8 100644 --- a/mlir/lib/Dialect/Tosa/Utils/ConversionUtils.cpp +++ b/mlir/lib/Dialect/Tosa/Utils/ConversionUtils.cpp @@ -260,3 +260,21 @@ bool mlir::tosa::getConstShapeValue(Operation *op, // for undefined op, return false. return false; } + +// returns a small vector of int64_t values that attr contains +SmallVector +mlir::tosa::convertFromIntAttr(const DenseElementsAttr &attr, const int rank) { + if (attr.isSplat()) { + int64_t v = attr.getSplatValue().getSExtValue(); + return SmallVector(rank, v); + } + + if (auto int_array_attr = llvm::dyn_cast(attr)) { + SmallVector vec; + for (APInt val : int_array_attr.getValues()) { + vec.push_back(val.getSExtValue()); + } + return vec; + } + return {}; +} diff --git a/mlir/test/Conversion/TosaToLinalg/tosa-to-linalg-resize.mlir b/mlir/test/Conversion/TosaToLinalg/tosa-to-linalg-resize.mlir index d42d0a46692d4..5a2ee7d9e8720 100644 --- a/mlir/test/Conversion/TosaToLinalg/tosa-to-linalg-resize.mlir +++ b/mlir/test/Conversion/TosaToLinalg/tosa-to-linalg-resize.mlir @@ -2,7 +2,10 @@ // CHECK-LABEL: @unary_resize_nearest_fp32 func.func @unary_resize_nearest_fp32(%arg0 : tensor<3x1x1x7xf32>) -> tensor<3x1x1x7xf32> { - %resize = "tosa.resize"(%arg0) {mode = "NEAREST_NEIGHBOR", scale = array, offset = array, border = array} : (tensor<3x1x1x7xf32>) -> tensor<3x1x1x7xf32> + %scale = tosa.const_shape { value = dense<[2, 2, 1, 1]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %resize = tosa.resize %arg0, %scale, %offset, %border {mode = "NEAREST_NEIGHBOR"} : (tensor<3x1x1x7xf32>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<3x1x1x7xf32> // CHECK: return %arg0 return %resize : tensor<3x1x1x7xf32> } @@ -11,7 +14,10 @@ func.func @unary_resize_nearest_fp32(%arg0 : tensor<3x1x1x7xf32>) -> tensor<3x1x // CHECK-LABEL: @unary_resize_nearest_fp16 func.func @unary_resize_nearest_fp16(%arg0 : tensor<3x1x1x7xf16>) -> tensor<3x1x1x7xf16> { - %resize = "tosa.resize"(%arg0) {mode = "NEAREST_NEIGHBOR", scale = array, offset = array, border = array} : (tensor<3x1x1x7xf16>) -> tensor<3x1x1x7xf16> + %scale = tosa.const_shape { value = dense<[2, 2, 1, 1]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %resize = tosa.resize %arg0, %scale, %offset, %border {mode = "NEAREST_NEIGHBOR"} : (tensor<3x1x1x7xf16>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<3x1x1x7xf16> // CHECK: return %arg0 return %resize : tensor<3x1x1x7xf16> } @@ -20,7 +26,10 @@ func.func @unary_resize_nearest_fp16(%arg0 : tensor<3x1x1x7xf16>) -> tensor<3x1x // CHECK-LABEL: @unary_resize_bilinear_fp32 func.func @unary_resize_bilinear_fp32(%arg0 : tensor<3x1x1x7xf32>) -> tensor<3x1x1x7xf32> { - %resize = "tosa.resize"(%arg0) {mode = "BILINEAR", scale = array, offset = array, border = array} : (tensor<3x1x1x7xf32>) -> tensor<3x1x1x7xf32> + %scale = tosa.const_shape { value = dense<[2, 2, 1, 1]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %resize = tosa.resize %arg0, %scale, %offset, %border {mode = "BILINEAR"} : (tensor<3x1x1x7xf32>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<3x1x1x7xf32> // CHECK: return %arg0 return %resize : tensor<3x1x1x7xf32> } @@ -29,7 +38,10 @@ func.func @unary_resize_bilinear_fp32(%arg0 : tensor<3x1x1x7xf32>) -> tensor<3x1 // CHECK-LABEL: @unary_resize_bilinear_fp16 func.func @unary_resize_bilinear_fp16(%arg0 : tensor<3x1x1x7xf16>) -> tensor<3x1x1x7xf16> { - %resize = "tosa.resize"(%arg0) {mode = "BILINEAR", scale = array, offset = array, border = array} : (tensor<3x1x1x7xf16>) -> tensor<3x1x1x7xf16> + %scale = tosa.const_shape { value = dense<[2, 2, 1, 1]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %resize = tosa.resize %arg0, %scale, %offset, %border {mode = "BILINEAR"} : (tensor<3x1x1x7xf16>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<3x1x1x7xf16> // CHECK: return %arg0 return %resize : tensor<3x1x1x7xf16> } @@ -38,7 +50,10 @@ func.func @unary_resize_bilinear_fp16(%arg0 : tensor<3x1x1x7xf16>) -> tensor<3x1 // CHECK-LABEL: @unary_resize_nearest_i8 func.func @unary_resize_nearest_i8(%arg0 : tensor<3x1x1x7xi8>) -> tensor<3x1x1x7xi8> { - %resize = "tosa.resize"(%arg0) {mode = "NEAREST_NEIGHBOR", scale = array, offset = array, border = array} : (tensor<3x1x1x7xi8>) -> tensor<3x1x1x7xi8> + %scale = tosa.const_shape { value = dense<[2, 1, 3, 1]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %resize = tosa.resize %arg0, %scale, %offset, %border {mode = "NEAREST_NEIGHBOR"} : (tensor<3x1x1x7xi8>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<3x1x1x7xi8> // CHECK: return %arg0 return %resize : tensor<3x1x1x7xi8> } @@ -50,14 +65,16 @@ func.func @broadcast_resize_nearest_f32(%arg0 : tensor<3x1x1x7xf32>) -> tensor<3 // CHECK: %[[COLLAPSE:.+]] = tensor.collapse_shape %arg0 // CHECK-NEXT{literal}: [[0], [1, 2, 3]] : tensor<3x1x1x7xf32> into tensor<3x7xf32> // CHECK: %[[EMPTY:.+]] = tensor.empty() : tensor<3x1x5x7xf32> - // CHECK: %[[GENERIC:.+]] = linalg.generic + // CHECK: %[[GENERIC:.+]] = linalg.generic // CHECK-SAME: indexing_maps = [#map, #map1], iterator_types = ["parallel", "parallel", "parallel", "parallel"]} // CHECK-SAME: ins(%[[COLLAPSE]] : tensor<3x7xf32>) outs(%[[EMPTY]] : tensor<3x1x5x7xf32>) // CHECK: ^bb0(%[[IN:.+]]: f32, %[[OUT:.+]]: f32): // CHECK: linalg.yield %[[IN]] : f32 - %resize = "tosa.resize"(%arg0) {mode = "NEAREST_NEIGHBOR", scale = array, offset = array, border = array} : (tensor<3x1x1x7xf32>) -> tensor<3x1x5x7xf32> + %scale = tosa.const_shape { value = dense<[2, 1, 3, 1]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %resize = tosa.resize %arg0, %scale, %offset, %border {mode = "NEAREST_NEIGHBOR"} : (tensor<3x1x1x7xf32>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<3x1x5x7xf32> - // CHECK: return %[[GENERIC]] return %resize : tensor<3x1x5x7xf32> } @@ -79,19 +96,22 @@ func.func @broadcast_resize_bilinear_i8(%arg0 : tensor<3x1x1x7xi8>) -> tensor<3x // CHECK: %[[OUT:.+]] = arith.muli %[[MUL]], %[[C3]] : i32 // CHECK: linalg.yield %[[OUT]] : i32 // CHECK: } -> tensor<3x7xi32> - // CHECK: %[[EXPAND:.+]] = tensor.expand_shape %1 - // CHECK-SAME{literal}: [[0], [1, 2, 3]] : tensor<3x7xi32> into tensor<3x1x1x7xi32> - // CHECK: %[[COLLAPSE:.+]] = tensor.collapse_shape %expanded + // CHECK: %[[EXPAND:.+]] = tensor.expand_shape %[[RESIZE]] + // CHECK-SAME{literal}: [[0], [1, 2, 3]] output_shape [3, 1, 1, 7] : + // CHECK-SAME: tensor<3x7xi32> into tensor<3x1x1x7xi32> + // CHECK: %[[COLLAPSE_0:.+]] = tensor.collapse_shape %[[EXPAND]] // CHECK-SAME{literal}:[[0], [1, 2, 3]] : tensor<3x1x1x7xi32> into tensor<3x7xi32> - // CHECK: %[[EMPTY:.+]] = tensor.empty() : tensor<3x4x5x7xi32> + // CHECK: %[[EMPTY_0:.+]] = tensor.empty() : tensor<3x4x5x7xi32> // CHECK: %[[BROADCAST:.+]] = linalg.generic // CHECK-SAME: indexing_maps = [#map1, #map2], iterator_types = ["parallel", "parallel", "parallel", "parallel"]} - // CHECK-SAME: ins(%[[COLLAPSE]] : tensor<3x7xi32>) outs(%[[EMPTY]] : tensor<3x4x5x7xi32>) { + // CHECK-SAME: ins(%[[COLLAPSE_0]] : tensor<3x7xi32>) outs(%[[EMPTY_0]] : tensor<3x4x5x7xi32>) { // CHECK: ^bb0(%[[IN:.+]]: i32, %[[OUT:.+]]: i32): // CHECK: linalg.yield %[[IN]] : i32 - %resize = "tosa.resize"(%arg0) {mode = "BILINEAR", scale = array, offset = array, border = array} : (tensor<3x1x1x7xi8>) -> tensor<3x4x5x7xi32> + %scale = tosa.const_shape { value = dense<[2, 1, 3, 1]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %resize = tosa.resize %arg0, %scale, %offset, %border {mode = "BILINEAR"} : (tensor<3x1x1x7xi8>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<3x4x5x7xi32> - // CHECK: return %[[BROADCAST]] return %resize : tensor<3x4x5x7xi32> } @@ -102,7 +122,7 @@ func.func @unary_resize_bilinear_i32(%arg0 : tensor<3x1x1x7xi8>) -> tensor<3x1x1 // CHECK: %[[COLLAPSE:.+]] = tensor.collapse_shape %arg0 // CHECK-SAME{literal}: [[0], [1, 2, 3]] : tensor<3x1x1x7xi8> into tensor<3x7xi8> // CHECK: %[[EMPTY:.+]] = tensor.empty() : tensor<3x7xi32> - // CHECK: %[[GENERIC:.+]] = linalg.generic + // CHECK: %[[GENERIC:.+]] = linalg.generic // CHECK-SAME: indexing_maps = [#map, #map] // CHECK-SAME: iterator_types = ["parallel", "parallel"]} // CHECK-SAME: ins(%[[COLLAPSE]] : tensor<3x7xi8>) outs(%[[EMPTY]] : tensor<3x7xi32>) { @@ -111,12 +131,15 @@ func.func @unary_resize_bilinear_i32(%arg0 : tensor<3x1x1x7xi8>) -> tensor<3x1x1 // CHECK-DAG: %[[C2:.+]] = arith.constant 2 : i32 // CHECK: %[[MUL0:.+]] = arith.muli %[[EXT]], %[[C2]] : i32 // CHECK-DAG: %[[C1:.+]] = arith.constant 2 : i32 - // CHECK: %4 = arith.muli %3, %[[C1]] : i32 - // CHECK: linalg.yield %4 : i32 + // CHECK: %7 = arith.muli %6, %[[C1]] : i32 + // CHECK: linalg.yield %7 : i32 // CHECK: } -> tensor<3x7xi32> // CHECK: %[[EXPAND:.+]] = tensor.expand_shape %[[GENERIC:.+]] // CHECK-SAME{literal} [[0], [1, 2, 3]] : tensor<3x7xi32> into tensor<3x1x1x7xi32> - %resize = "tosa.resize"(%arg0) {mode = "BILINEAR", scale = array, offset = array, border = array} : (tensor<3x1x1x7xi8>) -> tensor<3x1x1x7xi32> + %scale = tosa.const_shape { value = dense<[2, 1, 2, 1]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %resize = tosa.resize %arg0, %scale, %offset, %border {mode = "BILINEAR"} : (tensor<3x1x1x7xi8>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<3x1x1x7xi32> // CHECK: return %[[EXPAND]] return %resize : tensor<3x1x1x7xi32> @@ -184,7 +207,10 @@ func.func @resize_nearest_int(%arg0: tensor<1x15x13x1xi8>) -> () { // CHECK: linalg.yield %[[EXTRACT]] // Round to the nearest index. - %0 = "tosa.resize"(%arg0) {mode = "NEAREST_NEIGHBOR", scale = array, offset = array, border = array} : (tensor<1x15x13x1xi8>) -> tensor<1x23x179x1xi8> + %scale = tosa.const_shape { value = dense<[11, 7, 89, 6]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %0 = tosa.resize %arg0, %scale, %offset, %border {mode = "NEAREST_NEIGHBOR"} : (tensor<1x15x13x1xi8>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<1x23x179x1xi8> return } @@ -193,7 +219,7 @@ func.func @resize_nearest_int(%arg0: tensor<1x15x13x1xi8>) -> () { // CHECK-LABEL: @resize_bilinear_int // CHECK-SAME: (%[[ARG0:[0-9a-zA-Z_]*]]: func.func @resize_bilinear_int(%arg0: tensor<1x19x20x1xi8>) { - // CHECK: %[[INIT:.+]] = tensor.empty() : tensor<1x304x320x1xi48> + // CHECK: %[[INIT:.+]] = tensor.empty() : tensor<1x289x305x1xi48> // CHECK: %[[GENERIC:.+]] = linalg.generic // CHECK: %[[IDX_0:.+]] = linalg.index 0 // CHECK: %[[IDX_1:.+]] = linalg.index 1 @@ -285,7 +311,10 @@ func.func @resize_bilinear_int(%arg0: tensor<1x19x20x1xi8>) { // CHECK: linalg.yield %[[RESULT]] // Round to the nearest index. - %0 = "tosa.resize"(%arg0) {mode = "BILINEAR", scale = array, offset = array, border = array} : (tensor<1x19x20x1xi8>) -> tensor<1x304x320x1xi48> + %scale = tosa.const_shape { value = dense<[16, 1, 16, 1]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %0 = tosa.resize %arg0, %scale, %offset, %border {mode = "BILINEAR"} : (tensor<1x19x20x1xi8>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<1x289x305x1xi48> return } @@ -349,7 +378,10 @@ func.func @resize_nearest_fp32(%input: tensor<1x50x48x1xf32>) -> () { // CHECK: %[[EXTRACT:.+]] = tensor.extract %arg0[%[[IDX0]], %[[IDY]], %[[IDX]], %[[IDX3]]] // CHECK: linalg.yield %[[EXTRACT]] - %output = "tosa.resize"(%input) {mode = "NEAREST_NEIGHBOR", scale = array, offset = array, border = array} : (tensor<1x50x48x1xf32>) -> tensor<1x1600x1536x1xf32> + %scale = tosa.const_shape { value = dense<[64, 2, 64, 2]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<[-31, -31]> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<[31, 31]> : tensor<2xindex> } : () -> !tosa.shape<2> + %output = tosa.resize %input, %scale, %offset, %border {mode = "NEAREST_NEIGHBOR"} : (tensor<1x50x48x1xf32>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<1x1600x1536x1xf32> return } @@ -357,7 +389,7 @@ func.func @resize_nearest_fp32(%input: tensor<1x50x48x1xf32>) -> () { // CHECK-LABEL: @resize_bilinear_fp func.func @resize_bilinear_fp(%input: tensor<1x23x24x1xf32>) -> () { - // CHECK: %[[INIT:.+]] = tensor.empty() : tensor<1x92x96x1xf32> + // CHECK: %[[INIT:.+]] = tensor.empty() : tensor<1x89x93x1xf32> // CHECK: %[[GENERIC:.+]] = linalg.generic // CHECK: %[[IDX_0:.+]] = linalg.index 0 // CHECK: %[[IDX_1:.+]] = linalg.index 1 @@ -441,7 +473,10 @@ func.func @resize_bilinear_fp(%input: tensor<1x23x24x1xf32>) -> () { // CHECK: linalg.yield %[[RESULT]] // Round by bilinear interpolation - %output = "tosa.resize"(%input) {mode = "BILINEAR", scale = array, offset = array, border = array} : (tensor<1x23x24x1xf32>) -> tensor<1x92x96x1xf32> + %scale = tosa.const_shape { value = dense<[4, 1, 4, 1]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %output = tosa.resize %input, %scale, %offset, %border {mode = "BILINEAR"} : (tensor<1x23x24x1xf32>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<1x89x93x1xf32> return } @@ -455,7 +490,10 @@ func.func @resize_dyn(%input: tensor) -> () { // CHECK: %[[BATCH:.+]] = tensor.dim %arg0, %[[C0]] // CHECK: %[[INIT:.+]] = tensor.empty(%[[BATCH]]) : tensor // CHECK: %[[GENERIC:.+]] = linalg.generic - %output = "tosa.resize"(%input) { scale = array, offset = array, border = array, mode = "BILINEAR" } : (tensor) -> (tensor) + %scale = tosa.const_shape { value = dense<[4, 2, 4, 2]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<[-1, -1]> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<[1, 1]> : tensor<2xindex> } : () -> !tosa.shape<2> + %output = tosa.resize %input, %scale, %offset, %border { mode = "BILINEAR" } : (tensor, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> (tensor) return } @@ -463,14 +501,17 @@ func.func @resize_dyn(%input: tensor) -> () { // CHECK-LABEL: @resize_bilinear_int48 func.func @resize_bilinear_int48(%arg0: tensor<1x19x19x1xi16>) { - %0 = "tosa.resize"(%arg0) {mode = "BILINEAR", scale = array, offset = array, border = array} : (tensor<1x19x19x1xi16>) -> tensor<1x289x289x1xi48> + %scale = tosa.const_shape { value = dense<[16, 1, 16, 1]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %0 = tosa.resize %arg0, %scale, %offset, %border {mode = "BILINEAR"} : (tensor<1x19x19x1xi16>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<1x289x289x1xi48> return } // ----- // CHECK-LABEL: skip_interpolate_bilinear_i8 -func.func @skip_interpolate_bilinear_i8(%arg0 : tensor<3x1x2x7xi8>) -> tensor<3x1x5x7xi32> { +func.func @skip_interpolate_bilinear_i8(%arg0 : tensor<3x1x2x7xi8>) -> tensor<3x1x4x7xi32> { // CHECK: %[[GENERIC:.+]] = linalg.generic // CHECK: %[[BATCH:.+]] = linalg.index 0 // CHECK: %[[CHANNEL:.+]] = linalg.index 3 @@ -486,14 +527,17 @@ func.func @skip_interpolate_bilinear_i8(%arg0 : tensor<3x1x2x7xi8>) -> tensor<3x // CHECK: %[[ADD:.+]] = arith.addi %[[MUL0]], %[[MUL1]] // CHECK: %[[RES:.+]] = arith.muli %[[ADD]], %[[C2]] // CHECK: linalg.yield %[[RES]] - %resize = "tosa.resize"(%arg0) {mode = "BILINEAR", scale = array, offset = array, border = array} : (tensor<3x1x2x7xi8>) -> tensor<3x1x5x7xi32> + %scale = tosa.const_shape { value = dense<[2, 1, 3, 1]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %resize = tosa.resize %arg0, %scale, %offset, %border {mode = "BILINEAR"} : (tensor<3x1x2x7xi8>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<3x1x4x7xi32> // CHECK: return %[[GENERIC]] - return %resize : tensor<3x1x5x7xi32> + return %resize : tensor<3x1x4x7xi32> } // CHECK-LABEL: skip_interpolate_bilinear_f32 -func.func @skip_interpolate_bilinear_f32(%arg0 : tensor<3x1x2x7xf32>) -> tensor<3x1x5x7xf32> { +func.func @skip_interpolate_bilinear_f32(%arg0 : tensor<3x1x2x7xf32>) -> tensor<3x1x4x7xf32> { // CHECK: %[[GENERIC:.+]] = linalg.generic // CHECK: %[[BATCH:.+]] = linalg.index 0 : index // CHECK: %[[CHANNEL:.+]] = linalg.index 3 : index @@ -505,8 +549,11 @@ func.func @skip_interpolate_bilinear_f32(%arg0 : tensor<3x1x2x7xf32>) -> tensor< // CHECK: %[[MUL1:.+]] = arith.mulf %[[EXTRACT1]], %[[DX]] // CHECK: %[[ADD:.+]] = arith.addf %[[MUL0]], %[[MUL1]] // CHECK: linalg.yield %[[ADD]] - %resize = "tosa.resize"(%arg0) {mode = "BILINEAR", scale = array, offset = array, border = array} : (tensor<3x1x2x7xf32>) -> tensor<3x1x5x7xf32> + %scale = tosa.const_shape { value = dense<[2, 1, 3, 1]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %resize = tosa.resize %arg0, %scale, %offset, %border {mode = "BILINEAR"} : (tensor<3x1x2x7xf32>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<3x1x4x7xf32> // CHECK: return %[[GENERIC]] - return %resize : tensor<3x1x5x7xf32> + return %resize : tensor<3x1x4x7xf32> } diff --git a/mlir/test/Dialect/Tosa/canonicalize.mlir b/mlir/test/Dialect/Tosa/canonicalize.mlir index c160fca2cbc1f..5e02e2de5a64b 100644 --- a/mlir/test/Dialect/Tosa/canonicalize.mlir +++ b/mlir/test/Dialect/Tosa/canonicalize.mlir @@ -976,7 +976,10 @@ func.func @single_bit_reshape() -> tensor<1xi1> { // CHECK-LABEL: @fold_resize_nearest func.func @fold_resize_nearest(%arg0 : tensor<1x15x13x1xi8>) -> tensor<1x15x13x1xi8> { // CHECK: return %arg0 - %resize = tosa.resize %arg0 {mode = "NEAREST_NEIGHBOR" , scale = array, offset = array, border = array} : (tensor<1x15x13x1xi8>) -> tensor<1x15x13x1xi8> + %scale = tosa.const_shape { value = dense<[2, 2, 1, 1]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %resize = tosa.resize %arg0, %scale, %offset, %border {mode = "NEAREST_NEIGHBOR"} : (tensor<1x15x13x1xi8>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<1x15x13x1xi8> return %resize : tensor<1x15x13x1xi8> } @@ -985,7 +988,10 @@ func.func @fold_resize_nearest(%arg0 : tensor<1x15x13x1xi8>) -> tensor<1x15x13x1 // CHECK-LABEL: @fold_resize_bilinear func.func @fold_resize_bilinear(%arg0 : tensor<1x15x13x1xi8>) -> tensor<1x15x13x1xi8> { // CHECK: return %arg0 - %resize = tosa.resize %arg0 {mode = "BILINEAR" , scale = array, offset = array, border = array} : (tensor<1x15x13x1xi8>) -> tensor<1x15x13x1xi8> + %scale = tosa.const_shape { value = dense<[2, 2, 1, 1]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %resize = tosa.resize %arg0, %scale, %offset, %border {mode = "BILINEAR"} : (tensor<1x15x13x1xi8>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<1x15x13x1xi8> return %resize : tensor<1x15x13x1xi8> } diff --git a/mlir/test/Dialect/Tosa/invalid.mlir b/mlir/test/Dialect/Tosa/invalid.mlir index f325f7acb34df..4aa80f855953b 100644 --- a/mlir/test/Dialect/Tosa/invalid.mlir +++ b/mlir/test/Dialect/Tosa/invalid.mlir @@ -1162,3 +1162,88 @@ func.func @test_mul_non_broadcast(%arg0: tensor<13x21x2xf32>, %arg1: tensor<3x1x %0 = tosa.mul %arg0, %arg1, %shift : (tensor<13x21x2xf32>, tensor<3x1x3xf32>, tensor<1xi8>) -> tensor<13x21x3xf32> return %0 : tensor<13x21x3xf32> } + +// ----- +// CHECK-LABEL: test_resize_invalid_scale_values +func.func @test_resize_invalid_scale_values(%arg0: tensor<1x8x8x8xf32>) -> tensor { + %scale = tosa.const_shape { value = dense<[2, 0, -1, 2]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + // expected-error@+1 {{'tosa.resize' op expect all scale values to be > 0, got 2, 0, -1, 2}} + %1 = tosa.resize %arg0, %scale, %offset, %border { mode = "BILINEAR" } : (tensor<1x8x8x8xf32>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor + return %1 : tensor +} + +// ----- + +// CHECK-LABEL: test_resize_invalid_wholly_divisible_height +func.func @test_resize_invalid_wholly_divisible_height(%arg0: tensor<1x8x8x8xf32>) -> tensor<1x8x8x8xf32> { + %scale = tosa.const_shape { value = dense<[1, 3, 1, 1]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + // expected-error@+1 {{'tosa.resize' op expected (input_height - 1) * scale_y_n - offset_y + border_y to be wholly divisible by scale_y_d, got ((8 - 1) * 1 - 0 + 0) / 3}} + %1 = tosa.resize %arg0, %scale, %offset, %border { mode = "BILINEAR" } : (tensor<1x8x8x8xf32>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<1x8x8x8xf32> + return %1 : tensor<1x8x8x8xf32> +} + +// ----- + +// CHECK-LABEL: test_resize_invalid_output_height +func.func @test_resize_invalid_output_height(%arg0: tensor<1x8x8x8xf32>) -> tensor<1x9x8x8xf32> { + %scale = tosa.const_shape { value = dense<[2, 1, 1, 1]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + // expected-error@+1 {{'tosa.resize' op calculated output height did not match expected: calculated=15, expected=9}} + %1 = tosa.resize %arg0, %scale, %offset, %border { mode = "BILINEAR" } : (tensor<1x8x8x8xf32>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<1x9x8x8xf32> + return %1 : tensor<1x9x8x8xf32> +} + +// ----- + +// CHECK-LABEL: test_resize_invalid_wholly_divisible_width +func.func @test_resize_invalid_wholly_divisible_width(%arg0: tensor<1x8x8x8xf32>) -> tensor<1x8x8x8xf32> { + %scale = tosa.const_shape { value = dense<[1, 1, 1, 3]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + // expected-error@+1 {{'tosa.resize' op expected (input_width - 1) * scale_x_n - offset_x + border_x to be wholly divisible by scale_x_d, got ((8 - 1) * 1 - 0 + 0) / 3}} + %1 = tosa.resize %arg0, %scale, %offset, %border { mode = "BILINEAR" } : (tensor<1x8x8x8xf32>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<1x8x8x8xf32> + return %1 : tensor<1x8x8x8xf32> +} + +// ----- + +// CHECK-LABEL: test_resize_invalid_output_width +func.func @test_resize_invalid_output_width(%arg0: tensor<1x8x8x8xf32>) -> tensor<1x8x9x8xf32> { + %scale = tosa.const_shape { value = dense<[1, 1, 2, 1]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + // expected-error@+1 {{'tosa.resize' op calculated output width did not match expected: calculated=15, expected=9}} + %1 = tosa.resize %arg0, %scale, %offset, %border { mode = "BILINEAR" } : (tensor<1x8x8x8xf32>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<1x8x9x8xf32> + return %1 : tensor<1x8x9x8xf32> +} + +// ----- + +// CHECK-LABEL: broadcast_resize_nearest_f32 +func.func @broadcast_resize_nearest_f32(%arg0 : tensor<3x1x1x7xf32>) -> tensor<3x1x5x7xf32> { + %scale = tosa.const_shape { value = dense<[2, 1, 3, 1]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + // expected-error@+1 {{'tosa.resize' op calculated output width did not match expected: calculated=1, expected=5}} + %resize = tosa.resize %arg0, %scale, %offset, %border {mode = "NEAREST_NEIGHBOR"} : (tensor<3x1x1x7xf32>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<3x1x5x7xf32> + + return %resize : tensor<3x1x5x7xf32> +} + +// ----- + +// CHECK-LABEL: broadcast_resize_bilinear_i8 +func.func @broadcast_resize_bilinear_i8(%arg0 : tensor<3x1x1x7xi8>) -> tensor<3x4x5x7xi32> { + %scale = tosa.const_shape { value = dense<[2, 1, 3, 1]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + // expected-error@+1 {{'tosa.resize' op calculated output height did not match expected: calculated=1, expected=4}} + %resize = tosa.resize %arg0, %scale, %offset, %border {mode = "BILINEAR"} : (tensor<3x1x1x7xi8>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<3x4x5x7xi32> + + return %resize : tensor<3x4x5x7xi32> +} diff --git a/mlir/test/Dialect/Tosa/level_check.mlir b/mlir/test/Dialect/Tosa/level_check.mlir index a7f76f2d0fa64..6f49195d30e97 100644 --- a/mlir/test/Dialect/Tosa/level_check.mlir +++ b/mlir/test/Dialect/Tosa/level_check.mlir @@ -676,20 +676,26 @@ func.func @test_transpose_conv2d_stride_x(%arg0: tensor<1x32x32x8xf32>, %arg1: t // ----- -func.func @test_resize_scale_y(%arg0: tensor<1x32x32x8xf32>) -> tensor<1x64x64x8xf32> { +func.func @test_resize_scale_y(%arg0: tensor<1x32x32x8xf32>) -> tensor<1x7970x64x8xf32> { + %scale = tosa.const_shape { value = dense<[257, 1, 4, 2]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<[-1, -1]> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<[1, 1]> : tensor<2xindex> } : () -> !tosa.shape<2> // expected-error@+1 {{'tosa.resize' op failed level check: scale_y_n/scale_y_d <= MAX_SCALE}} - %1 = "tosa.resize"(%arg0) { scale = array, offset = array, border = array, mode = "BILINEAR"} : - (tensor<1x32x32x8xf32>) -> tensor<1x64x64x8xf32> - return %1 : tensor<1x64x64x8xf32> + %1 = tosa.resize %arg0, %scale, %offset, %border {mode = "BILINEAR"} : + (tensor<1x32x32x8xf32>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<1x7970x64x8xf32> + return %1 : tensor<1x7970x64x8xf32> } // ----- -func.func @test_resize_scale_x(%arg0: tensor<1x32x32x8xf32>) -> tensor<1x64x64x8xf32> { +func.func @test_resize_scale_x(%arg0: tensor<1x32x32x8xf32>) -> tensor<1x64x7970x8xf32> { + %scale = tosa.const_shape { value = dense<[4, 2, 257, 1]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<[-1, -1]> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<[1, 1]> : tensor<2xindex> } : () -> !tosa.shape<2> // expected-error@+1 {{'tosa.resize' op failed level check: scale_x_n/scale_x_d <= MAX_SCALE}} - %1 = "tosa.resize"(%arg0) { scale = array, offset = array, border = array, mode = "BILINEAR"} : - (tensor<1x32x32x8xf32>) -> tensor<1x64x64x8xf32> - return %1 : tensor<1x64x64x8xf32> + %1 = tosa.resize %arg0, %scale, %offset, %border {mode = "BILINEAR"} : + (tensor<1x32x32x8xf32>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<1x64x7970x8xf32> + return %1 : tensor<1x64x7970x8xf32> } // ----- diff --git a/mlir/test/Dialect/Tosa/ops.mlir b/mlir/test/Dialect/Tosa/ops.mlir index d7f7afa6ce937..860883e135750 100644 --- a/mlir/test/Dialect/Tosa/ops.mlir +++ b/mlir/test/Dialect/Tosa/ops.mlir @@ -670,7 +670,10 @@ func.func @test_scatter(%arg0: tensor<13x21x3xf32>, %arg1: tensor<13x26xi32>, %a // ----- // CHECK-LABEL: resize func.func @test_resize(%arg0: tensor<1x32x32x8xf32>) -> tensor<1x64x64x8xf32> { - %1 = tosa.resize %arg0 { scale = array, offset = array, border = array, mode = "BILINEAR" } : (tensor<1x32x32x8xf32>) -> tensor<1x64x64x8xf32> + %scale = tosa.const_shape { value = dense<[4, 2, 4, 2]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<[-1, -1]> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<[1, 1]> : tensor<2xindex> } : () -> !tosa.shape<2> + %1 = tosa.resize %arg0, %scale, %offset, %border { mode = "BILINEAR" } : (tensor<1x32x32x8xf32>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<1x64x64x8xf32> return %1 : tensor<1x64x64x8xf32> } diff --git a/mlir/test/Dialect/Tosa/tosa-infer-shapes.mlir b/mlir/test/Dialect/Tosa/tosa-infer-shapes.mlir index b8e109bd79dd9..55788183e0ecb 100644 --- a/mlir/test/Dialect/Tosa/tosa-infer-shapes.mlir +++ b/mlir/test/Dialect/Tosa/tosa-infer-shapes.mlir @@ -997,8 +997,11 @@ func.func @transpose_conv2d_strided(%arg0: tensor<1x5x7x1xf32>, %arg1: tensor<1x // CHECK-LABEL: @resize_int_horizontal func.func @resize_int_horizontal(%arg0: tensor<1x15x13x1xi8>) { + %scale = tosa.const_shape { value = dense<[11, 7, 89, 6]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> // CHECK: -> tensor<1x23x179x1xi8> - %0 = tosa.resize %arg0 {mode = "NEAREST_NEIGHBOR", scale = array, offset = array, border = array} : (tensor<1x15x13x1xi8>) -> tensor + %0 = tosa.resize %arg0, %scale, %offset, %border {mode = "NEAREST_NEIGHBOR"} : (tensor<1x15x13x1xi8>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor return } @@ -1006,8 +1009,11 @@ func.func @resize_int_horizontal(%arg0: tensor<1x15x13x1xi8>) { // CHECK-LABEL: @resize_int_vertical func.func @resize_int_vertical(%arg0: tensor<1x49x42x1xi16>) { + %scale = tosa.const_shape { value = dense<[37, 16, 219, 41]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> // CHECK: -> tensor<1x112x220x1xi16> - %0 = tosa.resize %arg0 {mode = "NEAREST_NEIGHBOR", scale = array, offset = array, border = array} : (tensor<1x49x42x1xi16>) -> tensor + %0 = tosa.resize %arg0, %scale, %offset, %border {mode = "NEAREST_NEIGHBOR"} : (tensor<1x49x42x1xi16>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor return } @@ -1015,8 +1021,11 @@ func.func @resize_int_vertical(%arg0: tensor<1x49x42x1xi16>) { // CHECK-LABEL: @resize_int_power_of_two_upscale func.func @resize_int_power_of_two_upscale(%arg0: tensor<1x23x19x1xi8>) { + %scale = tosa.const_shape { value = dense<[16, 1, 16, 1]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> // CHECK: -> tensor<1x353x289x1xi32> - %0 = tosa.resize %arg0 {mode = "BILINEAR", scale = array, offset = array, border = array} : (tensor<1x23x19x1xi8>) -> tensor + %0 = tosa.resize %arg0, %scale, %offset, %border {mode = "BILINEAR"} : (tensor<1x23x19x1xi8>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor return } @@ -1024,24 +1033,33 @@ func.func @resize_int_power_of_two_upscale(%arg0: tensor<1x23x19x1xi8>) { // CHECK-LABEL: @resize_int_power_of_two_upscale_offsetted func.func @resize_int_power_of_two_upscale_offsetted(%arg0: tensor<1x41x26x1xi16>) { + %scale = tosa.const_shape { value = dense<[16, 2, 16, 2]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<[-7, -7]> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<[7, 7]> : tensor<2xindex> } : () -> !tosa.shape<2> // CHECK: -> tensor<1x328x208x1xi48> - %0 = tosa.resize %arg0 {mode = "BILINEAR", scale = array, offset = array, border = array} : (tensor<1x41x26x1xi16>) -> tensor + %0 = tosa.resize %arg0, %scale, %offset, %border {mode = "BILINEAR"} : (tensor<1x41x26x1xi16>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor return } // ----- // CHECK-LABEL: @resize_fp_horizontal func.func @resize_fp_horizontal(%arg0: tensor<1x50x48x1xf32>) { + %scale = tosa.const_shape { value = dense<[15, 7, 84, 47]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> // CHECK: -> tensor<1x106x85x1xf32> - %0 = tosa.resize %arg0 {mode = "BILINEAR", scale = array, offset = array, border = array} : (tensor<1x50x48x1xf32>) -> tensor + %0 = tosa.resize %arg0, %scale, %offset, %border {mode = "BILINEAR"} : (tensor<1x50x48x1xf32>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor return } // ----- // CHECK-LABEL: @resize_fp_vertical func.func @resize_fp_vertical(%arg0: tensor<1x50x48x1xf32>) { + %scale = tosa.const_shape { value = dense<[127, 49, 12, 47]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> // CHECK: -> tensor<1x128x13x1xf32> - %0 = tosa.resize %arg0 {mode = "NEAREST_NEIGHBOR", scale = array, offset = array, border = array} : (tensor<1x50x48x1xf32>) -> tensor + %0 = tosa.resize %arg0, %scale, %offset, %border {mode = "NEAREST_NEIGHBOR"} : (tensor<1x50x48x1xf32>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor return } @@ -1049,8 +1067,11 @@ func.func @resize_fp_vertical(%arg0: tensor<1x50x48x1xf32>) { // CHECK-LABEL: @resize_fp_power_of_two_upscale func.func @resize_fp_power_of_two_upscale(%arg0: tensor<1x23x23x1xf32>) { + %scale = tosa.const_shape { value = dense<[4, 1, 4, 1]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<0> : tensor<2xindex> } : () -> !tosa.shape<2> // CHECK: -> tensor<1x89x89x1xf32> - %0 = tosa.resize %arg0 {mode = "BILINEAR", scale = array, offset = array, border = array} : (tensor<1x23x23x1xf32>) -> tensor + %0 = tosa.resize %arg0, %scale, %offset, %border {mode = "BILINEAR"} : (tensor<1x23x23x1xf32>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor return } @@ -1058,8 +1079,11 @@ func.func @resize_fp_power_of_two_upscale(%arg0: tensor<1x23x23x1xf32>) { // CHECK-LABEL: @resize_fp_power_of_two_upscale_offsetted func.func @resize_fp_power_of_two_upscale_offsetted(%arg0: tensor<1x50x48x1xf32>) { + %scale = tosa.const_shape { value = dense<[64, 2, 64, 2]> : tensor<4xindex> } : () -> !tosa.shape<4> + %offset = tosa.const_shape { value = dense<[-31, -31]> : tensor<2xindex> } : () -> !tosa.shape<2> + %border = tosa.const_shape { value = dense<[31, 31]> : tensor<2xindex> } : () -> !tosa.shape<2> // CHECK: -> tensor<1x1600x1536x1xf32> - %0 = tosa.resize %arg0 {mode = "NEAREST_NEIGHBOR", scale = array, offset = array, border = array} : (tensor<1x50x48x1xf32>) -> tensor + %0 = tosa.resize %arg0, %scale, %offset, %border {mode = "NEAREST_NEIGHBOR"} : (tensor<1x50x48x1xf32>, !tosa.shape<4>, !tosa.shape<2>, !tosa.shape<2>) -> tensor return }