Add native Windows gfx1201 HIP and ZLUDA diagnostics
This commit is contained in:
1 parent
f310d52b69
commit
e95e061e81
12 files changed
+931
-1
No files matched your search
@@ -0,0 +1,159 @@
|
||||
#include "hip_api.h"
|
||||
#include <algorithm>
|
||||
|
||||
// CUDA Driver API ABI subset. No CUDA SDK, CUDA runtime or NVIDIA binary is needed.
|
||||
using CUdevice = int;
|
||||
using CUdeviceptr = uint64_t;
|
||||
using CUcontext = void*;
|
||||
using CUmodule = void*;
|
||||
using CUfunction = void*;
|
||||
struct CudaApi {
|
||||
d4r::diag::Library library;
|
||||
#define D4R_CU(name, ret, ...) using name##_fn = ret(WINAPI*)(__VA_ARGS__); name##_fn name = library.symbol<name##_fn>(#name)
|
||||
D4R_CU(cuInit, int, unsigned);
|
||||
D4R_CU(cuDriverGetVersion, int, int*);
|
||||
D4R_CU(cuDeviceGetCount, int, int*);
|
||||
D4R_CU(cuDeviceGet, int, CUdevice*, int);
|
||||
D4R_CU(cuDeviceGetName, int, char*, int, CUdevice);
|
||||
D4R_CU(cuDeviceGetAttribute, int, int*, int, CUdevice);
|
||||
D4R_CU(cuDevicePrimaryCtxRetain, int, CUcontext*, CUdevice);
|
||||
D4R_CU(cuDevicePrimaryCtxRelease_v2, int, CUdevice);
|
||||
D4R_CU(cuCtxSetCurrent, int, CUcontext);
|
||||
D4R_CU(cuCtxCreate_v2, int, CUcontext*, unsigned, CUdevice);
|
||||
D4R_CU(cuCtxDestroy_v2, int, CUcontext);
|
||||
D4R_CU(cuCtxSynchronize, int);
|
||||
D4R_CU(cuMemAlloc_v2, int, CUdeviceptr*, size_t);
|
||||
D4R_CU(cuMemFree_v2, int, CUdeviceptr);
|
||||
D4R_CU(cuMemcpyHtoD_v2, int, CUdeviceptr, const void*, size_t);
|
||||
D4R_CU(cuMemcpyDtoH_v2, int, void*, CUdeviceptr, size_t);
|
||||
D4R_CU(cuModuleLoadData, int, CUmodule*, const void*);
|
||||
D4R_CU(cuModuleUnload, int, CUmodule);
|
||||
D4R_CU(cuModuleGetFunction, int, CUfunction*, CUmodule, const char*);
|
||||
D4R_CU(cuLaunchKernel, int, CUfunction, unsigned, unsigned, unsigned, unsigned, unsigned, unsigned,
|
||||
unsigned, void*, void**, void**);
|
||||
D4R_CU(cuGetErrorName, int, int, const char**);
|
||||
D4R_CU(cuGetErrorString, int, int, const char**);
|
||||
#undef D4R_CU
|
||||
explicit CudaApi(const std::string& path) : library(d4r::diag::wide(path)) {}
|
||||
void check(int result, const char* call) {
|
||||
const char* name = "unknown", *message = "unknown";
|
||||
cuGetErrorName(result, &name);
|
||||
cuGetErrorString(result, &message);
|
||||
std::printf("CUDA %s -> %d (%s)\n", call, result, name ? name : "unknown");
|
||||
if (result) throw std::runtime_error(std::string(call) + ": " + (message ? message : "unknown"));
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr char ptx[] = R"PTX(
|
||||
.version 7.0
|
||||
.target sm_75
|
||||
.address_size 64
|
||||
.visible .entry d4r_ptx_pattern(
|
||||
.param .u64 output, .param .u32 count, .param .u32 seed)
|
||||
{
|
||||
.reg .pred %p;
|
||||
.reg .b32 %r<8>;
|
||||
.reg .b64 %rd<4>;
|
||||
ld.param.u64 %rd0, [output];
|
||||
ld.param.u32 %r0, [count];
|
||||
ld.param.u32 %r1, [seed];
|
||||
mov.u32 %r2, %ctaid.x;
|
||||
mov.u32 %r3, %ntid.x;
|
||||
mov.u32 %r4, %tid.x;
|
||||
mad.lo.u32 %r5, %r2, %r3, %r4;
|
||||
setp.ge.u32 %p, %r5, %r0;
|
||||
@%p bra done;
|
||||
mad.lo.u32 %r6, %r5, 1664525, %r1;
|
||||
xor.b32 %r7, %r6, 0xa5a55a5a;
|
||||
mul.wide.u32 %rd1, %r5, 4;
|
||||
add.u64 %rd2, %rd0, %rd1;
|
||||
st.global.u32 [%rd2], %r7;
|
||||
done:
|
||||
ret;
|
||||
}
|
||||
)PTX";
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
using namespace d4r::diag;
|
||||
start();
|
||||
try {
|
||||
Args args(argc, argv);
|
||||
if (args.cuda_dll.empty()) throw std::runtime_error("--cuda-dll is required (absolute ZLUDA nvcuda.dll path)");
|
||||
if (args.context != "primary" && args.context != "created") throw std::runtime_error("context must be primary or created");
|
||||
// Require the real HIP architecture, then match the CUDA device by PCI identity.
|
||||
HipApi hip(args.hip_root);
|
||||
hipDeviceProp_t props{};
|
||||
hip.select_gfx1201(args.device, props);
|
||||
CudaApi api(args.cuda_dll);
|
||||
loaded_modules();
|
||||
std::printf("STAGE cuInit\n");
|
||||
api.check(api.cuInit(0), "cuInit");
|
||||
int driver = 0, count = 0;
|
||||
api.check(api.cuDriverGetVersion(&driver), "cuDriverGetVersion");
|
||||
api.check(api.cuDeviceGetCount(&count), "cuDeviceGetCount");
|
||||
std::printf("CUDA driver_api=%d devices=%d\n", driver, count);
|
||||
CUdevice device = -1;
|
||||
for (int ordinal = 0; ordinal < count; ++ordinal) {
|
||||
CUdevice candidate;
|
||||
api.check(api.cuDeviceGet(&candidate, ordinal), "cuDeviceGet");
|
||||
char name[256]{};
|
||||
int bus = -1, pci_device = -1, domain = -1;
|
||||
api.check(api.cuDeviceGetName(name, sizeof(name), candidate), "cuDeviceGetName");
|
||||
api.check(api.cuDeviceGetAttribute(&bus, 33, candidate), "cuDeviceGetAttribute(PCI_BUS_ID)");
|
||||
api.check(api.cuDeviceGetAttribute(&pci_device, 34, candidate), "cuDeviceGetAttribute(PCI_DEVICE_ID)");
|
||||
api.check(api.cuDeviceGetAttribute(&domain, 50, candidate), "cuDeviceGetAttribute(PCI_DOMAIN_ID)");
|
||||
std::printf("CUDA GPU ordinal=%d name=%s pci=%04x:%02x:%02x\n", ordinal, name, domain, bus, pci_device);
|
||||
if (bus == props.pciBusID && pci_device == props.pciDeviceID && domain == props.pciDomainID) device = candidate;
|
||||
}
|
||||
if (device < 0) throw std::runtime_error("CUDA/HIP PCI identity mismatch");
|
||||
CUcontext context = nullptr;
|
||||
const bool primary = args.context == "primary";
|
||||
if (primary) {
|
||||
api.check(api.cuDevicePrimaryCtxRetain(&context, device), "cuDevicePrimaryCtxRetain");
|
||||
} else api.check(api.cuCtxCreate_v2(&context, 0, device), "cuCtxCreate_v2");
|
||||
struct ContextCleanup {
|
||||
CudaApi& api; CUcontext context; CUdevice device; bool primary;
|
||||
~ContextCleanup() { if (context) { api.cuCtxSetCurrent(nullptr);
|
||||
if (primary) api.cuDevicePrimaryCtxRelease_v2(device); else api.cuCtxDestroy_v2(context); } }
|
||||
} cleanup{api, context, device, primary};
|
||||
api.check(api.cuCtxSetCurrent(context), "cuCtxSetCurrent");
|
||||
CUmodule module = nullptr;
|
||||
std::printf("STAGE cuModuleLoadData (PTX JIT)\n");
|
||||
api.check(api.cuModuleLoadData(&module, ptx), "cuModuleLoadData");
|
||||
struct ModuleCleanup { CudaApi& api; CUmodule m; ~ModuleCleanup() { if (m) api.cuModuleUnload(m); } } mc{api, module};
|
||||
loaded_modules();
|
||||
CUfunction function = nullptr;
|
||||
api.check(api.cuModuleGetFunction(&function, module, "d4r_ptx_pattern"), "cuModuleGetFunction");
|
||||
constexpr uint32_t nmax = 4099;
|
||||
std::vector<uint32_t> result(nmax + 61, 0xdeadbeefu);
|
||||
const size_t bytes = result.size() * sizeof(uint32_t);
|
||||
CUdeviceptr pointer = 0;
|
||||
api.check(api.cuMemAlloc_v2(&pointer, bytes), "cuMemAlloc_v2");
|
||||
struct MemCleanup { CudaApi& api; CUdeviceptr p; ~MemCleanup() { if (p) api.cuMemFree_v2(p); } } ac{api, pointer};
|
||||
for (unsigned i = 0; i < args.iterations; ++i) {
|
||||
uint32_t n = nmax - i % 129u, seed = 0x12345678u + i * 31u;
|
||||
std::fill(result.begin(), result.end(), 0xdeadbeefu);
|
||||
api.check(api.cuMemcpyHtoD_v2(pointer, result.data(), bytes), "cuMemcpyHtoD_v2(sentinel)");
|
||||
void* parameters[] = {&pointer, &n, &seed};
|
||||
api.check(api.cuLaunchKernel(function, (n + 127u) / 128u, 1, 1, 128, 1, 1,
|
||||
0, nullptr, parameters, nullptr), "cuLaunchKernel");
|
||||
api.check(api.cuCtxSynchronize(), "cuCtxSynchronize");
|
||||
api.check(api.cuMemcpyDtoH_v2(result.data(), pointer, bytes), "cuMemcpyDtoH_v2");
|
||||
if (!verify(result, n, seed)) return 5;
|
||||
}
|
||||
api.check(api.cuMemFree_v2(pointer), "cuMemFree_v2"); ac.p = 0;
|
||||
api.check(api.cuModuleUnload(module), "cuModuleUnload"); mc.m = nullptr;
|
||||
api.check(api.cuCtxSetCurrent(nullptr), "cuCtxSetCurrent(NULL)");
|
||||
if (primary) api.check(api.cuDevicePrimaryCtxRelease_v2(device), "cuDevicePrimaryCtxRelease_v2");
|
||||
else api.check(api.cuCtxDestroy_v2(context), "cuCtxDestroy_v2");
|
||||
cleanup.context = nullptr;
|
||||
std::printf("PASS CUDA architecture=gfx1201 context=%s iterations=%u guard_verified=1\n",
|
||||
args.context.c_str(), args.iterations);
|
||||
return 0;
|
||||
} catch (const std::exception& e) {
|
||||
std::fprintf(stderr, "FAIL CUDA %s\n", e.what());
|
||||
loaded_modules();
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <dbghelp.h>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
namespace d4r::diag {
|
||||
inline std::wstring wide(const std::string& s)
|
||||
{
|
||||
const int n = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, nullptr, 0);
|
||||
if (!n) throw std::runtime_error("Invalid UTF-8 path");
|
||||
std::wstring out(n, L'\0');
|
||||
MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, out.data(), n);
|
||||
out.pop_back();
|
||||
return out;
|
||||
}
|
||||
inline std::string utf8(const wchar_t* s)
|
||||
{
|
||||
const int n = WideCharToMultiByte(CP_UTF8, 0, s, -1, nullptr, 0, nullptr, nullptr);
|
||||
std::string out(n, '\0');
|
||||
WideCharToMultiByte(CP_UTF8, 0, s, -1, out.data(), n, nullptr, nullptr);
|
||||
if (n) out.pop_back();
|
||||
return out;
|
||||
}
|
||||
inline void loaded_modules()
|
||||
{
|
||||
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
|
||||
if (snapshot == INVALID_HANDLE_VALUE) return;
|
||||
MODULEENTRY32W entry{};
|
||||
entry.dwSize = sizeof(entry);
|
||||
if (Module32FirstW(snapshot, &entry)) do {
|
||||
DWORD unused = 0;
|
||||
DWORD size = GetFileVersionInfoSizeW(entry.szExePath, &unused);
|
||||
std::vector<unsigned char> bytes(size);
|
||||
VS_FIXEDFILEINFO* version = nullptr;
|
||||
UINT length = 0;
|
||||
std::printf("DLL path=%s", utf8(entry.szExePath).c_str());
|
||||
if (size && GetFileVersionInfoW(entry.szExePath, 0, size, bytes.data()) &&
|
||||
VerQueryValueW(bytes.data(), L"\\", reinterpret_cast<void**>(&version), &length) &&
|
||||
length >= sizeof(*version))
|
||||
std::printf(" version=%u.%u.%u.%u", HIWORD(version->dwFileVersionMS),
|
||||
LOWORD(version->dwFileVersionMS), HIWORD(version->dwFileVersionLS),
|
||||
LOWORD(version->dwFileVersionLS));
|
||||
std::printf("\n");
|
||||
} while (Module32NextW(snapshot, &entry));
|
||||
CloseHandle(snapshot);
|
||||
}
|
||||
inline LONG WINAPI unhandled(EXCEPTION_POINTERS* info)
|
||||
{
|
||||
std::fprintf(stderr, "EXCEPTION code=0x%08lx address=%p\n",
|
||||
info->ExceptionRecord->ExceptionCode, info->ExceptionRecord->ExceptionAddress);
|
||||
loaded_modules();
|
||||
wchar_t directory[32768]{};
|
||||
const DWORD n = GetEnvironmentVariableW(L"D4R_DIAG_DIR", directory, 32768);
|
||||
if (n && n < 32768) {
|
||||
const std::wstring path = std::wstring(directory) + L"\\crash-" +
|
||||
std::to_wstring(GetCurrentProcessId()) + L".dmp";
|
||||
HANDLE file = CreateFileW(path.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, nullptr);
|
||||
if (file != INVALID_HANDLE_VALUE) {
|
||||
MINIDUMP_EXCEPTION_INFORMATION exception{GetCurrentThreadId(), info, FALSE};
|
||||
const BOOL ok = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), file,
|
||||
MiniDumpNormal, &exception, nullptr, nullptr);
|
||||
std::fprintf(stderr, "MINIDUMP success=%d error=%lu\n", ok, ok ? 0 : GetLastError());
|
||||
CloseHandle(file);
|
||||
}
|
||||
}
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
inline void start()
|
||||
{
|
||||
std::setvbuf(stdout, nullptr, _IONBF, 0);
|
||||
std::setvbuf(stderr, nullptr, _IONBF, 0);
|
||||
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
|
||||
SetUnhandledExceptionFilter(unhandled);
|
||||
std::printf("d4r native Windows diagnostic x64 pid=%lu\n", GetCurrentProcessId());
|
||||
}
|
||||
struct Args {
|
||||
std::string hip_root, cuda_dll, module, context = "primary";
|
||||
unsigned iterations = 32;
|
||||
int device = -1;
|
||||
Args(int argc, char** argv) {
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
const std::string key = argv[i];
|
||||
if (i + 1 == argc) throw std::runtime_error("Missing value for " + key);
|
||||
const std::string value = argv[++i];
|
||||
if (key == "--hip-root") hip_root = value;
|
||||
else if (key == "--cuda-dll") cuda_dll = value;
|
||||
else if (key == "--module") module = value;
|
||||
else if (key == "--context") context = value;
|
||||
else if (key == "--iterations") {
|
||||
size_t end = 0;
|
||||
iterations = static_cast<unsigned>(std::stoul(value, &end));
|
||||
if (end != value.size() || iterations < 1 || iterations > 10000)
|
||||
throw std::runtime_error("iterations must be 1..10000");
|
||||
} else if (key == "--device") {
|
||||
size_t end = 0;
|
||||
device = std::stoi(value, &end);
|
||||
if (end != value.size() || device < 0) throw std::runtime_error("Invalid device ordinal");
|
||||
} else throw std::runtime_error("Unknown option " + key);
|
||||
}
|
||||
if (hip_root.empty()) throw std::runtime_error("--hip-root is required");
|
||||
}
|
||||
};
|
||||
class Library {
|
||||
HMODULE handle_ = nullptr;
|
||||
public:
|
||||
explicit Library(const std::filesystem::path& path) {
|
||||
if (!path.is_absolute()) throw std::runtime_error("DLL path must be absolute");
|
||||
handle_ = LoadLibraryExW(path.c_str(), nullptr,
|
||||
LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_USER_DIRS);
|
||||
if (!handle_) throw std::runtime_error("LoadLibrary " + utf8(path.c_str()) +
|
||||
" Win32=" + std::to_string(GetLastError()));
|
||||
std::printf("LOAD requested=%s\n", utf8(path.c_str()).c_str());
|
||||
}
|
||||
Library(const Library&) = delete;
|
||||
Library& operator=(const Library&) = delete;
|
||||
~Library() { if (handle_) FreeLibrary(handle_); }
|
||||
template<class T> T symbol(const char* name) const {
|
||||
FARPROC raw = GetProcAddress(handle_, name);
|
||||
if (!raw) throw std::runtime_error(std::string("Missing export ") + name);
|
||||
static_assert(sizeof(T) == sizeof(raw));
|
||||
T out;
|
||||
std::memcpy(&out, &raw, sizeof(out));
|
||||
return out;
|
||||
}
|
||||
};
|
||||
class SearchDirectory {
|
||||
DLL_DIRECTORY_COOKIE cookie_ = nullptr;
|
||||
public:
|
||||
explicit SearchDirectory(const std::filesystem::path& path) {
|
||||
if (!path.is_absolute()) throw std::runtime_error("HIP root must be absolute");
|
||||
cookie_ = AddDllDirectory(path.c_str());
|
||||
if (!cookie_) throw std::runtime_error("AddDllDirectory failed");
|
||||
}
|
||||
~SearchDirectory() { if (cookie_) RemoveDllDirectory(cookie_); }
|
||||
};
|
||||
inline uint32_t pattern(uint32_t i, uint32_t seed) { return (i * 1664525u + seed) ^ 0xa5a55a5au; }
|
||||
inline bool verify(const std::vector<uint32_t>& result, uint32_t count, uint32_t seed) {
|
||||
for (size_t i = 0; i < result.size(); ++i) {
|
||||
const uint32_t expected = i < count ? pattern(static_cast<uint32_t>(i), seed) : 0xdeadbeefu;
|
||||
if (result[i] != expected) {
|
||||
std::fprintf(stderr, "MISMATCH index=%zu expected=0x%08x actual=0x%08x\n", i, expected, result[i]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
#include "diagnostic.h"
|
||||
#include <hip/hip_runtime_api.h>
|
||||
|
||||
namespace d4r::diag {
|
||||
struct HipApi {
|
||||
SearchDirectory search;
|
||||
Library library;
|
||||
#define D4R_HIP_FUNCTION(name) decltype(&::name) name = library.symbol<decltype(&::name)>(#name)
|
||||
D4R_HIP_FUNCTION(hipInit);
|
||||
D4R_HIP_FUNCTION(hipGetDeviceCount);
|
||||
// hipGetDeviceProperties is an SDK macro; the ABI-versioned export is intentional.
|
||||
decltype(&::hipGetDeviceProperties) hipGetDeviceProperties =
|
||||
library.symbol<decltype(&::hipGetDeviceProperties)>("hipGetDevicePropertiesR0600");
|
||||
D4R_HIP_FUNCTION(hipSetDevice);
|
||||
D4R_HIP_FUNCTION(hipRuntimeGetVersion);
|
||||
D4R_HIP_FUNCTION(hipDriverGetVersion);
|
||||
D4R_HIP_FUNCTION(hipGetErrorName);
|
||||
D4R_HIP_FUNCTION(hipGetErrorString);
|
||||
using MallocFn = hipError_t (*)(void**, size_t);
|
||||
MallocFn hipMalloc = library.symbol<MallocFn>("hipMalloc");
|
||||
D4R_HIP_FUNCTION(hipFree);
|
||||
D4R_HIP_FUNCTION(hipMemcpy);
|
||||
D4R_HIP_FUNCTION(hipMemGetInfo);
|
||||
D4R_HIP_FUNCTION(hipModuleLoad);
|
||||
D4R_HIP_FUNCTION(hipModuleUnload);
|
||||
D4R_HIP_FUNCTION(hipModuleGetFunction);
|
||||
D4R_HIP_FUNCTION(hipModuleLaunchKernel);
|
||||
D4R_HIP_FUNCTION(hipDeviceSynchronize);
|
||||
#undef D4R_HIP_FUNCTION
|
||||
explicit HipApi(const std::string& root) :
|
||||
search(std::filesystem::path(wide(root)) / L"bin"),
|
||||
library(std::filesystem::path(wide(root)) / L"bin" / L"amdhip64_7.dll") {}
|
||||
void check(hipError_t result, const char* call) const {
|
||||
std::printf("HIP %s -> %d (%s)\n", call, static_cast<int>(result), hipGetErrorName(result));
|
||||
if (result != hipSuccess) throw std::runtime_error(std::string(call) + ": " + hipGetErrorString(result));
|
||||
}
|
||||
int select_gfx1201(int requested, hipDeviceProp_t& selected) const {
|
||||
// Architecture spoofing invalidates a gfx12 correctness test.
|
||||
for (const char* name : {"HSA_OVERRIDE_GFX_VERSION", "HSA_OVERRIDE_GFX_VERSION_0"})
|
||||
if (std::getenv(name)) throw std::runtime_error(std::string("Unset architecture override ") + name);
|
||||
check(hipInit(0), "hipInit");
|
||||
int version = 0, driver = 0, count = 0;
|
||||
check(hipRuntimeGetVersion(&version), "hipRuntimeGetVersion");
|
||||
check(hipDriverGetVersion(&driver), "hipDriverGetVersion");
|
||||
std::printf("HIP runtime=%d driver_api=%d headers=%d.%d.%d sizeof(properties)=%zu\n",
|
||||
version, driver, HIP_VERSION_MAJOR, HIP_VERSION_MINOR, HIP_VERSION_PATCH, sizeof(hipDeviceProp_t));
|
||||
check(hipGetDeviceCount(&count), "hipGetDeviceCount");
|
||||
int found = -1;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
hipDeviceProp_t props{};
|
||||
check(hipGetDeviceProperties(&props, i), "hipGetDevicePropertiesR0600");
|
||||
std::printf("GPU ordinal=%d name=%s gcnArchName=%s wave=%d vram=%zu pci=%04x:%02x:%02x\n",
|
||||
i, props.name, props.gcnArchName, props.warpSize, props.totalGlobalMem,
|
||||
props.pciDomainID, props.pciBusID, props.pciDeviceID);
|
||||
const std::string arch = props.gcnArchName;
|
||||
if (arch.substr(0, arch.find(':')) == "gfx1201" && (requested < 0 || requested == i) && found < 0) {
|
||||
found = i;
|
||||
selected = props;
|
||||
}
|
||||
}
|
||||
if (found < 0) throw std::runtime_error("No selected gfx1201 device; never substitute gfx110x");
|
||||
check(hipSetDevice(found), "hipSetDevice");
|
||||
std::printf("SELECTED HIP ordinal=%d architecture=gfx1201\n", found);
|
||||
return found;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#include "hip_api.h"
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
using namespace d4r::diag;
|
||||
start();
|
||||
try {
|
||||
Args args(argc, argv);
|
||||
if (args.module.empty()) throw std::runtime_error("--module is required");
|
||||
HipApi api(args.hip_root);
|
||||
hipDeviceProp_t props{};
|
||||
api.select_gfx1201(args.device, props);
|
||||
loaded_modules();
|
||||
size_t free = 0, total = 0;
|
||||
api.check(api.hipMemGetInfo(&free, &total), "hipMemGetInfo");
|
||||
std::printf("VRAM free=%zu total=%zu\n", free, total);
|
||||
hipModule_t module = nullptr;
|
||||
api.check(api.hipModuleLoad(&module, args.module.c_str()), "hipModuleLoad(gfx1201)");
|
||||
struct ModuleCleanup { HipApi& api; hipModule_t m; ~ModuleCleanup() { if (m) (void)api.hipModuleUnload(m); } } cleanup{api, module};
|
||||
hipFunction_t kernel = nullptr;
|
||||
api.check(api.hipModuleGetFunction(&kernel, module, "d4r_probe_pattern"), "hipModuleGetFunction");
|
||||
constexpr uint32_t count = 4099, guard = 61;
|
||||
std::vector<uint32_t> result(count + guard, 0xdeadbeefu);
|
||||
const size_t bytes = result.size() * sizeof(uint32_t);
|
||||
void* allocation = nullptr;
|
||||
api.check(api.hipMalloc(&allocation, bytes), "hipMalloc(VRAM)");
|
||||
struct AllocCleanup { HipApi& api; void* p; ~AllocCleanup() { if (p) (void)api.hipFree(p); } } allocation_cleanup{api, allocation};
|
||||
for (unsigned iteration = 0; iteration < args.iterations; ++iteration) {
|
||||
uint32_t n = count - iteration % 129u, seed = 0x12345678u + iteration * 31u;
|
||||
std::fill(result.begin(), result.end(), 0xdeadbeefu);
|
||||
api.check(api.hipMemcpy(allocation, result.data(), bytes, hipMemcpyHostToDevice), "hipMemcpy(HtoD sentinel)");
|
||||
void* parameters[] = {&allocation, &n, &seed};
|
||||
api.check(api.hipModuleLaunchKernel(kernel, (n + 127u) / 128u, 1, 1, 128, 1, 1,
|
||||
0, nullptr, parameters, nullptr), "hipModuleLaunchKernel");
|
||||
api.check(api.hipDeviceSynchronize(), "hipDeviceSynchronize");
|
||||
api.check(api.hipMemcpy(result.data(), allocation, bytes, hipMemcpyDeviceToHost), "hipMemcpy(DtoH)");
|
||||
if (!verify(result, n, seed)) return 5;
|
||||
}
|
||||
// Explicitly validate teardown too; disable RAII cleanup after success.
|
||||
api.check(api.hipFree(allocation), "hipFree");
|
||||
allocation_cleanup.p = nullptr;
|
||||
api.check(api.hipModuleUnload(module), "hipModuleUnload");
|
||||
cleanup.m = nullptr;
|
||||
std::printf("PASS HIP architecture=gfx1201 iterations=%u guard_verified=1\n", args.iterations);
|
||||
return 0;
|
||||
} catch (const std::exception& e) {
|
||||
std::fprintf(stderr, "FAIL HIP %s\n", e.what());
|
||||
loaded_modules();
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// No host SDK, CRT, or device library dependency. Compiled only for gfx1201.
|
||||
extern "C" __attribute__((global)) void d4r_probe_pattern(
|
||||
unsigned int* output, unsigned int count, unsigned int seed)
|
||||
{
|
||||
const unsigned int i = __builtin_amdgcn_workgroup_id_x() * 128u +
|
||||
__builtin_amdgcn_workitem_id_x();
|
||||
if (i < count)
|
||||
output[i] = (i * 1664525u + seed) ^ 0xa5a55a5au;
|
||||
}
|
||||
Reference in new issue
Block a user