MLIR 24.0.0git
VectorToSPIRV.cpp
Go to the documentation of this file.
1//===- VectorToSPIRV.cpp - Vector to SPIR-V Patterns ----------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements patterns to convert Vector dialect to SPIRV dialect.
10//
11//===----------------------------------------------------------------------===//
12
14
21#include "mlir/IR/Attributes.h"
24#include "mlir/IR/Location.h"
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"
33#include <cassert>
34#include <cstdint>
35#include <numeric>
36
37using namespace mlir;
38
39/// Returns the integer value from the first valid input element, assuming Value
40/// inputs are defined by a constant index ops and Attribute inputs are integer
41/// attributes.
42static uint64_t getFirstIntValue(ArrayAttr attr) {
43 return (*attr.getAsValueRange<IntegerAttr>().begin()).getZExtValue();
44}
45
46/// Returns the number of bits for the given scalar/vector type.
47static int getNumBits(Type type) {
48 // TODO: This does not take into account any memory layout or widening
49 // constraints. E.g., a vector<3xi57> may report to occupy 3x57=171 bit, even
50 // though in practice it will likely be stored as in a 4xi64 vector register.
51 if (auto vectorType = dyn_cast<VectorType>(type))
52 return vectorType.getNumElements() * vectorType.getElementTypeBitWidth();
53 return type.getIntOrFloatBitWidth();
54}
55
56namespace {
57struct MemoryRequirements {
58 spirv::MemoryAccessAttr memoryAccess;
59 IntegerAttr alignment;
60};
61} // namespace
62
63/// PhysicalStorageBuffer pointers always need Aligned, derived from pointee
64/// element size unless preferredAlignment is set. Other storage classes may
65/// omit it.
66static FailureOr<MemoryRequirements>
67calculateMemoryRequirements(Value accessedPtr, uint64_t preferredAlignment) {
68 if (preferredAlignment >= std::numeric_limits<uint32_t>::max())
69 return failure();
70
71 MLIRContext *ctx = accessedPtr.getContext();
72 auto ptrType = cast<spirv::PointerType>(accessedPtr.getType());
73 bool mayOmitAlignment =
74 !preferredAlignment &&
75 ptrType.getStorageClass() != spirv::StorageClass::PhysicalStorageBuffer;
76 if (mayOmitAlignment)
77 return MemoryRequirements{spirv::MemoryAccessAttr{}, IntegerAttr{}};
78
79 // PhysicalStorageBuffer pointers require Aligned. The vector pointer is a
80 // bitcast of a scalar element pointer, so only element alignment is known.
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();
87
88 if (!sizeInBytes)
89 return failure();
90
91 auto memoryAccess =
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);
96 return MemoryRequirements{memoryAccess, alignment};
97}
98
99namespace {
100
101struct VectorShapeCast final : public OpConversionPattern<vector::ShapeCastOp> {
102 using Base::Base;
103
104 LogicalResult
105 matchAndRewrite(vector::ShapeCastOp shapeCastOp, OpAdaptor adaptor,
106 ConversionPatternRewriter &rewriter) const override {
107 Type dstType = getTypeConverter()->convertType(shapeCastOp.getType());
108 if (!dstType)
109 return failure();
110
111 // If dstType is same as the source type or the vector size is 1, it can be
112 // directly replaced by the source.
113 if (dstType == adaptor.getSource().getType() ||
114 shapeCastOp.getResultVectorType().getNumElements() == 1) {
115 rewriter.replaceOp(shapeCastOp, adaptor.getSource());
116 return success();
117 }
118
119 // Lowering for size-n vectors when n > 1 hasn't been implemented.
120 return failure();
121 }
122};
123
124struct VectorBitcastConvert final
125 : public OpConversionPattern<vector::BitCastOp> {
126 using Base::Base;
127
128 LogicalResult
129 matchAndRewrite(vector::BitCastOp bitcastOp, OpAdaptor adaptor,
130 ConversionPatternRewriter &rewriter) const override {
131 Type dstType = getTypeConverter()->convertType(bitcastOp.getType());
132 if (!dstType)
133 return failure();
134
135 if (dstType == adaptor.getSource().getType()) {
136 rewriter.replaceOp(bitcastOp, adaptor.getSource());
137 return success();
138 }
139
140 // Check that the source and destination type have the same bitwidth.
141 // Depending on the target environment, we may need to emulate certain
142 // types, which can cause issue with bitcast.
143 Type srcType = adaptor.getSource().getType();
144 if (getNumBits(dstType) != getNumBits(srcType)) {
145 return rewriter.notifyMatchFailure(
146 bitcastOp,
147 llvm::formatv("different source ({0}) and target ({1}) bitwidth",
148 srcType, dstType));
149 }
150
151 rewriter.replaceOpWithNewOp<spirv::BitcastOp>(bitcastOp, dstType,
152 adaptor.getSource());
153 return success();
154 }
155};
156
157struct VectorBroadcastConvert final
158 : public OpConversionPattern<vector::BroadcastOp> {
159 using Base::Base;
160
161 LogicalResult
162 matchAndRewrite(vector::BroadcastOp castOp, OpAdaptor adaptor,
163 ConversionPatternRewriter &rewriter) const override {
164 Type resultType =
165 getTypeConverter()->convertType(castOp.getResultVectorType());
166 if (!resultType)
167 return failure();
168
169 if (isa<spirv::ScalarType>(resultType)) {
170 rewriter.replaceOp(castOp, adaptor.getSource());
171 return success();
172 }
173
174 SmallVector<Value, 4> source(castOp.getResultVectorType().getNumElements(),
175 adaptor.getSource());
176 rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(castOp, resultType,
177 source);
178 return success();
179 }
180};
181
182// SPIR-V does not have a concept of a poison index for certain instructions,
183// which creates a UB hazard when lowering from otherwise equivalent Vector
184// dialect instructions, because this index will be considered out-of-bounds.
185// To avoid this, this function implements a dynamic sanitization that returns
186// some arbitrary safe index. For power-of-two vector sizes, this uses a bitmask
187// (presumably more efficient), and otherwise index 0 (always in-bounds).
188static Value sanitizeDynamicIndex(ConversionPatternRewriter &rewriter,
189 Location loc, Value dynamicIndex,
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,
196 inBoundsMask);
197 }
198 Value poisonIndex = spirv::ConstantOp::create(
199 rewriter, loc, dynamicIndex.getType(),
200 rewriter.getIntegerAttr(dynamicIndex.getType(), kPoisonIndex));
201 Value cmpResult =
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),
206 dynamicIndex);
207}
208
209struct VectorExtractOpConvert final
210 : public OpConversionPattern<vector::ExtractOp> {
211 using Base::Base;
212
213 LogicalResult
214 matchAndRewrite(vector::ExtractOp extractOp, OpAdaptor adaptor,
215 ConversionPatternRewriter &rewriter) const override {
216 Type dstType = getTypeConverter()->convertType(extractOp.getType());
217 if (!dstType)
218 return failure();
219
220 if (isa<spirv::ScalarType>(adaptor.getSource().getType())) {
221 rewriter.replaceOp(extractOp, adaptor.getSource());
222 return success();
223 }
224
225 if (std::optional<int64_t> id =
226 getConstantIntValue(extractOp.getMixedPosition()[0])) {
227 if (id == vector::ExtractOp::kPoisonIndex)
228 return rewriter.notifyMatchFailure(
229 extractOp,
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()));
234 } else {
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);
241 }
242 return success();
243 }
244};
245
246struct VectorExtractStridedSliceOpConvert final
247 : public OpConversionPattern<vector::ExtractStridedSliceOp> {
248 using Base::Base;
249
250 LogicalResult
251 matchAndRewrite(vector::ExtractStridedSliceOp extractOp, OpAdaptor adaptor,
252 ConversionPatternRewriter &rewriter) const override {
253 Type dstType = getTypeConverter()->convertType(extractOp.getType());
254 if (!dstType)
255 return failure();
256
257 uint64_t offset = getFirstIntValue(extractOp.getOffsets());
258 uint64_t size = getFirstIntValue(extractOp.getSizes());
259 uint64_t stride = getFirstIntValue(extractOp.getStrides());
260 if (stride != 1)
261 return failure();
262
263 Value srcVector = adaptor.getOperands().front();
264
265 // Extract vector<1xT> case.
266 if (isa<spirv::ScalarType>(dstType)) {
267 rewriter.replaceOpWithNewOp<spirv::CompositeExtractOp>(extractOp,
268 srcVector, offset);
269 return success();
270 }
271
272 SmallVector<int32_t, 2> indices(size);
273 std::iota(indices.begin(), indices.end(), offset);
274
275 rewriter.replaceOpWithNewOp<spirv::VectorShuffleOp>(
276 extractOp, dstType, srcVector, srcVector,
277 rewriter.getI32ArrayAttr(indices));
278
279 return success();
280 }
281};
282
283template <class SPIRVFMAOp>
284struct VectorFmaOpConvert final : public OpConversionPattern<vector::FMAOp> {
285 using Base::Base;
286
287 LogicalResult
288 matchAndRewrite(vector::FMAOp fmaOp, OpAdaptor adaptor,
289 ConversionPatternRewriter &rewriter) const override {
290 Type dstType = getTypeConverter()->convertType(fmaOp.getType());
291 if (!dstType)
292 return failure();
293 rewriter.replaceOpWithNewOp<SPIRVFMAOp>(fmaOp, dstType, adaptor.getLhs(),
294 adaptor.getRhs(), adaptor.getAcc());
295 return success();
296 }
297};
298
299struct VectorFromElementsOpConvert final
300 : public OpConversionPattern<vector::FromElementsOp> {
301 using Base::Base;
302
303 LogicalResult
304 matchAndRewrite(vector::FromElementsOp op, OpAdaptor adaptor,
305 ConversionPatternRewriter &rewriter) const override {
306 Type resultType = getTypeConverter()->convertType(op.getType());
307 if (!resultType)
308 return failure();
309 ValueRange elements = adaptor.getElements();
310 if (isa<spirv::ScalarType>(resultType)) {
311 // In the case with a single scalar operand / single-element result,
312 // pass through the scalar.
313 rewriter.replaceOp(op, elements[0]);
314 return success();
315 }
316 // SPIRVTypeConverter rejects vectors with rank > 1, so multi-dimensional
317 // vector.from_elements cases should not need to be handled, only 1d.
318 assert(cast<VectorType>(resultType).getRank() == 1);
319 rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(op, resultType,
320 elements);
321 return success();
322 }
323};
324
325struct VectorInsertOpConvert final
326 : public OpConversionPattern<vector::InsertOp> {
327 using Base::Base;
328
329 LogicalResult
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");
337
338 // Special case for inserting scalar values into size-1 vectors.
339 if (insertOp.getValueToStoreType().isIntOrFloat() &&
340 insertOp.getDestVectorType().getNumElements() == 1) {
341 rewriter.replaceOp(insertOp, adaptor.getValueToStore());
342 return success();
343 }
344
345 if (std::optional<int64_t> id =
346 getConstantIntValue(insertOp.getMixedPosition()[0])) {
347 if (id == vector::InsertOp::kPoisonIndex)
348 return rewriter.notifyMatchFailure(
349 insertOp,
350 "Static use of poison index handled elsewhere (folded to poison)");
351 rewriter.replaceOpWithNewOp<spirv::CompositeInsertOp>(
352 insertOp, adaptor.getValueToStore(), adaptor.getDest(), id.value());
353 } else {
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(),
360 sanitizedIndex);
361 }
362 return success();
363 }
364};
365
366struct VectorInsertStridedSliceOpConvert final
367 : public OpConversionPattern<vector::InsertStridedSliceOp> {
368 using Base::Base;
369
370 LogicalResult
371 matchAndRewrite(vector::InsertStridedSliceOp insertOp, OpAdaptor adaptor,
372 ConversionPatternRewriter &rewriter) const override {
373 Value srcVector = adaptor.getOperands().front();
374 Value dstVector = adaptor.getOperands().back();
375
376 uint64_t stride = getFirstIntValue(insertOp.getStrides());
377 if (stride != 1)
378 return failure();
379 uint64_t offset = getFirstIntValue(insertOp.getOffsets());
380
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));
386 return success();
387 }
388
389 uint64_t totalSize = cast<VectorType>(dstVector.getType()).getNumElements();
390 uint64_t insertSize =
391 cast<VectorType>(srcVector.getType()).getNumElements();
392
393 SmallVector<int32_t, 2> indices(totalSize);
394 std::iota(indices.begin(), indices.end(), 0);
395 std::iota(indices.begin() + offset, indices.begin() + offset + insertSize,
396 totalSize);
397
398 rewriter.replaceOpWithNewOp<spirv::VectorShuffleOp>(
399 insertOp, dstVector.getType(), dstVector, srcVector,
400 rewriter.getI32ArrayAttr(indices));
401
402 return success();
403 }
404};
405
406static SmallVector<Value> extractAllElements(
407 vector::ReductionOp reduceOp, vector::ReductionOp::Adaptor adaptor,
408 VectorType srcVectorType, ConversionPatternRewriter &rewriter) {
409 int numElements = static_cast<int>(srcVectorType.getDimSize(0));
410 SmallVector<Value> values;
411 values.reserve(numElements + (adaptor.getAcc() ? 1 : 0));
412 Location loc = reduceOp.getLoc();
413
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})));
418 }
419 if (Value acc = adaptor.getAcc())
420 values.push_back(acc);
421
422 return values;
423}
424
425struct ReductionRewriteInfo {
426 Type resultType;
427 SmallVector<Value> extractedElements;
428};
429
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());
434 if (!resultType)
435 return failure();
436
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");
440
441 SmallVector<Value> extractedElements =
442 extractAllElements(op, adaptor, srcVectorType, rewriter);
443
444 return ReductionRewriteInfo{resultType, std::move(extractedElements)};
445}
446
447template <typename SPIRVUMaxOp, typename SPIRVUMinOp, typename SPIRVSMaxOp,
448 typename SPIRVSMinOp>
449struct VectorReductionPattern final : OpConversionPattern<vector::ReductionOp> {
450 using Base::Base;
451
452 LogicalResult
453 matchAndRewrite(vector::ReductionOp reduceOp, OpAdaptor adaptor,
454 ConversionPatternRewriter &rewriter) const override {
455 auto reductionInfo =
456 getReductionInfo(reduceOp, adaptor, rewriter, *getTypeConverter());
457 if (failed(reductionInfo))
458 return failure();
459
460 auto [resultType, extractedElements] = *reductionInfo;
461 Location loc = reduceOp->getLoc();
462
463 // Handle boolean reductions with spirv.Any / spirv.All.
464 if (resultType.isInteger(1)) {
465 vector::CombiningKind kind = reduceOp.getKind();
466
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,
472 acc);
473 rewriter.replaceOp(reduceOp, result);
474 return success();
475 }
476
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,
482 result, acc);
483 rewriter.replaceOp(reduceOp, result);
484 return success();
485 }
486 }
487
488 Value result = extractedElements.front();
489 for (Value next : llvm::drop_begin(extractedElements)) {
490 switch (reduceOp.getKind()) {
491
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); \
496 } else { \
497 assert(isa<FloatType>(resultType)); \
498 result = spirv::fop::create(rewriter, loc, resultType, result, next); \
499 } \
500 break
501
502#define INT_OR_FLOAT_CASE(kind, fop) \
503 case vector::CombiningKind::kind: \
504 result = fop::create(rewriter, loc, resultType, result, next); \
505 break
506
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); \
511 break
512
513 INT_AND_FLOAT_CASE(ADD, IAddOp, FAddOp);
514 INT_AND_FLOAT_CASE(MUL, IMulOp, FMulOp);
515 INT_OR_FLOAT_CASE(MINUI, SPIRVUMinOp);
516 INT_OR_FLOAT_CASE(MINSI, SPIRVSMinOp);
517 INT_OR_FLOAT_CASE(MAXUI, SPIRVUMaxOp);
518 INT_OR_FLOAT_CASE(MAXSI, SPIRVSMaxOp);
519 INT_CASE(AND, BitwiseAndOp);
520 INT_CASE(OR, BitwiseOrOp);
521 INT_CASE(XOR, BitwiseXorOp);
522
523 default:
524 return rewriter.notifyMatchFailure(reduceOp, "not handled here");
525 }
526#undef INT_AND_FLOAT_CASE
527#undef INT_OR_FLOAT_CASE
528#undef INT_CASE
529 }
530
531 rewriter.replaceOp(reduceOp, result);
532 return success();
533 }
534};
535
536template <typename SPIRVFMaxOp, typename SPIRVFMinOp>
537struct VectorReductionFloatMinMax final
538 : OpConversionPattern<vector::ReductionOp> {
539 using Base::Base;
540
541 LogicalResult
542 matchAndRewrite(vector::ReductionOp reduceOp, OpAdaptor adaptor,
543 ConversionPatternRewriter &rewriter) const override {
544 auto reductionInfo =
545 getReductionInfo(reduceOp, adaptor, rewriter, *getTypeConverter());
546 if (failed(reductionInfo))
547 return failure();
548
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()) {
554
555#define INT_OR_FLOAT_CASE(kind, fop) \
556 case vector::CombiningKind::kind: \
557 result = fop::create(rewriter, loc, resultType, result, next); \
558 break
559
560 INT_OR_FLOAT_CASE(MAXIMUMF, SPIRVFMaxOp);
561 INT_OR_FLOAT_CASE(MINIMUMF, SPIRVFMinOp);
562 INT_OR_FLOAT_CASE(MAXNUMF, SPIRVFMaxOp);
563 INT_OR_FLOAT_CASE(MINNUMF, SPIRVFMinOp);
564
565 default:
566 return rewriter.notifyMatchFailure(reduceOp, "not handled here");
567 }
568#undef INT_OR_FLOAT_CASE
569 }
570
571 rewriter.replaceOp(reduceOp, result);
572 return success();
573 }
574};
575
576class VectorScalarBroadcastPattern final
577 : public OpConversionPattern<vector::BroadcastOp> {
578public:
579 using Base::Base;
580
581 LogicalResult
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");
587 }
588 Type dstType = getTypeConverter()->convertType(op.getType());
589 if (!dstType)
590 return failure();
591 if (isa<spirv::ScalarType>(dstType)) {
592 rewriter.replaceOp(op, adaptor.getSource());
593 } else {
594 auto dstVecType = cast<VectorType>(dstType);
595 SmallVector<Value, 4> source(dstVecType.getNumElements(),
596 adaptor.getSource());
597 rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(op, dstType,
598 source);
599 }
600 return success();
601 }
602};
603
604struct VectorShuffleOpConvert final
605 : public OpConversionPattern<vector::ShuffleOp> {
606 using Base::Base;
607
608 LogicalResult
609 matchAndRewrite(vector::ShuffleOp shuffleOp, OpAdaptor adaptor,
610 ConversionPatternRewriter &rewriter) const override {
611 VectorType oldResultType = shuffleOp.getResultVectorType();
612 Type newResultType = getTypeConverter()->convertType(oldResultType);
613 if (!newResultType)
614 return rewriter.notifyMatchFailure(shuffleOp,
615 "unsupported result vector type");
616
617 auto mask = llvm::to_vector_of<int32_t>(shuffleOp.getMask());
618
619 VectorType oldV1Type = shuffleOp.getV1VectorType();
620 VectorType oldV2Type = shuffleOp.getV2VectorType();
621
622 // When both operands and the result are SPIR-V vectors, emit a SPIR-V
623 // shuffle.
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));
629 return success();
630 }
631
632 // When at least one of the operands or the result becomes a scalar after
633 // type conversion for SPIR-V, extract all the required elements and
634 // construct the result vector.
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);
640
641 if (auto vecTy = dyn_cast<VectorType>(scalarOrVec.getType()))
642 return spirv::CompositeExtractOp::create(rewriter, loc, scalarOrVec,
643 idx);
644
645 assert(idx == 0 && "Invalid scalar element index");
646 return scalarOrVec;
647 };
648
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;
657 }
658
659 newOperand = getElementAtIdx(vec, elementIdx);
660 }
661
662 // Handle the scalar result corner case.
663 if (newOperands.size() == 1) {
664 rewriter.replaceOp(shuffleOp, newOperands.front());
665 return success();
666 }
667
668 rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(
669 shuffleOp, newResultType, newOperands);
670 return success();
671 }
672};
673
674struct VectorInterleaveOpConvert final
675 : public OpConversionPattern<vector::InterleaveOp> {
676 using Base::Base;
677
678 LogicalResult
679 matchAndRewrite(vector::InterleaveOp interleaveOp, OpAdaptor adaptor,
680 ConversionPatternRewriter &rewriter) const override {
681 // Check the result vector type.
682 VectorType oldResultType = interleaveOp.getResultVectorType();
683 Type newResultType = getTypeConverter()->convertType(oldResultType);
684 if (!newResultType)
685 return rewriter.notifyMatchFailure(interleaveOp,
686 "unsupported result vector type");
687
688 // Interleave the indices.
689 VectorType sourceType = interleaveOp.getSourceVectorType();
690 int n = sourceType.getNumElements();
691
692 // Input vectors of size 1 are converted to scalars by the type converter.
693 // We cannot use `spirv::VectorShuffleOp` directly in this case, and need to
694 // use `spirv::CompositeConstructOp`.
695 if (n == 1) {
696 Value newOperands[] = {adaptor.getLhs(), adaptor.getRhs()};
697 rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(
698 interleaveOp, newResultType, newOperands);
699 return success();
700 }
701
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; });
705
706 // Emit a SPIR-V shuffle.
707 rewriter.replaceOpWithNewOp<spirv::VectorShuffleOp>(
708 interleaveOp, newResultType, adaptor.getLhs(), adaptor.getRhs(),
709 rewriter.getI32ArrayAttr(indices));
710
711 return success();
712 }
713};
714
715struct VectorDeinterleaveOpConvert final
716 : public OpConversionPattern<vector::DeinterleaveOp> {
717 using Base::Base;
718
719 LogicalResult
720 matchAndRewrite(vector::DeinterleaveOp deinterleaveOp, OpAdaptor adaptor,
721 ConversionPatternRewriter &rewriter) const override {
722
723 // Check the result vector type.
724 VectorType oldResultType = deinterleaveOp.getResultVectorType();
725 Type newResultType = getTypeConverter()->convertType(oldResultType);
726 if (!newResultType)
727 return rewriter.notifyMatchFailure(deinterleaveOp,
728 "unsupported result vector type");
729
730 Location loc = deinterleaveOp->getLoc();
731
732 // Deinterleave the indices.
733 Value sourceVector = adaptor.getSource();
734 VectorType sourceType = deinterleaveOp.getSourceVectorType();
735 int n = sourceType.getNumElements();
736
737 // Output vectors of size 1 are converted to scalars by the type converter.
738 // We cannot use `spirv::VectorShuffleOp` directly in this case, and need to
739 // use `spirv::CompositeExtractOp`.
740 if (n == 2) {
741 auto elem0 = spirv::CompositeExtractOp::create(
742 rewriter, loc, newResultType, sourceVector,
743 rewriter.getI32ArrayAttr({0}));
744
745 auto elem1 = spirv::CompositeExtractOp::create(
746 rewriter, loc, newResultType, sourceVector,
747 rewriter.getI32ArrayAttr({1}));
748
749 rewriter.replaceOp(deinterleaveOp, {elem0, elem1});
750 return success();
751 }
752
753 // Indices for `shuffleEven` (result 0).
754 auto seqEven = llvm::seq<int64_t>(n / 2);
755 auto indicesEven =
756 llvm::map_to_vector(seqEven, [](int i) { return i * 2; });
757
758 // Indices for `shuffleOdd` (result 1).
759 auto seqOdd = llvm::seq<int64_t>(n / 2);
760 auto indicesOdd =
761 llvm::map_to_vector(seqOdd, [](int i) { return i * 2 + 1; });
762
763 // Create two SPIR-V shuffles.
764 auto shuffleEven = spirv::VectorShuffleOp::create(
765 rewriter, loc, newResultType, sourceVector, sourceVector,
766 rewriter.getI32ArrayAttr(indicesEven));
767
768 auto shuffleOdd = spirv::VectorShuffleOp::create(
769 rewriter, loc, newResultType, sourceVector, sourceVector,
770 rewriter.getI32ArrayAttr(indicesOdd));
771
772 rewriter.replaceOp(deinterleaveOp, {shuffleEven, shuffleOdd});
773 return success();
774 }
775};
776
777struct VectorLoadOpConverter final
778 : public OpConversionPattern<vector::LoadOp> {
779 using Base::Base;
780
781 LogicalResult
782 matchAndRewrite(vector::LoadOp loadOp, OpAdaptor adaptor,
783 ConversionPatternRewriter &rewriter) const override {
784 auto memrefType = loadOp.getMemRefType();
785 auto attr =
786 dyn_cast_or_null<spirv::StorageClassAttr>(memrefType.getMemorySpace());
787 if (!attr)
788 return rewriter.notifyMatchFailure(
789 loadOp, "expected spirv.storage_class memory space");
790
791 const auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
792 auto loc = loadOp.getLoc();
793 Value accessChain = spirv::getElementPtr(
794 typeConverter, memrefType, adaptor.getBase(), adaptor.getIndices(), loc,
795 rewriter, loadOp.getVectorType().getNumElements());
796 if (!accessChain)
797 return rewriter.notifyMatchFailure(
798 loadOp, "failed to get memref element pointer");
799
800 spirv::StorageClass storageClass = attr.getValue();
801 auto vectorType = loadOp.getVectorType();
802 // Use the converted vector type instead of original (single element vector
803 // would get converted to scalar).
804 auto spirvVectorType = typeConverter.convertType(vectorType);
805 if (!spirvVectorType)
806 return rewriter.notifyMatchFailure(loadOp, "unsupported vector type");
807
808 auto vectorPtrType = spirv::PointerType::get(spirvVectorType, storageClass);
809
810 // For single element vectors, we don't need to bitcast the access chain to
811 // the original vector type. Both is going to be the same, a pointer
812 // to a scalar.
813 Value castedAccessChain =
814 (vectorType.getNumElements() == 1)
815 ? accessChain
816 : spirv::BitcastOp::create(rewriter, loc, vectorPtrType,
817 accessChain);
818
819 auto memoryRequirements = calculateMemoryRequirements(
820 castedAccessChain, loadOp.getAlignment().value_or(0));
821 if (failed(memoryRequirements))
822 return rewriter.notifyMatchFailure(
823 loadOp, "failed to determine memory requirements");
824
825 auto [memoryAccess, alignment] = *memoryRequirements;
826 rewriter.replaceOpWithNewOp<spirv::LoadOp>(
827 loadOp, spirvVectorType, castedAccessChain, memoryAccess, alignment);
828
829 return success();
830 }
831};
832
833struct VectorStoreOpConverter final
834 : public OpConversionPattern<vector::StoreOp> {
835 using Base::Base;
836
837 LogicalResult
838 matchAndRewrite(vector::StoreOp storeOp, OpAdaptor adaptor,
839 ConversionPatternRewriter &rewriter) const override {
840 auto memrefType = storeOp.getMemRefType();
841 auto attr =
842 dyn_cast_or_null<spirv::StorageClassAttr>(memrefType.getMemorySpace());
843 if (!attr)
844 return rewriter.notifyMatchFailure(
845 storeOp, "expected spirv.storage_class memory space");
846
847 const auto &typeConverter = *getTypeConverter<SPIRVTypeConverter>();
848 auto loc = storeOp.getLoc();
849 Value accessChain = spirv::getElementPtr(
850 typeConverter, memrefType, adaptor.getBase(), adaptor.getIndices(), loc,
851 rewriter, storeOp.getVectorType().getNumElements());
852 if (!accessChain)
853 return rewriter.notifyMatchFailure(
854 storeOp, "failed to get memref element pointer");
855
856 spirv::StorageClass storageClass = attr.getValue();
857 auto vectorType = storeOp.getVectorType();
858 // Use the converted vector type instead of original (single element vector
859 // would get converted to scalar).
860 auto spirvVectorType = typeConverter.convertType(vectorType);
861 if (!spirvVectorType)
862 return rewriter.notifyMatchFailure(storeOp, "unsupported vector type");
863
864 auto vectorPtrType = spirv::PointerType::get(spirvVectorType, storageClass);
865
866 // For single element vectors, we don't need to bitcast the access chain to
867 // the original vector type. Both is going to be the same, a pointer
868 // to a scalar.
869 Value castedAccessChain =
870 (vectorType.getNumElements() == 1)
871 ? accessChain
872 : spirv::BitcastOp::create(rewriter, loc, vectorPtrType,
873 accessChain);
874
875 auto memoryRequirements = calculateMemoryRequirements(
876 castedAccessChain, storeOp.getAlignment().value_or(0));
877 if (failed(memoryRequirements))
878 return rewriter.notifyMatchFailure(
879 storeOp, "failed to determine memory requirements");
880
881 auto [memoryAccess, alignment] = *memoryRequirements;
882 rewriter.replaceOpWithNewOp<spirv::StoreOp>(storeOp, castedAccessChain,
883 adaptor.getValueToStore(),
884 memoryAccess, alignment);
885
886 return success();
887 }
888};
889
890struct VectorReductionToIntDotProd final
891 : OpRewritePattern<vector::ReductionOp> {
892 using Base::Base;
893
894 LogicalResult matchAndRewrite(vector::ReductionOp op,
895 PatternRewriter &rewriter) const override {
896 if (op.getKind() != vector::CombiningKind::ADD)
897 return rewriter.notifyMatchFailure(op, "combining kind is not 'add'");
898
899 auto resultType = dyn_cast<IntegerType>(op.getType());
900 if (!resultType)
901 return rewriter.notifyMatchFailure(op, "result is not an integer");
902
903 int64_t resultBitwidth = resultType.getIntOrFloatBitWidth();
904 if (!llvm::is_contained({32, 64}, resultBitwidth))
905 return rewriter.notifyMatchFailure(op, "unsupported integer bitwidth");
906
907 VectorType inVecTy = op.getSourceVectorType();
908 if (!llvm::is_contained({4, 3}, inVecTy.getNumElements()) ||
909 inVecTy.getShape().size() != 1 || inVecTy.isScalable())
910 return rewriter.notifyMatchFailure(op, "unsupported vector shape");
911
912 auto mul = op.getVector().getDefiningOp<arith::MulIOp>();
913 if (!mul)
914 return rewriter.notifyMatchFailure(
915 op, "reduction operand is not 'arith.muli'");
916
917 if (succeeded(handleCase<arith::ExtSIOp, arith::ExtSIOp, spirv::SDotOp,
918 spirv::SDotAccSatOp, false>(op, mul, rewriter)))
919 return success();
920
921 if (succeeded(handleCase<arith::ExtUIOp, arith::ExtUIOp, spirv::UDotOp,
922 spirv::UDotAccSatOp, false>(op, mul, rewriter)))
923 return success();
924
925 if (succeeded(handleCase<arith::ExtSIOp, arith::ExtUIOp, spirv::SUDotOp,
926 spirv::SUDotAccSatOp, false>(op, mul, rewriter)))
927 return success();
928
929 if (succeeded(handleCase<arith::ExtUIOp, arith::ExtSIOp, spirv::SUDotOp,
930 spirv::SUDotAccSatOp, true>(op, mul, rewriter)))
931 return success();
932
933 return failure();
934 }
935
936private:
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>();
942 if (!lhs)
943 return failure();
944 Value lhsIn = lhs.getIn();
945 auto lhsInType = cast<VectorType>(lhsIn.getType());
946 if (!lhsInType.getElementType().isInteger(8))
947 return failure();
948
949 auto rhs = mul.getRhs().getDefiningOp<RhsExtensionOp>();
950 if (!rhs)
951 return failure();
952 Value rhsIn = rhs.getIn();
953 auto rhsInType = cast<VectorType>(rhsIn.getType());
954 if (!rhsInType.getElementType().isInteger(8))
955 return failure();
956
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,
963 ValueRange{lhsIn, zero});
964 rhsIn = spirv::CompositeConstructOp::create(rewriter, loc, v4i8Type,
965 ValueRange{rhsIn, zero});
966 }
967
968 // There's no variant of dot prod ops for unsigned LHS and signed RHS, so
969 // we have to swap operands instead in that case.
970 if (SwapOperands)
971 std::swap(lhsIn, rhsIn);
972
973 if (Value acc = op.getAcc()) {
974 rewriter.replaceOpWithNewOp<DotAccOp>(op, op.getType(), lhsIn, rhsIn, acc,
975 nullptr);
976 } else {
977 rewriter.replaceOpWithNewOp<DotOp>(op, op.getType(), lhsIn, rhsIn,
978 nullptr);
979 }
980
981 return success();
982 }
983};
984
985struct VectorReductionToFPDotProd final
986 : OpConversionPattern<vector::ReductionOp> {
987 using Base::Base;
988
989 LogicalResult
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'");
994
995 auto resultType = getTypeConverter()->convertType<FloatType>(op.getType());
996 if (!resultType)
997 return rewriter.notifyMatchFailure(op, "result is not a float");
998
999 Value vec = adaptor.getVector();
1000 Value acc = adaptor.getAcc();
1001
1002 auto vectorType = dyn_cast<VectorType>(vec.getType());
1003 if (!vectorType) {
1004 assert(isa<FloatType>(vec.getType()) &&
1005 "Expected the vector to be scalarized");
1006 if (acc) {
1007 rewriter.replaceOpWithNewOp<spirv::FAddOp>(op, acc, vec);
1008 return success();
1009 }
1010
1011 rewriter.replaceOp(op, vec);
1012 return success();
1013 }
1014
1015 Location loc = op.getLoc();
1016 Value lhs;
1017 Value rhs;
1018 if (auto mul = vec.getDefiningOp<arith::MulFOp>()) {
1019 lhs = mul.getLhs();
1020 rhs = mul.getRhs();
1021 } else {
1022 // If the operand is not a mul, use a vector of ones for the dot operand
1023 // to just sum up all values.
1024 lhs = vec;
1025 Attribute oneAttr =
1026 rewriter.getFloatAttr(vectorType.getElementType(), 1.0);
1027 oneAttr = SplatElementsAttr::get(vectorType, oneAttr);
1028 rhs = spirv::ConstantOp::create(rewriter, loc, vectorType, oneAttr);
1029 }
1030 assert(lhs);
1031 assert(rhs);
1032
1033 Value res = spirv::DotOp::create(rewriter, loc, resultType, lhs, rhs);
1034 if (acc)
1035 res = spirv::FAddOp::create(rewriter, loc, acc, res);
1036
1037 rewriter.replaceOp(op, res);
1038 return success();
1039 }
1040};
1041
1042struct VectorStepOpConvert final : OpConversionPattern<vector::StepOp> {
1043 using Base::Base;
1044
1045 LogicalResult
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());
1050 if (!dstType)
1051 return failure();
1052
1053 Location loc = stepOp.getLoc();
1054 int64_t numElements = stepOp.getType().getNumElements();
1055 // Use the element type; the type converter collapses size-1 vectors to
1056 // scalars, so `dstType` may already be the scalar element type.
1057 Type intType = isa<VectorType>(dstType)
1058 ? cast<VectorType>(dstType).getElementType()
1059 : dstType;
1060
1061 // Input vectors of size 1 are converted to scalars by the type converter.
1062 // We just create a constant in this case.
1063 if (numElements == 1) {
1064 Value zero = spirv::ConstantOp::getZero(intType, loc, rewriter);
1065 rewriter.replaceOp(stepOp, zero);
1066 return success();
1067 }
1068
1069 SmallVector<Value> source;
1070 source.reserve(numElements);
1071 for (int64_t i = 0; i < numElements; ++i) {
1072 Attribute intAttr = rewriter.getIntegerAttr(intType, i);
1073 Value constOp =
1074 spirv::ConstantOp::create(rewriter, loc, intType, intAttr);
1075 source.push_back(constOp);
1076 }
1077 rewriter.replaceOpWithNewOp<spirv::CompositeConstructOp>(stepOp, dstType,
1078 source);
1079 return success();
1080 }
1081};
1082
1083struct VectorToElementOpConvert final
1084 : OpConversionPattern<vector::ToElementsOp> {
1085 using Base::Base;
1086
1087 LogicalResult
1088 matchAndRewrite(vector::ToElementsOp toElementsOp, OpAdaptor adaptor,
1089 ConversionPatternRewriter &rewriter) const override {
1090
1091 SmallVector<Value> results(toElementsOp->getNumResults());
1092 Location loc = toElementsOp.getLoc();
1093
1094 // Input vectors of size 1 are converted to scalars by the type converter.
1095 // We cannot use `spirv::CompositeExtractOp` directly in this case.
1096 // For a scalar source, the result is just the scalar itself.
1097 if (isa<spirv::ScalarType>(adaptor.getSource().getType())) {
1098 results[0] = adaptor.getSource();
1099 rewriter.replaceOp(toElementsOp, results);
1100 return success();
1101 }
1102
1103 Type srcElementType = toElementsOp.getElements().getType().front();
1104 Type elementType = getTypeConverter()->convertType(srcElementType);
1105 if (!elementType)
1106 return rewriter.notifyMatchFailure(
1107 toElementsOp,
1108 llvm::formatv("failed to convert element type '{0}' to SPIR-V",
1109 srcElementType));
1110
1111 for (auto [idx, element] : llvm::enumerate(toElementsOp.getElements())) {
1112 // Create an CompositeExtract operation only for results that are not
1113 // dead.
1114 if (element.use_empty())
1115 continue;
1116
1117 Value result = spirv::CompositeExtractOp::create(
1118 rewriter, loc, elementType, adaptor.getSource(),
1119 rewriter.getI32ArrayAttr({static_cast<int32_t>(idx)}));
1120 results[idx] = result;
1121 }
1122
1123 rewriter.replaceOp(toElementsOp, results);
1124 return success();
1125 }
1126};
1127
1128} // namespace
1129#define CL_INT_MAX_MIN_OPS \
1130 spirv::CLUMaxOp, spirv::CLUMinOp, spirv::CLSMaxOp, spirv::CLSMinOp
1131
1132#define GL_INT_MAX_MIN_OPS \
1133 spirv::GLUMaxOp, spirv::GLUMinOp, spirv::GLSMaxOp, spirv::GLSMinOp
1134
1135#define CL_FLOAT_MAX_MIN_OPS spirv::CLFMaxOp, spirv::CLFMinOp
1136#define GL_FLOAT_MAX_MIN_OPS spirv::GLFMaxOp, spirv::GLFMinOp
1137
1139 const SPIRVTypeConverter &typeConverter, RewritePatternSet &patterns) {
1140 patterns.add<
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>(
1153 typeConverter, patterns.getContext(), PatternBenefit(1));
1154
1155 // Make sure that the more specialized dot product pattern has higher benefit
1156 // than the generic one that extracts all elements.
1157 patterns.add<VectorReductionToFPDotProd>(typeConverter, patterns.getContext(),
1158 PatternBenefit(2));
1159}
1160
1162 RewritePatternSet &patterns) {
1163 patterns.add<VectorReductionToIntDotProd>(patterns.getContext());
1164}
return success()
lhs
ArrayAttr()
static constexpr unsigned getNumBits()
#define MINUI(lhs, rhs)
#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...
#define mul(a, b)
IntegerType getI8Type()
Definition Builders.cpp:67
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:64
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...
Definition Types.h:74
bool isInteger() const
Return true if this is an integer type (with the specified width).
Definition Types.cpp:58
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
Definition Types.cpp:124
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
MLIRContext * getContext() const
Utility to get the associated MLIRContext that this value is defined in.
Definition Value.h:108
Type getType() const
Return the type of this value.
Definition Value.h:105
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
static PointerType get(Type pointeeType, StorageClass storageClass)
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:734
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...