diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 649cc7c188269..c35ce47e31277 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -554,8 +554,8 @@ ALIAS("generic", __generic , KEYOPENCLC | KEYOPENCLCXX) KEYWORD(__kernel , KEYOPENCLC | KEYOPENCLCXX) ALIAS("kernel", __kernel , KEYOPENCLC | KEYOPENCLCXX) // OpenCL access qualifiers -KEYWORD(__read_only , KEYOPENCLC | KEYOPENCLCXX) -KEYWORD(__write_only , KEYOPENCLC | KEYOPENCLCXX) +KEYWORD(__read_only , KEYOPENCLC | KEYOPENCLCXX | KEYSYCL) +KEYWORD(__write_only , KEYOPENCLC | KEYOPENCLCXX | KEYSYCL) KEYWORD(__read_write , KEYOPENCLC | KEYOPENCLCXX) ALIAS("read_only", __read_only , KEYOPENCLC | KEYOPENCLCXX) ALIAS("write_only", __write_only , KEYOPENCLC | KEYOPENCLCXX) @@ -570,6 +570,7 @@ KEYWORD(vec_step , KEYOPENCLC | KEYALTIVEC | KEYZVECTOR) KEYWORD(__builtin_omp_required_simd_align, KEYALL) KEYWORD(pipe , KEYOPENCLC | KEYOPENCLCXX) +KEYWORD(__pipe , KEYSYCL) // Borland Extensions. KEYWORD(__pascal , KEYALL) diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 5589297953b7c..5b873fbb444c7 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2562,7 +2562,8 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, // Early exit as Sema has a dedicated missing_actual_pipe_type diagnostic // for incomplete declarations such as `pipe p`. - if (getLangOpts().OpenCLCPlusPlus && DS.isTypeSpecPipe()) + if ((getLangOpts().OpenCLCPlusPlus || getLangOpts().SYCLIsDevice) && + DS.isTypeSpecPipe()) return false; if (getLangOpts().CPlusPlus && @@ -3783,6 +3784,11 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, } isInvalid = DS.SetTypePipe(true, Loc, PrevSpec, DiagID, Policy); break; + case tok::kw___pipe: + if (getLangOpts().SYCLIsDevice) + // __pipe keyword is defined only for SYCL kernel language + isInvalid = DS.SetTypePipe(true, Loc, PrevSpec, DiagID, Policy); + break; #define GENERIC_IMAGE_TYPE(ImgType, Id) \ case tok::kw_##ImgType##_t: \ isInvalid = DS.SetTypeSpecType(DeclSpec::TST_##ImgType##_t, Loc, PrevSpec, \ @@ -4902,8 +4908,9 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { default: return false; case tok::kw_pipe: + case tok::kw___pipe: return (getLangOpts().OpenCL && getLangOpts().OpenCLVersion >= 200) || - getLangOpts().OpenCLCPlusPlus; + getLangOpts().OpenCLCPlusPlus || getLangOpts().SYCLIsDevice; case tok::identifier: // foo::bar // Unfortunate hack to support "Class.factoryMethod" notation. @@ -5391,8 +5398,9 @@ static bool isPtrOperatorToken(tok::TokenKind Kind, const LangOptions &Lang, if (Kind == tok::star || Kind == tok::caret) return true; - if (Kind == tok::kw_pipe && - ((Lang.OpenCL && Lang.OpenCLVersion >= 200) || Lang.OpenCLCPlusPlus)) + if ((Kind == tok::kw_pipe || Kind == tok::kw___pipe) && + ((Lang.OpenCL && Lang.OpenCLVersion >= 200) || Lang.OpenCLCPlusPlus || + Lang.SYCLIsDevice)) return true; if (!Lang.CPlusPlus) diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index 1fcc9e20a1431..0097fcef80411 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -1464,6 +1464,8 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, case tok::kw___read_write: // OpenCL pipe case tok::kw_pipe: + // SYCL pipe + case tok::kw___pipe: // GNU case tok::kw_restrict: diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index e28323b66bc52..a4ca6afcbad8f 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -9286,15 +9286,11 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, for (const ParmVarDecl *Param : NewFD->parameters()) { QualType PT = Param->getType(); - // OpenCL 2.0 pipe restrictions forbids pipe packet types to be non-value - // types. - if (getLangOpts().OpenCLVersion >= 200 || getLangOpts().OpenCLCPlusPlus) { - if(const PipeType *PipeTy = PT->getAs()) { - QualType ElemTy = PipeTy->getElementType(); - if (ElemTy->isReferenceType() || ElemTy->isPointerType()) { - Diag(Param->getTypeSpecStartLoc(), diag::err_reference_pipe_type ); - D.setInvalidType(); - } + if (const PipeType *PipeTy = PT->getAs()) { + QualType ElemTy = PipeTy->getElementType(); + if (ElemTy->isReferenceType() || ElemTy->isPointerType()) { + Diag(Param->getTypeSpecStartLoc(), diag::err_reference_pipe_type ); + D.setInvalidType(); } } } diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 1092cc53ad8ad..292666c5ae1dc 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -1365,7 +1365,8 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { // errors. declarator.setInvalidType(true); } else if ((S.getLangOpts().OpenCLVersion >= 200 || - S.getLangOpts().OpenCLCPlusPlus) && + S.getLangOpts().OpenCLCPlusPlus || + S.getLangOpts().SYCLIsDevice) && DS.isTypeSpecPipe()) { S.Diag(DeclLoc, diag::err_missing_actual_pipe_type) << DS.getSourceRange(); @@ -2322,7 +2323,7 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM, // OpenCL v2.0 s6.12.5 - Arrays of blocks are not supported. // OpenCL v2.0 s6.16.13.1 - Arrays of pipe type are not supported. // OpenCL v2.0 s6.9.b - Arrays of image/sampler type are not supported. - if (getLangOpts().OpenCL || getLangOpts().SYCLIsDevice) { + if (getLangOpts().OpenCL) { const QualType ArrType = Context.getBaseElementType(T); if (ArrType->isBlockPointerType() || ArrType->isPipeType() || ArrType->isSamplerT() || ArrType->isImageType()) { @@ -4607,11 +4608,20 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, } } + if (LangOpts.OpenCL) { + // OpenCL v2.0 s6.12.5 - A pipe cannot be the return value of a + // function. Disrespect this for SYCL. + if (T->isPipeType()) { + S.Diag(D.getIdentifierLoc(), diag::err_opencl_invalid_return) + << T << 1 /*hint off*/; + D.setInvalidType(true); + } + } + if (LangOpts.OpenCL || LangOpts.SYCLIsDevice) { // OpenCL v2.0 s6.12.5 - A block cannot be the return value of a // function. - if (T->isBlockPointerType() || T->isImageType() || T->isSamplerT() || - T->isPipeType()) { + if (T->isBlockPointerType() || T->isImageType() || T->isSamplerT()) { S.Diag(D.getIdentifierLoc(), diag::err_opencl_invalid_return) << T << 1 /*hint off*/; D.setInvalidType(true); diff --git a/sycl/doc/extensions/SYCL_pipes_lowering_to_SPIRV.rst b/sycl/doc/extensions/SYCL_pipes_lowering_to_SPIRV.rst new file mode 100644 index 0000000000000..fdeaf611295ae --- /dev/null +++ b/sycl/doc/extensions/SYCL_pipes_lowering_to_SPIRV.rst @@ -0,0 +1,119 @@ +SYCL INTEL spatial pipes +======================== + +Introduction +============ + +SPIR-V is first class target in which SYCL pipes should be representable, and +pipes are already exposed within SPIR-V. For this implementation API functions +call for SPIR-V friendly mangled functions instead of OpenCL built-ins. +This document describes how SYCL pipes are being lowered to SPIR-V. + +OpenCL 2.2 program pipe representation in SPIR-V +================================================ + +The SPIR-V program pipe representation is used to be an underlying +representation of intra-kernel and inter-kernel static pipe connectivity. +The SPIR-V pipe representation exists in a series of pieces: + + - OpTypePipeStorage: Type representing memory allocated for storage of data + within a pipe. Used for OpenCL 2.2 program pipes (program-scope pipes) that + the host program is not aware of, but that enables connectivity between + kernels. + + - OpConstantPipeStorage: Instruction that creates an OpTypePipeStorage object. + Requires packet size (number of bytes) and capacity (number of packets) to be + defined. + + - OpTypePipe: A pipe object that can act as a read/write endpoint of some pipe + storage, either allocated by the host and passed as a kernel argument, or + allocated at "program scope" through a pipe storage object. + + - OpCreatePipeFromPipeStorage: Creates a pipe object (that can be read/written) + from an OpTypePipeStorage instance. + + - OpReadPipe / OpWritePipe: Read packet from or write packet to a pipe object. + +Lowering of kernel to kernel pipes to SPIR-V (non-blocking) +=========================================================== + +This connectivity is achieved through OpTypePipeStorage which allows a SPIR-V +device consumer to leverage static connectivity. An OpConstantPipeStorage +instruction must create a single instance of OpPipeStorage for each kernel to +kernel pipe type used by any kernel within the application. + +OpTypePipe objects is created from OpPipeStorage using +OpCreatePipeFromPipeStorage. The number of OpTypePipe objects created from an +OpPipeStorage object is an implementation detail, as are the access qualifiers +applied to those types. For example, an implementation is free to create a +different OpTypePipe corresponding to each read and write, with unidirectional +access qualifiers annotated, or it can create fewer OpTypePipe objects, although +read and write pipes must be distinct according to OpReadPipe and OpWritePipe +rules. + +NOTE: The SPIR-V OpReadPipe and OpWritePipe instructions are non-blocking. + +Details SPIR-V representation in LLVM IR +======================================== + +Pipe built-ins are mangled in LLVM IR to make it SPIR-V friendly. +As an example: + + SPIR-V built-in | Mangled built-in in LLVM IR + ----------------------------+----------------------------------------------- + OpReadPipe | __spirv_ReadPipe + ----------------------------+----------------------------------------------- + OpWritePipe | __spirv_WritePipe + ----------------------------+----------------------------------------------- + OpCreatePipeFromPipeStorage | __spirv_CreatePipeFromPipeStorage_{read|write} + +More about SPIR-V representation in LLVM IR can be found under the link: +.. _SPIRVRepresentationInLLVM.rst: https://github.com/KhronosGroup/SPIRV-LLVM-Translator/blob/master/docs/SPIRVRepresentationInLLVM.rst/ + +In SYCL headers the built-ins are declared as external functions with the +appropriate mangling. The translator will transform calls of these built-ins +into calls of SPIR-V instructions. + +Example of SYCL -> LLVM IR -> SPIR-V -> LLVM-IR code transformations +==================================================================== +Consider following SYCL device code: +.. code:: cpp + pipe::write(42, SuccessCode); + +After compiling this code with clang we will be given following piece of IR for +the write pipe function call (NOTE: for this implementation clang-known +OpenCL 2.0 pipe types are reused): +.. code:: cpp + define internal spir_func void @_ZN2cl4sycl4pipeIZ4mainE9some_pipeiLi1EE5writeEiRb(i32, i8* dereferenceable(1)) #4 align 2 { + //... + %12 = call spir_func i32 @_Z17__spirv_WritePipeIiEi8ocl_pipePT_ii(%opencl.pipe_wo_t addrspace(1)* %10, i32 addrspace(4)* %11, i32 4, i32 4) #8 + //... + } + +with following declaration: +.. code:: cpp + %12 = call spir_func i32 @_Z17__spirv_WritePipeIiEi8ocl_pipePT_ii(%opencl.pipe_wo_t addrspace(1)* %10, i32 addrspace(4)* %11, i32 4, i32 4) #8 + +SPIR-V translator will drop all of these manglings, just making a call of SPIR-V +write pipe built-in: +.. code:: cpp + 7 WritePipe 51 158 156 157 52 52 + +Resulting code for translation back to LLVM IR from SPIR-V are calls of OpenCL +built-ins: +.. code:: cpp + define internal spir_func void @_ZN2cl4sycl4pipeIZ4mainE9some_pipeiLi1EE5writeEiRb(i32, i8*) #0 { + //... + %9 = call spir_func i32 @__write_pipe_2(%opencl.pipe_wo_t addrspace(1)* %6, i8 addrspace(4)* %8, i32 4, i32 4) //... + } + +again with write pipe declaration (but now it's built-in!): +.. code:: cpp + declare spir_func i32 @__write_pipe_2(%opencl.pipe_wo_t addrspace(1)*, i8 addrspace(4)*, i32, i32) #0 + +The first argument in a call of __write_pipe_2 OpenCL built-in is a pipe object, +which is created as a result of SPIR-V built-in call +__spirv_CreatePipeFromPipeStorage_{read|write} which has no OpenCL +representation and therefore stays in IR before and after SPIR-V tool-chain as: +.. code:: cpp + %9 = call spir_func %opencl.pipe_wo_t addrspace(1)* @_Z39__spirv_CreatePipeFromPipeStorage_writeIiE8ocl_pipe11PipeStorage(%struct._ZTS11PipeStorage.PipeStorage* byval align 4 %6) #8 diff --git a/sycl/doc/extensions/SYCL_pipes_usage.rst b/sycl/doc/extensions/SYCL_pipes_usage.rst new file mode 100644 index 0000000000000..13420063bfa86 --- /dev/null +++ b/sycl/doc/extensions/SYCL_pipes_usage.rst @@ -0,0 +1,104 @@ +SYCL INTEL spatial pipes +======================== + +Introduction +============ + +Pipe is a memory object that stores data organized as a FIFO buffer. +This implementation enables 2 classes of pipe connectivity: + Cross kernel: Kernel A -> Kernel B + Intra-kernel: Kernel A -> Kernel A + +and 2 types of pipes: non-blocking and blocking. + +Blocking calls wait until there is available capacity to commit data, or until +data is available to be read. In all of these cases the pipe indirectly conveys +side channel control information to the program, which can be used for flow +control or many other purposes by an application. + +Unsuccessful non-blocking pipe reads or writes do not impact the state or +content of a pipe. + +Pipe identification +=================== + +Identification mechanism of a pipe is base on template type specialization of +pipe class. + +Consider following code: +.. code:: cpp + template class pipe; + +where 'name' is a name of a pipe that helps to identify pipe; + 'dataT' is a type of data to store into the pipe, it is required to have + a default constructor, a copy constructor and a destructor. + 'min_capacity' is the number of outstanding words that can be written to + a pipe but not yet read from it. + +The combined set of the three template parameters forms the type of a pipe. +Any uses of a read/write method on that type operate on the same pipe by +static connectivity of pipes. + +Interaction with SYCL API +========================= + +Following member functions of pipe class are available for user: +.. code:: cpp + // Non-blocking pipes + static dataT read(bool &success_code); + static void write(const dataT &data, bool &success_code); + + // Blocking pipes + static dataT read(); + static void write(const dataT &data); + +Writes to or reads from a pipe are accesses to a pipe with the same pipe type. + +Simple example of SYCL program +============================== +Non-blocking pipes: +.. code:: cpp + using Pipe = pipe; + Queue.submit([&](handler& cgh) { + auto read_acc = readBuf.get_access(cgh); + cgh.single_task([=]() { + bool SuccessCode; + do { + Pipe::write(read_add[0], SuccessCode); // Write into a some_pipe + // allocated for integers + // with a capacity of 1. + } while (!SuccessCode); + }); + }); + buffer writeBuf (data, range<1>(dataSize)); + Queue.submit([&](handler& cgh) { + auto write_acc = writeBuf.get_access(cgh); + cgh.single_task([=]() { + bool SuccessCode; + do { + write_acc[0] = Pipe::read(SuccessCode); // Read data stored in the + // pipe and put it in the + // SYCL buffer. + } while (!SuccessCode); + }); + }); + +Blocking pipes: +.. code:: cpp + using Pipe = pipe; + Queue.submit([&](handler& cgh) { + auto read_acc = readBuf.get_access(cgh); + cgh.single_task([=]() { + Pipe::write(read_add[0]); // Write '42' into a some_pipe allocated + // for integers with a capacity of 1. + }); + }); + buffer writeBuf (data, range<1>(dataSize)); + Queue.submit([&](handler& cgh) { + auto write_acc = writeBuf.get_access(cgh); + cgh.single_task([=]() { + write_acc[0] = Pipe::read(); // Read data stored in the + // pipe and put it in the + // SYCL buffer. + }); + }); diff --git a/sycl/include/CL/__spirv/spirv_ops.hpp b/sycl/include/CL/__spirv/spirv_ops.hpp index c5614f278bc31..86161f65c92d8 100644 --- a/sycl/include/CL/__spirv/spirv_ops.hpp +++ b/sycl/include/CL/__spirv/spirv_ops.hpp @@ -183,9 +183,21 @@ template extern void __spirv_SubgroupBlockWriteINTEL(__global uint32_t *Ptr, dataT Data) noexcept; +template +extern int32_t __spirv_ReadPipe(RPipeTy Pipe, dataT *Data, + int32_t Size, int32_t Alignment) noexcept; +template +extern int32_t __spirv_WritePipe(WPipeTy Pipe, dataT *Data, + int32_t Size, int32_t Alignment) noexcept; +template +extern RPipeTy __spirv_CreatePipeFromPipeStorage_read( + const ConstantPipeStorage *Storage) noexcept; +template +extern WPipeTy __spirv_CreatePipeFromPipeStorage_write( + const ConstantPipeStorage *Storage) noexcept; + extern void __spirv_ocl_prefetch(const __global char *Ptr, size_t NumBytes) noexcept; - #else // if !__SYCL_DEVICE_ONLY__ template diff --git a/sycl/include/CL/__spirv/spirv_types.hpp b/sycl/include/CL/__spirv/spirv_types.hpp index 485a16874bdf3..8b3998a63ef98 100644 --- a/sycl/include/CL/__spirv/spirv_types.hpp +++ b/sycl/include/CL/__spirv/spirv_types.hpp @@ -56,6 +56,21 @@ inline constexpr MemorySemanticsMask operator|(MemorySemanticsMask a, } // namespace __spv +#ifdef __SYCL_DEVICE_ONLY__ +// OpenCL pipe types +template +using RPipeTy = __read_only __pipe const dataT; +template +using WPipeTy = __write_only __pipe const dataT; + +// Struct representing layout of pipe storage +struct ConstantPipeStorage { + int32_t _PacketSize; + int32_t _PacketAlignment; + int32_t _Capacity; +}; +#endif // __SYCL_DEVICE_ONLY__ + // This class does not have definition, it is only predeclared here. // The pointers to this class objects can be passed to or returned from // SPIRV built-in functions. diff --git a/sycl/include/CL/sycl.hpp b/sycl/include/CL/sycl.hpp index dccb6e260b961..a1cf4af6a00c0 100644 --- a/sycl/include/CL/sycl.hpp +++ b/sycl/include/CL/sycl.hpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include diff --git a/sycl/include/CL/sycl/pipes.hpp b/sycl/include/CL/sycl/pipes.hpp new file mode 100644 index 0000000000000..ef6cff2dbcb32 --- /dev/null +++ b/sycl/include/CL/sycl/pipes.hpp @@ -0,0 +1,89 @@ +//==---------------- pipes.hpp - SYCL pipes ------------*- C++ -*-----------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// ===--------------------------------------------------------------------=== // + +#pragma once + +#include +#include +#include + +namespace cl { +namespace sycl { + +template class pipe { +public: + // Non-blocking pipes + // Reading from pipe is lowered to SPIR-V instruction OpReadPipe via SPIR-V + // friendly LLVM IR. + static dataT read(bool &Success) { +#ifdef __SYCL_DEVICE_ONLY__ + RPipeTy RPipe = + __spirv_CreatePipeFromPipeStorage_read(&m_Storage); + dataT TempData; + Success = static_cast( + __spirv_ReadPipe(RPipe, &TempData, m_Size, m_Alignment)); + return TempData; +#else + assert(!"Pipes are not supported on a host device!"); +#endif // __SYCL_DEVICE_ONLY__ + } + + // Writing to pipe is lowered to SPIR-V instruction OpWritePipe via SPIR-V + // friendly LLVM IR. + static void write(const dataT &Data, bool &Success) { +#ifdef __SYCL_DEVICE_ONLY__ + WPipeTy WPipe = + __spirv_CreatePipeFromPipeStorage_write(&m_Storage); + Success = static_cast( + __spirv_WritePipe(WPipe, &Data, m_Size, m_Alignment)); +#else + assert(!"Pipes are not supported on a host device!"); +#endif // __SYCL_DEVICE_ONLY__ + } + + // Blocking pipes + // Reading from pipe is lowered to SPIR-V instruction OpReadPipe via SPIR-V + // friendly LLVM IR. + static dataT read() { +#ifdef __SYCL_DEVICE_ONLY__ + RPipeTy RPipe = + __spirv_CreatePipeFromPipeStorage_read(&m_Storage); + dataT TempData; + // FIXME: this is workaround unless special SPIR-V decoration is implemented + while (!__spirv_ReadPipe(RPipe, &TempData, m_Size, m_Alignment)); + return TempData; +#else + assert(!"Pipes are not supported on a host device!"); +#endif // __SYCL_DEVICE_ONLY__ + } + + // Writing to pipe is lowered to SPIR-V instruction OpWritePipe via SPIR-V + // friendly LLVM IR. + static void write(const dataT &Data) { +#ifdef __SYCL_DEVICE_ONLY__ + WPipeTy WPipe = + __spirv_CreatePipeFromPipeStorage_write(&m_Storage); + // FIXME: this is workaround unless special SPIR-V decoration is implemented + while (!__spirv_WritePipe(WPipe, &Data, m_Size, m_Alignment)); +#else + assert(!"Pipes are not supported on a host device!"); +#endif // __SYCL_DEVICE_ONLY__ + } + +private: + static constexpr int32_t m_Size = sizeof(dataT); + static constexpr int32_t m_Alignment = alignof(dataT); + static constexpr int32_t m_Capacity = min_capacity; +#ifdef __SYCL_DEVICE_ONLY__ + static constexpr struct ConstantPipeStorage m_Storage = {m_Size, m_Alignment, + m_Capacity}; +#endif // __SYCL_DEVICE_ONLY__ +}; + +} // namespace sycl +} // namespace cl diff --git a/sycl/test/fpga_tests/fpga_pipes.cpp b/sycl/test/fpga_tests/fpga_pipes.cpp new file mode 100644 index 0000000000000..cd0154a91f899 --- /dev/null +++ b/sycl/test/fpga_tests/fpga_pipes.cpp @@ -0,0 +1,180 @@ +// RUN: %clang -std=c++11 -fsycl %s -o %t.out -lstdc++ -lOpenCL -lsycl +// RUN: %ACC_RUN_PLACEHOLDER %t.out +//==------------- fpga_pipes.cpp - SYCL FPGA pipes test --------------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include +#include + +// For pipes created with namespaces set +namespace some { +class pipe; +} + +using namespace cl::sycl; + +int main() { + int data[] = {0}; + + { + // Test for non-blocking pipes + queue Queue; + using Pipe = pipe; + + Queue.submit([&](handler &cgh) { + cgh.single_task([=]() { + bool SuccessCode = false; + while (!SuccessCode) + Pipe::write(42, SuccessCode); + }); + }); + + buffer writeBuf(data, 1); + Queue.submit([&](handler &cgh) { + auto write_acc = writeBuf.get_access(cgh); + + cgh.single_task([=]() { + bool SuccessCode = false; + while (!SuccessCode) + write_acc[0] = Pipe::read(SuccessCode); + }); + }); + + auto readHostBuffer = writeBuf.get_access(); + if (readHostBuffer[0] != 42) { + std::cout << "Result mismatches " << readHostBuffer[0] << " Vs expected " + << 42 << std::endl; + + return -1; + } + } + + { + // Test for simple non-blocking pipes with explicit type + queue Queue; + + buffer readBuf(data, 1); + Queue.submit([&](handler &cgh) { + cgh.single_task([=]() { + bool SuccessCode; + while (!SuccessCode) + pipe::write(42, SuccessCode); + }); + }); + + buffer writeBuf(data, 1); + Queue.submit([&](handler &cgh) { + auto write_acc = writeBuf.get_access(cgh); + + cgh.single_task([=]() { + bool SuccessCode; + while (!SuccessCode) + write_acc[0] = pipe::read(SuccessCode); + }); + }); + + auto readHostBuffer = writeBuf.get_access(); + if (readHostBuffer[0] != 42) { + std::cout << "Result mismatches " << readHostBuffer[0] << " Vs expected " + << 42 << std::endl; + + return -1; + } + } + + { + // Test for simple non-blocking pipes created with namespaces set + queue Queue; + + buffer readBuf(data, 1); + Queue.submit([&](handler &cgh) { + cgh.single_task([=]() { + bool SuccessCode; + while (!SuccessCode) + pipe::write(42, SuccessCode); + }); + }); + + buffer writeBuf(data, 1); + Queue.submit([&](handler &cgh) { + auto write_acc = writeBuf.get_access(cgh); + + cgh.single_task([=]() { + bool SuccessCode; + while (!SuccessCode) + write_acc[0] = pipe::read(SuccessCode); + }); + }); + + auto readHostBuffer = writeBuf.get_access(); + if (readHostBuffer[0] != 42) { + std::cout << "Result mismatches " << readHostBuffer[0] << " Vs expected " + << 42 << std::endl; + + return -1; + } + } + + { + // Test for forward declared pipes + queue Queue; + class pipe_type_for_lambdas; + + buffer readBuf(data, 1); + Queue.submit([&](handler &cgh) { + cgh.single_task([=]() { + bool SuccessCode; + while (!SuccessCode) + pipe::write(42, SuccessCode); + }); + }); + + buffer writeBuf(data, 1); + Queue.submit([&](handler &cgh) { + cgh.single_task([=]() { + bool SuccessCode; + while (!SuccessCode) + pipe::read(SuccessCode); + }); + }); + + auto readHostBuffer = writeBuf.get_access(); + if (readHostBuffer[0] != 42) { + std::cout << "Result mismatches " << readHostBuffer[0] << " Vs expected " + << 42 << std::endl; + + return -1; + } + } + + { + // Test for blocking pipes + queue Queue; + using Pipe = pipe; + + Queue.submit([&](handler &cgh) { + cgh.single_task([=]() { Pipe::write(42); }); + }); + + buffer writeBuf(data, 1); + Queue.submit([&](handler &cgh) { + auto write_acc = writeBuf.get_access(cgh); + + cgh.single_task([=]() { write_acc[0] = Pipe::read(); }); + }); + + auto readHostBuffer = writeBuf.get_access(); + if (readHostBuffer[0] != 42) { + std::cout << "Result mismatches " << readHostBuffer[0] << " Vs expected " + << 42 << std::endl; + + return -1; + } + } + + return 0; +}