diff --git a/clang/docs/SYCLSupport.md b/clang/docs/SYCLSupport.md --- a/clang/docs/SYCLSupport.md +++ b/clang/docs/SYCLSupport.md @@ -192,6 +192,124 @@ document [SYCL Kernel Parameter Handling and Array Support](https://github.com/intel/llvm/blob/sycl/sycl/doc/KernelParameterPassing.md). +### Address space handling + +The SYCL specification represents pointers to disjoint memory regions using C++ +wrapper classes on an accelerator to enable compilation with a standard C++ +toolchain and a SYCL compiler toolchain. Section 3.8.2 of SYCL 2020 +specification defines +[memory model](https://www.khronos.org/registry/SYCL/specs/sycl-2020/html/sycl-2020.html#_sycl_device_memory_model), +section 4.7.7 - [address space classes](https://www.khronos.org/registry/SYCL/specs/sycl-2020/html/sycl-2020.html#_address_space_classes) +and section 5.9 covers [address space deduction](https://www.khronos.org/registry/SYCL/specs/sycl-2020/html/sycl-2020.html#_address_space_deduction). + +The main address space semantic difference between SYCL and OpenCL is that SYCL +doesn't perform the address space qualifier inference detailed in +[OpenCL C v3.0 6.7.8](https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_C.html#addr-spaces-inference). + +Similar to other single-source C++-based GPU programming modes like +OpenMP/CUDA/HIP, SYCL uses clang's "default" address space for types with no +explicit address space attribute. This design has two important features: it +keeps the type system consistent with C++ and enable tools for emitting device +code aligned with SPIR memory model (and other GPU targets). + +So inside a function, this variable declaration: + +```C++ +int var; +``` + +The SYCL device compiler turns into: + +```C++ +VarDecl var 'int' +``` + +while the OpenCL compiler turns it into: + +```C++ +VarDecl var '__private int' +``` + +Changing the type of a variable can have observable effects in C++. For example, +this does not compile in C++ for OpenCL mode: + +```C++ +template +struct is_same { + static constexpr int value = 0; +}; + +template +struct is_same { + static constexpr int value = 1; +}; + +void foo(int p) { + static_assert(is_same::value, "int is not an int?"); // Fails: p is '__private int' != 'int' + static_assert(is_same::value, "int* is not an int*?"); // Fails: p is '__private int*' != '__generic int*' +} +``` + +`multi_ptr` class implementation example: + +``` C++ +// check that SYCL mode is ON and we can use non-standard decorations +#if defined(__SYCL_DEVICE_ONLY__) +// GPU/accelerator implementation +template class multi_ptr { + // DecoratedType applies corresponding address space attribute to the type T + // DecoratedType::type == "__attribute__((opencl_global)) T" + // See sycl/include/CL/sycl/access/access.hpp for more details + using pointer_t = typename DecoratedType::type *; + + pointer_t m_Pointer; + public: + pointer_t get() { return m_Pointer; } + T& operator* () { return *reinterpret_cast(m_Pointer); } +} +#else +// CPU/host implementation +template class multi_ptr { + T *m_Pointer; // regular undecorated pointer + public: + T *get() { return m_Pointer; } + T& operator* () { return *m_Pointer; } +} +#endif +``` + +Depending on the compiler mode, `multi_ptr` will either decorate its internal +data with the address space attribute or not. + +To utilize clang's existing functionality, we reuse the following OpenCL address +space attributes for pointers: + +| Address space attribute | SYCL address_space enumeration | +|-------------------------|--------------------------------| +| `__attribute__((opencl_global))` | global_space, constant_space | +| `__attribute__((opencl_local))` | local_space | +| `__attribute__((opencl_private))` | private_space | + + TODO: add support for `__attribute__((opencl_global_host))` and + `__attribute__((opencl_global_device))`. + +The default address space is "generic-memory", which is a virtual address space +that overlaps the global, local, and private address spaces. SYCL mode enables +conversion to/from the default address space from/to the address +space-attributed type. + +The SPIR target allocates SYCL namespace scope variables in the global address +space. + +Pointers to Default address space should get lowered into a pointer to a generic +address space (or flat to reuse more general terminology). But depending on the +allocation context, the default address space of a non-pointer type is assigned +to a specific address space. This is described in +https://www.khronos.org/registry/SYCL/specs/sycl-2020/html/sycl-2020.html#subsec:commonAddressSpace. + +This is also in line with the behaviour of CUDA (small example +https://godbolt.org/z/veqTfo9PK). + [//]: # (TODO: move KernelParameterPassing.md to llvm-project) [1]: https://github.com/intel/llvm/blob/sycl/sycl/doc/CompilerAndRuntimeDesign.md \ No newline at end of file