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;
82 Type pointeeType = ptrType.getPointeeType();
83 if (
auto vecType = dyn_cast<VectorType>(pointeeType))
84 pointeeType = vecType.getElementType();
85 if (
auto scalarType = dyn_cast<spirv::ScalarType>(pointeeType))
86 sizeInBytes = scalarType.getSizeInBytes();
92 spirv::MemoryAccessAttr::get(ctx, spirv::MemoryAccess::Aligned);
93 uint64_t alignmentValue =
94 preferredAlignment ? preferredAlignment : *sizeInBytes;
95 auto alignment = IntegerAttr::get(IntegerType::get(ctx, 32), alignmentValue);
101struct VectorShapeCast final :
public OpConversionPattern<vector::ShapeCastOp> {
105 matchAndRewrite(vector::ShapeCastOp shapeCastOp, OpAdaptor adaptor,
106 ConversionPatternRewriter &rewriter)
const override {
107 Type dstType = getTypeConverter()->convertType(shapeCastOp.getType());
113 if (dstType == adaptor.getSource().getType() ||
114 shapeCastOp.getResultVectorType().getNumElements() == 1) {
115 rewriter.replaceOp(shapeCastOp, adaptor.getSource());
124struct VectorBitcastConvert final
125 :
public OpConversionPattern<vector::BitCastOp> {
129 matchAndRewrite(vector::BitCastOp bitcastOp, OpAdaptor adaptor,
130 ConversionPatternRewriter &rewriter)
const override {
131 Type dstType = getTypeConverter()->convertType(bitcastOp.getType());
135 if (dstType == adaptor.getSource().getType()) {
136 rewriter.replaceOp(bitcastOp, adaptor.getSource());
143 Type srcType = adaptor.getSource().getType();
145 return rewriter.notifyMatchFailure(
147 llvm::formatv(
"different source ({0}) and target ({1}) bitwidth",
151 rewriter.replaceOpWithNewOp<spirv::BitcastOp>(bitcastOp, dstType,
152 adaptor.getSource());
157struct VectorBroadcastConvert final
158 :
public OpConversionPattern<vector::BroadcastOp> {
162 matchAndRewrite(vector::BroadcastOp castOp, OpAdaptor adaptor,
163 ConversionPatternRewriter &rewriter)
const override {
165 getTypeConverter()->convertType(castOp.getResultVectorType());
169 if (isa<spirv::ScalarType>(resultType)) {
170 rewriter.replaceOp(castOp, adaptor.getSource());
174 SmallVector<Value, 4> source(castOp.getResultVectorType().getNumElements(),
175 adaptor.getSource());
176 rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(castOp, resultType,
188static Value sanitizeDynamicIndex(ConversionPatternRewriter &rewriter,
190 int64_t kPoisonIndex,
unsigned vectorSize) {
191 if (llvm::isPowerOf2_32(vectorSize)) {
192 Value inBoundsMask = spirv::ConstantOp::create(
193 rewriter, loc, dynamicIndex.
getType(),
194 rewriter.getIntegerAttr(dynamicIndex.
getType(), vectorSize - 1));
195 return spirv::BitwiseAndOp::create(rewriter, loc, dynamicIndex,
198 Value poisonIndex = spirv::ConstantOp::create(
199 rewriter, loc, dynamicIndex.
getType(),
200 rewriter.getIntegerAttr(dynamicIndex.
getType(), kPoisonIndex));
202 spirv::IEqualOp::create(rewriter, loc, dynamicIndex, poisonIndex);
203 return spirv::SelectOp::create(
204 rewriter, loc, cmpResult,
205 spirv::ConstantOp::getZero(dynamicIndex.
getType(), loc, rewriter),
209struct VectorExtractOpConvert final
210 :
public OpConversionPattern<vector::ExtractOp> {
214 matchAndRewrite(vector::ExtractOp extractOp, OpAdaptor adaptor,
215 ConversionPatternRewriter &rewriter)
const override {
216 Type dstType = getTypeConverter()->convertType(extractOp.getType());
220 if (isa<spirv::ScalarType>(adaptor.getSource().getType())) {
221 rewriter.replaceOp(extractOp, adaptor.getSource());
225 if (std::optional<int64_t>
id =
227 if (
id == vector::ExtractOp::kPoisonIndex)
228 return rewriter.notifyMatchFailure(
230 "Static use of poison index handled elsewhere (folded to poison)");
231 rewriter.replaceOpWithNewOp<spirv::CompositeExtractOp>(
232 extractOp, dstType, adaptor.getSource(),
233 rewriter.getI32ArrayAttr(
id.value()));
235 Value sanitizedIndex = sanitizeDynamicIndex(
236 rewriter, extractOp.getLoc(), adaptor.getDynamicPosition()[0],
237 vector::ExtractOp::kPoisonIndex,
238 extractOp.getSourceVectorType().getNumElements());
239 rewriter.replaceOpWithNewOp<spirv::VectorExtractDynamicOp>(
240 extractOp, dstType, adaptor.getSource(), sanitizedIndex);
246struct VectorExtractStridedSliceOpConvert final
247 :
public OpConversionPattern<vector::ExtractStridedSliceOp> {
251 matchAndRewrite(vector::ExtractStridedSliceOp extractOp, OpAdaptor adaptor,
252 ConversionPatternRewriter &rewriter)
const override {
253 Type dstType = getTypeConverter()->convertType(extractOp.getType());
263 Value srcVector = adaptor.getOperands().front();
266 if (isa<spirv::ScalarType>(dstType)) {
267 rewriter.replaceOpWithNewOp<spirv::CompositeExtractOp>(extractOp,
272 SmallVector<int32_t, 2>
indices(size);
275 rewriter.replaceOpWithNewOp<spirv::VectorShuffleOp>(
276 extractOp, dstType, srcVector, srcVector,
277 rewriter.getI32ArrayAttr(
indices));
283template <
class SPIRVFMAOp>
284struct VectorFmaOpConvert final :
public OpConversionPattern<vector::FMAOp> {
288 matchAndRewrite(vector::FMAOp fmaOp, OpAdaptor adaptor,
289 ConversionPatternRewriter &rewriter)
const override {
290 Type dstType = getTypeConverter()->convertType(fmaOp.getType());
293 rewriter.replaceOpWithNewOp<SPIRVFMAOp>(fmaOp, dstType, adaptor.getLhs(),
294 adaptor.getRhs(), adaptor.getAcc());
299struct VectorFromElementsOpConvert final
300 :
public OpConversionPattern<vector::FromElementsOp> {
304 matchAndRewrite(vector::FromElementsOp op, OpAdaptor adaptor,
305 ConversionPatternRewriter &rewriter)
const override {
306 Type resultType = getTypeConverter()->convertType(op.getType());
310 if (isa<spirv::ScalarType>(resultType)) {
313 rewriter.replaceOp(op, elements[0]);
318 assert(cast<VectorType>(resultType).getRank() == 1);
319 rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(op, resultType,
325struct VectorInsertOpConvert final
326 :
public OpConversionPattern<vector::InsertOp> {
330 matchAndRewrite(vector::InsertOp insertOp, OpAdaptor adaptor,
331 ConversionPatternRewriter &rewriter)
const override {
332 if (isa<VectorType>(insertOp.getValueToStoreType()))
333 return rewriter.notifyMatchFailure(insertOp,
"unsupported vector source");
334 if (!getTypeConverter()->convertType(insertOp.getDestVectorType()))
335 return rewriter.notifyMatchFailure(insertOp,
336 "unsupported dest vector type");
339 if (insertOp.getValueToStoreType().isIntOrFloat() &&
340 insertOp.getDestVectorType().getNumElements() == 1) {
341 rewriter.replaceOp(insertOp, adaptor.getValueToStore());
345 if (std::optional<int64_t>
id =
347 if (
id == vector::InsertOp::kPoisonIndex)
348 return rewriter.notifyMatchFailure(
350 "Static use of poison index handled elsewhere (folded to poison)");
351 rewriter.replaceOpWithNewOp<spirv::CompositeInsertOp>(
352 insertOp, adaptor.getValueToStore(), adaptor.getDest(),
id.value());
354 Value sanitizedIndex = sanitizeDynamicIndex(
355 rewriter, insertOp.getLoc(), adaptor.getDynamicPosition()[0],
356 vector::InsertOp::kPoisonIndex,
357 insertOp.getDestVectorType().getNumElements());
358 rewriter.replaceOpWithNewOp<spirv::VectorInsertDynamicOp>(
359 insertOp, insertOp.getDest(), adaptor.getValueToStore(),
366struct VectorInsertStridedSliceOpConvert final
367 :
public OpConversionPattern<vector::InsertStridedSliceOp> {
371 matchAndRewrite(vector::InsertStridedSliceOp insertOp, OpAdaptor adaptor,
372 ConversionPatternRewriter &rewriter)
const override {
373 Value srcVector = adaptor.getOperands().front();
374 Value dstVector = adaptor.getOperands().back();
381 if (isa<spirv::ScalarType>(srcVector.
getType())) {
382 assert(!isa<spirv::ScalarType>(dstVector.
getType()));
383 rewriter.replaceOpWithNewOp<spirv::CompositeInsertOp>(
384 insertOp, dstVector.
getType(), srcVector, dstVector,
385 rewriter.getI32ArrayAttr(offset));
389 uint64_t totalSize = cast<VectorType>(dstVector.
getType()).getNumElements();
390 uint64_t insertSize =
391 cast<VectorType>(srcVector.
getType()).getNumElements();
393 SmallVector<int32_t, 2>
indices(totalSize);
395 std::iota(
indices.begin() + offset,
indices.begin() + offset + insertSize,
398 rewriter.replaceOpWithNewOp<spirv::VectorShuffleOp>(
399 insertOp, dstVector.
getType(), dstVector, srcVector,
400 rewriter.getI32ArrayAttr(
indices));
407 vector::ReductionOp reduceOp, vector::ReductionOp::Adaptor adaptor,
408 VectorType srcVectorType, ConversionPatternRewriter &rewriter) {
409 int numElements =
static_cast<int>(srcVectorType.getDimSize(0));
411 values.reserve(numElements + (adaptor.getAcc() ? 1 : 0));
414 for (
int i = 0; i < numElements; ++i) {
415 values.push_back(spirv::CompositeExtractOp::create(
416 rewriter, loc, srcVectorType.getElementType(), adaptor.getVector(),
417 rewriter.getI32ArrayAttr({i})));
420 values.push_back(
acc);
425struct ReductionRewriteInfo {
427 SmallVector<Value> extractedElements;
430FailureOr<ReductionRewriteInfo>
static getReductionInfo(
431 vector::ReductionOp op, vector::ReductionOp::Adaptor adaptor,
432 ConversionPatternRewriter &rewriter,
const TypeConverter &typeConverter) {
433 Type resultType = typeConverter.convertType(op.getType());
437 auto srcVectorType = dyn_cast<VectorType>(adaptor.getVector().getType());
438 if (!srcVectorType || srcVectorType.getRank() != 1)
439 return rewriter.notifyMatchFailure(op,
"not a 1-D vector source");
442 extractAllElements(op, adaptor, srcVectorType, rewriter);
444 return ReductionRewriteInfo{resultType, std::move(extractedElements)};
447template <
typename SPIRVUMaxOp,
typename SPIRVUMinOp,
typename SPIRVSMaxOp,
448 typename SPIRVSMinOp>
449struct VectorReductionPattern final : OpConversionPattern<vector::ReductionOp> {
453 matchAndRewrite(vector::ReductionOp reduceOp, OpAdaptor adaptor,
454 ConversionPatternRewriter &rewriter)
const override {
456 getReductionInfo(reduceOp, adaptor, rewriter, *getTypeConverter());
457 if (
failed(reductionInfo))
460 auto [resultType, extractedElements] = *reductionInfo;
461 Location loc = reduceOp->getLoc();
465 vector::CombiningKind kind = reduceOp.getKind();
467 if (kind == vector::CombiningKind::OR) {
468 Value
result = spirv::AnyOp::create(rewriter, loc, resultType,
469 adaptor.getVector());
470 if (Value acc = adaptor.getAcc())
471 result = spirv::LogicalOrOp::create(rewriter, loc, resultType,
result,
473 rewriter.replaceOp(reduceOp,
result);
477 if (kind == vector::CombiningKind::AND) {
478 Value
result = spirv::AllOp::create(rewriter, loc, resultType,
479 adaptor.getVector());
480 if (Value acc = adaptor.getAcc())
481 result = spirv::LogicalAndOp::create(rewriter, loc, resultType,
483 rewriter.replaceOp(reduceOp,
result);
488 Value
result = extractedElements.front();
489 for (Value next : llvm::drop_begin(extractedElements)) {
490 switch (reduceOp.getKind()) {
492#define INT_AND_FLOAT_CASE(kind, iop, fop) \
493 case vector::CombiningKind::kind: \
494 if (isa<IntegerType>(resultType)) { \
495 result = spirv::iop::create(rewriter, loc, resultType, result, next); \
497 assert(isa<FloatType>(resultType)); \
498 result = spirv::fop::create(rewriter, loc, resultType, result, next); \
502#define INT_OR_FLOAT_CASE(kind, fop) \
503 case vector::CombiningKind::kind: \
504 result = fop::create(rewriter, loc, resultType, result, next); \
507#define INT_CASE(kind, iop) \
508 case vector::CombiningKind::kind: \
509 assert(isa<IntegerType>(resultType)); \
510 result = spirv::iop::create(rewriter, loc, resultType, result, next); \
524 return rewriter.notifyMatchFailure(reduceOp,
"not handled here");
526#undef INT_AND_FLOAT_CASE
527#undef INT_OR_FLOAT_CASE
531 rewriter.replaceOp(reduceOp,
result);
536template <
typename SPIRVFMaxOp,
typename SPIRVFMinOp>
537struct VectorReductionFloatMinMax final
538 : OpConversionPattern<vector::ReductionOp> {
542 matchAndRewrite(vector::ReductionOp reduceOp, OpAdaptor adaptor,
543 ConversionPatternRewriter &rewriter)
const override {
545 getReductionInfo(reduceOp, adaptor, rewriter, *getTypeConverter());
546 if (
failed(reductionInfo))
549 auto [resultType, extractedElements] = *reductionInfo;
550 Location loc = reduceOp->getLoc();
551 Value
result = extractedElements.front();
552 for (Value next : llvm::drop_begin(extractedElements)) {
553 switch (reduceOp.getKind()) {
555#define INT_OR_FLOAT_CASE(kind, fop) \
556 case vector::CombiningKind::kind: \
557 result = fop::create(rewriter, loc, resultType, result, next); \
566 return rewriter.notifyMatchFailure(reduceOp,
"not handled here");
568#undef INT_OR_FLOAT_CASE
571 rewriter.replaceOp(reduceOp,
result);
576class VectorScalarBroadcastPattern final
577 :
public OpConversionPattern<vector::BroadcastOp> {
582 matchAndRewrite(vector::BroadcastOp op, OpAdaptor adaptor,
583 ConversionPatternRewriter &rewriter)
const override {
584 if (isa<VectorType>(op.getSourceType())) {
585 return rewriter.notifyMatchFailure(
586 op,
"only conversion of 'broadcast from scalar' is supported");
588 Type dstType = getTypeConverter()->convertType(op.getType());
591 if (isa<spirv::ScalarType>(dstType)) {
592 rewriter.replaceOp(op, adaptor.getSource());
594 auto dstVecType = cast<VectorType>(dstType);
595 SmallVector<Value, 4> source(dstVecType.getNumElements(),
596 adaptor.getSource());
597 rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(op, dstType,
604struct VectorShuffleOpConvert final
605 :
public OpConversionPattern<vector::ShuffleOp> {
609 matchAndRewrite(vector::ShuffleOp shuffleOp, OpAdaptor adaptor,
610 ConversionPatternRewriter &rewriter)
const override {
611 VectorType oldResultType = shuffleOp.getResultVectorType();
612 Type newResultType = getTypeConverter()->convertType(oldResultType);
614 return rewriter.notifyMatchFailure(shuffleOp,
615 "unsupported result vector type");
617 auto mask = llvm::to_vector_of<int32_t>(shuffleOp.getMask());
619 VectorType oldV1Type = shuffleOp.getV1VectorType();
620 VectorType oldV2Type = shuffleOp.getV2VectorType();
624 if (oldV1Type.getNumElements() > 1 && oldV2Type.getNumElements() > 1 &&
625 oldResultType.getNumElements() > 1) {
626 rewriter.replaceOpWithNewOp<spirv::VectorShuffleOp>(
627 shuffleOp, newResultType, adaptor.getV1(), adaptor.getV2(),
628 rewriter.getI32ArrayAttr(mask));
635 auto getElementAtIdx = [&rewriter, loc = shuffleOp.getLoc(),
636 scalarType = oldV1Type.getElementType()](
637 Value scalarOrVec, int32_t idx) -> Value {
638 if (idx == vector::ShuffleOp::kPoisonIndex)
639 return spirv::UndefOp::create(rewriter, loc, scalarType);
641 if (
auto vecTy = dyn_cast<VectorType>(scalarOrVec.getType()))
642 return spirv::CompositeExtractOp::create(rewriter, loc, scalarOrVec,
645 assert(idx == 0 &&
"Invalid scalar element index");
649 int32_t numV1Elems = oldV1Type.getNumElements();
650 SmallVector<Value> newOperands(mask.size());
651 for (
auto [shuffleIdx, newOperand] : llvm::zip_equal(mask, newOperands)) {
652 Value vec = adaptor.getV1();
653 int32_t elementIdx = shuffleIdx;
654 if (elementIdx >= numV1Elems) {
655 vec = adaptor.getV2();
656 elementIdx -= numV1Elems;
659 newOperand = getElementAtIdx(vec, elementIdx);
663 if (newOperands.size() == 1) {
664 rewriter.replaceOp(shuffleOp, newOperands.front());
668 rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(
669 shuffleOp, newResultType, newOperands);
674struct VectorInterleaveOpConvert final
675 :
public OpConversionPattern<vector::InterleaveOp> {
679 matchAndRewrite(vector::InterleaveOp interleaveOp, OpAdaptor adaptor,
680 ConversionPatternRewriter &rewriter)
const override {
682 VectorType oldResultType = interleaveOp.getResultVectorType();
683 Type newResultType = getTypeConverter()->convertType(oldResultType);
685 return rewriter.notifyMatchFailure(interleaveOp,
686 "unsupported result vector type");
689 VectorType sourceType = interleaveOp.getSourceVectorType();
690 int n = sourceType.getNumElements();
696 Value newOperands[] = {adaptor.getLhs(), adaptor.getRhs()};
697 rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(
698 interleaveOp, newResultType, newOperands);
702 auto seq = llvm::seq<int64_t>(2 * n);
703 auto indices = llvm::map_to_vector(
704 seq, [n](
int i) {
return (i % 2 ? n : 0) + i / 2; });
707 rewriter.replaceOpWithNewOp<spirv::VectorShuffleOp>(
708 interleaveOp, newResultType, adaptor.getLhs(), adaptor.getRhs(),
709 rewriter.getI32ArrayAttr(
indices));
715struct VectorDeinterleaveOpConvert final
716 :
public OpConversionPattern<vector::DeinterleaveOp> {
720 matchAndRewrite(vector::DeinterleaveOp deinterleaveOp, OpAdaptor adaptor,
721 ConversionPatternRewriter &rewriter)
const override {
724 VectorType oldResultType = deinterleaveOp.getResultVectorType();
725 Type newResultType = getTypeConverter()->convertType(oldResultType);
727 return rewriter.notifyMatchFailure(deinterleaveOp,
728 "unsupported result vector type");
730 Location loc = deinterleaveOp->getLoc();
733 Value sourceVector = adaptor.getSource();
734 VectorType sourceType = deinterleaveOp.getSourceVectorType();
735 int n = sourceType.getNumElements();
741 auto elem0 = spirv::CompositeExtractOp::create(
742 rewriter, loc, newResultType, sourceVector,
743 rewriter.getI32ArrayAttr({0}));
745 auto elem1 = spirv::CompositeExtractOp::create(
746 rewriter, loc, newResultType, sourceVector,
747 rewriter.getI32ArrayAttr({1}));
749 rewriter.replaceOp(deinterleaveOp, {elem0, elem1});
754 auto seqEven = llvm::seq<int64_t>(n / 2);
756 llvm::map_to_vector(seqEven, [](
int i) {
return i * 2; });
759 auto seqOdd = llvm::seq<int64_t>(n / 2);
761 llvm::map_to_vector(seqOdd, [](
int i) {
return i * 2 + 1; });
764 auto shuffleEven = spirv::VectorShuffleOp::create(
765 rewriter, loc, newResultType, sourceVector, sourceVector,
766 rewriter.getI32ArrayAttr(indicesEven));
768 auto shuffleOdd = spirv::VectorShuffleOp::create(
769 rewriter, loc, newResultType, sourceVector, sourceVector,
770 rewriter.getI32ArrayAttr(indicesOdd));
772 rewriter.replaceOp(deinterleaveOp, {shuffleEven, shuffleOdd});
777struct VectorLoadOpConverter final
778 :
public OpConversionPattern<vector::LoadOp> {
782 matchAndRewrite(vector::LoadOp loadOp, OpAdaptor adaptor,
783 ConversionPatternRewriter &rewriter)
const override {
784 auto memrefType = loadOp.getMemRefType();
786 dyn_cast_or_null<spirv::StorageClassAttr>(memrefType.getMemorySpace());
788 return rewriter.notifyMatchFailure(
789 loadOp,
"expected spirv.storage_class memory space");
791 const auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
792 auto loc = loadOp.getLoc();
794 typeConverter, memrefType, adaptor.getBase(), adaptor.getIndices(), loc,
795 rewriter, loadOp.getVectorType().getNumElements());
797 return rewriter.notifyMatchFailure(
798 loadOp,
"failed to get memref element pointer");
800 spirv::StorageClass storageClass = attr.getValue();
801 auto vectorType = loadOp.getVectorType();
804 auto spirvVectorType = typeConverter.convertType(vectorType);
805 if (!spirvVectorType)
806 return rewriter.notifyMatchFailure(loadOp,
"unsupported vector type");
813 Value castedAccessChain =
814 (vectorType.getNumElements() == 1)
816 : spirv::BitcastOp::create(rewriter, loc, vectorPtrType,
820 castedAccessChain, loadOp.getAlignment().value_or(0));
821 if (
failed(memoryRequirements))
822 return rewriter.notifyMatchFailure(
823 loadOp,
"failed to determine memory requirements");
825 auto [memoryAccess, alignment] = *memoryRequirements;
826 rewriter.replaceOpWithNewOp<spirv::LoadOp>(
827 loadOp, spirvVectorType, castedAccessChain, memoryAccess, alignment);
833struct VectorStoreOpConverter final
834 :
public OpConversionPattern<vector::StoreOp> {
838 matchAndRewrite(vector::StoreOp storeOp, OpAdaptor adaptor,
839 ConversionPatternRewriter &rewriter)
const override {
840 auto memrefType = storeOp.getMemRefType();
842 dyn_cast_or_null<spirv::StorageClassAttr>(memrefType.getMemorySpace());
844 return rewriter.notifyMatchFailure(
845 storeOp,
"expected spirv.storage_class memory space");
847 const auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
848 auto loc = storeOp.getLoc();
850 typeConverter, memrefType, adaptor.getBase(), adaptor.getIndices(), loc,
851 rewriter, storeOp.getVectorType().getNumElements());
853 return rewriter.notifyMatchFailure(
854 storeOp,
"failed to get memref element pointer");
856 spirv::StorageClass storageClass = attr.getValue();
857 auto vectorType = storeOp.getVectorType();
860 auto spirvVectorType = typeConverter.convertType(vectorType);
861 if (!spirvVectorType)
862 return rewriter.notifyMatchFailure(storeOp,
"unsupported vector type");
869 Value castedAccessChain =
870 (vectorType.getNumElements() == 1)
872 : spirv::BitcastOp::create(rewriter, loc, vectorPtrType,
876 castedAccessChain, storeOp.getAlignment().value_or(0));
877 if (
failed(memoryRequirements))
878 return rewriter.notifyMatchFailure(
879 storeOp,
"failed to determine memory requirements");
881 auto [memoryAccess, alignment] = *memoryRequirements;
882 rewriter.replaceOpWithNewOp<spirv::StoreOp>(storeOp, castedAccessChain,
883 adaptor.getValueToStore(),
884 memoryAccess, alignment);
890struct VectorReductionToIntDotProd final
894 LogicalResult matchAndRewrite(vector::ReductionOp op,
895 PatternRewriter &rewriter)
const override {
896 if (op.getKind() != vector::CombiningKind::ADD)
899 auto resultType = dyn_cast<IntegerType>(op.getType());
904 if (!llvm::is_contained({32, 64}, resultBitwidth))
907 VectorType inVecTy = op.getSourceVectorType();
908 if (!llvm::is_contained({4, 3}, inVecTy.getNumElements()) ||
909 inVecTy.getShape().size() != 1 || inVecTy.isScalable())
912 auto mul = op.getVector().getDefiningOp<arith::MulIOp>();
915 op,
"reduction operand is not 'arith.muli'");
917 if (succeeded(handleCase<arith::ExtSIOp, arith::ExtSIOp, spirv::SDotOp,
918 spirv::SDotAccSatOp,
false>(op,
mul, rewriter)))
921 if (succeeded(handleCase<arith::ExtUIOp, arith::ExtUIOp, spirv::UDotOp,
922 spirv::UDotAccSatOp,
false>(op,
mul, rewriter)))
925 if (succeeded(handleCase<arith::ExtSIOp, arith::ExtUIOp, spirv::SUDotOp,
926 spirv::SUDotAccSatOp,
false>(op,
mul, rewriter)))
929 if (succeeded(handleCase<arith::ExtUIOp, arith::ExtSIOp, spirv::SUDotOp,
930 spirv::SUDotAccSatOp,
true>(op,
mul, rewriter)))
937 template <
typename LhsExtensionOp,
typename RhsExtensionOp,
typename DotOp,
938 typename DotAccOp,
bool SwapOperands>
939 static LogicalResult handleCase(vector::ReductionOp op, arith::MulIOp
mul,
940 PatternRewriter &rewriter) {
941 auto lhs =
mul.getLhs().getDefiningOp<LhsExtensionOp>();
944 Value lhsIn =
lhs.getIn();
945 auto lhsInType = cast<VectorType>(lhsIn.
getType());
946 if (!lhsInType.getElementType().isInteger(8))
949 auto rhs =
mul.getRhs().getDefiningOp<RhsExtensionOp>();
952 Value rhsIn =
rhs.getIn();
953 auto rhsInType = cast<VectorType>(rhsIn.
getType());
954 if (!rhsInType.getElementType().isInteger(8))
957 if (op.getSourceVectorType().getNumElements() == 3) {
958 IntegerType i8Type = rewriter.
getI8Type();
959 auto v4i8Type = VectorType::get({4}, i8Type);
960 Location loc = op.getLoc();
961 Value zero = spirv::ConstantOp::getZero(i8Type, loc, rewriter);
962 lhsIn = spirv::CompositeConstructOp::create(rewriter, loc, v4i8Type,
964 rhsIn = spirv::CompositeConstructOp::create(rewriter, loc, v4i8Type,
971 std::swap(lhsIn, rhsIn);
973 if (Value acc = op.getAcc()) {
985struct VectorReductionToFPDotProd final
986 : OpConversionPattern<vector::ReductionOp> {
990 matchAndRewrite(vector::ReductionOp op, OpAdaptor adaptor,
991 ConversionPatternRewriter &rewriter)
const override {
992 if (op.getKind() != vector::CombiningKind::ADD)
993 return rewriter.notifyMatchFailure(op,
"combining kind is not 'add'");
995 auto resultType = getTypeConverter()->convertType<FloatType>(op.getType());
997 return rewriter.notifyMatchFailure(op,
"result is not a float");
999 Value vec = adaptor.getVector();
1000 Value acc = adaptor.getAcc();
1002 auto vectorType = dyn_cast<VectorType>(vec.
getType());
1004 assert(isa<FloatType>(vec.
getType()) &&
1005 "Expected the vector to be scalarized");
1007 rewriter.replaceOpWithNewOp<spirv::FAddOp>(op, acc, vec);
1011 rewriter.replaceOp(op, vec);
1015 Location loc = op.getLoc();
1026 rewriter.getFloatAttr(vectorType.getElementType(), 1.0);
1027 oneAttr = SplatElementsAttr::get(vectorType, oneAttr);
1028 rhs = spirv::ConstantOp::create(rewriter, loc, vectorType, oneAttr);
1033 Value res = spirv::DotOp::create(rewriter, loc, resultType,
lhs,
rhs);
1035 res = spirv::FAddOp::create(rewriter, loc, acc, res);
1037 rewriter.replaceOp(op, res);
1042struct VectorStepOpConvert final : OpConversionPattern<vector::StepOp> {
1046 matchAndRewrite(vector::StepOp stepOp, OpAdaptor adaptor,
1047 ConversionPatternRewriter &rewriter)
const override {
1048 const auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
1049 Type dstType = typeConverter.convertType(stepOp.getType());
1053 Location loc = stepOp.getLoc();
1054 int64_t numElements = stepOp.getType().getNumElements();
1057 Type intType = isa<VectorType>(dstType)
1058 ? cast<VectorType>(dstType).getElementType()
1063 if (numElements == 1) {
1064 Value zero = spirv::ConstantOp::getZero(intType, loc, rewriter);
1065 rewriter.replaceOp(stepOp, zero);
1069 SmallVector<Value> source;
1070 source.reserve(numElements);
1071 for (int64_t i = 0; i < numElements; ++i) {
1072 Attribute intAttr = rewriter.getIntegerAttr(intType, i);
1074 spirv::ConstantOp::create(rewriter, loc, intType, intAttr);
1075 source.push_back(constOp);
1077 rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(stepOp, dstType,
1083struct VectorToElementOpConvert final
1084 : OpConversionPattern<vector::ToElementsOp> {
1088 matchAndRewrite(vector::ToElementsOp toElementsOp, OpAdaptor adaptor,
1089 ConversionPatternRewriter &rewriter)
const override {
1091 SmallVector<Value> results(toElementsOp->getNumResults());
1092 Location loc = toElementsOp.getLoc();
1097 if (isa<spirv::ScalarType>(adaptor.getSource().getType())) {
1098 results[0] = adaptor.getSource();
1099 rewriter.replaceOp(toElementsOp, results);
1103 Type srcElementType = toElementsOp.getElements().getType().front();
1104 Type elementType = getTypeConverter()->convertType(srcElementType);
1106 return rewriter.notifyMatchFailure(
1108 llvm::formatv(
"failed to convert element type '{0}' to SPIR-V",
1111 for (
auto [idx, element] : llvm::enumerate(toElementsOp.getElements())) {
1114 if (element.use_empty())
1117 Value
result = spirv::CompositeExtractOp::create(
1118 rewriter, loc, elementType, adaptor.getSource(),
1119 rewriter.getI32ArrayAttr({static_cast<int32_t>(idx)}));
1123 rewriter.replaceOp(toElementsOp, results);
1129#define CL_INT_MAX_MIN_OPS \
1130 spirv::CLUMaxOp, spirv::CLUMinOp, spirv::CLSMaxOp, spirv::CLSMinOp
1132#define GL_INT_MAX_MIN_OPS \
1133 spirv::GLUMaxOp, spirv::GLUMinOp, spirv::GLSMaxOp, spirv::GLSMinOp
1135#define CL_FLOAT_MAX_MIN_OPS spirv::CLFMaxOp, spirv::CLFMinOp
1136#define GL_FLOAT_MAX_MIN_OPS spirv::GLFMaxOp, spirv::GLFMinOp
1141 VectorBitcastConvert, VectorBroadcastConvert, VectorExtractOpConvert,
1142 VectorExtractStridedSliceOpConvert, VectorFmaOpConvert<spirv::GLFmaOp>,
1143 VectorFmaOpConvert<spirv::CLFmaOp>, VectorFromElementsOpConvert,
1144 VectorToElementOpConvert, VectorInsertOpConvert,
1145 VectorReductionPattern<GL_INT_MAX_MIN_OPS>,
1146 VectorReductionPattern<CL_INT_MAX_MIN_OPS>,
1147 VectorReductionFloatMinMax<CL_FLOAT_MAX_MIN_OPS>,
1148 VectorReductionFloatMinMax<GL_FLOAT_MAX_MIN_OPS>, VectorShapeCast,
1149 VectorInsertStridedSliceOpConvert, VectorShuffleOpConvert,
1150 VectorInterleaveOpConvert, VectorDeinterleaveOpConvert,
1151 VectorScalarBroadcastPattern, VectorLoadOpConverter,
1152 VectorStoreOpConverter, VectorStepOpConvert>(
1157 patterns.
add<VectorReductionToFPDotProd>(typeConverter, patterns.
getContext(),
1163 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:
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...