28#include "llvm/ADT/ArrayRef.h"
29#include "llvm/ADT/STLExtras.h"
30#include "llvm/ADT/SmallVector.h"
31#include "llvm/ADT/SmallVectorExtras.h"
32#include "llvm/Support/FormatVariadic.h"
43 return (*attr.getAsValueRange<IntegerAttr>().begin()).getZExtValue();
51 if (
auto vectorType = dyn_cast<VectorType>(type))
52 return vectorType.getNumElements() * vectorType.getElementTypeBitWidth();
66static FailureOr<MemoryRequirements>
68 if (preferredAlignment >= std::numeric_limits<uint32_t>::max())
72 auto ptrType = cast<spirv::PointerType>(accessedPtr.
getType());
73 bool mayOmitAlignment =
74 !preferredAlignment &&
75 ptrType.getStorageClass() != spirv::StorageClass::PhysicalStorageBuffer;
81 std::optional<int64_t> sizeInBytes;
83 if (
auto scalarType = dyn_cast<spirv::ScalarType>(pointeeType))
84 sizeInBytes = scalarType.getSizeInBytes();
90 spirv::MemoryAccessAttr::get(ctx, spirv::MemoryAccess::Aligned);
91 uint64_t alignmentValue =
92 preferredAlignment ? preferredAlignment : *sizeInBytes;
93 auto alignment = IntegerAttr::get(IntegerType::get(ctx, 32), alignmentValue);
99struct VectorShapeCast final :
public OpConversionPattern<vector::ShapeCastOp> {
103 matchAndRewrite(vector::ShapeCastOp shapeCastOp, OpAdaptor adaptor,
104 ConversionPatternRewriter &rewriter)
const override {
105 Type dstType = getTypeConverter()->convertType(shapeCastOp.getType());
111 if (dstType == adaptor.getSource().getType() ||
112 shapeCastOp.getResultVectorType().getNumElements() == 1) {
113 rewriter.replaceOp(shapeCastOp, adaptor.getSource());
122struct VectorBitcastConvert final
123 :
public OpConversionPattern<vector::BitCastOp> {
127 matchAndRewrite(vector::BitCastOp bitcastOp, OpAdaptor adaptor,
128 ConversionPatternRewriter &rewriter)
const override {
129 Type dstType = getTypeConverter()->convertType(bitcastOp.getType());
133 if (dstType == adaptor.getSource().getType()) {
134 rewriter.replaceOp(bitcastOp, adaptor.getSource());
141 Type srcType = adaptor.getSource().getType();
143 return rewriter.notifyMatchFailure(
145 llvm::formatv(
"different source ({0}) and target ({1}) bitwidth",
149 rewriter.replaceOpWithNewOp<spirv::BitcastOp>(bitcastOp, dstType,
150 adaptor.getSource());
155struct VectorBroadcastConvert final
156 :
public OpConversionPattern<vector::BroadcastOp> {
160 matchAndRewrite(vector::BroadcastOp castOp, OpAdaptor adaptor,
161 ConversionPatternRewriter &rewriter)
const override {
163 getTypeConverter()->convertType(castOp.getResultVectorType());
167 if (isa<spirv::ScalarType>(resultType)) {
168 rewriter.replaceOp(castOp, adaptor.getSource());
172 SmallVector<Value, 4> source(castOp.getResultVectorType().getNumElements(),
173 adaptor.getSource());
174 rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(castOp, resultType,
186static Value sanitizeDynamicIndex(ConversionPatternRewriter &rewriter,
188 int64_t kPoisonIndex,
unsigned vectorSize) {
189 if (llvm::isPowerOf2_32(vectorSize)) {
190 Value inBoundsMask = spirv::ConstantOp::create(
191 rewriter, loc, dynamicIndex.
getType(),
192 rewriter.getIntegerAttr(dynamicIndex.
getType(), vectorSize - 1));
193 return spirv::BitwiseAndOp::create(rewriter, loc, dynamicIndex,
196 Value poisonIndex = spirv::ConstantOp::create(
197 rewriter, loc, dynamicIndex.
getType(),
198 rewriter.getIntegerAttr(dynamicIndex.
getType(), kPoisonIndex));
200 spirv::IEqualOp::create(rewriter, loc, dynamicIndex, poisonIndex);
201 return spirv::SelectOp::create(
202 rewriter, loc, cmpResult,
203 spirv::ConstantOp::getZero(dynamicIndex.
getType(), loc, rewriter),
207struct VectorExtractOpConvert final
208 :
public OpConversionPattern<vector::ExtractOp> {
212 matchAndRewrite(vector::ExtractOp extractOp, OpAdaptor adaptor,
213 ConversionPatternRewriter &rewriter)
const override {
214 Type dstType = getTypeConverter()->convertType(extractOp.getType());
218 if (isa<spirv::ScalarType>(adaptor.getSource().getType())) {
219 rewriter.replaceOp(extractOp, adaptor.getSource());
223 if (std::optional<int64_t>
id =
225 if (
id == vector::ExtractOp::kPoisonIndex)
226 return rewriter.notifyMatchFailure(
228 "Static use of poison index handled elsewhere (folded to poison)");
229 rewriter.replaceOpWithNewOp<spirv::CompositeExtractOp>(
230 extractOp, dstType, adaptor.getSource(),
231 rewriter.getI32ArrayAttr(
id.value()));
233 Value sanitizedIndex = sanitizeDynamicIndex(
234 rewriter, extractOp.getLoc(), adaptor.getDynamicPosition()[0],
235 vector::ExtractOp::kPoisonIndex,
236 extractOp.getSourceVectorType().getNumElements());
237 rewriter.replaceOpWithNewOp<spirv::VectorExtractDynamicOp>(
238 extractOp, dstType, adaptor.getSource(), sanitizedIndex);
244struct VectorExtractStridedSliceOpConvert final
245 :
public OpConversionPattern<vector::ExtractStridedSliceOp> {
249 matchAndRewrite(vector::ExtractStridedSliceOp extractOp, OpAdaptor adaptor,
250 ConversionPatternRewriter &rewriter)
const override {
251 Type dstType = getTypeConverter()->convertType(extractOp.getType());
261 Value srcVector = adaptor.getOperands().front();
264 if (isa<spirv::ScalarType>(dstType)) {
265 rewriter.replaceOpWithNewOp<spirv::CompositeExtractOp>(extractOp,
270 SmallVector<int32_t, 2>
indices(size);
273 rewriter.replaceOpWithNewOp<spirv::VectorShuffleOp>(
274 extractOp, dstType, srcVector, srcVector,
275 rewriter.getI32ArrayAttr(
indices));
281template <
class SPIRVFMAOp>
282struct VectorFmaOpConvert final :
public OpConversionPattern<vector::FMAOp> {
286 matchAndRewrite(vector::FMAOp fmaOp, OpAdaptor adaptor,
287 ConversionPatternRewriter &rewriter)
const override {
288 Type dstType = getTypeConverter()->convertType(fmaOp.getType());
291 rewriter.replaceOpWithNewOp<SPIRVFMAOp>(fmaOp, dstType, adaptor.getLhs(),
292 adaptor.getRhs(), adaptor.getAcc());
297struct VectorFromElementsOpConvert final
298 :
public OpConversionPattern<vector::FromElementsOp> {
302 matchAndRewrite(vector::FromElementsOp op, OpAdaptor adaptor,
303 ConversionPatternRewriter &rewriter)
const override {
304 Type resultType = getTypeConverter()->convertType(op.getType());
308 if (isa<spirv::ScalarType>(resultType)) {
311 rewriter.replaceOp(op, elements[0]);
316 assert(cast<VectorType>(resultType).getRank() == 1);
317 rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(op, resultType,
323struct VectorInsertOpConvert final
324 :
public OpConversionPattern<vector::InsertOp> {
328 matchAndRewrite(vector::InsertOp insertOp, OpAdaptor adaptor,
329 ConversionPatternRewriter &rewriter)
const override {
330 if (isa<VectorType>(insertOp.getValueToStoreType()))
331 return rewriter.notifyMatchFailure(insertOp,
"unsupported vector source");
332 if (!getTypeConverter()->convertType(insertOp.getDestVectorType()))
333 return rewriter.notifyMatchFailure(insertOp,
334 "unsupported dest vector type");
337 if (insertOp.getValueToStoreType().isIntOrFloat() &&
338 insertOp.getDestVectorType().getNumElements() == 1) {
339 rewriter.replaceOp(insertOp, adaptor.getValueToStore());
343 if (std::optional<int64_t>
id =
345 if (
id == vector::InsertOp::kPoisonIndex)
346 return rewriter.notifyMatchFailure(
348 "Static use of poison index handled elsewhere (folded to poison)");
349 rewriter.replaceOpWithNewOp<spirv::CompositeInsertOp>(
350 insertOp, adaptor.getValueToStore(), adaptor.getDest(),
id.value());
352 Value sanitizedIndex = sanitizeDynamicIndex(
353 rewriter, insertOp.getLoc(), adaptor.getDynamicPosition()[0],
354 vector::InsertOp::kPoisonIndex,
355 insertOp.getDestVectorType().getNumElements());
356 rewriter.replaceOpWithNewOp<spirv::VectorInsertDynamicOp>(
357 insertOp, insertOp.getDest(), adaptor.getValueToStore(),
364struct VectorInsertStridedSliceOpConvert final
365 :
public OpConversionPattern<vector::InsertStridedSliceOp> {
369 matchAndRewrite(vector::InsertStridedSliceOp insertOp, OpAdaptor adaptor,
370 ConversionPatternRewriter &rewriter)
const override {
371 Value srcVector = adaptor.getOperands().front();
372 Value dstVector = adaptor.getOperands().back();
379 if (isa<spirv::ScalarType>(srcVector.
getType())) {
380 assert(!isa<spirv::ScalarType>(dstVector.
getType()));
381 rewriter.replaceOpWithNewOp<spirv::CompositeInsertOp>(
382 insertOp, dstVector.
getType(), srcVector, dstVector,
383 rewriter.getI32ArrayAttr(offset));
387 uint64_t totalSize = cast<VectorType>(dstVector.
getType()).getNumElements();
388 uint64_t insertSize =
389 cast<VectorType>(srcVector.
getType()).getNumElements();
391 SmallVector<int32_t, 2>
indices(totalSize);
393 std::iota(
indices.begin() + offset,
indices.begin() + offset + insertSize,
396 rewriter.replaceOpWithNewOp<spirv::VectorShuffleOp>(
397 insertOp, dstVector.
getType(), dstVector, srcVector,
398 rewriter.getI32ArrayAttr(
indices));
405 vector::ReductionOp reduceOp, vector::ReductionOp::Adaptor adaptor,
406 VectorType srcVectorType, ConversionPatternRewriter &rewriter) {
407 int numElements =
static_cast<int>(srcVectorType.getDimSize(0));
409 values.reserve(numElements + (adaptor.getAcc() ? 1 : 0));
412 for (
int i = 0; i < numElements; ++i) {
413 values.push_back(spirv::CompositeExtractOp::create(
414 rewriter, loc, srcVectorType.getElementType(), adaptor.getVector(),
415 rewriter.getI32ArrayAttr({i})));
418 values.push_back(
acc);
423struct ReductionRewriteInfo {
425 SmallVector<Value> extractedElements;
428FailureOr<ReductionRewriteInfo>
static getReductionInfo(
429 vector::ReductionOp op, vector::ReductionOp::Adaptor adaptor,
430 ConversionPatternRewriter &rewriter,
const TypeConverter &typeConverter) {
431 Type resultType = typeConverter.convertType(op.getType());
435 auto srcVectorType = dyn_cast<VectorType>(adaptor.getVector().getType());
436 if (!srcVectorType || srcVectorType.getRank() != 1)
437 return rewriter.notifyMatchFailure(op,
"not a 1-D vector source");
440 extractAllElements(op, adaptor, srcVectorType, rewriter);
442 return ReductionRewriteInfo{resultType, std::move(extractedElements)};
445template <
typename SPIRVUMaxOp,
typename SPIRVUMinOp,
typename SPIRVSMaxOp,
446 typename SPIRVSMinOp>
447struct VectorReductionPattern final : OpConversionPattern<vector::ReductionOp> {
451 matchAndRewrite(vector::ReductionOp reduceOp, OpAdaptor adaptor,
452 ConversionPatternRewriter &rewriter)
const override {
454 getReductionInfo(reduceOp, adaptor, rewriter, *getTypeConverter());
455 if (
failed(reductionInfo))
458 auto [resultType, extractedElements] = *reductionInfo;
459 Location loc = reduceOp->getLoc();
463 vector::CombiningKind kind = reduceOp.getKind();
465 if (kind == vector::CombiningKind::OR) {
466 Value
result = spirv::AnyOp::create(rewriter, loc, resultType,
467 adaptor.getVector());
468 if (Value acc = adaptor.getAcc())
469 result = spirv::LogicalOrOp::create(rewriter, loc, resultType,
result,
471 rewriter.replaceOp(reduceOp,
result);
475 if (kind == vector::CombiningKind::AND) {
476 Value
result = spirv::AllOp::create(rewriter, loc, resultType,
477 adaptor.getVector());
478 if (Value acc = adaptor.getAcc())
479 result = spirv::LogicalAndOp::create(rewriter, loc, resultType,
481 rewriter.replaceOp(reduceOp,
result);
486 Value
result = extractedElements.front();
487 for (Value next : llvm::drop_begin(extractedElements)) {
488 switch (reduceOp.getKind()) {
490#define INT_AND_FLOAT_CASE(kind, iop, fop) \
491 case vector::CombiningKind::kind: \
492 if (isa<IntegerType>(resultType)) { \
493 result = spirv::iop::create(rewriter, loc, resultType, result, next); \
495 assert(isa<FloatType>(resultType)); \
496 result = spirv::fop::create(rewriter, loc, resultType, result, next); \
500#define INT_OR_FLOAT_CASE(kind, fop) \
501 case vector::CombiningKind::kind: \
502 result = fop::create(rewriter, loc, resultType, result, next); \
505#define INT_CASE(kind, iop) \
506 case vector::CombiningKind::kind: \
507 assert(isa<IntegerType>(resultType)); \
508 result = spirv::iop::create(rewriter, loc, resultType, result, next); \
522 return rewriter.notifyMatchFailure(reduceOp,
"not handled here");
524#undef INT_AND_FLOAT_CASE
525#undef INT_OR_FLOAT_CASE
529 rewriter.replaceOp(reduceOp,
result);
534template <
typename SPIRVFMaxOp,
typename SPIRVFMinOp>
535struct VectorReductionFloatMinMax final
536 : OpConversionPattern<vector::ReductionOp> {
540 matchAndRewrite(vector::ReductionOp reduceOp, OpAdaptor adaptor,
541 ConversionPatternRewriter &rewriter)
const override {
543 getReductionInfo(reduceOp, adaptor, rewriter, *getTypeConverter());
544 if (
failed(reductionInfo))
547 auto [resultType, extractedElements] = *reductionInfo;
548 Location loc = reduceOp->getLoc();
549 Value
result = extractedElements.front();
550 for (Value next : llvm::drop_begin(extractedElements)) {
551 switch (reduceOp.getKind()) {
553#define INT_OR_FLOAT_CASE(kind, fop) \
554 case vector::CombiningKind::kind: \
555 result = fop::create(rewriter, loc, resultType, result, next); \
564 return rewriter.notifyMatchFailure(reduceOp,
"not handled here");
566#undef INT_OR_FLOAT_CASE
569 rewriter.replaceOp(reduceOp,
result);
574class VectorScalarBroadcastPattern final
575 :
public OpConversionPattern<vector::BroadcastOp> {
580 matchAndRewrite(vector::BroadcastOp op, OpAdaptor adaptor,
581 ConversionPatternRewriter &rewriter)
const override {
582 if (isa<VectorType>(op.getSourceType())) {
583 return rewriter.notifyMatchFailure(
584 op,
"only conversion of 'broadcast from scalar' is supported");
586 Type dstType = getTypeConverter()->convertType(op.getType());
589 if (isa<spirv::ScalarType>(dstType)) {
590 rewriter.replaceOp(op, adaptor.getSource());
592 auto dstVecType = cast<VectorType>(dstType);
593 SmallVector<Value, 4> source(dstVecType.getNumElements(),
594 adaptor.getSource());
595 rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(op, dstType,
602struct VectorShuffleOpConvert final
603 :
public OpConversionPattern<vector::ShuffleOp> {
607 matchAndRewrite(vector::ShuffleOp shuffleOp, OpAdaptor adaptor,
608 ConversionPatternRewriter &rewriter)
const override {
609 VectorType oldResultType = shuffleOp.getResultVectorType();
610 Type newResultType = getTypeConverter()->convertType(oldResultType);
612 return rewriter.notifyMatchFailure(shuffleOp,
613 "unsupported result vector type");
615 auto mask = llvm::to_vector_of<int32_t>(shuffleOp.getMask());
617 VectorType oldV1Type = shuffleOp.getV1VectorType();
618 VectorType oldV2Type = shuffleOp.getV2VectorType();
622 if (oldV1Type.getNumElements() > 1 && oldV2Type.getNumElements() > 1 &&
623 oldResultType.getNumElements() > 1) {
624 rewriter.replaceOpWithNewOp<spirv::VectorShuffleOp>(
625 shuffleOp, newResultType, adaptor.getV1(), adaptor.getV2(),
626 rewriter.getI32ArrayAttr(mask));
633 auto getElementAtIdx = [&rewriter, loc = shuffleOp.getLoc(),
634 scalarType = oldV1Type.getElementType()](
635 Value scalarOrVec, int32_t idx) -> Value {
636 if (idx == vector::ShuffleOp::kPoisonIndex)
637 return spirv::UndefOp::create(rewriter, loc, scalarType);
639 if (
auto vecTy = dyn_cast<VectorType>(scalarOrVec.getType()))
640 return spirv::CompositeExtractOp::create(rewriter, loc, scalarOrVec,
643 assert(idx == 0 &&
"Invalid scalar element index");
647 int32_t numV1Elems = oldV1Type.getNumElements();
648 SmallVector<Value> newOperands(mask.size());
649 for (
auto [shuffleIdx, newOperand] : llvm::zip_equal(mask, newOperands)) {
650 Value vec = adaptor.getV1();
651 int32_t elementIdx = shuffleIdx;
652 if (elementIdx >= numV1Elems) {
653 vec = adaptor.getV2();
654 elementIdx -= numV1Elems;
657 newOperand = getElementAtIdx(vec, elementIdx);
661 if (newOperands.size() == 1) {
662 rewriter.replaceOp(shuffleOp, newOperands.front());
666 rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(
667 shuffleOp, newResultType, newOperands);
672struct VectorInterleaveOpConvert final
673 :
public OpConversionPattern<vector::InterleaveOp> {
677 matchAndRewrite(vector::InterleaveOp interleaveOp, OpAdaptor adaptor,
678 ConversionPatternRewriter &rewriter)
const override {
680 VectorType oldResultType = interleaveOp.getResultVectorType();
681 Type newResultType = getTypeConverter()->convertType(oldResultType);
683 return rewriter.notifyMatchFailure(interleaveOp,
684 "unsupported result vector type");
687 VectorType sourceType = interleaveOp.getSourceVectorType();
688 int n = sourceType.getNumElements();
694 Value newOperands[] = {adaptor.getLhs(), adaptor.getRhs()};
695 rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(
696 interleaveOp, newResultType, newOperands);
700 auto seq = llvm::seq<int64_t>(2 * n);
701 auto indices = llvm::map_to_vector(
702 seq, [n](
int i) {
return (i % 2 ? n : 0) + i / 2; });
705 rewriter.replaceOpWithNewOp<spirv::VectorShuffleOp>(
706 interleaveOp, newResultType, adaptor.getLhs(), adaptor.getRhs(),
707 rewriter.getI32ArrayAttr(
indices));
713struct VectorDeinterleaveOpConvert final
714 :
public OpConversionPattern<vector::DeinterleaveOp> {
718 matchAndRewrite(vector::DeinterleaveOp deinterleaveOp, OpAdaptor adaptor,
719 ConversionPatternRewriter &rewriter)
const override {
722 VectorType oldResultType = deinterleaveOp.getResultVectorType();
723 Type newResultType = getTypeConverter()->convertType(oldResultType);
725 return rewriter.notifyMatchFailure(deinterleaveOp,
726 "unsupported result vector type");
728 Location loc = deinterleaveOp->getLoc();
731 Value sourceVector = adaptor.getSource();
732 VectorType sourceType = deinterleaveOp.getSourceVectorType();
733 int n = sourceType.getNumElements();
739 auto elem0 = spirv::CompositeExtractOp::create(
740 rewriter, loc, newResultType, sourceVector,
741 rewriter.getI32ArrayAttr({0}));
743 auto elem1 = spirv::CompositeExtractOp::create(
744 rewriter, loc, newResultType, sourceVector,
745 rewriter.getI32ArrayAttr({1}));
747 rewriter.replaceOp(deinterleaveOp, {elem0, elem1});
752 auto seqEven = llvm::seq<int64_t>(n / 2);
754 llvm::map_to_vector(seqEven, [](
int i) {
return i * 2; });
757 auto seqOdd = llvm::seq<int64_t>(n / 2);
759 llvm::map_to_vector(seqOdd, [](
int i) {
return i * 2 + 1; });
762 auto shuffleEven = spirv::VectorShuffleOp::create(
763 rewriter, loc, newResultType, sourceVector, sourceVector,
764 rewriter.getI32ArrayAttr(indicesEven));
766 auto shuffleOdd = spirv::VectorShuffleOp::create(
767 rewriter, loc, newResultType, sourceVector, sourceVector,
768 rewriter.getI32ArrayAttr(indicesOdd));
770 rewriter.replaceOp(deinterleaveOp, {shuffleEven, shuffleOdd});
775struct VectorLoadOpConverter final
776 :
public OpConversionPattern<vector::LoadOp> {
780 matchAndRewrite(vector::LoadOp loadOp, OpAdaptor adaptor,
781 ConversionPatternRewriter &rewriter)
const override {
782 auto memrefType = loadOp.getMemRefType();
784 dyn_cast_or_null<spirv::StorageClassAttr>(memrefType.getMemorySpace());
786 return rewriter.notifyMatchFailure(
787 loadOp,
"expected spirv.storage_class memory space");
789 const auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
790 auto loc = loadOp.getLoc();
792 typeConverter, memrefType, adaptor.getBase(), adaptor.getIndices(), loc,
793 rewriter, loadOp.getVectorType().getNumElements());
795 return rewriter.notifyMatchFailure(
796 loadOp,
"failed to get memref element pointer");
798 spirv::StorageClass storageClass = attr.getValue();
799 auto vectorType = loadOp.getVectorType();
802 auto spirvVectorType = typeConverter.convertType(vectorType);
803 if (!spirvVectorType)
804 return rewriter.notifyMatchFailure(loadOp,
"unsupported vector type");
811 Value castedAccessChain =
812 (vectorType.getNumElements() == 1)
814 : spirv::BitcastOp::create(rewriter, loc, vectorPtrType,
818 castedAccessChain, loadOp.getAlignment().value_or(0));
819 if (
failed(memoryRequirements))
820 return rewriter.notifyMatchFailure(
821 loadOp,
"failed to determine memory requirements");
823 auto [memoryAccess, alignment] = *memoryRequirements;
824 rewriter.replaceOpWithNewOp<spirv::LoadOp>(
825 loadOp, spirvVectorType, castedAccessChain, memoryAccess, alignment);
831struct VectorStoreOpConverter final
832 :
public OpConversionPattern<vector::StoreOp> {
836 matchAndRewrite(vector::StoreOp storeOp, OpAdaptor adaptor,
837 ConversionPatternRewriter &rewriter)
const override {
838 auto memrefType = storeOp.getMemRefType();
840 dyn_cast_or_null<spirv::StorageClassAttr>(memrefType.getMemorySpace());
842 return rewriter.notifyMatchFailure(
843 storeOp,
"expected spirv.storage_class memory space");
845 const auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
846 auto loc = storeOp.getLoc();
848 typeConverter, memrefType, adaptor.getBase(), adaptor.getIndices(), loc,
849 rewriter, storeOp.getVectorType().getNumElements());
851 return rewriter.notifyMatchFailure(
852 storeOp,
"failed to get memref element pointer");
854 spirv::StorageClass storageClass = attr.getValue();
855 auto vectorType = storeOp.getVectorType();
858 auto spirvVectorType = typeConverter.convertType(vectorType);
859 if (!spirvVectorType)
860 return rewriter.notifyMatchFailure(storeOp,
"unsupported vector type");
867 Value castedAccessChain =
868 (vectorType.getNumElements() == 1)
870 : spirv::BitcastOp::create(rewriter, loc, vectorPtrType,
874 castedAccessChain, storeOp.getAlignment().value_or(0));
875 if (
failed(memoryRequirements))
876 return rewriter.notifyMatchFailure(
877 storeOp,
"failed to determine memory requirements");
879 auto [memoryAccess, alignment] = *memoryRequirements;
880 rewriter.replaceOpWithNewOp<spirv::StoreOp>(storeOp, castedAccessChain,
881 adaptor.getValueToStore(),
882 memoryAccess, alignment);
888struct VectorReductionToIntDotProd final
892 LogicalResult matchAndRewrite(vector::ReductionOp op,
893 PatternRewriter &rewriter)
const override {
894 if (op.getKind() != vector::CombiningKind::ADD)
897 auto resultType = dyn_cast<IntegerType>(op.getType());
902 if (!llvm::is_contained({32, 64}, resultBitwidth))
905 VectorType inVecTy = op.getSourceVectorType();
906 if (!llvm::is_contained({4, 3}, inVecTy.getNumElements()) ||
907 inVecTy.getShape().size() != 1 || inVecTy.isScalable())
910 auto mul = op.getVector().getDefiningOp<arith::MulIOp>();
913 op,
"reduction operand is not 'arith.muli'");
915 if (succeeded(handleCase<arith::ExtSIOp, arith::ExtSIOp, spirv::SDotOp,
916 spirv::SDotAccSatOp,
false>(op,
mul, rewriter)))
919 if (succeeded(handleCase<arith::ExtUIOp, arith::ExtUIOp, spirv::UDotOp,
920 spirv::UDotAccSatOp,
false>(op,
mul, rewriter)))
923 if (succeeded(handleCase<arith::ExtSIOp, arith::ExtUIOp, spirv::SUDotOp,
924 spirv::SUDotAccSatOp,
false>(op,
mul, rewriter)))
927 if (succeeded(handleCase<arith::ExtUIOp, arith::ExtSIOp, spirv::SUDotOp,
928 spirv::SUDotAccSatOp,
true>(op,
mul, rewriter)))
935 template <
typename LhsExtensionOp,
typename RhsExtensionOp,
typename DotOp,
936 typename DotAccOp,
bool SwapOperands>
937 static LogicalResult handleCase(vector::ReductionOp op, arith::MulIOp
mul,
938 PatternRewriter &rewriter) {
939 auto lhs =
mul.getLhs().getDefiningOp<LhsExtensionOp>();
942 Value lhsIn =
lhs.getIn();
943 auto lhsInType = cast<VectorType>(lhsIn.
getType());
944 if (!lhsInType.getElementType().isInteger(8))
947 auto rhs =
mul.getRhs().getDefiningOp<RhsExtensionOp>();
950 Value rhsIn =
rhs.getIn();
951 auto rhsInType = cast<VectorType>(rhsIn.
getType());
952 if (!rhsInType.getElementType().isInteger(8))
955 if (op.getSourceVectorType().getNumElements() == 3) {
956 IntegerType i8Type = rewriter.
getI8Type();
957 auto v4i8Type = VectorType::get({4}, i8Type);
958 Location loc = op.getLoc();
959 Value zero = spirv::ConstantOp::getZero(i8Type, loc, rewriter);
960 lhsIn = spirv::CompositeConstructOp::create(rewriter, loc, v4i8Type,
962 rhsIn = spirv::CompositeConstructOp::create(rewriter, loc, v4i8Type,
969 std::swap(lhsIn, rhsIn);
971 if (Value acc = op.getAcc()) {
983struct VectorReductionToFPDotProd final
984 : OpConversionPattern<vector::ReductionOp> {
988 matchAndRewrite(vector::ReductionOp op, OpAdaptor adaptor,
989 ConversionPatternRewriter &rewriter)
const override {
990 if (op.getKind() != vector::CombiningKind::ADD)
991 return rewriter.notifyMatchFailure(op,
"combining kind is not 'add'");
993 auto resultType = getTypeConverter()->convertType<FloatType>(op.getType());
995 return rewriter.notifyMatchFailure(op,
"result is not a float");
997 Value vec = adaptor.getVector();
998 Value acc = adaptor.getAcc();
1000 auto vectorType = dyn_cast<VectorType>(vec.
getType());
1002 assert(isa<FloatType>(vec.
getType()) &&
1003 "Expected the vector to be scalarized");
1005 rewriter.replaceOpWithNewOp<spirv::FAddOp>(op, acc, vec);
1009 rewriter.replaceOp(op, vec);
1013 Location loc = op.getLoc();
1024 rewriter.getFloatAttr(vectorType.getElementType(), 1.0);
1025 oneAttr = SplatElementsAttr::get(vectorType, oneAttr);
1026 rhs = spirv::ConstantOp::create(rewriter, loc, vectorType, oneAttr);
1031 Value res = spirv::DotOp::create(rewriter, loc, resultType,
lhs,
rhs);
1033 res = spirv::FAddOp::create(rewriter, loc, acc, res);
1035 rewriter.replaceOp(op, res);
1040struct VectorStepOpConvert final : OpConversionPattern<vector::StepOp> {
1044 matchAndRewrite(vector::StepOp stepOp, OpAdaptor adaptor,
1045 ConversionPatternRewriter &rewriter)
const override {
1046 const auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
1047 Type dstType = typeConverter.convertType(stepOp.getType());
1051 Location loc = stepOp.getLoc();
1052 int64_t numElements = stepOp.getType().getNumElements();
1055 Type intType = isa<VectorType>(dstType)
1056 ? cast<VectorType>(dstType).getElementType()
1061 if (numElements == 1) {
1062 Value zero = spirv::ConstantOp::getZero(intType, loc, rewriter);
1063 rewriter.replaceOp(stepOp, zero);
1067 SmallVector<Value> source;
1068 source.reserve(numElements);
1069 for (int64_t i = 0; i < numElements; ++i) {
1070 Attribute intAttr = rewriter.getIntegerAttr(intType, i);
1072 spirv::ConstantOp::create(rewriter, loc, intType, intAttr);
1073 source.push_back(constOp);
1075 rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(stepOp, dstType,
1081struct VectorToElementOpConvert final
1082 : OpConversionPattern<vector::ToElementsOp> {
1086 matchAndRewrite(vector::ToElementsOp toElementsOp, OpAdaptor adaptor,
1087 ConversionPatternRewriter &rewriter)
const override {
1089 SmallVector<Value> results(toElementsOp->getNumResults());
1090 Location loc = toElementsOp.getLoc();
1095 if (isa<spirv::ScalarType>(adaptor.getSource().getType())) {
1096 results[0] = adaptor.getSource();
1097 rewriter.replaceOp(toElementsOp, results);
1101 Type srcElementType = toElementsOp.getElements().getType().front();
1102 Type elementType = getTypeConverter()->convertType(srcElementType);
1104 return rewriter.notifyMatchFailure(
1106 llvm::formatv(
"failed to convert element type '{0}' to SPIR-V",
1109 for (
auto [idx, element] : llvm::enumerate(toElementsOp.getElements())) {
1112 if (element.use_empty())
1115 Value
result = spirv::CompositeExtractOp::create(
1116 rewriter, loc, elementType, adaptor.getSource(),
1117 rewriter.getI32ArrayAttr({static_cast<int32_t>(idx)}));
1121 rewriter.replaceOp(toElementsOp, results);
1127#define CL_INT_MAX_MIN_OPS \
1128 spirv::CLUMaxOp, spirv::CLUMinOp, spirv::CLSMaxOp, spirv::CLSMinOp
1130#define GL_INT_MAX_MIN_OPS \
1131 spirv::GLUMaxOp, spirv::GLUMinOp, spirv::GLSMaxOp, spirv::GLSMinOp
1133#define CL_FLOAT_MAX_MIN_OPS spirv::CLFMaxOp, spirv::CLFMinOp
1134#define GL_FLOAT_MAX_MIN_OPS spirv::GLFMaxOp, spirv::GLFMinOp
1139 VectorBitcastConvert, VectorBroadcastConvert, VectorExtractOpConvert,
1140 VectorExtractStridedSliceOpConvert, VectorFmaOpConvert<spirv::GLFmaOp>,
1141 VectorFmaOpConvert<spirv::CLFmaOp>, VectorFromElementsOpConvert,
1142 VectorToElementOpConvert, VectorInsertOpConvert,
1143 VectorReductionPattern<GL_INT_MAX_MIN_OPS>,
1144 VectorReductionPattern<CL_INT_MAX_MIN_OPS>,
1145 VectorReductionFloatMinMax<CL_FLOAT_MAX_MIN_OPS>,
1146 VectorReductionFloatMinMax<GL_FLOAT_MAX_MIN_OPS>, VectorShapeCast,
1147 VectorInsertStridedSliceOpConvert, VectorShuffleOpConvert,
1148 VectorInterleaveOpConvert, VectorDeinterleaveOpConvert,
1149 VectorScalarBroadcastPattern, VectorLoadOpConverter,
1150 VectorStoreOpConverter, VectorStepOpConvert>(
1155 patterns.
add<VectorReductionToFPDotProd>(typeConverter, patterns.
getContext(),
1161 patterns.
add<VectorReductionToIntDotProd>(patterns.
getContext());
static constexpr unsigned getNumBits()
#define INT_CASE(kind, iop)
static uint64_t getFirstIntValue(ArrayAttr attr)
Returns the integer value from the first valid input element, assuming Value inputs are defined by a ...
#define INT_AND_FLOAT_CASE(kind, iop, fop)
#define INT_OR_FLOAT_CASE(kind, fop)
static FailureOr< MemoryRequirements > calculateMemoryRequirements(Value accessedPtr, uint64_t preferredAlignment)
PhysicalStorageBuffer pointers always need Aligned, derived from pointee element size unless preferre...
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
This class represents the benefit of a pattern match in a unitless scheme that ranges from 0 (very li...
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.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
bool isInteger() const
Return true if this is an integer type (with the specified width).
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
MLIRContext * getContext() const
Utility to get the associated MLIRContext that this value is defined in.
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.
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:
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
void populateVectorToSPIRVPatterns(const SPIRVTypeConverter &typeConverter, RewritePatternSet &patterns)
Appends to a pattern list additional patterns for translating Vector Ops to SPIR-V ops.
spirv::MemoryAccessAttr memoryAccess
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...