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) {
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 // Empty attribute is allowed as default memory space.
613 if (!memorySpace)
614 return true;
615
616 // Supported built-in attributes.
617 if (llvm::isa<IntegerAttr, StringAttr, DictionaryAttr>(memorySpace))
618 return true;
619
620 // Allow custom dialect attributes.
621 if (!isa<BuiltinDialect>(memorySpace.getDialect()))
622 return true;
623
624 return false;
625}
626
628 MLIRContext *ctx) {
629 if (memorySpace == 0)
630 return nullptr;
631
632 return IntegerAttr::get(IntegerType::get(ctx, 64), memorySpace);
633}
634
636 IntegerAttr intMemorySpace = llvm::dyn_cast_or_null<IntegerAttr>(memorySpace);
637 if (intMemorySpace && intMemorySpace.getValue() == 0)
638 return nullptr;
639
640 return memorySpace;
641}
642
644 if (!memorySpace)
645 return 0;
646
647 assert(llvm::isa<IntegerAttr>(memorySpace) &&
648 "Using `getMemorySpaceInteger` with non-Integer attribute");
649
650 return static_cast<unsigned>(llvm::cast<IntegerAttr>(memorySpace).getInt());
651}
652
653unsigned MemRefType::getMemorySpaceAsInt() const {
654 return detail::getMemorySpaceAsInt(getMemorySpace());
655}
656
657MemRefType MemRefType::get(ArrayRef<int64_t> shape, Type elementType,
658 MemRefLayoutAttrInterface layout,
659 Attribute memorySpace) {
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::get(elementType.getContext(), shape, elementType, layout,
669 memorySpace);
670}
671
672MemRefType MemRefType::getChecked(
674 Type elementType, MemRefLayoutAttrInterface layout, Attribute memorySpace) {
675
676 // Use default layout for empty attribute.
677 if (!layout)
678 layout = AffineMapAttr::get(AffineMap::getMultiDimIdentityMap(
679 shape.size(), elementType.getContext()));
680
681 // Drop default memory space value and replace it with empty attribute.
682 memorySpace = skipDefaultMemorySpace(memorySpace);
683
684 return Base::getChecked(emitErrorFn, elementType.getContext(), shape,
685 elementType, layout, memorySpace);
686}
687
688MemRefType MemRefType::get(ArrayRef<int64_t> shape, Type elementType,
689 AffineMap map, Attribute memorySpace) {
690
691 // Use default layout for empty map.
692 if (!map)
694 elementType.getContext());
695
696 // Wrap AffineMap into Attribute.
697 auto layout = AffineMapAttr::get(map);
698
699 // Drop default memory space value and replace it with empty attribute.
700 memorySpace = skipDefaultMemorySpace(memorySpace);
701
702 return Base::get(elementType.getContext(), shape, elementType, layout,
703 memorySpace);
704}
705
706MemRefType
707MemRefType::getChecked(function_ref<InFlightDiagnostic()> emitErrorFn,
708 ArrayRef<int64_t> shape, Type elementType, AffineMap map,
709 Attribute memorySpace) {
710
711 // Use default layout for empty map.
712 if (!map)
714 elementType.getContext());
715
716 // Wrap AffineMap into Attribute.
717 auto layout = AffineMapAttr::get(map);
718
719 // Drop default memory space value and replace it with empty attribute.
720 memorySpace = skipDefaultMemorySpace(memorySpace);
721
722 return Base::getChecked(emitErrorFn, elementType.getContext(), shape,
723 elementType, layout, memorySpace);
724}
725
726MemRefType MemRefType::get(ArrayRef<int64_t> shape, Type elementType,
727 AffineMap map, unsigned memorySpaceInd) {
728
729 // Use default layout for empty map.
730 if (!map)
732 elementType.getContext());
733
734 // Wrap AffineMap into Attribute.
735 auto layout = AffineMapAttr::get(map);
736
737 // Convert deprecated integer-like memory space to Attribute.
738 Attribute memorySpace =
739 wrapIntegerMemorySpace(memorySpaceInd, elementType.getContext());
740
741 return Base::get(elementType.getContext(), shape, elementType, layout,
742 memorySpace);
743}
744
745MemRefType
746MemRefType::getChecked(function_ref<InFlightDiagnostic()> emitErrorFn,
747 ArrayRef<int64_t> shape, Type elementType, AffineMap map,
748 unsigned memorySpaceInd) {
749
750 // Use default layout for empty map.
751 if (!map)
753 elementType.getContext());
754
755 // Wrap AffineMap into Attribute.
756 auto layout = AffineMapAttr::get(map);
757
758 // Convert deprecated integer-like memory space to Attribute.
759 Attribute memorySpace =
760 wrapIntegerMemorySpace(memorySpaceInd, elementType.getContext());
761
762 return Base::getChecked(emitErrorFn, elementType.getContext(), shape,
763 elementType, layout, memorySpace);
764}
765
766LogicalResult MemRefType::verify(function_ref<InFlightDiagnostic()> emitError,
767 ArrayRef<int64_t> shape, Type elementType,
768 MemRefLayoutAttrInterface layout,
769 Attribute memorySpace) {
770 if (!BaseMemRefType::isValidElementType(elementType))
771 return emitError() << "invalid memref element type";
772
773 // Negative sizes are not allowed except for `kDynamic`.
774 for (int64_t s : shape)
775 if (s < 0 && ShapedType::isStatic(s))
776 return emitError() << "invalid memref size";
777
778 assert(layout && "missing layout specification");
779 if (failed(layout.verifyLayout(shape, emitError)))
780 return failure();
781
782 if (!isSupportedMemorySpace(memorySpace))
783 return emitError() << "unsupported memory space Attribute";
784
785 return success();
786}
787
788bool MemRefType::areTrailingDimsContiguous(int64_t n) {
789 assert(n <= getRank() &&
790 "number of dimensions to check must not exceed rank");
791 return n <= getNumContiguousTrailingDims();
792}
793
794int64_t MemRefType::getNumContiguousTrailingDims() {
795 const int64_t n = getRank();
796
797 // memrefs with identity layout are entirely contiguous.
798 if (getLayout().isIdentity())
799 return n;
800
801 // Get the strides (if any). Failing to do that, conservatively assume a
802 // non-contiguous layout.
803 int64_t offset;
804 SmallVector<int64_t> strides;
805 if (!succeeded(getStridesAndOffset(strides, offset)))
806 return 0;
807
809
810 // A memref with dimensions `d0, d1, ..., dn-1` and strides
811 // `s0, s1, ..., sn-1` is contiguous up to dimension `k`
812 // if each stride `si` is the product of the dimensions `di+1, ..., dn-1`,
813 // for `i` in `[k, n-1]`.
814 // Ignore stride elements if the corresponding dimension is 1, as they are
815 // of no consequence.
816 int64_t dimProduct = 1;
817 for (int64_t i = n - 1; i >= 0; --i) {
818 if (shape[i] == 1)
819 continue;
820 if (strides[i] != dimProduct)
821 return n - i - 1;
822 if (shape[i] == ShapedType::kDynamic)
823 return n - i;
824 dimProduct *= shape[i];
825 }
826
827 return n;
828}
829
830MemRefType MemRefType::canonicalizeStridedLayout() {
831 AffineMap m = getLayout().getAffineMap();
832
833 // Already in canonical form.
834 if (m.isIdentity())
835 return *this;
836
837 // Can't reduce to canonical identity form, return in canonical form.
838 if (m.getNumResults() > 1)
839 return *this;
840
841 // Corner-case for 0-D affine maps.
842 if (m.getNumDims() == 0 && m.getNumSymbols() == 0) {
843 if (auto cst = llvm::dyn_cast<AffineConstantExpr>(m.getResult(0)))
844 if (cst.getValue() == 0)
845 return MemRefType::Builder(*this).setLayout({});
846 return *this;
847 }
848
849 // 0-D corner case for empty shape that still have an affine map. Example:
850 // `memref<f32, affine_map<()[s0] -> (s0)>>`. This is a 1 element memref whose
851 // offset needs to remain, just return t.
852 if (getShape().empty())
853 return *this;
854
855 // If the canonical strided layout for the sizes of `t` is equal to the
856 // simplified layout of `t` we can just return an empty layout. Otherwise,
857 // just simplify the existing layout.
859 auto simplifiedLayoutExpr =
861 if (expr != simplifiedLayoutExpr)
862 return MemRefType::Builder(*this).setLayout(
863 AffineMapAttr::get(AffineMap::get(m.getNumDims(), m.getNumSymbols(),
864 simplifiedLayoutExpr)));
865 return MemRefType::Builder(*this).setLayout({});
866}
867
868LogicalResult MemRefType::getStridesAndOffset(SmallVectorImpl<int64_t> &strides,
869 int64_t &offset) const {
870 return getLayout().getStridesAndOffset(getShape(), strides, offset);
871}
872
873std::pair<SmallVector<int64_t>, int64_t>
874MemRefType::getStridesAndOffset() const {
875 SmallVector<int64_t> strides;
876 int64_t offset;
877 LogicalResult status = getStridesAndOffset(strides, offset);
878 (void)status;
879 assert(succeeded(status) && "Invalid use of check-free getStridesAndOffset");
880 return {strides, offset};
881}
882
883bool MemRefType::isStrided() {
884 int64_t offset;
886 auto res = getStridesAndOffset(strides, offset);
887 return succeeded(res);
888}
889
890bool MemRefType::isLastDimUnitStride() {
891 int64_t offset;
892 SmallVector<int64_t> strides;
893 auto successStrides = getStridesAndOffset(strides, offset);
894 return succeeded(successStrides) && (strides.empty() || strides.back() == 1);
895}
896
897//===----------------------------------------------------------------------===//
898// UnrankedMemRefType
899//===----------------------------------------------------------------------===//
900
901unsigned UnrankedMemRefType::getMemorySpaceAsInt() const {
902 return detail::getMemorySpaceAsInt(getMemorySpace());
903}
904
905LogicalResult
906UnrankedMemRefType::verify(function_ref<InFlightDiagnostic()> emitError,
907 Type elementType, Attribute memorySpace) {
908 if (!BaseMemRefType::isValidElementType(elementType))
909 return emitError() << "invalid memref element type";
910
911 if (!isSupportedMemorySpace(memorySpace))
912 return emitError() << "unsupported memory space Attribute";
913
914 return success();
915}
916
917//===----------------------------------------------------------------------===//
918/// TupleType
919//===----------------------------------------------------------------------===//
920
921/// Return the elements types for this tuple.
922ArrayRef<Type> TupleType::getTypes() const { return getImpl()->getTypes(); }
923
924/// Accumulate the types contained in this tuple and tuples nested within it.
925/// Note that this only flattens nested tuples, not any other container type,
926/// e.g. a tuple<i32, tensor<i32>, tuple<f32, tuple<i64>>> is flattened to
927/// (i32, tensor<i32>, f32, i64)
928void TupleType::getFlattenedTypes(SmallVectorImpl<Type> &types) {
929 for (Type type : getTypes()) {
930 if (auto nestedTuple = llvm::dyn_cast<TupleType>(type))
931 nestedTuple.getFlattenedTypes(types);
932 else
933 types.push_back(type);
934 }
935}
936
937/// Return the number of element types.
938size_t TupleType::size() const { return getImpl()->size(); }
939
940//===----------------------------------------------------------------------===//
941// Type Utilities
942//===----------------------------------------------------------------------===//
943
946 MLIRContext *context) {
947 // Size 0 corner case is useful for canonicalizations.
948 if (sizes.empty())
949 return getAffineConstantExpr(0, context);
950
951 assert(!exprs.empty() && "expected exprs");
952 auto maps = AffineMap::inferFromExprList(exprs, context);
953 assert(!maps.empty() && "Expected one non-empty map");
954 unsigned numDims = maps[0].getNumDims(), nSymbols = maps[0].getNumSymbols();
955
956 AffineExpr expr;
957 bool dynamicPoisonBit = false;
958 int64_t runningSize = 1;
959 for (auto en : llvm::zip(llvm::reverse(exprs), llvm::reverse(sizes))) {
960 int64_t size = std::get<1>(en);
961 AffineExpr dimExpr = std::get<0>(en);
962 AffineExpr stride = dynamicPoisonBit
963 ? getAffineSymbolExpr(nSymbols++, context)
964 : getAffineConstantExpr(runningSize, context);
965 expr = expr ? expr + dimExpr * stride : dimExpr * stride;
966 if (size > 0) {
967 auto result = llvm::checkedMul(runningSize, size);
968 if (!result) {
969 // Overflow occurred, treat as dynamic
970 dynamicPoisonBit = true;
971 } else {
972 runningSize = *result;
973 }
974 } else {
975 dynamicPoisonBit = true;
976 }
977 }
978 return simplifyAffineExpr(expr, numDims, nSymbols);
979}
980
982 MLIRContext *context) {
984 exprs.reserve(sizes.size());
985 for (auto dim : llvm::seq<unsigned>(0, sizes.size()))
986 exprs.push_back(getAffineDimExpr(dim, context));
987 return makeCanonicalStridedLayoutExpr(sizes, exprs, context);
988}
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)
static Type getElementType(Type type)
Determine the element type of type.
b getContext())
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
Dialect & getDialect() const
Get the dialect this attribute is registered to.
Definition Attributes.h:58
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.
bool isSupportedMemorySpace(Attribute memorySpace)
Checks if the memorySpace has supported Attribute type.
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:717
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:147
AffineExpr getAffineSymbolExpr(unsigned position, MLIRContext *context)