def LinalgIsViewTypePred : CPred<"$_self.isa<ViewType>()">;
def View : Type<LinalgIsViewTypePred, "view">;
-#endif // LINALG_BASE
\ No newline at end of file
+#endif // LINALG_BASE
#include "mlir/IR/Function.h"
#include "mlir/IR/Module.h"
#include "mlir/IR/OpDefinition.h"
+#include "mlir/IR/StandardTypes.h"
+#include "mlir/IR/Types.h"
#include "mlir/Linalg/IR/LinalgTraits.h"
#include "mlir/Linalg/IR/LinalgTypes.h"
#include "mlir/Support/LLVM.h"
class OperationFolder;
namespace linalg {
+class ViewOp;
/// A linalg.LoadOp is the counterpart of load but operating on ViewType
/// instead of MemRefType.
Value *step() { return getOperand(2); }
};
-/// The "linalg.slice" op produces a linalg.view which is a subview of a given
-/// base view. This allows defining a subregion within the underlying buffer to
-/// operate on only a subset of the buffer.
-///
-/// A "linalg.slice" op takes a base view and a variadic number of indexings and
-/// produces a linalg.view of the same elemental type as the buffer. An indexing
-/// is either:
-/// 1. a linalg.range, in which case it does not reduce the rank of the parent
-/// view.
-/// 2. an index, in which case it reduces the rank of the parent view by one.
-///
-/// The parent view must be a base view (i.e. either a function argument or has
-/// been produced by a linalg.view op). In other words, chains of
-/// linalg.slice operations cannot be constructed in the IR. This defines away
-/// problems related to keeping track of which dimensions of the base view have
-/// been rank-reduced.
-///
-/// Examples:
-/// 1. rank-preserving slice:
-///
-/// ```{.mlir}
-/// %4 = linalg.slice %0[%1, %2] : !linalg.view<?x?xf32>, !linalg.range,
-/// !linalg.range, !linalg.view<?x?xf32>
-/// ```
-///
-/// 2. rank-reducing slice (from 2-D to 1-D):
-///
-/// ```{.mlir}
-/// %4 = linalg.slice %0[%1, %2] : !linalg.view<?x?xf32>, index,
-/// !linalg.range, !linalg.view<?xf32>
-/// ```
-///
-/// 3. rank-reducing slice (from 2-D to 0-D):
-///
-/// ```{.mlir}
-/// %4 = linalg.slice %0[%1, %2] : !linalg.view<?x?xf32>, index, index,
-/// !linalg.view<f32>
-/// ```
-class ViewOp;
-class SliceOp : public Op<SliceOp, OpTrait::VariadicOperands,
- OpTrait::OneResult, OpTrait::HasNoSideEffect> {
- enum { FirstIndexingOperand = 1 };
-
-public:
- using Op::Op;
-
- // Hooks to customize the behavior of this op.
- static llvm::StringRef getOperationName() { return "linalg.slice"; }
- static void build(Builder *b, OperationState *result, Value *base,
- llvm::ArrayRef<Value *> indexings);
- LogicalResult verify();
- static ParseResult parse(OpAsmParser *parser, OperationState *result);
- void print(OpAsmPrinter *p);
-
- // Op-specific functionality.
- unsigned getRank() { return getViewType().getRank(); }
- Type getElementType() { return getViewType().getElementType(); }
- ViewType getViewType() { return getType().cast<ViewType>(); }
- Value *getBaseView() { return getOperand(0); }
- ViewOp getBaseViewOp();
- ViewType getBaseViewType();
- unsigned getBaseViewRank() { return getBaseViewType().getRank(); }
- // Get the underlying indexing at a given rank.
- Value *getIndexing(unsigned rank) { return *(getIndexings().begin() + rank); }
- // Get all the indexings in this view.
- Operation::operand_range getIndexings() {
- return {operand_begin() + SliceOp::FirstIndexingOperand, operand_end()};
- }
- // Get the subset of indexings that are of RangeType.
- SmallVector<Value *, 8> getRanges();
-};
-
/// A linalg.StoreOp is the counterpart of affine.store but operating on
/// ViewType instead of MemRefType.
///
let hasCanonicalizer = 1;
}
+def SliceOp : Linalg_Op<"slice", [NoSideEffect]>,
+ Arguments<(ins View:$view, Variadic<AnyTypeOf<[Range, Index]>>:$indexings)>,
+ Results<(outs View)> {
+ let summary = "Produce a linalg.view which is a subview of a base view.";
+ let description = [{
+ The "linalg.slice" op produces a linalg.view which is a subview of a given
+ base view. This allows defining a subregion within the underlying buffer to
+ operate on only a subset of the buffer.
+
+ A "linalg.slice" op takes a base view and a variadic number of indexings and
+ produces a linalg.view of the same elemental type as the buffer. An indexing
+ is either:
+ 1. a linalg.range, in which case it does not reduce the rank of the parent
+ view.
+ 2. an index, in which case it reduces the rank of the parent view by one.
+
+ The parent view must be a base view (i.e. either a function argument or has
+ been produced by a linalg.view op). In other words, chains of
+ linalg.slice operations cannot be constructed in the IR. This defines away
+ problems related to keeping track of which dimensions of the base view have
+ been rank-reduced.
+
+ Examples:
+ 1. rank-preserving slice:
+
+ %4 = linalg.slice %0[%1, %2] : !linalg.view<?x?xf32>, !linalg.range,
+ !linalg.range, !linalg.view<?x?xf32>
+
+ 2. rank-reducing slice (from 2-D to 1-D):
+
+ %4 = linalg.slice %0[%1, %2] : !linalg.view<?x?xf32>, index,
+ !linalg.range, !linalg.view<?xf32>
+
+ 3. rank-reducing slice (from 2-D to 0-D):
+
+ %4 = linalg.slice %0[%1, %2] : !linalg.view<?x?xf32>, index, index,
+ !linalg.view<f32>
+ }];
+
+ let builders = [OpBuilder<
+ "Builder *b, OperationState *result, Value *base, "
+ "ArrayRef<Value *> indexings">];
+
+ let extraClassDeclaration = [{
+ enum { FirstIndexingOperand = 1 };
+ unsigned getRank() { return getViewType().getRank(); }
+ Type getElementType() { return getViewType().getElementType(); }
+ ViewType getViewType() { return getType().cast<ViewType>(); }
+ unsigned getBaseViewRank() { return getBaseViewType().getRank(); }
+ ViewType getBaseViewType() { return view()->getType().cast<ViewType>(); }
+
+ // Get the underlying indexing at a given rank.
+ Value *indexing(unsigned rank) { return *(indexings().begin() + rank); }
+
+ // Get the subset of indexings that are of RangeType.
+ SmallVector<Value *, 8> getRanges() {
+ llvm::SmallVector<Value *, 8> res;
+ for (auto *operand : indexings())
+ if (!operand->getType().isa<IndexType>())
+ res.push_back(operand);
+ return res;
+ }
+ }];
+}
+
def SubViewOp : Linalg_Op<"subview", [NoSideEffect]>,
Arguments<(ins View:$view, Variadic<Index>:$ranges)>,
Results<(outs View)> {
if (isa<BlockArgument>(v))
return v;
if (auto slice = dyn_cast_or_null<SliceOp>(v->getDefiningOp())) {
- auto it = aliases.insert(std::make_pair(v, find(slice.getBaseView())));
+ auto it = aliases.insert(std::make_pair(v, find(slice.view())));
return it.first->second;
}
if (auto view = dyn_cast_or_null<ViewOp>(v->getDefiningOp())) {
parser->addTypeToList(type, result->types));
}
-//////////////////////////////////////////////////////////////////////////////
-// SliceOp
-//////////////////////////////////////////////////////////////////////////////
-void mlir::linalg::SliceOp::build(Builder *b, OperationState *result,
- Value *base, ArrayRef<Value *> indexings) {
- result->addOperands({base});
- result->addOperands(indexings);
-
- ViewType viewType = base->getType().cast<ViewType>();
- unsigned rank = viewType.getRank();
- for (auto *i : indexings)
- if (!i->getType().isa<RangeType>())
- rank--;
- Type elementType = viewType.getElementType();
- result->addTypes({ViewType::get(b->getContext(), elementType, rank)});
-}
-
-LogicalResult mlir::linalg::SliceOp::verify() {
- if (llvm::empty(getOperands()))
- return emitOpError(
- "requires at least a view operand followed by 'rank' indices");
- unsigned rank = getBaseViewRank();
- if (llvm::size(getIndexings()) != rank) {
- return emitOpError("requires at least a view operand followed by ")
- << rank << " indexings";
- }
- unsigned index = 0;
- for (auto indexing : getIndexings()) {
- if (!indexing->getType().isa<RangeType>() &&
- !indexing->getType().isa<IndexType>()) {
- return emitOpError() << index
- << "^th index must be of range or index type";
- }
- if (indexing->getType().isa<IndexType>())
- --rank;
- ++index;
- }
- if (getRank() != rank) {
- return emitOpError()
- << "the rank of the view must be the number of its range indices ("
- << rank << ") but got: " << getRank();
- }
- return success();
-}
-
-ParseResult mlir::linalg::SliceOp::parse(OpAsmParser *parser,
- OperationState *result) {
- OpAsmParser::OperandType baseInfo;
- SmallVector<OpAsmParser::OperandType, 8> indexingsInfo;
- SmallVector<Type, 8> types;
- if (parser->parseOperand(baseInfo) ||
- parser->parseOperandList(indexingsInfo, OpAsmParser::Delimiter::Square) ||
- parser->parseOptionalAttributeDict(result->attributes) ||
- parser->parseColonTypeList(types))
- return failure();
-
- if (types.size() != 2 + indexingsInfo.size())
- return parser->emitError(parser->getNameLoc(),
- "unexpected number of types ");
- ViewType baseViewType = types[0].dyn_cast<ViewType>();
- if (!baseViewType)
- return parser->emitError(parser->getNameLoc(),
- "view type expected for first type");
- if (indexingsInfo.size() != baseViewType.getRank())
- return parser->emitError(parser->getNameLoc(), "expected ")
- << baseViewType.getRank() << " indexings";
- ViewType viewType = types.back().dyn_cast<ViewType>();
- if (!viewType)
- return parser->emitError(parser->getNameLoc(), "view type expected");
-
- ArrayRef<Type> indexingTypes =
- ArrayRef<Type>(types).drop_front(1).drop_back(1);
- if (indexingTypes.size() != baseViewType.getRank())
- return parser->emitError(parser->getNameLoc(), "expected ")
- << baseViewType.getRank() << " indexing types";
- return failure(
- parser->resolveOperand(baseInfo, baseViewType, result->operands) ||
- (!indexingsInfo.empty() &&
- parser->resolveOperands(indexingsInfo, indexingTypes,
- indexingsInfo.front().location,
- result->operands)) ||
- parser->addTypeToList(viewType, result->types));
-}
-
-// A SliceOp prints as:
-//
-// ```{.mlir}
-// linalg.slice %0[%1, %2] :
-// !linalg.view<?x?xf32>, [indexing-types], !linalg.view<?x?xf32>
-// ```
-//
-// Where %0 is an ssa-value holding a view created from a buffer, %1 and %2 are
-// ssa-value each holding a range.
-void mlir::linalg::SliceOp::print(OpAsmPrinter *p) {
- *p << getOperationName() << " " << *getBaseView() << "[";
- interleave(
- getIndexings().begin(), getIndexings().end(), [p](Value *v) { *p << *v; },
- [p]() { *p << ", "; });
- *p << "] : " << getBaseViewType();
- for (auto indexing : getIndexings()) {
- *p << ", " << indexing->getType();
- }
- *p << ", " << getType();
-}
-
-ViewOp mlir::linalg::SliceOp::getBaseViewOp() {
- return cast<ViewOp>(getOperand(0)->getDefiningOp());
-}
-
-ViewType mlir::linalg::SliceOp::getBaseViewType() {
- return getOperand(0)->getType().cast<ViewType>();
-}
-
-SmallVector<Value *, 8> mlir::linalg::SliceOp::getRanges() {
- llvm::SmallVector<Value *, 8> res;
- for (auto *operand : getIndexings()) {
- if (!operand->getType().isa<IndexType>()) {
- res.push_back(operand);
- }
- }
- return res;
-}
-
////////////////////////////////////////////////////////////////////////////////
// StoreOp.
////////////////////////////////////////////////////////////////////////////////
}
//===----------------------------------------------------------------------===//
+// SliceOp
+//===----------------------------------------------------------------------===//
+
+void mlir::linalg::SliceOp::build(Builder *b, OperationState *result,
+ Value *base, ArrayRef<Value *> indexings) {
+ result->addOperands(base);
+ result->addOperands(indexings);
+
+ ViewType viewType = base->getType().cast<ViewType>();
+ unsigned rank = viewType.getRank();
+ for (auto *i : indexings)
+ if (!i->getType().isa<RangeType>())
+ rank--;
+ Type elementType = viewType.getElementType();
+ result->addTypes({ViewType::get(b->getContext(), elementType, rank)});
+}
+
+// A SliceOp prints as:
+//
+// ```{.mlir}
+// linalg.slice %0[%1, %2] :
+// !linalg.view<?x?xf32>, [indexing-types], !linalg.view<?x?xf32>
+// ```
+//
+// Where %0 is an ssa-value holding a view created from a buffer, %1 and %2 are
+// ssa-value each holding a range.
+static void print(OpAsmPrinter *p, SliceOp op) {
+ *p << SliceOp::getOperationName() << " " << *op.view() << "[";
+ p->printOperands(op.indexings());
+ *p << "] : " << op.getBaseViewType();
+ for (auto indexing : op.indexings()) {
+ *p << ", " << indexing->getType();
+ }
+ *p << ", " << op.getType();
+}
+
+static ParseResult parseSliceOp(OpAsmParser *parser, OperationState *result) {
+ OpAsmParser::OperandType baseInfo;
+ SmallVector<OpAsmParser::OperandType, 8> operands;
+ SmallVector<Type, 8> types;
+ if (parser->parseOperand(baseInfo) ||
+ parser->parseOperandList(operands, OpAsmParser::Delimiter::Square) ||
+ parser->parseOptionalAttributeDict(result->attributes) ||
+ parser->parseColonTypeList(types))
+ return failure();
+
+ if (types.size() < 2)
+ return parser->emitError(parser->getCurrentLocation(),
+ "expected at least input and result view types");
+
+ ArrayRef<Type> indexingTypes = ArrayRef<Type>(types).drop_front().drop_back();
+ return failure(
+ parser->resolveOperand(baseInfo, types.front(), result->operands) ||
+ (!operands.empty() &&
+ parser->resolveOperands(operands, indexingTypes,
+ operands.front().location, result->operands)) ||
+ parser->addTypeToList(types.back(), result->types));
+}
+
+static LogicalResult verify(SliceOp op) {
+ unsigned rank = op.getBaseViewRank();
+ if (llvm::size(op.indexings()) != rank)
+ return op.emitOpError("expected number of indexings to match view rank");
+ unsigned index = 0;
+ for (auto indexing : op.indexings()) {
+ if (indexing->getType().isa<IndexType>())
+ --rank;
+ ++index;
+ }
+ if (op.getRank() != rank)
+ return op.emitOpError()
+ << "expected rank of the view(" << op.getRank()
+ << ") to be the number of its range indices(" << rank << ")";
+ return success();
+}
+
+//===----------------------------------------------------------------------===//
// ViewOp
//===----------------------------------------------------------------------===//
void mlir::linalg::ViewOp::build(Builder *b, OperationState *result,
mlir::linalg::LinalgDialect::LinalgDialect(MLIRContext *context)
: Dialect(getDialectNamespace(), context) {
addTypes<BufferType, RangeType, ViewType>();
- addOperations<LoadOp, RangeOp, StoreOp, SliceOp>();
+ addOperations<LoadOp, RangeOp, StoreOp>();
addOperations<
#define GET_OP_LIST
#include "mlir/Linalg/IR/LinalgOps.cpp.inc"
for (int j = 0, e = viewType.getRank(); j < e; ++j) {
Value *indexing = operands[1 + j];
Value *min =
- sliceOp.getIndexing(j)->getType().isa<RangeType>()
+ sliceOp.indexing(j)->getType().isa<RangeType>()
? static_cast<Value *>(extractvalue(int64Ty, indexing, pos(0)))
: indexing;
Value *product = mul(min, strides[j]);
// Compute and insert view sizes (max - min along the range). Skip the
// non-range operands as they will be projected away from the view.
int i = 0, j = 0;
- for (Value *index : sliceOp.getIndexings()) {
+ for (Value *index : sliceOp.indexings()) {
if (!index->getType().isa<RangeType>()) {
++j;
continue;
// to non-range operands as they are projected away from the view.
i = 0;
for (int j = 0, e = strides.size(); j < e; ++j) {
- if (!sliceOp.getIndexing(j)->getType().isa<RangeType>())
+ if (!sliceOp.indexing(j)->getType().isa<RangeType>())
continue;
Value *step = extractvalue(int64Ty, operands[1 + j], pos(2));
Value *stride = mul(strides[j], step);
// -----
-// CHECK-LABEL: at_least_2_operands
-func @at_least_2_operands(%arg0: !linalg.view<f32>) {
+// CHECK-LABEL: slice_number_of_indexings
+func @slice_number_of_indexings(%arg0: !linalg.view<?x?xf32>) {
+ // expected-error @+2 {{expected number of indexings to match view rank}}
+ %c0 = constant 0: index
+ %0 = linalg.slice %arg0[%c0] : !linalg.view<?x?xf32>, index, !linalg.view<?x?xf32>
+ return
+}
+
+// -----
+
+// CHECK-LABEL: slice_rank_vs_range_indices
+func @slice_rank_vs_range_indices(%arg0: !linalg.view<?x?xf32>) {
+ // expected-error @+2 {{expected rank of the view(1) to be the number of its range indices(0)}}
+ %c0 = constant 0: index
+ %0 = linalg.slice %arg0[%c0, %c0] : !linalg.view<?x?xf32>, index, index, !linalg.view<?xf32>
+ return
+}
+
+// -----
+
+// CHECK-LABEL: generic_at_least_2_operands
+func @generic_at_least_2_operands(%arg0: !linalg.view<f32>) {
// expected-error @+1 {{op expected 2 or more operands}}
linalg.generic {
fun = @foo,
// -----
-// CHECK-LABEL: exactly_2_views
-func @exactly_2_views(%arg0: !linalg.view<f32>) {
+// CHECK-LABEL: generic_exactly_2_views
+func @generic_exactly_2_views(%arg0: !linalg.view<f32>) {
// expected-error @+1 {{op expected exactly 2 view operands}}
linalg.generic {
fun = @foo,
// -----
-// CHECK-LABEL: undefined_fun
-func @undefined_fun(%arg0: !linalg.view<f32>) {
+// CHECK-LABEL: generic_undefined_fun
+func @generic_undefined_fun(%arg0: !linalg.view<f32>) {
// expected-error @+1 {{op expected fun attribute to refer to a defined symbol}}
linalg.generic {
fun = @foo,
func @foo() { return }
-// CHECK-LABEL: mismatched_num_arguments
-func @mismatched_num_arguments(%arg0: !linalg.view<f32>) {
+// CHECK-LABEL: generic_mismatched_num_arguments
+func @generic_mismatched_num_arguments(%arg0: !linalg.view<f32>) {
// expected-error @+1 {{op expected fun arguments to match number of views}}
linalg.generic {
fun = @foo,
func @foo(%0: i32) { return }
-// CHECK-LABEL: mismatched_num_returns
-func @mismatched_num_returns(%arg0: !linalg.view<f32>) {
+// CHECK-LABEL: generic_mismatched_num_returns
+func @generic_mismatched_num_returns(%arg0: !linalg.view<f32>) {
// expected-error @+1 {{op expected fun results to match number of output views}}
linalg.generic {
fun = @foo,
func @foo(%0: i32) -> i32 { return %0: i32 }
-// CHECK-LABEL: symbol_in_map
-func @symbol_in_map(%arg0: !linalg.view<f32>) {
+// CHECK-LABEL: generic_symbol_in_map
+func @generic_symbol_in_map(%arg0: !linalg.view<f32>) {
// expected-error @+1 {{op expected indexing_map #0 to have no symbols}}
linalg.generic {
fun = @foo,
func @foo(%0: i32) -> i32 { return %0: i32 }
-// CHECK-LABEL: wrong_dim_in_map
-func @wrong_dim_in_map(%arg0: !linalg.view<f32>) {
+// CHECK-LABEL: generic_wrong_dim_in_map
+func @generic_wrong_dim_in_map(%arg0: !linalg.view<f32>) {
// expected-error @+1 {{op expected indexing_map #0 to have 1 dim(s) to match the number of loops}}
linalg.generic {
fun = @foo,
func @foo(%0: i32) -> i32 { return %0: i32 }
-// CHECK-LABEL: zero_d_view
-func @zero_d_view(%arg0: !linalg.view<f32>) {
+// CHECK-LABEL: generic_zero_d_view
+func @generic_zero_d_view(%arg0: !linalg.view<f32>) {
// expected-error @+1 {{op expected indexing_map #0 to be 0 to match 0-D view: '!linalg.view<f32>'}}
linalg.generic {
fun = @foo,
func @foo(%0: f32) -> f32 { return %0: f32 }
-// CHECK-LABEL: one_d_view
-func @one_d_view(%arg0: !linalg.view<?xf32>) {
+// CHECK-LABEL: generic_one_d_view
+func @generic_one_d_view(%arg0: !linalg.view<?xf32>) {
// expected-error @+1 {{op expected indexing_map #0 results to match view rank: '!linalg.view<?xf32>'}}
linalg.generic {
fun = @foo,
return %1: f32
}
-// CHECK-LABEL: fun_arg_0_element_type
-func @fun_arg_0_element_type(%arg0: !linalg.view<?xf32>) {
+// CHECK-LABEL: generic_fun_arg_0_element_type
+func @generic_fun_arg_0_element_type(%arg0: !linalg.view<?xf32>) {
// expected-error @+1 {{op expected fun argument 0 to match view element type: 'f32'}}
linalg.generic {
fun = @foo,
return %1: i4
}
-// CHECK-LABEL: fun_result_0_element_type
-func @fun_result_0_element_type(%arg0: !linalg.view<?xf32>) {
+// CHECK-LABEL: generic_fun_result_0_element_type
+func @generic_fun_result_0_element_type(%arg0: !linalg.view<?xf32>) {
// expected-error @+1 {{op expected fun result 0 to match output view element type: 'f32'}}
linalg.generic {
fun = @foo,
func @foo(%0: f32, %1: f32) -> f32 { return %1: f32 }
-// CHECK-LABEL: singular_maps
-func @singular_maps(%arg0: !linalg.view<?xf32>, %arg1: !linalg.view<?xf32>) {
+// CHECK-LABEL: generic_singular_maps
+func @generic_singular_maps(%arg0: !linalg.view<?xf32>, %arg1: !linalg.view<?xf32>) {
// expected-error @+1 {{op expected the concatenation of maps in indexing_map to be invertible}}
linalg.generic {
fun = @foo,
// -----
-// CHECK-LABEL: empty_region
-func @empty_region(%arg0: !linalg.view<f32>) {
+// CHECK-LABEL: generic_empty_region
+func @generic_empty_region(%arg0: !linalg.view<f32>) {
// expected-error @+1 {{op expected region with 1 block}}
linalg.generic {
indexing_maps = [ () -> (0) ],
// -----
-// CHECK-LABEL: mismatched_num_arguments
-func @mismatched_num_arguments(%arg0: !linalg.view<f32>) {
+// CHECK-LABEL: generic_mismatched_num_arguments
+func @generic_mismatched_num_arguments(%arg0: !linalg.view<f32>) {
// expected-error @+1 {{op expected number of block arguments to match number of views}}
linalg.generic {
indexing_maps = [ () -> (0) ],
// -----
-// CHECK-LABEL: block_arg_type
-func @block_arg_type(%arg0: !linalg.view<f32>) {
+// CHECK-LABEL: generic_block_arg_type
+func @generic_block_arg_type(%arg0: !linalg.view<f32>) {
// expected-error @+1 {{op expected block argument 0 of the same type as elemental type of output view: '!linalg.view<f32>'}}
linalg.generic {
indexing_maps = [ () -> (0) ],
// -----
-// CHECK-LABEL: fun_result_0_element_type
-func @fun_result_0_element_type(%arg0: !linalg.view<?xf32>) {
+// CHECK-LABEL: generic_fun_result_0_element_type
+func @generic_fun_result_0_element_type(%arg0: !linalg.view<?xf32>) {
// expected-error @+8 {{type of return operand 0 ('i1') doesn't match view element type ('f32')}}
linalg.generic {
indexing_maps = [ (i) -> (i) ],
// -----
-// CHECK-LABEL: wrong_yield_parent
-func @fun_result_0_element_type(%arg0: !linalg.view<?xf32>) {
+// CHECK-LABEL: generic_wrong_yield_parent
+func @generic_fun_result_0_element_type(%arg0: !linalg.view<?xf32>) {
// expected-error @+1 {{op expected 'linalg.generic' parent op}}
linalg.yield %arg0: !linalg.view<?xf32>
}