diff --git a/mlir/docs/Dialects/Linalg.md b/mlir/docs/Dialects/Linalg.md
--- a/mlir/docs/Dialects/Linalg.md
+++ b/mlir/docs/Dialects/Linalg.md
@@ -21,8 +21,8 @@
 one-off op knowledge.
 
 The textual form description of these transformations is left for future work.
-Still, it is useful to at least the key transformations that are performed on
-the Linalg IR and that have influenced its design:
+Still, it is useful to list the key transformations that are performed on the
+Linalg IR and that have influenced its design:
 
 1.  Progressive Buffer Allocation.
 1.  Parametric Tiling.
@@ -42,8 +42,25 @@
 [key transformations](#key_transformations), including lowering to scalar
 load/store and other operations or to external library calls and intrinsics.
 
-These ops can have ***either tensor or buffer operands***, subject to
-[conventions and limitations](#tensors_and_buffers).
+These ops can have ***either tensor or buffer*** as both input and output
+operands. Output tensors operands serve the purpose of providing a unifying
+abstraction and give a shape to the results. Output tensors can come in 2
+flavors and are always associated with a corresponding op result:
+
+1.  an "init tensor" output value which provides an initial value for a tensor
+    that is created by iteratively updating the result (also called "destructive
+    updates"). Such tensor is always materialized in some form. If enough fusion
+    occurs it may end up being materialized only as a register-level SSA value.
+    It is expected (but not required) that the destructive update pattern can be
+    rewritten as an inplace update on buffers.
+
+2.  a "shape-only" tensor output value whose underlying elements are not used in
+    the payload computation and only serves the purpose of carrying shape
+    information to lower levels of abstraction. In the future this will be
+    replaced by an appropriate shape type when it is available as a builtin type
+    (see the discourse discussion
+    [Linalg and Shapes](https://llvm.discourse.group/t/linalg-and-shapes/2421)
+    for more details).
 
 ### Payload-Carrying Ops
 
@@ -125,14 +142,15 @@
 (assuming dynamic operand dimensions agree with each other, which is the purpose
 of the `assert` runtime check).
 
-Before lowering to loop form, loop induction variables and iterators are *not
-yet materialized*. This is a necessary property if we want an abstraction that
-works on both tensor values and buffers because ***values don’t escape
-loops/nesting***.
+Before lowering to loop form, loop induction variables and iterators are
+implicit (i.e. *not yet materialized*).
 
-The main implications are that: 1. The semantics of the ops are *restricted to
-operate on structured data types*, on which we can define an iterator. 2. This
-does not model arbitrary code with side-effects.
+The main implications are that:
+
+1.  The semantics of the ops are *restricted to operate on structured data
+    types*, on which we can define an iterator.
+
+2.  This does not model arbitrary code with side-effects.
 
 We do not think these are serious limitations in practice because MLIR is all
 about mixing different levels of abstractions in the same IR. As long as Linalg
@@ -483,76 +501,6 @@
 compilers. As we lay those down and engage more with the community, we expect
 multiple rounds of discussions and design changes to the original architecture.
 
-### Tensors and Buffers: Conventions and Limitations 
-
-Tensors are immutable SSA values, buffers are mutable regions of memory subject
-to side-effects and aliasing. As a consequence, output buffers are passed as
-operands whereas output tensors are new SSA values corresponding to op results.
-Inputs can be arbitrary tensors or buffers and are always passed as operands.
-
-The following convention is currently in-flight and is in the process of
-replacing other existing conventions. The following convention currently applies
-to "named" structured ops which are auto-generated by the linalg-ods tool.
-
-The convention adopted is as follows:
-
-1.  A first block of `ins` op operands hold read-only inputs of ShapedType.
-2.  An optional second block of `outs` op operands hold read-write output
-    buffers of MemRefType.
-3.  An optional third block of `init` operands hold initialization tensors of
-    RankedTensorType. Such tensors can appear when the op performs a reduction
-    and returns a tensor.
-
-Structured ops with fully parallel semantics, have empty `init`. They may either
-write in-place into `outs` buffers or return new tensors.
-
-Structured ops with reduction semantics and output tensor(s) however have
-additional restrictions:
-
-1.  They can only return a single tensor for now.
-2.  They cannot have any output buffer operand (i.e. `outs` is empty).
-3.  They have exactly one `init` tensor of the same type as the unique output
-    tensor. Such an `init` tensor does not have an explicit associate indexing
-    map. Instead the map of the result tensor is used to signify that the `init`
-    and the `result` are "tied".
-
-Points 1. and 2. keep complexity of the representation in check by allowing only
-a single result tensor, when reductions are present.
-
-Point 3. is related to the fact that SSA values cannot represent in-place
-updates. Instead, linalg adopts a similar convention that exists in e.g.
-`vector.outerproduct`: the value that is reduced into is passed as an explicit
-argument and a new result of the same shape is produced.
-
-It is expected buffer allocation will fold this last input onto the result in a
-single output buffer argument, which is why the same indexing map is required:
-the last input operand is said to be "tied" to the result.
-
-Alternative, more complex representations, would allow for:
-
-1.  Multiple results and `init` tensors in arbitrary orders, which could be
-    captured by an extra ArrayAttr of position pairs.
-2.  Relaxing the conditions on the indexing map equalities on the each pair and
-    e.g. allow implicit broadcasts of the input.
-
-These representations are deemed unnecessarily complex for now and are left for
-future discussion.
-
-As an illustration, the syntax for a `linalg.matmul` writing into a buffer is:
-
-```
-linalg.matmul ins(%a, %b : memref, tensor)
-             outs(%c : memref)
-```
-
-, whereas the syntax for a `linalg.matmul` returning a new tensor is:
-
-```
-%d = linalg.matmul ins(%a, %b : tensor, memref)
-                  init(%c : tensor)
-                    -> tensor
-```
-
 ### Data Representation: Views
 
 The current implementation uses the
diff --git a/mlir/include/mlir/Dialect/Linalg/Analysis/DependenceAnalysis.h b/mlir/include/mlir/Dialect/Linalg/Analysis/DependenceAnalysis.h
--- a/mlir/include/mlir/Dialect/Linalg/Analysis/DependenceAnalysis.h
+++ b/mlir/include/mlir/Dialect/Linalg/Analysis/DependenceAnalysis.h
@@ -45,19 +45,17 @@
 class LinalgDependenceGraph {
 public:
   enum DependenceType { RAR = 0, RAW, WAR, WAW, NumTypes };
-  struct LinalgOpView {
-    Operation *op;
-    unsigned operandIndex;
-  };
+  // TODO: OpOperand tracks dependencies on buffer operands. Tensor result will
+  // need an extension to use OpResult.
   struct LinalgDependenceGraphElem {
     // dependentOpView may be either:
     //   1. src in the case of dependencesIntoGraphs.
     //   2. dst in the case of dependencesFromDstGraphs.
-    LinalgOpView dependentOpView;
+    OpOperand *dependentOpView;
     // View in the op that is used to index in the graph:
     //   1. src in the case of dependencesFromDstGraphs.
     //   2. dst in the case of dependencesIntoGraphs.
-    LinalgOpView indexingOpView;
+    OpOperand *indexingOpView;
     // Type of the dependence.
     DependenceType dependenceType;
   };
@@ -161,8 +159,8 @@
   // Uses std::pair to keep operations and view together and avoid usage errors
   // related to src/dst and producer/consumer terminology in the context of
   // dependences.
-  void addDependenceElem(DependenceType dt, LinalgOpView indexingOpView,
-                         LinalgOpView dependentOpView);
+  void addDependenceElem(DependenceType dt, OpOperand *indexingOpView,
+                         OpOperand *dependentOpView);
 
   /// Implementation detail for findCoveringxxx.
   SmallVector
diff --git a/mlir/include/mlir/Dialect/Linalg/EDSC/Builders.h b/mlir/include/mlir/Dialect/Linalg/EDSC/Builders.h
--- a/mlir/include/mlir/Dialect/Linalg/EDSC/Builders.h
+++ b/mlir/include/mlir/Dialect/Linalg/EDSC/Builders.h
@@ -30,8 +30,8 @@
 namespace edsc {
 inline void defaultRegionBuilder(ValueRange args) {}
 
-/// Build a `linalg.generic` op with the specified `inputs`, `outputBuffers`,
-/// `initTensors`, `resultTensorsTypes` and `region`.
+/// Build a `linalg.generic` op with the specified `inputs`, `outputs`,
+/// `resultTensorsTypes` and `region`.
 ///
 /// `otherValues` and `otherAttributes` may be passed and will be appended as
 /// operands and attributes respectively.
@@ -41,15 +41,12 @@
 ///
 /// 1. `inputs` may contain StructuredIndexed that capture either buffer or
 /// tensor values.
-/// 2. `outputsBuffers` may contain StructuredIndexed that capture buffer
-/// values.
-/// 3. `initTensors` contain tensor values, without indexing maps.
-/// 4. `resultTensorTypes` may contain StructuredIndexed that capture return
-/// tensor types.
+/// 2. `outputs` may contain StructuredIndexed that capture either buffer or
+/// tensor values. In the future this will be extended with ranked shape values.
+/// 4. `resultTensorTypes` may contain return tensor types.
 Operation *makeGenericLinalgOp(
     ArrayRef iteratorTypes, ArrayRef inputs,
-    ArrayRef outputBuffers, ArrayRef initTensors,
-    ArrayRef resultTensorTypes,
+    ArrayRef outputs, TypeRange resultTensorTypes,
     function_ref regionBuilder = defaultRegionBuilder,
     ArrayRef otherValues = {}, ArrayRef otherAttributes = {});
 
diff --git a/mlir/include/mlir/Dialect/Linalg/EDSC/Intrinsics.h b/mlir/include/mlir/Dialect/Linalg/EDSC/Intrinsics.h
--- a/mlir/include/mlir/Dialect/Linalg/EDSC/Intrinsics.h
+++ b/mlir/include/mlir/Dialect/Linalg/EDSC/Intrinsics.h
@@ -18,6 +18,7 @@
 using linalg_copy = OperationBuilder;
 using linalg_dot = OperationBuilder;
 using linalg_fill = OperationBuilder;
+using linalg_init_tensor = ValueBuilder;
 using linalg_matmul = OperationBuilder;
 using linalg_matvec = OperationBuilder;
 using linalg_vecmat = OperationBuilder;
diff --git a/mlir/include/mlir/Dialect/Linalg/IR/LinalgOps.h b/mlir/include/mlir/Dialect/Linalg/IR/LinalgOps.h
--- a/mlir/include/mlir/Dialect/Linalg/IR/LinalgOps.h
+++ b/mlir/include/mlir/Dialect/Linalg/IR/LinalgOps.h
@@ -9,7 +9,6 @@
 #ifndef MLIR_DIALECT_LINALG_LINALGOPS_H_
 #define MLIR_DIALECT_LINALG_LINALGOPS_H_
 
-#include "mlir/Dialect/Linalg/IR/LinalgTraits.h"
 #include "mlir/Dialect/Linalg/IR/LinalgTypes.h"
 #include "mlir/Dialect/StandardOps/IR/Ops.h"
 #include "mlir/Dialect/Utils/StructuredOpsUtils.h"
@@ -111,9 +110,17 @@
 void getDimsOfType(Operation *op, StringRef iteratorTypeName,
                    SmallVectorImpl &res);
 
+namespace detail {
+LogicalResult verifyStructuredOpInterface(Operation *op);
+} // namespace detail
 } // namespace linalg
 } // namespace mlir
 
+namespace mlir {
+namespace linalg {
+class IndexedGenericOp;
+} // namespace linalg
+} // namespace mlir
 #include "mlir/Dialect/Linalg/IR/LinalgStructuredOpsInterfaces.h.inc"
 
 #define GET_OP_CLASSES
diff --git a/mlir/include/mlir/Dialect/Linalg/IR/LinalgStructuredOps.td b/mlir/include/mlir/Dialect/Linalg/IR/LinalgStructuredOps.td
--- a/mlir/include/mlir/Dialect/Linalg/IR/LinalgStructuredOps.td
+++ b/mlir/include/mlir/Dialect/Linalg/IR/LinalgStructuredOps.td
@@ -19,26 +19,6 @@
 include "mlir/Interfaces/CopyOpInterface.td"
 include "mlir/Interfaces/SideEffectInterfaces.td"
 
-// The Linalg `NInputs` trait provides the API for ops that are known
-// to have a specified number of inputs, all passed as operands.
-// See Linalg/LinalgTraits.h for implementation details and usage.
-class NInputs :
-  NativeOpTrait<"linalg::NInputs<" # !cast(n) # ">::Impl"> {}
-
-// The Linalg `ZeroInitTensors` trait provides the API for ops that are known
-// to not have input tensor operands.
-// See Linalg/LinalgTraits.h for implementation details and usage.
-def ZeroInitTensors : NativeOpTrait<"linalg::ZeroInitTensors"> {}
-
-// The Linalg `NOutputs` trait provides the API for ops that are known
-// to have a specified number of outputs, all passed as operands.
-// See Linalg/LinalgTraits.h for implementation details and usage.
-class NOutputs :
-  NativeOpTrait<"linalg::NOutputs<" # !cast(n) # ">::Impl"> {}
-
-def StructuredOpTraits : NativeOpTrait<"linalg::StructuredOpTraits">;
-def NamedStructuredOpTrait : NativeOpTrait<"linalg::NamedStructuredOpTrait">;
-
 // Base Tablegen class for Linalg ops.
 // Linalg ops that correspond to library calls operate on ShapedType as their
 // first operands. These may be optionally followed by non-view operands
@@ -50,7 +30,6 @@
 class LinalgStructured_Op props>
   : LinalgStructuredBase_Op])> {
   code libraryCallName = [{
     std::string getLibraryCallName() {
@@ -65,12 +44,7 @@
 //===----------------------------------------------------------------------===//
 // At the moment these are not declarative and require a bunch of C++ code.
 // In the future, these should be migrated to a declarative specification.
-def CopyOp : LinalgStructured_Op<"copy", [
-    CopyOpInterface,
-    NInputs<1>,
-    ZeroInitTensors,
-    NOutputs<1>
-  ]> {
+def CopyOp : LinalgStructured_Op<"copy", [CopyOpInterface]> {
   let description = [{
     Copies the data in the input view into the output view.
 
@@ -137,6 +111,9 @@
     }]>];
 
   let extraClassDeclaration = libraryCallName # [{
+    ValueRange inputs() { return getOperands().take_front(); }
+    ValueRange outputs() { return getOperands().take_back(); }
+
     // Rank-polymorphic.
     //   filling_value -> O(ivs) with parallel iterators.
     ArrayAttr iterator_types() {
@@ -170,14 +147,13 @@
   let hasCanonicalizer = 1;
 }
 
-def FillOp : LinalgStructured_Op<"fill", [
-    NInputs<0>,
-    ZeroInitTensors,
-    NOutputs<1>]> {
-
+def FillOp : LinalgStructured_Op<"fill", []> {
   let arguments = (ins AnyStridedMemRef:$output,
                    AnyTypeOf<[AnyFloat, AnySignlessInteger, AnyVector]>:$value);
   let extraClassDeclaration = libraryCallName # [{
+    ValueRange inputs() { return {}; }
+    ValueRange outputs() { return getOperands().take_front(); }
+
     // Rank-polymorphic.
     //   filling_value -> O(ivs) with parallel iterators.
     ArrayAttr iterator_types() {
@@ -276,13 +252,8 @@
   }];
 }
 
-def ConvOp : PoolingBase_Op<"conv", [
-    NInputs<2>,
-    // Despite having reductions, this manually defined ConvOp may only take
-    // memref operands and can never have init tensors.
-    ZeroInitTensors,
-    NOutputs<1>]> {
-
+// Only support buffer semantics.
+def ConvOp : PoolingBase_Op<"conv", []> {
   let description = [{
     Generic n-D convolution as described in the TF documentation:
     https://www.tensorflow.org/versions/r2.0/api_docs/python/tf/nn/convolution
@@ -313,6 +284,9 @@
                    OptionalAttr:$padding);
 
   let extraClassDeclaration = commonUtils # [{
+    ValueRange inputs() { return getOperands().slice(0, 2); }
+    ValueRange outputs() { return getOperands().take_back(); }
+
     // TODO: extend to support more than 1 dimensions and potentially grouping
     // too.
     unsigned getNumBatchDimensions() { return 1; }
@@ -335,6 +309,12 @@
       // parallelized across; i.e. [zs] in the TF notation above whose number
       // match `xs` (i.e. 1 window loop per "image" dimension).
       // This may evolve in the future.
+      // Conditionally check nPar is large enough for cases of ill-formed op: 
+      // this avoids overflows before hitting the verifier.
+      assert(nPar > getNumBatchDimensions() + getNumInputFeatureDimensions() &&
+             "expected at least one window dimension (i.e. memref ranks greater "
+             "than 2). See 'func @conv_rank_limit' in "
+             "mlir/test/Dialect/Linalg/invalid.mlir");
       unsigned nWin =
         nPar - getNumBatchDimensions() - getNumInputFeatureDimensions();
       SmallVector iters(nPar, getParallelIteratorTypeName());
@@ -352,7 +332,8 @@
     ArrayAttr indexing_maps() {
       MLIRContext *context = getContext();
       auto nWin = getNumWindowLoops();
-      assert(nWin > 0 && "expected at least one window dimension");
+      assert(nWin > 0 && "expected at least one window dimension (i.e. memref "
+                         "ranks greater than 2)");
       unsigned idx = 0;
       // In the following, AffineDimExprs are indexed in loop order:
       //   [ b, xs, k,           q,                     zs]
@@ -394,13 +375,9 @@
   let hasCanonicalizer = 1;
 }
 
+// Only support buffer semantics.
 class SingleInputPoolingBase_Op
-  : PoolingBase_Op,
-    // Despite having reductions, this manually defined ConvOp may only take
-    // memref operands and can never have init tensors.
-    ZeroInitTensors,
-    NOutputs<1>]> {
+    : PoolingBase_Op {
   let description = [{
     A base class for single input pooling function.
 
@@ -420,6 +397,9 @@
                    OptionalAttr:$padding);
 
   let extraClassDeclaration = commonUtils# [{
+    ValueRange inputs() { return getOperands().slice(0, 2); }
+    ValueRange outputs() { return getOperands().take_back(); }
+
     ArrayAttr iterator_types() {
       // Outer parallel loops are always the number of output dimensions.
       unsigned nPar = getOutputShapedType(0).getRank();
@@ -493,11 +473,9 @@
 class GenericOpBase : LinalgStructuredBase_Op,
-    NamedStructuredOpTrait,
     SingleBlockImplicitTerminator<"YieldOp">]> {
   let arguments = (ins Variadic:$inputs,
-                       Variadic:$output_buffers,
-                       Variadic:$init_tensors,
+                       Variadic:$outputs,
                        AffineMapArrayAttr:$indexing_maps,
                        ArrayAttr:$iterator_types,
                        OptionalAttr:$doc,
@@ -622,34 +600,26 @@
     ```mlir
     %C = linalg.generic #trait_attribute
       ins(%A, %B : tensor, memref)
-      init(%C : tensor)
+      outs(%C : tensor)
       {other-optional-attributes}
       {region}
       -> (tensor)
     ```
-
-    The `init` operand and the conventions around mixing tensors and buffers are
-    described in more detail in the "Tensors and Buffers: Conventions and
-    Limitations" section in the [Linalg Document](../docs/Linalg.md)
-
-    Tensor values must be legalized by a buffer allocation pass before most
-    transformations can be applied. Such legalizations move tensor return values
-    into output buffer operands and updates the region arguments accordingly.
   }];
 
   let builders = [
     OpBuilderDAG<(ins "TypeRange":$resultTensorTypes, "ValueRange":$inputs,
-      "ValueRange":$outputBuffers, "ValueRange":$initTensors,
-      "ArrayRef":$indexingMaps, "ArrayRef":$iteratorTypes,
-      "StringRef":$doc, "StringRef":$libraryCall,
+      "ValueRange":$outputs, "ArrayRef":$indexingMaps,
+      "ArrayRef":$iteratorTypes, "StringRef":$doc,
+      "StringRef":$libraryCall,
       CArg<"function_ref", "nullptr">)>,
     OpBuilderDAG<(ins "ValueRange":$inputs, "ValueRange":$outputBuffers,
       "ArrayRef":$indexingMaps, "ArrayRef":$iteratorTypes,
       "StringRef":$doc, "StringRef":$libraryCall,
       CArg<"function_ref", "nullptr">)>,
     OpBuilderDAG<(ins "TypeRange":$resultTensorTypes, "ValueRange":$inputs,
-      "ValueRange":$outputBuffers, "ValueRange":$initTensors,
-      "ArrayRef":$indexingMaps, "ArrayRef":$iteratorTypes,
+      "ValueRange":$outputs, "ArrayRef":$indexingMaps,
+      "ArrayRef":$iteratorTypes,
       CArg<"function_ref", "nullptr">)>,
     OpBuilderDAG<(ins "ValueRange":$inputs, "ValueRange":$outputBuffers,
       "ArrayRef":$indexingMaps, "ArrayRef":$iteratorTypes,
@@ -714,8 +684,8 @@
 
     ```mlir
       linalg.indexed_generic #matmul_trait
-        ins(%A, %B : memref,
-                     memref)
+         ins(%A, %B : memref,
+                      memref)
         outs(%C : memref) {
       (%offset_m: index, %offset_n: index, %offset_k: index,
        %a: f32, %b: f32, %c: f32) :
@@ -761,27 +731,19 @@
 
     ```mlir
     %C = linalg.indexed_generic #trait_attribute
-      ins(%A, %B : tensor, memref)
-      init(%C : tensor)
+       ins(%A, %B : tensor, memref)
+      outs(%C : tensor)
       {other-optional-attributes}
       {region_with_index_arguments}
       -> (tensor)
     ```
-
-    The `init` operand and the conventions around mixing tensors and buffers are
-    described in more detail in the "Tensors and Buffers: Conventions and
-    Limitations" section in the [Linalg Document](../docs/Linalg.md)
-
-    Tensor values must be legalized by a buffer allocation pass before most
-    transformations can be applied. Such legalizations move tensor return values
-    into output buffer operands and update the region arguments accordingly.
   }];
 
   let builders = [
     OpBuilderDAG<(ins "TypeRange":$resultTensorTypes, "ValueRange":$inputs,
-      "ValueRange":$outputBuffers, "ValueRange":$initTensors,
-      "ArrayRef":$indexingMaps, "ArrayRef":$iteratorTypes,
-      "StringRef":$doc, "StringRef":$libraryCall,
+      "ValueRange":$outputs, "ArrayRef":$indexingMaps,
+      "ArrayRef":$iteratorTypes, "StringRef":$doc,
+      "StringRef":$libraryCall,
       CArg<"function_ref",
            "nullptr">)>,
     OpBuilderDAG<(ins "ValueRange":$inputs, "ValueRange":$outputBuffers,
@@ -790,8 +752,8 @@
       CArg<"function_ref",
            "nullptr">)>,
     OpBuilderDAG<(ins "TypeRange":$resultTensorTypes, "ValueRange":$inputs,
-      "ValueRange":$outputBuffers, "ValueRange":$initTensors,
-      "ArrayRef":$indexingMaps, "ArrayRef":$iteratorTypes,
+      "ValueRange":$outputs, "ArrayRef":$indexingMaps,
+      "ArrayRef":$iteratorTypes,
       CArg<"function_ref",
            "nullptr">)>,
     OpBuilderDAG<(ins "ValueRange":$inputs, "ValueRange":$outputBuffers,
diff --git a/mlir/include/mlir/Dialect/Linalg/IR/LinalgStructuredOpsInterface.td b/mlir/include/mlir/Dialect/Linalg/IR/LinalgStructuredOpsInterface.td
--- a/mlir/include/mlir/Dialect/Linalg/IR/LinalgStructuredOpsInterface.td
+++ b/mlir/include/mlir/Dialect/Linalg/IR/LinalgStructuredOpsInterface.td
@@ -20,6 +20,24 @@
 def LinalgStructuredInterface : OpInterface<"LinalgOp"> {
   let cppNamespace = "::mlir::linalg";
   let methods = [
+    //===------------------------------------------------------------------===//
+    // Loop types handling.
+    //===------------------------------------------------------------------===//
+    InterfaceMethod<
+      /*desc=*/[{
+        Return the number of induction variables in the basic block. This should
+        always be 0 for index-free linalg ops. For IndexedGeneric, this must be
+        equal to numLoops
+      }],
+      /*retTy=*/"unsigned",
+      /*methodName=*/"getNumPayloadInductionVariables",
+      /*args=*/(ins),
+      /*methodBody=*/"",
+      /*defaultImplementation=*/[{
+        return isa(this->getOperation()) ?
+          $_op.getNumLoops() : 0;
+      }]
+    >,
     //===------------------------------------------------------------------===//
     // Loop types handling.
     //===------------------------------------------------------------------===//
@@ -125,42 +143,60 @@
                getNumIterators(getReductionIteratorTypeName(), iters) == 1;
       }]>,
     //===------------------------------------------------------------------===//
-    // Num input/output/initTensors arguments handling.
+    // Num input/output arguments handling.
     //===------------------------------------------------------------------===//
-    // These special methods must be defined by each op that wants to implement
-    // the LinalgStructuredInterface. For now, this is either:
-    // - Explicitly specified in the op definition.
-    // - Derived from variadic attributes (for "named" ops, linalg.generic and
-    //   linalg.indexed_generic ops).
+    // `inputs` must be defined by each op that wants to implement the
+    // LinalgStructuredInterface.
+    InterfaceMethod<
+      /*desc=*/[{
+        Return the input shape operands.
+      }],
+      /*retTy=*/"ValueRange",
+      /*methodName=*/"inputs",
+      /*args=*/(ins)
+    >,
+    // These special methods rely on `inputs` and `outputs` being defined by
+    // each op that wants to implement the LinalgStructuredInterface.
     InterfaceMethod<
       /*desc=*/[{
         Return the number of inputs.
       }],
       /*retTy=*/"unsigned",
-      /*methodName=*/"getNumInputs"
+      /*methodName=*/"getNumInputs",
+      /*args=*/(ins),
+      /*methodBody=*/"",
+      /*defaultImplementation=*/[{
+        return $_op.inputs().size();
+      }]
     >,
+    // `outputs` must be defined by each op that wants to implement the
+    // LinalgStructuredInterface.
     InterfaceMethod<
       /*desc=*/[{
-        Return the number of init tensors.
+        Return the output shape operands.
       }],
-      /*retTy=*/"unsigned",
-      /*methodName=*/"getNumInitTensors"
+      /*retTy=*/"ValueRange",
+      /*methodName=*/"outputs",
+      /*args=*/(ins)
     >,
     InterfaceMethod<
       /*desc=*/[{
         Return the number of outputs.
       }],
       /*retTy=*/"unsigned",
-      /*methodName=*/"getNumOutputs"
+      /*methodName=*/"getNumOutputs",
+      /*args=*/(ins),
+      /*methodBody=*/"",
+      /*defaultImplementation=*/[{
+        return $_op.outputs().size();
+      }]
     >,
     //===------------------------------------------------------------------===//
-    // Input arguments handling.
+    // Input operands handling.
     //===------------------------------------------------------------------===//
     InterfaceMethod<
       /*desc=*/[{
-        Return the `i`-th input value.
-        The `i^th` input argument is always the `i^th` operand regardless of
-        whether we have tensors or buffers.
+        Return the `i`-th input operand.
       }],
       /*retTy=*/"Value",
       /*methodName=*/"getInput",
@@ -173,24 +209,7 @@
     >,
     InterfaceMethod<
       /*desc=*/[{
-        Return the index of the given input value `v`, or `None` if the value is
-        not an input.
-      }],
-      /*retTy=*/"llvm::Optional",
-      /*methodName=*/"getIndexOfInput",
-      /*args=*/(ins "Value":$value),
-      /*methodBody=*/"",
-      /*defaultImplementation=*/[{
-        auto it = llvm::find(getInputs(), value);
-        if (it != getInputs().end())
-          return it - getInputs().begin();
-        return llvm::None;
-      }]
-    >,
-    InterfaceMethod<
-      /*desc=*/[{
-        Return the `i`-th input shaped type, irrespective of buffer or tensor
-        type.
+        Return the `i`-th input shaped type
       }],
       /*retTy=*/"ShapedType",
       /*methodName=*/"getInputShapedType",
@@ -202,7 +221,7 @@
     >,
     InterfaceMethod<
       /*desc=*/[{
-        Return the input operands.
+        Return the range of input operands.
       }],
       /*retTy=*/"Operation::operand_range",
       /*methodName=*/"getInputs",
@@ -215,7 +234,19 @@
     >,
     InterfaceMethod<
       /*desc=*/[{
-        Return the range over the input operands that are of buffer type.
+        Return the OpOperands for the input operands.
+      }],
+      /*retTy=*/" MutableArrayRef",
+      /*methodName=*/"getInputOpOperands",
+      /*args=*/(ins),
+      /*methodBody=*/"",
+      /*defaultImplementation=*/[{
+        return this->getOperation()->getOpOperands().take_front(getNumInputs());
+      }]
+    >,
+    InterfaceMethod<
+      /*desc=*/[{
+        Return the subset of input operands that are of buffer type.
       }],
       /*retTy=*/"SmallVector",
       /*methodName=*/"getInputBuffers",
@@ -223,417 +254,504 @@
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
         return llvm::to_vector<4>(llvm::make_filter_range(
-          getInputs(), [](Value in){ return in.getType().isa(); }));
+          getInputs(), [](Value in){ return in.getType().template isa(); }));
       }]
     >,
     InterfaceMethod<
       /*desc=*/[{
-        Return the subset of input operands that are of ranked tensor type.
+        Return the number of input buffer operands.
       }],
-      /*retTy=*/"SmallVector",
-      /*methodName=*/"getInputTensorTypes" ,
+      /*retTy=*/"unsigned",
+      /*methodName=*/"getNumInputBuffers",
       /*args=*/(ins),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        SmallVector res;
-        for (Type type : getInputs().getTypes())
-          if (auto t = type.template dyn_cast())
-            res.push_back(t);
-        return res;
+        return $_op.getInputBuffers().size();
       }]
     >,
-    //===------------------------------------------------------------------===//
-    // Output arguments handling.
-    //===------------------------------------------------------------------===//
     InterfaceMethod<
       /*desc=*/[{
-        Return the output buffer at the given index, asserts that this is a
-        buffer operand and not a tensor result.
-        The `i^th` output argument is an operand (resp. a return value) iff it
-        is a value of buffer type (resp. a return value of tensor type).
+        Return the `index`^th input buffer.
       }],
       /*retTy=*/"Value",
-      /*methodName=*/"getOutputBuffer",
-      /*args=*/(ins "unsigned":$i),
+      /*methodName=*/"getInputBuffer",
+      /*args=*/(ins "unsigned":$index),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        // Output buffers are passed as output buffer operands (side-effecting).
-        // Output tensors are results.
-        // The union of the 2 are all the outputs and we want to ensure i does
-        // not overflow the buffer operands.
-        assert(i + this->getOperation()->getNumResults() < $_op.getNumOutputs()
-               && "overflowing output buffer index");
-        return this->getOperation()->getOperand($_op.getNumInputs() + i);
+        assert(index < getNumInputBuffers());
+        return getInputBuffers()[index];
       }]
     >,
     InterfaceMethod<
       /*desc=*/[{
-        Return the index of the given buffer value, or `None` if the value is
-        not part of the output buffers.
+        Return the subset of input operands that are of buffer type.
       }],
-      /*retTy=*/"llvm::Optional",
-      /*methodName=*/"getIndexOfOutputBuffer",
-      /*args=*/(ins "Value":$value),
+      /*retTy=*/"SmallVector",
+      /*methodName=*/"getInputBuffersOpOperands",
+      /*args=*/(ins),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        auto it = llvm::find(getOutputBuffers(), value);
-        if (it != getOutputBuffers().end())
-          return it - getOutputBuffers().begin();
-        return llvm::None;
+        SmallVector res;
+        res.reserve(getNumInputs());
+        for (OpOperand &o : getInputOpOperands())
+          if (o.get().getType().isa())
+            res.push_back(&o);
+        return res;
       }]
     >,
     InterfaceMethod<
       /*desc=*/[{
-        Return the type of the output buffer at the given index.
+        Return the subset of input operands that are of tensor type.
       }],
-      /*retTy=*/"MemRefType",
-      /*methodName=*/"getOutputBufferType",
-      /*args=*/(ins "unsigned":$i),
+      /*retTy=*/"SmallVector",
+      /*methodName=*/"getInputTensors",
+      /*args=*/(ins),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        return getOutputBuffer(i).getType().template cast();
-      }]>,
+        return llvm::to_vector<4>(llvm::make_filter_range(
+          getInputs(),
+          [](Value in){ return in.getType().template isa(); }));
+      }]
+    >,
     InterfaceMethod<
       /*desc=*/[{
-        Return the `i`-th output shaped type, irrespective of buffer or tensor
-        type.
+        Return the subset of op operands that are of tensor type.
       }],
-      /*retTy=*/"ShapedType",
-      /*methodName=*/"getOutputShapedType",
-      /*args=*/(ins "unsigned":$i),
+      /*retTy=*/"SmallVector",
+      /*methodName=*/"getInputTensorsOpOperands",
+      /*args=*/(ins),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        return getShapedType(i + $_op.getNumInputs());
-      }]>,
+        SmallVector res;
+        res.reserve(getNumInputs());
+        for (OpOperand &o : getInputOpOperands())
+          if (o.get().getType().isa())
+            res.push_back(&o);
+        return res;
+      }]
+    >,
     InterfaceMethod<
       /*desc=*/[{
-        Return the results that are of ranked tensor type.
+        Return the types of the subset of input operands that are of buffer type.
       }],
-      /*retTy=*/"SmallVector",
-      /*methodName=*/"getOutputTensorTypes",
+      /*retTy=*/"SmallVector",
+      /*methodName=*/"getInputBufferTypes" ,
       /*args=*/(ins),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        SmallVector res;
-        for (Type type : this->getOperation()->getResults().getTypes())
-          res.push_back(type.template cast());
-        return res;
-      }]>,
+        return llvm::to_vector<4>(
+          llvm::map_range(
+            llvm::make_filter_range(
+              ValueRange(getInputs()).getTypes(),
+              [](Type in){ return in.isa(); }),
+            [](Type in){ return in.cast(); }));
+      }]
+    >,
     InterfaceMethod<
       /*desc=*/[{
-        Return the output buffers (operands).
+        Return the types of the subset of input operands that are of ranked
+        tensor type.
       }],
-      /*retTy=*/"Operation::operand_range",
-      /*methodName=*/"getOutputBuffers",
+      /*retTy=*/"SmallVector",
+      /*methodName=*/"getInputTensorTypes" ,
       /*args=*/(ins),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        auto range = this->getOperation()->getOperands();
-        return {range.begin() + $_op.getNumInputs(),
-                range.begin() + getNumInputsAndOutputBuffers()};
+        return llvm::to_vector<4>(
+          llvm::map_range(
+            llvm::make_filter_range(
+              ValueRange(getInputs()).getTypes(),
+              [](Type in){ return in.isa(); }),
+            [](Type in){ return in.cast(); }));
       }]
     >,
 
     //===------------------------------------------------------------------===//
-    // Input and Output arguments handling.
+    // Output operands handling.
     //===------------------------------------------------------------------===//
     InterfaceMethod<
       /*desc=*/[{
-        Return one single buffer at position `$i`.
+        Return the `i`-th output operand.
       }],
       /*retTy=*/"Value",
-      /*methodName=*/"getBuffer",
+      /*methodName=*/"getOutput",
       /*args=*/(ins "unsigned":$i),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        assert(i < getNumInputsAndOutputBuffers() && "overflowing buffers index");
-        return this->getOperation()->getOperand(i);
+        assert(i < $_op.getNumOutputs());
+        return this->getOperation()->getOperand(i + $_op.getNumInputs());
       }]
     >,
     InterfaceMethod<
       /*desc=*/[{
-        Return the number of output buffers
+        Return the `i`-th output shaped type
       }],
-      /*retTy=*/"unsigned",
-      /*methodName=*/"getNumOutputBuffers",
+      /*retTy=*/"ShapedType",
+      /*methodName=*/"getOutputShapedType",
+      /*args=*/(ins "unsigned":$i),
+      /*methodBody=*/"",
+      /*defaultImplementation=*/[{
+        return getOutput(i).getType().template cast();
+      }]
+    >,
+    InterfaceMethod<
+      /*desc=*/[{
+        Return the range of output operands.
+      }],
+      /*retTy=*/"Operation::operand_range",
+      /*methodName=*/"getOutputs",
       /*args=*/(ins),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        return $_op.getNumOutputs() - this->getOperation()->getNumResults();
+        auto start =
+          this->getOperation()->getOperands().begin() + $_op.getNumInputs();
+        return {start, start + $_op.getNumOutputs()};
       }]
     >,
     InterfaceMethod<
       /*desc=*/[{
-        Return the number of inputs and outputs, irrespective of their buffer or
-        tensor type.
+        Return the OpOperands for the output operands.
       }],
-      /*retTy=*/"unsigned",
-      /*methodName=*/"getNumInputsAndOutputs",
+      /*retTy=*/" MutableArrayRef",
+      /*methodName=*/"getOutputOpOperands",
       /*args=*/(ins),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        return $_op.getNumInputs() + $_op.getNumOutputs();
+        return this->getOperation()->getOpOperands().slice(
+          getNumInputs(), getNumOutputs());
       }]
     >,
     InterfaceMethod<
       /*desc=*/[{
-        Return the number of inputs, irrespective of their buffer or tensor type
-        and output buffers
+        Return the subset of output operands that are of buffer type.
       }],
-      /*retTy=*/"unsigned",
-      /*methodName=*/"getNumInputsAndOutputBuffers",
+      /*retTy=*/"SmallVector",
+      /*methodName=*/"getOutputBuffers",
       /*args=*/(ins),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        return $_op.getNumInputs() + $_op.getNumOutputs() -
-          this->getOperation()->getNumResults();
+        return llvm::to_vector<4>(llvm::make_filter_range(
+          getOutputs(), [](Value in){ return in.getType().template isa(); }));
       }]
     >,
     InterfaceMethod<
       /*desc=*/[{
-        Return the range over inputs (irrespective of type) and output buffers.
+        Return the `index`^th output buffer.
       }],
-      /*retTy=*/"Operation::operand_range",
-      /*methodName=*/"getInputsAndOutputBuffers",
+      /*retTy=*/"Value",
+      /*methodName=*/"getOutputBuffer",
+      /*args=*/(ins "unsigned":$index),
+      /*methodBody=*/"",
+      /*defaultImplementation=*/[{
+        assert(index < getNumOutputBuffers());
+        return getOutputBuffers()[index];
+      }]
+    >,
+    InterfaceMethod<
+      /*desc=*/[{
+        Return the subset of output operands that are of buffer type.
+      }],
+      /*retTy=*/"SmallVector",
+      /*methodName=*/"getOutputBuffersOpOperands",
       /*args=*/(ins),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        auto range = this->getOperation()->getOperands();
-        return {range.begin(), range.begin() + getNumInputsAndOutputBuffers()};
+        SmallVector res;
+        res.reserve(getNumOutputs());
+        for (OpOperand &o : getOutputOpOperands())
+          if (o.get().getType().isa())
+            res.push_back(&o);
+        return res;
       }]
     >,
     InterfaceMethod<
       /*desc=*/[{
-        Return the range over init tensors.
+        Return the number of output buffer operands.
       }],
-      /*retTy=*/"Operation::operand_range",
-      /*methodName=*/"getInitTensors",
+      /*retTy=*/"unsigned",
+      /*methodName=*/"getNumOutputBuffers",
       /*args=*/(ins),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        auto range = this->getOperation()->getOperands();
-        auto base = range.begin() + getNumInputsAndOutputBuffers();
-        return {base, base + $_op.getNumInitTensors()};
+        return $_op.getOutputBuffers().size();
       }]
     >,
     InterfaceMethod<
       /*desc=*/[{
-        Return one single init tensor at position `$i`.
+        Return the subset of output operands that are of tensor type.
       }],
-      /*retTy=*/"Value",
-      /*methodName=*/"getInitTensor",
-      /*args=*/(ins "unsigned":$i),
+      /*retTy=*/"SmallVector",
+      /*methodName=*/"getOutputTensors",
+      /*args=*/(ins),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        assert(i < $_op.getNumInitTensors() && "overflowing init tensor index");
-        return getInitTensors()[i];
+        return llvm::to_vector<4>(llvm::make_filter_range(
+          getOutputs(),
+          [](Value in){ return in.getType().template isa(); }));
       }]
     >,
     InterfaceMethod<
       /*desc=*/[{
-        Return true if the shaped operand index `i` is the index of an init
-        tensor.
+        Return the subset of output operands that are of tensor type.
       }],
-      /*retTy=*/"bool",
-      /*methodName=*/"isIndexOfAnInitTensor",
-      /*args=*/(ins "unsigned":$i),
+      /*retTy=*/"SmallVector",
+      /*methodName=*/"getOutputTensorsOpOperands",
+      /*args=*/(ins),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        assert(i < $_op.getNumShapedOperands() && "overflowing shaped operand index");
-        return i >= $_op.getNumInputs() + getNumOutputBuffers();
+        SmallVector res;
+        res.reserve(getNumOutputs());
+        for (OpOperand &o : getOutputOpOperands())
+          if (o.get().getType().isa())
+            res.push_back(&o);
+        return res;
       }]
     >,
     InterfaceMethod<
       /*desc=*/[{
-        Return the relative init tensor index of the shaped operand index.
+        Return the number of output tensor operands.
       }],
       /*retTy=*/"unsigned",
-      /*methodName=*/"getInitTensorIndexFromShapedIndex",
-      /*args=*/(ins "unsigned":$i),
+      /*methodName=*/"getNumOutputTensors",
+      /*args=*/(ins),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        assert(isIndexOfAnInitTensor(i) && "expected an init tensor index");
-        return i - $_op.getNumInputs() - getNumOutputBuffers();
+        return $_op.getOutputTensors().size();
       }]
     >,
     InterfaceMethod<
       /*desc=*/[{
-        Return the index of the given init tensor value, or `None` if the value 
-        is not part of the init tensors.
+        Return the types of the subset of output operands that are of buffer type.
       }],
-      /*retTy=*/"llvm::Optional",
-      /*methodName=*/"getIndexOfInitTensor",
-      /*args=*/(ins "Value":$value),
+      /*retTy=*/"SmallVector",
+      /*methodName=*/"getOutputBufferTypes" ,
+      /*args=*/(ins),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        auto it = llvm::find(getInitTensors(), value);
-        if (it != getInitTensors().end())
-          return it - getInitTensors().begin();
-        return llvm::None;
+        return llvm::to_vector<4>(
+          llvm::map_range(
+            llvm::make_filter_range(
+              ValueRange(getOutputs()).getTypes(),
+              [](Type in){ return in.isa(); }),
+            [](Type in){ return in.cast(); }));
       }]
     >,
     InterfaceMethod<
       /*desc=*/[{
-        Return the number of inputs, output buffers and init tensors operands.
+        Return the types of the subset of output operands that are of ranked
+        tensor type.
       }],
-      /*retTy=*/"unsigned",
-      /*methodName=*/"getNumShapedOperands",
+      /*retTy=*/"SmallVector",
+      /*methodName=*/"getOutputTensorTypes" ,
       /*args=*/(ins),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        return getNumInputsAndOutputBuffers() + $_op.getNumInitTensors();
+        return llvm::to_vector<4>(
+          llvm::map_range(
+            llvm::make_filter_range(
+              ValueRange(getOutputs()).getTypes(),
+              [](Type in){ return in.isa(); }),
+            [](Type in){ return in.cast(); }));
       }]
     >,
+
+    //===------------------------------------------------------------------===//
+    // Input and Output arguments handling.
+    //===------------------------------------------------------------------===//
     InterfaceMethod<
       /*desc=*/[{
-        Return the `i`-th shaped operand value, which can be an arbitrary input
-        tensor/buffer, init tensor or output buffer.
+        Return true if the payload uses the value loaded from `opOperand`. This
+        is useful to avoid loading from "write-only" memory that may be
+        uninitialized, as well as properly cloning "read-write" operands.
       }],
-      /*retTy=*/"Value",
-      /*methodName=*/"getShapedOperand",
-      /*args=*/(ins "unsigned":$i),
+      /*retTy=*/"bool",
+      /*methodName=*/"payloadUsesValueFromOpOperand",
+      /*args=*/(ins "OpOperand *":$opOperand),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        assert(i < $_op.getNumShapedOperands());
-        return this->getOperation()->getOperand(i);
+        unsigned bbArgNumber =
+          getNumPayloadInductionVariables() + opOperand->getOperandNumber();
+        // Safeguard against the named linalg ops that are manually defined and
+        // that only support buffer semantics: we should not be there.
+        // Such ops have an empty regionBuilder and are not constructed with a
+        // region for now. In the future they are slated to disappear.
+        assert(this->getOperation()->getNumRegions() == 1 && "unexpected "
+               "missing region (calling `payloadUsesValueFromOpOperand` on "
+               "manually defined named Linalg op?)");
+        Block &block = this->getOperation()->getRegion(0).front();
+        // Init tensors have uses.
+        return !block.getArgument(bbArgNumber).use_empty();
       }]
     >,
     InterfaceMethod<
       /*desc=*/[{
-        Return the range over inputs, output buffers and init tensors.
+        Return true if the payload uses the value loaded from input operand
+        `index`.
       }],
-      /*retTy=*/"Operation::operand_range",
-      /*methodName=*/"getShapedOperands",
-      /*args=*/(ins),
+      /*retTy=*/"bool",
+      /*methodName=*/"payloadUsesValueFromInputOperandIndex",
+      /*args=*/(ins "unsigned":$index),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        auto range = this->getOperation()->getOperands();
-        return {range.begin(), range.begin() + getNumShapedOperands()};
+        return payloadUsesValueFromOpOperand(&getInputOpOperands()[index]);
       }]
     >,
     InterfaceMethod<
       /*desc=*/[{
-        Return the `i`-th shaped type, there are 3 cases:
-          1. if `i < $_op.getNumInputs()` then return `getInputShapedType(i)`;
-             otherwise
-          2. if `i < getNumInputsAndOutputBuffers()` then return the
-             `getOutputBufferType(i - $_op.getNumInputs())`; otherwise
-          3. return the `i - getNumInputsAndOutputBuffers()` result type.
+        Return true if the payload uses the value loaded from output operand
+        `index`.
       }],
-      /*retTy=*/"ShapedType",
-      /*methodName=*/"getShapedType",
-      /*args=*/(ins "unsigned":$i),
+      /*retTy=*/"bool",
+      /*methodName=*/"payloadUsesValueFromOutputOperandIndex",
+      /*args=*/(ins "unsigned":$index),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        if (i < $_op.getNumInputs())
-          return getInputShapedType(i);
-        if (i < getNumInputsAndOutputBuffers())
-          return getOutputBufferType(i - $_op.getNumInputs());
-        return this->getOperation()->getResult(
-          i - getNumInputsAndOutputBuffers()).
-          getType().template cast();
-      }]>,
+        return payloadUsesValueFromOpOperand(&getOutputOpOperands()[index]);
+      }]
+    >,
     InterfaceMethod<
       /*desc=*/[{
-        Return the shaped types for all the inputs and outputs
+        Return true if `opOperand` is an init tensor. This is true when it is
+        an output tensor operand whose value is used in the payload region.
       }],
-      /*retTy=*/"SmallVector",
-      /*methodName=*/"getInputOutputShapedTypes",
+      /*retTy=*/"bool",
+      /*methodName=*/"isInitTensor",
+      /*args=*/(ins "OpOperand *":$opOperand),
+      /*methodBody=*/"",
+      /*defaultImplementation=*/[{
+        if (!opOperand->get().getType().template isa())
+          return false;
+        if (opOperand->getOperandNumber() < $_op.getNumInputs())
+          return false;
+        return payloadUsesValueFromOpOperand(opOperand);
+      }]
+    >,
+    InterfaceMethod<
+      /*desc=*/[{
+        Return true if the operand at output index `index` is an init tensor.
+      }],
+      /*retTy=*/"bool",
+      /*methodName=*/"isIndexOfInitTensor",
+      /*args=*/(ins "unsigned":$index),
+      /*methodBody=*/"",
+      /*defaultImplementation=*/[{
+        assert(index < getNumOutputs());
+        return isInitTensor(
+          &this->getOperation()->getOpOperands()[$_op.getNumInputs() + index]);
+      }]
+    >,
+    InterfaceMethod<
+      /*desc=*/[{
+        Return the output operands that are init tensors.
+      }],
+      /*retTy=*/"SmallVector",
+      /*methodName=*/"getInitTensors",
       /*args=*/(ins),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        SmallVector inputOutputTypes(
-            this->getOperation()->operand_type_begin(),
-            this->getOperation()->operand_type_end());
-        inputOutputTypes.append(this->getOperation()->result_type_begin(),
-                                this->getOperation()->result_type_end());
+        auto start =
+          this->getOperation()->getOpOperands().begin() + $_op.getNumInputs();
         return llvm::to_vector<4>(
-            llvm::map_range(inputOutputTypes, [](Type type) -> ShapedType {
-              return type.cast();
-            }));
+          llvm::map_range(
+            llvm::make_filter_range(
+              llvm::make_range(start, start + $_op.getNumOutputs()),
+              [&](OpOperand &opOperand) {
+                return $_op.isInitTensor(&opOperand);
+              }),
+              [&](OpOperand &opOperand) {
+                return opOperand.get();
+              }));
       }]
     >,
     InterfaceMethod<
       /*desc=*/[{
-        Return the first position of the shaped operand in the operand list.
+        Return the number of init tensor operands.
       }],
-      /*retTy=*/"Optional",
-      /*methodName=*/"getIndexOfShapedOperand",
-      /*args=*/(ins "Value":$value),
+      /*retTy=*/"unsigned",
+      /*methodName=*/"getNumInitTensors",
+      /*args=*/(ins),
+      /*methodBody=*/"",
+      /*defaultImplementation=*/[{
+        return getInitTensors().size();
+      }]
+    >,
+    InterfaceMethod<
+      /*desc=*/[{
+        Return the number of input and output operands.
+      }],
+      /*retTy=*/"unsigned",
+      /*methodName=*/"getNumShapedOperands",
+      /*args=*/(ins),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        Optional inputIndex = getIndexOfInput(value);
-        if (inputIndex.hasValue()) return inputIndex.getValue();
-        Optional outputIndex = getIndexOfOutputBuffer(value);
-        if (outputIndex.hasValue())
-          return $_op.getNumInputs() + outputIndex.getValue();
-        Optional initTensorIndex = getIndexOfInitTensor(value);
-        if (initTensorIndex.hasValue())
-          return $_op.getNumInputs() + $_op.getNumOutputBuffers() + initTensorIndex.getValue();
-        return llvm::None;
+        return $_op.getNumInputs() + $_op.getNumOutputs();
       }]
     >,
     InterfaceMethod<
       /*desc=*/[{
-        Returns the operand index given the input index. Returns None
-        of the input index is invalid.
+        Return the `i`-th shaped operand value.
       }],
-      /*retTy=*/"Optional",
-      /*methodName=*/"getOperandIndexForInputIndex",
-      /*args=*/(ins "unsigned":$input_index),
+      /*retTy=*/"Value",
+      /*methodName=*/"getShapedOperand",
+      /*args=*/(ins "unsigned":$i),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        if (input_index >= $_op.getNumInputs())
-          return llvm::None;
-        return input_index;
+        assert(i < $_op.getNumShapedOperands());
+        return this->getOperation()->getOperand(i);
       }]
     >,
     InterfaceMethod<
       /*desc=*/[{
-        Returns the operand index given the output index. Returns None
-        of the output index is invalid.
+        Return the range over input and output operands.
       }],
-      /*retTy=*/"Optional",
-      /*methodName=*/"getOperandIndexForOutputIndex",
-      /*args=*/(ins "unsigned":$output_index),
+      /*retTy=*/"Operation::operand_range",
+      /*methodName=*/"getShapedOperands",
+      /*args=*/(ins),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        if (output_index >= $_op.getNumOutputs())
-          return llvm::None;
-        return output_index + $_op.getNumInputs();
+        auto range = this->getOperation()->getOperands();
+        return {range.begin(), range.begin() + getNumShapedOperands()};
       }]
     >,
     InterfaceMethod<
       /*desc=*/[{
-        Returns the input index given the operand index. Return None
-        if the operand index doesnt corresponding to an input.
+        Return the OpOperands for all the shaped operands.
       }],
-      /*retTy=*/"Optional",
-      /*methodName=*/"getInputIndex",
-      /*args=*/(ins "unsigned":$operand_index),
+      /*retTy=*/" MutableArrayRef",
+      /*methodName=*/"getShapedOpOperands",
+      /*args=*/(ins),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-         if (operand_index >= $_op.getNumInputs())
-           return llvm::None;
-         return operand_index;
+        return this->getOperation()->getOpOperands().take_front(
+          getNumShapedOperands());
       }]
     >,
     InterfaceMethod<
       /*desc=*/[{
-        Returns the output index given the operand index. Return None
-        if the operand index doesnt corresponding to an output.
+        Return the range over input and output operands.
       }],
-      /*retTy=*/"Optional",
-      /*methodName=*/"getOutputIndex",
-      /*args=*/(ins "unsigned":$operand_index),
+      /*retTy=*/"SmallVector",
+      /*methodName=*/"getShapedOperandTypes",
+      /*args=*/(ins),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-         if (operand_index < $_op.getNumInputs() ||
-             operand_index >= $_op.getNumInputs() + $_op.getNumOutputs())
-           return llvm::None;
-         return operand_index - $_op.getNumInputs();
+        return llvm::to_vector<4>(
+          llvm::map_range(
+            getShapedOperands(),
+            [](Value v) { return v.getType().cast(); }));
       }]
     >,
+    InterfaceMethod<
+      /*desc=*/[{
+        Return the `i`-th shaped type
+      }],
+      /*retTy=*/"ShapedType",
+      /*methodName=*/"getShapedType",
+      /*args=*/(ins "unsigned":$i),
+      /*methodBody=*/"",
+      /*defaultImplementation=*/[{
+        return $_op.getShapedOperand(i).getType().template cast();
+      }]>,
 
     //===------------------------------------------------------------------===//
     // Other interface methods.
@@ -679,7 +797,7 @@
       /*args=*/(ins "unsigned":$i),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        assert(i < getNumInputsAndOutputs());
+        assert(i < $_op.getNumShapedOperands());
         return getIndexingMaps()[i];
       }]
     >,
@@ -719,8 +837,8 @@
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
         return this->getOperation()->getNumResults() == 0 &&
-          llvm::all_of(getInputs(),
-                       [](Value v) { return v.getType().isa(); });
+          llvm::all_of(getShapedOperands(), [](Value v) {
+            return v.getType().template isa(); });
       }]
     >,
     InterfaceMethod<
@@ -732,11 +850,9 @@
       /*args=*/(ins),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        auto isTensorType = [](Value v) {
-          return v.getType().isa();
-        };
-        return llvm::all_of(getInputs(), isTensorType) &&
-               llvm::all_of(this->getOperation()->getResults(), isTensorType);
+        return llvm::all_of(getShapedOperands(), [](Value v) {
+          return v.getType().template isa();
+        });
       }]
     >,
     InterfaceMethod<
@@ -748,7 +864,8 @@
       /*args=*/(ins),
       /*methodBody=*/"",
       /*defaultImplementation=*/[{
-        return $_op->getAttr(getSparseAttrName()).template dyn_cast_or_null() != nullptr;
+        return $_op->getAttr(getSparseAttrName()).
+          template dyn_cast_or_null() != nullptr;
       }]
     >,
     InterfaceMethod<
@@ -871,7 +988,7 @@
   ];
 
   let extraClassDeclaration = [{
-    /// Return the flat list of all operand dimension sizes in the order they 
+    /// Return the flat list of all operand dimension sizes in the order they
     /// appear in the operands.
     SmallVector createFlatListOfOperandDims(OpBuilder &, Location);
 
@@ -893,7 +1010,7 @@
       for (unsigned i = 0; i < nExtraOperands; ++i) {
         res.push_back(getOperation()->getOperand(numShapedOperands + i));
         assert((res.back().getType().isSignlessIntOrIndexOrFloat()
-                || res.back().getType().isa()) &&
+                || res.back().getType().template isa()) &&
                "expected scalar or vector type");
       }
       return res;
@@ -904,7 +1021,6 @@
     //========================================================================//
     void setNumInputs(unsigned num) { setOperandSegmentAt(0, num); }
     void setNumOutputBuffers(unsigned num) { setOperandSegmentAt(1, num); }
-    void setNumInitTensors(unsigned num) { setOperandSegmentAt(2, num); }
 
     private:
     void setOperandSegmentAt(unsigned idx, unsigned val) {
@@ -916,6 +1032,8 @@
       getOperation()->setAttr("operand_segment_sizes", newAttr);
     }
   }];
+
+  let verify = [{ return detail::verifyStructuredOpInterface($_op); }];
 }
 
 #endif // LINALG_IR_STRUCTURED_OPS_INTERFACE
diff --git a/mlir/include/mlir/Dialect/Linalg/IR/LinalgTraits.h b/mlir/include/mlir/Dialect/Linalg/IR/LinalgTraits.h
deleted file mode 100644
--- a/mlir/include/mlir/Dialect/Linalg/IR/LinalgTraits.h
+++ /dev/null
@@ -1,166 +0,0 @@
-//===- LinalgTraits.h - Linalg Traits ---------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef MLIR_DIALECT_LINALG_LINALGTRAITS_H_
-#define MLIR_DIALECT_LINALG_LINALGTRAITS_H_
-
-#include "mlir/Dialect/Linalg/IR/LinalgTypes.h"
-#include "mlir/Dialect/Utils/StructuredOpsUtils.h"
-#include "mlir/IR/AffineMap.h"
-#include "mlir/IR/BuiltinOps.h"
-#include "mlir/IR/BuiltinTypes.h"
-#include "mlir/IR/OpDefinition.h"
-#include "mlir/Support/LLVM.h"
-
-namespace mlir {
-namespace OpTrait {
-namespace linalg {
-
-/// This class provides the API for ops that are known to have a specified
-/// number of inputs, all passed as operands. Use as a trait as follows:
-///
-///   class DotOp : public Op::Impl> {
-///
-template  class NInputs {
-public:
-  template 
-  class Impl : public OpTrait::TraitBase::Impl> {
-  public:
-    static unsigned getNumInputs() { return N; }
-  };
-};
-
-/// This class provides the API for ops that are known to not have init tensor
-/// operands. Use as a trait as follows:
-///
-///   class CopyOp : public Op {
-///
-template 
-class ZeroInitTensors : public TraitBase {
-public:
-  static unsigned getNumInitTensors() { return 0; }
-};
-
-/// This class provides the API for ops that are known to have a specified
-/// number of outputs, all passed as operands. Use as a trait as follows:
-///
-///   class DotOp : public Op::Impl> {
-///
-template  class NOutputs {
-public:
-  template 
-  class Impl : public OpTrait::TraitBase::Impl> {
-  public:
-    static unsigned getNumOutputs() { return N; }
-  };
-};
-
-/// This class provides a verifier for structured ops that are known to operate
-/// on buffers or tensors. This trait must be used in conjunction with an op
-/// definition or a trait that provides the methods `getNumInputs` and
-/// `getNumOutputs`. Use as a trait as follows:
-///
-///   class DotOp : public Op {
-///
-template 
-class StructuredOpTraits
-    : public OpTrait::TraitBase {
-public:
-  static LogicalResult verifyTrait(Operation *op) {
-    ConcreteType concreteOp = cast(op);
-    auto nOperands = concreteOp.getNumInputsAndOutputBuffers();
-    if (failed(OpTrait::impl::verifyAtLeastNOperands(op, nOperands)))
-      return failure();
-    if (op->getNumResults() > concreteOp.getNumOutputs())
-      return op->emitError("unexpected #results > #outputs");
-    return success();
-  }
-};
-
-/// This class provides a verifier for structured ops that are known to operate
-/// on buffers or tensors and that support `ins`, `outs` and `init` arguments.
-/// This trait must be used in conjunction with an op definition or a trait that
-/// provides the methods `getNumInputs` and `getNumOutputs`.
-///
-/// Use as a trait as follows:
-///
-///   class MatmulOp : public Op {
-///
-template 
-class NamedStructuredOpTrait
-    : public OpTrait::TraitBase {
-public:
-  unsigned getNumInputs() {
-    return cast(this->getOperation()).inputs().size();
-  }
-  unsigned getNumInitTensors() {
-    return cast(this->getOperation()).init_tensors().size();
-  }
-  unsigned getNumOutputs() {
-    ConcreteType concreteOp = cast(this->getOperation());
-    return concreteOp.output_buffers().size() +
-           concreteOp.result_tensors().size();
-  }
-  static LogicalResult verifyTrait(Operation *op) {
-    ConcreteType concreteOp = cast(op);
-    unsigned nInputAndBufferOperands =
-        concreteOp.getNumInputsAndOutputBuffers();
-    if (failed(
-            OpTrait::impl::verifyAtLeastNOperands(op, nInputAndBufferOperands)))
-      return failure();
-
-    SmallVector redDims;
-    concreteOp.getReductionDims(redDims);
-    // If no result and no reduction, only check there is no init tensor and we
-    // are done.
-    if (redDims.empty() || op->getNumResults() == 0) {
-      if (!concreteOp.init_tensors().empty())
-        return op->emitError("expected empty `init` when op has no "
-                             "results or no reduction dims");
-      return success();
-    }
-
-    // Only a single tensor result supported atm.
-    if (op->getNumResults() != 1)
-      return op->emitError(
-          "expected single tensor result when reduction present");
-
-    if (concreteOp.init_tensors().size() != op->getNumResults())
-      return op->emitError(
-          "expected #init tensors to match #results when reduction present");
-
-    for (unsigned idx = 0, e = op->getNumResults(); idx < e; ++idx)
-      if (concreteOp.init_tensors()[idx].getType() != op->getResultTypes()[idx])
-        return op->emitError("expected init tensor #")
-               << idx << " of the same type as result #" << idx;
-
-    // Output tensor indexing map may not depend on reduction index.
-    // TODO: this is not yet tested. Add a test when linalg.generic switches to
-    // this representation.
-    for (unsigned idx = 0, e = concreteOp.getNumOutputs(); idx < e; ++idx) {
-      AffineMap outputMap = concreteOp.getOutputIndexingMap(idx);
-      for (auto expr : outputMap.getResults()) {
-        for (auto dim : redDims) {
-          unsigned pos = dim.cast().getPosition();
-          if (expr.isFunctionOfDim(pos))
-            return op->emitError(
-                       "unexpected single tensor output indexing map ")
-                   << "is function of reduction dim @" << pos;
-        }
-      }
-    }
-
-    return success();
-  }
-};
-
-} // namespace linalg
-} // namespace OpTrait
-} // namespace mlir
-
-#endif // MLIR_DIALECT_LINALG_LINALGTRAITS_H_
diff --git a/mlir/include/mlir/IR/OpBase.td b/mlir/include/mlir/IR/OpBase.td
--- a/mlir/include/mlir/IR/OpBase.td
+++ b/mlir/include/mlir/IR/OpBase.td
@@ -673,6 +673,11 @@
             MemRefRankOf<[AnyType], [rank]>.predicate]>,
        AnyStridedMemRef.description # " of rank " # rank>;
 
+class StridedMemRefRankOf allowedTypes, list ranks> :
+    Type.predicate, HasAnyRankOfPred]>,
+         StrJoin.result # " " #
+         MemRefOf.description>;
+
 // This represents a generic tuple without any constraints on element type.
 def AnyTuple : Type;
 
diff --git a/mlir/integration_test/Dialect/Linalg/CPU/test-tensor-matmul.mlir b/mlir/integration_test/Dialect/Linalg/CPU/test-tensor-matmul.mlir
--- a/mlir/integration_test/Dialect/Linalg/CPU/test-tensor-matmul.mlir
+++ b/mlir/integration_test/Dialect/Linalg/CPU/test-tensor-matmul.mlir
@@ -22,7 +22,7 @@
   %C = constant dense<1000.0> : tensor<2x4xf32>
 
   %D = linalg.matmul ins(%A, %B: tensor<2x3xf32>, tensor<3x4xf32>)
-                     init(%C: tensor<2x4xf32>) -> tensor<2x4xf32>
+                     outs(%C: tensor<2x4xf32>) -> tensor<2x4xf32>
 
   %unranked = tensor.cast %D : tensor<2x4xf32> to tensor<*xf32>
   call @print_memref_f32(%unranked) : (tensor<*xf32>) -> ()
diff --git a/mlir/lib/Dialect/Linalg/Analysis/DependenceAnalysis.cpp b/mlir/lib/Dialect/Linalg/Analysis/DependenceAnalysis.cpp
--- a/mlir/lib/Dialect/Linalg/Analysis/DependenceAnalysis.cpp
+++ b/mlir/lib/Dialect/Linalg/Analysis/DependenceAnalysis.cpp
@@ -13,6 +13,7 @@
 #include "mlir/Dialect/Linalg/Analysis/DependenceAnalysis.h"
 #include "mlir/Dialect/Linalg/IR/LinalgOps.h"
 #include "mlir/Dialect/StandardOps/IR/Ops.h"
+#include "mlir/IR/BuiltinOps.h"
 
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
@@ -113,15 +114,16 @@
 }
 
 void LinalgDependenceGraph::addDependenceElem(DependenceType dt,
-                                              LinalgOpView indexingOpView,
-                                              LinalgOpView dependentOpView) {
+                                              OpOperand *indexingOpView,
+                                              OpOperand *dependentOpView) {
   LLVM_DEBUG(dbgs() << "\nAdd dep type " << getDependenceTypeStr(dt) << ":\t ("
-                    << *indexingOpView.op << ", " << indexingOpView.operandIndex
-                    << ") -> \n\t\t(" << *dependentOpView.op << ", "
-                    << dependentOpView.operandIndex << ")");
-  dependencesFromGraphs[dt][indexingOpView.op].push_back(
+                    << indexingOpView->get() << " @"
+                    << indexingOpView->getOperandNumber() << ") -> \n\t\t("
+                    << dependentOpView->get() << " @"
+                    << dependentOpView->getOperandNumber() << ")");
+  dependencesFromGraphs[dt][indexingOpView->getOwner()].push_back(
       LinalgDependenceGraphElem{dependentOpView, indexingOpView, dt});
-  dependencesIntoGraphs[dt][dependentOpView.op].push_back(
+  dependencesIntoGraphs[dt][dependentOpView->getOwner()].push_back(
       LinalgDependenceGraphElem{indexingOpView, dependentOpView, dt});
 }
 
@@ -156,57 +158,25 @@
 }
 
 void LinalgDependenceGraph::addDependencesBetween(LinalgOp src, LinalgOp dst) {
-  for (auto srcView : llvm::enumerate(src.getOutputBuffers())) { // W
-    unsigned srcIndex =
-        src.getOperandIndexForOutputIndex(srcView.index()).getValue();
+  for (OpOperand *srcOpOperand : src.getOutputBuffersOpOperands()) { // W
     // RAW graph
-    for (auto dstView : llvm::enumerate(dst.getInputBuffers())) { // R
-      if (aliases.alias(srcView.value(),
-                        dstView.value())) { // if alias, fill RAW
-        unsigned dstIndex =
-            dst.getOperandIndexForInputIndex(dstView.index()).getValue();
-        addDependenceElem(DependenceType::RAW,
-                          LinalgOpView{src.getOperation(), srcIndex},
-                          LinalgOpView{dst.getOperation(), dstIndex});
-      }
-    }
+    for (OpOperand *dstOpOperand : dst.getInputBuffersOpOperands()) // R
+      if (aliases.alias(srcOpOperand->get(), dstOpOperand->get()))  // RAW alias
+        addDependenceElem(DependenceType::RAW, srcOpOperand, dstOpOperand);
     // WAW graph
-    for (auto dstView : llvm::enumerate(dst.getOutputBuffers())) { // W
-      if (aliases.alias(srcView.value(),
-                        dstView.value())) { // if alias, fill WAW
-        unsigned dstIndex =
-            dst.getOperandIndexForOutputIndex(dstView.index()).getValue();
-        addDependenceElem(DependenceType::WAW,
-                          LinalgOpView{src.getOperation(), srcIndex},
-                          LinalgOpView{dst.getOperation(), dstIndex});
-      }
-    }
+    for (OpOperand *dstOpOperand : dst.getOutputBuffersOpOperands()) // W
+      if (aliases.alias(srcOpOperand->get(), dstOpOperand->get())) // WAW alias
+        addDependenceElem(DependenceType::WAW, srcOpOperand, dstOpOperand);
   }
-  for (auto srcView : llvm::enumerate(src.getInputBuffers())) { // R
-    unsigned srcIndex =
-        src.getOperandIndexForInputIndex(srcView.index()).getValue();
+  for (OpOperand *srcOpOperand : src.getInputBuffersOpOperands()) { // R
     // RAR graph
-    for (auto dstView : llvm::enumerate(dst.getInputBuffers())) { // R
-      if (aliases.alias(srcView.value(),
-                        dstView.value())) { // if alias, fill RAR
-        unsigned dstIndex =
-            dst.getOperandIndexForInputIndex(dstView.index()).getValue();
-        addDependenceElem(DependenceType::RAR,
-                          LinalgOpView{src.getOperation(), srcIndex},
-                          LinalgOpView{dst.getOperation(), dstIndex});
-      }
-    }
+    for (OpOperand *dstOpOperand : dst.getInputBuffersOpOperands()) // R
+      if (aliases.alias(srcOpOperand->get(), dstOpOperand->get()))  // RAR alias
+        addDependenceElem(DependenceType::RAR, srcOpOperand, dstOpOperand);
     // WAR graph
-    for (auto dstView : llvm::enumerate(dst.getOutputBuffers())) { // W
-      if (aliases.alias(srcView.value(),
-                        dstView.value())) { // if alias, fill WAR
-        unsigned dstIndex =
-            dst.getOperandIndexForOutputIndex(dstView.index()).getValue();
-        addDependenceElem(DependenceType::WAR,
-                          LinalgOpView{src.getOperation(), srcIndex},
-                          LinalgOpView{dst.getOperation(), dstIndex});
-      }
-    }
+    for (OpOperand *dstOpOperand : dst.getOutputBuffersOpOperands()) // W
+      if (aliases.alias(srcOpOperand->get(), dstOpOperand->get())) // WAR alias
+        addDependenceElem(DependenceType::WAR, srcOpOperand, dstOpOperand);
   }
 }
 
@@ -248,17 +218,15 @@
   // TODO: we are not considering paths yet, just interleaved positions.
   for (auto dt : types) {
     for (auto dependence : getDependencesFrom(src, dt)) {
-      auto interimPos = linalgOpPositions.lookup(dependence.dependentOpView.op);
+      auto interimPos =
+          linalgOpPositions.lookup(dependence.dependentOpView->getOwner());
       // Skip if not interleaved.
       if (interimPos >= dstPos || interimPos <= srcPos)
         continue;
-      linalg::LinalgOp consumer =
-          cast(dependence.indexingOpView.op);
-      Value consumerView =
-          consumer.getShapedOperand(dependence.indexingOpView.operandIndex);
+      Value consumerView = dependence.indexingOpView->get();
       if (view && !aliases.alias(view, consumerView))
         continue;
-      auto *op = dependence.dependentOpView.op;
+      auto *op = dependence.dependentOpView->getOwner();
       LLVM_DEBUG(dbgs() << "\n***Found covering dependence of type "
                         << getDependenceTypeStr(dt) << ": " << *src << " -> "
                         << *op << " on " << consumerView);
@@ -271,12 +239,10 @@
 bool LinalgDependenceGraph::hasDependenceFrom(
     LinalgOp srcLinalgOp, LinalgOp dstLinalgOp,
     ArrayRef depTypes) const {
-  for (auto dep : depTypes) {
-    for (auto dependence : getDependencesInto(dstLinalgOp, dep)) {
-      if (dependence.dependentOpView.op == srcLinalgOp)
+  for (auto dep : depTypes)
+    for (auto dependence : getDependencesInto(dstLinalgOp, dep))
+      if (dependence.dependentOpView->getOwner() == srcLinalgOp)
         return true;
-    }
-  }
   return false;
 }
 
diff --git a/mlir/lib/Dialect/Linalg/EDSC/Builders.cpp b/mlir/lib/Dialect/Linalg/EDSC/Builders.cpp
--- a/mlir/lib/Dialect/Linalg/EDSC/Builders.cpp
+++ b/mlir/lib/Dialect/Linalg/EDSC/Builders.cpp
@@ -23,36 +23,25 @@
 
 Operation *mlir::edsc::makeGenericLinalgOp(
     ArrayRef iteratorTypes, ArrayRef inputs,
-    ArrayRef outputBuffers, ArrayRef initTensors,
-    ArrayRef resultTensorTypes,
+    ArrayRef outputs, TypeRange resultTensorTypes,
     function_ref regionBuilder, ArrayRef otherValues,
     ArrayRef otherAttributes) {
   OpBuilder &builder = edsc::ScopedContext::getBuilderRef();
 
   // Build maps
   SmallVector, 4> exprsList;
-  exprsList.reserve(inputs.size() + outputBuffers.size() + initTensors.size());
-  for (auto container : {inputs, outputBuffers, resultTensorTypes})
+  exprsList.reserve(inputs.size() + outputs.size());
+
+  for (auto container : {inputs, outputs})
     for (const StructuredIndexed &s : container)
       exprsList.emplace_back(s.getExprs().begin(), s.getExprs().end());
   auto maps = AffineMap::inferFromExprList(exprsList);
 
-  SmallVector types;
-  assert(llvm::all_of(resultTensorTypes, [](const StructuredIndexed &s) {
-    return !s.hasValue();
-  }));
-  std::copy(resultTensorTypes.begin(), resultTensorTypes.end(),
-            std::back_inserter(types));
-
-  SmallVector inputValues, outputBufferValues, initTensorValues;
+  SmallVector inputValues, outputValues;
   inputValues.reserve(inputs.size());
-  outputBufferValues.reserve(outputBuffers.size());
-  initTensorValues.reserve(initTensors.size());
+  outputValues.reserve(outputs.size());
   std::copy(inputs.begin(), inputs.end(), std::back_inserter(inputValues));
-  std::copy(outputBuffers.begin(), outputBuffers.end(),
-            std::back_inserter(outputBufferValues));
-  std::copy(initTensors.begin(), initTensors.end(),
-            std::back_inserter(initTensorValues));
+  std::copy(outputs.begin(), outputs.end(), std::back_inserter(outputValues));
 
   auto iteratorStrTypes =
       llvm::to_vector<8>(llvm::map_range(iteratorTypes, toString));
@@ -61,10 +50,9 @@
       edsc::ScopedContext::getBuilderRef()
           .create(
               edsc::ScopedContext::getLocation(),
-              types,
+              resultTensorTypes,
               inputValues,
-              outputBufferValues,
-              initTensorValues,
+              outputValues,
               builder.getAffineMapArrayAttr(maps),
               builder.getStrArrayAttr(iteratorStrTypes),
               StringAttr() /*doc*/,
@@ -77,12 +65,10 @@
 
   using namespace edsc;
   SmallVector blockTypes;
-  blockTypes.reserve(inputs.size() + outputBuffers.size() + initTensors.size());
-  for (auto container : {inputs, outputBuffers})
+  blockTypes.reserve(inputs.size() + outputs.size());
+  for (auto container : {inputs, outputs})
     for (const StructuredIndexed &s : container)
       blockTypes.push_back(getElementTypeOrSelf(s.getType()));
-  for (Value v : initTensors)
-    blockTypes.push_back(getElementTypeOrSelf(v.getType()));
 
   assert(op->getNumRegions() == 1);
   assert(op->getRegion(0).empty());
@@ -119,11 +105,10 @@
     linalg_yield(unaryOp(a));
   };
   if (O.getType().isa())
-    return makeGenericLinalgOp(iterTypes, /*inputs=*/{I}, /*outputBuffers=*/{},
-                               /*initTensors=*/{}, /*resultTensorTypes=*/{O},
-                               fun);
-  return makeGenericLinalgOp(iterTypes, /*inputs=*/{I}, /*outputBuffers=*/{O},
-                             /*initTensors=*/{}, /*resultTensorTypes=*/{}, fun);
+    return makeGenericLinalgOp(iterTypes, /*inputs=*/{I}, /*outputs=*/{O},
+                               /*resultTensorTypes=*/{O}, fun);
+  return makeGenericLinalgOp(iterTypes, /*inputs=*/{I}, /*outputs=*/{O},
+                             /*resultTensorTypes=*/{}, fun);
 }
 
 Operation *mlir::edsc::ops::linalg_generic_pointwise_tanh(StructuredIndexed I,
@@ -144,12 +129,10 @@
     linalg_yield(binaryOp(a, b));
   };
   if (O.getType().isa())
-    return makeGenericLinalgOp(
-        iterTypes, /*inputs=*/{I1, I2}, /*outputBuffers=*/{},
-        /*initTensors=*/{}, /*resultTensorTypes=*/{O}, fun);
+    return makeGenericLinalgOp(iterTypes, /*inputs=*/{I1, I2}, /*outputs=*/{O},
+                               /*resultTensorTypes=*/{O}, fun);
   return makeGenericLinalgOp(iterTypes, /*inputs=*/{I1, I2},
-                             /*outputBuffers=*/{O},
-                             /*initTensors=*/{}, /*resultTensorTypes=*/{}, fun);
+                             /*outputs=*/{O}, /*resultTensorTypes=*/{}, fun);
 }
 
 Operation *mlir::edsc::ops::linalg_generic_pointwise_add(StructuredIndexed I1,
@@ -181,8 +164,7 @@
   return makeGenericLinalgOp(
     {IteratorType::Parallel, IteratorType::Parallel, IteratorType::Reduction},
     /*inputs=*/{A({m, k}), B({k, n})},
-    /*outputBuffers=*/{C({m, n})},
-    /*initTensors=*/{},
+    /*outputs=*/{C({m, n})},
     /*resultTensorTypes=*/{},
     regionBuilder);
   // clang-format on
@@ -199,8 +181,7 @@
   return makeGenericLinalgOp(
     {IteratorType::Parallel, IteratorType::Parallel, IteratorType::Reduction},
     /*inputs=*/{A({m, k}), B({k, n})},
-    /*outputBuffers=*/{},
-    /*initTensors=*/{C({m, n})},
+    /*outputs=*/{C({m, n})},
     /*resultTensorTypes=*/{D({m, n})},
     regionBuilder);
   // clang-format on
@@ -236,8 +217,7 @@
          simplifyAffineExpr(s[1] * w + d[1] * kw, numDims, 0),
          c}),
       W({kh, kw, c, f}) },
-    /*outputBuffers=*/{ O({b, h, w, f}) },
-    /*initTensors=*/{},
+    /*outputs=*/{ O({b, h, w, f}) },
     /*resultTensorTypes=*/{},
     macRegionBuilder);
   // clang-format on
@@ -272,9 +252,8 @@
          simplifyAffineExpr(s[1] * w + d[1] * kw, numDims, 0),
          c}),
       W({kh, kw, c, dm})},
-    /*outputBuffers=*/{
+    /*outputs=*/{
       O({b, h, w, simplifyAffineExpr(c * depth_multiplier + dm, numDims, 0)})},
-    /*initTensors=*/{},
     /*resultTensorTypes=*/{},
     macRegionBuilder);
   // clang-format on
diff --git a/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp b/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
--- a/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
+++ b/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
@@ -88,22 +88,20 @@
 
 /// Forward declarations.
 template 
-static void buildNamedStructuredOpRegionAndAttributes(
-    OpBuilder &opBuilder, OperationState &result, TypeRange inputTypes,
-    TypeRange outputBufferTypes, TypeRange initTensorTypes,
-    TypeRange resultTypes);
+static void buildNamedStructuredOpRegionAndAttributes(OpBuilder &opBuilder,
+                                                      OperationState &result,
+                                                      TypeRange inputTypes,
+                                                      TypeRange outputTypes);
 
 static ParseResult
 parseCommonStructuredOpParts(OpAsmParser &parser, OperationState &result,
                              SmallVectorImpl &inputTypes,
-                             SmallVectorImpl &outputBufferTypes,
-                             SmallVectorImpl &initTensorTypes);
+                             SmallVectorImpl &outputTypes);
 
 template 
 static ParseResult
 parseNamedStructuredOpRegion(OpAsmParser &parser, Region ®ion,
-                             TypeRange inputTypes, TypeRange outputBufferTypes,
-                             TypeRange initTensorTypes, TypeRange resultTypes);
+                             TypeRange inputTypes, TypeRange outputTypes);
 static ParseResult
 parseNamedStructuredOpResults(OpAsmParser &parser,
                               SmallVectorImpl &resultTypes);
@@ -122,9 +120,6 @@
 template 
 static void printNamedStructuredOp(OpAsmPrinter &p, NamedStructuredOpType op);
 
-template 
-static LogicalResult verifyNamedStructuredOp(NamedStructuredOpType op);
-
 /// This is a common class used for patterns of the form
 /// ```
 ///    someop(memrefcast) -> someop
@@ -152,11 +147,10 @@
 //===----------------------------------------------------------------------===//
 void GenericOp::build(
     OpBuilder &builder, OperationState &result, TypeRange resultTensorTypes,
-    ValueRange inputs, ValueRange outputBuffers, ValueRange initTensors,
-    ArrayRef indexingMaps, ArrayRef iteratorTypes,
-    StringRef doc, StringRef libraryCall,
+    ValueRange inputs, ValueRange outputs, ArrayRef indexingMaps,
+    ArrayRef iteratorTypes, StringRef doc, StringRef libraryCall,
     function_ref bodyBuild) {
-  build(builder, result, resultTensorTypes, inputs, outputBuffers, initTensors,
+  build(builder, result, resultTensorTypes, inputs, outputs,
         builder.getAffineMapArrayAttr(indexingMaps),
         builder.getStrArrayAttr(iteratorTypes),
         doc.empty() ? StringAttr() : builder.getStringAttr(doc),
@@ -166,7 +160,7 @@
     return;
 
   SmallVector blockArgTypes;
-  for (ValueRange container : {inputs, outputBuffers, initTensors})
+  for (ValueRange container : {inputs, outputs})
     for (Value v : container)
       blockArgTypes.push_back(v.getType().cast().getElementType());
 
@@ -178,41 +172,40 @@
 
 void GenericOp::build(
     OpBuilder &builder, OperationState &result, ValueRange inputs,
-    ValueRange outputBuffers, ArrayRef indexingMaps,
+    ValueRange outputs, ArrayRef indexingMaps,
     ArrayRef iteratorTypes, StringRef doc, StringRef libraryCall,
     function_ref bodyBuild) {
-  build(builder, result, TypeRange{}, inputs, outputBuffers, ValueRange{},
-        indexingMaps, iteratorTypes, doc, libraryCall, bodyBuild);
+  build(builder, result, TypeRange{}, inputs, outputs, indexingMaps,
+        iteratorTypes, doc, libraryCall, bodyBuild);
 }
 
 void GenericOp::build(
     OpBuilder &builder, OperationState &result, ValueRange inputs,
-    ValueRange outputBuffers, ArrayRef indexingMaps,
+    ValueRange outputs, ArrayRef indexingMaps,
     ArrayRef iteratorTypes,
     function_ref bodyBuild) {
-  build(builder, result, inputs, outputBuffers, indexingMaps, iteratorTypes,
+  build(builder, result, inputs, outputs, indexingMaps, iteratorTypes,
         /*doc=*/"",
         /*libraryCall=*/"", bodyBuild);
 }
 
 void GenericOp::build(
     OpBuilder &builder, OperationState &result, TypeRange resultTensorTypes,
-    ValueRange inputs, ValueRange outputBuffers, ValueRange initTensors,
-    ArrayRef indexingMaps, ArrayRef iteratorTypes,
+    ValueRange inputs, ValueRange outputs, ArrayRef indexingMaps,
+    ArrayRef iteratorTypes,
     function_ref bodyBuild) {
-  build(builder, result, resultTensorTypes, inputs, outputBuffers, initTensors,
-        indexingMaps, iteratorTypes,
+  build(builder, result, resultTensorTypes, inputs, outputs, indexingMaps,
+        iteratorTypes,
         /*doc=*/"",
         /*libraryCall=*/"", bodyBuild);
 }
 void IndexedGenericOp::build(
     OpBuilder &builder, OperationState &result, TypeRange resultTensorTypes,
-    ValueRange inputs, ValueRange outputBuffers, ValueRange initTensors,
-    ArrayRef indexingMaps, ArrayRef iteratorTypes,
-    StringRef doc, StringRef libraryCall,
+    ValueRange inputs, ValueRange outputs, ArrayRef indexingMaps,
+    ArrayRef iteratorTypes, StringRef doc, StringRef libraryCall,
     function_ref
         bodyBuild) {
-  build(builder, result, resultTensorTypes, inputs, outputBuffers, initTensors,
+  build(builder, result, resultTensorTypes, inputs, outputs,
         builder.getAffineMapArrayAttr(indexingMaps),
         builder.getStrArrayAttr(iteratorTypes),
         doc.empty() ? StringAttr() : builder.getStringAttr(doc),
@@ -223,7 +216,7 @@
 
   unsigned nLoops = iteratorTypes.size();
   SmallVector blockArgTypes(nLoops, builder.getIndexType());
-  for (ValueRange container : {inputs, outputBuffers, initTensors})
+  for (ValueRange container : {inputs, outputs})
     for (Value v : container)
       blockArgTypes.push_back(v.getType().cast().getElementType());
 
@@ -237,32 +230,32 @@
 
 void IndexedGenericOp::build(
     OpBuilder &builder, OperationState &result, ValueRange inputs,
-    ValueRange outputBuffers, ArrayRef indexingMaps,
+    ValueRange outputs, ArrayRef indexingMaps,
     ArrayRef iteratorTypes, StringRef doc, StringRef libraryCall,
     function_ref
         bodyBuild) {
-  build(builder, result, TypeRange{}, inputs, outputBuffers, ValueRange{},
-        indexingMaps, iteratorTypes, doc, libraryCall, bodyBuild);
+  build(builder, result, TypeRange{}, inputs, outputs, indexingMaps,
+        iteratorTypes, doc, libraryCall, bodyBuild);
 }
 
 void IndexedGenericOp::build(
     OpBuilder &builder, OperationState &result, ValueRange inputs,
-    ValueRange outputBuffers, ArrayRef indexingMaps,
+    ValueRange outputs, ArrayRef indexingMaps,
     ArrayRef iteratorTypes,
     function_ref
         bodyBuild) {
-  build(builder, result, inputs, outputBuffers, indexingMaps, iteratorTypes,
+  build(builder, result, inputs, outputs, indexingMaps, iteratorTypes,
         /*doc=*/"", /*libraryCall=*/"", bodyBuild);
 }
 
 void IndexedGenericOp::build(
     OpBuilder &builder, OperationState &result, TypeRange resultTensorTypes,
-    ValueRange inputs, ValueRange outputBuffers, ValueRange initTensors,
-    ArrayRef indexingMaps, ArrayRef iteratorTypes,
+    ValueRange inputs, ValueRange outputs, ArrayRef indexingMaps,
+    ArrayRef iteratorTypes,
     function_ref
         bodyBuild) {
-  build(builder, result, resultTensorTypes, inputs, outputBuffers, initTensors,
-        indexingMaps, iteratorTypes,
+  build(builder, result, resultTensorTypes, inputs, outputs, indexingMaps,
+        iteratorTypes,
         /*doc=*/"",
         /*libraryCall=*/"", bodyBuild);
 }
@@ -327,9 +320,8 @@
                            dictAttr.getValue().end());
 
   // Parsing is shared with named ops, except for the region.
-  SmallVector inputTypes, outputBufferTypes, initTensorTypes;
-  if (parseCommonStructuredOpParts(parser, result, inputTypes,
-                                   outputBufferTypes, initTensorTypes))
+  SmallVector inputTypes, outputTypes;
+  if (parseCommonStructuredOpParts(parser, result, inputTypes, outputTypes))
     return failure();
 
   // Optional attributes may be added.
@@ -360,7 +352,7 @@
 static void getGenericEffectsImpl(
     SmallVectorImpl>
         &effects,
-    ValueRange results, ValueRange inputBuffers, ValueRange outputBuffers) {
+    ValueRange results, ValueRange inputBuffers, ValueRange outputs) {
   for (Value value : results) {
     effects.emplace_back(MemoryEffects::Allocate::get(), value,
                          SideEffects::DefaultResource::get());
@@ -369,7 +361,7 @@
     effects.emplace_back(MemoryEffects::Read::get(), value,
                          SideEffects::DefaultResource::get());
   }
-  for (Value value : outputBuffers) {
+  for (Value value : outputs) {
     effects.emplace_back(MemoryEffects::Read::get(), value,
                          SideEffects::DefaultResource::get());
     effects.emplace_back(MemoryEffects::Write::get(), value,
@@ -391,65 +383,150 @@
                         getInputBuffers(), getOutputBuffers());
 }
 
-namespace {
+LogicalResult mlir::linalg::detail::verifyStructuredOpInterface(Operation *op) {
+  LinalgOp linalgOp = cast(op);
+  // Expect at least one shaped operand.
+  // This means an op that constructs a tensor out of indices cannot be a
+  // LinalgOp at the moment. For now this will have to be a special op until we
+  // have output shape operands that are not tensors.
+  auto nShapedOperands = linalgOp.getNumShapedOperands();
+  if (nShapedOperands == 0)
+    return linalgOp.emitOpError("expected at least 1 Shaped operand");
+  if (failed(OpTrait::impl::verifyAtLeastNOperands(op, nShapedOperands)))
+    return failure();
+  // Should have at least one output tensor per result tensor.
+  // Can also have outbut buffers that do not correspond to results.
+  if (op->getNumResults() > linalgOp.getNumOutputTensors())
+    return op->emitError("unexpected #results > #outputs");
+
+  // All shaped operands must be indexed.
+  if (linalgOp.indexing_maps().size() != linalgOp.getNumShapedOperands())
+    return linalgOp.emitOpError("expected the number of indexing_map (")
+           << linalgOp.indexing_maps().size()
+           << ") to be equal to the number of shaped operands ("
+           << linalgOp.getNumShapedOperands() << ")";
 
-template 
-struct BlockArgsVerifier {
-  static LogicalResult verify(GenericOpType op, Block &block);
-};
+  SmallVector indexingMaps;
+  indexingMaps.reserve(linalgOp.indexing_maps().size());
+  for (auto en : llvm::enumerate(linalgOp.indexing_maps())) {
+    auto idx = en.index();
+    auto m = en.value().template cast().getValue();
+    indexingMaps.push_back(m); // Save reference to map for further checks.
+    auto shapedValue = linalgOp.getShapedType(idx);
 
-template 
-LogicalResult BlockArgsVerifier::verify(GenericOpType op,
-                                                       Block &block) {
-  auto nOperands = op.getNumOperands();
-  if (block.getNumArguments() != nOperands)
-    return op.emitOpError("expected number of block arguments to match number "
-                          "of operands");
+    // Symbols disallowed.
+    if (m.getNumSymbols() != 0)
+      return linalgOp.emitOpError("unexpected symbols in indexing_map #")
+             << idx;
 
-  // Note: the number and type of yield values are checked in the YieldOp.
-  auto nInputViews = op.getNumInputs();
-  for (unsigned i = 0; i < nOperands; ++i) {
-    auto viewType = op.getShapedType(i);
-    if (viewType.getElementType() != block.getArgument(i).getType())
-      return op.emitOpError("expected block argument ")
-             << (i + 1) << " of the same type as elemental type of "
-             << ((i < nInputViews) ? "input " : "output ")
-             << "operand: " << viewType;
+    // Domain must be consistent.
+    auto nLoops = linalgOp.getNumLoops();
+    if (m.getNumDims() != nLoops)
+      return linalgOp.emitOpError("expected indexing_map #")
+             << idx << " to have " << nLoops
+             << " dim(s) to match the number of loops";
+
+    if (m.getNumResults() != shapedValue.getRank())
+      return linalgOp.emitOpError("expected shaped value rank (")
+             << shapedValue.getRank()
+             << ") to match the result rank of indexing_map #" << idx << " ("
+             << m.getNumResults() << ")";
   }
-  return success();
-}
 
-template <>
-LogicalResult BlockArgsVerifier::verify(IndexedGenericOp op,
-                                                          Block &block) {
-  auto nInputViews = op.getNumInputs();
-  auto nLoops = op.getNumLoops();
-  auto nOperands = op.getNumOperands();
-  if (block.getNumArguments() != nOperands + nLoops)
-    return op.emitOpError(
-        "expected number of block arguments to match number of operands + "
-        "number of loops");
+  SmallVector redDims;
+  linalgOp.getReductionDims(redDims);
+
+  // Simplifying assumption: either full tensor or full buffer mode.
+  // This allows simpler verification of output operands vs result types
+  // without premature tracking of which operand is what in mixed-mode.
+  // TODO: relax when mixed-mode needs to pass verification.
+  if (linalgOp.getNumOutputBuffers() > 0 && linalgOp.getNumOutputTensors() > 0)
+    return op->emitError("expected output operands to all have tensor type or "
+                         "all have buffer type");
+
+  for (auto it :
+       llvm::zip(linalgOp.getOutputOpOperands(), op->getResultTypes())) {
+    if (!std::get<0>(it).get().getType().isa())
+      continue;
+    if (std::get<0>(it).get().getType() != std::get<1>(it))
+      return op->emitError("expected type of operand #")
+             << std::get<0>(it).getOperandNumber() << " ("
+             << std::get<0>(it).get().getType() << ")"
+             << " to match type of corresponding result (" << std::get<1>(it)
+             << ")";
+  }
+
+  // Output tensor indexing map may not depend on reduction indices.
+  for (OpOperand &opOperand : linalgOp.getOutputOpOperands()) {
+    AffineMap outputMap = linalgOp.getIndexingMap(opOperand.getOperandNumber());
+    for (auto expr : outputMap.getResults()) {
+      for (auto dim : redDims) {
+        unsigned pos = dim.cast().getPosition();
+        if (expr.isFunctionOfDim(pos)) {
+          std::string exprStr;
+          {
+            llvm::raw_string_ostream os(exprStr);
+            os << expr;
+          }
+          return op->emitError(
+                     "unexpected output tensor expression in indexing map #")
+                 << (opOperand.getOperandNumber() - linalgOp.getNumInputs())
+                 << " a.k.a '" << exprStr
+                 << "' is function of reduction iterator 'd" << pos << "'";
+        }
+      }
+    }
+  }
+
+  // Named ops that are defined manually have a region builder but no region at
+  // this time. Assume the region is well-formed by specification.
+  // TODO: use linalg-ods-gen for all ops when we have enough expressive power.
+  if (linalgOp->getNumRegions() == 0) {
+    assert(!linalgOp.getRegionBuilder() && "regionBuilder but no region");
+    return success();
+  }
+
+  auto ®ion = linalgOp->getRegion(0);
+  if (linalgOp->getNumRegions() > 1 || !llvm::hasSingleElement(region))
+    return op->emitOpError("expected 1 region with 1 block");
+
+  if (!linalgOp.getShapesToLoopsMap())
+    return op->emitOpError("expected the shape-to-loops map to be non-null");
+
+  // Simplifying assumption: bbargs match 1-1 with shape operands elemental
+  // types.
+  // TODO: once ranked shape types are plugged in, we may want to drop the
+  // corresponding bbargs, that can never be read from. This will be subject to
+  // consistency discussions (i.e. what to do with output tensors whose bbarg is
+  // not used).
+  Block &block = linalgOp->getRegion(0).front();
+  unsigned numBBIvs = linalgOp.getNumPayloadInductionVariables();
+
+  if (linalgOp.getNumShapedOperands() + numBBIvs != block.getNumArguments())
+    return op->emitError("expected as many non-induction variable region "
+                         "arguments as the number of shaped operands");
 
   // Note: the number and type of yield values are checked in the YieldOp.
-  for (unsigned i = 0; i < nLoops; ++i)
+  for (unsigned i = 0; i < numBBIvs; ++i)
     if (!block.getArgument(i).getType().isIndex())
-      return op.emitOpError("expected block argument ")
-             << (i + 1) << " to be an index";
-
-  for (unsigned i = 0; i < nOperands; ++i) {
-    unsigned memrefArgIndex = i + nLoops;
-    auto viewType = op.getShapedType(i);
-    if (viewType.getElementType() !=
-        block.getArgument(memrefArgIndex).getType())
-      return op.emitOpError("expected block argument ")
-             << (memrefArgIndex + 1)
-             << " of the same type as elemental type of "
-             << ((i < nInputViews) ? "input " : "output ")
-             << "operand: " << viewType;
+      return op->emitOpError("expected index block argument #") << i;
+
+  unsigned idx = 0;
+  for (auto it : llvm::zip(linalgOp.getShapedOperandTypes(),
+                           block.getArguments().drop_front(numBBIvs))) {
+    if (std::get<0>(it).getElementType() != std::get<1>(it).getType())
+      return op->emitError("expected type of bb argument #")
+             << (idx + numBBIvs) << " (" << std::get<1>(it).getType() << ")"
+             << " to match element type of corresponding shaped operand ("
+             << std::get<0>(it).getElementType() << ")";
+    ++idx;
   }
+
   return success();
 }
 
+namespace {
+
 template 
 struct AnnotationsVerifier {
   static LogicalResult verify(GenericOpType op) { return success(); }
@@ -465,7 +542,7 @@
     return op.emitOpError("expected sparse annotations on tensors only");
   if (op.getNumOutputs() != 1)
     return op.emitOpError("expected single output tensor");
-  unsigned numTensors = op.getNumInputsAndOutputs();
+  unsigned numTensors = op.getNumShapedOperands();
   if (sparseAttr.size() != numTensors)
     return op.emitOpError("expected one sparse annotation for each tensor");
   for (unsigned t = 0; t < numTensors; t++) {
@@ -497,49 +574,6 @@
 
 template 
 static LogicalResult verifyGenericOp(GenericOpType op) {
-  auto nLoops = op.getNumLoops();
-
-  if (op.inputs().size() + op.output_buffers().size() +
-          op.init_tensors().size() + op.getNumResults() ==
-      0)
-    return op.emitOpError("expected at least 1 Shaped operand or return");
-
-  auto ®ion = op.region();
-  if (!llvm::hasSingleElement(region))
-    return op.emitOpError("expected region with 1 block");
-  if (failed(BlockArgsVerifier::verify(op, region.front())))
-    return failure();
-
-  if (op.indexing_maps().size() != op.getNumInputsAndOutputs())
-    return op.emitOpError("expected the number of indexing_map (")
-           << op.indexing_maps().size()
-           << ") to be equal to the number of inputs and outputs ("
-           << op.getNumInputsAndOutputs() << ")";
-
-  SmallVector indexingMaps;
-  indexingMaps.reserve(op.indexing_maps().size());
-  for (auto en : llvm::enumerate(op.indexing_maps())) {
-    auto idx = en.index();
-    auto m = en.value().template cast().getValue();
-    indexingMaps.push_back(m); // Save reference to map for further checks.
-    auto view = op.getShapedType(idx);
-
-    if (m.getNumSymbols() != 0)
-      return op.emitOpError("unexpected symbols in indexing_map #") << idx;
-
-    if (m.getNumDims() != nLoops)
-      return op.emitOpError("expected indexing_map #")
-             << idx << " to have " << nLoops
-             << " dim(s) to match the number of loops";
-
-    if (m.getNumResults() != view.getRank())
-      return op.emitOpError("expected indexing_map #")
-             << idx << " results to match view rank: " << view;
-  }
-
-  if (!op.getShapesToLoopsMap())
-    return op.emitOpError("expected the shape-to-loops map to be non-null");
-
   if (failed(AnnotationsVerifier::verify(op)))
     return failure();
 
@@ -1380,8 +1414,6 @@
     return op.emitOpError("expects memref elemental types to match");
   if (oType.getRank() != iType.getRank() || oType.getRank() != fType.getRank())
     return op.emitOpError("expects memref ranks to match");
-  if (oType.getRank() <= 2)
-    return op.emitOpError("expects memref ranks to be greater than 2");
   if (auto strides = op.strides()) {
     if (failed(
             verifyStrideOrDilation(op, strides->getValue(), /*isStride=*/true)))
@@ -1591,13 +1623,12 @@
 template