DPC++ Runtime
Runtime libraries for oneAPI DPC++
pipes.hpp
Go to the documentation of this file.
1 //==---------------- pipes.hpp - SYCL pipes ------------*- C++ -*-----------==//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 // ===--------------------------------------------------------------------=== //
8 
9 #pragma once
10 
11 #include <sycl/detail/export.hpp> // for __SYCL_EX...
12 #include <sycl/device.hpp> // for device
13 #include <sycl/event.hpp> // for event
14 #include <sycl/exception.hpp> // for make_erro...
15 #include <sycl/ext/intel/experimental/pipe_properties.hpp> // for protocol_...
16 #include <sycl/ext/oneapi/properties/properties.hpp> // for ValueOrDe...
17 #include <sycl/handler.hpp> // for handler
18 #include <sycl/info/info_desc.hpp> // for event_com...
19 #include <sycl/memory_enums.hpp> // for memory_order
20 #include <sycl/queue.hpp> // for queue
21 
22 #ifdef __SYCL_DEVICE_ONLY__
25 #endif
26 
27 #ifdef XPTI_ENABLE_INSTRUMENTATION
28 #include <xpti/xpti_data_types.h>
29 #include <xpti/xpti_trace_framework.hpp>
30 #endif
31 
32 #include <stdint.h> // for int32_t
33 #include <string> // for string
34 #include <tuple> // for _Swallow_...
35 
36 namespace sycl {
37 inline namespace _V1 {
38 namespace ext {
39 namespace intel {
40 namespace experimental {
41 
42 // A helper templateless base class.
43 class pipe_base {
44 
45 protected:
48 
49  __SYCL_EXPORT static std::string get_pipe_name(const void *HostPipePtr);
50  __SYCL_EXPORT static bool wait_non_blocking(const event &E);
51 };
52 
53 template <class _name, class _dataT, int32_t _min_capacity = 0,
54  class _propertiesT = decltype(oneapi::experimental::properties{}),
55  class = void>
56 class pipe : public pipe_base {
57 public:
58  struct
59 #ifdef __SYCL_DEVICE_ONLY__
60  [[__sycl_detail__::add_ir_attributes_global_variable(
61  "sycl-host-pipe", "sycl-host-pipe-size", nullptr,
62  sizeof(_dataT))]] [[__sycl_detail__::sycl_type(host_pipe)]]
63 #endif // __SYCL_DEVICE_ONLY___
65 #ifdef __SYCL_DEVICE_ONLY__
66  : ConstantPipeStorage
67 #endif // __SYCL_DEVICE_ONLY___
68  {
69  int32_t _ReadyLatency;
70  int32_t _BitsPerSymbol;
71  bool _UsesValid;
74  };
75 
76  // Non-blocking pipes
77 
78  // Host API
79  static _dataT read(queue &Q, bool &Success,
81  // Order is currently unused.
82  std::ignore = Order;
83 
84  const device Dev = Q.get_device();
85  bool IsPipeSupported =
86  Dev.has_extension("cl_intel_program_scope_host_pipe");
87  if (!IsPipeSupported) {
88  return _dataT();
89  }
90  _dataT Data;
91  void *DataPtr = &Data;
92  const void *HostPipePtr = &m_Storage;
93  const std::string PipeName = pipe_base::get_pipe_name(HostPipePtr);
94 
95  event E = Q.submit([=](handler &CGH) {
96  CGH.ext_intel_read_host_pipe(PipeName, DataPtr,
97  sizeof(_dataT) /* non-blocking */);
98  });
99  // In OpenCL 1.0 waiting for a failed event does not return an error, so we
100  // need to check the execution status here as well.
101  Success = wait_non_blocking(E) &&
102  E.get_info<sycl::info::event::command_execution_status>() ==
103  sycl::info::event_command_status::complete;
104  ;
105  return Success ? *(_dataT *)DataPtr : _dataT();
106  }
107 
108  static void write(queue &Q, const _dataT &Data, bool &Success,
110  // Order is currently unused.
111  std::ignore = Order;
112 
113  const device Dev = Q.get_device();
114  bool IsPipeSupported =
115  Dev.has_extension("cl_intel_program_scope_host_pipe");
116  if (!IsPipeSupported) {
117  return;
118  }
119 
120  const void *HostPipePtr = &m_Storage;
121  const std::string PipeName = pipe_base::get_pipe_name(HostPipePtr);
122  void *DataPtr = const_cast<_dataT *>(&Data);
123 
124  event E = Q.submit([=](handler &CGH) {
125  CGH.ext_intel_write_host_pipe(PipeName, DataPtr,
126  sizeof(_dataT) /* non-blocking */);
127  });
128  // In OpenCL 1.0 waiting for a failed event does not return an error, so we
129  // need to check the execution status here as well.
130  Success = wait_non_blocking(E) &&
131  E.get_info<sycl::info::event::command_execution_status>() ==
132  sycl::info::event_command_status::complete;
133  }
134 
135  // Reading from pipe is lowered to SPIR-V instruction OpReadPipe via SPIR-V
136  // friendly LLVM IR.
137  template <typename _functionPropertiesT>
138  static _dataT read(bool &Success, _functionPropertiesT) {
139 #ifdef __SYCL_DEVICE_ONLY__
140  // Get latency control properties
141  using _latency_anchor_id_prop = typename detail::GetOrDefaultValT<
142  _functionPropertiesT, latency_anchor_id_key,
144  using _latency_constraint_prop = typename detail::GetOrDefaultValT<
145  _functionPropertiesT, latency_constraint_key,
147 
148  // Get latency control property values
149  static constexpr int32_t _anchor_id = _latency_anchor_id_prop::value;
150  static constexpr int32_t _target_anchor = _latency_constraint_prop::target;
151  static constexpr latency_control_type _control_type =
152  _latency_constraint_prop::type;
153  static constexpr int32_t _relative_cycle = _latency_constraint_prop::cycle;
154 
155  int32_t _control_type_code = 0; // latency_control_type::none is default
156  if constexpr (_control_type == latency_control_type::exact) {
157  _control_type_code = 1;
158  } else if constexpr (_control_type == latency_control_type::max) {
159  _control_type_code = 2;
160  } else if constexpr (_control_type == latency_control_type::min) {
161  _control_type_code = 3;
162  }
163 
164  __ocl_RPipeTy<_dataT> _RPipe =
165  __spirv_CreatePipeFromPipeStorage_read<_dataT>(&m_Storage);
166  _dataT TempData;
167  Success = !static_cast<bool>(__latency_control_nb_read_wrapper(
168  _RPipe, &TempData, _anchor_id, _target_anchor, _control_type_code,
169  _relative_cycle));
170  return TempData;
171 #else
172  (void)Success;
173  throw sycl::exception(
174  sycl::make_error_code(sycl::errc::feature_not_supported),
175  "Device-side API are not supported on a host device. Please use "
176  "host-side API instead.");
177 #endif // __SYCL_DEVICE_ONLY__
178  }
179 
180  static _dataT read(bool &Success) {
181  return read(Success, oneapi::experimental::properties{});
182  }
183 
184  // Writing to pipe is lowered to SPIR-V instruction OpWritePipe via SPIR-V
185  // friendly LLVM IR.
186  template <typename _functionPropertiesT>
187  static void write(const _dataT &Data, bool &Success, _functionPropertiesT) {
188 #ifdef __SYCL_DEVICE_ONLY__
189  // Get latency control properties
190  using _latency_anchor_id_prop = typename detail::GetOrDefaultValT<
191  _functionPropertiesT, latency_anchor_id_key,
193  using _latency_constraint_prop = typename detail::GetOrDefaultValT<
194  _functionPropertiesT, latency_constraint_key,
196 
197  // Get latency control property values
198  static constexpr int32_t _anchor_id = _latency_anchor_id_prop::value;
199  static constexpr int32_t _target_anchor = _latency_constraint_prop::target;
200  static constexpr latency_control_type _control_type =
201  _latency_constraint_prop::type;
202  static constexpr int32_t _relative_cycle = _latency_constraint_prop::cycle;
203 
204  int32_t _control_type_code = 0; // latency_control_type::none is default
205  if constexpr (_control_type == latency_control_type::exact) {
206  _control_type_code = 1;
207  } else if constexpr (_control_type == latency_control_type::max) {
208  _control_type_code = 2;
209  } else if constexpr (_control_type == latency_control_type::min) {
210  _control_type_code = 3;
211  }
212 
213  __ocl_WPipeTy<_dataT> _WPipe =
214  __spirv_CreatePipeFromPipeStorage_write<_dataT>(&m_Storage);
215  Success = !static_cast<bool>(__latency_control_nb_write_wrapper(
216  _WPipe, &Data, _anchor_id, _target_anchor, _control_type_code,
217  _relative_cycle));
218 #else
219  (void)Success;
220  (void)Data;
221  throw sycl::exception(
222  sycl::make_error_code(sycl::errc::feature_not_supported),
223  "Device-side API are not supported on a host device. Please use "
224  "host-side API instead.");
225 #endif // __SYCL_DEVICE_ONLY__
226  }
227 
228  static void write(const _dataT &Data, bool &Success) {
229  write(Data, Success, oneapi::experimental::properties{});
230  }
231 
232  static const void *get_host_ptr() { return &m_Storage; }
233 
234  // Blocking pipes
235 
236  // Host API
237  static _dataT read(queue &Q, memory_order Order = memory_order::seq_cst) {
238  // Order is currently unused.
239  std::ignore = Order;
240 
241  const device Dev = Q.get_device();
242  bool IsPipeSupported =
243  Dev.has_extension("cl_intel_program_scope_host_pipe");
244  if (!IsPipeSupported) {
245  return _dataT();
246  }
247  _dataT Data;
248  void *DataPtr = &Data;
249  const void *HostPipePtr = &m_Storage;
250  const std::string PipeName = pipe_base::get_pipe_name(HostPipePtr);
251  event E = Q.submit([=](handler &CGH) {
252  CGH.ext_intel_read_host_pipe(PipeName, DataPtr, sizeof(_dataT),
253  true /*blocking*/);
254  });
255  E.wait();
256  return *(_dataT *)DataPtr;
257  }
258 
259  static void write(queue &Q, const _dataT &Data,
261  // Order is currently unused.
262  std::ignore = Order;
263 
264  const device Dev = Q.get_device();
265  bool IsPipeSupported =
266  Dev.has_extension("cl_intel_program_scope_host_pipe");
267  if (!IsPipeSupported) {
268  return;
269  }
270  const void *HostPipePtr = &m_Storage;
271  const std::string PipeName = pipe_base::get_pipe_name(HostPipePtr);
272  void *DataPtr = const_cast<_dataT *>(&Data);
273  event E = Q.submit([=](handler &CGH) {
274  CGH.ext_intel_write_host_pipe(PipeName, DataPtr, sizeof(_dataT),
275  true /*blocking */);
276  });
277  E.wait();
278  }
279 
280  // Reading from pipe is lowered to SPIR-V instruction OpReadPipe via SPIR-V
281  // friendly LLVM IR.
282  template <typename _functionPropertiesT>
283  static _dataT read(_functionPropertiesT) {
284 #ifdef __SYCL_DEVICE_ONLY__
285  // Get latency control properties
286  using _latency_anchor_id_prop = typename detail::GetOrDefaultValT<
287  _functionPropertiesT, latency_anchor_id_key,
289  using _latency_constraint_prop = typename detail::GetOrDefaultValT<
290  _functionPropertiesT, latency_constraint_key,
292 
293  // Get latency control property values
294  static constexpr int32_t _anchor_id = _latency_anchor_id_prop::value;
295  static constexpr int32_t _target_anchor = _latency_constraint_prop::target;
296  static constexpr latency_control_type _control_type =
297  _latency_constraint_prop::type;
298  static constexpr int32_t _relative_cycle = _latency_constraint_prop::cycle;
299 
300  int32_t _control_type_code = 0; // latency_control_type::none is default
301  if constexpr (_control_type == latency_control_type::exact) {
302  _control_type_code = 1;
303  } else if constexpr (_control_type == latency_control_type::max) {
304  _control_type_code = 2;
305  } else if constexpr (_control_type == latency_control_type::min) {
306  _control_type_code = 3;
307  }
308 
309  __ocl_RPipeTy<_dataT> _RPipe =
310  __spirv_CreatePipeFromPipeStorage_read<_dataT>(&m_Storage);
311  _dataT TempData;
312  __latency_control_bl_read_wrapper(_RPipe, &TempData, _anchor_id,
313  _target_anchor, _control_type_code,
314  _relative_cycle);
315  return TempData;
316 #else
317  throw sycl::exception(
318  sycl::make_error_code(sycl::errc::feature_not_supported),
319  "Device-side API are not supported on a host device. Please use "
320  "host-side API instead.");
321 #endif // __SYCL_DEVICE_ONLY__
322  }
323 
324  static _dataT read() { return read(oneapi::experimental::properties{}); }
325 
326  // Writing to pipe is lowered to SPIR-V instruction OpWritePipe via SPIR-V
327  // friendly LLVM IR.
328  template <typename _functionPropertiesT>
329  static void write(const _dataT &Data, _functionPropertiesT) {
330 #ifdef __SYCL_DEVICE_ONLY__
331  // Get latency control properties
332  using _latency_anchor_id_prop = typename detail::GetOrDefaultValT<
333  _functionPropertiesT, latency_anchor_id_key,
335  using _latency_constraint_prop = typename detail::GetOrDefaultValT<
336  _functionPropertiesT, latency_constraint_key,
338 
339  // Get latency control property values
340  static constexpr int32_t _anchor_id = _latency_anchor_id_prop::value;
341  static constexpr int32_t _target_anchor = _latency_constraint_prop::target;
342  static constexpr latency_control_type _control_type =
343  _latency_constraint_prop::type;
344  static constexpr int32_t _relative_cycle = _latency_constraint_prop::cycle;
345 
346  int32_t _control_type_code = 0; // latency_control_type::none is default
347  if constexpr (_control_type == latency_control_type::exact) {
348  _control_type_code = 1;
349  } else if constexpr (_control_type == latency_control_type::max) {
350  _control_type_code = 2;
351  } else if constexpr (_control_type == latency_control_type::min) {
352  _control_type_code = 3;
353  }
354 
355  __ocl_WPipeTy<_dataT> _WPipe =
356  __spirv_CreatePipeFromPipeStorage_write<_dataT>(&m_Storage);
357  __latency_control_bl_write_wrapper(_WPipe, &Data, _anchor_id,
358  _target_anchor, _control_type_code,
359  _relative_cycle);
360 #else
361  (void)Data;
362  throw sycl::exception(
363  sycl::make_error_code(sycl::errc::feature_not_supported),
364  "Device-side API are not supported on a host device. Please use "
365  "host-side API instead.");
366 #endif // __SYCL_DEVICE_ONLY__
367  }
368 
369  static void write(const _dataT &Data) {
370  write(Data, oneapi::experimental::properties{});
371  }
372 
373 private:
374  static constexpr int32_t m_Size = sizeof(_dataT);
375  static constexpr int32_t m_Alignment = alignof(_dataT);
376  static constexpr int32_t m_Capacity = _min_capacity;
377 
378  static constexpr int32_t m_ready_latency =
380  _propertiesT, ready_latency_key>::template get<int32_t>(0);
381  static constexpr int32_t m_bits_per_symbol =
383  _propertiesT, bits_per_symbol_key>::template get<int32_t>(8);
384  static constexpr bool m_uses_valid =
386  _propertiesT, uses_valid_key>::template get<bool>(true);
387  static constexpr bool m_first_symbol_in_high_order_bits =
389  _propertiesT,
390  first_symbol_in_high_order_bits_key>::template get<int32_t>(0);
391  static constexpr protocol_name m_protocol = oneapi::experimental::detail::
392  ValueOrDefault<_propertiesT, protocol_key>::template get<protocol_name>(
394 
395 public:
396  static constexpr struct ConstantPipeStorageExp m_Storage = {
397 #ifdef __SYCL_DEVICE_ONLY__
398  {m_Size, m_Alignment, m_Capacity},
399 #endif // __SYCL_DEVICE_ONLY___
400  m_ready_latency,
401  m_bits_per_symbol,
402  m_uses_valid,
403  m_first_symbol_in_high_order_bits,
404  m_protocol};
405 
406 #ifdef __SYCL_DEVICE_ONLY__
407 private:
408  // FPGA BE will recognize this function and extract its arguments.
409  // TODO: Pass latency control parameters via the __spirv_* builtin when ready.
410  template <typename _T>
411  static int32_t __latency_control_nb_read_wrapper(
412  __ocl_RPipeTy<_T> Pipe, _T *Data, int32_t /* AnchorID */,
413  int32_t /* TargetAnchor */, int32_t /* Type */, int32_t /* Cycle */) {
414  return __spirv_ReadPipe(Pipe, Data, m_Size, m_Alignment);
415  }
416 
417  // FPGA BE will recognize this function and extract its arguments.
418  // TODO: Pass latency control parameters via the __spirv_* builtin when ready.
419  template <typename _T>
420  static int32_t __latency_control_nb_write_wrapper(
421  __ocl_WPipeTy<_T> Pipe, const _T *Data, int32_t /* AnchorID */,
422  int32_t /* TargetAnchor */, int32_t /* Type */, int32_t /* Cycle */) {
423  return __spirv_WritePipe(Pipe, Data, m_Size, m_Alignment);
424  }
425 
426  // FPGA BE will recognize this function and extract its arguments.
427  // TODO: Pass latency control parameters via the __spirv_* builtin when ready.
428  template <typename _T>
429  static void __latency_control_bl_read_wrapper(
430  __ocl_RPipeTy<_T> Pipe, _T *Data, int32_t /* AnchorID */,
431  int32_t /* TargetAnchor */, int32_t /* Type */, int32_t /* Cycle */) {
432  return __spirv_ReadPipeBlockingINTEL(Pipe, Data, m_Size, m_Alignment);
433  }
434 
435  // FPGA BE will recognize this function and extract its arguments.
436  // TODO: Pass latency control parameters via the __spirv_* builtin when ready.
437  template <typename _T>
438  static void __latency_control_bl_write_wrapper(
439  __ocl_WPipeTy<_T> Pipe, const _T *Data, int32_t /* AnchorID*/,
440  int32_t /* TargetAnchor */, int32_t /* Type */, int32_t /* Cycle */) {
441  return __spirv_WritePipeBlockingINTEL(Pipe, Data, m_Size, m_Alignment);
442  }
443 #endif // __SYCL_DEVICE_ONLY__
444 };
445 
446 } // namespace experimental
447 } // namespace intel
448 } // namespace ext
449 } // namespace _V1
450 } // namespace sycl
The SYCL device class encapsulates a single SYCL device on which kernels may be executed.
Definition: device.hpp:64
bool has_extension(const std::string &extension_name) const
Check SYCL extension support by device.
Definition: device.cpp:124
An event object can be used to synchronize memory transfers, enqueues of kernels and signaling barrie...
Definition: event.hpp:44
detail::is_event_info_desc< Param >::return_type get_info() const
Queries this SYCL event for information.
Definition: event.cpp:76
static bool wait_non_blocking(const event &E)
Definition: pipes.cpp:26
static std::string get_pipe_name(const void *HostPipePtr)
Definition: pipes.cpp:20
static _dataT read(queue &Q, bool &Success, memory_order Order=memory_order::seq_cst)
Definition: pipes.hpp:79
static void write(const _dataT &Data, bool &Success, _functionPropertiesT)
Definition: pipes.hpp:187
static void write(const _dataT &Data)
Definition: pipes.hpp:369
static _dataT read(bool &Success)
Definition: pipes.hpp:180
static void write(queue &Q, const _dataT &Data, bool &Success, memory_order Order=memory_order::seq_cst)
Definition: pipes.hpp:108
static void write(const _dataT &Data, bool &Success)
Definition: pipes.hpp:228
static _dataT read(bool &Success, _functionPropertiesT)
Definition: pipes.hpp:138
static void write(const _dataT &Data, _functionPropertiesT)
Definition: pipes.hpp:329
static const void * get_host_ptr()
Definition: pipes.hpp:232
static _dataT read(_functionPropertiesT)
Definition: pipes.hpp:283
static void write(queue &Q, const _dataT &Data, memory_order Order=memory_order::seq_cst)
Definition: pipes.hpp:259
static _dataT read(queue &Q, memory_order Order=memory_order::seq_cst)
Definition: pipes.hpp:237
Command group handler class.
Definition: handler.hpp:458
Encapsulates a single SYCL queue which schedules kernels on a SYCL device.
Definition: queue.hpp:111
device get_device() const
Definition: queue.cpp:76
std::enable_if_t< std::is_invocable_r_v< void, T, handler & >, event > submit(T CGF, const detail::code_location &CodeLoc=detail::code_location::current())
Submits a command group function object to the queue, in order to be scheduled for execution on the d...
Definition: queue.hpp:346
std::error_code make_error_code(sycl::errc E) noexcept
Constructs an error code using e and sycl_category()
Definition: exception.cpp:87
Definition: access.hpp:18