TFL StridedSlice not lowered to TFL Slice if masks are set and strides are 1
@michaelpoluektov Could you please avoid using masks with StridedSlice when strides are all 1. Instead, explicitly use the Slice operator for better performance. 

Thank you! @sushreebarsa Thanks for your reply! I'm working on a TFLite backend, and unfortunately I don't get to pick which operators our clients use.

I hacked together a pass that does this, but I don't see a reason some version of this shouldn't be included upstream. I don't think this is best practice though: I believe the MLIR way of doing this is to define a canonicalization rule for strided slice?

I am willing to submit a PR but I'm not particularly experienced with the TF codebase: where do you have canonicalizations defined? Are you running an MLIR `CanonicalizerPass` anywhere?

```cpp
#include "mlir/Pass/Pass.h"
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
#include "tensorflow/compiler/mlir/lite/ir/tfl_ops.h"

namespace mlir {

namespace {
// Replace TFL StridedSlice with TFL Slice wherever possible.
struct ReplaceStridedSlice
    : public PassWrapper<ReplaceStridedSlice, OperationPass<func::FuncOp>> {
  MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(ReplaceStridedSlice)

  void getDependentDialects(DialectRegistry &registry) const final {
    registry.insert<TFL::TensorFlowLiteDialect>();
  }
  StringRef getArgument() const final { return "xcore-replace-stridedslice"; }
  StringRef getDescription() const final {
    return "Replace TFL StridedSlice with TFL Slice";
  }
  void runOnOperation() override;
};

// Utility to check if a StridedSliceOp can be replaced with a SliceOp
bool canReplaceWithSlice(TFL::StridedSliceOp stridedSliceOp) {
  // Check input shape is static
  auto inputType = stridedSliceOp.getInput().getType().dyn_cast<ShapedType>();
  if (!inputType || !inputType.hasStaticShape()) {
    return false;
  }

  // Check all strides are 1
  DenseIntElementsAttr stridesAttr;
  matchPattern(stridedSliceOp.getStrides(), m_Constant(&stridesAttr));
  if (!stridesAttr)
    return false;
  for (auto stride : stridesAttr)
    if (!stride.isOne())
      return false;

  if (stridedSliceOp.getEllipsisMask() != 0 ||
      stridedSliceOp.getNewAxisMask() != 0) {
    return false;
  }
  return true;
}

struct ReplaceStridedSlicePattern
    : public OpRewritePattern<TFL::StridedSliceOp> {
  using OpRewritePattern<TFL::StridedSliceOp>::OpRewritePattern;

  LogicalResult matchAndRewrite(TFL::StridedSliceOp stridedSliceOp,
                                PatternRewriter &rewriter) const override {

    if (!canReplaceWithSlice(stridedSliceOp))
      return failure();

    auto inputType = stridedSliceOp.getInput().getType().dyn_cast<ShapedType>();
    auto rank =
        stridedSliceOp.getInput().getType().cast<ShapedType>().getRank();

    // Get begin/end attributes
    DenseIntElementsAttr beginAttr;
    matchPattern(stridedSliceOp.getBegin(), m_Constant(&beginAttr));
    if (!beginAttr)
      return failure();
    auto begin = beginAttr.getValues<int32_t>();

    DenseIntElementsAttr endAttr;
    matchPattern(stridedSliceOp.getEnd(), m_Constant(&endAttr));
    if (!beginAttr)
      return failure();
    auto end = endAttr.getValues<int32_t>();

    std::vector<int32_t> newBegin(rank), newSize(rank);

    // If mask is set, set begin and end to 0 and input shape
    // respectively
    // If mask is not set, set begin and end to the actual values
    // If the value is negative, it means size - value
    // StridedSliceOp has an end attribute, SliceOp has size
    // Size is end - begin.
    for (int i = 0; i < rank; i++) {
      if (stridedSliceOp.getBeginMask() & (1 << i))
        newBegin[i] = 0;
      else
        newBegin[i] =
            begin[i] < 0 ? inputType.getShape()[i] + begin[i] : begin[i];
      if (stridedSliceOp.getEndMask() & (1 << i))
        newSize[i] = inputType.getShape()[i] - newBegin[i];
      else {
        auto currentEnd =
            end[i] < 0 ? inputType.getShape()[i] + end[i] : end[i];
        newSize[i] = currentEnd - newBegin[i];
      }
    }
    int64_t shrinkMask = stridedSliceOp.getShrinkAxisMask();
    std::vector<int32_t> newOutputShape;
    for (int i = 0; i < rank; ++i) {
      if (!(shrinkMask & (1 << i))) {         // Check if we should NOT shrink
        newOutputShape.push_back(newSize[i]); // Retain size
      }
    }

    auto shapeAttrType =
        RankedTensorType::get({rank}, rewriter.getIntegerType(32));

    // create constant ops for begin and size
    auto beginConstantOp = rewriter.create<arith::ConstantOp>(
        stridedSliceOp.getLoc(), shapeAttrType,
        DenseIntElementsAttr::get(shapeAttrType, newBegin));
    auto sizeConstantOp = rewriter.create<arith::ConstantOp>(
        stridedSliceOp.getLoc(), shapeAttrType,
        DenseIntElementsAttr::get(shapeAttrType, newSize));

    // RankedTensorType needs int64_t
    std::vector<int64_t> newSize64(newSize.begin(), newSize.end());

    // create sliceOp
    auto sliceOp = rewriter.create<TFL::SliceOp>(
        stridedSliceOp.getLoc(),
        RankedTensorType::get(ArrayRef<int64_t>(newSize64),
                              stridedSliceOp.getType().getElementType()),
        stridedSliceOp.getInput(), beginConstantOp, sizeConstantOp);

    // add reshape if shrinkMask is not 0
    if (shrinkMask != 0) {
      auto newShapeAttrType =
          RankedTensorType::get({static_cast<int64_t>(newOutputShape.size())},
                                rewriter.getIntegerType(32));
      auto shapeConstantOp = rewriter.create<arith::ConstantOp>(
          stridedSliceOp.getLoc(), newShapeAttrType,
          DenseIntElementsAttr::get(newShapeAttrType, newOutputShape));
      std::vector<int64_t> newOutputShape64(newOutputShape.begin(),
                                            newOutputShape.end());
      auto newOutputType = RankedTensorType::get(
          newOutputShape64, sliceOp.getType().getElementType());
      auto reshape = rewriter.create<TFL::ReshapeOp>(
          stridedSliceOp.getLoc(), newOutputType, sliceOp, shapeConstantOp);
      rewriter.replaceOp(stridedSliceOp, reshape.getOutput());
    } else {
      rewriter.replaceOp(stridedSliceOp, sliceOp.getOutput());
    }
    return success();
  }
};

void ReplaceStridedSlice::runOnOperation() {
  auto *ctx = &getContext();
  func::FuncOp func = getOperation();
  RewritePatternSet patterns(ctx);
  patterns.insert<ReplaceStridedSlicePattern>(ctx);
  (void)applyPatternsAndFoldGreedily(func, std::move(patterns));
}
} // namespace

// Creates an instance of the ReplaceStridedSlice pass.
std::unique_ptr<OperationPass<func::FuncOp>> createReplaceStridedSlicePass() {
  return std::make_unique<ReplaceStridedSlice>();
}

static PassRegistration<ReplaceStridedSlice> pass;

} // namespace mlir

``` Hi @michaelpoluektov, https://github.com/tensorflow/tensorflow/tree/master/tensorflow/compiler/mlir/lite this readme is a good high level overview of the passes TF --> TFL goes through, I would say at this point it is outdated but it's a good start to understand the original intention.

Also as noted in the README: https://github.com/tensorflow/tensorflow/blob/master/tensorflow/compiler/mlir/lite/tf_tfl_passes.cc has the actual passes, there are multiple Canonicalization passes, but it's best to review the code to determine where your logic may fit. If you are able to create a PR we will greatly appreciate the help. Thanks! This issue is stale because it has been open for 7 days with no activity. It will be closed if no further activity occurs. Thank you. This issue was closed because it has been inactive for 7 days since being marked as stale. Please reopen if you'd like to work on this further.
The following code:

```python
import numpy as np
import tensorflow as tf

input_shape = (8, 10, 10)
input_data = tf.keras.Input(shape=input_shape, dtype=tf.int8, batch_size=1)
sliced_output = input_data[:, 1:4, 2:]
model = tf.keras.Model(inputs=input_data, outputs=sliced_output)
converter = tf.lite.TFLiteConverter.from_keras_model(model)
def representative_dataset_gen():
    for _ in range(100):
        yield [np.random.uniform(low=-127, high=127, size=input_shape).astype(np.int8)]
converter.representative_dataset = representative_dataset_gen
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
converter.inference_input_type = tf.int8
converter.inference_output_type = tf.int8
tflite_model = converter.convert()
model_name = f'test_slice.tflite'
with open(model_name, 'wb') as f:
    f.write(tflite_model)
```

Will produce a TFLite model with the following MLIR:

```
func.func @main(%arg0: tensor<1x8x10x10xi8> {tf_saved_model.index_path = ["input_1"]}) -> (tensor<1x3x8x10xi8> {tf_saved_model.index_path = ["tf.__operators__.getitem"]}) attributes {tf.entry_function = {inputs = "serving_default_input_1:0", outputs = "PartitionedCall:0"}, tf_saved_model.exported_names = ["serving_default"]} {
  %0 = "tfl.pseudo_const"() {value = dense<[0, 1, 2, 0]> : tensor<4xi32>} : () -> tensor<4xi32>
  %1 = "tfl.pseudo_const"() {value = dense<[0, 4, 0, 10]> : tensor<4xi32>} : () -> tensor<4xi32>
  %2 = "tfl.pseudo_const"() {value = dense<1> : tensor<4xi32>} : () -> tensor<4xi32>
  %3 = "tfl.strided_slice"(%arg0, %0, %1, %2) {begin_mask = 9 : i32, ellipsis_mask = 0 : i32, end_mask = 13 : i32, new_axis_mask = 0 : i32, offset = false, shrink_axis_mask = 0 : i32} : (tensor<1x8x10x10xi8>, tensor<4xi32>, tensor<4xi32>, tensor<4xi32>) -> tensor<1x3x8x10xi8>
  return %3 : tensor<1x3x8x10xi8>
}
```

Despite begin and end being constants, and the strides being all set to 1, `tfl.strided_slice` is not lowered to `tfl.slice`, because the TensorFlow code generates a strided slice with masks. A pass was added in 6db21275a7579e0772f134c029b737a8c6407e01 to lower strided slices with masks set to zero, but this case is not covered.

I would like to add a pass that lowers such cases: 
- if begin_mask is set, and begin is constant, set begin_mask to zero and set the corresponding element in begin to zero
- same for end_mask, except a check has to be added to make sure the input shape is static

It seems like `ellipsis_mask` is being lowered already, and `new_axis_mask` inserts a `tfl.reshape` after the strided slice (in the TF to TFLite conversion), however `shrink_axis_mask` is not lowered: I would like to lower it to include a reshape as well.

After all of those transformations, the existing lowering pass should be able to convert any `strided_slice` with strides set to 1 to a regular `tfl.slice`.