/home/arjun/llvm-project/mlir/lib/IR/OperationSupport.cpp
| Line | Count | Source (jump to first uncovered line) | 
| 1 |  | //===- OperationSupport.cpp -----------------------------------------------===// | 
| 2 |  | // | 
| 3 |  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
| 4 |  | // See https://llvm.org/LICENSE.txt for license information. | 
| 5 |  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
| 6 |  | // | 
| 7 |  | //===----------------------------------------------------------------------===// | 
| 8 |  | // | 
| 9 |  | // This file contains out-of-line implementations of the support types that | 
| 10 |  | // Operation and related classes build on top of. | 
| 11 |  | // | 
| 12 |  | //===----------------------------------------------------------------------===// | 
| 13 |  |  | 
| 14 |  | #include "mlir/IR/OperationSupport.h" | 
| 15 |  | #include "mlir/IR/Block.h" | 
| 16 |  | #include "mlir/IR/OpDefinition.h" | 
| 17 |  | #include "mlir/IR/Operation.h" | 
| 18 |  | #include "mlir/IR/StandardTypes.h" | 
| 19 |  | using namespace mlir; | 
| 20 |  |  | 
| 21 |  | //===----------------------------------------------------------------------===// | 
| 22 |  | // NamedAttrList | 
| 23 |  | //===----------------------------------------------------------------------===// | 
| 24 |  |  | 
| 25 | 0 | NamedAttrList::NamedAttrList(ArrayRef<NamedAttribute> attributes) { | 
| 26 | 0 |   assign(attributes.begin(), attributes.end()); | 
| 27 | 0 | } | 
| 28 |  |  | 
| 29 | 0 | NamedAttrList::NamedAttrList(const_iterator in_start, const_iterator in_end) { | 
| 30 | 0 |   assign(in_start, in_end); | 
| 31 | 0 | } | 
| 32 |  |  | 
| 33 | 0 | ArrayRef<NamedAttribute> NamedAttrList::getAttrs() const { return attrs; } | 
| 34 |  |  | 
| 35 | 0 | DictionaryAttr NamedAttrList::getDictionary(MLIRContext *context) const { | 
| 36 | 0 |   if (!isSorted()) { | 
| 37 | 0 |     DictionaryAttr::sortInPlace(attrs); | 
| 38 | 0 |     dictionarySorted.setPointerAndInt(nullptr, true); | 
| 39 | 0 |   } | 
| 40 | 0 |   if (!dictionarySorted.getPointer()) | 
| 41 | 0 |     dictionarySorted.setPointer(DictionaryAttr::getWithSorted(attrs, context)); | 
| 42 | 0 |   return dictionarySorted.getPointer().cast<DictionaryAttr>(); | 
| 43 | 0 | } | 
| 44 |  |  | 
| 45 | 0 | NamedAttrList::operator MutableDictionaryAttr() const { | 
| 46 | 0 |   if (attrs.empty()) | 
| 47 | 0 |     return MutableDictionaryAttr(); | 
| 48 | 0 |   return getDictionary(attrs.front().second.getContext()); | 
| 49 | 0 | } | 
| 50 |  |  | 
| 51 |  | /// Add an attribute with the specified name. | 
| 52 | 0 | void NamedAttrList::append(StringRef name, Attribute attr) { | 
| 53 | 0 |   append(Identifier::get(name, attr.getContext()), attr); | 
| 54 | 0 | } | 
| 55 |  |  | 
| 56 |  | /// Add an attribute with the specified name. | 
| 57 | 0 | void NamedAttrList::append(Identifier name, Attribute attr) { | 
| 58 | 0 |   push_back({name, attr}); | 
| 59 | 0 | } | 
| 60 |  |  | 
| 61 |  | /// Add an array of named attributes. | 
| 62 | 0 | void NamedAttrList::append(ArrayRef<NamedAttribute> newAttributes) { | 
| 63 | 0 |   append(newAttributes.begin(), newAttributes.end()); | 
| 64 | 0 | } | 
| 65 |  |  | 
| 66 |  | /// Add a range of named attributes. | 
| 67 | 0 | void NamedAttrList::append(const_iterator in_start, const_iterator in_end) { | 
| 68 | 0 |   // TODO: expand to handle case where values appended are in order & after | 
| 69 | 0 |   // end of current list. | 
| 70 | 0 |   dictionarySorted.setPointerAndInt(nullptr, false); | 
| 71 | 0 |   attrs.append(in_start, in_end); | 
| 72 | 0 | } | 
| 73 |  |  | 
| 74 |  | /// Replaces the attributes with new list of attributes. | 
| 75 | 0 | void NamedAttrList::assign(const_iterator in_start, const_iterator in_end) { | 
| 76 | 0 |   DictionaryAttr::sort(ArrayRef<NamedAttribute>{in_start, in_end}, attrs); | 
| 77 | 0 |   dictionarySorted.setPointerAndInt(nullptr, true); | 
| 78 | 0 | } | 
| 79 |  |  | 
| 80 | 0 | void NamedAttrList::push_back(NamedAttribute newAttribute) { | 
| 81 | 0 |   if (isSorted()) | 
| 82 | 0 |     dictionarySorted.setInt( | 
| 83 | 0 |         attrs.empty() || | 
| 84 | 0 |         strcmp(attrs.back().first.data(), newAttribute.first.data()) < 0); | 
| 85 | 0 |   dictionarySorted.setPointer(nullptr); | 
| 86 | 0 |   attrs.push_back(newAttribute); | 
| 87 | 0 | } | 
| 88 |  |  | 
| 89 |  | /// Helper function to find attribute in possible sorted vector of | 
| 90 |  | /// NamedAttributes. | 
| 91 |  | template <typename T> | 
| 92 |  | static auto *findAttr(SmallVectorImpl<NamedAttribute> &attrs, T name, | 
| 93 | 0 |                       bool sorted) { | 
| 94 | 0 |   if (!sorted) { | 
| 95 | 0 |     return llvm::find_if( | 
| 96 | 0 |         attrs, [name](NamedAttribute attr) { return attr.first == name; });Unexecuted instantiation: OperationSupport.cpp:_ZZL8findAttrIN4llvm9StringRefEEPDaRNS0_15SmallVectorImplISt4pairIN4mlir10IdentifierENS5_9AttributeEEEET_bENKUlS8_E_clES8_Unexecuted instantiation: OperationSupport.cpp:_ZZL8findAttrIN4mlir10IdentifierEEPDaRN4llvm15SmallVectorImplISt4pairIS1_NS0_9AttributeEEEET_bENKUlS7_E_clES7_ | 
| 97 | 0 |   } | 
| 98 | 0 | 
 | 
| 99 | 0 |   auto *it = llvm::lower_bound(attrs, name); | 
| 100 | 0 |   if (it == attrs.end() || it->first != name) | 
| 101 | 0 |     return attrs.end(); | 
| 102 | 0 |   return it; | 
| 103 | 0 | } Unexecuted instantiation: OperationSupport.cpp:_ZL8findAttrIN4llvm9StringRefEEPDaRNS0_15SmallVectorImplISt4pairIN4mlir10IdentifierENS5_9AttributeEEEET_bUnexecuted instantiation: OperationSupport.cpp:_ZL8findAttrIN4mlir10IdentifierEEPDaRN4llvm15SmallVectorImplISt4pairIS1_NS0_9AttributeEEEET_b | 
| 104 |  |  | 
| 105 |  | /// Return the specified attribute if present, null otherwise. | 
| 106 | 0 | Attribute NamedAttrList::get(StringRef name) const { | 
| 107 | 0 |   auto *it = findAttr(attrs, name, isSorted()); | 
| 108 | 0 |   return it != attrs.end() ? it->second : nullptr; | 
| 109 | 0 | } | 
| 110 |  |  | 
| 111 |  | /// Return the specified attribute if present, null otherwise. | 
| 112 | 0 | Attribute NamedAttrList::get(Identifier name) const { | 
| 113 | 0 |   auto *it = findAttr(attrs, name, isSorted()); | 
| 114 | 0 |   return it != attrs.end() ? it->second : nullptr; | 
| 115 | 0 | } | 
| 116 |  |  | 
| 117 |  | /// Return the specified named attribute if present, None otherwise. | 
| 118 | 0 | Optional<NamedAttribute> NamedAttrList::getNamed(StringRef name) const { | 
| 119 | 0 |   auto *it = findAttr(attrs, name, isSorted()); | 
| 120 | 0 |   return it != attrs.end() ? *it : Optional<NamedAttribute>(); | 
| 121 | 0 | } | 
| 122 | 0 | Optional<NamedAttribute> NamedAttrList::getNamed(Identifier name) const { | 
| 123 | 0 |   auto *it = findAttr(attrs, name, isSorted()); | 
| 124 | 0 |   return it != attrs.end() ? *it : Optional<NamedAttribute>(); | 
| 125 | 0 | } | 
| 126 |  |  | 
| 127 |  | /// If the an attribute exists with the specified name, change it to the new | 
| 128 |  | /// value.  Otherwise, add a new attribute with the specified name/value. | 
| 129 | 0 | void NamedAttrList::set(Identifier name, Attribute value) { | 
| 130 | 0 |   assert(value && "attributes may never be null"); | 
| 131 | 0 | 
 | 
| 132 | 0 |   // Look for an existing value for the given name, and set it in-place. | 
| 133 | 0 |   auto *it = findAttr(attrs, name, isSorted()); | 
| 134 | 0 |   if (it != attrs.end()) { | 
| 135 | 0 |     // Bail out early if the value is the same as what we already have. | 
| 136 | 0 |     if (it->second == value) | 
| 137 | 0 |       return; | 
| 138 | 0 |     dictionarySorted.setPointer(nullptr); | 
| 139 | 0 |     it->second = value; | 
| 140 | 0 |     return; | 
| 141 | 0 |   } | 
| 142 | 0 |  | 
| 143 | 0 |   // Otherwise, insert the new attribute into its sorted position. | 
| 144 | 0 |   it = llvm::lower_bound(attrs, name); | 
| 145 | 0 |   dictionarySorted.setPointer(nullptr); | 
| 146 | 0 |   attrs.insert(it, {name, value}); | 
| 147 | 0 | } | 
| 148 | 0 | void NamedAttrList::set(StringRef name, Attribute value) { | 
| 149 | 0 |   assert(value && "setting null attribute not supported"); | 
| 150 | 0 |   return set(mlir::Identifier::get(name, value.getContext()), value); | 
| 151 | 0 | } | 
| 152 |  |  | 
| 153 |  | NamedAttrList & | 
| 154 | 0 | NamedAttrList::operator=(const SmallVectorImpl<NamedAttribute> &rhs) { | 
| 155 | 0 |   assign(rhs.begin(), rhs.end()); | 
| 156 | 0 |   return *this; | 
| 157 | 0 | } | 
| 158 |  |  | 
| 159 | 0 | NamedAttrList::operator ArrayRef<NamedAttribute>() const { return attrs; } | 
| 160 |  |  | 
| 161 |  | //===----------------------------------------------------------------------===// | 
| 162 |  | // OperationState | 
| 163 |  | //===----------------------------------------------------------------------===// | 
| 164 |  |  | 
| 165 |  | OperationState::OperationState(Location location, StringRef name) | 
| 166 | 0 |     : location(location), name(name, location->getContext()) {} | 
| 167 |  |  | 
| 168 |  | OperationState::OperationState(Location location, OperationName name) | 
| 169 | 0 |     : location(location), name(name) {} | 
| 170 |  |  | 
| 171 |  | OperationState::OperationState(Location location, StringRef name, | 
| 172 |  |                                ValueRange operands, ArrayRef<Type> types, | 
| 173 |  |                                ArrayRef<NamedAttribute> attributes, | 
| 174 |  |                                ArrayRef<Block *> successors, | 
| 175 |  |                                MutableArrayRef<std::unique_ptr<Region>> regions) | 
| 176 |  |     : location(location), name(name, location->getContext()), | 
| 177 |  |       operands(operands.begin(), operands.end()), | 
| 178 |  |       types(types.begin(), types.end()), | 
| 179 |  |       attributes(attributes.begin(), attributes.end()), | 
| 180 | 0 |       successors(successors.begin(), successors.end()) { | 
| 181 | 0 |   for (std::unique_ptr<Region> &r : regions) | 
| 182 | 0 |     this->regions.push_back(std::move(r)); | 
| 183 | 0 | } | 
| 184 |  |  | 
| 185 | 0 | void OperationState::addOperands(ValueRange newOperands) { | 
| 186 | 0 |   operands.append(newOperands.begin(), newOperands.end()); | 
| 187 | 0 | } | 
| 188 |  |  | 
| 189 | 0 | void OperationState::addSuccessors(SuccessorRange newSuccessors) { | 
| 190 | 0 |   successors.append(newSuccessors.begin(), newSuccessors.end()); | 
| 191 | 0 | } | 
| 192 |  |  | 
| 193 | 0 | Region *OperationState::addRegion() { | 
| 194 | 0 |   regions.emplace_back(new Region); | 
| 195 | 0 |   return regions.back().get(); | 
| 196 | 0 | } | 
| 197 |  |  | 
| 198 | 0 | void OperationState::addRegion(std::unique_ptr<Region> &®ion) { | 
| 199 | 0 |   regions.push_back(std::move(region)); | 
| 200 | 0 | } | 
| 201 |  |  | 
| 202 |  | //===----------------------------------------------------------------------===// | 
| 203 |  | // OperandStorage | 
| 204 |  | //===----------------------------------------------------------------------===// | 
| 205 |  |  | 
| 206 |  | detail::OperandStorage::OperandStorage(Operation *owner, ValueRange values) | 
| 207 | 0 |     : representation(0) { | 
| 208 | 0 |   auto &inlineStorage = getInlineStorage(); | 
| 209 | 0 |   inlineStorage.numOperands = inlineStorage.capacity = values.size(); | 
| 210 | 0 |   auto *operandPtrBegin = getTrailingObjects<OpOperand>(); | 
| 211 | 0 |   for (unsigned i = 0, e = inlineStorage.numOperands; i < e; ++i) | 
| 212 | 0 |     new (&operandPtrBegin[i]) OpOperand(owner, values[i]); | 
| 213 | 0 | } | 
| 214 |  |  | 
| 215 | 0 | detail::OperandStorage::~OperandStorage() { | 
| 216 | 0 |   // Destruct the current storage container. | 
| 217 | 0 |   if (isDynamicStorage()) { | 
| 218 | 0 |     TrailingOperandStorage &storage = getDynamicStorage(); | 
| 219 | 0 |     storage.~TrailingOperandStorage(); | 
| 220 | 0 |     free(&storage); | 
| 221 | 0 |   } else { | 
| 222 | 0 |     getInlineStorage().~TrailingOperandStorage(); | 
| 223 | 0 |   } | 
| 224 | 0 | } | 
| 225 |  |  | 
| 226 |  | /// Replace the operands contained in the storage with the ones provided in | 
| 227 |  | /// 'values'. | 
| 228 | 0 | void detail::OperandStorage::setOperands(Operation *owner, ValueRange values) { | 
| 229 | 0 |   MutableArrayRef<OpOperand> storageOperands = resize(owner, values.size()); | 
| 230 | 0 |   for (unsigned i = 0, e = values.size(); i != e; ++i) | 
| 231 | 0 |     storageOperands[i].set(values[i]); | 
| 232 | 0 | } | 
| 233 |  |  | 
| 234 |  | /// Replace the operands beginning at 'start' and ending at 'start' + 'length' | 
| 235 |  | /// with the ones provided in 'operands'. 'operands' may be smaller or larger | 
| 236 |  | /// than the range pointed to by 'start'+'length'. | 
| 237 |  | void detail::OperandStorage::setOperands(Operation *owner, unsigned start, | 
| 238 | 0 |                                          unsigned length, ValueRange operands) { | 
| 239 | 0 |   // If the new size is the same, we can update inplace. | 
| 240 | 0 |   unsigned newSize = operands.size(); | 
| 241 | 0 |   if (newSize == length) { | 
| 242 | 0 |     MutableArrayRef<OpOperand> storageOperands = getOperands(); | 
| 243 | 0 |     for (unsigned i = 0, e = length; i != e; ++i) | 
| 244 | 0 |       storageOperands[start + i].set(operands[i]); | 
| 245 | 0 |     return; | 
| 246 | 0 |   } | 
| 247 | 0 |   // If the new size is greater, remove the extra operands and set the rest | 
| 248 | 0 |   // inplace. | 
| 249 | 0 |   if (newSize < length) { | 
| 250 | 0 |     eraseOperands(start + operands.size(), length - newSize); | 
| 251 | 0 |     setOperands(owner, start, newSize, operands); | 
| 252 | 0 |     return; | 
| 253 | 0 |   } | 
| 254 | 0 |   // Otherwise, the new size is greater so we need to grow the storage. | 
| 255 | 0 |   auto storageOperands = resize(owner, size() + (newSize - length)); | 
| 256 | 0 | 
 | 
| 257 | 0 |   // Shift operands to the right to make space for the new operands. | 
| 258 | 0 |   unsigned rotateSize = storageOperands.size() - (start + length); | 
| 259 | 0 |   auto rbegin = storageOperands.rbegin(); | 
| 260 | 0 |   std::rotate(rbegin, std::next(rbegin, newSize - length), rbegin + rotateSize); | 
| 261 | 0 | 
 | 
| 262 | 0 |   // Update the operands inplace. | 
| 263 | 0 |   for (unsigned i = 0, e = operands.size(); i != e; ++i) | 
| 264 | 0 |     storageOperands[start + i].set(operands[i]); | 
| 265 | 0 | } | 
| 266 |  |  | 
| 267 |  | /// Erase an operand held by the storage. | 
| 268 | 0 | void detail::OperandStorage::eraseOperands(unsigned start, unsigned length) { | 
| 269 | 0 |   TrailingOperandStorage &storage = getStorage(); | 
| 270 | 0 |   MutableArrayRef<OpOperand> operands = storage.getOperands(); | 
| 271 | 0 |   assert((start + length) <= operands.size()); | 
| 272 | 0 |   storage.numOperands -= length; | 
| 273 | 0 | 
 | 
| 274 | 0 |   // Shift all operands down if the operand to remove is not at the end. | 
| 275 | 0 |   if (start != storage.numOperands) { | 
| 276 | 0 |     auto *indexIt = std::next(operands.begin(), start); | 
| 277 | 0 |     std::rotate(indexIt, std::next(indexIt, length), operands.end()); | 
| 278 | 0 |   } | 
| 279 | 0 |   for (unsigned i = 0; i != length; ++i) | 
| 280 | 0 |     operands[storage.numOperands + i].~OpOperand(); | 
| 281 | 0 | } | 
| 282 |  |  | 
| 283 |  | /// Resize the storage to the given size. Returns the array containing the new | 
| 284 |  | /// operands. | 
| 285 |  | MutableArrayRef<OpOperand> detail::OperandStorage::resize(Operation *owner, | 
| 286 | 0 |                                                           unsigned newSize) { | 
| 287 | 0 |   TrailingOperandStorage &storage = getStorage(); | 
| 288 | 0 | 
 | 
| 289 | 0 |   // If the number of operands is less than or equal to the current amount, we | 
| 290 | 0 |   // can just update in place. | 
| 291 | 0 |   unsigned &numOperands = storage.numOperands; | 
| 292 | 0 |   MutableArrayRef<OpOperand> operands = storage.getOperands(); | 
| 293 | 0 |   if (newSize <= numOperands) { | 
| 294 | 0 |     // If the number of new size is less than the current, remove any extra | 
| 295 | 0 |     // operands. | 
| 296 | 0 |     for (unsigned i = newSize; i != numOperands; ++i) | 
| 297 | 0 |       operands[i].~OpOperand(); | 
| 298 | 0 |     numOperands = newSize; | 
| 299 | 0 |     return operands.take_front(newSize); | 
| 300 | 0 |   } | 
| 301 | 0 | 
 | 
| 302 | 0 |   // If the new size is within the original inline capacity, grow inplace. | 
| 303 | 0 |   if (newSize <= storage.capacity) { | 
| 304 | 0 |     OpOperand *opBegin = operands.data(); | 
| 305 | 0 |     for (unsigned e = newSize; numOperands != e; ++numOperands) | 
| 306 | 0 |       new (&opBegin[numOperands]) OpOperand(owner); | 
| 307 | 0 |     return MutableArrayRef<OpOperand>(opBegin, newSize); | 
| 308 | 0 |   } | 
| 309 | 0 | 
 | 
| 310 | 0 |   // Otherwise, we need to allocate a new storage. | 
| 311 | 0 |   unsigned newCapacity = | 
| 312 | 0 |       std::max(unsigned(llvm::NextPowerOf2(storage.capacity + 2)), newSize); | 
| 313 | 0 |   auto *newStorageMem = | 
| 314 | 0 |       malloc(TrailingOperandStorage::totalSizeToAlloc<OpOperand>(newCapacity)); | 
| 315 | 0 |   auto *newStorage = ::new (newStorageMem) TrailingOperandStorage(); | 
| 316 | 0 |   newStorage->numOperands = newSize; | 
| 317 | 0 |   newStorage->capacity = newCapacity; | 
| 318 | 0 | 
 | 
| 319 | 0 |   // Move the current operands to the new storage. | 
| 320 | 0 |   MutableArrayRef<OpOperand> newOperands = newStorage->getOperands(); | 
| 321 | 0 |   std::uninitialized_copy(std::make_move_iterator(operands.begin()), | 
| 322 | 0 |                           std::make_move_iterator(operands.end()), | 
| 323 | 0 |                           newOperands.begin()); | 
| 324 | 0 | 
 | 
| 325 | 0 |   // Destroy the original operands. | 
| 326 | 0 |   for (auto &operand : operands) | 
| 327 | 0 |     operand.~OpOperand(); | 
| 328 | 0 | 
 | 
| 329 | 0 |   // Initialize any new operands. | 
| 330 | 0 |   for (unsigned e = newSize; numOperands != e; ++numOperands) | 
| 331 | 0 |     new (&newOperands[numOperands]) OpOperand(owner); | 
| 332 | 0 | 
 | 
| 333 | 0 |   // If the current storage is also dynamic, free it. | 
| 334 | 0 |   if (isDynamicStorage()) | 
| 335 | 0 |     free(&storage); | 
| 336 | 0 | 
 | 
| 337 | 0 |   // Update the storage representation to use the new dynamic storage. | 
| 338 | 0 |   representation = reinterpret_cast<intptr_t>(newStorage); | 
| 339 | 0 |   representation |= DynamicStorageBit; | 
| 340 | 0 |   return newOperands; | 
| 341 | 0 | } | 
| 342 |  |  | 
| 343 |  | //===----------------------------------------------------------------------===// | 
| 344 |  | // ResultStorage | 
| 345 |  | //===----------------------------------------------------------------------===// | 
| 346 |  |  | 
| 347 |  | /// Returns the parent operation of this trailing result. | 
| 348 | 0 | Operation *detail::TrailingOpResult::getOwner() { | 
| 349 | 0 |   // We need to do some arithmetic to get the operation pointer. Move the | 
| 350 | 0 |   // trailing owner to the start of the array. | 
| 351 | 0 |   TrailingOpResult *trailingIt = this - trailingResultNumber; | 
| 352 | 0 | 
 | 
| 353 | 0 |   // Move the owner past the inline op results to get to the operation. | 
| 354 | 0 |   auto *inlineResultIt = reinterpret_cast<InLineOpResult *>(trailingIt) - | 
| 355 | 0 |                          OpResult::getMaxInlineResults(); | 
| 356 | 0 |   return reinterpret_cast<Operation *>(inlineResultIt) - 1; | 
| 357 | 0 | } | 
| 358 |  |  | 
| 359 |  | //===----------------------------------------------------------------------===// | 
| 360 |  | // Operation Value-Iterators | 
| 361 |  | //===----------------------------------------------------------------------===// | 
| 362 |  |  | 
| 363 |  | //===----------------------------------------------------------------------===// | 
| 364 |  | // TypeRange | 
| 365 |  |  | 
| 366 |  | TypeRange::TypeRange(ArrayRef<Type> types) | 
| 367 | 0 |     : TypeRange(types.data(), types.size()) {} | 
| 368 |  | TypeRange::TypeRange(OperandRange values) | 
| 369 | 0 |     : TypeRange(values.begin().getBase(), values.size()) {} | 
| 370 |  | TypeRange::TypeRange(ResultRange values) | 
| 371 |  |     : TypeRange(values.getBase()->getResultTypes().slice(values.getStartIndex(), | 
| 372 | 0 |                                                          values.size())) {} | 
| 373 |  | TypeRange::TypeRange(ArrayRef<Value> values) | 
| 374 | 0 |     : TypeRange(values.data(), values.size()) {} | 
| 375 | 0 | TypeRange::TypeRange(ValueRange values) : TypeRange(OwnerT(), values.size()) { | 
| 376 | 0 |   detail::ValueRangeOwner owner = values.begin().getBase(); | 
| 377 | 0 |   if (auto *op = reinterpret_cast<Operation *>(owner.ptr.dyn_cast<void *>())) | 
| 378 | 0 |     this->base = op->getResultTypes().drop_front(owner.startIndex).data(); | 
| 379 | 0 |   else if (auto *operand = owner.ptr.dyn_cast<OpOperand *>()) | 
| 380 | 0 |     this->base = operand; | 
| 381 | 0 |   else | 
| 382 | 0 |     this->base = owner.ptr.get<const Value *>(); | 
| 383 | 0 | } | 
| 384 |  |  | 
| 385 |  | /// See `llvm::detail::indexed_accessor_range_base` for details. | 
| 386 | 0 | TypeRange::OwnerT TypeRange::offset_base(OwnerT object, ptrdiff_t index) { | 
| 387 | 0 |   if (auto *value = object.dyn_cast<const Value *>()) | 
| 388 | 0 |     return {value + index}; | 
| 389 | 0 |   if (auto *operand = object.dyn_cast<OpOperand *>()) | 
| 390 | 0 |     return {operand + index}; | 
| 391 | 0 |   return {object.dyn_cast<const Type *>() + index}; | 
| 392 | 0 | } | 
| 393 |  | /// See `llvm::detail::indexed_accessor_range_base` for details. | 
| 394 | 0 | Type TypeRange::dereference_iterator(OwnerT object, ptrdiff_t index) { | 
| 395 | 0 |   if (auto *value = object.dyn_cast<const Value *>()) | 
| 396 | 0 |     return (value + index)->getType(); | 
| 397 | 0 |   if (auto *operand = object.dyn_cast<OpOperand *>()) | 
| 398 | 0 |     return (operand + index)->get().getType(); | 
| 399 | 0 |   return object.dyn_cast<const Type *>()[index]; | 
| 400 | 0 | } | 
| 401 |  |  | 
| 402 |  | //===----------------------------------------------------------------------===// | 
| 403 |  | // OperandRange | 
| 404 |  |  | 
| 405 |  | OperandRange::OperandRange(Operation *op) | 
| 406 | 0 |     : OperandRange(op->getOpOperands().data(), op->getNumOperands()) {} | 
| 407 |  |  | 
| 408 |  | /// Return the operand index of the first element of this range. The range | 
| 409 |  | /// must not be empty. | 
| 410 | 0 | unsigned OperandRange::getBeginOperandIndex() const { | 
| 411 | 0 |   assert(!empty() && "range must not be empty"); | 
| 412 | 0 |   return base->getOperandNumber(); | 
| 413 | 0 | } | 
| 414 |  |  | 
| 415 |  | //===----------------------------------------------------------------------===// | 
| 416 |  | // MutableOperandRange | 
| 417 |  |  | 
| 418 |  | /// Construct a new mutable range from the given operand, operand start index, | 
| 419 |  | /// and range length. | 
| 420 |  | MutableOperandRange::MutableOperandRange( | 
| 421 |  |     Operation *owner, unsigned start, unsigned length, | 
| 422 |  |     ArrayRef<OperandSegment> operandSegments) | 
| 423 |  |     : owner(owner), start(start), length(length), | 
| 424 | 0 |       operandSegments(operandSegments.begin(), operandSegments.end()) { | 
| 425 | 0 |   assert((start + length) <= owner->getNumOperands() && "invalid range"); | 
| 426 | 0 | } | 
| 427 |  | MutableOperandRange::MutableOperandRange(Operation *owner) | 
| 428 | 0 |     : MutableOperandRange(owner, /*start=*/0, owner->getNumOperands()) {} | 
| 429 |  |  | 
| 430 |  | /// Slice this range into a sub range, with the additional operand segment. | 
| 431 |  | MutableOperandRange | 
| 432 |  | MutableOperandRange::slice(unsigned subStart, unsigned subLen, | 
| 433 | 0 |                            Optional<OperandSegment> segment) { | 
| 434 | 0 |   assert((subStart + subLen) <= length && "invalid sub-range"); | 
| 435 | 0 |   MutableOperandRange subSlice(owner, start + subStart, subLen, | 
| 436 | 0 |                                operandSegments); | 
| 437 | 0 |   if (segment) | 
| 438 | 0 |     subSlice.operandSegments.push_back(*segment); | 
| 439 | 0 |   return subSlice; | 
| 440 | 0 | } | 
| 441 |  |  | 
| 442 |  | /// Append the given values to the range. | 
| 443 | 0 | void MutableOperandRange::append(ValueRange values) { | 
| 444 | 0 |   if (values.empty()) | 
| 445 | 0 |     return; | 
| 446 | 0 |   owner->insertOperands(start + length, values); | 
| 447 | 0 |   updateLength(length + values.size()); | 
| 448 | 0 | } | 
| 449 |  |  | 
| 450 |  | /// Assign this range to the given values. | 
| 451 | 0 | void MutableOperandRange::assign(ValueRange values) { | 
| 452 | 0 |   owner->setOperands(start, length, values); | 
| 453 | 0 |   if (length != values.size()) | 
| 454 | 0 |     updateLength(/*newLength=*/values.size()); | 
| 455 | 0 | } | 
| 456 |  |  | 
| 457 |  | /// Assign the range to the given value. | 
| 458 | 0 | void MutableOperandRange::assign(Value value) { | 
| 459 | 0 |   if (length == 1) { | 
| 460 | 0 |     owner->setOperand(start, value); | 
| 461 | 0 |   } else { | 
| 462 | 0 |     owner->setOperands(start, length, value); | 
| 463 | 0 |     updateLength(/*newLength=*/1); | 
| 464 | 0 |   } | 
| 465 | 0 | } | 
| 466 |  |  | 
| 467 |  | /// Erase the operands within the given sub-range. | 
| 468 | 0 | void MutableOperandRange::erase(unsigned subStart, unsigned subLen) { | 
| 469 | 0 |   assert((subStart + subLen) <= length && "invalid sub-range"); | 
| 470 | 0 |   if (length == 0) | 
| 471 | 0 |     return; | 
| 472 | 0 |   owner->eraseOperands(start + subStart, subLen); | 
| 473 | 0 |   updateLength(length - subLen); | 
| 474 | 0 | } | 
| 475 |  |  | 
| 476 |  | /// Clear this range and erase all of the operands. | 
| 477 | 0 | void MutableOperandRange::clear() { | 
| 478 | 0 |   if (length != 0) { | 
| 479 | 0 |     owner->eraseOperands(start, length); | 
| 480 | 0 |     updateLength(/*newLength=*/0); | 
| 481 | 0 |   } | 
| 482 | 0 | } | 
| 483 |  |  | 
| 484 |  | /// Allow implicit conversion to an OperandRange. | 
| 485 | 0 | MutableOperandRange::operator OperandRange() const { | 
| 486 | 0 |   return owner->getOperands().slice(start, length); | 
| 487 | 0 | } | 
| 488 |  |  | 
| 489 |  | /// Update the length of this range to the one provided. | 
| 490 | 0 | void MutableOperandRange::updateLength(unsigned newLength) { | 
| 491 | 0 |   int32_t diff = int32_t(newLength) - int32_t(length); | 
| 492 | 0 |   length = newLength; | 
| 493 | 0 | 
 | 
| 494 | 0 |   // Update any of the provided segment attributes. | 
| 495 | 0 |   for (OperandSegment &segment : operandSegments) { | 
| 496 | 0 |     auto attr = segment.second.second.cast<DenseIntElementsAttr>(); | 
| 497 | 0 |     SmallVector<int32_t, 8> segments(attr.getValues<int32_t>()); | 
| 498 | 0 |     segments[segment.first] += diff; | 
| 499 | 0 |     segment.second.second = DenseIntElementsAttr::get(attr.getType(), segments); | 
| 500 | 0 |     owner->setAttr(segment.second.first, segment.second.second); | 
| 501 | 0 |   } | 
| 502 | 0 | } | 
| 503 |  |  | 
| 504 |  | //===----------------------------------------------------------------------===// | 
| 505 |  | // ResultRange | 
| 506 |  |  | 
| 507 |  | ResultRange::ResultRange(Operation *op) | 
| 508 | 0 |     : ResultRange(op, /*startIndex=*/0, op->getNumResults()) {} | 
| 509 |  |  | 
| 510 | 0 | ArrayRef<Type> ResultRange::getTypes() const { | 
| 511 | 0 |   return getBase()->getResultTypes().slice(getStartIndex(), size()); | 
| 512 | 0 | } | 
| 513 |  |  | 
| 514 |  | /// See `llvm::indexed_accessor_range` for details. | 
| 515 | 0 | OpResult ResultRange::dereference(Operation *op, ptrdiff_t index) { | 
| 516 | 0 |   return op->getResult(index); | 
| 517 | 0 | } | 
| 518 |  |  | 
| 519 |  | //===----------------------------------------------------------------------===// | 
| 520 |  | // ValueRange | 
| 521 |  |  | 
| 522 |  | ValueRange::ValueRange(ArrayRef<Value> values) | 
| 523 | 0 |     : ValueRange(values.data(), values.size()) {} | 
| 524 |  | ValueRange::ValueRange(OperandRange values) | 
| 525 | 0 |     : ValueRange(values.begin().getBase(), values.size()) {} | 
| 526 |  | ValueRange::ValueRange(ResultRange values) | 
| 527 |  |     : ValueRange( | 
| 528 |  |           {values.getBase(), static_cast<unsigned>(values.getStartIndex())}, | 
| 529 | 0 |           values.size()) {} | 
| 530 |  |  | 
| 531 |  | /// See `llvm::detail::indexed_accessor_range_base` for details. | 
| 532 |  | ValueRange::OwnerT ValueRange::offset_base(const OwnerT &owner, | 
| 533 | 0 |                                            ptrdiff_t index) { | 
| 534 | 0 |   if (auto *value = owner.ptr.dyn_cast<const Value *>()) | 
| 535 | 0 |     return {value + index}; | 
| 536 | 0 |   if (auto *operand = owner.ptr.dyn_cast<OpOperand *>()) | 
| 537 | 0 |     return {operand + index}; | 
| 538 | 0 |   Operation *operation = reinterpret_cast<Operation *>(owner.ptr.get<void *>()); | 
| 539 | 0 |   return {operation, owner.startIndex + static_cast<unsigned>(index)}; | 
| 540 | 0 | } | 
| 541 |  | /// See `llvm::detail::indexed_accessor_range_base` for details. | 
| 542 | 0 | Value ValueRange::dereference_iterator(const OwnerT &owner, ptrdiff_t index) { | 
| 543 | 0 |   if (auto *value = owner.ptr.dyn_cast<const Value *>()) | 
| 544 | 0 |     return value[index]; | 
| 545 | 0 |   if (auto *operand = owner.ptr.dyn_cast<OpOperand *>()) | 
| 546 | 0 |     return operand[index].get(); | 
| 547 | 0 |   Operation *operation = reinterpret_cast<Operation *>(owner.ptr.get<void *>()); | 
| 548 | 0 |   return operation->getResult(owner.startIndex + index); | 
| 549 | 0 | } | 
| 550 |  |  | 
| 551 |  | //===----------------------------------------------------------------------===// | 
| 552 |  | // Operation Equivalency | 
| 553 |  | //===----------------------------------------------------------------------===// | 
| 554 |  |  | 
| 555 | 0 | llvm::hash_code OperationEquivalence::computeHash(Operation *op, Flags flags) { | 
| 556 | 0 |   // Hash operations based upon their: | 
| 557 | 0 |   //   - Operation Name | 
| 558 | 0 |   //   - Attributes | 
| 559 | 0 |   llvm::hash_code hash = | 
| 560 | 0 |       llvm::hash_combine(op->getName(), op->getMutableAttrDict()); | 
| 561 | 0 | 
 | 
| 562 | 0 |   //   - Result Types | 
| 563 | 0 |   ArrayRef<Type> resultTypes = op->getResultTypes(); | 
| 564 | 0 |   switch (resultTypes.size()) { | 
| 565 | 0 |   case 0: | 
| 566 | 0 |     // We don't need to add anything to the hash. | 
| 567 | 0 |     break; | 
| 568 | 0 |   case 1: | 
| 569 | 0 |     // Add in the result type. | 
| 570 | 0 |     hash = llvm::hash_combine(hash, resultTypes.front()); | 
| 571 | 0 |     break; | 
| 572 | 0 |   default: | 
| 573 | 0 |     // Use the type buffer as the hash, as we can guarantee it is the same for | 
| 574 | 0 |     // any given range of result types. This takes advantage of the fact the | 
| 575 | 0 |     // result types >1 are stored in a TupleType and uniqued. | 
| 576 | 0 |     hash = llvm::hash_combine(hash, resultTypes.data()); | 
| 577 | 0 |     break; | 
| 578 | 0 |   } | 
| 579 | 0 |  | 
| 580 | 0 |   //   - Operands | 
| 581 | 0 |   bool ignoreOperands = flags & Flags::IgnoreOperands; | 
| 582 | 0 |   if (!ignoreOperands) { | 
| 583 | 0 |     // TODO: Allow commutative operations to have different ordering. | 
| 584 | 0 |     hash = llvm::hash_combine( | 
| 585 | 0 |         hash, llvm::hash_combine_range(op->operand_begin(), op->operand_end())); | 
| 586 | 0 |   } | 
| 587 | 0 |   return hash; | 
| 588 | 0 | } | 
| 589 |  |  | 
| 590 |  | bool OperationEquivalence::isEquivalentTo(Operation *lhs, Operation *rhs, | 
| 591 | 0 |                                           Flags flags) { | 
| 592 | 0 |   if (lhs == rhs) | 
| 593 | 0 |     return true; | 
| 594 | 0 |  | 
| 595 | 0 |   // Compare the operation name. | 
| 596 | 0 |   if (lhs->getName() != rhs->getName()) | 
| 597 | 0 |     return false; | 
| 598 | 0 |   // Check operand counts. | 
| 599 | 0 |   if (lhs->getNumOperands() != rhs->getNumOperands()) | 
| 600 | 0 |     return false; | 
| 601 | 0 |   // Compare attributes. | 
| 602 | 0 |   if (lhs->getMutableAttrDict() != rhs->getMutableAttrDict()) | 
| 603 | 0 |     return false; | 
| 604 | 0 |   // Compare result types. | 
| 605 | 0 |   ArrayRef<Type> lhsResultTypes = lhs->getResultTypes(); | 
| 606 | 0 |   ArrayRef<Type> rhsResultTypes = rhs->getResultTypes(); | 
| 607 | 0 |   if (lhsResultTypes.size() != rhsResultTypes.size()) | 
| 608 | 0 |     return false; | 
| 609 | 0 |   switch (lhsResultTypes.size()) { | 
| 610 | 0 |   case 0: | 
| 611 | 0 |     break; | 
| 612 | 0 |   case 1: | 
| 613 | 0 |     // Compare the single result type. | 
| 614 | 0 |     if (lhsResultTypes.front() != rhsResultTypes.front()) | 
| 615 | 0 |       return false; | 
| 616 | 0 |     break; | 
| 617 | 0 |   default: | 
| 618 | 0 |     // Use the type buffer for the comparison, as we can guarantee it is the | 
| 619 | 0 |     // same for any given range of result types. This takes advantage of the | 
| 620 | 0 |     // fact the result types >1 are stored in a TupleType and uniqued. | 
| 621 | 0 |     if (lhsResultTypes.data() != rhsResultTypes.data()) | 
| 622 | 0 |       return false; | 
| 623 | 0 |     break; | 
| 624 | 0 |   } | 
| 625 | 0 |   // Compare operands. | 
| 626 | 0 |   bool ignoreOperands = flags & Flags::IgnoreOperands; | 
| 627 | 0 |   if (ignoreOperands) | 
| 628 | 0 |     return true; | 
| 629 | 0 |   // TODO: Allow commutative operations to have different ordering. | 
| 630 | 0 |   return std::equal(lhs->operand_begin(), lhs->operand_end(), | 
| 631 | 0 |                     rhs->operand_begin()); | 
| 632 | 0 | } |