Add a linalg.range_intersect op.
authorNicolas Vasilache <ntv@google.com>
Thu, 16 May 2019 15:15:10 +0000 (08:15 -0700)
committerMehdi Amini <joker.eph@gmail.com>
Mon, 20 May 2019 20:44:07 +0000 (13:44 -0700)
    This CL adds an operation whose purpose is to encode boundary conditions directly in the view type. In particular, full/partial tile distinction can
    occur at the level of metadata only.
    This CL also adopts a Linalg_Op pattern that is similar to Std_Op.

--

PiperOrigin-RevId: 248529469

mlir/include/mlir/Linalg/IR/LinalgBase.td
mlir/include/mlir/Linalg/IR/LinalgLibraryOps.td
mlir/include/mlir/Linalg/IR/LinalgOps.td
mlir/lib/Linalg/IR/LinalgOps.cpp
mlir/lib/Linalg/Transforms/Tiling.cpp
mlir/test/Linalg/roundtrip.mlir
mlir/test/Linalg/tile.mlir

index 42e5bcd..55bd4e7 100644 (file)
 //
 //===----------------------------------------------------------------------===//
 
-#ifdef LINALG_OPS
-#else
+include "mlir/IR/OpBase.td"
 
-#ifdef OP_BASE
+#ifdef LINALG_BASE
 #else
-include "mlir/IR/OpBase.td"
-#endif // OP_BASE
+#define LINALG_BASE
 
 def Linalg_Dialect : Dialect {
   let name = "linalg";
@@ -35,8 +33,12 @@ def Linalg_Dialect : Dialect {
 def LinalgIsBufferTypePred : CPred<"$_self.isa<BufferType>()">;
 def Buffer : Type<LinalgIsBufferTypePred, "buffer">;
 
+// Whether a type is a RangeType.
+def LinalgIsRangeTypePred : CPred<"$_self.isa<RangeType>()">;
+def Range : Type<LinalgIsRangeTypePred, "range">;
+
 // Whether a type is a ViewType.
 def LinalgIsViewTypePred : CPred<"$_self.isa<ViewType>()">;
 def View : Type<LinalgIsViewTypePred, "view">;
 
-#endif // LINALG_OPS
\ No newline at end of file
+#endif // LINALG_BASE
\ No newline at end of file
index 15b9fab..70d8637 100644 (file)
 //
 //===----------------------------------------------------------------------===//
 
-#ifdef LINALG_OPS
-#else
+include "mlir/Linalg/IR/LinalgBase.td"
 
-#ifdef LINALG_BASE
+#ifdef LINALG_LIBRARY_OPS
 #else
-include "mlir/Linalg/IR/LinalgBase.td"
-#endif // LINALG_BASE
+#define LINALG_LIBRARY_OPS
 
 class LinalgParametricNativeOpTrait<string prop, string parameters> :
   NativeOpTrait<"linalg::" # prop # parameters>
@@ -68,7 +66,7 @@ LinalgParametricIntNativeOpTrait<"ViewRanks", ranks>
 {}
 
 // Base Tablegen class for Linalg ops.
-class LinalgOp<string mnemonic, list<OpTrait> props> :
+class LinalgLibrary_Op<string mnemonic, list<OpTrait> props> :
 Op<Linalg_Dialect, mnemonic, props> {
   let arguments = (ins Variadic<View>); // default variadic builder
   let parser = [{ return parseLinalgLibraryOp(parser, result); }];
@@ -78,14 +76,14 @@ Op<Linalg_Dialect, mnemonic, props> {
 ////////////////////////////////////////////////////////////////////////////////
 // Concrete Linalg ops.
 ////////////////////////////////////////////////////////////////////////////////
-def DotOp : LinalgOp<"dot", [NInputsAndOutputs<2, 1>,
+def DotOp : LinalgLibrary_Op<"dot", [NInputsAndOutputs<2, 1>,
                              NLoopTypes<0, 1, 0>,
                              ViewRanks<[1, 1, 0]>]> {}
-def MatvecOp : LinalgOp<"matvec", [NInputsAndOutputs<2, 1>,
+def MatvecOp : LinalgLibrary_Op<"matvec", [NInputsAndOutputs<2, 1>,
                                    NLoopTypes<1, 1, 0>,
                                    ViewRanks<[2, 1, 1]>]> {}
-def MatmulOp : LinalgOp<"matmul", [NInputsAndOutputs<2, 1>,
+def MatmulOp : LinalgLibrary_Op<"matmul", [NInputsAndOutputs<2, 1>,
                                    NLoopTypes<2, 1, 0>,
                                    ViewRanks<[2, 2, 2]>]> {}
 
-#endif // LINALG_OPS
\ No newline at end of file
+#endif // LINALG_LIBRARY_OPS
\ No newline at end of file
index ecdb111..c595b4c 100644 (file)
 //
 //===----------------------------------------------------------------------===//
 
+include "mlir/Linalg/IR/LinalgBase.td"
+
 #ifdef LINALG_OPS
 #else
+#define LINALG_OPS
 
-#ifdef LINALG_BASE
-#else
-include "mlir/Linalg/IR/LinalgBase.td"
-#endif // LINALG_BASE
+// Base class for Linalg dialect ops that do not correspond to library calls.
+class Linalg_Op<string mnemonic, list<OpTrait> traits = []> :
+    Op<Linalg_Dialect, mnemonic, traits> {
+  // For every linalg op, there needs to be a:
+  //   * void print(OpAsmPrinter *p, ${C++ class of Op} op)
+  //   * LogicalResult verify(${C++ class of Op} op)
+  //   * ParseResult parse${C++ class of Op}(OpAsmParser *parser,
+  //                                         OperationState *result)
+  // functions.
+  let printer = [{ return ::print(p, *this); }];
+  let verifier = [{ return ::verify(*this); }];
+  let parser = [{ return ::parse$cppClass(parser, result); }];
+}
 
 def BufferSizeOp :
-    Op<Linalg_Dialect, "buffer_size", [NoSideEffect]>,
+    Linalg_Op<"buffer_size", [NoSideEffect]>,
     Arguments<(ins Buffer)>,
-    Results<(outs Index)>
-{
-  let parser = [{ return parseBufferSizeOp(parser, result); }];
-  let printer = [{ return printBufferSizeOp(p, *this); }];
+    Results<(outs Index)> {
+  let summary = "buffer size operation";
+  let description = [{
+    The "linalg.buffer_size" operation takes a linalg.buffer and returns an
+    "index". For example:
+    
+       %0 = linalg.buffer_size %arg0 : !linalg.buffer<f32>
+  }];
+  // Fully specified by traits.
+  let verifier = ?;
 }
 
-def DimOp : Op<Linalg_Dialect, "dim", [NoSideEffect]>,
+def DimOp : Linalg_Op<"dim", [NoSideEffect]>,
     Arguments<(ins View:$view, APIntAttr:$index)>,
     Results<(outs Index)> {
   let summary = "dimension index operation";
@@ -48,12 +66,14 @@ def DimOp : Op<Linalg_Dialect, "dim", [NoSideEffect]>,
       %1 = linalg.dim %0, 2 : view<?x?x?xf32>
   }];
 
-  let parser = [{ return parseDimOp(parser, result); }];
-  let printer = [{ return printDimOp(p, *this); }];
-  let verifier = [{ return ::verify(*this); }];
+  let verifier = [{
+    if (getIndex() >= getViewType().getRank())
+      return emitOpError("index is out of range");
+    return success();
+  }];
 
   let builders = [OpBuilder<
-    "Builder *builder, OperationState *result, Value *view," "unsigned index",
+    "Builder *builder, OperationState *result, Value *view, unsigned index",
     [{
       result->addOperands(view);
       result->addAttribute(
@@ -65,7 +85,31 @@ def DimOp : Op<Linalg_Dialect, "dim", [NoSideEffect]>,
     unsigned getIndex() {
       return getAttrOfType<IntegerAttr>("index").getValue().getZExtValue();
     }
+    ViewType getViewType() { return getOperand()->getType().cast<ViewType>(); }
   }];
 }
 
+def RangeIntersectOp : Linalg_Op<"range_intersect", [NoSideEffect]>,
+    Arguments<(ins Range, Range)>,
+    Results<(outs Range)> {
+  let summary = "range intersection operation";
+  let description = [{
+    The "linalg.range_intersect" operation takes two linalg.range and returns a
+    linalg.range that represents their intersection. This assumes both steps
+    are one for now. For example:
+
+      %2 = linalg.range_intersect %0, %1 : !linalg.range
+  }];
+
+  // Fully verified by traits.
+  let verifier = ?;
+
+  let builders = [OpBuilder<
+    "Builder *builder, OperationState *result, Value *range1, Value *range2",
+    [{
+      result->addOperands({range1, range2});
+      result->types.push_back(builder->getType<RangeType>());
+    }]>];
+}
+
 #endif // LINALG_OPS
\ No newline at end of file
index d077927..c0d1856 100644 (file)
@@ -488,12 +488,12 @@ void mlir::linalg::ViewOp::print(OpAsmPrinter *p) {
   *p << "] : " << getType();
 }
 
-/// Buffer size prints as:
-///
-/// ``` {.mlir}
-///    %0 = linalg.buffer_size %arg0 : !linalg.buffer<f32>
-/// ```
-static void printBufferSizeOp(OpAsmPrinter *p, BufferSizeOp op) {
+///////////////////// Operations defined with Tablegen /////////////////////////
+// For such operations that do not correspond to library calls (i.e. defined in
+// LinalgOps.td), we define an overloaded `print` function and a
+// parse`className` function.
+
+static void print(OpAsmPrinter *p, BufferSizeOp op) {
   *p << op.getOperationName() << " " << *op.getOperand();
   p->printOptionalAttrDict(op.getAttrs());
   *p << " : " << op.getOperand()->getType();
@@ -511,7 +511,7 @@ static ParseResult parseBufferSizeOp(OpAsmParser *parser,
                                        result->types));
 }
 
-static void printDimOp(OpAsmPrinter *p, DimOp op) {
+static void print(OpAsmPrinter *p, linalg::DimOp op) {
   *p << op.getOperationName() << " " << *op.getOperand() << ", "
      << op.getIndex();
   p->printOptionalAttrDict(op.getAttrs(), /*elidedAttrs=*/{"index"});
@@ -532,24 +532,29 @@ static ParseResult parseDimOp(OpAsmParser *parser, OperationState *result) {
                  parser->addTypeToList(indexType, result->types));
 }
 
-static LogicalResult verify(linalg::DimOp op) {
-  // Check that we have an integer index operand.
-  auto indexAttr = op.getAttrOfType<IntegerAttr>("index");
-  if (!indexAttr)
-    return op.emitOpError("requires an integer attribute named 'index'");
-
-  uint64_t index = indexAttr.getValue().getZExtValue();
-  auto type = op.getOperand()->getType();
-  if (auto viewType = type.dyn_cast<ViewType>()) {
-    if (index >= viewType.getRank())
-      return op.emitOpError("index is out of range");
-  } else {
-    return op.emitOpError("requires an operand with view type");
-  }
+static void print(OpAsmPrinter *p, RangeIntersectOp op) {
+  *p << op.getOperationName() << " " << *op.getOperand(0) << ", "
+     << *op.getOperand(1);
+  p->printOptionalAttrDict(op.getAttrs());
+  *p << " : " << op.getOperand(0)->getType();
+}
 
-  return success();
+static ParseResult parseRangeIntersectOp(OpAsmParser *parser,
+                                         OperationState *result) {
+  SmallVector<OpAsmParser::OperandType, 2> ops;
+  Type type;
+  return failure(parser->parseOperandList(ops) ||
+                 parser->parseOptionalAttributeDict(result->attributes) ||
+                 parser->parseColonType(type) ||
+                 parser->resolveOperands(ops, type, result->operands) ||
+                 parser->addTypeToList(type, result->types));
 }
 
+/////// Operations corresponding to library calls defined with Tablegen ////////
+// For such operations correspond to library calls (i.e. defined in
+// LinalgLibraryOps.td), we define an overloaded `print` function and a
+// parse`className` function.
+
 // A LinalgLibraryOp prints as:
 //
 // ```{.mlir}
index 483b6d3..f330cf4 100644 (file)
@@ -234,13 +234,13 @@ static SmallVector<Value *, 4> makeTiledViews(FuncBuilder *b, Location loc,
       unsigned pos2 = it - nzMap.getResults().begin();
       using edsc::op::operator+;
       using range = ValueBuilder<RangeOp>;
+      using range_intersect = ValueBuilder<RangeIntersectOp>;
       ScopedContext scope(*b, loc);
       ValueHandle iv(ivs[pos2]), step(tileSizes[pos]);
       auto min = ValueHandle(extractRangePart(ranges[j], RangePart::Min));
       // zero case is important enough to fold away by special-casing.
       auto newMin = isZero(min) ? iv : min + iv;
-      // TODO(ntv): intersect with current range once the operation exists.
-      Value *r = range(newMin, newMin + step, step);
+      Value *r = range_intersect(ranges[j], range(newMin, newMin + step, step));
       newRanges.push_back(r);
     }
     res.push_back(createOrReturnView(b, loc, viewDefiningOp, newRanges));
index c2eed72..c42b8d7 100644 (file)
@@ -63,3 +63,10 @@ func @dim(%arg0: !linalg.view<?x?xf32>) {
 //  CHECK-NEXT:   %1 = linalg.buffer_alloc %0 : !linalg.buffer<f32>
 //  CHECK-NEXT:   linalg.buffer_dealloc %1 : !linalg.buffer<f32>
 
+func @range_intersect(%arg0: !linalg.range, %arg1: !linalg.range) -> !linalg.range {
+  %0 = linalg.range_intersect %arg0, %arg1 : !linalg.range
+  return %0 : !linalg.range
+}
+// CHECK-LABEL: func @range_intersect(%arg0: !linalg.range, %arg1: !linalg.range) -> !linalg.range {
+//  CHECK-NEXT:   %0 = linalg.range_intersect %arg0, %arg1 : !linalg.range
+//  CHECK-NEXT:   return %0 : !linalg.range
index aeebb70..cc6f0e9 100644 (file)
@@ -32,10 +32,12 @@ func @matmul(%arg0: !linalg.buffer<f32>, %arg1: index, %arg2: index, %arg3: inde
 //  TILE-2-NEXT: %[[C:.*]] = linalg.view %arg0[{{.*}}] : !linalg.view<?x?xf32>
 //       TILE-2: affine.for %i0 = #[[ID]](%c0{{.*}}) to #[[ID]](%arg1) step 2 {
 //  TILE-2-NEXT:   %[[a:.*]] = affine.apply #[[UB0]](%i0)
-//  TILE-2-NEXT:   %[[ra:.*]] = linalg.range %i0:%[[a]]:%c2 : !linalg.range
+//  TILE-2-NEXT:   %[[ra0:.*]] = linalg.range %i0:%[[a]]:%c2 : !linalg.range
+//  TILE-2-NEXT:   %[[ra:.*]] = linalg.range_intersect %{{.}}, %[[ra0]] : !linalg.range
 //  TILE-2-NEXT:   %[[sAi:.*]] = linalg.slice %[[A]][%[[ra]], %2] : !linalg.view<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
 //  TILE-2-NEXT:   %[[c:.*]] = affine.apply #[[UB0]](%i0)
-//  TILE-2-NEXT:   %[[rc:.*]] = linalg.range %i0:%[[c]]:%c2 : !linalg.range
+//  TILE-2-NEXT:   %[[rc0:.*]] = linalg.range %i0:%[[c]]:%c2 : !linalg.range
+//  TILE-2-NEXT:   %[[rc:.*]] = linalg.range_intersect %{{.}}, %[[rc0]] : !linalg.range
 //  TILE-2-NEXT:   %[[sCi:.*]] = linalg.slice %[[C]][%[[rc]], %1] : !linalg.view<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
 //  TILE-2-NEXT:   linalg.matmul(%[[sAi]], %[[B]], %[[sCi]]) : !linalg.view<?x?xf32>, !linalg.view<?x?xf32>, !linalg.view<?x?xf32>
 
@@ -45,10 +47,12 @@ func @matmul(%arg0: !linalg.buffer<f32>, %arg1: index, %arg2: index, %arg3: inde
 //  TILE-02-NEXT: %[[C:.*]] = linalg.view %arg0[{{.*}}] : !linalg.view<?x?xf32>
 //       TILE-02: affine.for %i0 = #[[ID]](%c0_0) to #[[ID]](%arg2) step 2 {
 //  TILE-02-NEXT:   %[[b:.*]] = affine.apply #[[UB0]](%i0)
-//  TILE-02-NEXT:   %[[rb:.*]] = linalg.range %i0:%[[b]]:%c2 : !linalg.range
+//  TILE-02-NEXT:   %[[rb0:.*]] = linalg.range %i0:%[[b]]:%c2 : !linalg.range
+//  TILE-02-NEXT:   %[[rb:.*]] = linalg.range_intersect %{{.}}, %[[rb0]] : !linalg.range
 //  TILE-02-NEXT:   %[[sBj:.*]] = linalg.slice %[[B]][%{{.*}}, %[[rb]]] : !linalg.view<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
 //  TILE-02-NEXT:   %[[c:.*]] = affine.apply #[[UB0]](%i0)
-//  TILE-02-NEXT:   %[[rc:.*]] = linalg.range %i0:%[[c]]:%c2 : !linalg.range
+//  TILE-02-NEXT:   %[[rc0:.*]] = linalg.range %i0:%[[c]]:%c2 : !linalg.range
+//  TILE-02-NEXT:   %[[rc:.*]] = linalg.range_intersect %{{.}}, %[[rc0]] : !linalg.range
 //  TILE-02-NEXT:   %[[sCj:.*]] = linalg.slice %[[C]][%{{.*}}, %[[rc]]] : !linalg.view<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
 //  TILE-02-NEXT:   linalg.matmul(%[[A]], %[[sBj]], %[[sCj]]) : !linalg.view<?x?xf32>, !linalg.view<?x?xf32>, !linalg.view<?x?xf32>
 
@@ -58,10 +62,12 @@ func @matmul(%arg0: !linalg.buffer<f32>, %arg1: index, %arg2: index, %arg3: inde
 //  TILE-002-NEXT: %[[C:.*]] = linalg.view %arg0[{{.*}}] : !linalg.view<?x?xf32>
 //       TILE-002: affine.for %i0 = #[[ID]](%c0{{.*}}) to #[[ID]](%arg3) step 2 {
 //  TILE-002-NEXT:   %[[a:.*]] = affine.apply #[[UB0]](%i0)
-//  TILE-002-NEXT:   %[[ra:.*]] = linalg.range %i0:%[[a]]:%c2 : !linalg.range
+//  TILE-002-NEXT:   %[[ra0:.*]] = linalg.range %i0:%[[a]]:%c2 : !linalg.range
+//  TILE-002-NEXT:   %[[ra:.*]] = linalg.range_intersect %{{.}}, %[[ra0]] : !linalg.range
 //  TILE-002-NEXT:   %[[sAj:.*]] = linalg.slice %[[A]][%{{.*}}, %[[ra]]] : !linalg.view<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
 //  TILE-002-NEXT:   %[[b:.*]] = affine.apply #[[UB0]](%i0)
-//  TILE-002-NEXT:   %[[rb:.*]] = linalg.range %i0:%[[b]]:%c2 : !linalg.range
+//  TILE-002-NEXT:   %[[rb0:.*]] = linalg.range %i0:%[[b]]:%c2 : !linalg.range
+//  TILE-002-NEXT:   %[[rb:.*]] = linalg.range_intersect %{{.}}, %[[rb0]] : !linalg.range
 //  TILE-002-NEXT:   %[[sBj:.*]] = linalg.slice %[[B]][%[[rb]], %{{.*}}] : !linalg.view<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
 //  TILE-002-NEXT:   linalg.matmul(%[[sAj]], %[[sBj]], %[[C]]) : !linalg.view<?x?xf32>, !linalg.view<?x?xf32>, !linalg.view<?x?xf32>
 
@@ -73,19 +79,25 @@ func @matmul(%arg0: !linalg.buffer<f32>, %arg1: index, %arg2: index, %arg3: inde
 //  TILE-234-NEXT:    affine.for %i1 = #[[ID]](%c0{{.*}}) to #[[ID]](%arg2) step 3 {
 //  TILE-234-NEXT:      affine.for %i2 = #[[ID]](%c0{{.*}}) to #[[ID]](%arg3) step 4 {
 //  TILE-234-NEXT:        %[[ai:.*]]  = affine.apply #[[UB0]](%i0)
-//  TILE-234-NEXT:        %[[rai:.*]] = linalg.range %i0:%[[ai]]:%c2{{.*}} : !linalg.range
+//  TILE-234-NEXT:        %[[rai0:.*]] = linalg.range %i0:%[[ai]]:%c2 : !linalg.range
+//  TILE-234-NEXT:        %[[rai:.*]] = linalg.range_intersect %{{.}}, %[[rai0]] : !linalg.range
 //  TILE-234-NEXT:        %[[ak:.*]] = affine.apply #[[UB2]](%i2)
-//  TILE-234-NEXT:        %[[rak:.*]] = linalg.range %i2:%[[ak]]:%c4{{.*}} : !linalg.range
+//  TILE-234-NEXT:        %[[rak0:.*]] = linalg.range %i2:%[[ak]]:%c4{{.*}} : !linalg.range
+//  TILE-234-NEXT:        %[[rak:.*]] = linalg.range_intersect %{{.}}, %[[rak0]] : !linalg.range
 //  TILE-234-NEXT:        %[[sAik:.*]] = linalg.slice %[[A]][%[[rai]], %[[rak]]] : !linalg.view<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
 //  TILE-234-NEXT:        %[[bk:.*]] = affine.apply #[[UB2]](%i2)
-//  TILE-234-NEXT:        %[[rbk:.*]] = linalg.range %i2:%[[bk]]:%c4{{.*}} : !linalg.range
+//  TILE-234-NEXT:        %[[rbk0:.*]] = linalg.range %i2:%[[bk]]:%c4{{.*}} : !linalg.range
+//  TILE-234-NEXT:        %[[rbk:.*]] = linalg.range_intersect %{{.}}, %[[rbk0]] : !linalg.range
 //  TILE-234-NEXT:        %[[bj:.*]] = affine.apply #[[UB1]](%i1)
-//  TILE-234-NEXT:        %[[rbj:.*]] = linalg.range %i1:%[[bj]]:%c3{{.*}} : !linalg.range
+//  TILE-234-NEXT:        %[[rbj0:.*]] = linalg.range %i1:%[[bj]]:%c3{{.*}} : !linalg.range
+//  TILE-234-NEXT:        %[[rbj:.*]] = linalg.range_intersect %{{.}}, %[[rbj0]] : !linalg.range
 //  TILE-234-NEXT:        %[[sBkj:.*]] = linalg.slice %[[B]][%[[rbk]], %[[rbj]]] : !linalg.view<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
 //  TILE-234-NEXT:        %[[ci:.*]] = affine.apply #[[UB0]](%i0)
-//  TILE-234-NEXT:        %[[rci:.*]] = linalg.range %i0:%[[ci]]:%c2{{.*}} : !linalg.range
+//  TILE-234-NEXT:        %[[rci0:.*]] = linalg.range %i0:%[[ci]]:%c2{{.*}} : !linalg.range
+//  TILE-234-NEXT:        %[[rci:.*]] = linalg.range_intersect %{{.}}, %[[rci0]] : !linalg.range
 //  TILE-234-NEXT:        %[[cj:.*]] = affine.apply #[[UB1]](%i1)
-//  TILE-234-NEXT:        %[[rcj:.*]] = linalg.range %i1:%[[cj]]:%c3{{.*}} : !linalg.range
+//  TILE-234-NEXT:        %[[rcj0:.*]] = linalg.range %i1:%[[cj]]:%c3{{.*}} : !linalg.range
+//  TILE-234-NEXT:        %[[rcj:.*]] = linalg.range_intersect %{{.}}, %[[rcj0]] : !linalg.range
 //  TILE-234-NEXT:        %[[sCij:.*]] = linalg.slice %[[C]][%[[rci]], %[[rcj]]] : !linalg.view<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
 //  TILE-234-NEXT:        linalg.matmul(%[[sAik]], %[[sBkj]], %[[sCij]]) : !linalg.view<?x?xf32>, !linalg.view<?x?xf32>, !linalg.view<?x?xf32>
 
@@ -106,10 +118,12 @@ func @matvec(%arg0: !linalg.buffer<f32>, %arg1: index, %arg2: index, %arg3: inde
 //  TILE-2-NEXT: %[[C:.*]] = linalg.view %arg0[{{.*}}] : !linalg.view<?xf32>
 //       TILE-2: affine.for %i0 = #[[ID]](%c0{{.*}}) to #[[ID]](%arg1) step 2 {
 //  TILE-2-NEXT:   %[[a:.*]] = affine.apply #[[UB0]](%i0)
-//  TILE-2-NEXT:   %[[ra:.*]] = linalg.range %i0:%[[a]]:%c2 : !linalg.range
+//  TILE-2-NEXT:   %[[ra0:.*]] = linalg.range %i0:%[[a]]:%c2 : !linalg.range
+//  TILE-2-NEXT:   %[[ra:.*]] = linalg.range_intersect %{{.}}, %[[ra0]] : !linalg.range
 //  TILE-2-NEXT:   %[[sAi:.*]] = linalg.slice %[[A]][%[[ra]], %{{.*}}] : !linalg.view<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
 //  TILE-2-NEXT:   %[[c:.*]] = affine.apply #[[UB0]](%i0)
-//  TILE-2-NEXT:   %[[rc:.*]] = linalg.range %i0:%[[c]]:%c2 : !linalg.range
+//  TILE-2-NEXT:   %[[rc0:.*]] = linalg.range %i0:%[[c]]:%c2 : !linalg.range
+//  TILE-2-NEXT:   %[[rc:.*]] = linalg.range_intersect %{{.}}, %[[rc0]] : !linalg.range
 //  TILE-2-NEXT:   %[[sCi:.*]] = linalg.slice %[[C]][%[[rc]]] : !linalg.view<?xf32>, !linalg.range, !linalg.view<?xf32>
 //  TILE-2-NEXT:   linalg.matvec(%[[sAi]], %[[B]], %[[sCi]]) : !linalg.view<?x?xf32>, !linalg.view<?xf32>, !linalg.view<?xf32>
 
@@ -119,10 +133,12 @@ func @matvec(%arg0: !linalg.buffer<f32>, %arg1: index, %arg2: index, %arg3: inde
 //  TILE-02-NEXT: %[[C:.*]] = linalg.view %arg0[{{.*}}] : !linalg.view<?xf32>
 //       TILE-02: affine.for %i0 = #[[ID]](%c0{{.*}}) to #[[ID]](%arg2) step 2 {
 //  TILE-02-NEXT:   %[[a:.*]] = affine.apply #[[UB0]](%i0)
-//  TILE-02-NEXT:   %[[ra:.*]] = linalg.range %i0:%[[a]]:%c2{{.*}} : !linalg.range
+//  TILE-02-NEXT:   %[[ra0:.*]] = linalg.range %i0:%[[a]]:%c2 : !linalg.range
+//  TILE-02-NEXT:   %[[ra:.*]] = linalg.range_intersect %{{.}}, %[[ra0]] : !linalg.range
 //  TILE-02-NEXT:   %[[sAj:.*]] = linalg.slice %[[A]][%{{.*}}, %[[ra]]] : !linalg.view<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
 //  TILE-02-NEXT:   %[[b:.*]] = affine.apply #[[UB0]](%i0)
-//  TILE-02-NEXT:   %[[rb:.*]] = linalg.range %i0:%[[b]]:%c2{{.*}} : !linalg.range
+//  TILE-02-NEXT:   %[[rb0:.*]] = linalg.range %i0:%[[b]]:%c2 : !linalg.range
+//  TILE-02-NEXT:   %[[rb:.*]] = linalg.range_intersect %{{.}}, %[[rb0]] : !linalg.range
 //  TILE-02-NEXT:   %[[sBj:.*]] = linalg.slice %[[B]][%[[rb]]] : !linalg.view<?xf32>, !linalg.range, !linalg.view<?xf32>
 //  TILE-02-NEXT:   linalg.matvec(%[[sAj]], %[[sBj]], %[[C]]) : !linalg.view<?x?xf32>, !linalg.view<?xf32>, !linalg.view<?xf32>
 
@@ -136,15 +152,19 @@ func @matvec(%arg0: !linalg.buffer<f32>, %arg1: index, %arg2: index, %arg3: inde
 //       TILE-234:  affine.for %i0 = #[[ID]](%c0{{.*}}) to #[[ID]](%arg1) step 2 {
 //  TILE-234-NEXT:    affine.for %i1 = #[[ID]](%c0{{.*}}) to #[[ID]](%arg2) step 3 {
 //  TILE-234-NEXT:      %[[ai:.*]] = affine.apply #[[UB0]](%i0)
-//  TILE-234-NEXT:      %[[rai:.*]] = linalg.range %i0:%[[ai]]:%c2 : !linalg.range
+//  TILE-234-NEXT:      %[[rai0:.*]] = linalg.range %i0:%[[ai]]:%c2 : !linalg.range
+//  TILE-234-NEXT:      %[[rai:.*]] = linalg.range_intersect %{{.}}, %[[rai0]] : !linalg.range
 //  TILE-234-NEXT:      %[[aj:.*]] = affine.apply #[[UB1]](%i1)
-//  TILE-234-NEXT:      %[[raj:.*]] = linalg.range %i1:%[[aj]]:%c3 : !linalg.range
+//  TILE-234-NEXT:      %[[raj0:.*]] = linalg.range %i1:%[[aj]]:%c3 : !linalg.range
+//  TILE-234-NEXT:      %[[raj:.*]] = linalg.range_intersect %{{.}}, %[[raj0]] : !linalg.range
 //  TILE-234-NEXT:      %[[sAij:.*]] = linalg.slice %[[A]][%[[rai]], %[[raj]]] : !linalg.view<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
 //  TILE-234-NEXT:      %[[b:.*]] = affine.apply #[[UB1]](%i1)
-//  TILE-234-NEXT:      %[[rb:.*]] = linalg.range %i1:%[[b]]:%c3 : !linalg.range
+//  TILE-234-NEXT:      %[[rb0:.*]] = linalg.range %i1:%[[b]]:%c3 : !linalg.range
+//  TILE-234-NEXT:      %[[rb:.*]] = linalg.range_intersect %{{.}}, %[[rb0]] : !linalg.range
 //  TILE-234-NEXT:      %[[sB:.*]] = linalg.slice %[[B]][%[[rb]]] : !linalg.view<?xf32>, !linalg.range, !linalg.view<?xf32>
 //  TILE-234-NEXT:      %[[c:.*]] = affine.apply #[[UB0]](%i0)
-//  TILE-234-NEXT:      %[[rc:.*]] = linalg.range %i0:%[[c]]:%c2 : !linalg.range
+//  TILE-234-NEXT:      %[[rc0:.*]] = linalg.range %i0:%[[c]]:%c2 : !linalg.range
+//  TILE-234-NEXT:      %[[rc:.*]] = linalg.range_intersect %{{.}}, %[[rc0]] : !linalg.range
 //  TILE-234-NEXT:      %[[sC:.*]] = linalg.slice %[[C]][%[[rc]]] : !linalg.view<?xf32>, !linalg.range, !linalg.view<?xf32>
 //  TILE-234-NEXT:      linalg.matvec(%[[sAij]], %[[sB]], %[[sC]]) : !linalg.view<?x?xf32>, !linalg.view<?xf32>, !linalg.view<?xf32>
 
@@ -164,10 +184,12 @@ func @dot(%arg0: !linalg.buffer<f32>, %arg1: index, %arg2: index, %arg3: index)
 //  TILE-2-NEXT: %[[C:.*]] = linalg.view %arg0[{{.*}}] : !linalg.view<f32>
 //       TILE-2: affine.for %i0 = #[[ID]](%c0{{.*}}) to #[[ID]](%arg1) step 2 {
 //  TILE-2-NEXT:   %[[a:.*]] = affine.apply #[[UB0]](%i0)
-//  TILE-2-NEXT:   %[[ra:.*]] = linalg.range %i0:%[[a]]:%c2 : !linalg.range
+//  TILE-2-NEXT:   %[[ra0:.*]] = linalg.range %i0:%[[a]]:%c2 : !linalg.range
+//  TILE-2-NEXT:   %[[ra:.*]] = linalg.range_intersect %{{.}}, %[[ra0]] : !linalg.range
 //  TILE-2-NEXT:   %[[sAi:.*]] = linalg.slice %[[A]][%[[ra]]] : !linalg.view<?xf32>, !linalg.range, !linalg.view<?xf32>
 //  TILE-2-NEXT:   %[[b:.*]] = affine.apply #[[UB0]](%i0)
-//  TILE-2-NEXT:   %[[rb:.*]] = linalg.range %i0:%[[b]]:%c2 : !linalg.range
+//  TILE-2-NEXT:   %[[rb0:.*]] = linalg.range %i0:%[[b]]:%c2 : !linalg.range
+//  TILE-2-NEXT:   %[[rb:.*]] = linalg.range_intersect %{{.}}, %[[rb0]] : !linalg.range
 //  TILE-2-NEXT:   %[[sBi:.*]] = linalg.slice %[[B]][%[[rb]]] : !linalg.view<?xf32>, !linalg.range, !linalg.view<?xf32>
 //  TILE-2-NEXT:   linalg.dot(%[[sAi]], %[[sBi]], %[[C]]) : !linalg.view<?xf32>, !linalg.view<?xf32>, !linalg.view<f32>
 
@@ -183,9 +205,11 @@ func @dot(%arg0: !linalg.buffer<f32>, %arg1: index, %arg2: index, %arg3: index)
 //  TILE-234-NEXT: %[[C:.*]] = linalg.view %arg0[{{.*}}] : !linalg.view<f32>
 //       TILE-234:  affine.for %i0 = #[[ID]](%c0{{.*}}) to #[[ID]](%arg1) step 2 {
 //  TILE-234-NEXT:    %[[a:.*]] = affine.apply #[[UB0]](%i0)
-//  TILE-234-NEXT:    %[[ra:.*]] = linalg.range %i0:%[[a]]:%c2 : !linalg.range
+//  TILE-234-NEXT:    %[[ra0:.*]] = linalg.range %i0:%[[a]]:%c2 : !linalg.range
+//  TILE-234-NEXT:    %[[ra:.*]] = linalg.range_intersect %{{.}}, %[[ra0]] : !linalg.range
 //  TILE-234-NEXT:    %[[sA:.*]] = linalg.slice %[[A]][%[[ra]]] : !linalg.view<?xf32>, !linalg.range, !linalg.view<?xf32>
 //  TILE-234-NEXT:    %[[b:.*]] = affine.apply #[[UB0]](%i0)
-//  TILE-234-NEXT:    %[[rb:.*]] = linalg.range %i0:%[[b]]:%c2 : !linalg.range
+//  TILE-234-NEXT:    %[[rb0:.*]] = linalg.range %i0:%[[b]]:%c2 : !linalg.range
+//  TILE-234-NEXT:    %[[rb:.*]] = linalg.range_intersect %{{.}}, %[[rb0]] : !linalg.range
 //  TILE-234-NEXT:    %[[sB:.*]] = linalg.slice %[[B]][%[[rb]]] : !linalg.view<?xf32>, !linalg.range, !linalg.view<?xf32>
 //  TILE-234-NEXT:    linalg.dot(%[[sA]], %[[sB]], %[[C]]) : !linalg.view<?xf32>, !linalg.view<?xf32>, !linalg.view<f32>