提交 4ff58c26 编写于 作者: R Robert Suderman 提交者: TensorFlower Gardener

Cleanup tfl.resize lowering to tosa.resize

Existing lowering missed cases where the width/height of
the input or output are 1. These cases were difficult to
address in the current implementation so they were cleaned
up. Then power-of-2 specific code was removed as it was
easier to just depend on GCD to do the right thing.

PiperOrigin-RevId: 480936509
上级 79584a00
......@@ -1739,9 +1739,19 @@ func.func @test_leaky_relu_qi8(%arg0: tensor<14x19x!quant.uniform<i8:f32, 0.0155
// -----
// CHECK-LABEL: test_resize_bilinear_qi8
// CHECK-DAG: %[[VAR1:.*]] = "tosa.resize"(%arg0) {border = [7, 7], mode = "BILINEAR", offset = [-7, -7], scale = [16, 2, 16, 2]}
// CHECK-DAG: %[[VAR1:.*]] = "tosa.resize"(%arg0) {border = [14, 14], mode = "BILINEAR", offset = [0, 0], scale = [16, 2, 16, 2]}
// CHECK: %[[VAR2:.*]] = "tosa.rescale"(%[[VAR1]]) {double_round = false, input_zp = 0 : i32, multiplier = [1073741824 : i32], output_zp = 0 : i32, per_channel = false, scale32 = true, shift = [38 : i32]}
func.func @test_resize_bilinear_qi8(%arg0: tensor<1x80x80x2x!quant.uniform<i8:f32, 0.42546585202217102>>) -> tensor<1x640x640x2x!quant.uniform<i8:f32, 0.42546585202217102>> {
%0 = "tfl.pseudo_const"() {value = dense<640> : tensor<2xi32>} : () -> tensor<2xi32>
%1 = "tfl.resize_bilinear"(%arg0, %0) {align_corners = false, half_pixel_centers = false} : (tensor<1x80x80x2x!quant.uniform<i8:f32, 0.42546585202217102>>, tensor<2xi32>) -> tensor<1x640x640x2x!quant.uniform<i8:f32, 0.42546585202217102>>
func.return %1 : tensor<1x640x640x2x!quant.uniform<i8:f32, 0.42546585202217102>>
}
// -----
// CHECK-LABEL: test_resize_bilinear_half_qi8
// CHECK-DAG: %[[VAR1:.*]] = "tosa.resize"(%arg0) {border = [7, 7], mode = "BILINEAR", offset = [-7, -7], scale = [16, 2, 16, 2]}
func.func @test_resize_bilinear_half_qi8(%arg0: tensor<1x80x80x2x!quant.uniform<i8:f32, 0.42546585202217102>>) -> tensor<1x640x640x2x!quant.uniform<i8:f32, 0.42546585202217102>> {
%0 = "tfl.pseudo_const"() {value = dense<640> : tensor<2xi32>} : () -> tensor<2xi32>
%1 = "tfl.resize_bilinear"(%arg0, %0) {align_corners = false, half_pixel_centers = true} : (tensor<1x80x80x2x!quant.uniform<i8:f32, 0.42546585202217102>>, tensor<2xi32>) -> tensor<1x640x640x2x!quant.uniform<i8:f32, 0.42546585202217102>>
func.return %1 : tensor<1x640x640x2x!quant.uniform<i8:f32, 0.42546585202217102>>
......@@ -1749,6 +1759,67 @@ func.func @test_resize_bilinear_qi8(%arg0: tensor<1x80x80x2x!quant.uniform<i8:f3
// -----
// CHECK-LABEL: test_resize_bilinear_align_qi8
// CHECK-DAG: %[[VAR1:.*]] = "tosa.resize"(%arg0) {border = [0, 0], mode = "BILINEAR", offset = [0, 0], scale = [1278, 158, 1278, 158]}
func.func @test_resize_bilinear_align_qi8(%arg0: tensor<1x80x80x2x!quant.uniform<i8:f32, 0.42546585202217102>>) -> tensor<1x640x640x2x!quant.uniform<i8:f32, 0.42546585202217102>> {
%0 = "tfl.pseudo_const"() {value = dense<640> : tensor<2xi32>} : () -> tensor<2xi32>
%1 = "tfl.resize_bilinear"(%arg0, %0) {align_corners = true, half_pixel_centers = false} : (tensor<1x80x80x2x!quant.uniform<i8:f32, 0.42546585202217102>>, tensor<2xi32>) -> tensor<1x640x640x2x!quant.uniform<i8:f32, 0.42546585202217102>>
func.return %1 : tensor<1x640x640x2x!quant.uniform<i8:f32, 0.42546585202217102>>
}
// -----
// CHECK-LABEL: test_resize_bilinear_align_half_qi8
// CHECK-DAG: %[[VAR1:.*]] = "tosa.resize"(%arg0) {border = [-560, -560], mode = "BILINEAR", offset = [-560, -560], scale = [1278, 158, 1278, 158]}
func.func @test_resize_bilinear_align_half_qi8(%arg0: tensor<1x80x80x2x!quant.uniform<i8:f32, 0.42546585202217102>>) -> tensor<1x640x640x2x!quant.uniform<i8:f32, 0.42546585202217102>> {
%0 = "tfl.pseudo_const"() {value = dense<640> : tensor<2xi32>} : () -> tensor<2xi32>
%1 = "tfl.resize_bilinear"(%arg0, %0) {align_corners = true, half_pixel_centers = true} : (tensor<1x80x80x2x!quant.uniform<i8:f32, 0.42546585202217102>>, tensor<2xi32>) -> tensor<1x640x640x2x!quant.uniform<i8:f32, 0.42546585202217102>>
func.return %1 : tensor<1x640x640x2x!quant.uniform<i8:f32, 0.42546585202217102>>
}
// -----
// CHECK-LABEL: test_resize_nearest_qi8
// CHECK: %[[VAR1:.*]] = "tosa.resize"(%arg0) {border = [14, 14], mode = "NEAREST_NEIGHBOR", offset = [0, 0], scale = [16, 2, 16, 2]}
func.func @test_resize_nearest_qi8(%arg0: tensor<1x80x80x2x!quant.uniform<i8:f32, 0.42546585202217102>>) -> tensor<1x640x640x2x!quant.uniform<i8:f32, 0.42546585202217102>> {
%0 = "tfl.pseudo_const"() {value = dense<640> : tensor<2xi32>} : () -> tensor<2xi32>
%1 = "tfl.resize_nearest_neighbor"(%arg0, %0) {align_corners = false, half_pixel_centers = false} : (tensor<1x80x80x2x!quant.uniform<i8:f32, 0.42546585202217102>>, tensor<2xi32>) -> tensor<1x640x640x2x!quant.uniform<i8:f32, 0.42546585202217102>>
func.return %1 : tensor<1x640x640x2x!quant.uniform<i8:f32, 0.42546585202217102>>
}
// -----
// CHECK-LABEL: test_resize_nearest_half_qi8
// CHECK: %[[VAR1:.*]] = "tosa.resize"(%arg0) {border = [15, 15], mode = "NEAREST_NEIGHBOR", offset = [1, 1], scale = [16, 2, 16, 2]}
func.func @test_resize_nearest_half_qi8(%arg0: tensor<1x80x80x2x!quant.uniform<i8:f32, 0.42546585202217102>>) -> tensor<1x640x640x2x!quant.uniform<i8:f32, 0.42546585202217102>> {
%0 = "tfl.pseudo_const"() {value = dense<640> : tensor<2xi32>} : () -> tensor<2xi32>
%1 = "tfl.resize_nearest_neighbor"(%arg0, %0) {align_corners = false, half_pixel_centers = true} : (tensor<1x80x80x2x!quant.uniform<i8:f32, 0.42546585202217102>>, tensor<2xi32>) -> tensor<1x640x640x2x!quant.uniform<i8:f32, 0.42546585202217102>>
func.return %1 : tensor<1x640x640x2x!quant.uniform<i8:f32, 0.42546585202217102>>
}
// -----
// CHECK-LABEL: test_resize_nearest_align_qi8
// CHECK: %[[VAR1:.*]] = "tosa.resize"(%arg0) {border = [639, 639], mode = "NEAREST_NEIGHBOR", offset = [639, 639], scale = [1278, 158, 1278, 158]}
func.func @test_resize_nearest_align_qi8(%arg0: tensor<1x80x80x2x!quant.uniform<i8:f32, 0.42546585202217102>>) -> tensor<1x640x640x2x!quant.uniform<i8:f32, 0.42546585202217102>> {
%0 = "tfl.pseudo_const"() {value = dense<640> : tensor<2xi32>} : () -> tensor<2xi32>
%1 = "tfl.resize_nearest_neighbor"(%arg0, %0) {align_corners = true, half_pixel_centers = false} : (tensor<1x80x80x2x!quant.uniform<i8:f32, 0.42546585202217102>>, tensor<2xi32>) -> tensor<1x640x640x2x!quant.uniform<i8:f32, 0.42546585202217102>>
func.return %1 : tensor<1x640x640x2x!quant.uniform<i8:f32, 0.42546585202217102>>
}
// -----
// CHECK-LABEL: test_resize_nearest_align_half_qi8
// CHECK: %[[VAR1:.*]] = "tosa.resize"(%arg0) {border = [718, 718], mode = "NEAREST_NEIGHBOR", offset = [718, 718], scale = [1278, 158, 1278, 158]}
func.func @test_resize_nearest_align_half_qi8(%arg0: tensor<1x80x80x2x!quant.uniform<i8:f32, 0.42546585202217102>>) -> tensor<1x640x640x2x!quant.uniform<i8:f32, 0.42546585202217102>> {
%0 = "tfl.pseudo_const"() {value = dense<640> : tensor<2xi32>} : () -> tensor<2xi32>
%1 = "tfl.resize_nearest_neighbor"(%arg0, %0) {align_corners = true, half_pixel_centers = true} : (tensor<1x80x80x2x!quant.uniform<i8:f32, 0.42546585202217102>>, tensor<2xi32>) -> tensor<1x640x640x2x!quant.uniform<i8:f32, 0.42546585202217102>>
func.return %1 : tensor<1x640x640x2x!quant.uniform<i8:f32, 0.42546585202217102>>
}
// -----
// CHECK-LABEL: test_fullyconnected_qi8
// CHECK-DAG: %[[VAR0:.*]] = "tosa.const"() {value = dense<[1, 0]> : tensor<2xi32>}
// CHECK-DAG: %[[VAR1:.*]] = "tosa.const"() {value = dense<0> : tensor<28xi32>}
......
......@@ -33,7 +33,6 @@ limitations under the License.
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/FormatVariadic.h"
#include "mlir/Dialect/Quant/QuantTypes.h" // from @llvm-project
#include "mlir/Dialect/Tensor/IR/Tensor.h" // from @llvm-project
#include "mlir/Dialect/Tosa/IR/TosaOps.h" // from @llvm-project
#include "mlir/IR/BuiltinTypes.h" // from @llvm-project
#include "mlir/IR/Matchers.h" // from @llvm-project
......@@ -2973,6 +2972,8 @@ llvm::Optional<Value> convertResizeOp(PatternRewriter& rewriter, Operation* op,
Value input_value, StringRef mode,
bool align_corners,
bool half_pixel_centers) {
const bool is_bilinear = mode == "BILINEAR";
const bool is_nearest = mode == "NEAREST_NEIGHBOR";
RankedTensorType input_type =
input_value.getType().dyn_cast<RankedTensorType>();
if (!input_type) return llvm::None;
......@@ -3021,39 +3022,6 @@ llvm::Optional<Value> convertResizeOp(PatternRewriter& rewriter, Operation* op,
size_t output_height = output_shape[1];
size_t output_width = output_shape[2];
// By defining the scaling factor as a ratio of integers so that exact output
// dimensions can be derived from input dimensions without rounding.
//
// a.For power of two upscale [OH, OW] = (1 << k) * [IH, IW],
// sampling range approximately (-0.5, -0.5) to (IH - 0.5, IW - 0.5), set:
//
// scale_y_n = 2 << k, scale_y_d = 2,
// offset_y = -(1 << k) + 1, border_y = (1 << k) - 1
//
// scale_x_n = 2 << k, scale_x_d = 2,
// offset_x = -(1 << k) + 1, border_x = (1 << k) - 1
//
// b.For power of two upscale [OH - 1 ,OW - 1] = (1 << k) * [IH - 1, IW - 1],
// sampling between (0,0) and (IH - 1,IW - 1), set:
//
// scale_y_n = (1 << k), scale_y_d = 1, offset_y = 0, border_y = 0
//
// scale_x_n = (1 << k), scale_x_d = 1, offset_x = 0, border_x = 0
//
// c.For approximate uniform input
// sampling between (0, 0) and (IH - 1, IW - 1) set:
//
// scale_y_n/scale_y_d = (OH - 1)/(IH - 1) as integer ratios
// offset_y = 0, border_y = 0
//
// scale_x_n/scale_x_d = (OW - 1)/(IW - 1) as integer ratios
// offset_x = 0, border_x = 0
int scale_y_n, scale_y_d, scale_x_n, scale_x_d;
int offset_y = 0, offset_x = 0;
int border_y = 0, border_x = 0;
bool uniform_sampling = true;
// The ratio below is a non-zero positive value if this is a power-of-two
// upscaling.
int height_ratio = 0;
......@@ -3072,69 +3040,51 @@ llvm::Optional<Value> convertResizeOp(PatternRewriter& rewriter, Operation* op,
}
}
int height_minus_one_ratio = 0;
if ((output_height - 1) % (input_height - 1) == 0) {
int quotient = (output_height - 1) / (input_height - 1);
if (llvm::isPowerOf2_64(quotient)) {
height_minus_one_ratio = quotient;
// Align corners sets the scaling ratio to (OH - 1)/(IH - 1)
// rather than OH / IH. Similarly for width.
auto normalize = [&](int input, int output, int& n, int& d, int& offset,
int& border) {
// Dimension is length 1, we are just sampling from one value.
if (input == 1) {
n = 0;
d = 1;
offset = 0;
border = output - 1;
return;
}
}
int width_minus_one_ratio = 0;
if ((output_width - 1) % (input_width - 1) == 0) {
int quotient = (output_width - 1) / (input_width - 1);
if (llvm::isPowerOf2_64(quotient)) {
width_minus_one_ratio = quotient;
// Apply if aligned and capable to be aligned.
bool apply_aligned = align_corners && (output > 1);
n = apply_aligned ? (output - 1) : output;
d = apply_aligned ? (input - 1) : input;
// Simplify the scalers, make sure they are even values.
int gcd = std::gcd(n, d);
n = 2 * n / gcd;
d = 2 * d / gcd;
// If half pixel centers we need to sample half a pixel inward.
offset = half_pixel_centers ? d / 2 : 0;
// If nearest neighbours we need to guarantee we round up.
if (is_nearest && align_corners) {
offset += n / 2;
}
}
// True if [OH, OW] = (1 << k) * [IH, IW],
if ((height_ratio != 0) && (height_ratio == width_ratio)) {
// Find the shift value 'k' that satisfy '1 << k = OH / IH'.
int k = llvm::Log2_64(height_ratio);
scale_y_n = 2 << k;
scale_y_d = 2;
offset_y = -(1 << k) + 1;
border_y = (1 << k) - 1;
scale_x_n = scale_y_n;
scale_x_d = scale_y_d;
offset_x = offset_y;
border_x = border_y;
uniform_sampling = false;
} else if ((height_minus_one_ratio != 0) &&
(height_minus_one_ratio == width_minus_one_ratio)) {
// True if [OH - 1, OW - 1] = (1 << k) * [IH - 1, IW - 1],
int k = llvm::Log2_64(height_minus_one_ratio);
scale_y_n = 1 << k;
scale_y_d = 1;
scale_x_n = scale_y_n;
scale_x_d = 1;
uniform_sampling = false;
}
if (is_bilinear && half_pixel_centers) {
offset -= n / 2;
}
// Align corners sets the scaling ratio to (OH - 1)/(IH - 1)
// rather than OH / IH. Similarly for width.
if (align_corners || uniform_sampling) {
int gcd_y = std::gcd(input_height - 1, output_height - 1);
int gcd_x = std::gcd(input_width - 1, output_width - 1);
scale_y_n = (output_height - 1) / gcd_y;
scale_y_d = (input_height - 1) / gcd_y;
scale_x_n = (output_width - 1) / gcd_x;
scale_x_d = (input_width - 1) / gcd_x;
offset_y = 0;
offset_x = 0;
border_y = 0;
border_x = 0;
}
if (!align_corners && !half_pixel_centers) {
// Adds a sampling offset.
offset_x += 1;
offset_y += 1;
// Adjust the borders to match an expected output shape.
border_x += 1;
border_y += 1;
}
// We can compute this directly based on previous values.
border = d * (output - 1) - n * (input - 1) + offset;
};
int scale_y_n, scale_y_d, offset_y, border_y;
int scale_x_n, scale_x_d, offset_x, border_x;
normalize(input_height, output_height, scale_y_n, scale_y_d, offset_y,
border_y);
normalize(input_width, output_width, scale_x_n, scale_x_d, offset_x,
border_x);
ArrayAttr scale =
rewriter.getI64ArrayAttr({scale_y_n, scale_y_d, scale_x_n, scale_x_d});
......@@ -3161,7 +3111,7 @@ llvm::Optional<Value> convertResizeOp(PatternRewriter& rewriter, Operation* op,
}
// If quantized bilinear mode, need to lower to RESIZE + RESCALE pair.
if (mode == "BILINEAR") {
if (is_bilinear) {
RankedTensorType output_acc_type;
auto input_element_qtype =
input_type.getElementType().cast<mlir::quant::UniformQuantizedType>();
......@@ -3231,7 +3181,7 @@ llvm::Optional<Value> convertResizeOp(PatternRewriter& rewriter, Operation* op,
is_scale32);
#endif
} else if (mode == "NEAREST_NEIGHBOR") {
} else if (is_nearest) {
auto resize_op = CreateOpAndInfer<tosa::ResizeOp>(
rewriter, op->getLoc(), output_type, input_value, scale, offset,
border, resize_mode);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册