CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
351 lines
12 KiB
C++
351 lines
12 KiB
C++
//===------- Offload API tests - gtest fixtures --==-----------------------===//
|
|
//
|
|
// 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 <OffloadAPI.h>
|
|
#include <OffloadPrint.hpp>
|
|
#include <gtest/gtest.h>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <thread>
|
|
|
|
#include "Environment.hpp"
|
|
|
|
#pragma once
|
|
|
|
#ifndef ASSERT_SUCCESS
|
|
#define ASSERT_SUCCESS(ACTUAL) \
|
|
do { \
|
|
ol_result_t Res = ACTUAL; \
|
|
if (Res && Res->Code != OL_ERRC_SUCCESS) { \
|
|
GTEST_FAIL() << #ACTUAL " returned " << Res->Code << ": " \
|
|
<< Res->Details; \
|
|
} \
|
|
} while (0)
|
|
#endif
|
|
|
|
#ifndef ASSERT_SUCCESS_OR_UNSUPPORTED
|
|
#define ASSERT_SUCCESS_OR_UNSUPPORTED(ACTUAL) \
|
|
do { \
|
|
ol_result_t Res = ACTUAL; \
|
|
if (Res && Res->Code == OL_ERRC_UNSUPPORTED) { \
|
|
GTEST_SKIP() << #ACTUAL " returned unsupported; skipping test"; \
|
|
return; \
|
|
} else if (Res && Res->Code != OL_ERRC_SUCCESS) { \
|
|
GTEST_FAIL() << #ACTUAL " returned " << Res->Code << ": " \
|
|
<< Res->Details; \
|
|
} \
|
|
} while (0)
|
|
#endif
|
|
|
|
#ifndef ASSERT_ERROR
|
|
#define ASSERT_ERROR(EXPECTED, ACTUAL) \
|
|
do { \
|
|
ol_result_t Res = ACTUAL; \
|
|
if (!Res) \
|
|
GTEST_FAIL() << #ACTUAL " succeeded when we expected it to fail"; \
|
|
if (Res->Code != EXPECTED) \
|
|
GTEST_FAIL() << #ACTUAL " was expected to return " \
|
|
<< #EXPECTED " but instead returned " << Res->Code << ": " \
|
|
<< Res->Details; \
|
|
} while (0)
|
|
#endif
|
|
|
|
#ifndef ASSERT_ANY_ERROR
|
|
#define ASSERT_ANY_ERROR(ACTUAL) \
|
|
do { \
|
|
ol_result_t Res = ACTUAL; \
|
|
ASSERT_TRUE(Res); \
|
|
} while (0)
|
|
#endif
|
|
|
|
struct BackendMatcher {
|
|
ol_platform_backend_t Backend;
|
|
std::string Message;
|
|
|
|
BackendMatcher(ol_platform_backend_t B, std::string M = {})
|
|
: Backend(B), Message(std::move(M)) {}
|
|
};
|
|
|
|
struct LevelZero : BackendMatcher {
|
|
LevelZero(std::string M = {})
|
|
: BackendMatcher(OL_PLATFORM_BACKEND_LEVEL_ZERO, std::move(M)) {}
|
|
};
|
|
|
|
struct CUDA : BackendMatcher {
|
|
CUDA(std::string M = {})
|
|
: BackendMatcher(OL_PLATFORM_BACKEND_CUDA, std::move(M)) {}
|
|
};
|
|
|
|
struct AMDGPU : BackendMatcher {
|
|
AMDGPU(std::string M = {})
|
|
: BackendMatcher(OL_PLATFORM_BACKEND_AMDGPU, std::move(M)) {}
|
|
};
|
|
|
|
inline std::string knownFailureMessage(const BackendMatcher &M) {
|
|
std::string Msg;
|
|
llvm::raw_string_ostream OS(Msg);
|
|
OS << "Known failure on " << M.Backend;
|
|
if (!M.Message.empty())
|
|
OS << ": " << M.Message;
|
|
return Msg;
|
|
}
|
|
|
|
inline std::optional<std::string>
|
|
findKnownFailure(ol_platform_backend_t CurBackend,
|
|
std::initializer_list<BackendMatcher> Matchers) {
|
|
for (const auto &M : Matchers) {
|
|
if (M.Backend == CurBackend)
|
|
return knownFailureMessage(M);
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
#define SKIP_KNOWN_FAILURE(...) \
|
|
if (auto KFMsg = \
|
|
::findKnownFailure(this->getPlatformBackend(), {__VA_ARGS__})) \
|
|
GTEST_SKIP() << *KFMsg
|
|
|
|
#define RETURN_ON_FATAL_FAILURE(...) \
|
|
__VA_ARGS__; \
|
|
if (this->HasFatalFailure() || this->IsSkipped()) { \
|
|
return; \
|
|
} \
|
|
(void)0
|
|
|
|
inline std::string SanitizeString(const std::string &Str) {
|
|
auto NewStr = Str;
|
|
std::replace_if(
|
|
NewStr.begin(), NewStr.end(), [](char C) { return !std::isalnum(C); },
|
|
'_');
|
|
return NewStr;
|
|
}
|
|
|
|
template <typename Fn> inline void threadify(Fn body) {
|
|
std::vector<std::thread> Threads;
|
|
for (size_t I = 0; I < 20; I++) {
|
|
Threads.emplace_back(
|
|
[&body](size_t I) {
|
|
std::string ScopeMsg{"Thread #"};
|
|
ScopeMsg.append(std::to_string(I));
|
|
SCOPED_TRACE(ScopeMsg);
|
|
body(I);
|
|
},
|
|
I);
|
|
}
|
|
for (auto &T : Threads) {
|
|
T.join();
|
|
}
|
|
}
|
|
|
|
/// Enqueues a task to the queue that can be manually resolved.
|
|
// It will block until `trigger` is called.
|
|
struct ManuallyTriggeredTask {
|
|
std::mutex M;
|
|
std::condition_variable CV;
|
|
bool Flag = false;
|
|
ol_event_handle_t CompleteEvent;
|
|
|
|
ol_result_t enqueue(ol_queue_handle_t Queue) {
|
|
if (auto Err = olLaunchHostFunction(
|
|
Queue,
|
|
[](void *That) {
|
|
static_cast<ManuallyTriggeredTask *>(That)->wait();
|
|
},
|
|
this))
|
|
return Err;
|
|
|
|
return olCreateEvent(Queue, OL_EVENT_FLAGS_NONE, &CompleteEvent);
|
|
}
|
|
|
|
void wait() {
|
|
std::unique_lock<std::mutex> lk(M);
|
|
CV.wait_for(lk, std::chrono::milliseconds(1000), [&] { return Flag; });
|
|
EXPECT_TRUE(Flag);
|
|
}
|
|
|
|
ol_result_t trigger() {
|
|
Flag = true;
|
|
CV.notify_one();
|
|
|
|
return olSyncEvent(CompleteEvent);
|
|
}
|
|
};
|
|
|
|
struct OffloadTest : ::testing::Test {
|
|
ol_device_handle_t Host = TestEnvironment::getHostDevice();
|
|
};
|
|
|
|
struct OffloadDeviceTest
|
|
: OffloadTest,
|
|
::testing::WithParamInterface<TestEnvironment::Device> {
|
|
void SetUp() override {
|
|
RETURN_ON_FATAL_FAILURE(OffloadTest::SetUp());
|
|
|
|
auto DeviceParam = GetParam();
|
|
Device = DeviceParam.Handle;
|
|
if (Device == nullptr)
|
|
GTEST_SKIP() << "No available devices.";
|
|
}
|
|
|
|
ol_platform_backend_t getPlatformBackend() const {
|
|
ol_platform_handle_t Platform = nullptr;
|
|
if (olGetDeviceInfo(Device, OL_DEVICE_INFO_PLATFORM,
|
|
sizeof(ol_platform_handle_t), &Platform))
|
|
return OL_PLATFORM_BACKEND_UNKNOWN;
|
|
ol_platform_backend_t Backend;
|
|
if (olGetPlatformInfo(Platform, OL_PLATFORM_INFO_BACKEND,
|
|
sizeof(ol_platform_backend_t), &Backend))
|
|
return OL_PLATFORM_BACKEND_UNKNOWN;
|
|
return Backend;
|
|
}
|
|
|
|
ol_device_handle_t Device = nullptr;
|
|
};
|
|
|
|
struct OffloadPlatformTest : OffloadDeviceTest {
|
|
void SetUp() override {
|
|
RETURN_ON_FATAL_FAILURE(OffloadDeviceTest::SetUp());
|
|
|
|
ASSERT_SUCCESS(olGetDeviceInfo(Device, OL_DEVICE_INFO_PLATFORM,
|
|
sizeof(Platform), &Platform));
|
|
ASSERT_NE(Platform, nullptr);
|
|
}
|
|
|
|
ol_platform_handle_t Platform = nullptr;
|
|
};
|
|
|
|
// Fixture for a generic program test. If you want a different program, use
|
|
// offloadQueueTest and create your own program handle with the binary you want.
|
|
struct OffloadProgramTest : OffloadDeviceTest {
|
|
void SetUp() override { SetUpWith("foo"); }
|
|
|
|
void SetUpWith(const char *ProgramName) {
|
|
RETURN_ON_FATAL_FAILURE(OffloadDeviceTest::SetUp());
|
|
ASSERT_TRUE(
|
|
TestEnvironment::loadDeviceBinary(ProgramName, Device, DeviceBin));
|
|
ASSERT_GE(DeviceBin->getBufferSize(), 0lu);
|
|
ASSERT_SUCCESS(olCreateProgram(Device, DeviceBin->getBufferStart(),
|
|
DeviceBin->getBufferSize(), &Program));
|
|
}
|
|
|
|
void TearDown() override {
|
|
if (Program) {
|
|
olDestroyProgram(Program);
|
|
}
|
|
RETURN_ON_FATAL_FAILURE(OffloadDeviceTest::TearDown());
|
|
}
|
|
|
|
ol_program_handle_t Program = nullptr;
|
|
std::unique_ptr<llvm::MemoryBuffer> DeviceBin;
|
|
};
|
|
|
|
struct OffloadKernelTest : OffloadProgramTest {
|
|
void SetUp() override {
|
|
RETURN_ON_FATAL_FAILURE(OffloadProgramTest::SetUp());
|
|
ASSERT_SUCCESS(olGetSymbol(Program, "foo", OL_SYMBOL_KIND_KERNEL, &Kernel));
|
|
}
|
|
|
|
void TearDown() override {
|
|
RETURN_ON_FATAL_FAILURE(OffloadProgramTest::TearDown());
|
|
}
|
|
|
|
ol_symbol_handle_t Kernel = nullptr;
|
|
};
|
|
|
|
struct OffloadGlobalTest : OffloadProgramTest {
|
|
void SetUp() override {
|
|
RETURN_ON_FATAL_FAILURE(OffloadProgramTest::SetUpWith("global"));
|
|
ASSERT_SUCCESS(olGetSymbol(Program, "global",
|
|
OL_SYMBOL_KIND_GLOBAL_VARIABLE, &Global));
|
|
}
|
|
|
|
void TearDown() override {
|
|
RETURN_ON_FATAL_FAILURE(OffloadProgramTest::TearDown());
|
|
}
|
|
|
|
ol_symbol_handle_t Global = nullptr;
|
|
};
|
|
|
|
struct OffloadQueueTest : OffloadDeviceTest {
|
|
void SetUp() override {
|
|
RETURN_ON_FATAL_FAILURE(OffloadDeviceTest::SetUp());
|
|
ASSERT_SUCCESS(olCreateQueue(Device, &Queue));
|
|
}
|
|
|
|
void TearDown() override {
|
|
if (Queue) {
|
|
olDestroyQueue(Queue);
|
|
}
|
|
RETURN_ON_FATAL_FAILURE(OffloadDeviceTest::TearDown());
|
|
}
|
|
|
|
ol_queue_handle_t Queue = nullptr;
|
|
};
|
|
|
|
struct OffloadEventTest : OffloadQueueTest {
|
|
void SetUp() override {
|
|
RETURN_ON_FATAL_FAILURE(OffloadQueueTest::SetUp());
|
|
ASSERT_SUCCESS(olCreateEvent(Queue, OL_EVENT_FLAGS_NONE, &Event));
|
|
ASSERT_SUCCESS(olSyncQueue(Queue));
|
|
}
|
|
|
|
void TearDown() override {
|
|
if (Event)
|
|
olDestroyEvent(Event);
|
|
RETURN_ON_FATAL_FAILURE(OffloadQueueTest::TearDown());
|
|
}
|
|
|
|
ol_event_handle_t Event = nullptr;
|
|
};
|
|
|
|
struct LaunchKernelTestBase : OffloadQueueTest {
|
|
void SetUpProgram(const char *program) {
|
|
RETURN_ON_FATAL_FAILURE(OffloadQueueTest::SetUp());
|
|
ASSERT_TRUE(TestEnvironment::loadDeviceBinary(program, Device, DeviceBin));
|
|
ASSERT_GE(DeviceBin->getBufferSize(), 0lu);
|
|
ASSERT_SUCCESS(olCreateProgram(Device, DeviceBin->getBufferStart(),
|
|
DeviceBin->getBufferSize(), &Program));
|
|
|
|
LaunchArgs.Dimensions = 1;
|
|
LaunchArgs.GroupSize = {64, 1, 1};
|
|
LaunchArgs.NumGroups = {1, 1, 1};
|
|
LaunchArgs.DynSharedMemory = 0;
|
|
}
|
|
|
|
void TearDown() override {
|
|
if (Program)
|
|
olDestroyProgram(Program);
|
|
RETURN_ON_FATAL_FAILURE(OffloadQueueTest::TearDown());
|
|
}
|
|
|
|
std::unique_ptr<llvm::MemoryBuffer> DeviceBin;
|
|
ol_program_handle_t Program = nullptr;
|
|
ol_kernel_launch_size_args_t LaunchArgs{};
|
|
};
|
|
|
|
struct LaunchSingleKernelTestBase : LaunchKernelTestBase {
|
|
void SetUpKernel(const char *kernel) {
|
|
RETURN_ON_FATAL_FAILURE(SetUpProgram(kernel));
|
|
ASSERT_SUCCESS(
|
|
olGetSymbol(Program, kernel, OL_SYMBOL_KIND_KERNEL, &Kernel));
|
|
}
|
|
|
|
ol_symbol_handle_t Kernel = nullptr;
|
|
};
|
|
|
|
// Devices might not be available for offload testing, so allow uninstantiated
|
|
// tests (as the device list will be empty). This means that all tests requiring
|
|
// a device will be silently skipped.
|
|
#define OFFLOAD_TESTS_INSTANTIATE_DEVICE_FIXTURE(FIXTURE) \
|
|
INSTANTIATE_TEST_SUITE_P( \
|
|
, FIXTURE, ::testing::ValuesIn(TestEnvironment::getDevices()), \
|
|
[](const ::testing::TestParamInfo<TestEnvironment::Device> &info) { \
|
|
return SanitizeString(info.param.Name); \
|
|
}); \
|
|
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(FIXTURE)
|