Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SYCL][HIP] Implemented supported make_* interop functions. #10526

Merged
merged 16 commits into from
Aug 30, 2023
6 changes: 4 additions & 2 deletions sycl/include/sycl/backend.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,9 +298,11 @@ std::enable_if_t<detail::InteropFeatureSupportMap<Backend>::MakeQueue == true,
make_queue(const typename backend_traits<Backend>::template input_type<queue>
&BackendObject,
const context &TargetContext, const async_handler Handler = {}) {
auto KeepOwnership =
Backend == backend::ext_oneapi_cuda || Backend == backend::ext_oneapi_hip;
return detail::make_queue(detail::pi::cast<pi_native_handle>(BackendObject),
false, TargetContext, nullptr, false, {}, Handler,
Backend);
false, TargetContext, nullptr, KeepOwnership, {},
Handler, Backend);
}

template <backend Backend>
Expand Down
12 changes: 12 additions & 0 deletions sycl/include/sycl/detail/backend_traits_hip.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,18 @@ template <> struct BackendReturn<backend::ext_oneapi_hip, queue> {
using type = HIPstream;
};

template <> struct InteropFeatureSupportMap<backend::ext_oneapi_hip> {
static constexpr bool MakePlatform = false;
static constexpr bool MakeDevice = true;
static constexpr bool MakeContext = false;
static constexpr bool MakeQueue = true;
static constexpr bool MakeEvent = true;
static constexpr bool MakeBuffer = false;
static constexpr bool MakeKernel = false;
static constexpr bool MakeKernelBundle = false;
static constexpr bool MakeImage = false;
};

} // namespace detail
} // namespace _V1
} // namespace sycl
20 changes: 19 additions & 1 deletion sycl/include/sycl/ext/oneapi/backend/hip.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ inline namespace _V1 {
template <>
inline backend_return_t<backend::ext_oneapi_hip, device>
get_native<backend::ext_oneapi_hip, device>(const device &Obj) {
// TODO use SYCL 2020 exception when implemented
// TODO swap with SYCL 2020 exception when in ABI-break window
if (Obj.get_backend() != backend::ext_oneapi_hip) {
throw sycl::runtime_error(errc::backend_mismatch, "Backends mismatch",
PI_ERROR_INVALID_OPERATION);
Expand All @@ -27,5 +27,23 @@ get_native<backend::ext_oneapi_hip, device>(const device &Obj) {
Obj.getNative());
}

template <>
inline device make_device<backend::ext_oneapi_hip>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this code unique to HIP?

Copy link
Contributor Author

@JackAKirk JackAKirk Jul 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a hierarchy of reasons. Firstly if we want to unique devices (#6055) like in the cuda backend, #7550 , then the code is unique to HIP (since the cuda implementation is in the experimental namespace). If we chose to not unique the device, then we would still need the code to be unique to hip, since, like the cuda backend, we would need to static_cast between pi_native_handle and backend_input_t<backend::ext_oneapi_cuda, device> since the default make_device uses reinterpret_cast which does not work.

const backend_input_t<backend::ext_oneapi_hip, device> &BackendObject) {
auto devs = device::get_devices(info::device_type::gpu);
for (auto &dev : devs) {
if (dev.get_backend() == backend::ext_oneapi_hip &&
BackendObject == get_native<backend::ext_oneapi_hip>(dev)) {
return dev;
}
}
// The ext_oneapi_hip platform(s) adds all n available devices where n
// is returned from call to `hipGetDeviceCount`.
// Hence if this code is reached then the requested device ordinal must
// not be visible to the driver.
throw sycl::exception(make_error_code(errc::invalid),
"Native device has an invalid ordinal.");
}

} // namespace _V1
} // namespace sycl
16 changes: 8 additions & 8 deletions sycl/plugins/unified_runtime/ur/adapters/cuda/queue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,6 @@ UR_APIEXPORT ur_result_t UR_APICALL urQueueCreateWithNativeHandle(
ur_native_handle_t hNativeQueue, ur_context_handle_t hContext,
ur_device_handle_t hDevice, const ur_queue_native_properties_t *pProperties,
ur_queue_handle_t *phQueue) {
(void)pProperties;
(void)hDevice;

unsigned int CuFlags;
Expand All @@ -263,13 +262,14 @@ UR_APIEXPORT ur_result_t UR_APICALL urQueueCreateWithNativeHandle(

// Create queue and set num_compute_streams to 1, as computeCuStreams has
// valid stream
*phQueue = new ur_queue_handle_t_{std::move(ComputeCuStreams),
std::move(TransferCuStreams),
hContext,
hContext->getDevice(),
CuFlags,
Flags,
/*backend_owns*/ false};
*phQueue =
new ur_queue_handle_t_{std::move(ComputeCuStreams),
std::move(TransferCuStreams),
hContext,
hContext->getDevice(),
CuFlags,
Flags,
/*backend_owns*/ pProperties->isNativeHandleOwned};
(*phQueue)->NumComputeStreams = 1;

return Return;
Expand Down
37 changes: 26 additions & 11 deletions sycl/plugins/unified_runtime/ur/adapters/hip/event.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ ur_event_handle_t_::ur_event_handle_t_(ur_command_t Type,
ur_context_handle_t Context,
ur_queue_handle_t Queue,
hipStream_t Stream, uint32_t StreamToken)
: CommandType{Type}, RefCount{1}, HasBeenWaitedOn{false}, IsRecorded{false},
IsStarted{false}, StreamToken{StreamToken}, EvEnd{nullptr},
EvStart{nullptr}, EvQueued{nullptr}, Queue{Queue}, Stream{Stream},
Context{Context} {
: CommandType{Type}, RefCount{1}, HasOwnership{true},
HasBeenWaitedOn{false}, IsRecorded{false}, IsStarted{false},
StreamToken{StreamToken}, EvEnd{nullptr}, EvStart{nullptr},
EvQueued{nullptr}, Queue{Queue}, Stream{Stream}, Context{Context} {

bool ProfilingEnabled = Queue->URFlags & UR_QUEUE_FLAG_PROFILING_ENABLE;

Expand All @@ -36,6 +36,15 @@ ur_event_handle_t_::ur_event_handle_t_(ur_command_t Type,
urContextRetain(Context);
}

ur_event_handle_t_::ur_event_handle_t_(ur_context_handle_t Context,
hipEvent_t EventNative)
: CommandType{UR_COMMAND_EVENTS_WAIT}, RefCount{1}, HasOwnership{false},
HasBeenWaitedOn{false}, IsRecorded{false}, IsStarted{false},
StreamToken{std::numeric_limits<uint32_t>::max()}, EvEnd{EventNative},
EvStart{nullptr}, EvQueued{nullptr}, Queue{nullptr}, Context{Context} {
urContextRetain(Context);
}

ur_event_handle_t_::~ur_event_handle_t_() {
if (Queue != nullptr) {
urQueueRelease(Queue);
Expand Down Expand Up @@ -160,6 +169,9 @@ ur_result_t ur_event_handle_t_::wait() {
}

ur_result_t ur_event_handle_t_::release() {
if (!backendHasOwnership())
return UR_RESULT_SUCCESS;

assert(Queue != nullptr);
UR_CHECK_ERROR(hipEventDestroy(EvEnd));

Expand Down Expand Up @@ -302,15 +314,18 @@ UR_APIEXPORT ur_result_t UR_APICALL urEventGetNativeHandle(
}

/// Created a UR event object from a HIP event handle.
/// TODO: Implement this.
/// NOTE: The created UR object takes ownership of the native handle.
/// NOTE: The created UR object doesn't take ownership of the native handle.
///
/// \param[in] hNativeEvent The native handle to create UR event object from.
/// \param[out] phEvent Set to the UR event object created from native handle.
///
/// \return UR_RESULT_ERROR_UNSUPPORTED_FEATURE
UR_APIEXPORT ur_result_t UR_APICALL urEventCreateWithNativeHandle(
ur_native_handle_t, ur_context_handle_t,
const ur_event_native_properties_t *, ur_event_handle_t *) {
return UR_RESULT_ERROR_UNSUPPORTED_FEATURE;
ur_native_handle_t hNativeEvent, ur_context_handle_t hContext,
const ur_event_native_properties_t *pProperties,
ur_event_handle_t *phEvent) {
std::ignore = pProperties;

*phEvent = ur_event_handle_t_::makeWithNative(
hContext, reinterpret_cast<hipEvent_t>(hNativeEvent));

return UR_RESULT_SUCCESS;
}
13 changes: 13 additions & 0 deletions sycl/plugins/unified_runtime/ur/adapters/hip/event.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ struct ur_event_handle_t_ {

uint32_t getEventId() const noexcept { return EventId; }

bool backendHasOwnership() const noexcept { return HasOwnership; }

// Returns the counter time when the associated command(s) were enqueued
uint64_t getQueuedTime() const;

Expand All @@ -77,6 +79,11 @@ struct ur_event_handle_t_ {
StreamToken);
}

static ur_event_handle_t makeWithNative(ur_context_handle_t context,
hipEvent_t eventNative) {
return new ur_event_handle_t_(context, eventNative);
}

ur_result_t release();

~ur_event_handle_t_();
Expand All @@ -88,10 +95,16 @@ struct ur_event_handle_t_ {
ur_queue_handle_t Queue, hipStream_t Stream,
uint32_t StreamToken);

// This constructor is private to force programmers to use the
// makeWithNative for event interop
ur_event_handle_t_(ur_context_handle_t Context, hipEvent_t EventNative);

ur_command_t CommandType; // The type of command associated with event.

std::atomic_uint32_t RefCount; // Event reference count.

bool HasOwnership; // Signifies if event owns the native type.

bool HasBeenWaitedOn; // Signifies whether the event has been waited
// on through a call to wait(), which implies
// that it has completed.
Expand Down
48 changes: 38 additions & 10 deletions sycl/plugins/unified_runtime/ur/adapters/hip/queue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@ UR_APIEXPORT ur_result_t UR_APICALL urQueueRelease(ur_queue_handle_t hQueue) {
try {
std::unique_ptr<ur_queue_handle_t_> QueueImpl(hQueue);

if (!hQueue->backendHasOwnership())
return UR_RESULT_SUCCESS;

ScopedContext Active(hQueue->getContext()->getDevice());

hQueue->forEachStream([](hipStream_t S) {
Expand Down Expand Up @@ -252,19 +255,44 @@ urQueueGetNativeHandle(ur_queue_handle_t hQueue, ur_queue_native_desc_t *,
}

/// Created a UR queue object from a HIP queue handle.
/// TODO: Implement this.
/// NOTE: The created UR object takes ownership of the native handle.
/// NOTE: The created UR object doesn't takes ownership of the native handle.
///
/// \param[in] hNativeQueue The native handle to create UR queue object from.
/// \param[in] hContext is the UR context of the queue.
/// \param[out] phQueue Set to the UR queue object created from native handle.
/// \param pProperties->isNativeHandleOwned tells if SYCL RT should assume the
/// ownership of
/// the native handle, if it can.
///
/// \return UR_RESULT_ERROR_UNSUPPORTED_FEATURE
UR_APIEXPORT ur_result_t UR_APICALL urQueueCreateWithNativeHandle(
ur_native_handle_t, ur_context_handle_t, ur_device_handle_t,
const ur_queue_native_properties_t *, ur_queue_handle_t *) {
return UR_RESULT_ERROR_UNSUPPORTED_FEATURE;
ur_native_handle_t hNativeQueue, ur_context_handle_t hContext,
ur_device_handle_t hDevice, const ur_queue_native_properties_t *pProperties,
ur_queue_handle_t *phQueue) {
(void)hDevice;

unsigned int HIPFlags;
hipStream_t HIPStream = reinterpret_cast<hipStream_t>(hNativeQueue);

auto Return = UR_CHECK_ERROR(hipStreamGetFlags(HIPStream, &HIPFlags));

ur_queue_flags_t Flags = 0;
if (HIPFlags == hipStreamDefault)
Flags = UR_QUEUE_FLAG_USE_DEFAULT_STREAM;
else if (HIPFlags == hipStreamNonBlocking)
Flags = UR_QUEUE_FLAG_SYNC_WITH_DEFAULT_STREAM;
else
detail::ur::die("Unknown hip stream");

std::vector<hipStream_t> ComputeHIPStreams(1, HIPStream);
std::vector<hipStream_t> TransferHIPStreams(0);

// Create queue and set num_compute_streams to 1, as computeHIPStreams has
// valid stream
*phQueue =
new ur_queue_handle_t_{std::move(ComputeHIPStreams),
std::move(TransferHIPStreams),
hContext,
hContext->getDevice(),
HIPFlags,
Flags,
/*backend_owns*/ pProperties->isNativeHandleOwned};
(*phQueue)->NumComputeStreams = 1;

return Return;
}
8 changes: 6 additions & 2 deletions sycl/plugins/unified_runtime/ur/adapters/hip/queue.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,13 @@ struct ur_queue_handle_t_ {
std::mutex ComputeStreamMutex;
std::mutex TransferStreamMutex;
std::mutex BarrierMutex;
bool HasOwnership;

ur_queue_handle_t_(std::vector<native_type> &&ComputeStreams,
std::vector<native_type> &&TransferStreams,
ur_context_handle_t Context, ur_device_handle_t Device,
unsigned int Flags, ur_queue_flags_t URFlags)
unsigned int Flags, ur_queue_flags_t URFlags,
bool BackendOwns = true)
: ComputeStreams{std::move(ComputeStreams)},
TransferStreams{std::move(TransferStreams)},
DelayCompute(this->ComputeStreams.size(), false),
Expand All @@ -62,7 +64,7 @@ struct ur_queue_handle_t_ {
Device{Device}, RefCount{1}, EventCount{0}, ComputeStreamIdx{0},
TransferStreamIdx{0}, NumComputeStreams{0}, NumTransferStreams{0},
LastSyncComputeStreams{0}, LastSyncTransferStreams{0}, Flags(Flags),
URFlags(URFlags) {
URFlags(URFlags), HasOwnership{BackendOwns} {
urContextRetain(Context);
urDeviceRetain(Device);
}
Expand Down Expand Up @@ -235,4 +237,6 @@ struct ur_queue_handle_t_ {
uint32_t getReferenceCount() const noexcept { return RefCount; }

uint32_t getNextEventId() noexcept { return ++EventCount; }

bool backendHasOwnership() const noexcept { return HasOwnership; }
};
2 changes: 2 additions & 0 deletions sycl/source/backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ static const PluginPtr &getPlugin(backend Backend) {
return pi::getPlugin<backend::ext_oneapi_level_zero>();
case backend::ext_oneapi_cuda:
return pi::getPlugin<backend::ext_oneapi_cuda>();
case backend::ext_oneapi_hip:
return pi::getPlugin<backend::ext_oneapi_hip>();
default:
throw sycl::exception(sycl::make_error_code(sycl::errc::runtime),
"getPlugin: Unsupported backend " +
Expand Down
4 changes: 4 additions & 0 deletions sycl/test/basic_tests/interop-hip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,9 @@ int main() {
hip_event = get_native<backend::ext_oneapi_hip>(Event);
hip_queue = get_native<backend::ext_oneapi_hip>(Queue);

device InteropDevice = make_device<backend::ext_oneapi_hip>(hip_device);
event InteropEvent = make_event<backend::ext_oneapi_hip>(hip_event, Context);
queue InteropQueue = make_queue<backend::ext_oneapi_hip>(hip_queue, Context);

return 0;
}