From 5ee066e4b6389761e79e9a2b392cb3c72a38fd67 Mon Sep 17 00:00:00 2001 From: Andrew Lamzed-Short Date: Mon, 14 Nov 2022 08:46:11 +0000 Subject: [PATCH] [SYCL] Add queue shortcut functions with placeholder accessors (#7266) Implementation of the explicit copy-based queue shortcut functions defined in Table 29 of [Section 4.6.5.2 of the specification](https://registry.khronos.org/SYCL/specs/sycl-2020/html/sycl-2020.html#sec:queue-shortcuts) (the last 7 rows of the table to be precise). --- sycl/include/sycl/queue.hpp | 118 +++++++++++ sycl/unittests/queue/CMakeLists.txt | 1 + sycl/unittests/queue/ShortcutFunctions.cpp | 235 +++++++++++++++++++++ 3 files changed, 354 insertions(+) create mode 100644 sycl/unittests/queue/ShortcutFunctions.cpp diff --git a/sycl/include/sycl/queue.hpp b/sycl/include/sycl/queue.hpp index d1d2e92d39989..2c14089e8e3b4 100644 --- a/sycl/include/sycl/queue.hpp +++ b/sycl/include/sycl/queue.hpp @@ -1133,6 +1133,124 @@ class __SYCL_EXPORT queue { CodeLoc); } + /// Copies data from a memory region pointed to by a placeholder accessor to + /// another memory region pointed to by a shared_ptr. + /// + /// \param Src is a placeholder accessor to the source memory. + /// \param Dest is a shared_ptr to the destination memory. + /// \return an event representing copy operation. + template + event copy(accessor Src, + std::shared_ptr Dest _CODELOCPARAM(&CodeLoc)) { + return submit([&](handler &CGH) { + CGH.require(Src); + CGH.copy(Src, Dest); + } _CODELOCFW(CodeLoc)); + } + + /// Copies data from a memory region pointed to by a shared_ptr to another + /// memory region pointed to by a placeholder accessor. + /// + /// \param Src is a shared_ptr to the source memory. + /// \param Dest is a placeholder accessor to the destination memory. + /// \return an event representing copy operation. + template + event copy(std::shared_ptr Src, + accessor Dest + _CODELOCPARAM(&CodeLoc)) { + return submit([&](handler &CGH) { + CGH.require(Dest); + CGH.copy(Src, Dest); + } _CODELOCFW(CodeLoc)); + } + + /// Copies data from a memory region pointed to by a placeholder accessor to + /// another memory region pointed to by a raw pointer. + /// + /// \param Src is a placeholder accessor to the source memory. + /// \param Dest is a raw pointer to the destination memory. + /// \return an event representing copy operation. + template + event copy(accessor Src, + DestT *Dest _CODELOCPARAM(&CodeLoc)) { + return submit([&](handler &CGH) { + CGH.require(Src); + CGH.copy(Src, Dest); + } _CODELOCFW(CodeLoc)); + } + + /// Copies data from a memory region pointed to by a raw pointer to another + /// memory region pointed to by a placeholder accessor. + /// + /// \param Src is a raw pointer to the source memory. + /// \param Dest is a placeholder accessor to the destination memory. + /// \return an event representing copy operation. + template + event copy(const SrcT *Src, + accessor Dest + _CODELOCPARAM(&CodeLoc)) { + return submit([&](handler &CGH) { + CGH.require(Dest); + CGH.copy(Src, Dest); + } _CODELOCFW(CodeLoc)); + } + + /// Copies data from one memory region to another, both pointed by placeholder + /// accessors. + /// + /// \param Src is a placeholder accessor to the source memory. + /// \param Dest is a placeholder accessor to the destination memory. + /// \return an event representing copy operation. + template + event + copy(accessor Src, + accessor Dest + _CODELOCPARAM(&CodeLoc)) { + return submit([&](handler &CGH) { + CGH.require(Src); + CGH.require(Dest); + CGH.copy(Src, Dest); + } _CODELOCFW(CodeLoc)); + } + + /// Provides guarantees that the memory object accessed via Acc is updated + /// on the host after operation is complete. + /// + /// \param Acc is a SYCL accessor that needs to be updated on host. + /// \return an event representing update_host operation. + template + event update_host( + accessor Acc _CODELOCPARAM(&CodeLoc)) { + return submit([&](handler &CGH) { + CGH.require(Acc); + CGH.update_host(Acc); + } _CODELOCFW(CodeLoc)); + } + + /// Fills the specified memory with the specified data. + /// + /// \param Dest is the placeholder accessor to the memory to fill. + /// \param Src is the data to fill the memory with. T should be + /// trivially copyable. + /// \return an event representing fill operation. + template + event fill(accessor Dest, + const T &Src _CODELOCPARAM(&CodeLoc)) { + return submit([&](handler &CGH) { + CGH.require(Dest); + CGH.fill(Dest, Src); + } _CODELOCFW(CodeLoc)); + } + // Clean KERNELFUNC macros. #undef _KERNELFUNCPARAM diff --git a/sycl/unittests/queue/CMakeLists.txt b/sycl/unittests/queue/CMakeLists.txt index d5210051a1298..e284c07766903 100644 --- a/sycl/unittests/queue/CMakeLists.txt +++ b/sycl/unittests/queue/CMakeLists.txt @@ -4,4 +4,5 @@ add_sycl_unittest(QueueTests OBJECT USM.cpp Wait.cpp GetProfilingInfo.cpp + ShortcutFunctions.cpp ) diff --git a/sycl/unittests/queue/ShortcutFunctions.cpp b/sycl/unittests/queue/ShortcutFunctions.cpp new file mode 100644 index 0000000000000..c0bf747d753be --- /dev/null +++ b/sycl/unittests/queue/ShortcutFunctions.cpp @@ -0,0 +1,235 @@ +//==-------------- ShortcutFunctions.cpp --- queue unit tests --------------==// +// +// 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 +#include +#include +#include +#include +#include + +#include + +using namespace sycl; + +namespace { +struct TestCtx { + bool BufferFillCalled = false; + bool BufferReadCalled = false; + bool BufferWriteCalled = false; + bool BufferCopyCalled = false; +}; +} // namespace + +static std::unique_ptr TestContext; + +pi_result redefinedEnqueueMemBufferWrite(pi_queue command_queue, pi_mem buffer, + pi_bool blocking_write, size_t offset, + size_t size, const void *ptr, + pi_uint32 num_events_in_wait_list, + const pi_event *event_wait_list, + pi_event *event) { + TestContext->BufferWriteCalled = true; + return PI_SUCCESS; +} + +pi_result redefinedEnqueueMemBufferRead(pi_queue queue, pi_mem buffer, + pi_bool blocking_read, size_t offset, + size_t size, void *ptr, + pi_uint32 num_events_in_wait_list, + const pi_event *event_wait_list, + pi_event *event) { + TestContext->BufferReadCalled = true; + return PI_SUCCESS; +} + +pi_result redefinedEnqueueMemBufferCopy(pi_queue command_queue, + pi_mem src_buffer, pi_mem dst_buffer, + size_t src_offset, size_t dst_offset, + size_t size, + pi_uint32 num_events_in_wait_list, + const pi_event *event_wait_list, + pi_event *event) { + TestContext->BufferCopyCalled = true; + return PI_SUCCESS; +} + +pi_result redefinedEnqueueMemBufferFill(pi_queue command_queue, pi_mem buffer, + const void *pattern, + size_t pattern_size, size_t offset, + size_t size, + pi_uint32 num_events_in_wait_list, + const pi_event *event_wait_list, + pi_event *event) { + TestContext->BufferFillCalled = true; + return PI_SUCCESS; +} + +TEST(ShortcutFunctions, ShortcutsCallCorrectPIFunctions) { + unittest::PiMock Mock; + platform Plt = Mock.getPlatform(); + + Mock.redefine( + redefinedEnqueueMemBufferWrite); + Mock.redefine( + redefinedEnqueueMemBufferRead); + Mock.redefine( + redefinedEnqueueMemBufferCopy); + + Mock.redefine( + redefinedEnqueueMemBufferFill); + + context Ctx(Plt); + queue Q{Ctx, default_selector()}; + + constexpr std::size_t Size = 1; + + // Queue.copy(accessor src, shared_ptr dest); + { + TestContext.reset(new TestCtx()); + + int Data[Size]; + buffer Buf(Data, Size); + + accessor> + Src(Buf); + ASSERT_TRUE(Src.is_placeholder()); + + std::shared_ptr Dest = std::make_shared(0); + + Q.copy(Src, Dest); + Q.wait(); + + EXPECT_TRUE(TestContext->BufferReadCalled); + } + + // Queue.copy(shared_ptr src, accessor dest); + { + TestContext.reset(new TestCtx()); + + int Data[Size]; + buffer Buf(Data, Size); + + std::shared_ptr Src = std::make_shared(42); + + accessor> + Dest(Buf); + ASSERT_TRUE(Dest.is_placeholder()); + + Q.copy(Src, Dest); + Q.wait(); + + EXPECT_TRUE(TestContext->BufferWriteCalled); + } + + // Queue.copy(accessor src, ptr* dest); + { + TestContext.reset(new TestCtx()); + + int Data[Size]; + buffer Buf(Data, Size); + + accessor> + Src(Buf); + ASSERT_TRUE(Src.is_placeholder()); + + std::unique_ptr Dest = std::make_unique(0); + + Q.copy(Src, Dest.get()); + Q.wait(); + + EXPECT_TRUE(TestContext->BufferReadCalled); + } + + // Queue.copy(ptr* src, accessor dest); + { + TestContext.reset(new TestCtx()); + + int Data[Size]; + buffer Buf(Data, Size); + + std::unique_ptr Src = std::make_unique(42); + + accessor> + Dest(Buf); + ASSERT_TRUE(Dest.is_placeholder()); + + Q.copy(Src.get(), Dest); + Q.wait(); + + EXPECT_TRUE(TestContext->BufferWriteCalled); + } + + // Queue.copy(accessor src, accessor dest); + { + TestContext.reset(new TestCtx()); + + int SrcData[Size]; + buffer SrcBuf(SrcData, Size); + + int DestData[Size]; + buffer DestBuf(DestData, Size); + + accessor> + Src(SrcBuf); + accessor> + Dest(DestBuf); + + ASSERT_TRUE(Src.is_placeholder()); + ASSERT_TRUE(Dest.is_placeholder()); + + Q.copy(Src, Dest); + Q.wait(); + + EXPECT_TRUE(TestContext->BufferCopyCalled); + } + + // Queue.update_host(accessor acc); + { + TestContext.reset(new TestCtx()); + + int Data[Size]; + buffer Buf(Data, Size); + + accessor> + Acc(Buf); + + ASSERT_TRUE(Acc.is_placeholder()); + + Q.update_host(Acc); + Q.wait(); + + // No PI functions expected. + } + + // Queue.fill(accessor Dest, T src) + { + TestContext.reset(new TestCtx()); + + int Data[Size]; + buffer Buf(Data, Size); + + accessor> + Acc(Buf); + ASSERT_TRUE(Acc.is_placeholder()); + + Q.fill(Acc, 42); + Q.wait(); + + EXPECT_TRUE(TestContext->BufferFillCalled); + } +}