diff --git a/README.md b/README.md index 748d045..6b6eed3 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ from this registry in the future. | CP013 | [P1436 & P1437: Papers for affinity-based execution](affinity/index.md) | ISO C++ SG1, SG14, LEWG | 15 November 2017 | 17 June 2019 | _Published_ | | CP014 | [Shared Virtual Memory](svm/index.md) | SYCL 2.2 | 22 January 2018 | 22 January 2018 | _Work in Progress_ | | CP015 | [Specialization Constant](spec-constant/index.md) | SYCL 1.2.1 extension | 24 April 2018 | 24 April 2018 | _Work in Progress_ | +| CP016 | [Casting multi_ptr pointers](multi_ptr-cast/index.md) | SYCL 1.2.1 extension / SYCL 2.2 | 19 December 2018 | 21 September 2019 | _Work in Progress_ | | CP017 | [Host Access](host_access/index.md) | SYCL 1.2.1 vendor extension | 17 September 2018 | 13 December 2018 | _Available since CE 1.0.3_ | | CP018 | [Built-in kernels](builtin_kernels/index.md) | SYCL 1.2.1 vendor extension | 12 October 2018 | 12 October 2018 | _Available since CE 1.0.3_ | | CP019 | [On-chip Memory Allocation](onchip-memory/index.md) | SYCL 1.2.1 vendor extension | 03 December 2018 | 03 December 2018 | _Available since CE 1.0.3_ | diff --git a/multi_ptr-cast/index.md b/multi_ptr-cast/index.md new file mode 100644 index 0000000..9348671 --- /dev/null +++ b/multi_ptr-cast/index.md @@ -0,0 +1,20 @@ +# Casting multi_ptr pointers + +| Proposal ID | CP016 | +|-------------|--------| +| Name | Casting multi_ptr pointers | +| Date of Creation | 19 December 2018 | +| Target | SYCL 1.2.1 vendor extension | +| Current Status | _Work in Progress_ | +| Reply-to | Peter Žužek | +| Original author | Peter Žužek | +| Contributors | Ruyman Reyes | + +## Overview + +This paper proposes the addition of pointer casting functions to SYCL +in order to simplify casting of the `multi_ptr` class. + +## Versions + +[Version 1](sycl-2.2/index.md) diff --git a/multi_ptr-cast/sycl-2.2/index.md b/multi_ptr-cast/sycl-2.2/index.md new file mode 100644 index 0000000..91b31f0 --- /dev/null +++ b/multi_ptr-cast/sycl-2.2/index.md @@ -0,0 +1,212 @@ +# Casting multi_ptr pointers + +## Motivation + +The current SYCL 1.2.1 specification contains the `multi_ptr` class +which is designed mostly for OpenCL interoperability +and calling certain functions which rely on OpenCL builtins. +The `multi_ptr` class has two template parameters: +`ElementType` as the type of the underlying data, +and `Space` to designate the address space of the pointer. +It is not allowed to cast pointers to a different address space, +but casting pointers to a different underlying type is completely valid C++, +within the type restrictions for the cast. + +Programmers wanting to cast `multi_ptr` to `multi_ptr` +don't have many options to do so in SYCL 1.2.1. +There are only two specified casts in SYCL 1.2.1: + * Using `static_cast` to cast `multi_ptr` to `multi_ptr` + (defined through an explicit conversion operator) + * Using `static_cast` to cast `multi_ptr` to `multi_ptr` + (defined through an explicit conversion operator) + +Therefore, the only option to perform a cast from +`multi_ptr` to `multi_ptr` +is to use both casts like so: +```cpp +// Using global_ptr here to simplify the code +// decltype(multiPtrA) == global_ptr +auto multiPtrB = static_cast>( + static_cast>(multiPtrA)); +``` + +This is problematic on a few levels: + * Verbosity + * Only allows static casts + * Allows casting the underlying `A*` pointer to a `B*` pointer + even if the type system forbids it + * Does not handle `const` qualifiers + +Therefore, there is a clear need to provide more casting options for the `multi_ptr` class +in order to make the casting safer and easier to use. + +## Summary + +This proposal adds a few explicit conversion operators +as member functions of the `multi_ptr` class, +and also adds several free functions to the `cl::sycl` namespace +that follow the naming and semantics of the `std::shared_ptr` pointer cast functions +defined by the C++17 standard: https://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast. + +## Explicit conversion operators + +The new interface of the `multi_ptr` class would look like this: + +```cpp +namespace cl { +namespace sycl { + +template +class multi_ptr { + public: + /// All existing members here + + ... + + // Explicit conversion to `multi_ptr` + template + explicit operator multi_ptr() const; + + // Implicit conversion to `multi_ptr` + operator multi_ptr() const; +}; + +} // namespace sycl +} // namespace cl +``` + +The conversion operator to `multi_ptr` replaces +the existing explicit conversion to `multi_ptr`. + +| Member function | Description | +|-----------------|-------------| +| *`template explicit operator multi_ptr() const`* | Performs a static cast of the underlying pointer `ElementType*` to `ElementTypeU*` and returns a new `multi_ptr` instance containing the cast pointer. The address space stays the same. This conversion is only valid if the `static_cast` from `ElementType*` to `ElementTypeU*` is valid. | +| *`operator multi_ptr() const`* | Performs a static cast of the underlying pointer `ElementType*` to `const ElementType*` and returns a new `multi_ptr` instance containing the cast pointer. The address space stays the same. This conversion is implicit because it is always valid to add a `const` qualifier. | + +## Conversion functions + +In addition to the conversion operators, +we propose adding the following free functions to the `cl::sycl` namespace: + +```cpp +namespace cl { +namespace sycl { + +// Performs a static_cast of the contained pointer +template +multi_ptr + static_pointer_cast(const multi_ptr& multiPtr); + +// Performs a dynamic_cast of the contained pointer +template +multi_ptr + dynamic_pointer_cast(const multi_ptr& multiPtr); + +// Performs a const_cast of the contained pointer +template +multi_ptr + const_pointer_cast(const multi_ptr& multiPtr); + +// Performs a reinterpret_cast of the contained pointer +template +multi_ptr + reinterpret_pointer_cast(const multi_ptr& multiPtr); + +} // namespace sycl +} // namespace cl +``` + +In the table below, each function has the following template parameters: +```cpp +template +``` + +| Function | Description | +|-----------------|-------------| +| *`multi_ptr static_pointer_cast(const multi_ptr& multiPtr)`* | Performs a `static_cast` of the underlying pointer `ElementTypeT*` contained within `multiPtr` to `ElementTypeU*` and returns a new `multi_ptr` instance containing the cast pointer. The address space stays the same. This conversion is only valid if the `static_cast` from `ElementType*` to `ElementTypeU*` is valid. | +| *`multi_ptr dynamic_pointer_cast(const multi_ptr& multiPtr)`* | Performs a `dynamic_cast` of the underlying pointer `ElementTypeT*` contained within `multiPtr` to `ElementTypeU*` and returns a new `multi_ptr` instance containing the cast pointer. The address space stays the same. This conversion is only valid if the `dynamic_cast` from `ElementType*` to `ElementTypeU*` is valid. | +| *`multi_ptr const_pointer_cast(const multi_ptr& multiPtr)`* | Performs a `const_cast` of the underlying pointer `ElementTypeT*` contained within `multiPtr` to `ElementTypeU*` and returns a new `multi_ptr` instance containing the cast pointer. The address space stays the same. This conversion is only valid if the `const_cast` from `ElementType*` to `ElementTypeU*` is valid. | +| *`multi_ptr reinterpret_pointer_cast(const multi_ptr& multiPtr)`* | Performs a `reinterpret_cast` of the underlying pointer `ElementTypeT*` contained within `multiPtr` to `ElementTypeU*` and returns a new `multi_ptr` instance containing the cast pointer. The address space stays the same. This conversion is only valid if the `reinterpret_cast` from `ElementType*` to `ElementTypeU*` is valid. | + +## Examples + +### Simple casts + +These examples focus on `global_ptr` for brevity, +but the same operation is valid on any other `multi_ptr` type. + +```cpp +using namespace cl::sycl; + +const global_ptr ptrInt = get_some_global_ptr(); + +// Conversion operator +auto ptrVoid1 = static_cast>(ptrInt); +auto ptrConstInt1 = static_cast>(ptrInt); + +// static_pointer_cast +global_ptr ptrVoid2 = + static_pointer_cast(ptrInt); +global_ptr ptrConstInt2 = + static_pointer_cast(ptrInt); + +// const_pointer_cast +global_ptr ptrConstInt3 = + const_pointer_cast(ptrInt); +// global_ptr ptrIntStripConst = +// static_cast>(ptrConstInt1); // illegal +global_ptr ptrIntStripConst = + const_pointer_cast(ptrConstInt1); + +// reinterpret_pointer_cast +global_ptr ptrFloat4 = + reinterpret_pointer_cast(ptrInt); +global_ptr ptrVoid4 = + reinterpret_pointer_cast(ptrInt); +global_ptr ptrConstInt4 = + reinterpret_pointer_cast(ptrInt); +``` + +### `dynamic_pointer_cast` + +```cpp +struct Base { + virtual void foo() const {} + virtual ~Base(){} +}; +struct Derived : public Base { + void foo() const override {} +}; + +using namespace cl::sycl; +const global_ptr ptrBase = get_some_global_ptr(); + +auto ptrDerived = dynamic_pointer_cast(ptrBase); +auto ptrBase1 = dynamic_pointer_cast(ptrDerived); +``` + +### Passing `multi_ptr` to functions + +```cpp +using namespace cl::sycl; + +template +void function_taking_ptr(const multi_ptr& ptr); +template +void function_taking_const_ptr(const multi_ptr& ptr); + +const global_ptr ptrInt = get_some_global_ptr(); + +function_taking_ptr(ptrInt); +// Implicit conversion to global_ptr: +function_taking_const_ptr(ptrInt); + +const global_ptr ptrFloat = get_some_global_ptr(); + +// Convert float to int pointer +function_taking_ptr(reinterpret_ptr_cast(ptrFloat)); +// First explicit conversion to global_ptr +// and then implicit conversion to global_ptr: +function_taking_const_ptr(reinterpret_ptr_cast(ptrFloat)); + +```