blob: 5cad8eb140528c690d19ca333d36aa6467f559be [file] [log] [blame] [edit]
//===--- Target RTLs Implementation ---------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// RTL for SPIR-V/Xe machine.
//
//===----------------------------------------------------------------------===//
#include <level_zero/zes_api.h>
#include "L0Device.h"
#include "L0Interop.h"
#include "L0Kernel.h"
#include "L0Plugin.h"
#include "L0Trace.h"
namespace llvm::omp::target::plugin {
using namespace llvm::omp::target;
using namespace error;
#pragma clang diagnostic ignored "-Wglobal-constructors"
// Common data across all possible plugin instantiations.
L0OptionsTy LevelZeroPluginTy::Options;
Expected<int32_t> LevelZeroPluginTy::findDevices() {
CALL_ZE_RET_ERROR(zeInit, ZE_INIT_FLAG_GPU_ONLY);
uint32_t NumDrivers = 0;
CALL_ZE_RET_ERROR(zeDriverGet, &NumDrivers, nullptr);
if (NumDrivers == 0) {
DP("Cannot find any drivers.\n");
return 0;
}
// We expect multiple drivers on Windows to support different device types,
// so we need to maintain multiple drivers and contexts in general.
llvm::SmallVector<ze_driver_handle_t> FoundDrivers(NumDrivers);
CALL_ZE_RET_ERROR(zeDriverGet, &NumDrivers, FoundDrivers.data());
struct RootInfoTy {
uint32_t OrderId;
ze_device_handle_t ZeDevice;
L0ContextTy *Driver;
bool IsDiscrete;
};
llvm::SmallVector<RootInfoTy> RootDevices;
uint32_t OrderId = 0;
for (uint32_t DriverId = 0; DriverId < NumDrivers; DriverId++) {
const auto &Driver = FoundDrivers[DriverId];
uint32_t DeviceCount = 0;
ze_result_t RC;
CALL_ZE(RC, zeDeviceGet, Driver, &DeviceCount, nullptr);
if (RC != ZE_RESULT_SUCCESS || DeviceCount == 0) {
DP("Cannot find any devices from driver " DPxMOD ".\n", DPxPTR(Driver));
continue;
}
// We have a driver that supports at least one device.
ContextList.emplace_back(*this, Driver, DriverId);
auto &DrvInfo = ContextList.back();
if (auto Err = DrvInfo.init())
return std::move(Err);
llvm::SmallVector<ze_device_handle_t> FoundDevices(DeviceCount);
CALL_ZE_RET_ERROR(zeDeviceGet, Driver, &DeviceCount, FoundDevices.data());
for (auto &zeDevice : FoundDevices)
RootDevices.push_back(
{OrderId++, zeDevice, &DrvInfo, L0DeviceTy::isDiscrete(zeDevice)});
}
// Move discrete devices to the front.
std::sort(RootDevices.begin(), RootDevices.end(),
[](const RootInfoTy &A, const RootInfoTy &B) {
// If both are discrete, order by OrderId.
// If both are not discrete, order by OrderId.
// Otherwise, discrete goes first.
if (A.IsDiscrete && B.IsDiscrete)
return A.OrderId < B.OrderId;
if (!A.IsDiscrete && !B.IsDiscrete)
return A.OrderId < B.OrderId;
return A.IsDiscrete;
});
for (size_t RootId = 0; RootId < RootDevices.size(); RootId++) {
const auto ZeDevice = RootDevices[RootId].ZeDevice;
auto *RootDriver = RootDevices[RootId].Driver;
DetectedDevices.push_back(DeviceInfoTy{
{ZeDevice, static_cast<int32_t>(RootId), -1, -1}, RootDriver});
}
int32_t NumDevices = DetectedDevices.size();
DP("Found %" PRIu32 " devices.\n", NumDevices);
DP("List of devices (DeviceID[.SubID[.CCSID]])\n");
for (auto &DeviceInfo : DetectedDevices) {
(void)DeviceInfo; // Avoid unused variable warning in non-debug builds.
DP("-- Device %" PRIu32 "%s%s\n", DeviceInfo.Id.RootId,
(DeviceInfo.Id.SubId < 0
? ""
: ("." + std::to_string(DeviceInfo.Id.SubId)).c_str()),
(DeviceInfo.Id.CCSId < 0
? ""
: ("." + std::to_string(DeviceInfo.Id.CCSId)).c_str()));
}
return NumDevices;
}
Expected<int32_t> LevelZeroPluginTy::initImpl() {
DP("Level0 NG plugin initialization\n");
// Process options before anything else.
Options.init();
return findDevices();
}
Error LevelZeroPluginTy::deinitImpl() {
DP("Deinit Level0 plugin!\n");
if (auto Err = ContextTLSTable.deinit())
return Err;
if (auto Err = DeviceTLSTable.deinit())
return Err;
for (auto &Context : ContextList)
if (auto Err = Context.deinit())
return Err;
ContextList.clear();
DP("Level0 plugin deinitialized successfully\n");
return Plugin::success();
}
GenericDeviceTy *LevelZeroPluginTy::createDevice(GenericPluginTy &Plugin,
int32_t DeviceId,
int32_t NumDevices) {
auto &DeviceInfo = DetectedDevices[DeviceId];
auto RootId = DeviceInfo.Id.RootId;
auto SubId = DeviceInfo.Id.SubId;
auto CCSId = DeviceInfo.Id.CCSId;
auto zeDevice = DeviceInfo.Id.zeId;
auto *zeDriver = DeviceInfo.Driver;
std::string IdStr = std::to_string(RootId) +
(SubId < 0 ? "" : "." + std::to_string(SubId)) +
(CCSId < 0 ? "" : "." + std::to_string(CCSId));
return new L0DeviceTy(static_cast<LevelZeroPluginTy &>(Plugin), DeviceId,
NumDevices, zeDevice, *zeDriver, std::move(IdStr),
CCSId < 0 ? 0 : CCSId /* ComputeIndex */);
}
GenericGlobalHandlerTy *LevelZeroPluginTy::createGlobalHandler() {
return new L0GlobalHandlerTy();
}
Error LevelZeroPluginTy::flushQueueImpl(omp_interop_val_t *Interop) {
return Plugin::success();
}
Expected<bool> LevelZeroPluginTy::isELFCompatible(uint32_t DeviceId,
StringRef Image) const {
uint64_t MajorVer, MinorVer;
return isValidOneOmpImage(Image, MajorVer, MinorVer);
}
Error LevelZeroPluginTy::syncBarrierImpl(omp_interop_val_t *Interop) {
if (!Interop) {
return Plugin::error(ErrorCode::INVALID_ARGUMENT,
"Invalid/inconsistent OpenMP interop " DPxMOD "\n",
DPxPTR(Interop));
}
if (!Interop->async_info || !Interop->async_info->Queue)
return Plugin::success();
const auto L0 = static_cast<L0Interop::Property *>(Interop->rtl_property);
const auto device_id = Interop->device_id;
auto &l0Device = getDeviceFromId(device_id);
// We can synchronize both L0 & SYCL objects with the same ze command.
if (l0Device.useImmForInterop()) {
DP("LevelZeroPluginTy::sync_barrier: Synchronizing " DPxMOD
" with ImmCmdList barrier\n",
DPxPTR(Interop));
auto ImmCmdList = L0->ImmCmdList;
CALL_ZE_RET_ERROR(zeCommandListHostSynchronize, ImmCmdList,
L0DefaultTimeout);
} else {
DP("LevelZeroPluginTy::sync_barrier: Synchronizing " DPxMOD
" with queue synchronize\n",
DPxPTR(Interop));
auto CmdQueue = L0->CommandQueue;
CALL_ZE_RET_ERROR(zeCommandQueueSynchronize, CmdQueue, L0DefaultTimeout);
}
return Plugin::success();
}
Error LevelZeroPluginTy::asyncBarrierImpl(omp_interop_val_t *Interop) {
if (!Interop) {
return Plugin::error(ErrorCode::INVALID_ARGUMENT,
"Invalid/inconsistent OpenMP interop " DPxMOD "\n",
DPxPTR(Interop));
}
if (!Interop->async_info || !Interop->async_info->Queue)
return Plugin::success();
const auto L0 = static_cast<L0Interop::Property *>(Interop->rtl_property);
const auto device_id = Interop->device_id;
if (Interop->attrs.inorder)
return Plugin::success();
auto &l0Device = getDeviceFromId(device_id);
if (l0Device.useImmForInterop()) {
DP("LevelZeroPluginTy::async_barrier: Appending ImmCmdList barrier "
"to " DPxMOD "\n",
DPxPTR(Interop));
auto ImmCmdList = L0->ImmCmdList;
CALL_ZE_RET_ERROR(zeCommandListAppendBarrier, ImmCmdList, nullptr, 0,
nullptr);
} else {
#if 0
// TODO: re-enable once we have a way to delay the CmdList reset .
DP("LevelZeroPluginTy::async_barrier: Appending CmdList barrier to " DPxMOD
"\n",
DPxPTR(Interop));
auto CmdQueue = L0->CommandQueue;
ze_command_list_handle_t CmdList = l0Device.getCmdList();
CALL_ZE_RET_ERROR(zeCommandListAppendBarrier, CmdList, nullptr, 0, nullptr);
CALL_ZE_RET_ERROR(zeCommandListClose, CmdList);
CALL_ZE_RET_ERROR(zeCommandQueueExecuteCommandLists, CmdQueue, 1, &CmdList,
nullptr);
CALL_ZE_RET_ERROR(zeCommandListReset, CmdList);
#else
return syncBarrierImpl(Interop);
#endif
}
return Plugin::success();
}
} // namespace llvm::omp::target::plugin
extern "C" {
llvm::omp::target::plugin::GenericPluginTy *createPlugin_level_zero() {
return new llvm::omp::target::plugin::LevelZeroPluginTy();
}
}