30 #include "llvm/ADT/ArrayRef.h"
31 #include "llvm/ADT/STLExtras.h"
32 #include "llvm/ADT/SmallVector.h"
33 #include "llvm/ADT/SmallVectorExtras.h"
34 #include "llvm/Support/FormatVariadic.h"
45 return (*attr.getAsValueRange<IntegerAttr>().begin()).getZExtValue();
53 if (
auto vectorType = dyn_cast<VectorType>(type))
54 return vectorType.getNumElements() * vectorType.getElementTypeBitWidth();
64 matchAndRewrite(vector::ShapeCastOp shapeCastOp, OpAdaptor adaptor,
66 Type dstType = getTypeConverter()->convertType(shapeCastOp.getType());
72 if (dstType == adaptor.getSource().getType() ||
73 shapeCastOp.getResultVectorType().getNumElements() == 1) {
74 rewriter.
replaceOp(shapeCastOp, adaptor.getSource());
83 struct VectorBitcastConvert final
88 matchAndRewrite(vector::BitCastOp bitcastOp, OpAdaptor adaptor,
90 Type dstType = getTypeConverter()->convertType(bitcastOp.getType());
94 if (dstType == adaptor.getSource().getType()) {
95 rewriter.
replaceOp(bitcastOp, adaptor.getSource());
102 Type srcType = adaptor.getSource().getType();
106 llvm::formatv(
"different source ({0}) and target ({1}) bitwidth",
111 adaptor.getSource());
116 struct VectorBroadcastConvert final
121 matchAndRewrite(vector::BroadcastOp castOp, OpAdaptor adaptor,
124 getTypeConverter()->convertType(castOp.getResultVectorType());
128 if (isa<spirv::ScalarType>(resultType)) {
129 rewriter.
replaceOp(castOp, adaptor.getSource());
134 adaptor.getSource());
141 struct VectorExtractOpConvert final
146 matchAndRewrite(vector::ExtractOp extractOp, OpAdaptor adaptor,
148 Type dstType = getTypeConverter()->convertType(extractOp.getType());
152 if (isa<spirv::ScalarType>(adaptor.getVector().getType())) {
153 rewriter.
replaceOp(extractOp, adaptor.getVector());
157 if (std::optional<int64_t>
id =
160 extractOp, dstType, adaptor.getVector(),
164 extractOp, dstType, adaptor.getVector(),
165 adaptor.getDynamicPosition()[0]);
170 struct VectorExtractStridedSliceOpConvert final
175 matchAndRewrite(vector::ExtractStridedSliceOp extractOp, OpAdaptor adaptor,
177 Type dstType = getTypeConverter()->convertType(extractOp.getType());
187 Value srcVector = adaptor.getOperands().front();
190 if (isa<spirv::ScalarType>(dstType)) {
197 std::iota(indices.begin(), indices.end(), offset);
200 extractOp, dstType, srcVector, srcVector,
207 template <
class SPIRVFMAOp>
212 matchAndRewrite(vector::FMAOp fmaOp, OpAdaptor adaptor,
214 Type dstType = getTypeConverter()->convertType(fmaOp.getType());
218 adaptor.getRhs(), adaptor.getAcc());
223 struct VectorInsertOpConvert final
228 matchAndRewrite(vector::InsertOp insertOp, OpAdaptor adaptor,
230 if (isa<VectorType>(insertOp.getSourceType()))
232 if (!getTypeConverter()->convertType(insertOp.getDestVectorType()))
234 "unsupported dest vector type");
237 if (insertOp.getSourceType().isIntOrFloat() &&
238 insertOp.getDestVectorType().getNumElements() == 1) {
239 rewriter.
replaceOp(insertOp, adaptor.getSource());
243 if (std::optional<int64_t>
id =
246 insertOp, adaptor.getSource(), adaptor.getDest(),
id.value());
249 insertOp, insertOp.getDest(), adaptor.getSource(),
250 adaptor.getDynamicPosition()[0]);
255 struct VectorExtractElementOpConvert final
260 matchAndRewrite(vector::ExtractElementOp extractOp, OpAdaptor adaptor,
262 Type resultType = getTypeConverter()->convertType(extractOp.getType());
266 if (isa<spirv::ScalarType>(adaptor.getVector().getType())) {
267 rewriter.
replaceOp(extractOp, adaptor.getVector());
274 extractOp, resultType, adaptor.getVector(),
278 extractOp, resultType, adaptor.getVector(), adaptor.getPosition());
283 struct VectorInsertElementOpConvert final
288 matchAndRewrite(vector::InsertElementOp insertOp, OpAdaptor adaptor,
290 Type vectorType = getTypeConverter()->convertType(insertOp.getType());
294 if (isa<spirv::ScalarType>(vectorType)) {
295 rewriter.
replaceOp(insertOp, adaptor.getSource());
302 insertOp, adaptor.getSource(), adaptor.getDest(),
303 cstPos.getSExtValue());
306 insertOp, vectorType, insertOp.getDest(), adaptor.getSource(),
307 adaptor.getPosition());
312 struct VectorInsertStridedSliceOpConvert final
317 matchAndRewrite(vector::InsertStridedSliceOp insertOp, OpAdaptor adaptor,
319 Value srcVector = adaptor.getOperands().front();
320 Value dstVector = adaptor.getOperands().back();
327 if (isa<spirv::ScalarType>(srcVector.
getType())) {
328 assert(!isa<spirv::ScalarType>(dstVector.
getType()));
330 insertOp, dstVector.
getType(), srcVector, dstVector,
335 uint64_t totalSize = cast<VectorType>(dstVector.getType()).getNumElements();
336 uint64_t insertSize =
337 cast<VectorType>(srcVector.
getType()).getNumElements();
340 std::iota(indices.begin(), indices.end(), 0);
341 std::iota(indices.begin() + offset, indices.begin() + offset + insertSize,
345 insertOp, dstVector.getType(), dstVector, srcVector,
353 vector::ReductionOp reduceOp, vector::ReductionOp::Adaptor adaptor,
355 int numElements =
static_cast<int>(srcVectorType.getDimSize(0));
357 values.reserve(numElements + (adaptor.getAcc() ? 1 : 0));
360 for (
int i = 0; i < numElements; ++i) {
361 values.push_back(rewriter.
create<spirv::CompositeExtractOp>(
362 loc, srcVectorType.getElementType(), adaptor.getVector(),
365 if (
Value acc = adaptor.getAcc())
366 values.push_back(acc);
371 struct ReductionRewriteInfo {
376 FailureOr<ReductionRewriteInfo>
static getReductionInfo(
377 vector::ReductionOp op, vector::ReductionOp::Adaptor adaptor,
383 auto srcVectorType = dyn_cast<VectorType>(adaptor.getVector().getType());
384 if (!srcVectorType || srcVectorType.getRank() != 1)
388 extractAllElements(op, adaptor, srcVectorType, rewriter);
390 return ReductionRewriteInfo{resultType, std::move(extractedElements)};
393 template <
typename SPIRVUMaxOp,
typename SPIRVUMinOp,
typename SPIRVSMaxOp,
394 typename SPIRVSMinOp>
399 matchAndRewrite(vector::ReductionOp reduceOp, OpAdaptor adaptor,
402 getReductionInfo(reduceOp, adaptor, rewriter, *getTypeConverter());
403 if (failed(reductionInfo))
406 auto [resultType, extractedElements] = *reductionInfo;
408 Value result = extractedElements.front();
409 for (
Value next : llvm::drop_begin(extractedElements)) {
410 switch (reduceOp.getKind()) {
412 #define INT_AND_FLOAT_CASE(kind, iop, fop) \
413 case vector::CombiningKind::kind: \
414 if (llvm::isa<IntegerType>(resultType)) { \
415 result = rewriter.create<spirv::iop>(loc, resultType, result, next); \
417 assert(llvm::isa<FloatType>(resultType)); \
418 result = rewriter.create<spirv::fop>(loc, resultType, result, next); \
422 #define INT_OR_FLOAT_CASE(kind, fop) \
423 case vector::CombiningKind::kind: \
424 result = rewriter.create<fop>(loc, resultType, result, next); \
434 case vector::CombiningKind::AND:
435 case vector::CombiningKind::OR:
436 case vector::CombiningKind::XOR:
441 #undef INT_AND_FLOAT_CASE
442 #undef INT_OR_FLOAT_CASE
450 template <
typename SPIRVFMaxOp,
typename SPIRVFMinOp>
451 struct VectorReductionFloatMinMax final
456 matchAndRewrite(vector::ReductionOp reduceOp, OpAdaptor adaptor,
459 getReductionInfo(reduceOp, adaptor, rewriter, *getTypeConverter());
460 if (failed(reductionInfo))
463 auto [resultType, extractedElements] = *reductionInfo;
465 Value result = extractedElements.front();
466 for (
Value next : llvm::drop_begin(extractedElements)) {
467 switch (reduceOp.getKind()) {
469 #define INT_OR_FLOAT_CASE(kind, fop) \
470 case vector::CombiningKind::kind: \
471 result = rewriter.create<fop>(loc, resultType, result, next); \
482 #undef INT_OR_FLOAT_CASE
495 matchAndRewrite(vector::SplatOp op, OpAdaptor adaptor,
497 Type dstType = getTypeConverter()->convertType(op.getType());
500 if (isa<spirv::ScalarType>(dstType)) {
501 rewriter.
replaceOp(op, adaptor.getInput());
503 auto dstVecType = cast<VectorType>(dstType);
513 struct VectorShuffleOpConvert final
518 matchAndRewrite(vector::ShuffleOp shuffleOp, OpAdaptor adaptor,
520 VectorType oldResultType = shuffleOp.getResultVectorType();
521 Type newResultType = getTypeConverter()->convertType(oldResultType);
524 "unsupported result vector type");
526 auto mask = llvm::to_vector_of<int32_t>(shuffleOp.getMask());
528 VectorType oldV1Type = shuffleOp.getV1VectorType();
529 VectorType oldV2Type = shuffleOp.getV2VectorType();
533 if (oldV1Type.getNumElements() > 1 && oldV2Type.getNumElements() > 1 &&
534 oldResultType.getNumElements() > 1) {
536 shuffleOp, newResultType, adaptor.getV1(), adaptor.getV2(),
544 auto getElementAtIdx = [&rewriter, loc = shuffleOp.getLoc()](
546 if (
auto vecTy = dyn_cast<VectorType>(scalarOrVec.getType()))
547 return rewriter.
create<spirv::CompositeExtractOp>(loc, scalarOrVec,
550 assert(idx == 0 &&
"Invalid scalar element index");
554 int32_t numV1Elems = oldV1Type.getNumElements();
556 for (
auto [shuffleIdx, newOperand] : llvm::zip_equal(mask, newOperands)) {
557 Value vec = adaptor.getV1();
558 int32_t elementIdx = shuffleIdx;
559 if (elementIdx >= numV1Elems) {
560 vec = adaptor.getV2();
561 elementIdx -= numV1Elems;
564 newOperand = getElementAtIdx(vec, elementIdx);
568 if (newOperands.size() == 1) {
569 rewriter.
replaceOp(shuffleOp, newOperands.front());
574 shuffleOp, newResultType, newOperands);
579 struct VectorInterleaveOpConvert final
584 matchAndRewrite(vector::InterleaveOp interleaveOp, OpAdaptor adaptor,
587 VectorType oldResultType = interleaveOp.getResultVectorType();
588 Type newResultType = getTypeConverter()->convertType(oldResultType);
591 "unsupported result vector type");
594 VectorType sourceType = interleaveOp.getSourceVectorType();
595 int n = sourceType.getNumElements();
601 Value newOperands[] = {adaptor.getLhs(), adaptor.getRhs()};
603 interleaveOp, newResultType, newOperands);
607 auto seq = llvm::seq<int64_t>(2 * n);
608 auto indices = llvm::map_to_vector(
609 seq, [n](
int i) {
return (i % 2 ? n : 0) + i / 2; });
613 interleaveOp, newResultType, adaptor.getLhs(), adaptor.getRhs(),
620 struct VectorDeinterleaveOpConvert final
625 matchAndRewrite(vector::DeinterleaveOp deinterleaveOp, OpAdaptor adaptor,
629 VectorType oldResultType = deinterleaveOp.getResultVectorType();
630 Type newResultType = getTypeConverter()->convertType(oldResultType);
633 "unsupported result vector type");
635 Location loc = deinterleaveOp->getLoc();
638 Value sourceVector = adaptor.getSource();
639 VectorType sourceType = deinterleaveOp.getSourceVectorType();
640 int n = sourceType.getNumElements();
646 auto elem0 = rewriter.
create<spirv::CompositeExtractOp>(
649 auto elem1 = rewriter.
create<spirv::CompositeExtractOp>(
652 rewriter.
replaceOp(deinterleaveOp, {elem0, elem1});
657 auto seqEven = llvm::seq<int64_t>(n / 2);
659 llvm::map_to_vector(seqEven, [](
int i) {
return i * 2; });
662 auto seqOdd = llvm::seq<int64_t>(n / 2);
664 llvm::map_to_vector(seqOdd, [](
int i) {
return i * 2 + 1; });
667 auto shuffleEven = rewriter.
create<spirv::VectorShuffleOp>(
668 loc, newResultType, sourceVector, sourceVector,
671 auto shuffleOdd = rewriter.
create<spirv::VectorShuffleOp>(
672 loc, newResultType, sourceVector, sourceVector,
675 rewriter.
replaceOp(deinterleaveOp, {shuffleEven, shuffleOdd});
680 struct VectorLoadOpConverter final
685 matchAndRewrite(vector::LoadOp loadOp, OpAdaptor adaptor,
687 auto memrefType = loadOp.getMemRefType();
689 dyn_cast_or_null<spirv::StorageClassAttr>(memrefType.getMemorySpace());
692 loadOp,
"expected spirv.storage_class memory space");
694 const auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
695 auto loc = loadOp.getLoc();
698 adaptor.getIndices(), loc, rewriter);
701 loadOp,
"failed to get memref element pointer");
703 spirv::StorageClass storageClass = attr.getValue();
704 auto vectorType = loadOp.getVectorType();
706 Value castedAccessChain =
707 rewriter.
create<spirv::BitcastOp>(loc, vectorPtrType, accessChain);
715 struct VectorStoreOpConverter final
720 matchAndRewrite(vector::StoreOp storeOp, OpAdaptor adaptor,
722 auto memrefType = storeOp.getMemRefType();
724 dyn_cast_or_null<spirv::StorageClassAttr>(memrefType.getMemorySpace());
727 storeOp,
"expected spirv.storage_class memory space");
729 const auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
730 auto loc = storeOp.getLoc();
733 adaptor.getIndices(), loc, rewriter);
736 storeOp,
"failed to get memref element pointer");
738 spirv::StorageClass storageClass = attr.getValue();
739 auto vectorType = storeOp.getVectorType();
741 Value castedAccessChain =
742 rewriter.
create<spirv::BitcastOp>(loc, vectorPtrType, accessChain);
744 adaptor.getValueToStore());
750 struct VectorReductionToIntDotProd final
754 LogicalResult matchAndRewrite(vector::ReductionOp op,
756 if (op.getKind() != vector::CombiningKind::ADD)
759 auto resultType = dyn_cast<IntegerType>(op.getType());
764 if (!llvm::is_contained({32, 64}, resultBitwidth))
767 VectorType inVecTy = op.getSourceVectorType();
768 if (!llvm::is_contained({4, 3}, inVecTy.getNumElements()) ||
769 inVecTy.getShape().size() != 1 || inVecTy.isScalable())
772 auto mul = op.getVector().getDefiningOp<arith::MulIOp>();
775 op,
"reduction operand is not 'arith.muli'");
777 if (succeeded(handleCase<arith::ExtSIOp, arith::ExtSIOp, spirv::SDotOp,
778 spirv::SDotAccSatOp,
false>(op, mul, rewriter)))
781 if (succeeded(handleCase<arith::ExtUIOp, arith::ExtUIOp, spirv::UDotOp,
782 spirv::UDotAccSatOp,
false>(op, mul, rewriter)))
785 if (succeeded(handleCase<arith::ExtSIOp, arith::ExtUIOp, spirv::SUDotOp,
786 spirv::SUDotAccSatOp,
false>(op, mul, rewriter)))
789 if (succeeded(handleCase<arith::ExtUIOp, arith::ExtSIOp, spirv::SUDotOp,
790 spirv::SUDotAccSatOp,
true>(op, mul, rewriter)))
797 template <
typename LhsExtensionOp,
typename RhsExtensionOp,
typename DotOp,
798 typename DotAccOp,
bool SwapOperands>
799 static LogicalResult handleCase(vector::ReductionOp op, arith::MulIOp mul,
801 auto lhs = mul.getLhs().getDefiningOp<LhsExtensionOp>();
804 Value lhsIn = lhs.getIn();
805 auto lhsInType = cast<VectorType>(lhsIn.
getType());
806 if (!lhsInType.getElementType().isInteger(8))
809 auto rhs = mul.getRhs().getDefiningOp<RhsExtensionOp>();
812 Value rhsIn = rhs.getIn();
813 auto rhsInType = cast<VectorType>(rhsIn.
getType());
814 if (!rhsInType.getElementType().isInteger(8))
817 if (op.getSourceVectorType().getNumElements() == 3) {
818 IntegerType i8Type = rewriter.
getI8Type();
822 lhsIn = rewriter.
create<spirv::CompositeConstructOp>(
824 rhsIn = rewriter.
create<spirv::CompositeConstructOp>(
831 std::swap(lhsIn, rhsIn);
833 if (
Value acc = op.getAcc()) {
845 struct VectorReductionToFPDotProd final
850 matchAndRewrite(vector::ReductionOp op, OpAdaptor adaptor,
852 if (op.getKind() != vector::CombiningKind::ADD)
855 auto resultType = getTypeConverter()->convertType<
FloatType>(op.getType());
859 Value vec = adaptor.getVector();
860 Value acc = adaptor.getAcc();
862 auto vectorType = dyn_cast<VectorType>(vec.
getType());
864 assert(isa<FloatType>(vec.
getType()) &&
865 "Expected the vector to be scalarized");
886 rewriter.
getFloatAttr(vectorType.getElementType(), 1.0);
888 rhs = rewriter.
create<spirv::ConstantOp>(loc, vectorType, oneAttr);
893 Value res = rewriter.
create<spirv::DotOp>(loc, resultType, lhs, rhs);
895 res = rewriter.
create<spirv::FAddOp>(loc, acc, res);
906 matchAndRewrite(vector::StepOp stepOp, OpAdaptor adaptor,
908 const auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
914 int64_t numElements = stepOp.getType().getNumElements();
920 if (numElements == 1) {
927 source.reserve(numElements);
928 for (int64_t i = 0; i < numElements; ++i) {
930 Value constOp = rewriter.
create<spirv::ConstantOp>(loc, intType, intAttr);
931 source.push_back(constOp);
940 #define CL_INT_MAX_MIN_OPS \
941 spirv::CLUMaxOp, spirv::CLUMinOp, spirv::CLSMaxOp, spirv::CLSMinOp
943 #define GL_INT_MAX_MIN_OPS \
944 spirv::GLUMaxOp, spirv::GLUMinOp, spirv::GLSMaxOp, spirv::GLSMinOp
946 #define CL_FLOAT_MAX_MIN_OPS spirv::CLFMaxOp, spirv::CLFMinOp
947 #define GL_FLOAT_MAX_MIN_OPS spirv::GLFMaxOp, spirv::GLFMinOp
952 VectorBitcastConvert, VectorBroadcastConvert,
953 VectorExtractElementOpConvert, VectorExtractOpConvert,
954 VectorExtractStridedSliceOpConvert, VectorFmaOpConvert<spirv::GLFmaOp>,
955 VectorFmaOpConvert<spirv::CLFmaOp>, VectorInsertElementOpConvert,
956 VectorInsertOpConvert, VectorReductionPattern<GL_INT_MAX_MIN_OPS>,
957 VectorReductionPattern<CL_INT_MAX_MIN_OPS>,
958 VectorReductionFloatMinMax<CL_FLOAT_MAX_MIN_OPS>,
959 VectorReductionFloatMinMax<GL_FLOAT_MAX_MIN_OPS>, VectorShapeCast,
960 VectorInsertStridedSliceOpConvert, VectorShuffleOpConvert,
961 VectorInterleaveOpConvert, VectorDeinterleaveOpConvert,
962 VectorSplatPattern, VectorLoadOpConverter, VectorStoreOpConverter,
963 VectorStepOpConvert>(typeConverter, patterns.
getContext(),
968 patterns.
add<VectorReductionToFPDotProd>(typeConverter, patterns.
getContext(),
974 patterns.
add<VectorReductionToIntDotProd>(patterns.
getContext());
static Value getZero(OpBuilder &b, Location loc, Type elementType)
Get zero value for an element type.
static uint64_t getFirstIntValue(ArrayAttr attr)
Returns the integer value from the first valid input element, assuming Value inputs are defined by a ...
static int getNumBits(Type type)
Returns the number of bits for the given scalar/vector type.
#define INT_AND_FLOAT_CASE(kind, iop, fop)
#define INT_OR_FLOAT_CASE(kind, fop)
Attributes are known-constant values of operations.
IntegerAttr getIntegerAttr(Type type, int64_t value)
ArrayAttr getI32ArrayAttr(ArrayRef< int32_t > values)
FloatAttr getFloatAttr(Type type, double value)
IntegerType getIntegerType(unsigned width)
This class implements a pattern rewriter for use with ConversionPatterns.
void replaceOp(Operation *op, ValueRange newValues) override
Replace the given operation with the new values.
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
OpConversionPattern is a wrapper around ConversionPattern that allows for matching and rewriting agai...
OpConversionPattern(MLIRContext *context, PatternBenefit benefit=1)
This class represents the benefit of a pattern match in a unitless scheme that ranges from 0 (very li...
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
Type conversion from builtin types to SPIR-V types for shader interface.
LogicalResult convertType(Type t, SmallVectorImpl< Type > &results) const
Convert the given type.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
This class provides an abstraction over the different types of ranges over Values.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
static PointerType get(Type pointeeType, StorageClass storageClass)
Value getElementPtr(const SPIRVTypeConverter &typeConverter, MemRefType baseType, Value basePtr, ValueRange indices, Location loc, OpBuilder &builder)
Performs the index computation to get to the element at indices of the memory pointed to by basePtr,...
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
detail::constant_int_value_binder m_ConstantInt(IntegerAttr::ValueType *bind_value)
Matches a constant holding a scalar/vector/tensor integer (splat) and writes the integer value to bin...
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
void populateVectorReductionToSPIRVDotProductPatterns(RewritePatternSet &patterns)
Appends patterns to convert vector reduction of the form:
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
void populateVectorToSPIRVPatterns(const SPIRVTypeConverter &typeConverter, RewritePatternSet &patterns)
Appends to a pattern list additional patterns for translating Vector Ops to SPIR-V ops.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
OpRewritePattern(MLIRContext *context, PatternBenefit benefit=1, ArrayRef< StringRef > generatedNames={})
Patterns must specify the root operation name they match against, and can also specify the benefit of...