1 //============================================================================ 2 // Copyright (c) Kitware, Inc. 3 // All rights reserved. 4 // See LICENSE.txt for details. 5 // 6 // This software is distributed WITHOUT ANY WARRANTY; without even 7 // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 8 // PURPOSE. See the above copyright notice for more information. 9 //============================================================================ 10 11 #ifndef vtk_m_cont_BitField_h 12 #define vtk_m_cont_BitField_h 13 14 #include <vtkm/cont/ArrayHandle.h> 15 #include <vtkm/cont/vtkm_cont_export.h> 16 17 #include <vtkm/Atomic.h> 18 #include <vtkm/Deprecated.h> 19 #include <vtkm/List.h> 20 #include <vtkm/Types.h> 21 22 #include <cassert> 23 #include <climits> 24 #include <memory> 25 #include <type_traits> 26 27 namespace vtkm 28 { 29 namespace cont 30 { 31 32 class BitField; 33 34 namespace internal 35 { 36 37 struct StorageTagBitField; 38 39 struct VTKM_ALWAYS_EXPORT BitFieldMetaData 40 { 41 vtkm::Id NumberOfBits = 0; 42 }; 43 44 } 45 46 namespace detail 47 { 48 49 struct BitFieldTraits 50 { 51 // Allocations will occur in blocks of BlockSize bytes. This ensures that 52 // power-of-two word sizes up to BlockSize will not access invalid data 53 // during word-based access, and that atomic values will be properly aligned. 54 // We use the default StorageBasic alignment for this. 55 constexpr static vtkm::Id BlockSize = VTKM_ALLOCATION_ALIGNMENT; 56 57 // Make sure the blocksize is at least 64. Eventually we may implement SIMD 58 // bit operations, and the current largest vector width is 512 bits. 59 VTKM_STATIC_ASSERT(BlockSize >= 64); 60 61 /// Require an unsigned integral type that is <= BlockSize bytes. 62 template <typename WordType> 63 using IsValidWordType = 64 std::integral_constant<bool, 65 /* is unsigned */ 66 std::is_unsigned<WordType>::value && 67 /* doesn't exceed blocksize */ 68 sizeof(WordType) <= static_cast<size_t>(BlockSize) && 69 /* BlockSize is a multiple of WordType */ 70 static_cast<size_t>(BlockSize) % sizeof(WordType) == 0>; 71 72 /// Require an unsigned integral type that is <= BlockSize bytes, and is 73 /// is supported by the specified AtomicInterface. 74 template <typename WordType> 75 using IsValidWordTypeAtomic = 76 std::integral_constant<bool, 77 /* is unsigned */ 78 std::is_unsigned<WordType>::value && 79 /* doesn't exceed blocksize */ 80 sizeof(WordType) <= static_cast<size_t>(BlockSize) && 81 /* BlockSize is a multiple of WordType */ 82 static_cast<size_t>(BlockSize) % sizeof(WordType) == 0 && 83 /* Supported by atomic interface */ 84 vtkm::ListHas<vtkm::AtomicTypesSupported, WordType>::value>; 85 }; 86 87 /// Identifies a bit in a BitField by Word and BitOffset. Note that these 88 /// values are dependent on the type of word used to generate the coordinate. 89 struct BitCoordinate 90 { 91 /// The word containing the specified bit. 92 vtkm::Id WordIndex; 93 94 /// The zero-indexed bit in the word. 95 vtkm::Int32 BitOffset; // [0, bitsInWord) 96 }; 97 98 /// Portal for performing bit or word operations on a BitField. 99 /// 100 /// This is the implementation used by BitPortal and BitPortalConst. 101 template <bool IsConst> 102 class BitPortalBase 103 { 104 // Checks if PortalType has a GetIteratorBegin() method that returns a 105 // pointer. 106 template <typename PortalType, 107 typename PointerType = decltype(std::declval<PortalType>().GetIteratorBegin())> 108 struct HasPointerAccess : public std::is_pointer<PointerType> 109 { 110 }; 111 112 // Determine whether we should store a const vs. mutable pointer: 113 template <typename T> 114 using MaybeConstPointer = typename std::conditional<IsConst, T const*, T*>::type; 115 using BufferType = MaybeConstPointer<void>; // void* or void const*, as appropriate 116 117 public: 118 /// The fastest word type for performing bitwise operations through AtomicInterface. 119 using WordTypePreferred = vtkm::AtomicTypePreferred; 120 121 /// MPL check for whether a WordType may be used for non-atomic operations. 122 template <typename WordType> 123 using IsValidWordType = BitFieldTraits::IsValidWordType<WordType>; 124 125 /// MPL check for whether a WordType may be used for atomic operations. 126 template <typename WordType> 127 using IsValidWordTypeAtomic = BitFieldTraits::IsValidWordTypeAtomic<WordType>; 128 129 VTKM_STATIC_ASSERT_MSG(IsValidWordType<WordTypeDefault>::value, 130 "Internal error: Default word type is invalid."); 131 VTKM_STATIC_ASSERT_MSG(IsValidWordType<WordTypePreferred>::value, 132 "Device-specific fast word type is invalid."); 133 134 VTKM_STATIC_ASSERT_MSG(IsValidWordTypeAtomic<WordTypeDefault>::value, 135 "Internal error: Default word type is invalid."); 136 VTKM_STATIC_ASSERT_MSG(IsValidWordTypeAtomic<WordTypePreferred>::value, 137 "Device-specific fast word type is invalid for atomic operations."); 138 139 protected: 140 friend class vtkm::cont::BitField; 141 friend class vtkm::cont::internal::Storage<bool, vtkm::cont::internal::StorageTagBitField>; 142 143 /// Construct a BitPortal from a raw array. BitPortalBase(BufferType rawArray,vtkm::Id numberOfBits)144 VTKM_CONT BitPortalBase(BufferType rawArray, vtkm::Id numberOfBits) 145 : Data{ rawArray } 146 , NumberOfBits{ numberOfBits } 147 { 148 } 149 150 public: 151 BitPortalBase() noexcept = default; 152 BitPortalBase(const BitPortalBase&) noexcept = default; 153 BitPortalBase(BitPortalBase&&) noexcept = default; 154 BitPortalBase& operator=(const BitPortalBase&) noexcept = default; 155 BitPortalBase& operator=(BitPortalBase&&) noexcept = default; 156 157 /// Returns the number of bits in the BitField. 158 VTKM_EXEC_CONT GetNumberOfBits()159 vtkm::Id GetNumberOfBits() const noexcept { return this->NumberOfBits; } 160 161 /// Returns how many words of type @a WordTypePreferred exist in the dataset. 162 /// Note that this is rounded up and may contain partial words. See 163 /// also GetFinalWordMask to handle the trailing partial word. 164 template <typename WordType = WordTypePreferred> GetNumberOfWords()165 VTKM_EXEC_CONT vtkm::Id GetNumberOfWords() const noexcept 166 { 167 VTKM_STATIC_ASSERT(IsValidWordType<WordType>::value); 168 static constexpr vtkm::Id WordSize = static_cast<vtkm::Id>(sizeof(WordType)); 169 static constexpr vtkm::Id WordBits = WordSize * CHAR_BIT; 170 return (this->NumberOfBits + WordBits - 1) / WordBits; 171 } 172 173 /// Return a mask in which the valid bits in the final word (of type @a 174 /// WordType) are set to 1. 175 template <typename WordType = WordTypePreferred> GetFinalWordMask()176 VTKM_EXEC_CONT WordType GetFinalWordMask() const noexcept 177 { 178 if (this->NumberOfBits == 0) 179 { 180 return WordType{ 0 }; 181 } 182 183 static constexpr vtkm::Int32 BitsPerWord = 184 static_cast<vtkm::Int32>(sizeof(WordType) * CHAR_BIT); 185 186 const auto maxBit = this->NumberOfBits - 1; 187 const auto coord = this->GetBitCoordinateFromIndex<WordType>(maxBit); 188 const vtkm::Int32 shift = BitsPerWord - coord.BitOffset - 1; 189 return (~WordType{ 0 }) >> shift; 190 } 191 192 /// Given a bit index, compute a @a BitCoordinate that identifies the 193 /// corresponding word index and bit offset. 194 template <typename WordType = WordTypePreferred> GetBitCoordinateFromIndex(vtkm::Id bitIdx)195 VTKM_EXEC_CONT static BitCoordinate GetBitCoordinateFromIndex(vtkm::Id bitIdx) noexcept 196 { 197 VTKM_STATIC_ASSERT(IsValidWordType<WordType>::value); 198 static constexpr vtkm::Id BitsPerWord = static_cast<vtkm::Id>(sizeof(WordType) * CHAR_BIT); 199 return { static_cast<vtkm::Id>(bitIdx / BitsPerWord), 200 static_cast<vtkm::Int32>(bitIdx % BitsPerWord) }; 201 } 202 203 /// Set the bit at @a bitIdx to @a val. This method is not thread-safe -- 204 /// threads modifying bits nearby may interfere with this operation. 205 /// Additionally, this should not be used for synchronization, as there are 206 /// no memory ordering requirements. See SetBitAtomic for those usecases. 207 VTKM_EXEC_CONT SetBit(vtkm::Id bitIdx,bool val)208 void SetBit(vtkm::Id bitIdx, bool val) const noexcept 209 { 210 VTKM_STATIC_ASSERT_MSG(!IsConst, "'Set' method called on const BitField portal."); 211 using WordType = WordTypePreferred; 212 const auto coord = this->GetBitCoordinateFromIndex<WordType>(bitIdx); 213 const auto mask = WordType(1) << coord.BitOffset; 214 WordType* wordAddr = this->GetWordAddress<WordType>(coord.WordIndex); 215 if (val) 216 { 217 *wordAddr |= mask; 218 } 219 else 220 { 221 *wordAddr &= ~mask; 222 } 223 } 224 225 /// Set the bit at @a bitIdx to @a val using atomic operations. This method 226 /// is thread-safe and guarantees, at minimum, "release" memory ordering. 227 VTKM_EXEC_CONT SetBitAtomic(vtkm::Id bitIdx,bool val)228 void SetBitAtomic(vtkm::Id bitIdx, bool val) const 229 { 230 VTKM_STATIC_ASSERT_MSG(!IsConst, "'Set' method called on const BitField portal."); 231 using WordType = WordTypePreferred; 232 const auto coord = this->GetBitCoordinateFromIndex<WordType>(bitIdx); 233 const auto mask = WordType(1) << coord.BitOffset; 234 if (val) 235 { 236 this->OrWordAtomic(coord.WordIndex, mask); 237 } 238 else 239 { 240 this->AndWordAtomic(coord.WordIndex, ~mask); 241 } 242 } 243 244 /// Return whether or not the bit at @a bitIdx is set. Note that this uses 245 /// non-atomic loads and thus should not be used for synchronization. 246 VTKM_EXEC_CONT GetBit(vtkm::Id bitIdx)247 bool GetBit(vtkm::Id bitIdx) const noexcept 248 { 249 using WordType = WordTypePreferred; 250 const auto coord = this->GetBitCoordinateFromIndex<WordType>(bitIdx); 251 const auto word = this->GetWord<WordType>(coord.WordIndex); 252 const auto mask = WordType(1) << coord.BitOffset; 253 return (word & mask) != WordType(0); 254 } 255 256 /// Return whether or not the bit at @a bitIdx is set using atomic loads. 257 /// This method is thread safe and guarantees, at minimum, "acquire" memory 258 /// ordering. 259 VTKM_EXEC_CONT GetBitAtomic(vtkm::Id bitIdx)260 bool GetBitAtomic(vtkm::Id bitIdx) const 261 { 262 using WordType = WordTypePreferred; 263 const auto coord = this->GetBitCoordinateFromIndex<WordType>(bitIdx); 264 const auto word = this->GetWordAtomic<WordType>(coord.WordIndex); 265 const auto mask = WordType(1) << coord.BitOffset; 266 return (word & mask) != WordType(0); 267 } 268 269 /// Set the word (of type @a WordType) at @a wordIdx to @a word using 270 /// non-atomic operations. 271 template <typename WordType = WordTypePreferred> SetWord(vtkm::Id wordIdx,WordType word)272 VTKM_EXEC_CONT void SetWord(vtkm::Id wordIdx, WordType word) const noexcept 273 { 274 VTKM_STATIC_ASSERT_MSG(!IsConst, "'Set' method called on const BitField portal."); 275 *this->GetWordAddress<WordType>(wordIdx) = word; 276 } 277 278 /// Set the word (of type @a WordType) at @a wordIdx to @a word using atomic 279 /// operations. The store guarantees, at minimum, "release" memory ordering. 280 template <typename WordType = WordTypePreferred> SetWordAtomic(vtkm::Id wordIdx,WordType word)281 VTKM_EXEC_CONT void SetWordAtomic(vtkm::Id wordIdx, WordType word) const 282 { 283 VTKM_STATIC_ASSERT_MSG(!IsConst, "'Set' method called on const BitField portal."); 284 VTKM_STATIC_ASSERT_MSG(IsValidWordTypeAtomic<WordType>::value, 285 "Requested WordType does not support atomic" 286 " operations on target execution platform."); 287 vtkm::AtomicStore(this->GetWordAddress<WordType>(wordIdx), word); 288 } 289 290 /// Get the word (of type @a WordType) at @a wordIdx using non-atomic 291 /// operations. 292 template <typename WordType = WordTypePreferred> GetWord(vtkm::Id wordIdx)293 VTKM_EXEC_CONT WordType GetWord(vtkm::Id wordIdx) const noexcept 294 { 295 return *this->GetWordAddress<WordType>(wordIdx); 296 } 297 298 /// Get the word (of type @a WordType) at @ wordIdx using an atomic read with, 299 /// at minimum, "acquire" memory ordering. 300 template <typename WordType = WordTypePreferred> GetWordAtomic(vtkm::Id wordIdx)301 VTKM_EXEC_CONT WordType GetWordAtomic(vtkm::Id wordIdx) const 302 { 303 VTKM_STATIC_ASSERT_MSG(IsValidWordTypeAtomic<WordType>::value, 304 "Requested WordType does not support atomic" 305 " operations on target execution platform."); 306 return vtkm::AtomicLoad(this->GetWordAddress<WordType>(wordIdx)); 307 } 308 309 /// Toggle the bit at @a bitIdx, returning the original value. This method 310 /// uses atomic operations and a full memory barrier. 311 VTKM_EXEC_CONT NotBitAtomic(vtkm::Id bitIdx)312 bool NotBitAtomic(vtkm::Id bitIdx) const 313 { 314 VTKM_STATIC_ASSERT_MSG(!IsConst, "Attempt to modify const BitField portal."); 315 using WordType = WordTypePreferred; 316 const auto coord = this->GetBitCoordinateFromIndex<WordType>(bitIdx); 317 const auto mask = WordType(1) << coord.BitOffset; 318 const auto oldWord = this->XorWordAtomic(coord.WordIndex, mask); 319 return (oldWord & mask) != WordType(0); 320 } 321 322 /// Perform a bitwise "not" operation on the word at @a wordIdx, returning the 323 /// original word. This uses atomic operations and a full memory barrier. 324 template <typename WordType = WordTypePreferred> NotWordAtomic(vtkm::Id wordIdx)325 VTKM_EXEC_CONT WordType NotWordAtomic(vtkm::Id wordIdx) const 326 { 327 VTKM_STATIC_ASSERT_MSG(!IsConst, "Attempt to modify const BitField portal."); 328 VTKM_STATIC_ASSERT_MSG(IsValidWordTypeAtomic<WordType>::value, 329 "Requested WordType does not support atomic" 330 " operations on target execution platform."); 331 WordType* addr = this->GetWordAddress<WordType>(wordIdx); 332 return vtkm::AtomicNot(addr); 333 } 334 335 /// Perform an "and" operation between the bit at @a bitIdx and @a val, 336 /// returning the original value at @a bitIdx. This method uses atomic 337 /// operations and a full memory barrier. 338 VTKM_EXEC_CONT AndBitAtomic(vtkm::Id bitIdx,bool val)339 bool AndBitAtomic(vtkm::Id bitIdx, bool val) const 340 { 341 VTKM_STATIC_ASSERT_MSG(!IsConst, "Attempt to modify const BitField portal."); 342 using WordType = WordTypePreferred; 343 const auto coord = this->GetBitCoordinateFromIndex<WordType>(bitIdx); 344 const auto bitmask = WordType(1) << coord.BitOffset; 345 // wordmask is all 1's, except for BitOffset which is (val ? 1 : 0) 346 const auto wordmask = val ? ~WordType(0) : ~bitmask; 347 const auto oldWord = this->AndWordAtomic(coord.WordIndex, wordmask); 348 return (oldWord & bitmask) != WordType(0); 349 } 350 351 /// Perform an "and" operation between the word at @a wordIdx and @a wordMask, 352 /// returning the original word at @a wordIdx. This method uses atomic 353 /// operations and a full memory barrier. 354 template <typename WordType = WordTypePreferred> AndWordAtomic(vtkm::Id wordIdx,WordType wordmask)355 VTKM_EXEC_CONT WordType AndWordAtomic(vtkm::Id wordIdx, WordType wordmask) const 356 { 357 VTKM_STATIC_ASSERT_MSG(!IsConst, "Attempt to modify const BitField portal."); 358 VTKM_STATIC_ASSERT_MSG(IsValidWordTypeAtomic<WordType>::value, 359 "Requested WordType does not support atomic" 360 " operations on target execution platform."); 361 WordType* addr = this->GetWordAddress<WordType>(wordIdx); 362 return vtkm::AtomicAnd(addr, wordmask); 363 } 364 365 /// Perform an "of" operation between the bit at @a bitIdx and @a val, 366 /// returning the original value at @a bitIdx. This method uses atomic 367 /// operations and a full memory barrier. 368 VTKM_EXEC_CONT OrBitAtomic(vtkm::Id bitIdx,bool val)369 bool OrBitAtomic(vtkm::Id bitIdx, bool val) const 370 { 371 VTKM_STATIC_ASSERT_MSG(!IsConst, "Attempt to modify const BitField portal."); 372 using WordType = WordTypePreferred; 373 const auto coord = this->GetBitCoordinateFromIndex<WordType>(bitIdx); 374 const auto bitmask = WordType(1) << coord.BitOffset; 375 // wordmask is all 0's, except for BitOffset which is (val ? 1 : 0) 376 const auto wordmask = val ? bitmask : WordType(0); 377 const auto oldWord = this->OrWordAtomic(coord.WordIndex, wordmask); 378 return (oldWord & bitmask) != WordType(0); 379 } 380 381 /// Perform an "or" operation between the word at @a wordIdx and @a wordMask, 382 /// returning the original word at @a wordIdx. This method uses atomic 383 /// operations and a full memory barrier. 384 template <typename WordType = WordTypePreferred> OrWordAtomic(vtkm::Id wordIdx,WordType wordmask)385 VTKM_EXEC_CONT WordType OrWordAtomic(vtkm::Id wordIdx, WordType wordmask) const 386 { 387 VTKM_STATIC_ASSERT_MSG(!IsConst, "Attempt to modify const BitField portal."); 388 VTKM_STATIC_ASSERT_MSG(IsValidWordTypeAtomic<WordType>::value, 389 "Requested WordType does not support atomic" 390 " operations on target execution platform."); 391 WordType* addr = this->GetWordAddress<WordType>(wordIdx); 392 return vtkm::AtomicOr(addr, wordmask); 393 } 394 395 /// Perform an "xor" operation between the bit at @a bitIdx and @a val, 396 /// returning the original value at @a bitIdx. This method uses atomic 397 /// operations and a full memory barrier. 398 VTKM_EXEC_CONT XorBitAtomic(vtkm::Id bitIdx,bool val)399 bool XorBitAtomic(vtkm::Id bitIdx, bool val) const 400 { 401 VTKM_STATIC_ASSERT_MSG(!IsConst, "Attempt to modify const BitField portal."); 402 using WordType = WordTypePreferred; 403 const auto coord = this->GetBitCoordinateFromIndex<WordType>(bitIdx); 404 const auto bitmask = WordType(1) << coord.BitOffset; 405 // wordmask is all 0's, except for BitOffset which is (val ? 1 : 0) 406 const auto wordmask = val ? bitmask : WordType(0); 407 const auto oldWord = this->XorWordAtomic(coord.WordIndex, wordmask); 408 return (oldWord & bitmask) != WordType(0); 409 } 410 411 /// Perform an "xor" operation between the word at @a wordIdx and @a wordMask, 412 /// returning the original word at @a wordIdx. This method uses atomic 413 /// operations and a full memory barrier. 414 template <typename WordType = WordTypePreferred> XorWordAtomic(vtkm::Id wordIdx,WordType wordmask)415 VTKM_EXEC_CONT WordType XorWordAtomic(vtkm::Id wordIdx, WordType wordmask) const 416 { 417 VTKM_STATIC_ASSERT_MSG(!IsConst, "Attempt to modify const BitField portal."); 418 VTKM_STATIC_ASSERT_MSG(IsValidWordTypeAtomic<WordType>::value, 419 "Requested WordType does not support atomic" 420 " operations on target execution platform."); 421 WordType* addr = this->GetWordAddress<WordType>(wordIdx); 422 return vtkm::AtomicXor(addr, wordmask); 423 } 424 425 /// Perform an atomic compare-and-swap operation on the bit at @a bitIdx. 426 /// If the value in memory is equal to @a oldBit, it is replaced with 427 /// the value of @a newBit and true is returned. If the value in memory is 428 /// not equal to @oldBit, @oldBit is changed to that value and false is 429 /// returned. This method implements a full memory barrier around the atomic 430 /// operation. 431 VTKM_EXEC_CONT CompareExchangeBitAtomic(vtkm::Id bitIdx,bool * oldBit,bool newBit)432 bool CompareExchangeBitAtomic(vtkm::Id bitIdx, bool* oldBit, bool newBit) const 433 { 434 VTKM_STATIC_ASSERT_MSG(!IsConst, "Attempt to modify const BitField portal."); 435 using WordType = WordTypePreferred; 436 const auto coord = this->GetBitCoordinateFromIndex<WordType>(bitIdx); 437 const auto bitmask = WordType(1) << coord.BitOffset; 438 439 WordType oldWord = this->GetWord<WordType>(coord.WordIndex); 440 do 441 { 442 bool actualBit = (oldWord & bitmask) != WordType(0); 443 if (actualBit != *oldBit) 444 { // The bit-of-interest does not match what we expected. 445 *oldBit = actualBit; 446 return false; 447 } 448 else if (actualBit == newBit) 449 { // The bit hasn't changed, but also already matches newVal. We're done. 450 return true; 451 } 452 453 // Attempt to update the word with a compare-exchange in the loop condition. 454 // If the old word changed since last queried, oldWord will get updated and 455 // the loop will continue until it succeeds. 456 } while (!this->CompareExchangeWordAtomic(coord.WordIndex, &oldWord, oldWord ^ bitmask)); 457 458 return true; 459 } 460 461 VTKM_DEPRECATED(1.6, "Use CompareExchangeBitAtomic. (Note the changed interface.)") CompareAndSwapBitAtomic(vtkm::Id bitIdx,bool newBit,bool expectedBit)462 VTKM_EXEC_CONT bool CompareAndSwapBitAtomic(vtkm::Id bitIdx, bool newBit, bool expectedBit) const 463 { 464 this->CompareExchangeBitAtomic(bitIdx, &expectedBit, newBit); 465 return expectedBit; 466 } 467 468 /// Perform an atomic compare-exchange operation on the word at @a wordIdx. 469 /// If the word in memory is equal to @a oldWord, it is replaced with 470 /// the value of @a newWord and true returned. If the word in memory is not 471 /// equal to @oldWord, @oldWord is set to the word in memory and false is 472 /// returned. This method implements a full memory barrier around the atomic 473 /// operation. 474 template <typename WordType = WordTypePreferred> CompareExchangeWordAtomic(vtkm::Id wordIdx,WordType * oldWord,WordType newWord)475 VTKM_EXEC_CONT bool CompareExchangeWordAtomic(vtkm::Id wordIdx, 476 WordType* oldWord, 477 WordType newWord) const 478 { 479 VTKM_STATIC_ASSERT_MSG(!IsConst, "Attempt to modify const BitField portal."); 480 VTKM_STATIC_ASSERT_MSG(IsValidWordTypeAtomic<WordType>::value, 481 "Requested WordType does not support atomic" 482 " operations on target execution platform."); 483 WordType* addr = this->GetWordAddress<WordType>(wordIdx); 484 return vtkm::AtomicCompareExchange(addr, oldWord, newWord); 485 } 486 487 template <typename WordType = WordTypePreferred> 488 VTKM_DEPRECATED(1.6, "Use CompareExchangeWordAtomic. (Note the changed interface.)") 489 VTKM_EXEC_CONT WordType CompareAndSwapWordAtomic(vtkm::Id wordIdx,WordType newWord,WordType expected)490 CompareAndSwapWordAtomic(vtkm::Id wordIdx, WordType newWord, WordType expected) const 491 { 492 this->CompareExchangeWordAtomic(wordIdx, &expected, newWord); 493 return expected; 494 } 495 496 private: 497 template <typename WordType> GetWordAddress(vtkm::Id wordId)498 VTKM_EXEC_CONT MaybeConstPointer<WordType> GetWordAddress(vtkm::Id wordId) const noexcept 499 { 500 VTKM_STATIC_ASSERT(IsValidWordType<WordType>::value); 501 return reinterpret_cast<MaybeConstPointer<WordType>>(this->Data) + wordId; 502 } 503 504 BufferType Data{ nullptr }; 505 vtkm::Id NumberOfBits{ 0 }; 506 }; 507 508 using BitPortal = BitPortalBase<false>; 509 510 using BitPortalConst = BitPortalBase<true>; 511 512 template <typename WordType, typename Device> 513 struct IsValidWordTypeDeprecated 514 { 515 using type VTKM_DEPRECATED( 516 1.6, 517 "BitField::IsValidWordTypeAtomic no longer takes a second Device parameter.") = 518 detail::BitFieldTraits::IsValidWordTypeAtomic<WordType>; 519 }; 520 521 template <typename WordType> 522 struct IsValidWordTypeDeprecated<WordType, void> 523 { 524 using type = detail::BitFieldTraits::IsValidWordTypeAtomic<WordType>; 525 }; 526 527 } // end namespace detail 528 529 class VTKM_CONT_EXPORT BitField 530 { 531 static constexpr vtkm::Id BlockSize = detail::BitFieldTraits::BlockSize; 532 533 public: 534 /// The type array handle used to store the bit data internally: 535 using ArrayHandleType VTKM_DEPRECATED(1.6, "BitField now uses a Buffer to store data.") = 536 ArrayHandle<vtkm::WordTypeDefault, StorageTagBasic>; 537 538 /// The BitPortal used in the control environment. 539 using WritePortalType = detail::BitPortal; 540 541 /// A read-only BitPortal used in the control environment. 542 using ReadPortalType = detail::BitPortalConst; 543 544 using PortalControl VTKM_DEPRECATED(1.6, 545 "Use BitField::WritePortalType instead.") = detail::BitPortal; 546 using PortalConstControl VTKM_DEPRECATED(1.6, "Use ArrayBitField::ReadPortalType instead.") = 547 detail::BitPortalConst; 548 549 template <typename Device> 550 struct ExecutionTypes 551 { 552 /// The preferred word type used by the specified device. 553 using WordTypePreferred = vtkm::AtomicTypePreferred; 554 555 /// A BitPortal that is usable on the specified device. 556 using Portal = detail::BitPortal; 557 558 /// A read-only BitPortal that is usable on the specified device. 559 using PortalConst = detail::BitPortalConst; 560 }; 561 562 /// Check whether a word type is valid for non-atomic operations. 563 template <typename WordType> 564 using IsValidWordType = detail::BitFieldTraits::IsValidWordType<WordType>; 565 566 /// Check whether a word type is valid for atomic operations. 567 template <typename WordType, typename Device = void> 568 using IsValidWordTypeAtomic = detail::BitFieldTraits::IsValidWordTypeAtomic<WordType>; 569 570 /// Check whether a word type is valid for atomic operations from the control 571 /// environment. 572 template <typename WordType> 573 using IsValidWordTypeAtomicControl VTKM_DEPRECATED(1.6, "Use IsValidWordTypeAtomic instead.") = 574 detail::BitFieldTraits::IsValidWordTypeAtomic<WordType>; 575 576 VTKM_CONT BitField(); 577 VTKM_CONT BitField(const BitField&) = default; 578 VTKM_CONT BitField(BitField&&) noexcept = default; 579 VTKM_CONT ~BitField() = default; 580 VTKM_CONT BitField& operator=(const BitField&) = default; 581 VTKM_CONT BitField& operator=(BitField&&) noexcept = default; 582 583 VTKM_CONT 584 bool operator==(const BitField& rhs) const { return this->Buffer == rhs.Buffer; } 585 586 VTKM_CONT 587 bool operator!=(const BitField& rhs) const { return this->Buffer != rhs.Buffer; } 588 589 /// Return the internal `Buffer` used to store the `BitField`. 590 VTKM_CONT vtkm::cont::internal::Buffer GetBuffer() const { return this->Buffer; } 591 592 /// Return the internal ArrayHandle used to store the BitField. 593 VTKM_CONT VTKM_DEPRECATED(1.6, "BitField now uses a Buffer to store data.") 594 ArrayHandle<vtkm::WordTypeDefault, StorageTagBasic> GetData() const 595 { 596 return vtkm::cont::ArrayHandle<vtkm::WordTypeDefault, StorageTagBasic>(&this->Buffer); 597 } 598 599 /// Return the number of bits stored by this BitField. 600 VTKM_CONT vtkm::Id GetNumberOfBits() const; 601 602 /// Return the number of words (of @a WordType) stored in this bit fields. 603 /// 604 template <typename WordType> 605 VTKM_CONT vtkm::Id GetNumberOfWords() const 606 { 607 VTKM_STATIC_ASSERT(IsValidWordType<WordType>::value); 608 static constexpr vtkm::Id WordBits = static_cast<vtkm::Id>(sizeof(WordType) * CHAR_BIT); 609 return (this->GetNumberOfBits() + WordBits - 1) / WordBits; 610 } 611 612 /// Allocate the requested number of bits. 613 VTKM_CONT void Allocate(vtkm::Id numberOfBits, 614 vtkm::CopyFlag preserve, 615 vtkm::cont::Token& token) const; 616 617 /// Allocate the requested number of bits. 618 VTKM_CONT void Allocate(vtkm::Id numberOfBits, 619 vtkm::CopyFlag preserve = vtkm::CopyFlag::Off) const 620 { 621 vtkm::cont::Token token; 622 this->Allocate(numberOfBits, preserve, token); 623 } 624 625 /// Shrink the bit field to the requested number of bits. 626 VTKM_CONT VTKM_DEPRECATED(1.6, 627 "Use Allocate with preserve = On.") void Shrink(vtkm::Id numberOfBits) 628 { 629 this->Allocate(numberOfBits, vtkm::CopyFlag::On); 630 } 631 632 /// Release all execution-side resources held by this BitField. 633 VTKM_CONT void ReleaseResourcesExecution(); 634 635 /// Release all resources held by this BitField and reset to empty. 636 VTKM_CONT void ReleaseResources(); 637 638 /// Force the control array to sync with the last-used device. 639 VTKM_CONT void SyncControlArray() const; 640 641 /// Returns true if the `BitField`'s data is on the given device. If the data are on the given 642 /// device, then preparing for that device should not require any data movement. 643 /// 644 VTKM_CONT bool IsOnDevice(vtkm::cont::DeviceAdapterId device) const; 645 646 /// Returns true if the `BitField`'s data is on the host. If the data are on the given 647 /// device, then calling `ReadPortal` or `WritePortal` should not require any data movement. 648 /// 649 VTKM_CONT bool IsOnHost() const 650 { 651 return this->IsOnDevice(vtkm::cont::DeviceAdapterTagUndefined{}); 652 } 653 654 VTKM_CONT VTKM_DEPRECATED(1.6, "Data can be on multiple devices. Use IsOnDevice.") 655 vtkm::cont::DeviceAdapterId GetDeviceAdapterId() const; 656 657 /// \brief Get a portal to the data that is usable from the control environment. 658 /// 659 /// As long as this portal is in scope, no one else will be able to read or write the BitField. 660 VTKM_CONT WritePortalType WritePortal() const; 661 662 /// \brief Get a read-only portal to the data that is usable from the control environment. 663 /// 664 /// As long as this portal is in scope, no one else will be able to write in the BitField. 665 VTKM_CONT ReadPortalType ReadPortal() const; 666 667 VTKM_CONT 668 VTKM_DEPRECATED(1.6, 669 "Use BitField::WritePortal() instead. " 670 "Note that the returned portal will lock the array while it is in scope.") 671 detail::BitPortal GetPortalControl() { return this->WritePortal(); } 672 673 /// Get a read-only portal to the data that is usable from the control 674 /// environment. 675 VTKM_CONT 676 VTKM_DEPRECATED(1.6, 677 "Use BitField::ReadPortal() instead. " 678 "Note that the returned portal will lock the array while it is in scope.") 679 detail::BitPortalConst GetPortalConstControl() const { return this->ReadPortal(); } 680 681 /// Prepares this BitField to be used as an input to an operation in the 682 /// execution environment. If necessary, copies data to the execution 683 /// environment. Can throw an exception if this BitField does not yet contain 684 /// any data. Returns a portal that can be used in code running in the 685 /// execution environment. 686 VTKM_CONT ReadPortalType PrepareForInput(vtkm::cont::DeviceAdapterId device, 687 vtkm::cont::Token& token) const; 688 689 template <typename DeviceAdapterTag> 690 VTKM_CONT VTKM_DEPRECATED(1.6, "PrepareForInput now requires a vtkm::cont::Token object.") 691 typename ExecutionTypes<DeviceAdapterTag>::PortalConst 692 PrepareForInput(DeviceAdapterTag device) const 693 { 694 vtkm::cont::Token token; 695 return this->PrepareForInput(device, token); 696 } 697 698 /// Prepares (allocates) this BitField to be used as an output from an 699 /// operation in the execution environment. The internal state of this class 700 /// is set to have valid data in the execution BitField with the assumption 701 /// that the array will be filled soon (i.e. before any other methods of this 702 /// object are called). Returns a portal that can be used in code running in 703 /// the execution environment. 704 VTKM_CONT WritePortalType PrepareForOutput(vtkm::Id numBits, 705 vtkm::cont::DeviceAdapterId device, 706 vtkm::cont::Token& token) const; 707 708 template <typename DeviceAdapterTag> 709 VTKM_CONT VTKM_DEPRECATED(1.6, "PrepareForOutput now requires a vtkm::cont::Token object.") 710 typename ExecutionTypes<DeviceAdapterTag>::Portal 711 PrepareForOutput(vtkm::Id numBits, DeviceAdapterTag device) const 712 { 713 vtkm::cont::Token token; 714 return this->PrepareForOutput(numBits, device, token); 715 } 716 717 /// Prepares this BitField to be used in an in-place operation (both as input 718 /// and output) in the execution environment. If necessary, copies data to 719 /// the execution environment. Can throw an exception if this BitField does 720 /// not yet contain any data. Returns a portal that can be used in code 721 /// running in the execution environment. 722 VTKM_CONT WritePortalType PrepareForInPlace(vtkm::cont::DeviceAdapterId device, 723 vtkm::cont::Token& token) const; 724 725 template <typename DeviceAdapterTag> 726 VTKM_CONT VTKM_DEPRECATED(1.6, "PrepareForInPlace now requires a vtkm::cont::Token object.") 727 typename ExecutionTypes<DeviceAdapterTag>::Portal 728 PrepareForInPlace(DeviceAdapterTag device) const 729 { 730 vtkm::cont::Token token; 731 return this->PrepareForInPlace(device, token); 732 } 733 734 private: 735 mutable vtkm::cont::internal::Buffer Buffer; 736 }; 737 } 738 } // end namespace vtkm::cont 739 740 #endif // vtk_m_cont_BitField_h 741