Skip to content

Commit a0bc20f

Browse files
committed
Casting accessors
1. Implicit conversion to `const dataT` 2. Type traits for `const` conversions * For resolving access modes 3. Free functions for casting accessors * `static_pointer_cast`, `dynamic_pointer_cast`, `const_pointer_cast`, `reinterpret_pointer_cast` 4. Converting to a type of a different size requires `get_count`, `get_range`, and `get_offset` to return different values than the original accessor 5. Examples for all casts
1 parent c9c87e8 commit a0bc20f

File tree

3 files changed

+340
-0
lines changed

3 files changed

+340
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,4 @@ from this registry in the future.
6262
| 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_ |
6363
| CP020 | [Interop Task](interop_task/interop_task.md) | SYCL 1.2.1 | 16 January 2019 | 16 January 2019 | _Available since CE 1.0.5_ |
6464
| CP021 | [Default-Constructed Buffers](default-constructed-buffers/default-constructed-buffers.md) | SYCL 1.2.1 | 27 August 2019 | 5 September 2019 | _Draft_ |
65+
| CP022 | [Casting accessors](accessor-cast/index.md) | SYCL 1.2.1 vendor extension | 21 September 2019 | 22 September 2019 | _Work in Progress_ |

accessor-cast/index.md

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Casting accessors
2+
3+
| Proposal ID | CP022 |
4+
|-------------|--------|
5+
| Name | Casting accessors |
6+
| Date of Creation | 21 September 2019 |
7+
| Target | SYCL 1.2.1 vendor extension |
8+
| Current Status | _Work in Progress_ |
9+
| Reply-to | Peter Žužek <[email protected]> |
10+
| Original author | Peter Žužek <[email protected]> |
11+
| Contributors | |
12+
13+
## Overview
14+
15+
This paper proposes the addition of pointer casting functions to SYCL
16+
in order to simplify casting of the `accessor` class.
17+
18+
## Versions
19+
20+
[Version 1](sycl-2.2/index.md)

accessor-cast/sycl-2.2/index.md

+319
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
# Casting accessors
2+
3+
## Motivation
4+
5+
The current SYCL 1.2.1 specification allows casting buffers
6+
using the `reinterpret` member function.
7+
This essentially provides a different view of the same data,
8+
however it cannot be performed inside a kernel.
9+
10+
One option would be to cast the `multi_ptr` obtained from an accessor.
11+
However, that is a separate proposal,
12+
plus it would add another layer of first obtaining the pointer
13+
and then using that pointer instead of the accessor.
14+
15+
## Summary
16+
17+
This proposal adds an implicit conversion operator
18+
to a const-qualified accessor type
19+
as a member function of the `accessor` class,
20+
and also adds several free functions to the `cl::sycl` namespace
21+
that follow the naming and semantics of the `std::shared_ptr` pointer cast functions
22+
defined by the C++17 standard: https://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast.
23+
In order to help with the `const` conversions
24+
we also propose adding a few type traits.
25+
26+
## Const casting type traits
27+
28+
In order to help with casting the const qualifier,
29+
we propose adding a few type traits:
30+
1. `add_const_access` -
31+
resolves any access mode to `access::mode::read`,
32+
except for `access::mode::atomic`,
33+
which resolves to the same mode.
34+
2. `remove_const_access` -
35+
resolves `access::mode::read` to `access::mode::read_write`,
36+
and keeps all other modes the same.
37+
3. `access_mode_from_cast` -
38+
takes an access mode and two types
39+
and then decides, based on the types,
40+
whether to use `add_const_access`, `remove_const_access`, or none,
41+
in order to resolve the access mode.
42+
43+
```cpp
44+
namespace cl {
45+
namespace sycl {
46+
47+
/// access_mode_constant
48+
template <access::mode mode>
49+
using access_mode_constant =
50+
std::integral_constant<access::mode, mode>;
51+
52+
/// add_const_access
53+
template <access::mode mode>
54+
struct add_const_access
55+
: access_mode_constant<access::mode::read> {};
56+
template <>
57+
struct add_const_access<access::mode::atomic>
58+
: access_mode_constant<access::mode::atomic> {};
59+
60+
/// remove_const_access
61+
template <access::mode mode>
62+
struct remove_const_access
63+
: access_mode_constant<mode> {};
64+
template <>
65+
struct remove_const_access<access::mode::read>
66+
: access_mode_constant<access::mode::read_write> {};
67+
68+
/// access_mode_from_cast
69+
template <access::mode mode, typename oldT, typename newT>
70+
struct access_mode_from_cast
71+
: access_mode_constant<mode> {};
72+
template <access::mode mode, typename oldT, typename newT>
73+
struct access_mode_from_cast<mode, const oldT, const newT>
74+
: access_mode_constant<mode> {};
75+
template <access::mode mode, typename oldT, typename newT>
76+
struct access_mode_from_cast<mode, const oldT, newT>
77+
: remove_const_access<mode> {};
78+
template <access::mode mode, typename oldT, typename newT>
79+
struct access_mode_from_cast<mode, oldT, const newT>
80+
: add_const_access<mode> {};
81+
82+
} // namespace sycl
83+
} // namespace cl
84+
```
85+
86+
| Type trait | Description |
87+
|-----------------|-------------|
88+
| *`template <access::mode mode> access_mode_constant`* | Alias that stores `access::mode` into `std::integral_constant`. |
89+
| *`template <access::mode mode> add_const_access`* | Used when casting an accessor by adding `const` to the type, deduces the new access mode. Resolves any access mode to `access::mode::read`, except for `access::mode::atomic`, which resolves to the same mode. |
90+
| *`template <access::mode mode> remove_const_access`* | Used when casting an accessor by removing `const` from the type, deduces the new access mode. Resolves `access::mode::read` to `access::mode::read_write`, and keeps all other modes the same, since other modes already allow write access. |
91+
| *`template <access::mode mode, typename oldT, typename newT> access_mode_from_cast`* | Used when casting an accessor from `oldT` to `newT`, deduces the new access mode. If both types have the same constness, `mode` is deduced as the new access mode. Otherwise it uses either `add_const_access` or `remove_const_access` to deduce the new access mode based on how the constness changed. |
92+
93+
## Conversion operator
94+
95+
The new interface of the `accessor` class would look like this:
96+
97+
```cpp
98+
namespace cl {
99+
namespace sycl {
100+
101+
template <typename dataT,
102+
int dimensions,
103+
access::mode accMode,
104+
access::target accTarget,
105+
access::placeholder isPlaceholder>
106+
class accessor {
107+
public:
108+
/// All existing members here
109+
110+
...
111+
112+
// Implicit conversion to `const dataT`
113+
operator accessor<const dataT,
114+
dimensions,
115+
add_const_access<dataT>::value,
116+
accTarget,
117+
isPlaceholder>() const;
118+
};
119+
120+
} // namespace sycl
121+
} // namespace cl
122+
```
123+
124+
| Member function | Description |
125+
|-----------------|-------------|
126+
| *`operator accessor<const dataT, dimensions, add_const_access<accMode>::value, accTarget, isPlaceholder>() const`* | Returns a new accessor of type `const dataT`. The access mode of the new accessor is a read-only mode. |
127+
128+
## Conversion functions
129+
130+
In addition to the conversion operator,
131+
we propose adding the following free functions to the `cl::sycl` namespace:
132+
133+
```cpp
134+
namespace cl {
135+
namespace sycl {
136+
137+
// Performs a static_cast of the contained pointer
138+
template <typename dataU,
139+
typename dataT,
140+
int dimensions,
141+
access::mode accMode,
142+
access::target accTarget,
143+
access::placeholder isPlaceholder>
144+
accessor<dataU,
145+
dimensions,
146+
access_mode_from_cast<accMode, dataT, dataU>::value,
147+
accTarget,
148+
isPlaceholder>
149+
static_pointer_cast(
150+
const accessor<
151+
dataT, dimensions, accMode, accTarget, isPlaceholder>&
152+
acc);
153+
154+
// Performs a dynamic_cast of the contained pointer
155+
template <typename dataU,
156+
typename dataT,
157+
int dimensions,
158+
access::mode accMode,
159+
access::target accTarget,
160+
access::placeholder isPlaceholder>
161+
accessor<dataU,
162+
dimensions,
163+
accMode,
164+
accTarget,
165+
isPlaceholder>
166+
dynamic_pointer_cast(
167+
const accessor<
168+
dataT, dimensions, accMode, accTarget, isPlaceholder>&
169+
acc);
170+
171+
// Performs a const_cast of the contained pointer
172+
template <typename dataU,
173+
typename dataT,
174+
int dimensions,
175+
access::mode accMode,
176+
access::target accTarget,
177+
access::placeholder isPlaceholder>
178+
accessor<dataU,
179+
dimensions,
180+
access_mode_from_cast<accMode, dataT, dataU>::value,
181+
accTarget,
182+
isPlaceholder>
183+
const_pointer_cast(
184+
const accessor<
185+
dataT, dimensions, accMode, accTarget, isPlaceholder>&
186+
acc);
187+
188+
// Performs a reinterpret_cast of the contained pointer
189+
template <typename dataU,
190+
typename dataT,
191+
int dimensions,
192+
access::mode accMode,
193+
access::target accTarget,
194+
access::placeholder isPlaceholder>
195+
accessor<dataU,
196+
dimensions,
197+
accMode,
198+
accTarget,
199+
isPlaceholder>
200+
reinterpret_pointer_cast(
201+
const accessor<
202+
dataT, dimensions, accMode, accTarget, isPlaceholder>&
203+
acc);
204+
205+
} // namespace sycl
206+
} // namespace cl
207+
```
208+
209+
The following function declarations are simplified
210+
in order to reduce table verbosity.
211+
For full declarations see the code above.
212+
213+
| Function | Description |
214+
|-----------------|-------------|
215+
| *`template <typename dataU, typename dataT, ...> accessor<dataU, ...> static_pointer_cast(const accessor<dataT, ...>& acc)`* | Performs a `static_cast` of the underlying pointer `dataT*` contained within `acc` to `dataU*` and returns a new `accessor` instance containing the cast pointer. When casting from a non-`const` to a `const` type, `add_const_access` is used to resolve the access mode. All other template parameters stay the same. This conversion is only valid if the `static_cast` from `dataT*` to `dataU*` is valid. |
216+
| *`template <typename dataU, typename dataT, ...> accessor<dataU, ...> dynamic_pointer_cast(const accessor<dataT, ...>& acc)`* | Performs a `dynamic_cast` of the underlying pointer `dataT*` contained within `acc` to `dataU*` and returns a new `accessor` instance containing the cast pointer. All other template parameters stay the same. This conversion is only valid if the `dynamic_cast` from `dataT*` to `dataU*` is valid. If `sizeof(dataT) != sizeof(dataU)`, member functions `get_count()`, `get_range()`, and `get_offset()` of the returned accessor return values appropriate to the new type size. |
217+
| *`template <typename dataU, typename dataT, ...> accessor<dataU, ...> const_pointer_cast(const accessor<dataT, ...>& acc)`* | Performs a `const_cast` of the underlying pointer `dataT*` contained within `acc` to `dataU*` and returns a new `accessor` instance containing the cast pointer. `access_mode_from_cast` is used to resolve the access mode. All other template parameters stay the same. This conversion is only valid if the `const_cast` from `dataT*` to `dataU*` is valid. |
218+
| *`template <typename dataU, typename dataT, ...> accessor<dataU, ...> reinterpret_pointer_cast(const accessor<dataT, ...>& acc)`* | Performs a `reinterpret_cast` of the underlying pointer `dataT*` contained within `acc` to `dataU*` and returns a new `accessor` instance containing the cast pointer. All other template parameters stay the same. This conversion is only valid if the `reinterpret_cast` from `dataT*` to `dataU*` is valid. If `sizeof(dataT) != sizeof(dataU)`, member functions `get_count()`, `get_range()`, and `get_offset()` of the returned accessor return values appropriate to the new type size. |
219+
220+
## Examples
221+
222+
All examples use
223+
`dimensions == 1`,
224+
`accTarget == access::target::global_buffer`,
225+
and `isPlaceholder == access::placeholder::false_t`,
226+
but they should generally apply to all accessor types.
227+
228+
### Simple casts
229+
230+
```cpp
231+
using namespace cl::sycl;
232+
static constexpr auto accTarget = access::target::global_buffer;
233+
234+
// Obtain some accessors
235+
accessor<int, 1, access::mode::read, accTarget>
236+
accIntR =
237+
buf.get_access<access::mode::read, accTarget>(cgh);
238+
accessor<int, 1, access::mode::write, accTarget>
239+
accIntW =
240+
buf.get_access<access::mode::write, accTarget>(cgh);
241+
accessor<int, 1, access::mode::read_write, accTarget>
242+
accIntRW =
243+
buf.get_access<access::mode::read_write, accTarget>(cgh);
244+
accessor<const int, 1, access::mode::read, accTarget>
245+
accIntCR =
246+
buf.get_access<access::mode::read, accTarget>(cgh);
247+
248+
// Conversion operator
249+
auto rAccIntCR = static_cast<
250+
accessor<const int, 1, access::mode::read, accTarget>>(
251+
accIntR);
252+
auto rAccIntCW = static_cast<
253+
accessor<const int, 1, access::mode::read, accTarget>>(
254+
accIntW);
255+
auto rAccIntCRW = static_cast<
256+
accessor<const int, 1, access::mode::read, accTarget>>(
257+
accIntRW);
258+
auto rAccIntCCR = static_cast<
259+
accessor<const int, 1, access::mode::read, accTarget>>(
260+
accIntCR);
261+
262+
// static_pointer_cast
263+
accessor<const int, 1, access::mode::read, accTarget>
264+
rAccIntCR2 = static_pointer_cast<const int>(accIntR);
265+
accessor<const int, 1, access::mode::read, accTarget>
266+
rAccIntCW2 = static_pointer_cast<const int>(accIntW);
267+
accessor<const int, 1, access::mode::read, accTarget>
268+
rAccIntCRW2 = static_pointer_cast<const int>(accIntRW);
269+
accessor<const int, 1, access::mode::read, accTarget>
270+
rAccIntCCR2 = static_pointer_cast<const int>(accIntCR);
271+
272+
// const_pointer_cast
273+
accessor<int, 1, access::mode::read_write, accTarget>
274+
rAccIntR = const_pointer_cast<int>(accIntR);
275+
accessor<int, 1, access::mode::write, accTarget>
276+
rAccIntW = const_pointer_cast<int>(accIntW);
277+
accessor<int, 1, access::mode::read_write, accTarget>
278+
rAccIntRW = const_pointer_cast<int>(accIntRW);
279+
accessor<int, 1, access::mode::read_write, accTarget>
280+
rAccIntCR3 = const_pointer_cast<int>(accIntCR);
281+
282+
// reinterpret_pointer_cast
283+
accessor<float, 1, access::mode::read, accTarget>
284+
rAccFloatR = reinterpret_pointer_cast<float>(accIntR);
285+
accessor<float, 1, access::mode::write, accTarget>
286+
rAccFloatW = reinterpret_pointer_cast<float>(accIntW);
287+
accessor<float, 1, access::mode::read_write, accTarget>
288+
rAccFloatRW = reinterpret_pointer_cast<float>(accIntRW);
289+
accessor<const float, 1, access::mode::read, accTarget>
290+
rAccFloatCR3 = reinterpret_pointer_cast<const float>(accIntCR);
291+
292+
```
293+
294+
### `dynamic_pointer_cast`
295+
296+
All of these examples also use the same access mode for all casts.
297+
298+
```cpp
299+
using namespace cl::sycl;
300+
static constexpr auto accMode = access::mode::read_write;
301+
static constexpr auto accTarget = access::target::global_buffer;
302+
303+
struct Base {
304+
virtual void foo() const {}
305+
virtual ~Base(){}
306+
};
307+
struct Derived : public Base {
308+
void foo() const override {}
309+
};
310+
311+
accessor<Base, 1, accMode, accTarget>
312+
accBase =
313+
buf.get_access<accMode, accTarget>(cgh);
314+
315+
accessor<Derived, 1, accMode, accTarget>
316+
accDerived = dynamic_pointer_cast<Derived>(accBase);
317+
accessor<Base, 1, accMode, accTarget>
318+
accBase2 = dynamic_pointer_cast<Base>(accDerived);
319+
```

0 commit comments

Comments
 (0)