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