MLIR 24.0.0git
BuiltinTypes.cpp
Go to the documentation of this file.
1//===- BuiltinTypes.cpp - MLIR Builtin Type Classes -----------------------===//
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
10#include "TypeDetail.h"
12#include "mlir/IR/AffineExpr.h"
13#include "mlir/IR/AffineMap.h"
17#include "mlir/IR/Diagnostics.h"
18#include "mlir/IR/Dialect.h"
21#include "llvm/ADT/APFixedPoint.h"
22#include "llvm/ADT/APFloat.h"
23#include "llvm/ADT/APInt.h"
24#include "llvm/ADT/Sequence.h"
25#include "llvm/ADT/TypeSwitch.h"
26#include "llvm/Support/CheckedArithmetic.h"
27#include <cstring>
28
29using namespace mlir;
30using namespace mlir::detail;
31
32//===----------------------------------------------------------------------===//
33/// Tablegen Type Definitions
34//===----------------------------------------------------------------------===//
35
36#define GET_TYPEDEF_CLASSES
37#include "mlir/IR/BuiltinTypes.cpp.inc"
38
39namespace mlir {
40#include "mlir/IR/BuiltinTypeConstraints.cpp.inc"
41} // namespace mlir
42
43//===----------------------------------------------------------------------===//
44// BuiltinDialect
45//===----------------------------------------------------------------------===//
46
47void BuiltinDialect::registerTypes() {
48 addTypes<
49#define GET_TYPEDEF_LIST
50#include "mlir/IR/BuiltinTypes.cpp.inc"
51 >();
52}
53
54//===----------------------------------------------------------------------===//
55/// ComplexType
56//===----------------------------------------------------------------------===//
57
58/// Verify the construction of an integer type.
59LogicalResult ComplexType::verify(function_ref<InFlightDiagnostic()> emitError,
60 Type elementType) {
61 if (!elementType.isIntOrFloat())
62 return emitError() << "invalid element type for complex";
63 return success();
64}
65
66size_t ComplexType::getDenseElementBitSize() const {
67 auto elemTy = cast<DenseElementType>(getElementType());
68 return llvm::alignTo<8>(elemTy.getDenseElementBitSize()) * 2;
69}
70
71Attribute ComplexType::convertToAttribute(ArrayRef<char> rawData) const {
72 auto elemTy = cast<DenseElementType>(getElementType());
73 size_t singleElementBytes =
74 llvm::alignTo<8>(elemTy.getDenseElementBitSize()) / 8;
76 elemTy.convertToAttribute(rawData.take_front(singleElementBytes));
78 elemTy.convertToAttribute(rawData.take_back(singleElementBytes));
79 return ArrayAttr::get(getContext(), {real, imag});
80}
81
82LogicalResult
83ComplexType::convertFromAttribute(Attribute attr,
85 auto arrayAttr = dyn_cast<ArrayAttr>(attr);
86 if (!arrayAttr || arrayAttr.size() != 2)
87 return failure();
88 auto elemTy = cast<DenseElementType>(getElementType());
89 SmallVector<char> realData, imagData;
90 if (failed(elemTy.convertFromAttribute(arrayAttr[0], realData)))
91 return failure();
92 if (failed(elemTy.convertFromAttribute(arrayAttr[1], imagData)))
93 return failure();
94 result.append(realData);
95 result.append(imagData);
96 return success();
97}
98
99//===----------------------------------------------------------------------===//
100// Integer Type
101//===----------------------------------------------------------------------===//
102
103/// Verify the construction of an integer type.
104LogicalResult IntegerType::verify(function_ref<InFlightDiagnostic()> emitError,
105 unsigned width,
106 SignednessSemantics signedness) {
107 if (width > IntegerType::kMaxWidth) {
108 return emitError() << "integer bitwidth is limited to "
109 << IntegerType::kMaxWidth << " bits";
110 }
111 return success();
112}
113
114unsigned IntegerType::getWidth() const { return getImpl()->width; }
115
116IntegerType::SignednessSemantics IntegerType::getSignedness() const {
117 return getImpl()->signedness;
118}
119
120IntegerType IntegerType::scaleElementBitwidth(unsigned scale) {
121 if (!scale)
122 return IntegerType();
123 return IntegerType::get(getContext(), scale * getWidth(), getSignedness());
124}
125
126size_t IntegerType::getDenseElementBitSize() const {
127 // Return the actual bit width. Storage alignment is handled separately.
128 return getWidth();
129}
130
131Attribute IntegerType::convertToAttribute(ArrayRef<char> rawData) const {
132 APInt value = detail::readBits(rawData.data(), /*bitPos=*/0, getWidth());
133 return IntegerAttr::get(*this, value);
134}
135
137 size_t byteSize = llvm::divideCeil(apInt.getBitWidth(), CHAR_BIT);
138 size_t bitPos = result.size() * CHAR_BIT;
139 result.resize(result.size() + byteSize);
140 detail::writeBits(result.data(), bitPos, apInt);
141}
142
143LogicalResult
144IntegerType::convertFromAttribute(Attribute attr,
146 auto intAttr = dyn_cast<IntegerAttr>(attr);
147 if (!intAttr || intAttr.getType() != *this)
148 return failure();
149 writeAPIntToVector(intAttr.getValue(), result);
150 return success();
151}
152
153//===----------------------------------------------------------------------===//
154// MXI8 Type
155//===----------------------------------------------------------------------===//
156
157llvm::FixedPointSemantics MXI8Type::getFixedPointSemantics() {
158 // Saturation is selected by a conversion operation, not by the value type.
159 return {/*Width=*/8, /*Scale=*/6, /*IsSigned=*/true,
160 /*IsSaturated=*/false, /*HasUnsignedPadding=*/false};
161}
162
163size_t MXI8Type::getDenseElementBitSize() const { return 8; }
164
165Attribute MXI8Type::convertToAttribute(ArrayRef<char> rawData) const {
166 return IntegerType::get(getContext(), 8).convertToAttribute(rawData);
167}
168
169LogicalResult
170MXI8Type::convertFromAttribute(Attribute attr,
172 auto intAttr = dyn_cast<IntegerAttr>(attr);
173 if (!intAttr || !intAttr.getType().isSignlessInteger(8))
174 return failure();
175 return cast<IntegerType>(intAttr.getType())
176 .convertFromAttribute(attr, result);
177}
178
179//===----------------------------------------------------------------------===//
180// Index Type
181//===----------------------------------------------------------------------===//
182
183size_t IndexType::getDenseElementBitSize() const {
184 return kInternalStorageBitWidth;
185}
186
187Attribute IndexType::convertToAttribute(ArrayRef<char> rawData) const {
188 APInt value =
189 detail::readBits(rawData.data(), /*bitPos=*/0, kInternalStorageBitWidth);
190 return IntegerAttr::get(*this, value);
191}
192
193LogicalResult
194IndexType::convertFromAttribute(Attribute attr,
196 auto intAttr = dyn_cast<IntegerAttr>(attr);
197 if (!intAttr || intAttr.getType() != *this)
198 return failure();
199 writeAPIntToVector(intAttr.getValue(), result);
200 return success();
201}
202
203//===----------------------------------------------------------------------===//
204// Float Types
205//===----------------------------------------------------------------------===//
206
207// Mapping from MLIR FloatType to APFloat semantics.
208#define FLOAT_TYPE_SEMANTICS(TYPE, SEM) \
209 const llvm::fltSemantics &TYPE::getFloatSemantics() const { \
210 return APFloat::SEM(); \
211 }
212FLOAT_TYPE_SEMANTICS(Float4E2M1FNType, Float4E2M1FN)
213FLOAT_TYPE_SEMANTICS(Float6E2M3FNType, Float6E2M3FN)
214FLOAT_TYPE_SEMANTICS(Float6E3M2FNType, Float6E3M2FN)
215FLOAT_TYPE_SEMANTICS(Float8E5M2Type, Float8E5M2)
216FLOAT_TYPE_SEMANTICS(Float8E4M3Type, Float8E4M3)
217FLOAT_TYPE_SEMANTICS(Float8E4M3FNType, Float8E4M3FN)
218FLOAT_TYPE_SEMANTICS(Float8E5M2FNUZType, Float8E5M2FNUZ)
219FLOAT_TYPE_SEMANTICS(Float8E4M3FNUZType, Float8E4M3FNUZ)
220FLOAT_TYPE_SEMANTICS(Float8E4M3B11FNUZType, Float8E4M3B11FNUZ)
221FLOAT_TYPE_SEMANTICS(Float8E3M4Type, Float8E3M4)
222FLOAT_TYPE_SEMANTICS(Float8E8M0FNUType, Float8E8M0FNU)
223FLOAT_TYPE_SEMANTICS(Float8E5M3FNUType, Float8E5M3FNU)
224FLOAT_TYPE_SEMANTICS(BFloat16Type, BFloat)
225FLOAT_TYPE_SEMANTICS(Float16Type, IEEEhalf)
226FLOAT_TYPE_SEMANTICS(FloatTF32Type, FloatTF32)
227FLOAT_TYPE_SEMANTICS(Float32Type, IEEEsingle)
228FLOAT_TYPE_SEMANTICS(Float64Type, IEEEdouble)
229FLOAT_TYPE_SEMANTICS(Float80Type, x87DoubleExtended)
230FLOAT_TYPE_SEMANTICS(Float128Type, IEEEquad)
231#undef FLOAT_TYPE_SEMANTICS
232
233FloatType Float16Type::scaleElementBitwidth(unsigned scale) const {
234 if (scale == 2)
235 return Float32Type::get(getContext());
236 if (scale == 4)
237 return Float64Type::get(getContext());
238 return FloatType();
239}
240
241FloatType BFloat16Type::scaleElementBitwidth(unsigned scale) const {
242 if (scale == 2)
243 return Float32Type::get(getContext());
244 if (scale == 4)
245 return Float64Type::get(getContext());
246 return FloatType();
247}
248
249FloatType Float32Type::scaleElementBitwidth(unsigned scale) const {
250 if (scale == 2)
251 return Float64Type::get(getContext());
252 return FloatType();
253}
254
255//===----------------------------------------------------------------------===//
256// FunctionType
257//===----------------------------------------------------------------------===//
258
259unsigned FunctionType::getNumInputs() const { return getImpl()->numInputs; }
260
261ArrayRef<Type> FunctionType::getInputs() const {
262 return getImpl()->getInputs();
263}
264
265unsigned FunctionType::getNumResults() const { return getImpl()->numResults; }
266
267ArrayRef<Type> FunctionType::getResults() const {
268 return getImpl()->getResults();
269}
270
271FunctionType FunctionType::clone(TypeRange inputs, TypeRange results) const {
272 return get(getContext(), inputs, results);
273}
274
275/// Returns a new function type with the specified arguments and results
276/// inserted.
277FunctionType FunctionType::getWithArgsAndResults(
278 ArrayRef<unsigned> argIndices, TypeRange argTypes,
279 ArrayRef<unsigned> resultIndices, TypeRange resultTypes) {
280 SmallVector<Type> argStorage, resultStorage;
281 TypeRange newArgTypes =
282 insertTypesInto(getInputs(), argIndices, argTypes, argStorage);
283 TypeRange newResultTypes =
284 insertTypesInto(getResults(), resultIndices, resultTypes, resultStorage);
285 return clone(newArgTypes, newResultTypes);
286}
287
288/// Returns a new function type without the specified arguments and results.
289FunctionType
290FunctionType::getWithoutArgsAndResults(const BitVector &argIndices,
291 const BitVector &resultIndices) {
292 SmallVector<Type> argStorage, resultStorage;
293 TypeRange newArgTypes = filterTypesOut(getInputs(), argIndices, argStorage);
294 TypeRange newResultTypes =
295 filterTypesOut(getResults(), resultIndices, resultStorage);
296 return clone(newArgTypes, newResultTypes);
297}
298
299//===----------------------------------------------------------------------===//
300// GraphType
301//===----------------------------------------------------------------------===//
302
303unsigned GraphType::getNumInputs() const { return getImpl()->numInputs; }
304
305ArrayRef<Type> GraphType::getInputs() const { return getImpl()->getInputs(); }
306
307unsigned GraphType::getNumResults() const { return getImpl()->numResults; }
308
309ArrayRef<Type> GraphType::getResults() const { return getImpl()->getResults(); }
310
311GraphType GraphType::clone(TypeRange inputs, TypeRange results) const {
312 return get(getContext(), inputs, results);
313}
314
315/// Returns a new function type with the specified arguments and results
316/// inserted.
317GraphType GraphType::getWithArgsAndResults(ArrayRef<unsigned> argIndices,
318 TypeRange argTypes,
319 ArrayRef<unsigned> resultIndices,
320 TypeRange resultTypes) {
321 SmallVector<Type> argStorage, resultStorage;
322 TypeRange newArgTypes =
323 insertTypesInto(getInputs(), argIndices, argTypes, argStorage);
324 TypeRange newResultTypes =
325 insertTypesInto(getResults(), resultIndices, resultTypes, resultStorage);
326 return clone(newArgTypes, newResultTypes);
327}
328
329/// Returns a new function type without the specified arguments and results.
330GraphType GraphType::getWithoutArgsAndResults(const BitVector &argIndices,
331 const BitVector &resultIndices) {
332 SmallVector<Type> argStorage, resultStorage;
333 TypeRange newArgTypes = filterTypesOut(getInputs(), argIndices, argStorage);
334 TypeRange newResultTypes =
335 filterTypesOut(getResults(), resultIndices, resultStorage);
336 return clone(newArgTypes, newResultTypes);
337}
338//===----------------------------------------------------------------------===//
339// OpaqueType
340//===----------------------------------------------------------------------===//
341
342/// Verify the construction of an opaque type.
343LogicalResult OpaqueType::verify(function_ref<InFlightDiagnostic()> emitError,
344 StringAttr dialect, StringRef typeData) {
345 if (!Dialect::isValidNamespace(dialect.strref()))
346 return emitError() << "invalid dialect namespace '" << dialect << "'";
347
348 // Check that the dialect is actually registered.
349 MLIRContext *context = dialect.getContext();
350 if (!context->allowsUnregisteredDialects() &&
351 !context->getLoadedDialect(dialect.strref())) {
352 return emitError()
353 << "`!" << dialect << "<\"" << typeData << "\">"
354 << "` type created with unregistered dialect. If this is "
355 "intended, please call allowUnregisteredDialects() on the "
356 "MLIRContext, or use -allow-unregistered-dialect with "
357 "the MLIR opt tool used";
358 }
359
360 return success();
361}
362
363//===----------------------------------------------------------------------===//
364// VectorType
365//===----------------------------------------------------------------------===//
366
367bool VectorType::isValidElementType(Type t) {
369}
370
371LogicalResult VectorType::verify(function_ref<InFlightDiagnostic()> emitError,
372 ArrayRef<int64_t> shape, Type elementType,
373 ArrayRef<bool> scalableDims) {
374 if (!isValidElementType(elementType))
375 return emitError()
376 << "vector elements must be int/index/float type but got "
377 << elementType;
378
379 if (any_of(shape, [](int64_t i) { return i <= 0; }))
380 return emitError()
381 << "vector types must have positive constant sizes but got "
382 << shape;
383
384 if (scalableDims.size() != shape.size())
385 return emitError() << "number of dims must match, got "
386 << scalableDims.size() << " and " << shape.size();
387
388 return success();
389}
390
391VectorType VectorType::scaleElementBitwidth(unsigned scale) {
392 if (!scale)
393 return VectorType();
394 if (auto et = llvm::dyn_cast<IntegerType>(getElementType()))
395 if (auto scaledEt = et.scaleElementBitwidth(scale))
396 return VectorType::get(getShape(), scaledEt, getScalableDims());
397 if (auto et = llvm::dyn_cast<FloatType>(getElementType()))
398 if (auto scaledEt = et.scaleElementBitwidth(scale))
399 return VectorType::get(getShape(), scaledEt, getScalableDims());
400 return VectorType();
401}
402
403VectorType VectorType::cloneWith(std::optional<ArrayRef<int64_t>> shape,
404 Type elementType) const {
405 return VectorType::get(shape.value_or(getShape()), elementType,
406 getScalableDims());
407}
408
409//===----------------------------------------------------------------------===//
410// TensorType
411//===----------------------------------------------------------------------===//
412
415 .Case<RankedTensorType, UnrankedTensorType>(
416 [](auto type) { return type.getElementType(); });
417}
418
420 return !llvm::isa<UnrankedTensorType>(*this);
421}
422
424 return llvm::cast<RankedTensorType>(*this).getShape();
425}
426
428 Type elementType) const {
429 if (llvm::dyn_cast<UnrankedTensorType>(*this)) {
430 if (shape)
431 return RankedTensorType::get(*shape, elementType);
432 return UnrankedTensorType::get(elementType);
433 }
434
435 auto rankedTy = llvm::cast<RankedTensorType>(*this);
436 if (!shape)
437 return RankedTensorType::get(rankedTy.getShape(), elementType,
438 rankedTy.getEncoding());
439 return RankedTensorType::get(shape.value_or(rankedTy.getShape()), elementType,
440 rankedTy.getEncoding());
441}
442
444 Type elementType) const {
445 return ::llvm::cast<RankedTensorType>(cloneWith(shape, elementType));
446}
447
448RankedTensorType TensorType::clone(::llvm::ArrayRef<int64_t> shape) const {
449 return ::llvm::cast<RankedTensorType>(cloneWith(shape, getElementType()));
450}
451
452// Check if "elementType" can be an element type of a tensor.
453static LogicalResult
455 Type elementType) {
456 if (!TensorType::isValidElementType(elementType))
457 return emitError() << "invalid tensor element type: " << elementType;
458 return success();
459}
460
461/// Return true if the specified element type is ok in a tensor.
463 // Note: Non standard/builtin types are allowed to exist within tensor
464 // types. Dialects are expected to verify that tensor types have a valid
465 // element type within that dialect.
466 return llvm::isa<ComplexType, FloatType, IntegerType, OpaqueType, VectorType,
467 IndexType, MXI8Type>(type) ||
468 !llvm::isa<BuiltinDialect>(type.getDialect());
469}
470
471//===----------------------------------------------------------------------===//
472// RankedTensorType
473//===----------------------------------------------------------------------===//
474
475LogicalResult
476RankedTensorType::verify(function_ref<InFlightDiagnostic()> emitError,
477 ArrayRef<int64_t> shape, Type elementType,
478 Attribute encoding) {
479 for (int64_t s : shape)
480 if (s < 0 && ShapedType::isStatic(s))
481 return emitError() << "invalid tensor dimension size";
482 if (auto v = llvm::dyn_cast_or_null<VerifiableTensorEncoding>(encoding))
483 if (failed(v.verifyEncoding(shape, elementType, emitError)))
484 return failure();
485 return checkTensorElementType(emitError, elementType);
486}
487
488//===----------------------------------------------------------------------===//
489// UnrankedTensorType
490//===----------------------------------------------------------------------===//
491
492LogicalResult
493UnrankedTensorType::verify(function_ref<InFlightDiagnostic()> emitError,
494 Type elementType) {
495 return checkTensorElementType(emitError, elementType);
496}
497
498//===----------------------------------------------------------------------===//
499// BaseMemRefType
500//===----------------------------------------------------------------------===//
501
504 .Case<MemRefType, UnrankedMemRefType>(
505 [](auto type) { return type.getElementType(); });
506}
507
509 return !llvm::isa<UnrankedMemRefType>(*this);
510}
511
513 return llvm::cast<MemRefType>(*this).getShape();
514}
515
517 Type elementType) const {
518 if (llvm::dyn_cast<UnrankedMemRefType>(*this)) {
519 if (!shape)
520 return UnrankedMemRefType::get(elementType, getMemorySpace());
521 MemRefType::Builder builder(*shape, elementType);
523 return builder;
524 }
525
526 MemRefType::Builder builder(llvm::cast<MemRefType>(*this));
527 if (shape)
528 builder.setShape(*shape);
529 builder.setElementType(elementType);
530 return builder;
531}
532
533FailureOr<PtrLikeTypeInterface>
535 std::optional<Type> elementType) const {
536 Type eTy = elementType ? *elementType : getElementType();
537 if (llvm::dyn_cast<UnrankedMemRefType>(*this))
538 return cast<PtrLikeTypeInterface>(
539 UnrankedMemRefType::get(eTy, memorySpace));
540
541 MemRefType::Builder builder(llvm::cast<MemRefType>(*this));
542 builder.setElementType(eTy);
543 builder.setMemorySpace(memorySpace);
544 return cast<PtrLikeTypeInterface>(static_cast<MemRefType>(builder));
545}
546
548 Type elementType) const {
549 return ::llvm::cast<MemRefType>(cloneWith(shape, elementType));
550}
551
553 return ::llvm::cast<MemRefType>(cloneWith(shape, getElementType()));
554}
555
557 if (auto rankedMemRefTy = llvm::dyn_cast<MemRefType>(*this))
558 return rankedMemRefTy.getMemorySpace();
559 return llvm::cast<UnrankedMemRefType>(*this).getMemorySpace();
560}
561
563 if (auto rankedMemRefTy = llvm::dyn_cast<MemRefType>(*this))
564 return rankedMemRefTy.getMemorySpaceAsInt();
565 return llvm::cast<UnrankedMemRefType>(*this).getMemorySpaceAsInt();
566}
567
568//===----------------------------------------------------------------------===//
569// MemRefType
570//===----------------------------------------------------------------------===//
571
572std::optional<llvm::SmallDenseSet<unsigned>>
574 ArrayRef<int64_t> reducedShape,
575 bool matchDynamic) {
576 size_t originalRank = originalShape.size(), reducedRank = reducedShape.size();
577 llvm::SmallDenseSet<unsigned> unusedDims;
578 unsigned reducedIdx = 0;
579 for (unsigned originalIdx = 0; originalIdx < originalRank; ++originalIdx) {
580 // Greedily insert `originalIdx` if match.
581 int64_t origSize = originalShape[originalIdx];
582 // if `matchDynamic`, count dynamic dims as a match, unless `origSize` is 1.
583 if (matchDynamic && reducedIdx < reducedRank && origSize != 1 &&
584 (ShapedType::isDynamic(reducedShape[reducedIdx]) ||
585 ShapedType::isDynamic(origSize))) {
586 reducedIdx++;
587 continue;
588 }
589 if (reducedIdx < reducedRank && origSize == reducedShape[reducedIdx]) {
590 reducedIdx++;
591 continue;
592 }
593
594 unusedDims.insert(originalIdx);
595 // If no match on `originalIdx`, the `originalShape` at this dimension
596 // must be 1, otherwise we bail.
597 if (origSize != 1)
598 return std::nullopt;
599 }
600 // The whole reducedShape must be scanned, otherwise we bail.
601 if (reducedIdx != reducedRank)
602 return std::nullopt;
603 return unusedDims;
604}
605
607mlir::isRankReducedType(ShapedType originalType,
608 ShapedType candidateReducedType) {
609 if (originalType == candidateReducedType)
611
612 ShapedType originalShapedType = llvm::cast<ShapedType>(originalType);
613 ShapedType candidateReducedShapedType =
614 llvm::cast<ShapedType>(candidateReducedType);
615
616 // Rank and size logic is valid for all ShapedTypes.
617 ArrayRef<int64_t> originalShape = originalShapedType.getShape();
618 ArrayRef<int64_t> candidateReducedShape =
619 candidateReducedShapedType.getShape();
620 unsigned originalRank = originalShape.size(),
621 candidateReducedRank = candidateReducedShape.size();
622 if (candidateReducedRank > originalRank)
624
625 auto optionalUnusedDimsMask =
626 computeRankReductionMask(originalShape, candidateReducedShape);
627
628 // Sizes cannot be matched in case empty vector is returned.
629 if (!optionalUnusedDimsMask)
631
632 if (originalShapedType.getElementType() !=
633 candidateReducedShapedType.getElementType())
635
637}
638
640 MLIRContext *ctx) {
641 if (memorySpace == 0)
642 return nullptr;
643
644 return IntegerAttr::get(IntegerType::get(ctx, 64), memorySpace);
645}
646
648 IntegerAttr intMemorySpace = llvm::dyn_cast_or_null<IntegerAttr>(memorySpace);
649 if (intMemorySpace && intMemorySpace.getValue() == 0)
650 return nullptr;
651
652 return memorySpace;
653}
654
656 if (!memorySpace)
657 return 0;
658
659 assert(llvm::isa<IntegerAttr>(memorySpace) &&
660 "Using `getMemorySpaceInteger` with non-Integer attribute");
661
662 return static_cast<unsigned>(llvm::cast<IntegerAttr>(memorySpace).getInt());
663}
664
665unsigned MemRefType::getMemorySpaceAsInt() const {
666 return detail::getMemorySpaceAsInt(getMemorySpace());
667}
668
669MemRefType MemRefType::get(ArrayRef<int64_t> shape, Type elementType,
670 MemRefLayoutAttrInterface layout,
671 Attribute memorySpace) {
672 // Use default layout for empty attribute.
673 if (!layout)
674 layout = AffineMapAttr::get(AffineMap::getMultiDimIdentityMap(
675 shape.size(), elementType.getContext()));
676
677 // Drop default memory space value and replace it with empty attribute.
678 memorySpace = skipDefaultMemorySpace(memorySpace);
679
680 return Base::get(elementType.getContext(), shape, elementType, layout,
681 memorySpace);
682}
683
684MemRefType MemRefType::getChecked(
686 Type elementType, MemRefLayoutAttrInterface layout, Attribute memorySpace) {
687
688 // Use default layout for empty attribute.
689 if (!layout)
690 layout = AffineMapAttr::get(AffineMap::getMultiDimIdentityMap(
691 shape.size(), elementType.getContext()));
692
693 // Drop default memory space value and replace it with empty attribute.
694 memorySpace = skipDefaultMemorySpace(memorySpace);
695
696 return Base::getChecked(emitErrorFn, elementType.getContext(), shape,
697 elementType, layout, memorySpace);
698}
699
700MemRefType MemRefType::get(ArrayRef<int64_t> shape, Type elementType,
701 AffineMap map, Attribute memorySpace) {
702
703 // Use default layout for empty map.
704 if (!map)
706 elementType.getContext());
707
708 // Wrap AffineMap into Attribute.
709 auto layout = AffineMapAttr::get(map);
710
711 // Drop default memory space value and replace it with empty attribute.
712 memorySpace = skipDefaultMemorySpace(memorySpace);
713
714 return Base::get(elementType.getContext(), shape, elementType, layout,
715 memorySpace);
716}
717
718MemRefType
719MemRefType::getChecked(function_ref<InFlightDiagnostic()> emitErrorFn,
720 ArrayRef<int64_t> shape, Type elementType, AffineMap map,
721 Attribute memorySpace) {
722
723 // Use default layout for empty map.
724 if (!map)
726 elementType.getContext());
727
728 // Wrap AffineMap into Attribute.
729 auto layout = AffineMapAttr::get(map);
730
731 // Drop default memory space value and replace it with empty attribute.
732 memorySpace = skipDefaultMemorySpace(memorySpace);
733
734 return Base::getChecked(emitErrorFn, elementType.getContext(), shape,
735 elementType, layout, memorySpace);
736}
737
738MemRefType MemRefType::get(ArrayRef<int64_t> shape, Type elementType,
739 AffineMap map, unsigned memorySpaceInd) {
740
741 // Use default layout for empty map.
742 if (!map)
744 elementType.getContext());
745
746 // Wrap AffineMap into Attribute.
747 auto layout = AffineMapAttr::get(map);
748
749 // Convert deprecated integer-like memory space to Attribute.
750 Attribute memorySpace =
751 wrapIntegerMemorySpace(memorySpaceInd, elementType.getContext());
752
753 return Base::get(elementType.getContext(), shape, elementType, layout,
754 memorySpace);
755}
756
757MemRefType
758MemRefType::getChecked(function_ref<InFlightDiagnostic()> emitErrorFn,
759 ArrayRef<int64_t> shape, Type elementType, AffineMap map,
760 unsigned memorySpaceInd) {
761
762 // Use default layout for empty map.
763 if (!map)
765 elementType.getContext());
766
767 // Wrap AffineMap into Attribute.
768 auto layout = AffineMapAttr::get(map);
769
770 // Convert deprecated integer-like memory space to Attribute.
771 Attribute memorySpace =
772 wrapIntegerMemorySpace(memorySpaceInd, elementType.getContext());
773
774 return Base::getChecked(emitErrorFn, elementType.getContext(), shape,
775 elementType, layout, memorySpace);
776}
777
778LogicalResult MemRefType::verify(function_ref<InFlightDiagnostic()> emitError,
779 ArrayRef<int64_t> shape, Type elementType,
780 MemRefLayoutAttrInterface layout,
781 Attribute memorySpace) {
782 if (!BaseMemRefType::isValidElementType(elementType))
783 return emitError() << "invalid memref element type";
784
785 // Negative sizes are not allowed except for `kDynamic`.
786 for (int64_t s : shape)
787 if (s < 0 && ShapedType::isStatic(s))
788 return emitError() << "invalid memref size";
789
790 assert(layout && "missing layout specification");
791 if (failed(layout.verifyLayout(shape, emitError)))
792 return failure();
793
794 return success();
795}
796
797bool MemRefType::areTrailingDimsContiguous(int64_t n) {
798 assert(n <= getRank() &&
799 "number of dimensions to check must not exceed rank");
800 return n <= getNumContiguousTrailingDims();
801}
802
803int64_t MemRefType::getNumContiguousTrailingDims() {
804 const int64_t n = getRank();
805
806 // memrefs with identity layout are entirely contiguous.
807 if (getLayout().isIdentity())
808 return n;
809
810 // Get the strides (if any). Failing to do that, conservatively assume a
811 // non-contiguous layout.
812 int64_t offset;
813 SmallVector<int64_t> strides;
814 if (!succeeded(getStridesAndOffset(strides, offset)))
815 return 0;
816
818
819 // A memref with dimensions `d0, d1, ..., dn-1` and strides
820 // `s0, s1, ..., sn-1` is contiguous up to dimension `k`
821 // if each stride `si` is the product of the dimensions `di+1, ..., dn-1`,
822 // for `i` in `[k, n-1]`.
823 // Ignore stride elements if the corresponding dimension is 1, as they are
824 // of no consequence.
825 int64_t dimProduct = 1;
826 for (int64_t i = n - 1; i >= 0; --i) {
827 if (shape[i] == 1)
828 continue;
829 if (strides[i] != dimProduct)
830 return n - i - 1;
831 if (shape[i] == ShapedType::kDynamic)
832 return n - i;
833 dimProduct *= shape[i];
834 }
835
836 return n;
837}
838
839MemRefType MemRefType::canonicalizeStridedLayout() {
840 AffineMap m = getLayout().getAffineMap();
841
842 // Already in canonical form.
843 if (m.isIdentity())
844 return *this;
845
846 // Can't reduce to canonical identity form, return in canonical form.
847 if (m.getNumResults() > 1)
848 return *this;
849
850 // Corner-case for 0-D affine maps.
851 if (m.getNumDims() == 0 && m.getNumSymbols() == 0) {
852 if (auto cst = llvm::dyn_cast<AffineConstantExpr>(m.getResult(0)))
853 if (cst.getValue() == 0)
854 return MemRefType::Builder(*this).setLayout({});
855 return *this;
856 }
857
858 // 0-D corner case for empty shape that still have an affine map. Example:
859 // `memref<f32, affine_map<()[s0] -> (s0)>>`. This is a 1 element memref whose
860 // offset needs to remain, just return t.
861 if (getShape().empty())
862 return *this;
863
864 // If the canonical strided layout for the sizes of `t` is equal to the
865 // simplified layout of `t` we can just return an empty layout. Otherwise,
866 // just simplify the existing layout.
868 auto simplifiedLayoutExpr =
870 if (expr != simplifiedLayoutExpr)
871 return MemRefType::Builder(*this).setLayout(
872 AffineMapAttr::get(AffineMap::get(m.getNumDims(), m.getNumSymbols(),
873 simplifiedLayoutExpr)));
874 return MemRefType::Builder(*this).setLayout({});
875}
876
877LogicalResult MemRefType::getStridesAndOffset(SmallVectorImpl<int64_t> &strides,
878 int64_t &offset) const {
879 return getLayout().getStridesAndOffset(getShape(), strides, offset);
880}
881
882std::pair<SmallVector<int64_t>, int64_t>
883MemRefType::getStridesAndOffset() const {
884 SmallVector<int64_t> strides;
885 int64_t offset;
886 LogicalResult status = getStridesAndOffset(strides, offset);
887 (void)status;
888 assert(succeeded(status) && "Invalid use of check-free getStridesAndOffset");
889 return {strides, offset};
890}
891
892bool MemRefType::isStrided() {
893 int64_t offset;
895 auto res = getStridesAndOffset(strides, offset);
896 return succeeded(res);
897}
898
899bool MemRefType::isLastDimUnitStride() {
900 int64_t offset;
901 SmallVector<int64_t> strides;
902 auto successStrides = getStridesAndOffset(strides, offset);
903 return succeeded(successStrides) && (strides.empty() || strides.back() == 1);
904}
905
906//===----------------------------------------------------------------------===//
907// UnrankedMemRefType
908//===----------------------------------------------------------------------===//
909
910unsigned UnrankedMemRefType::getMemorySpaceAsInt() const {
911 return detail::getMemorySpaceAsInt(getMemorySpace());
912}
913
914LogicalResult
915UnrankedMemRefType::verify(function_ref<InFlightDiagnostic()> emitError,
916 Type elementType, Attribute memorySpace) {
917 if (!BaseMemRefType::isValidElementType(elementType))
918 return emitError() << "invalid memref element type";
919
920 return success();
921}
922
923//===----------------------------------------------------------------------===//
924/// TupleType
925//===----------------------------------------------------------------------===//
926
927/// Return the elements types for this tuple.
928ArrayRef<Type> TupleType::getTypes() const { return getImpl()->getTypes(); }
929
930/// Accumulate the types contained in this tuple and tuples nested within it.
931/// Note that this only flattens nested tuples, not any other container type,
932/// e.g. a tuple<i32, tensor<i32>, tuple<f32, tuple<i64>>> is flattened to
933/// (i32, tensor<i32>, f32, i64)
934void TupleType::getFlattenedTypes(SmallVectorImpl<Type> &types) {
935 for (Type type : getTypes()) {
936 if (auto nestedTuple = llvm::dyn_cast<TupleType>(type))
937 nestedTuple.getFlattenedTypes(types);
938 else
939 types.push_back(type);
940 }
941}
942
943/// Return the number of element types.
944size_t TupleType::size() const { return getImpl()->size(); }
945
946//===----------------------------------------------------------------------===//
947// Type Utilities
948//===----------------------------------------------------------------------===//
949
952 MLIRContext *context) {
953 // Size 0 corner case is useful for canonicalizations.
954 if (sizes.empty())
955 return getAffineConstantExpr(0, context);
956
957 assert(!exprs.empty() && "expected exprs");
958 auto maps = AffineMap::inferFromExprList(exprs, context);
959 assert(!maps.empty() && "Expected one non-empty map");
960 unsigned numDims = maps[0].getNumDims(), nSymbols = maps[0].getNumSymbols();
961
962 AffineExpr expr;
963 bool dynamicPoisonBit = false;
964 int64_t runningSize = 1;
965 for (auto en : llvm::zip(llvm::reverse(exprs), llvm::reverse(sizes))) {
966 int64_t size = std::get<1>(en);
967 AffineExpr dimExpr = std::get<0>(en);
968 AffineExpr stride = dynamicPoisonBit
969 ? getAffineSymbolExpr(nSymbols++, context)
970 : getAffineConstantExpr(runningSize, context);
971 expr = expr ? expr + dimExpr * stride : dimExpr * stride;
972 if (size > 0) {
973 auto result = llvm::checkedMul(runningSize, size);
974 if (!result) {
975 // Overflow occurred, treat as dynamic
976 dynamicPoisonBit = true;
977 } else {
978 runningSize = *result;
979 }
980 } else {
981 dynamicPoisonBit = true;
982 }
983 }
984 return simplifyAffineExpr(expr, numDims, nSymbols);
985}
986
988 MLIRContext *context) {
990 exprs.reserve(sizes.size());
991 for (auto dim : llvm::seq<unsigned>(0, sizes.size()))
992 exprs.push_back(getAffineDimExpr(dim, context));
993 return makeCanonicalStridedLayoutExpr(sizes, exprs, context);
994}
return success()
static LogicalResult getStridesAndOffset(AffineMap m, ArrayRef< int64_t > shape, SmallVectorImpl< AffineExpr > &strides, AffineExpr &offset)
A stride specification is a list of integer values that are either static or dynamic (encoded with Sh...
static void writeAPIntToVector(APInt apInt, SmallVectorImpl< char > &result)
static LogicalResult checkTensorElementType(function_ref< InFlightDiagnostic()> emitError, Type elementType)
#define FLOAT_TYPE_SEMANTICS(TYPE, SEM)
b getContext())
static Type getElementType(Type type, ArrayRef< int32_t > indices, function_ref< InFlightDiagnostic(StringRef)> emitErrorFn)
Walks the given type hierarchy with the given indices, potentially down to component granularity,...
Definition SPIRVOps.cpp:229
static ArrayRef< int64_t > getShape(Type type)
Returns the shape of the given type.
Definition Traits.cpp:117
Base type for affine expression.
Definition AffineExpr.h:68
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
Definition AffineMap.h:46
static AffineMap getMultiDimIdentityMap(unsigned numDims, MLIRContext *context)
Returns an AffineMap with 'numDims' identity result dim exprs.
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
unsigned getNumSymbols() const
unsigned getNumDims() const
unsigned getNumResults() const
static SmallVector< AffineMap, 4 > inferFromExprList(ArrayRef< ArrayRef< AffineExpr > > exprsList, MLIRContext *context)
Returns a vector of AffineMaps; each with as many results as exprs.size(), as many dims as the larges...
AffineExpr getResult(unsigned idx) const
bool isIdentity() const
Returns true if this affine map is an identity affine map.
Attributes are known-constant values of operations.
Definition Attributes.h:25
This class provides a shared interface for ranked and unranked memref types.
ArrayRef< int64_t > getShape() const
Returns the shape of this memref type.
static bool isValidElementType(Type type)
Return true if the specified element type is ok in a memref.
FailureOr< PtrLikeTypeInterface > clonePtrWith(Attribute memorySpace, std::optional< Type > elementType) const
Clone this type with the given memory space and element type.
constexpr Type()=default
Attribute getMemorySpace() const
Returns the memory space in which data referred to by this memref resides.
unsigned getMemorySpaceAsInt() const
[deprecated] Returns the memory space in old raw integer representation.
BaseMemRefType cloneWith(std::optional< ArrayRef< int64_t > > shape, Type elementType) const
Clone this type with the given shape and element type.
bool hasRank() const
Returns if this type is ranked, i.e. it has a known number of dimensions.
Type getElementType() const
Returns the element type of this memref type.
MemRefType clone(ArrayRef< int64_t > shape, Type elementType) const
Return a clone of this type with the given new shape and element type.
static bool isValidNamespace(StringRef str)
Utility function that returns if the given string is a valid dialect namespace.
Definition Dialect.cpp:95
This class represents a diagnostic that is inflight and set to be reported.
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:71
Dialect * getLoadedDialect(StringRef name)
Get a registered IR dialect with the given namespace.
bool allowsUnregisteredDialects()
Return true if we allow to create operation for unregistered dialects.
This is a builder type that keeps local references to arguments.
Builder & setShape(ArrayRef< int64_t > newShape)
Builder & setMemorySpace(Attribute newMemorySpace)
Builder & setElementType(Type newElementType)
Builder & setLayout(MemRefLayoutAttrInterface newLayout)
Tensor types represent multi-dimensional arrays, and have two variants: RankedTensorType and Unranked...
TensorType cloneWith(std::optional< ArrayRef< int64_t > > shape, Type elementType) const
Clone this type with the given shape and element type.
constexpr Type()=default
static bool isValidElementType(Type type)
Return true if the specified element type is ok in a tensor.
ArrayRef< int64_t > getShape() const
Returns the shape of this tensor type.
bool hasRank() const
Returns if this type is ranked, i.e. it has a known number of dimensions.
RankedTensorType clone(ArrayRef< int64_t > shape, Type elementType) const
Return a clone of this type with the given new shape and element type.
Type getElementType() const
Returns the element type of this tensor type.
This class provides an abstraction over the various different ranges of value types.
Definition TypeRange.h:40
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
Dialect & getDialect() const
Get the dialect this type is registered to.
Definition Types.h:107
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
Definition Types.cpp:35
bool isIntOrFloat() const
Return true if this is an integer (of any signedness) or a float type.
Definition Types.cpp:118
AttrTypeReplacer.
Attribute wrapIntegerMemorySpace(unsigned memorySpace, MLIRContext *ctx)
Wraps deprecated integer memory space to the new Attribute form.
unsigned getMemorySpaceAsInt(Attribute memorySpace)
[deprecated] Returns the memory space in old raw integer representation.
Attribute skipDefaultMemorySpace(Attribute memorySpace)
Replaces default memorySpace (integer == 0) with empty Attribute.
void writeBits(char *rawData, size_t bitPos, llvm::APInt value)
Write value to byte-aligned position bitPos in rawData.
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:853
Include the generated interface declarations.
bool isValidVectorTypeElementType(::mlir::Type type)
SliceVerificationResult
Enum that captures information related to verifier error conditions on slice insert/extract type of o...
constexpr T real(const NonFloatComplex< T > &x)
Definition Complex.h:255
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
TypeRange filterTypesOut(TypeRange types, const BitVector &indices, SmallVectorImpl< Type > &storage)
Filters out any elements referenced by indices.
constexpr T imag(const NonFloatComplex< T > &x)
Definition Complex.h:260
Operation * clone(OpBuilder &b, Operation *op, TypeRange newResultTypes, ValueRange newOperands)
AffineExpr getAffineConstantExpr(int64_t constant, MLIRContext *context)
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
AffineExpr makeCanonicalStridedLayoutExpr(ArrayRef< int64_t > sizes, ArrayRef< AffineExpr > exprs, MLIRContext *context)
Given MemRef sizes that are either static or dynamic, returns the canonical "contiguous" strides Affi...
std::optional< llvm::SmallDenseSet< unsigned > > computeRankReductionMask(ArrayRef< int64_t > originalShape, ArrayRef< int64_t > reducedShape, bool matchDynamic=false)
Given an originalShape and a reducedShape assumed to be a subset of originalShape with some 1 entries...
AffineExpr simplifyAffineExpr(AffineExpr expr, unsigned numDims, unsigned numSymbols)
Simplify an affine expression by flattening and some amount of simple analysis.
AffineExpr getAffineDimExpr(unsigned position, MLIRContext *context)
These free functions allow clients of the API to not use classes in detail.
SliceVerificationResult isRankReducedType(ShapedType originalType, ShapedType candidateReducedType)
Check if originalType can be rank reduced to candidateReducedType type by dropping some dimensions wi...
TypeRange insertTypesInto(TypeRange oldTypes, ArrayRef< unsigned > indices, TypeRange newTypes, SmallVectorImpl< Type > &storage)
Insert a set of newTypes into oldTypes at the given indices.
llvm::function_ref< Fn > function_ref
Definition LLVM.h:155
AffineExpr getAffineSymbolExpr(unsigned position, MLIRContext *context)