Skip to content

Commit ad47d5e

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 b15fa36 commit ad47d5e

File tree

3 files changed

+339
-0
lines changed

3 files changed

+339
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,4 @@ Each proposal in the table below will be tagged with one of the following states
5757
| 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_ |
5858
| 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_ |
5959
| 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_ |
60+
| CP021 | [Casting accessors](accessor-cast/index.md) | SYCL 1.2.1 vendor extension | 21 September 2019 | 21 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 | CP021 |
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

+318
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
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_const` -
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+
/// add_const_access
48+
template <access::mode mode>
49+
struct add_const_access {
50+
static constexpr auto value = access::mode::read;
51+
};
52+
template <>
53+
struct add_const_access<access::mode::atomic> {
54+
static constexpr auto value = access::mode::atomic;
55+
};
56+
57+
/// remove_const_access
58+
template <access::mode mode>
59+
struct remove_const_access {
60+
static constexpr auto value = mode;
61+
};
62+
template <>
63+
struct remove_const_access<access::mode::read> {
64+
static constexpr auto value = access::mode::read_write;
65+
};
66+
67+
/// access_mode_from_const
68+
template <access::mode mode, typename oldT, typename newT>
69+
struct access_mode_from_const {
70+
static constexpr auto value = mode;
71+
};
72+
template <access::mode mode, typename oldT, typename newT>
73+
struct access_mode_from_const<
74+
mode, const oldT, const newT>
75+
: access_mode_from_const<
76+
mode, oldT, newT> {};
77+
template <access::mode mode, typename oldT, typename newT>
78+
struct access_mode_from_const<
79+
mode, const oldT, newT>
80+
: access_mode_from_const<
81+
remove_const_access<mode>::value, oldT, newT> {};
82+
template <access::mode mode, typename oldT, typename newT>
83+
struct access_mode_from_const<
84+
mode, oldT, const newT>
85+
: access_mode_from_const<
86+
add_const_access<mode>::value, oldT, newT> {};
87+
88+
} // namespace sycl
89+
} // namespace cl
90+
```
91+
92+
## Conversion operator
93+
94+
The new interface of the `accessor` class would look like this:
95+
96+
```cpp
97+
namespace cl {
98+
namespace sycl {
99+
100+
template <typename dataT,
101+
int dimensions,
102+
access::mode accMode,
103+
access::target accTarget,
104+
access::placeholder isPlaceholder>
105+
class accessor {
106+
public:
107+
/// All existing members here
108+
109+
...
110+
111+
// Implicit conversion to `const dataT`
112+
operator accessor<const dataT,
113+
dimensions,
114+
add_const_access<dataT>::value,
115+
accTarget,
116+
isPlaceholder>() const;
117+
};
118+
119+
} // namespace sycl
120+
} // namespace cl
121+
```
122+
123+
| Member function | Description |
124+
|-----------------|-------------|
125+
| *`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. |
126+
127+
## Conversion functions
128+
129+
In addition to the conversion operator,
130+
we propose adding the following free functions to the `cl::sycl` namespace:
131+
132+
```cpp
133+
namespace cl {
134+
namespace sycl {
135+
136+
// Performs a static_cast of the contained pointer
137+
template <typename dataU,
138+
typename dataT,
139+
int dimensions,
140+
access::mode accMode,
141+
access::target accTarget,
142+
access::placeholder isPlaceholder>
143+
accessor<dataU,
144+
dimensions,
145+
access_mode_from_const<accMode, dataT, dataU>::value,
146+
accTarget,
147+
isPlaceholder>
148+
static_pointer_cast(
149+
const accessor<
150+
dataT, dimensions, accMode, accTarget, isPlaceholder>&
151+
acc);
152+
153+
// Performs a dynamic_cast of the contained pointer
154+
template <typename dataU,
155+
typename dataT,
156+
int dimensions,
157+
access::mode accMode,
158+
access::target accTarget,
159+
access::placeholder isPlaceholder>
160+
accessor<dataU,
161+
dimensions,
162+
accMode,
163+
accTarget,
164+
isPlaceholder>
165+
dynamic_pointer_cast(
166+
const accessor<
167+
dataT, dimensions, accMode, accTarget, isPlaceholder>&
168+
acc);
169+
170+
// Performs a const_cast of the contained pointer
171+
template <typename dataU,
172+
typename dataT,
173+
int dimensions,
174+
access::mode accMode,
175+
access::target accTarget,
176+
access::placeholder isPlaceholder>
177+
accessor<dataU,
178+
dimensions,
179+
access_mode_from_const<accMode, dataT, dataU>::value,
180+
accTarget,
181+
isPlaceholder>
182+
const_pointer_cast(
183+
const accessor<
184+
dataT, dimensions, accMode, accTarget, isPlaceholder>&
185+
acc);
186+
187+
// Performs a reinterpret_cast of the contained pointer
188+
template <typename dataU,
189+
typename dataT,
190+
int dimensions,
191+
access::mode accMode,
192+
access::target accTarget,
193+
access::placeholder isPlaceholder>
194+
accessor<dataU,
195+
dimensions,
196+
accMode,
197+
accTarget,
198+
isPlaceholder>
199+
reinterpret_pointer_cast(
200+
const accessor<
201+
dataT, dimensions, accMode, accTarget, isPlaceholder>&
202+
acc);
203+
204+
} // namespace sycl
205+
} // namespace cl
206+
```
207+
208+
The following function declarations are simplified
209+
in order to reduce table verbosity.
210+
For full declarations see the code above.
211+
212+
| Function | Description |
213+
|-----------------|-------------|
214+
| *`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. |
215+
| *`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. |
216+
| *`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_const` 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. |
217+
| *`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. |
218+
219+
## Examples
220+
221+
All examples use
222+
`dimensions == 1`,
223+
`accTarget == access::target::global_buffer`,
224+
and `isPlaceholder == access::placeholder::false_t`,
225+
but they should generally apply to all accessor types.
226+
227+
### Simple casts
228+
229+
```cpp
230+
using namespace cl::sycl;
231+
static constexpr auto accTarget = access::target::global_buffer;
232+
233+
// Obtain some accessors
234+
accessor<int, 1, access::mode::read, accTarget>
235+
accIntR =
236+
buf.get_access<access::mode::read, accTarget>(cgh);
237+
accessor<int, 1, access::mode::write, accTarget>
238+
accIntW =
239+
buf.get_access<access::mode::write, accTarget>(cgh);
240+
accessor<int, 1, access::mode::read_write, accTarget>
241+
accIntRW =
242+
buf.get_access<access::mode::read_write, accTarget>(cgh);
243+
accessor<const int, 1, access::mode::read, accTarget>
244+
accIntCR =
245+
buf.get_access<access::mode::read, accTarget>(cgh);
246+
247+
// Conversion operator
248+
auto rAccIntCR = static_cast<
249+
accessor<const int, 1, access::mode::read, accTarget>>(
250+
accIntR);
251+
auto rAccIntCW = static_cast<
252+
accessor<const int, 1, access::mode::read, accTarget>>(
253+
accIntW);
254+
auto rAccIntCRW = static_cast<
255+
accessor<const int, 1, access::mode::read, accTarget>>(
256+
accIntRW);
257+
auto rAccIntCCR = static_cast<
258+
accessor<const int, 1, access::mode::read, accTarget>>(
259+
accIntCR);
260+
261+
// static_pointer_cast
262+
accessor<const int, 1, access::mode::read, accTarget>
263+
rAccIntCR2 = static_pointer_cast<const int>(accIntR);
264+
accessor<const int, 1, access::mode::read, accTarget>
265+
rAccIntCW2 = static_pointer_cast<const int>(accIntW);
266+
accessor<const int, 1, access::mode::read, accTarget>
267+
rAccIntCRW2 = static_pointer_cast<const int>(accIntRW);
268+
accessor<const int, 1, access::mode::read, accTarget>
269+
rAccIntCCR2 = static_pointer_cast<const int>(accIntCR);
270+
271+
// const_pointer_cast
272+
accessor<int, 1, access::mode::read_write, accTarget>
273+
rAccIntR = const_pointer_cast<int>(accIntR);
274+
accessor<int, 1, access::mode::write, accTarget>
275+
rAccIntW = const_pointer_cast<int>(accIntW);
276+
accessor<int, 1, access::mode::read_write, accTarget>
277+
rAccIntRW = const_pointer_cast<int>(accIntRW);
278+
accessor<int, 1, access::mode::read_write, accTarget>
279+
rAccIntCR3 = const_pointer_cast<int>(accIntCR);
280+
281+
// reinterpret_pointer_cast
282+
accessor<float, 1, access::mode::read, accTarget>
283+
rAccFloatR = reinterpret_pointer_cast<float>(accIntR);
284+
accessor<float, 1, access::mode::write, accTarget>
285+
rAccFloatW = reinterpret_pointer_cast<float>(accIntW);
286+
accessor<float, 1, access::mode::read_write, accTarget>
287+
rAccFloatRW = reinterpret_pointer_cast<float>(accIntRW);
288+
accessor<const float, 1, access::mode::read, accTarget>
289+
rAccFloatCR3 = reinterpret_pointer_cast<const float>(accIntCR);
290+
291+
```
292+
293+
### `dynamic_pointer_cast`
294+
295+
All of these examples also use the same access mode for all casts.
296+
297+
```cpp
298+
using namespace cl::sycl;
299+
static constexpr auto accMode = access::mode::read_write;
300+
static constexpr auto accTarget = access::target::global_buffer;
301+
302+
struct Base {
303+
virtual void foo() const {}
304+
virtual ~Base(){}
305+
};
306+
struct Derived : public Base {
307+
void foo() const override {}
308+
};
309+
310+
accessor<Base, 1, accMode, accTarget>
311+
accBase =
312+
buf.get_access<accMode, accTarget>(cgh);
313+
314+
accessor<Derived, 1, accMode, accTarget>
315+
accDerived = dynamic_pointer_cast<Derived>(accBase);
316+
accessor<Base, 1, accMode, accTarget>
317+
accBase2 = dynamic_pointer_cast<Base>(accDerived);
318+
```

0 commit comments

Comments
 (0)