1842 lines · cpp
1//===- BuiltinAttributes.cpp - MLIR Builtin Attribute 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-exception6//7//===----------------------------------------------------------------------===//8 9#include "mlir/IR/BuiltinAttributes.h"10#include "AttributeDetail.h"11#include "mlir/IR/AffineMap.h"12#include "mlir/IR/BuiltinDialect.h"13#include "mlir/IR/Dialect.h"14#include "mlir/IR/DialectResourceBlobManager.h"15#include "mlir/IR/IntegerSet.h"16#include "mlir/IR/OpImplementation.h"17#include "mlir/IR/Operation.h"18#include "mlir/IR/SymbolTable.h"19#include "mlir/IR/Types.h"20#include "llvm/ADT/APSInt.h"21#include "llvm/Support/Debug.h"22#include "llvm/Support/DebugLog.h"23#include "llvm/Support/Endian.h"24#include <optional>25 26#define DEBUG_TYPE "builtinattributes"27 28using namespace mlir;29using namespace mlir::detail;30 31//===----------------------------------------------------------------------===//32/// Tablegen Attribute Definitions33//===----------------------------------------------------------------------===//34 35#define GET_ATTRDEF_CLASSES36#include "mlir/IR/BuiltinAttributes.cpp.inc"37 38//===----------------------------------------------------------------------===//39// BuiltinDialect40//===----------------------------------------------------------------------===//41 42void BuiltinDialect::registerAttributes() {43 addAttributes<44#define GET_ATTRDEF_LIST45#include "mlir/IR/BuiltinAttributes.cpp.inc"46 >();47 addAttributes<DistinctAttr>();48}49 50//===----------------------------------------------------------------------===//51// DictionaryAttr52//===----------------------------------------------------------------------===//53 54/// Helper function that does either an in place sort or sorts from source array55/// into destination. If inPlace then storage is both the source and the56/// destination, else value is the source and storage destination. Returns57/// whether source was sorted.58template <bool inPlace>59static bool dictionaryAttrSort(ArrayRef<NamedAttribute> value,60 SmallVectorImpl<NamedAttribute> &storage) {61 // Specialize for the common case.62 switch (value.size()) {63 case 0:64 // Zero already sorted.65 if (!inPlace)66 storage.clear();67 break;68 case 1:69 // One already sorted but may need to be copied.70 if (!inPlace)71 storage.assign({value[0]});72 break;73 case 2: {74 bool isSorted = value[0] < value[1];75 if (inPlace) {76 if (!isSorted)77 std::swap(storage[0], storage[1]);78 } else if (isSorted) {79 storage.assign({value[0], value[1]});80 } else {81 storage.assign({value[1], value[0]});82 }83 return !isSorted;84 }85 default:86 if (!inPlace)87 storage.assign(value.begin(), value.end());88 // Check to see they are sorted already.89 bool isSorted = llvm::is_sorted(value);90 // If not, do a general sort.91 if (!isSorted)92 llvm::array_pod_sort(storage.begin(), storage.end());93 return !isSorted;94 }95 return false;96}97 98/// Returns an entry with a duplicate name from the given sorted array of named99/// attributes. Returns std::nullopt if all elements have unique names.100static std::optional<NamedAttribute>101findDuplicateElement(ArrayRef<NamedAttribute> value) {102 const std::optional<NamedAttribute> none{std::nullopt};103 if (value.size() < 2)104 return none;105 106 if (value.size() == 2)107 return value[0].getName() == value[1].getName() ? value[0] : none;108 109 const auto *it = std::adjacent_find(value.begin(), value.end(),110 [](NamedAttribute l, NamedAttribute r) {111 return l.getName() == r.getName();112 });113 return it != value.end() ? *it : none;114}115 116bool DictionaryAttr::sort(ArrayRef<NamedAttribute> value,117 SmallVectorImpl<NamedAttribute> &storage) {118 bool isSorted = dictionaryAttrSort</*inPlace=*/false>(value, storage);119 assert(!findDuplicateElement(storage) &&120 "DictionaryAttr element names must be unique");121 return isSorted;122}123 124bool DictionaryAttr::sortInPlace(SmallVectorImpl<NamedAttribute> &array) {125 bool isSorted = dictionaryAttrSort</*inPlace=*/true>(array, array);126 assert(!findDuplicateElement(array) &&127 "DictionaryAttr element names must be unique");128 return isSorted;129}130 131std::optional<NamedAttribute>132DictionaryAttr::findDuplicate(SmallVectorImpl<NamedAttribute> &array,133 bool isSorted) {134 if (!isSorted)135 dictionaryAttrSort</*inPlace=*/true>(array, array);136 return findDuplicateElement(array);137}138 139DictionaryAttr DictionaryAttr::get(MLIRContext *context,140 ArrayRef<NamedAttribute> value) {141 if (value.empty())142 return DictionaryAttr::getEmpty(context);143 144 // We need to sort the element list to canonicalize it.145 SmallVector<NamedAttribute, 8> storage;146 if (dictionaryAttrSort</*inPlace=*/false>(value, storage))147 value = storage;148 assert(!findDuplicateElement(value) &&149 "DictionaryAttr element names must be unique");150 return Base::get(context, value);151}152/// Construct a dictionary with an array of values that is known to already be153/// sorted by name and uniqued.154DictionaryAttr DictionaryAttr::getWithSorted(MLIRContext *context,155 ArrayRef<NamedAttribute> value) {156 if (value.empty())157 return DictionaryAttr::getEmpty(context);158 // Ensure that the attribute elements are unique and sorted.159 assert(llvm::is_sorted(160 value, [](NamedAttribute l, NamedAttribute r) { return l < r; }) &&161 "expected attribute values to be sorted");162 assert(!findDuplicateElement(value) &&163 "DictionaryAttr element names must be unique");164 return Base::get(context, value);165}166 167/// Return the specified attribute if present, null otherwise.168Attribute DictionaryAttr::get(StringRef name) const {169 auto it = impl::findAttrSorted(begin(), end(), name);170 return it.second ? it.first->getValue() : Attribute();171}172Attribute DictionaryAttr::get(StringAttr name) const {173 auto it = impl::findAttrSorted(begin(), end(), name);174 return it.second ? it.first->getValue() : Attribute();175}176 177/// Return the specified named attribute if present, std::nullopt otherwise.178std::optional<NamedAttribute> DictionaryAttr::getNamed(StringRef name) const {179 auto it = impl::findAttrSorted(begin(), end(), name);180 return it.second ? *it.first : std::optional<NamedAttribute>();181}182std::optional<NamedAttribute> DictionaryAttr::getNamed(StringAttr name) const {183 auto it = impl::findAttrSorted(begin(), end(), name);184 return it.second ? *it.first : std::optional<NamedAttribute>();185}186 187/// Return whether the specified attribute is present.188bool DictionaryAttr::contains(StringRef name) const {189 return impl::findAttrSorted(begin(), end(), name).second;190}191bool DictionaryAttr::contains(StringAttr name) const {192 return impl::findAttrSorted(begin(), end(), name).second;193}194 195DictionaryAttr::iterator DictionaryAttr::begin() const {196 return getValue().begin();197}198DictionaryAttr::iterator DictionaryAttr::end() const {199 return getValue().end();200}201size_t DictionaryAttr::size() const { return getValue().size(); }202 203DictionaryAttr DictionaryAttr::getEmptyUnchecked(MLIRContext *context) {204 return Base::get(context, ArrayRef<NamedAttribute>());205}206 207//===----------------------------------------------------------------------===//208// StridedLayoutAttr209//===----------------------------------------------------------------------===//210 211/// Prints a strided layout attribute.212void StridedLayoutAttr::print(llvm::raw_ostream &os) const {213 auto printIntOrQuestion = [&](int64_t value) {214 if (ShapedType::isDynamic(value))215 os << "?";216 else217 os << value;218 };219 220 os << "strided<[";221 llvm::interleaveComma(getStrides(), os, printIntOrQuestion);222 os << "]";223 224 if (getOffset() != 0) {225 os << ", offset: ";226 printIntOrQuestion(getOffset());227 }228 os << ">";229}230 231/// Returns true if this layout is static, i.e. the strides and offset all have232/// a known value > 0.233bool StridedLayoutAttr::hasStaticLayout() const {234 return ShapedType::isStatic(getOffset()) &&235 ShapedType::isStaticShape(getStrides());236}237 238/// Returns the strided layout as an affine map.239AffineMap StridedLayoutAttr::getAffineMap() const {240 return makeStridedLinearLayoutMap(getStrides(), getOffset(), getContext());241}242 243/// Checks that the type-agnostic strided layout invariants are satisfied.244LogicalResult245StridedLayoutAttr::verify(function_ref<InFlightDiagnostic()> emitError,246 int64_t offset, ArrayRef<int64_t> strides) {247 return success();248}249 250/// Checks that the type-specific strided layout invariants are satisfied.251LogicalResult StridedLayoutAttr::verifyLayout(252 ArrayRef<int64_t> shape,253 function_ref<InFlightDiagnostic()> emitError) const {254 if (shape.size() != getStrides().size())255 return emitError() << "expected the number of strides to match the rank";256 257 return success();258}259 260LogicalResult261StridedLayoutAttr::getStridesAndOffset(ArrayRef<int64_t>,262 SmallVectorImpl<int64_t> &strides,263 int64_t &offset) const {264 llvm::append_range(strides, getStrides());265 offset = getOffset();266 return success();267}268 269//===----------------------------------------------------------------------===//270// StringAttr271//===----------------------------------------------------------------------===//272 273StringAttr StringAttr::getEmptyStringAttrUnchecked(MLIRContext *context) {274 return Base::get(context, "", NoneType::get(context));275}276 277/// Twine support for StringAttr.278StringAttr StringAttr::get(MLIRContext *context, const Twine &twine) {279 // Fast-path empty twine.280 if (twine.isTriviallyEmpty())281 return get(context);282 SmallVector<char, 32> tempStr;283 return Base::get(context, twine.toStringRef(tempStr), NoneType::get(context));284}285 286/// Twine support for StringAttr.287StringAttr StringAttr::get(const Twine &twine, Type type) {288 SmallVector<char, 32> tempStr;289 return Base::get(type.getContext(), twine.toStringRef(tempStr), type);290}291 292StringRef StringAttr::getValue() const { return getImpl()->value; }293 294Type StringAttr::getType() const { return getImpl()->type; }295 296Dialect *StringAttr::getReferencedDialect() const {297 return getImpl()->referencedDialect;298}299 300//===----------------------------------------------------------------------===//301// FloatAttr302//===----------------------------------------------------------------------===//303 304double FloatAttr::getValueAsDouble() const {305 return getValueAsDouble(getValue());306}307double FloatAttr::getValueAsDouble(APFloat value) {308 if (&value.getSemantics() != &APFloat::IEEEdouble()) {309 bool losesInfo = false;310 value.convert(APFloat::IEEEdouble(), APFloat::rmNearestTiesToEven,311 &losesInfo);312 }313 return value.convertToDouble();314}315 316LogicalResult FloatAttr::verify(function_ref<InFlightDiagnostic()> emitError,317 Type type, APFloat value) {318 // Verify that the type is correct.319 if (!llvm::isa<FloatType>(type))320 return emitError() << "expected floating point type";321 322 // Verify that the type semantics match that of the value.323 if (&llvm::cast<FloatType>(type).getFloatSemantics() !=324 &value.getSemantics()) {325 return emitError()326 << "FloatAttr type doesn't match the type implied by its value";327 }328 return success();329}330 331//===----------------------------------------------------------------------===//332// SymbolRefAttr333//===----------------------------------------------------------------------===//334 335SymbolRefAttr SymbolRefAttr::get(MLIRContext *ctx, StringRef value,336 ArrayRef<FlatSymbolRefAttr> nestedRefs) {337 return get(StringAttr::get(ctx, value), nestedRefs);338}339 340FlatSymbolRefAttr SymbolRefAttr::get(MLIRContext *ctx, StringRef value) {341 return llvm::cast<FlatSymbolRefAttr>(get(ctx, value, {}));342}343 344FlatSymbolRefAttr SymbolRefAttr::get(StringAttr value) {345 return llvm::cast<FlatSymbolRefAttr>(get(value, {}));346}347 348FlatSymbolRefAttr SymbolRefAttr::get(Operation *symbol) {349 auto symName =350 symbol->getAttrOfType<StringAttr>(SymbolTable::getSymbolAttrName());351 assert(symName && "value does not have a valid symbol name");352 return SymbolRefAttr::get(symName);353}354 355StringAttr SymbolRefAttr::getLeafReference() const {356 ArrayRef<FlatSymbolRefAttr> nestedRefs = getNestedReferences();357 return nestedRefs.empty() ? getRootReference() : nestedRefs.back().getAttr();358}359 360//===----------------------------------------------------------------------===//361// IntegerAttr362//===----------------------------------------------------------------------===//363 364int64_t IntegerAttr::getInt() const {365 assert((getType().isIndex() || getType().isSignlessInteger()) &&366 "must be signless integer");367 return getValue().getSExtValue();368}369 370int64_t IntegerAttr::getSInt() const {371 assert(getType().isSignedInteger() && "must be signed integer");372 return getValue().getSExtValue();373}374 375uint64_t IntegerAttr::getUInt() const {376 assert(getType().isUnsignedInteger() && "must be unsigned integer");377 return getValue().getZExtValue();378}379 380/// Return the value as an APSInt which carries the signed from the type of381/// the attribute. This traps on signless integers types!382APSInt IntegerAttr::getAPSInt() const {383 assert(!getType().isSignlessInteger() &&384 "Signless integers don't carry a sign for APSInt");385 return APSInt(getValue(), getType().isUnsignedInteger());386}387 388LogicalResult IntegerAttr::verify(function_ref<InFlightDiagnostic()> emitError,389 Type type, APInt value) {390 if (IntegerType integerType = llvm::dyn_cast<IntegerType>(type)) {391 if (integerType.getWidth() != value.getBitWidth())392 return emitError() << "integer type bit width (" << integerType.getWidth()393 << ") doesn't match value bit width ("394 << value.getBitWidth() << ")";395 return success();396 }397 if (llvm::isa<IndexType>(type)) {398 if (value.getBitWidth() != IndexType::kInternalStorageBitWidth)399 return emitError()400 << "value bit width (" << value.getBitWidth()401 << ") doesn't match index type internal storage bit width ("402 << IndexType::kInternalStorageBitWidth << ")";403 return success();404 }405 return emitError() << "expected integer or index type";406}407 408BoolAttr IntegerAttr::getBoolAttrUnchecked(IntegerType type, bool value) {409 auto attr = Base::get(type.getContext(), type, APInt(/*numBits=*/1, value));410 return llvm::cast<BoolAttr>(attr);411}412 413//===----------------------------------------------------------------------===//414// BoolAttr415//===----------------------------------------------------------------------===//416 417bool BoolAttr::getValue() const {418 auto *storage = reinterpret_cast<IntegerAttrStorage *>(impl);419 return storage->value.getBoolValue();420}421 422bool BoolAttr::classof(Attribute attr) {423 IntegerAttr intAttr = llvm::dyn_cast<IntegerAttr>(attr);424 return intAttr && intAttr.getType().isSignlessInteger(1);425}426 427//===----------------------------------------------------------------------===//428// OpaqueAttr429//===----------------------------------------------------------------------===//430 431LogicalResult OpaqueAttr::verify(function_ref<InFlightDiagnostic()> emitError,432 StringAttr dialect, StringRef attrData,433 Type type) {434 if (!Dialect::isValidNamespace(dialect.strref()))435 return emitError() << "invalid dialect namespace '" << dialect << "'";436 437 // Check that the dialect is actually registered.438 MLIRContext *context = dialect.getContext();439 if (!context->allowsUnregisteredDialects() &&440 !context->getLoadedDialect(dialect.strref())) {441 return emitError()442 << "#" << dialect << "<\"" << attrData << "\"> : " << type443 << " attribute created with unregistered dialect. If this is "444 "intended, please call allowUnregisteredDialects() on the "445 "MLIRContext, or use -allow-unregistered-dialect with "446 "the MLIR opt tool used";447 }448 449 return success();450}451 452//===----------------------------------------------------------------------===//453// DenseElementsAttr Utilities454//===----------------------------------------------------------------------===//455 456const char DenseIntOrFPElementsAttrStorage::kSplatTrue = ~0;457const char DenseIntOrFPElementsAttrStorage::kSplatFalse = 0;458 459/// Get the bitwidth of a dense element type within the buffer.460/// DenseElementsAttr requires bitwidths greater than 1 to be aligned by 8.461static size_t getDenseElementStorageWidth(size_t origWidth) {462 return origWidth == 1 ? origWidth : llvm::alignTo<8>(origWidth);463}464static size_t getDenseElementStorageWidth(Type elementType) {465 return getDenseElementStorageWidth(getDenseElementBitWidth(elementType));466}467 468/// Set a bit to a specific value.469static void setBit(char *rawData, size_t bitPos, bool value) {470 if (value)471 rawData[bitPos / CHAR_BIT] |= (1 << (bitPos % CHAR_BIT));472 else473 rawData[bitPos / CHAR_BIT] &= ~(1 << (bitPos % CHAR_BIT));474}475 476/// Return the value of the specified bit.477static bool getBit(const char *rawData, size_t bitPos) {478 return (rawData[bitPos / CHAR_BIT] & (1 << (bitPos % CHAR_BIT))) != 0;479}480 481/// Copy actual `numBytes` data from `value` (APInt) to char array(`result`) for482/// BE format.483static void copyAPIntToArrayForBEmachine(APInt value, size_t numBytes,484 char *result) {485 assert(llvm::endianness::native == llvm::endianness::big);486 assert(value.getNumWords() * APInt::APINT_WORD_SIZE >= numBytes);487 488 // Copy the words filled with data.489 // For example, when `value` has 2 words, the first word is filled with data.490 // `value` (10 bytes, BE):|abcdefgh|------ij| ==> `result` (BE):|abcdefgh|--|491 size_t numFilledWords = (value.getNumWords() - 1) * APInt::APINT_WORD_SIZE;492 std::copy_n(reinterpret_cast<const char *>(value.getRawData()),493 numFilledWords, result);494 // Convert last word of APInt to LE format and store it in char495 // array(`valueLE`).496 // ex. last word of `value` (BE): |------ij| ==> `valueLE` (LE): |ji------|497 size_t lastWordPos = numFilledWords;498 SmallVector<char, 8> valueLE(APInt::APINT_WORD_SIZE);499 DenseIntOrFPElementsAttr::convertEndianOfCharForBEmachine(500 reinterpret_cast<const char *>(value.getRawData()) + lastWordPos,501 valueLE.begin(), APInt::APINT_BITS_PER_WORD, 1);502 // Extract actual APInt data from `valueLE`, convert endianness to BE format,503 // and store it in `result`.504 // ex. `valueLE` (LE): |ji------| ==> `result` (BE): |abcdefgh|ij|505 DenseIntOrFPElementsAttr::convertEndianOfCharForBEmachine(506 valueLE.begin(), result + lastWordPos,507 (numBytes - lastWordPos) * CHAR_BIT, 1);508}509 510/// Copy `numBytes` data from `inArray`(char array) to `result`(APINT) for BE511/// format.512static void copyArrayToAPIntForBEmachine(const char *inArray, size_t numBytes,513 APInt &result) {514 assert(llvm::endianness::native == llvm::endianness::big);515 assert(result.getNumWords() * APInt::APINT_WORD_SIZE >= numBytes);516 517 // Copy the data that fills the word of `result` from `inArray`.518 // For example, when `result` has 2 words, the first word will be filled with519 // data. So, the first 8 bytes are copied from `inArray` here.520 // `inArray` (10 bytes, BE): |abcdefgh|ij|521 // ==> `result` (2 words, BE): |abcdefgh|--------|522 size_t numFilledWords = (result.getNumWords() - 1) * APInt::APINT_WORD_SIZE;523 std::copy_n(524 inArray, numFilledWords,525 const_cast<char *>(reinterpret_cast<const char *>(result.getRawData())));526 527 // Convert array data which will be last word of `result` to LE format, and528 // store it in char array(`inArrayLE`).529 // ex. `inArray` (last two bytes, BE): |ij| ==> `inArrayLE` (LE): |ji------|530 size_t lastWordPos = numFilledWords;531 SmallVector<char, 8> inArrayLE(APInt::APINT_WORD_SIZE);532 DenseIntOrFPElementsAttr::convertEndianOfCharForBEmachine(533 inArray + lastWordPos, inArrayLE.begin(),534 (numBytes - lastWordPos) * CHAR_BIT, 1);535 536 // Convert `inArrayLE` to BE format, and store it in last word of `result`.537 // ex. `inArrayLE` (LE): |ji------| ==> `result` (BE): |abcdefgh|------ij|538 DenseIntOrFPElementsAttr::convertEndianOfCharForBEmachine(539 inArrayLE.begin(),540 const_cast<char *>(reinterpret_cast<const char *>(result.getRawData())) +541 lastWordPos,542 APInt::APINT_BITS_PER_WORD, 1);543}544 545/// Writes value to the bit position `bitPos` in array `rawData`.546static void writeBits(char *rawData, size_t bitPos, APInt value) {547 size_t bitWidth = value.getBitWidth();548 549 // If the bitwidth is 1 we just toggle the specific bit.550 if (bitWidth == 1)551 return setBit(rawData, bitPos, value.isOne());552 553 // Otherwise, the bit position is guaranteed to be byte aligned.554 assert((bitPos % CHAR_BIT) == 0 && "expected bitPos to be 8-bit aligned");555 if (llvm::endianness::native == llvm::endianness::big) {556 // Copy from `value` to `rawData + (bitPos / CHAR_BIT)`.557 // Copying the first `llvm::divideCeil(bitWidth, CHAR_BIT)` bytes doesn't558 // work correctly in BE format.559 // ex. `value` (2 words including 10 bytes)560 // ==> BE: |abcdefgh|------ij|, LE: |hgfedcba|ji------|561 copyAPIntToArrayForBEmachine(value, llvm::divideCeil(bitWidth, CHAR_BIT),562 rawData + (bitPos / CHAR_BIT));563 } else {564 std::copy_n(reinterpret_cast<const char *>(value.getRawData()),565 llvm::divideCeil(bitWidth, CHAR_BIT),566 rawData + (bitPos / CHAR_BIT));567 }568}569 570/// Reads the next `bitWidth` bits from the bit position `bitPos` in array571/// `rawData`.572static APInt readBits(const char *rawData, size_t bitPos, size_t bitWidth) {573 // Handle a boolean bit position.574 if (bitWidth == 1)575 return APInt(1, getBit(rawData, bitPos) ? 1 : 0);576 577 // Otherwise, the bit position must be 8-bit aligned.578 assert((bitPos % CHAR_BIT) == 0 && "expected bitPos to be 8-bit aligned");579 APInt result(bitWidth, 0);580 if (llvm::endianness::native == llvm::endianness::big) {581 // Copy from `rawData + (bitPos / CHAR_BIT)` to `result`.582 // Copying the first `llvm::divideCeil(bitWidth, CHAR_BIT)` bytes doesn't583 // work correctly in BE format.584 // ex. `result` (2 words including 10 bytes)585 // ==> BE: |abcdefgh|------ij|, LE: |hgfedcba|ji------| This function586 copyArrayToAPIntForBEmachine(rawData + (bitPos / CHAR_BIT),587 llvm::divideCeil(bitWidth, CHAR_BIT), result);588 } else {589 std::copy_n(rawData + (bitPos / CHAR_BIT),590 llvm::divideCeil(bitWidth, CHAR_BIT),591 const_cast<char *>(592 reinterpret_cast<const char *>(result.getRawData())));593 }594 return result;595}596 597/// Returns true if 'values' corresponds to a splat, i.e. one element, or has598/// the same element count as 'type'.599template <typename Values>600static bool hasSameNumElementsOrSplat(ShapedType type, const Values &values) {601 return (values.size() == 1) ||602 (type.getNumElements() == static_cast<int64_t>(values.size()));603}604 605//===----------------------------------------------------------------------===//606// DenseElementsAttr Iterators607//===----------------------------------------------------------------------===//608 609//===----------------------------------------------------------------------===//610// AttributeElementIterator611//===----------------------------------------------------------------------===//612 613DenseElementsAttr::AttributeElementIterator::AttributeElementIterator(614 DenseElementsAttr attr, size_t index)615 : llvm::indexed_accessor_iterator<AttributeElementIterator, const void *,616 Attribute, Attribute, Attribute>(617 attr.getAsOpaquePointer(), index) {}618 619Attribute DenseElementsAttr::AttributeElementIterator::operator*() const {620 auto owner = llvm::cast<DenseElementsAttr>(getFromOpaquePointer(base));621 Type eltTy = owner.getElementType();622 if (llvm::dyn_cast<IntegerType>(eltTy))623 return IntegerAttr::get(eltTy, *IntElementIterator(owner, index));624 if (llvm::isa<IndexType>(eltTy))625 return IntegerAttr::get(eltTy, *IntElementIterator(owner, index));626 if (auto floatEltTy = llvm::dyn_cast<FloatType>(eltTy)) {627 IntElementIterator intIt(owner, index);628 FloatElementIterator floatIt(floatEltTy.getFloatSemantics(), intIt);629 return FloatAttr::get(eltTy, *floatIt);630 }631 if (auto complexTy = llvm::dyn_cast<ComplexType>(eltTy)) {632 auto complexEltTy = complexTy.getElementType();633 ComplexIntElementIterator complexIntIt(owner, index);634 if (llvm::isa<IntegerType>(complexEltTy)) {635 auto value = *complexIntIt;636 auto real = IntegerAttr::get(complexEltTy, value.real());637 auto imag = IntegerAttr::get(complexEltTy, value.imag());638 return ArrayAttr::get(complexTy.getContext(),639 ArrayRef<Attribute>{real, imag});640 }641 642 ComplexFloatElementIterator complexFloatIt(643 llvm::cast<FloatType>(complexEltTy).getFloatSemantics(), complexIntIt);644 auto value = *complexFloatIt;645 auto real = FloatAttr::get(complexEltTy, value.real());646 auto imag = FloatAttr::get(complexEltTy, value.imag());647 return ArrayAttr::get(complexTy.getContext(),648 ArrayRef<Attribute>{real, imag});649 }650 if (llvm::isa<DenseStringElementsAttr>(owner)) {651 ArrayRef<StringRef> vals = owner.getRawStringData();652 return StringAttr::get(owner.isSplat() ? vals.front() : vals[index], eltTy);653 }654 llvm_unreachable("unexpected element type");655}656 657//===----------------------------------------------------------------------===//658// BoolElementIterator659//===----------------------------------------------------------------------===//660 661DenseElementsAttr::BoolElementIterator::BoolElementIterator(662 DenseElementsAttr attr, size_t dataIndex)663 : DenseElementIndexedIteratorImpl<BoolElementIterator, bool, bool, bool>(664 attr.getRawData().data(), attr.isSplat(), dataIndex) {}665 666bool DenseElementsAttr::BoolElementIterator::operator*() const {667 return getBit(getData(), getDataIndex());668}669 670//===----------------------------------------------------------------------===//671// IntElementIterator672//===----------------------------------------------------------------------===//673 674DenseElementsAttr::IntElementIterator::IntElementIterator(675 DenseElementsAttr attr, size_t dataIndex)676 : DenseElementIndexedIteratorImpl<IntElementIterator, APInt, APInt, APInt>(677 attr.getRawData().data(), attr.isSplat(), dataIndex),678 bitWidth(getDenseElementBitWidth(attr.getElementType())) {}679 680APInt DenseElementsAttr::IntElementIterator::operator*() const {681 return readBits(getData(),682 getDataIndex() * getDenseElementStorageWidth(bitWidth),683 bitWidth);684}685 686//===----------------------------------------------------------------------===//687// ComplexIntElementIterator688//===----------------------------------------------------------------------===//689 690DenseElementsAttr::ComplexIntElementIterator::ComplexIntElementIterator(691 DenseElementsAttr attr, size_t dataIndex)692 : DenseElementIndexedIteratorImpl<ComplexIntElementIterator,693 std::complex<APInt>, std::complex<APInt>,694 std::complex<APInt>>(695 attr.getRawData().data(), attr.isSplat(), dataIndex) {696 auto complexType = llvm::cast<ComplexType>(attr.getElementType());697 bitWidth = getDenseElementBitWidth(complexType.getElementType());698}699 700std::complex<APInt>701DenseElementsAttr::ComplexIntElementIterator::operator*() const {702 size_t storageWidth = getDenseElementStorageWidth(bitWidth);703 size_t offset = getDataIndex() * storageWidth * 2;704 return {readBits(getData(), offset, bitWidth),705 readBits(getData(), offset + storageWidth, bitWidth)};706}707 708//===----------------------------------------------------------------------===//709// DenseArrayAttr710//===----------------------------------------------------------------------===//711 712LogicalResult713DenseArrayAttr::verify(function_ref<InFlightDiagnostic()> emitError,714 Type elementType, int64_t size, ArrayRef<char> rawData) {715 if (!elementType.isIntOrIndexOrFloat())716 return emitError() << "expected integer or floating point element type";717 int64_t dataSize = rawData.size();718 int64_t elementSize =719 llvm::divideCeil(elementType.getIntOrFloatBitWidth(), CHAR_BIT);720 if (size * elementSize != dataSize) {721 return emitError() << "expected data size (" << size << " elements, "722 << elementSize723 << " bytes each) does not match: " << dataSize724 << " bytes";725 }726 return success();727}728 729namespace {730/// Instantiations of this class provide utilities for interacting with native731/// data types in the context of DenseArrayAttr.732template <size_t width,733 IntegerType::SignednessSemantics signedness = IntegerType::Signless>734struct DenseArrayAttrIntUtil {735 static bool checkElementType(Type eltType) {736 auto type = llvm::dyn_cast<IntegerType>(eltType);737 if (!type || type.getWidth() != width)738 return false;739 return type.getSignedness() == signedness;740 }741 742 static Type getElementType(MLIRContext *ctx) {743 return IntegerType::get(ctx, width, signedness);744 }745 746 template <typename T>747 static void printElement(raw_ostream &os, T value) {748 os << value;749 }750 751 template <typename T>752 static ParseResult parseElement(AsmParser &parser, T &value) {753 return parser.parseInteger(value);754 }755};756template <typename T>757struct DenseArrayAttrUtil;758 759/// Specialization for boolean elements to print 'true' and 'false' literals for760/// elements.761template <>762struct DenseArrayAttrUtil<bool> : public DenseArrayAttrIntUtil<1> {763 static void printElement(raw_ostream &os, bool value) {764 os << (value ? "true" : "false");765 }766};767 768/// Specialization for 8-bit integers to ensure values are printed as integers769/// and not characters.770template <>771struct DenseArrayAttrUtil<int8_t> : public DenseArrayAttrIntUtil<8> {772 static void printElement(raw_ostream &os, int8_t value) {773 os << static_cast<int>(value);774 }775};776template <>777struct DenseArrayAttrUtil<int16_t> : public DenseArrayAttrIntUtil<16> {};778template <>779struct DenseArrayAttrUtil<int32_t> : public DenseArrayAttrIntUtil<32> {};780template <>781struct DenseArrayAttrUtil<int64_t> : public DenseArrayAttrIntUtil<64> {};782 783/// Specialization for 32-bit floats.784template <>785struct DenseArrayAttrUtil<float> {786 static bool checkElementType(Type eltType) { return eltType.isF32(); }787 static Type getElementType(MLIRContext *ctx) { return Float32Type::get(ctx); }788 static void printElement(raw_ostream &os, float value) { os << value; }789 790 /// Parse a double and cast it to a float.791 static ParseResult parseElement(AsmParser &parser, float &value) {792 double doubleVal;793 if (parser.parseFloat(doubleVal))794 return failure();795 value = doubleVal;796 return success();797 }798};799 800/// Specialization for 64-bit floats.801template <>802struct DenseArrayAttrUtil<double> {803 static bool checkElementType(Type eltType) { return eltType.isF64(); }804 static Type getElementType(MLIRContext *ctx) { return Float64Type::get(ctx); }805 static void printElement(raw_ostream &os, float value) { os << value; }806 static ParseResult parseElement(AsmParser &parser, double &value) {807 return parser.parseFloat(value);808 }809};810} // namespace811 812template <typename T>813void DenseArrayAttrImpl<T>::print(AsmPrinter &printer) const {814 print(printer.getStream());815}816 817template <typename T>818void DenseArrayAttrImpl<T>::printWithoutBraces(raw_ostream &os) const {819 llvm::interleaveComma(asArrayRef(), os, [&](T value) {820 DenseArrayAttrUtil<T>::printElement(os, value);821 });822}823 824template <typename T>825void DenseArrayAttrImpl<T>::print(raw_ostream &os) const {826 os << "[";827 printWithoutBraces(os);828 os << "]";829}830 831/// Parse a DenseArrayAttr without the braces: `1, 2, 3`832template <typename T>833Attribute DenseArrayAttrImpl<T>::parseWithoutBraces(AsmParser &parser,834 Type odsType) {835 SmallVector<T> data;836 if (failed(parser.parseCommaSeparatedList([&]() {837 T value;838 if (DenseArrayAttrUtil<T>::parseElement(parser, value))839 return failure();840 data.push_back(value);841 return success();842 })))843 return {};844 return get(parser.getContext(), data);845}846 847/// Parse a DenseArrayAttr: `[ 1, 2, 3 ]`848template <typename T>849Attribute DenseArrayAttrImpl<T>::parse(AsmParser &parser, Type odsType) {850 if (parser.parseLSquare())851 return {};852 // Handle empty list case.853 if (succeeded(parser.parseOptionalRSquare()))854 return get(parser.getContext(), {});855 Attribute result = parseWithoutBraces(parser, odsType);856 if (parser.parseRSquare())857 return {};858 return result;859}860 861/// Conversion from DenseArrayAttr<T> to ArrayRef<T>.862template <typename T>863DenseArrayAttrImpl<T>::operator ArrayRef<T>() const {864 ArrayRef<char> raw = getRawData();865 assert((raw.size() % sizeof(T)) == 0);866 return ArrayRef<T>(reinterpret_cast<const T *>(raw.data()),867 raw.size() / sizeof(T));868}869 870/// Builds a DenseArrayAttr<T> from an ArrayRef<T>.871template <typename T>872DenseArrayAttrImpl<T> DenseArrayAttrImpl<T>::get(MLIRContext *context,873 ArrayRef<T> content) {874 Type elementType = DenseArrayAttrUtil<T>::getElementType(context);875 auto rawArray = ArrayRef<char>(reinterpret_cast<const char *>(content.data()),876 content.size() * sizeof(T));877 return llvm::cast<DenseArrayAttrImpl<T>>(878 Base::get(context, elementType, content.size(), rawArray));879}880 881template <typename T>882bool DenseArrayAttrImpl<T>::classof(Attribute attr) {883 if (auto denseArray = llvm::dyn_cast<DenseArrayAttr>(attr))884 return DenseArrayAttrUtil<T>::checkElementType(denseArray.getElementType());885 return false;886}887 888namespace mlir {889namespace detail {890// Explicit instantiation for all the supported DenseArrayAttr.891template class DenseArrayAttrImpl<bool>;892template class DenseArrayAttrImpl<int8_t>;893template class DenseArrayAttrImpl<int16_t>;894template class DenseArrayAttrImpl<int32_t>;895template class DenseArrayAttrImpl<int64_t>;896template class DenseArrayAttrImpl<float>;897template class DenseArrayAttrImpl<double>;898} // namespace detail899} // namespace mlir900 901//===----------------------------------------------------------------------===//902// DenseElementsAttr903//===----------------------------------------------------------------------===//904 905/// Method for support type inquiry through isa, cast and dyn_cast.906bool DenseElementsAttr::classof(Attribute attr) {907 return llvm::isa<DenseIntOrFPElementsAttr, DenseStringElementsAttr>(attr);908}909 910DenseElementsAttr DenseElementsAttr::get(ShapedType type,911 ArrayRef<Attribute> values) {912 assert(hasSameNumElementsOrSplat(type, values));913 914 Type eltType = type.getElementType();915 916 // Take care complex type case first.917 if (auto complexType = llvm::dyn_cast<ComplexType>(eltType)) {918 if (complexType.getElementType().isIntOrIndex()) {919 SmallVector<std::complex<APInt>> complexValues;920 complexValues.reserve(values.size());921 for (Attribute attr : values) {922 assert(llvm::isa<ArrayAttr>(attr) && "expected ArrayAttr for complex");923 auto arrayAttr = llvm::cast<ArrayAttr>(attr);924 assert(arrayAttr.size() == 2 && "expected 2 element for complex");925 auto attr0 = arrayAttr[0];926 auto attr1 = arrayAttr[1];927 complexValues.push_back(928 std::complex<APInt>(llvm::cast<IntegerAttr>(attr0).getValue(),929 llvm::cast<IntegerAttr>(attr1).getValue()));930 }931 return DenseElementsAttr::get(type, complexValues);932 }933 // Must be float.934 SmallVector<std::complex<APFloat>> complexValues;935 complexValues.reserve(values.size());936 for (Attribute attr : values) {937 assert(llvm::isa<ArrayAttr>(attr) && "expected ArrayAttr for complex");938 auto arrayAttr = llvm::cast<ArrayAttr>(attr);939 assert(arrayAttr.size() == 2 && "expected 2 element for complex");940 auto attr0 = arrayAttr[0];941 auto attr1 = arrayAttr[1];942 complexValues.push_back(943 std::complex<APFloat>(llvm::cast<FloatAttr>(attr0).getValue(),944 llvm::cast<FloatAttr>(attr1).getValue()));945 }946 return DenseElementsAttr::get(type, complexValues);947 }948 949 // If the element type is not based on int/float/index, assume it is a string950 // type.951 if (!eltType.isIntOrIndexOrFloat()) {952 SmallVector<StringRef, 8> stringValues;953 stringValues.reserve(values.size());954 for (Attribute attr : values) {955 assert(llvm::isa<StringAttr>(attr) &&956 "expected string value for non integer/index/float element");957 stringValues.push_back(llvm::cast<StringAttr>(attr).getValue());958 }959 return get(type, stringValues);960 }961 962 // Otherwise, get the raw storage width to use for the allocation.963 size_t bitWidth = getDenseElementBitWidth(eltType);964 size_t storageBitWidth = getDenseElementStorageWidth(bitWidth);965 966 // Compress the attribute values into a character buffer.967 SmallVector<char, 8> data(968 llvm::divideCeil(storageBitWidth * values.size(), CHAR_BIT));969 APInt intVal;970 for (unsigned i = 0, e = values.size(); i < e; ++i) {971 if (auto floatAttr = llvm::dyn_cast<FloatAttr>(values[i])) {972 assert(floatAttr.getType() == eltType &&973 "expected float attribute type to equal element type");974 intVal = floatAttr.getValue().bitcastToAPInt();975 } else {976 auto intAttr = llvm::cast<IntegerAttr>(values[i]);977 assert(intAttr.getType() == eltType &&978 "expected integer attribute type to equal element type");979 intVal = intAttr.getValue();980 }981 982 assert(intVal.getBitWidth() == bitWidth &&983 "expected value to have same bitwidth as element type");984 writeBits(data.data(), i * storageBitWidth, intVal);985 }986 987 // Handle the special encoding of splat of bool.988 if (values.size() == 1 && eltType.isInteger(1))989 data[0] = data[0] ? -1 : 0;990 991 return DenseIntOrFPElementsAttr::getRaw(type, data);992}993 994DenseElementsAttr DenseElementsAttr::get(ShapedType type,995 ArrayRef<bool> values) {996 assert(hasSameNumElementsOrSplat(type, values));997 assert(type.getElementType().isInteger(1));998 999 SmallVector<char> buff(llvm::divideCeil(values.size(), CHAR_BIT));1000 1001 if (!values.empty()) {1002 bool isSplat = true;1003 bool firstValue = values[0];1004 for (int i = 0, e = values.size(); i != e; ++i) {1005 isSplat &= values[i] == firstValue;1006 setBit(buff.data(), i, values[i]);1007 }1008 1009 // Splat of bool is encoded as a byte with all-ones in it.1010 if (isSplat) {1011 buff.resize(1);1012 buff[0] = values[0] ? -1 : 0;1013 }1014 }1015 1016 return DenseIntOrFPElementsAttr::getRaw(type, buff);1017}1018 1019DenseElementsAttr DenseElementsAttr::get(ShapedType type,1020 ArrayRef<StringRef> values) {1021 assert(!type.getElementType().isIntOrFloat());1022 return DenseStringElementsAttr::get(type, values);1023}1024 1025/// Constructs a dense integer elements attribute from an array of APInt1026/// values. Each APInt value is expected to have the same bitwidth as the1027/// element type of 'type'.1028DenseElementsAttr DenseElementsAttr::get(ShapedType type,1029 ArrayRef<APInt> values) {1030 assert(type.getElementType().isIntOrIndex());1031 assert(hasSameNumElementsOrSplat(type, values));1032 size_t storageBitWidth = getDenseElementStorageWidth(type.getElementType());1033 return DenseIntOrFPElementsAttr::getRaw(type, storageBitWidth, values);1034}1035DenseElementsAttr DenseElementsAttr::get(ShapedType type,1036 ArrayRef<std::complex<APInt>> values) {1037 ComplexType complex = llvm::cast<ComplexType>(type.getElementType());1038 assert(llvm::isa<IntegerType>(complex.getElementType()));1039 assert(hasSameNumElementsOrSplat(type, values));1040 size_t storageBitWidth = getDenseElementStorageWidth(complex) / 2;1041 ArrayRef<APInt> intVals(reinterpret_cast<const APInt *>(values.data()),1042 values.size() * 2);1043 return DenseIntOrFPElementsAttr::getRaw(type, storageBitWidth, intVals);1044}1045 1046// Constructs a dense float elements attribute from an array of APFloat1047// values. Each APFloat value is expected to have the same bitwidth as the1048// element type of 'type'.1049DenseElementsAttr DenseElementsAttr::get(ShapedType type,1050 ArrayRef<APFloat> values) {1051 assert(llvm::isa<FloatType>(type.getElementType()));1052 assert(hasSameNumElementsOrSplat(type, values));1053 size_t storageBitWidth = getDenseElementStorageWidth(type.getElementType());1054 return DenseIntOrFPElementsAttr::getRaw(type, storageBitWidth, values);1055}1056DenseElementsAttr1057DenseElementsAttr::get(ShapedType type,1058 ArrayRef<std::complex<APFloat>> values) {1059 ComplexType complex = llvm::cast<ComplexType>(type.getElementType());1060 assert(llvm::isa<FloatType>(complex.getElementType()));1061 assert(hasSameNumElementsOrSplat(type, values));1062 ArrayRef<APFloat> apVals(reinterpret_cast<const APFloat *>(values.data()),1063 values.size() * 2);1064 size_t storageBitWidth = getDenseElementStorageWidth(complex) / 2;1065 return DenseIntOrFPElementsAttr::getRaw(type, storageBitWidth, apVals);1066}1067 1068/// Construct a dense elements attribute from a raw buffer representing the1069/// data for this attribute. Users should generally not use this methods as1070/// the expected buffer format may not be a form the user expects.1071DenseElementsAttr1072DenseElementsAttr::getFromRawBuffer(ShapedType type, ArrayRef<char> rawBuffer) {1073 return DenseIntOrFPElementsAttr::getRaw(type, rawBuffer);1074}1075 1076/// Returns true if the given buffer is a valid raw buffer for the given type.1077bool DenseElementsAttr::isValidRawBuffer(ShapedType type,1078 ArrayRef<char> rawBuffer,1079 bool &detectedSplat) {1080 size_t storageWidth = getDenseElementStorageWidth(type.getElementType());1081 size_t rawBufferWidth = rawBuffer.size() * CHAR_BIT;1082 int64_t numElements = type.getNumElements();1083 1084 // The initializer is always a splat if the result type has a single element.1085 detectedSplat = numElements == 1;1086 1087 // Storage width of 1 is special as it is packed by the bit.1088 if (storageWidth == 1) {1089 // Check for a splat, or a buffer equal to the number of elements which1090 // consists of either all 0's or all 1's.1091 if (rawBuffer.size() == 1) {1092 auto rawByte = static_cast<uint8_t>(rawBuffer[0]);1093 if (rawByte == 0 || rawByte == 0xff) {1094 detectedSplat = true;1095 return true;1096 }1097 }1098 1099 // This is a valid non-splat buffer if it has the right size.1100 return rawBufferWidth == llvm::alignTo<8>(numElements);1101 }1102 1103 // All other types are 8-bit aligned, so we can just check the buffer width1104 // to know if only a single initializer element was passed in.1105 if (rawBufferWidth == storageWidth) {1106 detectedSplat = true;1107 return true;1108 }1109 1110 // The raw buffer is valid if it has the right size.1111 return rawBufferWidth == storageWidth * numElements;1112}1113 1114/// Check the information for a C++ data type, check if this type is valid for1115/// the current attribute. This method is used to verify specific type1116/// invariants that the templatized 'getValues' method cannot.1117static bool isValidIntOrFloat(Type type, int64_t dataEltSize, bool isInt,1118 bool isSigned) {1119 // Make sure that the data element size is the same as the type element width.1120 auto denseEltBitWidth = getDenseElementBitWidth(type);1121 auto dataSize = static_cast<size_t>(dataEltSize * CHAR_BIT);1122 if (denseEltBitWidth != dataSize) {1123 LDBG() << "expected dense element bit width " << denseEltBitWidth1124 << " to match data size " << dataSize << " for type " << type;1125 return false;1126 }1127 1128 // Check that the element type is either float or integer or index.1129 if (!isInt) {1130 bool valid = llvm::isa<FloatType>(type);1131 if (!valid)1132 LDBG() << "expected float type when isInt is false, but found " << type;1133 return valid;1134 }1135 if (type.isIndex())1136 return true;1137 1138 auto intType = llvm::dyn_cast<IntegerType>(type);1139 if (!intType) {1140 LDBG() << "expected integer type when isInt is true, but found " << type;1141 return false;1142 }1143 1144 // Make sure signedness semantics is consistent.1145 if (intType.isSignless())1146 return true;1147 1148 bool valid = intType.isSigned() == isSigned;1149 if (!valid)1150 LDBG() << "expected signedness " << isSigned << " to match type " << type;1151 return valid;1152}1153 1154/// Defaults down the subclass implementation.1155DenseElementsAttr DenseElementsAttr::getRawComplex(ShapedType type,1156 ArrayRef<char> data,1157 int64_t dataEltSize,1158 bool isInt, bool isSigned) {1159 return DenseIntOrFPElementsAttr::getRawComplex(type, data, dataEltSize, isInt,1160 isSigned);1161}1162DenseElementsAttr DenseElementsAttr::getRawIntOrFloat(ShapedType type,1163 ArrayRef<char> data,1164 int64_t dataEltSize,1165 bool isInt,1166 bool isSigned) {1167 return DenseIntOrFPElementsAttr::getRawIntOrFloat(type, data, dataEltSize,1168 isInt, isSigned);1169}1170 1171bool DenseElementsAttr::isValidIntOrFloat(int64_t dataEltSize, bool isInt,1172 bool isSigned) const {1173 return ::isValidIntOrFloat(getElementType(), dataEltSize, isInt, isSigned);1174}1175bool DenseElementsAttr::isValidComplex(int64_t dataEltSize, bool isInt,1176 bool isSigned) const {1177 return ::isValidIntOrFloat(1178 llvm::cast<ComplexType>(getElementType()).getElementType(),1179 dataEltSize / 2, isInt, isSigned);1180}1181 1182/// Returns true if this attribute corresponds to a splat, i.e. if all element1183/// values are the same.1184bool DenseElementsAttr::isSplat() const {1185 return static_cast<DenseElementsAttributeStorage *>(impl)->isSplat;1186}1187 1188/// Return if the given complex type has an integer element type.1189static bool isComplexOfIntType(Type type) {1190 return llvm::isa<IntegerType>(llvm::cast<ComplexType>(type).getElementType());1191}1192 1193auto DenseElementsAttr::tryGetComplexIntValues() const1194 -> FailureOr<iterator_range_impl<ComplexIntElementIterator>> {1195 if (!isComplexOfIntType(getElementType()))1196 return failure();1197 return iterator_range_impl<ComplexIntElementIterator>(1198 getType(), ComplexIntElementIterator(*this, 0),1199 ComplexIntElementIterator(*this, getNumElements()));1200}1201 1202auto DenseElementsAttr::tryGetFloatValues() const1203 -> FailureOr<iterator_range_impl<FloatElementIterator>> {1204 auto eltTy = llvm::dyn_cast<FloatType>(getElementType());1205 if (!eltTy)1206 return failure();1207 const auto &elementSemantics = eltTy.getFloatSemantics();1208 return iterator_range_impl<FloatElementIterator>(1209 getType(), FloatElementIterator(elementSemantics, raw_int_begin()),1210 FloatElementIterator(elementSemantics, raw_int_end()));1211}1212 1213auto DenseElementsAttr::tryGetComplexFloatValues() const1214 -> FailureOr<iterator_range_impl<ComplexFloatElementIterator>> {1215 auto complexTy = llvm::dyn_cast<ComplexType>(getElementType());1216 if (!complexTy)1217 return failure();1218 auto eltTy = llvm::dyn_cast<FloatType>(complexTy.getElementType());1219 if (!eltTy)1220 return failure();1221 const auto &semantics = eltTy.getFloatSemantics();1222 return iterator_range_impl<ComplexFloatElementIterator>(1223 getType(), {semantics, {*this, 0}},1224 {semantics, {*this, static_cast<size_t>(getNumElements())}});1225}1226 1227/// Return the raw storage data held by this attribute.1228ArrayRef<char> DenseElementsAttr::getRawData() const {1229 return static_cast<DenseIntOrFPElementsAttrStorage *>(impl)->data;1230}1231 1232ArrayRef<StringRef> DenseElementsAttr::getRawStringData() const {1233 return static_cast<DenseStringElementsAttrStorage *>(impl)->data;1234}1235 1236/// Return a new DenseElementsAttr that has the same data as the current1237/// attribute, but has been reshaped to 'newType'. The new type must have the1238/// same total number of elements as well as element type.1239DenseElementsAttr DenseElementsAttr::reshape(ShapedType newType) {1240 ShapedType curType = getType();1241 if (curType == newType)1242 return *this;1243 1244 assert(newType.getElementType() == curType.getElementType() &&1245 "expected the same element type");1246 assert(newType.getNumElements() == curType.getNumElements() &&1247 "expected the same number of elements");1248 return DenseIntOrFPElementsAttr::getRaw(newType, getRawData());1249}1250 1251DenseElementsAttr DenseElementsAttr::resizeSplat(ShapedType newType) {1252 assert(isSplat() && "expected a splat type");1253 1254 ShapedType curType = getType();1255 if (curType == newType)1256 return *this;1257 1258 assert(newType.getElementType() == curType.getElementType() &&1259 "expected the same element type");1260 return DenseIntOrFPElementsAttr::getRaw(newType, getRawData());1261}1262 1263/// Return a new DenseElementsAttr that has the same data as the current1264/// attribute, but has bitcast elements such that it is now 'newType'. The new1265/// type must have the same shape and element types of the same bitwidth as the1266/// current type.1267DenseElementsAttr DenseElementsAttr::bitcast(Type newElType) {1268 ShapedType curType = getType();1269 Type curElType = curType.getElementType();1270 if (curElType == newElType)1271 return *this;1272 1273 assert(getDenseElementBitWidth(newElType) ==1274 getDenseElementBitWidth(curElType) &&1275 "expected element types with the same bitwidth");1276 return DenseIntOrFPElementsAttr::getRaw(curType.clone(newElType),1277 getRawData());1278}1279 1280DenseElementsAttr1281DenseElementsAttr::mapValues(Type newElementType,1282 function_ref<APInt(const APInt &)> mapping) const {1283 return llvm::cast<DenseIntElementsAttr>(*this).mapValues(newElementType,1284 mapping);1285}1286 1287DenseElementsAttr DenseElementsAttr::mapValues(1288 Type newElementType, function_ref<APInt(const APFloat &)> mapping) const {1289 return llvm::cast<DenseFPElementsAttr>(*this).mapValues(newElementType,1290 mapping);1291}1292 1293ShapedType DenseElementsAttr::getType() const {1294 return static_cast<const DenseElementsAttributeStorage *>(impl)->type;1295}1296 1297Type DenseElementsAttr::getElementType() const {1298 return getType().getElementType();1299}1300 1301int64_t DenseElementsAttr::getNumElements() const {1302 return getType().getNumElements();1303}1304 1305//===----------------------------------------------------------------------===//1306// DenseIntOrFPElementsAttr1307//===----------------------------------------------------------------------===//1308 1309/// Utility method to write a range of APInt values to a buffer.1310template <typename APRangeT>1311static void writeAPIntsToBuffer(size_t storageWidth,1312 SmallVectorImpl<char> &data,1313 APRangeT &&values) {1314 size_t numValues = llvm::size(values);1315 data.resize(llvm::divideCeil(storageWidth * numValues, CHAR_BIT));1316 size_t offset = 0;1317 for (auto it = values.begin(), e = values.end(); it != e;1318 ++it, offset += storageWidth) {1319 assert((*it).getBitWidth() <= storageWidth);1320 writeBits(data.data(), offset, *it);1321 }1322 1323 // Handle the special encoding of splat of a boolean.1324 if (numValues == 1 && (*values.begin()).getBitWidth() == 1)1325 data[0] = data[0] ? -1 : 0;1326}1327 1328/// Constructs a dense elements attribute from an array of raw APFloat values.1329/// Each APFloat value is expected to have the same bitwidth as the element1330/// type of 'type'. 'type' must be a vector or tensor with static shape.1331DenseElementsAttr DenseIntOrFPElementsAttr::getRaw(ShapedType type,1332 size_t storageWidth,1333 ArrayRef<APFloat> values) {1334 SmallVector<char> data;1335 auto unwrapFloat = [](const APFloat &val) { return val.bitcastToAPInt(); };1336 writeAPIntsToBuffer(storageWidth, data, llvm::map_range(values, unwrapFloat));1337 return DenseIntOrFPElementsAttr::getRaw(type, data);1338}1339 1340/// Constructs a dense elements attribute from an array of raw APInt values.1341/// Each APInt value is expected to have the same bitwidth as the element type1342/// of 'type'.1343DenseElementsAttr DenseIntOrFPElementsAttr::getRaw(ShapedType type,1344 size_t storageWidth,1345 ArrayRef<APInt> values) {1346 SmallVector<char> data;1347 writeAPIntsToBuffer(storageWidth, data, values);1348 return DenseIntOrFPElementsAttr::getRaw(type, data);1349}1350 1351DenseElementsAttr DenseIntOrFPElementsAttr::getRaw(ShapedType type,1352 ArrayRef<char> data) {1353 assert(type.hasStaticShape() && "type must have static shape");1354 bool isSplat = false;1355 bool isValid = isValidRawBuffer(type, data, isSplat);1356 assert(isValid);1357 (void)isValid;1358 return Base::get(type.getContext(), type, data, isSplat);1359}1360 1361/// Overload of the raw 'get' method that asserts that the given type is of1362/// complex type. This method is used to verify type invariants that the1363/// templatized 'get' method cannot.1364DenseElementsAttr DenseIntOrFPElementsAttr::getRawComplex(ShapedType type,1365 ArrayRef<char> data,1366 int64_t dataEltSize,1367 bool isInt,1368 bool isSigned) {1369 assert(::isValidIntOrFloat(1370 llvm::cast<ComplexType>(type.getElementType()).getElementType(),1371 dataEltSize / 2, isInt, isSigned) &&1372 "Try re-running with -debug-only=builtinattributes");1373 1374 int64_t numElements = data.size() / dataEltSize;1375 (void)numElements;1376 assert(numElements == 1 || numElements == type.getNumElements());1377 return getRaw(type, data);1378}1379 1380/// Overload of the 'getRaw' method that asserts that the given type is of1381/// integer type. This method is used to verify type invariants that the1382/// templatized 'get' method cannot.1383DenseElementsAttr1384DenseIntOrFPElementsAttr::getRawIntOrFloat(ShapedType type, ArrayRef<char> data,1385 int64_t dataEltSize, bool isInt,1386 bool isSigned) {1387 assert(::isValidIntOrFloat(type.getElementType(), dataEltSize, isInt,1388 isSigned) &&1389 "Try re-running with -debug-only=builtinattributes");1390 1391 int64_t numElements = data.size() / dataEltSize;1392 assert(numElements == 1 || numElements == type.getNumElements());1393 (void)numElements;1394 return getRaw(type, data);1395}1396 1397void DenseIntOrFPElementsAttr::convertEndianOfCharForBEmachine(1398 const char *inRawData, char *outRawData, size_t elementBitWidth,1399 size_t numElements) {1400 using llvm::support::ulittle16_t;1401 using llvm::support::ulittle32_t;1402 using llvm::support::ulittle64_t;1403 1404 assert(llvm::endianness::native == llvm::endianness::big);1405 // NOLINT to avoid warning message about replacing by static_assert()1406 1407 // Following std::copy_n always converts endianness on BE machine.1408 switch (elementBitWidth) {1409 case 16: {1410 const ulittle16_t *inRawDataPos =1411 reinterpret_cast<const ulittle16_t *>(inRawData);1412 uint16_t *outDataPos = reinterpret_cast<uint16_t *>(outRawData);1413 std::copy_n(inRawDataPos, numElements, outDataPos);1414 break;1415 }1416 case 32: {1417 const ulittle32_t *inRawDataPos =1418 reinterpret_cast<const ulittle32_t *>(inRawData);1419 uint32_t *outDataPos = reinterpret_cast<uint32_t *>(outRawData);1420 std::copy_n(inRawDataPos, numElements, outDataPos);1421 break;1422 }1423 case 64: {1424 const ulittle64_t *inRawDataPos =1425 reinterpret_cast<const ulittle64_t *>(inRawData);1426 uint64_t *outDataPos = reinterpret_cast<uint64_t *>(outRawData);1427 std::copy_n(inRawDataPos, numElements, outDataPos);1428 break;1429 }1430 default: {1431 size_t nBytes = elementBitWidth / CHAR_BIT;1432 for (size_t i = 0; i < nBytes; i++)1433 std::copy_n(inRawData + (nBytes - 1 - i), 1, outRawData + i);1434 break;1435 }1436 }1437}1438 1439void DenseIntOrFPElementsAttr::convertEndianOfArrayRefForBEmachine(1440 ArrayRef<char> inRawData, MutableArrayRef<char> outRawData,1441 ShapedType type) {1442 size_t numElements = type.getNumElements();1443 Type elementType = type.getElementType();1444 if (ComplexType complexTy = llvm::dyn_cast<ComplexType>(elementType)) {1445 elementType = complexTy.getElementType();1446 numElements = numElements * 2;1447 }1448 size_t elementBitWidth = getDenseElementStorageWidth(elementType);1449 assert(numElements * elementBitWidth == inRawData.size() * CHAR_BIT &&1450 inRawData.size() <= outRawData.size());1451 if (elementBitWidth <= CHAR_BIT)1452 std::memcpy(outRawData.begin(), inRawData.begin(), inRawData.size());1453 else1454 convertEndianOfCharForBEmachine(inRawData.begin(), outRawData.begin(),1455 elementBitWidth, numElements);1456}1457 1458//===----------------------------------------------------------------------===//1459// DenseFPElementsAttr1460//===----------------------------------------------------------------------===//1461 1462template <typename Fn, typename Attr>1463static ShapedType mappingHelper(Fn mapping, Attr &attr, ShapedType inType,1464 Type newElementType,1465 llvm::SmallVectorImpl<char> &data) {1466 size_t bitWidth = getDenseElementBitWidth(newElementType);1467 size_t storageBitWidth = getDenseElementStorageWidth(bitWidth);1468 1469 ShapedType newArrayType = inType.cloneWith(inType.getShape(), newElementType);1470 1471 size_t numRawElements = attr.isSplat() ? 1 : newArrayType.getNumElements();1472 data.resize(llvm::divideCeil(storageBitWidth * numRawElements, CHAR_BIT));1473 1474 // Functor used to process a single element value of the attribute.1475 auto processElt = [&](decltype(*attr.begin()) value, size_t index) {1476 auto newInt = mapping(value);1477 assert(newInt.getBitWidth() == bitWidth);1478 writeBits(data.data(), index * storageBitWidth, newInt);1479 };1480 1481 // Check for the splat case.1482 if (attr.isSplat()) {1483 if (bitWidth == 1) {1484 // Handle the special encoding of splat of bool.1485 data[0] = mapping(*attr.begin()).isZero() ? 0 : -1;1486 } else {1487 processElt(*attr.begin(), /*index=*/0);1488 }1489 return newArrayType;1490 }1491 1492 // Otherwise, process all of the element values.1493 uint64_t elementIdx = 0;1494 for (auto value : attr)1495 processElt(value, elementIdx++);1496 return newArrayType;1497}1498 1499DenseElementsAttr DenseFPElementsAttr::mapValues(1500 Type newElementType, function_ref<APInt(const APFloat &)> mapping) const {1501 llvm::SmallVector<char, 8> elementData;1502 auto newArrayType =1503 mappingHelper(mapping, *this, getType(), newElementType, elementData);1504 1505 return getRaw(newArrayType, elementData);1506}1507 1508/// Method for supporting type inquiry through isa, cast and dyn_cast.1509bool DenseFPElementsAttr::classof(Attribute attr) {1510 if (auto denseAttr = llvm::dyn_cast<DenseElementsAttr>(attr))1511 return llvm::isa<FloatType>(denseAttr.getType().getElementType());1512 return false;1513}1514 1515//===----------------------------------------------------------------------===//1516// DenseIntElementsAttr1517//===----------------------------------------------------------------------===//1518 1519DenseElementsAttr DenseIntElementsAttr::mapValues(1520 Type newElementType, function_ref<APInt(const APInt &)> mapping) const {1521 llvm::SmallVector<char, 8> elementData;1522 auto newArrayType =1523 mappingHelper(mapping, *this, getType(), newElementType, elementData);1524 return getRaw(newArrayType, elementData);1525}1526 1527/// Method for supporting type inquiry through isa, cast and dyn_cast.1528bool DenseIntElementsAttr::classof(Attribute attr) {1529 if (auto denseAttr = llvm::dyn_cast<DenseElementsAttr>(attr))1530 return denseAttr.getType().getElementType().isIntOrIndex();1531 return false;1532}1533 1534//===----------------------------------------------------------------------===//1535// DenseResourceElementsAttr1536//===----------------------------------------------------------------------===//1537 1538DenseResourceElementsAttr1539DenseResourceElementsAttr::get(ShapedType type,1540 DenseResourceElementsHandle handle) {1541 return Base::get(type.getContext(), type, handle);1542}1543 1544DenseResourceElementsAttr DenseResourceElementsAttr::get(ShapedType type,1545 StringRef blobName,1546 AsmResourceBlob blob) {1547 // Extract the builtin dialect resource manager from context and construct a1548 // handle by inserting a new resource using the provided blob.1549 auto &manager =1550 DenseResourceElementsHandle::getManagerInterface(type.getContext());1551 return get(type, manager.insert(blobName, std::move(blob)));1552}1553 1554ArrayRef<char> DenseResourceElementsAttr::getData() {1555 if (AsmResourceBlob *blob = this->getRawHandle().getBlob())1556 return blob->getDataAs<char>();1557 return {};1558}1559 1560//===----------------------------------------------------------------------===//1561// DenseResourceElementsAttrBase1562//===----------------------------------------------------------------------===//1563 1564namespace {1565/// Instantiations of this class provide utilities for interacting with native1566/// data types in the context of DenseResourceElementsAttr.1567template <typename T>1568struct DenseResourceAttrUtil;1569template <size_t width, bool isSigned>1570struct DenseResourceElementsAttrIntUtil {1571 static bool checkElementType(Type eltType) {1572 IntegerType type = llvm::dyn_cast<IntegerType>(eltType);1573 if (!type || type.getWidth() != width)1574 return false;1575 return isSigned ? !type.isUnsigned() : !type.isSigned();1576 }1577};1578template <>1579struct DenseResourceAttrUtil<bool> {1580 static bool checkElementType(Type eltType) {1581 return eltType.isSignlessInteger(1);1582 }1583};1584template <>1585struct DenseResourceAttrUtil<int8_t>1586 : public DenseResourceElementsAttrIntUtil<8, true> {};1587template <>1588struct DenseResourceAttrUtil<uint8_t>1589 : public DenseResourceElementsAttrIntUtil<8, false> {};1590template <>1591struct DenseResourceAttrUtil<int16_t>1592 : public DenseResourceElementsAttrIntUtil<16, true> {};1593template <>1594struct DenseResourceAttrUtil<uint16_t>1595 : public DenseResourceElementsAttrIntUtil<16, false> {};1596template <>1597struct DenseResourceAttrUtil<int32_t>1598 : public DenseResourceElementsAttrIntUtil<32, true> {};1599template <>1600struct DenseResourceAttrUtil<uint32_t>1601 : public DenseResourceElementsAttrIntUtil<32, false> {};1602template <>1603struct DenseResourceAttrUtil<int64_t>1604 : public DenseResourceElementsAttrIntUtil<64, true> {};1605template <>1606struct DenseResourceAttrUtil<uint64_t>1607 : public DenseResourceElementsAttrIntUtil<64, false> {};1608template <>1609struct DenseResourceAttrUtil<float> {1610 static bool checkElementType(Type eltType) { return eltType.isF32(); }1611};1612template <>1613struct DenseResourceAttrUtil<double> {1614 static bool checkElementType(Type eltType) { return eltType.isF64(); }1615};1616} // namespace1617 1618template <typename T>1619DenseResourceElementsAttrBase<T>1620DenseResourceElementsAttrBase<T>::get(ShapedType type, StringRef blobName,1621 AsmResourceBlob blob) {1622 // Check that the blob is in the form we were expecting.1623 assert(blob.getDataAlignment() == alignof(T) &&1624 "alignment mismatch between expected alignment and blob alignment");1625 assert(((blob.getData().size() % sizeof(T)) == 0) &&1626 "size mismatch between expected element width and blob size");1627 assert(DenseResourceAttrUtil<T>::checkElementType(type.getElementType()) &&1628 "invalid shape element type for provided type `T`");1629 return llvm::cast<DenseResourceElementsAttrBase<T>>(1630 DenseResourceElementsAttr::get(type, blobName, std::move(blob)));1631}1632 1633template <typename T>1634std::optional<ArrayRef<T>>1635DenseResourceElementsAttrBase<T>::tryGetAsArrayRef() const {1636 if (AsmResourceBlob *blob = this->getRawHandle().getBlob())1637 return blob->template getDataAs<T>();1638 return std::nullopt;1639}1640 1641template <typename T>1642bool DenseResourceElementsAttrBase<T>::classof(Attribute attr) {1643 auto resourceAttr = llvm::dyn_cast<DenseResourceElementsAttr>(attr);1644 return resourceAttr && DenseResourceAttrUtil<T>::checkElementType(1645 resourceAttr.getElementType());1646}1647 1648namespace mlir {1649namespace detail {1650// Explicit instantiation for all the supported DenseResourceElementsAttr.1651template class DenseResourceElementsAttrBase<bool>;1652template class DenseResourceElementsAttrBase<int8_t>;1653template class DenseResourceElementsAttrBase<int16_t>;1654template class DenseResourceElementsAttrBase<int32_t>;1655template class DenseResourceElementsAttrBase<int64_t>;1656template class DenseResourceElementsAttrBase<uint8_t>;1657template class DenseResourceElementsAttrBase<uint16_t>;1658template class DenseResourceElementsAttrBase<uint32_t>;1659template class DenseResourceElementsAttrBase<uint64_t>;1660template class DenseResourceElementsAttrBase<float>;1661template class DenseResourceElementsAttrBase<double>;1662} // namespace detail1663} // namespace mlir1664 1665//===----------------------------------------------------------------------===//1666// SparseElementsAttr1667//===----------------------------------------------------------------------===//1668 1669/// Get a zero APFloat for the given sparse attribute.1670APFloat SparseElementsAttr::getZeroAPFloat() const {1671 auto eltType = llvm::cast<FloatType>(getElementType());1672 return APFloat(eltType.getFloatSemantics());1673}1674 1675/// Get a zero APInt for the given sparse attribute.1676APInt SparseElementsAttr::getZeroAPInt() const {1677 auto eltType = llvm::cast<IntegerType>(getElementType());1678 return APInt::getZero(eltType.getWidth());1679}1680 1681/// Get a zero attribute for the given attribute type.1682Attribute SparseElementsAttr::getZeroAttr() const {1683 auto eltType = getElementType();1684 1685 // Handle floating point elements.1686 if (llvm::isa<FloatType>(eltType))1687 return FloatAttr::get(eltType, 0);1688 1689 // Handle complex elements.1690 if (auto complexTy = llvm::dyn_cast<ComplexType>(eltType)) {1691 auto eltType = complexTy.getElementType();1692 Attribute zero;1693 if (llvm::isa<FloatType>(eltType))1694 zero = FloatAttr::get(eltType, 0);1695 else // must be integer1696 zero = IntegerAttr::get(eltType, 0);1697 return ArrayAttr::get(complexTy.getContext(),1698 ArrayRef<Attribute>{zero, zero});1699 }1700 1701 // Handle string type.1702 if (llvm::isa<DenseStringElementsAttr>(getValues()))1703 return StringAttr::get("", eltType);1704 1705 // Otherwise, this is an integer.1706 return IntegerAttr::get(eltType, 0);1707}1708 1709/// Flatten, and return, all of the sparse indices in this attribute in1710/// row-major order.1711SmallVector<ptrdiff_t> SparseElementsAttr::getFlattenedSparseIndices() const {1712 SmallVector<ptrdiff_t> flatSparseIndices;1713 1714 // The sparse indices are 64-bit integers, so we can reinterpret the raw data1715 // as a 1-D index array.1716 auto sparseIndices = getIndices();1717 auto sparseIndexValues = sparseIndices.getValues<uint64_t>();1718 if (sparseIndices.isSplat()) {1719 SmallVector<uint64_t, 8> indices(getType().getRank(),1720 *sparseIndexValues.begin());1721 flatSparseIndices.push_back(getFlattenedIndex(indices));1722 return flatSparseIndices;1723 }1724 1725 // Otherwise, reinterpret each index as an ArrayRef when flattening.1726 auto numSparseIndices = sparseIndices.getType().getDimSize(0);1727 size_t rank = getType().getRank();1728 for (size_t i = 0, e = numSparseIndices; i != e; ++i)1729 flatSparseIndices.push_back(getFlattenedIndex(1730 {&*std::next(sparseIndexValues.begin(), i * rank), rank}));1731 return flatSparseIndices;1732}1733 1734LogicalResult1735SparseElementsAttr::verify(function_ref<InFlightDiagnostic()> emitError,1736 ShapedType type, DenseIntElementsAttr sparseIndices,1737 DenseElementsAttr values) {1738 ShapedType valuesType = values.getType();1739 if (valuesType.getRank() != 1)1740 return emitError() << "expected 1-d tensor for sparse element values";1741 1742 // Verify the indices and values shape.1743 ShapedType indicesType = sparseIndices.getType();1744 auto emitShapeError = [&]() {1745 return emitError() << "expected shape ([" << type.getShape()1746 << "]); inferred shape of indices literal (["1747 << indicesType.getShape()1748 << "]); inferred shape of values literal (["1749 << valuesType.getShape() << "])";1750 };1751 // Verify indices shape.1752 size_t rank = type.getRank(), indicesRank = indicesType.getRank();1753 if (indicesRank == 2) {1754 if (indicesType.getDimSize(1) != static_cast<int64_t>(rank))1755 return emitShapeError();1756 } else if (indicesRank != 1 || rank != 1) {1757 return emitShapeError();1758 }1759 // Verify the values shape.1760 int64_t numSparseIndices = indicesType.getDimSize(0);1761 if (numSparseIndices != valuesType.getDimSize(0))1762 return emitShapeError();1763 1764 // Verify that the sparse indices are within the value shape.1765 auto emitIndexError = [&](unsigned indexNum, ArrayRef<uint64_t> index) {1766 return emitError()1767 << "sparse index #" << indexNum1768 << " is not contained within the value shape, with index=[" << index1769 << "], and type=" << type;1770 };1771 1772 // Handle the case where the index values are a splat.1773 auto sparseIndexValues = sparseIndices.getValues<uint64_t>();1774 if (sparseIndices.isSplat()) {1775 SmallVector<uint64_t> indices(rank, *sparseIndexValues.begin());1776 if (!ElementsAttr::isValidIndex(type, indices))1777 return emitIndexError(0, indices);1778 return success();1779 }1780 1781 // Otherwise, reinterpret each index as an ArrayRef.1782 for (size_t i = 0, e = numSparseIndices; i != e; ++i) {1783 ArrayRef<uint64_t> index(&*std::next(sparseIndexValues.begin(), i * rank),1784 rank);1785 if (!ElementsAttr::isValidIndex(type, index))1786 return emitIndexError(i, index);1787 }1788 1789 return success();1790}1791 1792//===----------------------------------------------------------------------===//1793// DistinctAttr1794//===----------------------------------------------------------------------===//1795 1796DistinctAttr DistinctAttr::create(Attribute referencedAttr) {1797 return Base::get(referencedAttr.getContext(), referencedAttr);1798}1799 1800Attribute DistinctAttr::getReferencedAttr() const {1801 return getImpl()->referencedAttr;1802}1803 1804//===----------------------------------------------------------------------===//1805// Attribute Utilities1806//===----------------------------------------------------------------------===//1807 1808AffineMap mlir::makeStridedLinearLayoutMap(ArrayRef<int64_t> strides,1809 int64_t offset,1810 MLIRContext *context) {1811 AffineExpr expr;1812 unsigned nSymbols = 0;1813 1814 // AffineExpr for offset.1815 // Static case.1816 if (ShapedType::isStatic(offset)) {1817 auto cst = getAffineConstantExpr(offset, context);1818 expr = cst;1819 } else {1820 // Dynamic case, new symbol for the offset.1821 auto sym = getAffineSymbolExpr(nSymbols++, context);1822 expr = sym;1823 }1824 1825 // AffineExpr for strides.1826 for (const auto &en : llvm::enumerate(strides)) {1827 auto dim = en.index();1828 auto stride = en.value();1829 auto d = getAffineDimExpr(dim, context);1830 AffineExpr mult;1831 // Static case.1832 if (ShapedType::isStatic(stride))1833 mult = getAffineConstantExpr(stride, context);1834 else1835 // Dynamic case, new symbol for each new stride.1836 mult = getAffineSymbolExpr(nSymbols++, context);1837 expr = expr + d * mult;1838 }1839 1840 return AffineMap::get(strides.size(), nSymbols, expr);1841}1842