SyclMemoryModel.h (21856B)
1 /*************************************************************************** 2 * Copyright (C) 2017 Codeplay Software Limited 3 * This Source Code Form is subject to the terms of the Mozilla 4 * Public License v. 2.0. If a copy of the MPL was not distributed 5 * with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 * 7 * 8 * SyclMemoryModel.h 9 * 10 * Description: 11 * Interface for SYCL buffers to behave as a non-dereferenceable pointer 12 * Interface for Placeholder accessor to behave as a pointer on both host 13 * and device 14 * 15 * Authors: 16 * 17 * Ruyman Reyes Codeplay Software Ltd. 18 * Mehdi Goli Codeplay Software Ltd. 19 * Vanya Yaneva Codeplay Software Ltd. 20 * 21 **************************************************************************/ 22 23 #if defined(EIGEN_USE_SYCL) && \ 24 !defined(EIGEN_CXX11_TENSOR_TENSOR_SYCL_STORAGE_MEMORY_H) 25 #define EIGEN_CXX11_TENSOR_TENSOR_SYCL_STORAGE_MEMORY_H 26 27 #include <CL/sycl.hpp> 28 #ifdef EIGEN_EXCEPTIONS 29 #include <stdexcept> 30 #endif 31 #include <cstddef> 32 #include <queue> 33 #include <set> 34 #include <unordered_map> 35 36 namespace Eigen { 37 namespace TensorSycl { 38 namespace internal { 39 40 using sycl_acc_target = cl::sycl::access::target; 41 using sycl_acc_mode = cl::sycl::access::mode; 42 43 /** 44 * Default values for template arguments 45 */ 46 using buffer_data_type_t = uint8_t; 47 const sycl_acc_target default_acc_target = sycl_acc_target::global_buffer; 48 const sycl_acc_mode default_acc_mode = sycl_acc_mode::read_write; 49 50 /** 51 * PointerMapper 52 * Associates fake pointers with buffers. 53 * 54 */ 55 class PointerMapper { 56 public: 57 using base_ptr_t = std::intptr_t; 58 59 /* Structure of a virtual pointer 60 * 61 * |================================================| 62 * | POINTER ADDRESS | 63 * |================================================| 64 */ 65 struct virtual_pointer_t { 66 /* Type for the pointers 67 */ 68 base_ptr_t m_contents; 69 70 /** Conversions from virtual_pointer_t to 71 * void * should just reinterpret_cast the integer number 72 */ 73 operator void *() const { return reinterpret_cast<void *>(m_contents); } 74 75 /** 76 * Convert back to the integer number. 77 */ 78 operator base_ptr_t() const { return m_contents; } 79 80 /** 81 * Add a certain value to the pointer to create a 82 * new pointer to that offset 83 */ 84 virtual_pointer_t operator+(size_t off) { return m_contents + off; } 85 86 /* Numerical order for sorting pointers in containers. */ 87 bool operator<(virtual_pointer_t rhs) const { 88 return (static_cast<base_ptr_t>(m_contents) < 89 static_cast<base_ptr_t>(rhs.m_contents)); 90 } 91 92 bool operator>(virtual_pointer_t rhs) const { 93 return (static_cast<base_ptr_t>(m_contents) > 94 static_cast<base_ptr_t>(rhs.m_contents)); 95 } 96 97 /** 98 * Numerical order for sorting pointers in containers 99 */ 100 bool operator==(virtual_pointer_t rhs) const { 101 return (static_cast<base_ptr_t>(m_contents) == 102 static_cast<base_ptr_t>(rhs.m_contents)); 103 } 104 105 /** 106 * Simple forward to the equality overload. 107 */ 108 bool operator!=(virtual_pointer_t rhs) const { 109 return !(this->operator==(rhs)); 110 } 111 112 /** 113 * Converts a void * into a virtual pointer structure. 114 * Note that this will only work if the void * was 115 * already a virtual_pointer_t, but we have no way of 116 * checking 117 */ 118 virtual_pointer_t(const void *ptr) 119 : m_contents(reinterpret_cast<base_ptr_t>(ptr)){}; 120 121 /** 122 * Creates a virtual_pointer_t from the given integer 123 * number 124 */ 125 virtual_pointer_t(base_ptr_t u) : m_contents(u){}; 126 }; 127 128 /* Definition of a null pointer 129 */ 130 const virtual_pointer_t null_virtual_ptr = nullptr; 131 132 /** 133 * Whether if a pointer is null or not. 134 * A pointer is nullptr if the value is of null_virtual_ptr 135 */ 136 static inline bool is_nullptr(virtual_pointer_t ptr) { 137 return (static_cast<void *>(ptr) == nullptr); 138 } 139 140 /* basic type for all buffers 141 */ 142 using buffer_t = cl::sycl::buffer_mem; 143 144 /** 145 * Node that stores information about a device allocation. 146 * Nodes are sorted by size to organise a free list of nodes 147 * that can be recovered. 148 */ 149 struct pMapNode_t { 150 buffer_t m_buffer; 151 size_t m_size; 152 bool m_free; 153 154 pMapNode_t(buffer_t b, size_t size, bool f) 155 : m_buffer{b}, m_size{size}, m_free{f} { 156 m_buffer.set_final_data(nullptr); 157 } 158 159 bool operator<=(const pMapNode_t &rhs) { return (m_size <= rhs.m_size); } 160 }; 161 162 /** Storage of the pointer / buffer tree 163 */ 164 using pointerMap_t = std::map<virtual_pointer_t, pMapNode_t>; 165 166 /** 167 * Obtain the insertion point in the pointer map for 168 * a pointer of the given size. 169 * \param requiredSize Size attemted to reclaim 170 */ 171 typename pointerMap_t::iterator get_insertion_point(size_t requiredSize) { 172 typename pointerMap_t::iterator retVal; 173 bool reuse = false; 174 if (!m_freeList.empty()) { 175 // try to re-use an existing block 176 for (auto freeElem : m_freeList) { 177 if (freeElem->second.m_size >= requiredSize) { 178 retVal = freeElem; 179 reuse = true; 180 // Element is not going to be free anymore 181 m_freeList.erase(freeElem); 182 break; 183 } 184 } 185 } 186 if (!reuse) { 187 retVal = std::prev(m_pointerMap.end()); 188 } 189 return retVal; 190 } 191 192 /** 193 * Returns an iterator to the node that stores the information 194 * of the given virtual pointer from the given pointer map structure. 195 * If pointer is not found, throws std::out_of_range. 196 * If the pointer map structure is empty, throws std::out_of_range 197 * 198 * \param pMap the pointerMap_t structure storing all the pointers 199 * \param virtual_pointer_ptr The virtual pointer to obtain the node of 200 * \throws std::out:of_range if the pointer is not found or pMap is empty 201 */ 202 typename pointerMap_t::iterator get_node(const virtual_pointer_t ptr) { 203 if (this->count() == 0) { 204 m_pointerMap.clear(); 205 EIGEN_THROW_X(std::out_of_range("There are no pointers allocated\n")); 206 207 } 208 if (is_nullptr(ptr)) { 209 m_pointerMap.clear(); 210 EIGEN_THROW_X(std::out_of_range("Cannot access null pointer\n")); 211 } 212 // The previous element to the lower bound is the node that 213 // holds this memory address 214 auto node = m_pointerMap.lower_bound(ptr); 215 // If the value of the pointer is not the one of the node 216 // then we return the previous one 217 if (node == std::end(m_pointerMap)) { 218 --node; 219 } else if (node->first != ptr) { 220 if (node == std::begin(m_pointerMap)) { 221 m_pointerMap.clear(); 222 EIGEN_THROW_X( 223 std::out_of_range("The pointer is not registered in the map\n")); 224 225 } 226 --node; 227 } 228 229 return node; 230 } 231 232 /* get_buffer. 233 * Returns a buffer from the map using the pointer address 234 */ 235 template <typename buffer_data_type = buffer_data_type_t> 236 cl::sycl::buffer<buffer_data_type, 1> get_buffer( 237 const virtual_pointer_t ptr) { 238 using sycl_buffer_t = cl::sycl::buffer<buffer_data_type, 1>; 239 240 // get_node() returns a `buffer_mem`, so we need to cast it to a `buffer<>`. 241 // We can do this without the `buffer_mem` being a pointer, as we 242 // only declare member variables in the base class (`buffer_mem`) and not in 243 // the child class (`buffer<>). 244 auto node = get_node(ptr); 245 eigen_assert(node->first == ptr || node->first < ptr); 246 eigen_assert(ptr < static_cast<virtual_pointer_t>(node->second.m_size + 247 node->first)); 248 return *(static_cast<sycl_buffer_t *>(&node->second.m_buffer)); 249 } 250 251 /** 252 * @brief Returns an accessor to the buffer of the given virtual pointer 253 * @param accessMode 254 * @param accessTarget 255 * @param ptr The virtual pointer 256 */ 257 template <sycl_acc_mode access_mode = default_acc_mode, 258 sycl_acc_target access_target = default_acc_target, 259 typename buffer_data_type = buffer_data_type_t> 260 cl::sycl::accessor<buffer_data_type, 1, access_mode, access_target> 261 get_access(const virtual_pointer_t ptr) { 262 auto buf = get_buffer<buffer_data_type>(ptr); 263 return buf.template get_access<access_mode, access_target>(); 264 } 265 266 /** 267 * @brief Returns an accessor to the buffer of the given virtual pointer 268 * in the given command group scope 269 * @param accessMode 270 * @param accessTarget 271 * @param ptr The virtual pointer 272 * @param cgh Reference to the command group scope 273 */ 274 template <sycl_acc_mode access_mode = default_acc_mode, 275 sycl_acc_target access_target = default_acc_target, 276 typename buffer_data_type = buffer_data_type_t> 277 cl::sycl::accessor<buffer_data_type, 1, access_mode, access_target> 278 get_access(const virtual_pointer_t ptr, cl::sycl::handler &cgh) { 279 auto buf = get_buffer<buffer_data_type>(ptr); 280 return buf.template get_access<access_mode, access_target>(cgh); 281 } 282 283 /* 284 * Returns the offset from the base address of this pointer. 285 */ 286 inline std::ptrdiff_t get_offset(const virtual_pointer_t ptr) { 287 // The previous element to the lower bound is the node that 288 // holds this memory address 289 auto node = get_node(ptr); 290 auto start = node->first; 291 eigen_assert(start == ptr || start < ptr); 292 eigen_assert(ptr < start + node->second.m_size); 293 return (ptr - start); 294 } 295 296 /* 297 * Returns the number of elements by which the given pointer is offset from 298 * the base address. 299 */ 300 template <typename buffer_data_type> 301 inline size_t get_element_offset(const virtual_pointer_t ptr) { 302 return get_offset(ptr) / sizeof(buffer_data_type); 303 } 304 305 /** 306 * Constructs the PointerMapper structure. 307 */ 308 PointerMapper(base_ptr_t baseAddress = 4096) 309 : m_pointerMap{}, m_freeList{}, m_baseAddress{baseAddress} { 310 if (m_baseAddress == 0) { 311 EIGEN_THROW_X(std::invalid_argument("Base address cannot be zero\n")); 312 } 313 }; 314 315 /** 316 * PointerMapper cannot be copied or moved 317 */ 318 PointerMapper(const PointerMapper &) = delete; 319 320 /** 321 * Empty the pointer list 322 */ 323 inline void clear() { 324 m_freeList.clear(); 325 m_pointerMap.clear(); 326 } 327 328 /* add_pointer. 329 * Adds an existing pointer to the map and returns the virtual pointer id. 330 */ 331 inline virtual_pointer_t add_pointer(const buffer_t &b) { 332 return add_pointer_impl(b); 333 } 334 335 /* add_pointer. 336 * Adds a pointer to the map and returns the virtual pointer id. 337 */ 338 inline virtual_pointer_t add_pointer(buffer_t &&b) { 339 return add_pointer_impl(b); 340 } 341 342 /** 343 * @brief Fuses the given node with the previous nodes in the 344 * pointer map if they are free 345 * 346 * @param node A reference to the free node to be fused 347 */ 348 void fuse_forward(typename pointerMap_t::iterator &node) { 349 while (node != std::prev(m_pointerMap.end())) { 350 // if following node is free 351 // remove it and extend the current node with its size 352 auto fwd_node = std::next(node); 353 if (!fwd_node->second.m_free) { 354 break; 355 } 356 auto fwd_size = fwd_node->second.m_size; 357 m_freeList.erase(fwd_node); 358 m_pointerMap.erase(fwd_node); 359 360 node->second.m_size += fwd_size; 361 } 362 } 363 364 /** 365 * @brief Fuses the given node with the following nodes in the 366 * pointer map if they are free 367 * 368 * @param node A reference to the free node to be fused 369 */ 370 void fuse_backward(typename pointerMap_t::iterator &node) { 371 while (node != m_pointerMap.begin()) { 372 // if previous node is free, extend it 373 // with the size of the current one 374 auto prev_node = std::prev(node); 375 if (!prev_node->second.m_free) { 376 break; 377 } 378 prev_node->second.m_size += node->second.m_size; 379 380 // remove the current node 381 m_freeList.erase(node); 382 m_pointerMap.erase(node); 383 384 // point to the previous node 385 node = prev_node; 386 } 387 } 388 389 /* remove_pointer. 390 * Removes the given pointer from the map. 391 * The pointer is allowed to be reused only if ReUse if true. 392 */ 393 template <bool ReUse = true> 394 void remove_pointer(const virtual_pointer_t ptr) { 395 if (is_nullptr(ptr)) { 396 return; 397 } 398 auto node = this->get_node(ptr); 399 400 node->second.m_free = true; 401 m_freeList.emplace(node); 402 403 // Fuse the node 404 // with free nodes before and after it 405 fuse_forward(node); 406 fuse_backward(node); 407 408 // If after fusing the node is the last one 409 // simply remove it (since it is free) 410 if (node == std::prev(m_pointerMap.end())) { 411 m_freeList.erase(node); 412 m_pointerMap.erase(node); 413 } 414 } 415 416 /* count. 417 * Return the number of active pointers (i.e, pointers that 418 * have been malloc but not freed). 419 */ 420 size_t count() const { return (m_pointerMap.size() - m_freeList.size()); } 421 422 private: 423 /* add_pointer_impl. 424 * Adds a pointer to the map and returns the virtual pointer id. 425 * BufferT is either a const buffer_t& or a buffer_t&&. 426 */ 427 template <class BufferT> 428 virtual_pointer_t add_pointer_impl(BufferT b) { 429 virtual_pointer_t retVal = nullptr; 430 size_t bufSize = b.get_count(); 431 pMapNode_t p{b, bufSize, false}; 432 // If this is the first pointer: 433 if (m_pointerMap.empty()) { 434 virtual_pointer_t initialVal{m_baseAddress}; 435 m_pointerMap.emplace(initialVal, p); 436 return initialVal; 437 } 438 439 auto lastElemIter = get_insertion_point(bufSize); 440 // We are recovering an existing free node 441 if (lastElemIter->second.m_free) { 442 lastElemIter->second.m_buffer = b; 443 lastElemIter->second.m_free = false; 444 445 // If the recovered node is bigger than the inserted one 446 // add a new free node with the remaining space 447 if (lastElemIter->second.m_size > bufSize) { 448 // create a new node with the remaining space 449 auto remainingSize = lastElemIter->second.m_size - bufSize; 450 pMapNode_t p2{b, remainingSize, true}; 451 452 // update size of the current node 453 lastElemIter->second.m_size = bufSize; 454 455 // add the new free node 456 auto newFreePtr = lastElemIter->first + bufSize; 457 auto freeNode = m_pointerMap.emplace(newFreePtr, p2).first; 458 m_freeList.emplace(freeNode); 459 } 460 461 retVal = lastElemIter->first; 462 } else { 463 size_t lastSize = lastElemIter->second.m_size; 464 retVal = lastElemIter->first + lastSize; 465 m_pointerMap.emplace(retVal, p); 466 } 467 return retVal; 468 } 469 470 /** 471 * Compare two iterators to pointer map entries according to 472 * the size of the allocation on the device. 473 */ 474 struct SortBySize { 475 bool operator()(typename pointerMap_t::iterator a, 476 typename pointerMap_t::iterator b) const { 477 return ((a->first < b->first) && (a->second <= b->second)) || 478 ((a->first < b->first) && (b->second <= a->second)); 479 } 480 }; 481 482 /* Maps the pointer addresses to buffer and size pairs. 483 */ 484 pointerMap_t m_pointerMap; 485 486 /* List of free nodes available for re-using 487 */ 488 std::set<typename pointerMap_t::iterator, SortBySize> m_freeList; 489 490 /* Base address used when issuing the first virtual pointer, allows users 491 * to specify alignment. Cannot be zero. */ 492 std::intptr_t m_baseAddress; 493 }; 494 495 /* remove_pointer. 496 * Removes the given pointer from the map. 497 * The pointer is allowed to be reused only if ReUse if true. 498 */ 499 template <> 500 inline void PointerMapper::remove_pointer<false>(const virtual_pointer_t ptr) { 501 if (is_nullptr(ptr)) { 502 return; 503 } 504 m_pointerMap.erase(this->get_node(ptr)); 505 } 506 507 /** 508 * Malloc-like interface to the pointer-mapper. 509 * Given a size, creates a byte-typed buffer and returns a 510 * fake pointer to keep track of it. 511 * \param size Size in bytes of the desired allocation 512 * \throw cl::sycl::exception if error while creating the buffer 513 */ 514 inline void *SYCLmalloc(size_t size, PointerMapper &pMap) { 515 if (size == 0) { 516 return nullptr; 517 } 518 // Create a generic buffer of the given size 519 using buffer_t = cl::sycl::buffer<buffer_data_type_t, 1>; 520 auto thePointer = pMap.add_pointer(buffer_t(cl::sycl::range<1>{size})); 521 // Store the buffer on the global list 522 return static_cast<void *>(thePointer); 523 } 524 525 /** 526 * Free-like interface to the pointer mapper. 527 * Given a fake-pointer created with the virtual-pointer malloc, 528 * destroys the buffer and remove it from the list. 529 * If ReUse is false, the pointer is not added to the freeList, 530 * it should be false only for sub-buffers. 531 */ 532 template <bool ReUse = true, typename PointerMapper> 533 inline void SYCLfree(void *ptr, PointerMapper &pMap) { 534 pMap.template remove_pointer<ReUse>(ptr); 535 } 536 537 /** 538 * Clear all the memory allocated by SYCL. 539 */ 540 template <typename PointerMapper> 541 inline void SYCLfreeAll(PointerMapper &pMap) { 542 pMap.clear(); 543 } 544 545 template <cl::sycl::access::mode AcMd, typename T> 546 struct RangeAccess { 547 static const auto global_access = cl::sycl::access::target::global_buffer; 548 static const auto is_place_holder = cl::sycl::access::placeholder::true_t; 549 typedef T scalar_t; 550 typedef scalar_t &ref_t; 551 typedef typename cl::sycl::global_ptr<scalar_t>::pointer_t ptr_t; 552 553 // the accessor type does not necessarily the same as T 554 typedef cl::sycl::accessor<scalar_t, 1, AcMd, global_access, is_place_holder> 555 accessor; 556 557 typedef RangeAccess<AcMd, T> self_t; 558 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE RangeAccess(accessor access, 559 size_t offset, 560 std::intptr_t virtual_ptr) 561 : access_(access), offset_(offset), virtual_ptr_(virtual_ptr) {} 562 563 RangeAccess(cl::sycl::buffer<scalar_t, 1> buff = 564 cl::sycl::buffer<scalar_t, 1>(cl::sycl::range<1>(1))) 565 : access_{accessor{buff}}, offset_(0), virtual_ptr_(-1) {} 566 567 // This should be only used for null constructor on the host side 568 RangeAccess(std::nullptr_t) : RangeAccess() {} 569 // This template parameter must be removed and scalar_t should be replaced 570 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE ptr_t get_pointer() const { 571 return (access_.get_pointer().get() + offset_); 572 } 573 template <typename Index> 574 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE self_t &operator+=(Index offset) { 575 offset_ += (offset); 576 return *this; 577 } 578 template <typename Index> 579 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE self_t operator+(Index offset) const { 580 return self_t(access_, offset_ + offset, virtual_ptr_); 581 } 582 template <typename Index> 583 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE self_t operator-(Index offset) const { 584 return self_t(access_, offset_ - offset, virtual_ptr_); 585 } 586 template <typename Index> 587 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE self_t &operator-=(Index offset) { 588 offset_ -= offset; 589 return *this; 590 } 591 592 // THIS IS FOR NULL COMPARISON ONLY 593 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE friend bool operator==( 594 const RangeAccess &lhs, std::nullptr_t) { 595 return ((lhs.virtual_ptr_ == -1)); 596 } 597 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE friend bool operator!=( 598 const RangeAccess &lhs, std::nullptr_t i) { 599 return !(lhs == i); 600 } 601 602 // THIS IS FOR NULL COMPARISON ONLY 603 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE friend bool operator==( 604 std::nullptr_t, const RangeAccess &rhs) { 605 return ((rhs.virtual_ptr_ == -1)); 606 } 607 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE friend bool operator!=( 608 std::nullptr_t i, const RangeAccess &rhs) { 609 return !(i == rhs); 610 } 611 // Prefix operator (Increment and return value) 612 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE self_t &operator++() { 613 offset_++; 614 return (*this); 615 } 616 617 // Postfix operator (Return value and increment) 618 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE self_t operator++(int i) { 619 EIGEN_UNUSED_VARIABLE(i); 620 self_t temp_iterator(*this); 621 offset_++; 622 return temp_iterator; 623 } 624 625 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE std::ptrdiff_t get_size() const { 626 return (access_.get_count() - offset_); 627 } 628 629 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE std::ptrdiff_t get_offset() const { 630 return offset_; 631 } 632 633 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void set_offset(std::ptrdiff_t offset) { 634 offset_ = offset; 635 } 636 637 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE ref_t operator*() const { 638 return *get_pointer(); 639 } 640 641 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE ref_t operator*() { 642 return *get_pointer(); 643 } 644 645 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE ptr_t operator->() = delete; 646 647 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE ref_t operator[](int x) { 648 return *(get_pointer() + x); 649 } 650 651 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE ref_t operator[](int x) const { 652 return *(get_pointer() + x); 653 } 654 655 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE scalar_t *get_virtual_pointer() const { 656 return reinterpret_cast<scalar_t *>(virtual_ptr_ + 657 (offset_ * sizeof(scalar_t))); 658 } 659 660 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE explicit operator bool() const { 661 return (virtual_ptr_ != -1); 662 } 663 664 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE operator RangeAccess<AcMd, const T>() { 665 return RangeAccess<AcMd, const T>(access_, offset_, virtual_ptr_); 666 } 667 668 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE 669 operator RangeAccess<AcMd, const T>() const { 670 return RangeAccess<AcMd, const T>(access_, offset_, virtual_ptr_); 671 } 672 // binding placeholder accessors to a command group handler for SYCL 673 EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void bind( 674 cl::sycl::handler &cgh) const { 675 cgh.require(access_); 676 } 677 678 private: 679 accessor access_; 680 size_t offset_; 681 std::intptr_t virtual_ptr_; // the location of the buffer in the map 682 }; 683 684 template <cl::sycl::access::mode AcMd, typename T> 685 struct RangeAccess<AcMd, const T> : RangeAccess<AcMd, T> { 686 typedef RangeAccess<AcMd, T> Base; 687 using Base::Base; 688 }; 689 690 } // namespace internal 691 } // namespace TensorSycl 692 } // namespace Eigen 693 694 #endif // EIGEN_CXX11_TENSOR_TENSOR_SYCL_STORAGE_MEMORY_H