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