Skip to content

Commit 661d91a

Browse files
committed
[clang] Make amdgpu-arch tool work on Windows
Currently amdgpu-arch tool detects AMD GPU by dynamically loading HSA runtime shared library and using HSA API's, which is not available on Windows. This patch makes it work on Windows by dynamically loading HIP runtime dll and using HIP API's. Reviewed by: Matt Arsenault, Joseph Huber, Johannes Doerfert Differential Revision: https://reviews.llvm.org/D153725
1 parent 1e02158 commit 661d91a

File tree

4 files changed

+228
-102
lines changed

4 files changed

+228
-102
lines changed

clang/tools/amdgpu-arch/AMDGPUArch.cpp

Lines changed: 9 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,13 @@
66
//
77
//===----------------------------------------------------------------------===//
88
//
9-
// This file implements a tool for detecting name of AMDGPU installed in system
10-
// using HSA. This tool is used by AMDGPU OpenMP driver.
9+
// This file implements a tool for detecting name of AMDGPU installed in system.
10+
// This tool is used by AMDGPU OpenMP and HIP driver.
1111
//
1212
//===----------------------------------------------------------------------===//
1313

1414
#include "clang/Basic/Version.h"
1515
#include "llvm/Support/CommandLine.h"
16-
#include "llvm/Support/DynamicLibrary.h"
17-
#include "llvm/Support/Error.h"
18-
#include <memory>
19-
#include <string>
20-
#include <vector>
2116

2217
using namespace llvm;
2318

@@ -30,76 +25,8 @@ static void PrintVersion(raw_ostream &OS) {
3025
OS << clang::getClangToolFullVersion("amdgpu-arch") << '\n';
3126
}
3227

33-
typedef enum {
34-
HSA_STATUS_SUCCESS = 0x0,
35-
} hsa_status_t;
36-
37-
typedef enum {
38-
HSA_DEVICE_TYPE_CPU = 0,
39-
HSA_DEVICE_TYPE_GPU = 1,
40-
} hsa_device_type_t;
41-
42-
typedef enum {
43-
HSA_AGENT_INFO_NAME = 0,
44-
HSA_AGENT_INFO_DEVICE = 17,
45-
} hsa_agent_info_t;
46-
47-
typedef struct hsa_agent_s {
48-
uint64_t handle;
49-
} hsa_agent_t;
50-
51-
hsa_status_t (*hsa_init)();
52-
hsa_status_t (*hsa_shut_down)();
53-
hsa_status_t (*hsa_agent_get_info)(hsa_agent_t, hsa_agent_info_t, void *);
54-
hsa_status_t (*hsa_iterate_agents)(hsa_status_t (*)(hsa_agent_t, void *),
55-
void *);
56-
57-
constexpr const char *DynamicHSAPath = "libhsa-runtime64.so";
58-
59-
llvm::Error loadHSA() {
60-
std::string ErrMsg;
61-
auto DynlibHandle = std::make_unique<llvm::sys::DynamicLibrary>(
62-
llvm::sys::DynamicLibrary::getPermanentLibrary(DynamicHSAPath, &ErrMsg));
63-
if (!DynlibHandle->isValid()) {
64-
return llvm::createStringError(llvm::inconvertibleErrorCode(),
65-
"Failed to 'dlopen' %s", DynamicHSAPath);
66-
}
67-
#define DYNAMIC_INIT(SYMBOL) \
68-
{ \
69-
void *SymbolPtr = DynlibHandle->getAddressOfSymbol(#SYMBOL); \
70-
if (!SymbolPtr) \
71-
return llvm::createStringError(llvm::inconvertibleErrorCode(), \
72-
"Failed to 'dlsym' " #SYMBOL); \
73-
SYMBOL = reinterpret_cast<decltype(SYMBOL)>(SymbolPtr); \
74-
}
75-
DYNAMIC_INIT(hsa_init);
76-
DYNAMIC_INIT(hsa_shut_down);
77-
DYNAMIC_INIT(hsa_agent_get_info);
78-
DYNAMIC_INIT(hsa_iterate_agents);
79-
#undef DYNAMIC_INIT
80-
return llvm::Error::success();
81-
}
82-
83-
static hsa_status_t iterateAgentsCallback(hsa_agent_t Agent, void *Data) {
84-
hsa_device_type_t DeviceType;
85-
hsa_status_t Status =
86-
hsa_agent_get_info(Agent, HSA_AGENT_INFO_DEVICE, &DeviceType);
87-
88-
// continue only if device type if GPU
89-
if (Status != HSA_STATUS_SUCCESS || DeviceType != HSA_DEVICE_TYPE_GPU) {
90-
return Status;
91-
}
92-
93-
std::vector<std::string> *GPUs =
94-
static_cast<std::vector<std::string> *>(Data);
95-
char GPUName[64];
96-
Status = hsa_agent_get_info(Agent, HSA_AGENT_INFO_NAME, GPUName);
97-
if (Status != HSA_STATUS_SUCCESS) {
98-
return Status;
99-
}
100-
GPUs->push_back(GPUName);
101-
return HSA_STATUS_SUCCESS;
102-
}
28+
int printGPUsByHSA();
29+
int printGPUsByHIP();
10330

10431
int main(int argc, char *argv[]) {
10532
cl::HideUnrelatedOptions(AMDGPUArchCategory);
@@ -117,29 +44,10 @@ int main(int argc, char *argv[]) {
11744
return 0;
11845
}
11946

120-
// Attempt to load the HSA runtime.
121-
if (llvm::Error Err = loadHSA()) {
122-
logAllUnhandledErrors(std::move(Err), llvm::errs());
123-
return 1;
124-
}
125-
126-
hsa_status_t Status = hsa_init();
127-
if (Status != HSA_STATUS_SUCCESS) {
128-
return 1;
129-
}
130-
131-
std::vector<std::string> GPUs;
132-
Status = hsa_iterate_agents(iterateAgentsCallback, &GPUs);
133-
if (Status != HSA_STATUS_SUCCESS) {
134-
return 1;
135-
}
136-
137-
for (const auto &GPU : GPUs)
138-
printf("%s\n", GPU.c_str());
139-
140-
if (GPUs.size() < 1)
141-
return 1;
47+
#ifndef _WIN32
48+
if (!printGPUsByHSA())
49+
return 0;
50+
#endif
14251

143-
hsa_shut_down();
144-
return 0;
52+
return printGPUsByHIP();
14553
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
//===- AMDGPUArch.cpp - list AMDGPU installed ----------*- C++ -*---------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file implements a tool for detecting name of AMDGPU installed in system
10+
// using HIP runtime. This tool is used by AMDGPU OpenMP and HIP driver.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#include "llvm/Support/DynamicLibrary.h"
15+
#include "llvm/Support/Error.h"
16+
#include "llvm/Support/raw_ostream.h"
17+
18+
using namespace llvm;
19+
20+
typedef struct {
21+
char padding[396];
22+
char gcnArchName[256];
23+
char padding2[1024];
24+
} hipDeviceProp_t;
25+
26+
typedef enum {
27+
hipSuccess = 0,
28+
} hipError_t;
29+
30+
typedef hipError_t (*hipGetDeviceCount_t)(int *);
31+
typedef hipError_t (*hipDeviceGet_t)(int *, int);
32+
typedef hipError_t (*hipGetDeviceProperties_t)(hipDeviceProp_t *, int);
33+
34+
int printGPUsByHIP() {
35+
#ifdef _WIN32
36+
constexpr const char *DynamicHIPPath = "amdhip64.dll";
37+
#else
38+
constexpr const char *DynamicHIPPath = "libamdhip64.so";
39+
#endif
40+
41+
std::string ErrMsg;
42+
auto DynlibHandle = std::make_unique<llvm::sys::DynamicLibrary>(
43+
llvm::sys::DynamicLibrary::getPermanentLibrary(DynamicHIPPath, &ErrMsg));
44+
if (!DynlibHandle->isValid()) {
45+
llvm::errs() << "Failed to load " << DynamicHIPPath << ": " << ErrMsg
46+
<< '\n';
47+
return 1;
48+
}
49+
50+
#define DYNAMIC_INIT_HIP(SYMBOL) \
51+
{ \
52+
void *SymbolPtr = DynlibHandle->getAddressOfSymbol(#SYMBOL); \
53+
if (!SymbolPtr) { \
54+
llvm::errs() << "Failed to find symbol " << #SYMBOL << '\n'; \
55+
return 1; \
56+
} \
57+
SYMBOL = reinterpret_cast<decltype(SYMBOL)>(SymbolPtr); \
58+
}
59+
60+
hipGetDeviceCount_t hipGetDeviceCount;
61+
hipDeviceGet_t hipDeviceGet;
62+
hipGetDeviceProperties_t hipGetDeviceProperties;
63+
64+
DYNAMIC_INIT_HIP(hipGetDeviceCount);
65+
DYNAMIC_INIT_HIP(hipDeviceGet);
66+
DYNAMIC_INIT_HIP(hipGetDeviceProperties);
67+
68+
#undef DYNAMIC_INIT_HIP
69+
70+
int deviceCount;
71+
hipError_t err = hipGetDeviceCount(&deviceCount);
72+
if (err != hipSuccess) {
73+
llvm::errs() << "Failed to get device count\n";
74+
return 1;
75+
}
76+
77+
for (int i = 0; i < deviceCount; ++i) {
78+
int deviceId;
79+
err = hipDeviceGet(&deviceId, i);
80+
if (err != hipSuccess) {
81+
llvm::errs() << "Failed to get device id for ordinal " << i << '\n';
82+
return 1;
83+
}
84+
85+
hipDeviceProp_t prop;
86+
err = hipGetDeviceProperties(&prop, deviceId);
87+
if (err != hipSuccess) {
88+
llvm::errs() << "Failed to get device properties for device " << deviceId
89+
<< '\n';
90+
return 1;
91+
}
92+
llvm::outs() << prop.gcnArchName << '\n';
93+
}
94+
95+
return 0;
96+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
//===- AMDGPUArchLinux.cpp - list AMDGPU installed ------*- C++ -*---------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file implements a tool for detecting name of AMDGPU installed in system
10+
// using HSA on Linux. This tool is used by AMDGPU OpenMP and HIP driver.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#include "clang/Basic/Version.h"
15+
#include "llvm/Support/CommandLine.h"
16+
#include "llvm/Support/DynamicLibrary.h"
17+
#include "llvm/Support/Error.h"
18+
#include "llvm/Support/raw_ostream.h"
19+
#include <memory>
20+
#include <string>
21+
#include <vector>
22+
23+
using namespace llvm;
24+
25+
typedef enum {
26+
HSA_STATUS_SUCCESS = 0x0,
27+
} hsa_status_t;
28+
29+
typedef enum {
30+
HSA_DEVICE_TYPE_CPU = 0,
31+
HSA_DEVICE_TYPE_GPU = 1,
32+
} hsa_device_type_t;
33+
34+
typedef enum {
35+
HSA_AGENT_INFO_NAME = 0,
36+
HSA_AGENT_INFO_DEVICE = 17,
37+
} hsa_agent_info_t;
38+
39+
typedef struct hsa_agent_s {
40+
uint64_t handle;
41+
} hsa_agent_t;
42+
43+
hsa_status_t (*hsa_init)();
44+
hsa_status_t (*hsa_shut_down)();
45+
hsa_status_t (*hsa_agent_get_info)(hsa_agent_t, hsa_agent_info_t, void *);
46+
hsa_status_t (*hsa_iterate_agents)(hsa_status_t (*)(hsa_agent_t, void *),
47+
void *);
48+
49+
constexpr const char *DynamicHSAPath = "libhsa-runtime64.so";
50+
51+
llvm::Error loadHSA() {
52+
std::string ErrMsg;
53+
auto DynlibHandle = std::make_unique<llvm::sys::DynamicLibrary>(
54+
llvm::sys::DynamicLibrary::getPermanentLibrary(DynamicHSAPath, &ErrMsg));
55+
if (!DynlibHandle->isValid()) {
56+
return llvm::createStringError(llvm::inconvertibleErrorCode(),
57+
"Failed to 'dlopen' %s", DynamicHSAPath);
58+
}
59+
#define DYNAMIC_INIT(SYMBOL) \
60+
{ \
61+
void *SymbolPtr = DynlibHandle->getAddressOfSymbol(#SYMBOL); \
62+
if (!SymbolPtr) \
63+
return llvm::createStringError(llvm::inconvertibleErrorCode(), \
64+
"Failed to 'dlsym' " #SYMBOL); \
65+
SYMBOL = reinterpret_cast<decltype(SYMBOL)>(SymbolPtr); \
66+
}
67+
DYNAMIC_INIT(hsa_init);
68+
DYNAMIC_INIT(hsa_shut_down);
69+
DYNAMIC_INIT(hsa_agent_get_info);
70+
DYNAMIC_INIT(hsa_iterate_agents);
71+
#undef DYNAMIC_INIT
72+
return llvm::Error::success();
73+
}
74+
75+
static hsa_status_t iterateAgentsCallback(hsa_agent_t Agent, void *Data) {
76+
hsa_device_type_t DeviceType;
77+
hsa_status_t Status =
78+
hsa_agent_get_info(Agent, HSA_AGENT_INFO_DEVICE, &DeviceType);
79+
80+
// continue only if device type if GPU
81+
if (Status != HSA_STATUS_SUCCESS || DeviceType != HSA_DEVICE_TYPE_GPU) {
82+
return Status;
83+
}
84+
85+
std::vector<std::string> *GPUs =
86+
static_cast<std::vector<std::string> *>(Data);
87+
char GPUName[64];
88+
Status = hsa_agent_get_info(Agent, HSA_AGENT_INFO_NAME, GPUName);
89+
if (Status != HSA_STATUS_SUCCESS) {
90+
return Status;
91+
}
92+
GPUs->push_back(GPUName);
93+
return HSA_STATUS_SUCCESS;
94+
}
95+
96+
int printGPUsByHSA() {
97+
// Attempt to load the HSA runtime.
98+
if (llvm::Error Err = loadHSA()) {
99+
logAllUnhandledErrors(std::move(Err), llvm::errs());
100+
return 1;
101+
}
102+
103+
hsa_status_t Status = hsa_init();
104+
if (Status != HSA_STATUS_SUCCESS) {
105+
return 1;
106+
}
107+
108+
std::vector<std::string> GPUs;
109+
Status = hsa_iterate_agents(iterateAgentsCallback, &GPUs);
110+
if (Status != HSA_STATUS_SUCCESS) {
111+
return 1;
112+
}
113+
114+
for (const auto &GPU : GPUs)
115+
llvm::outs() << GPU << '\n';
116+
117+
if (GPUs.size() < 1)
118+
return 1;
119+
120+
hsa_shut_down();
121+
return 0;
122+
}

clang/tools/amdgpu-arch/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88

99
set(LLVM_LINK_COMPONENTS Support)
1010

11-
add_clang_tool(amdgpu-arch AMDGPUArch.cpp)
11+
add_clang_tool(amdgpu-arch AMDGPUArch.cpp AMDGPUArchByHSA.cpp AMDGPUArchByHIP.cpp)
1212

1313
target_link_libraries(amdgpu-arch PRIVATE clangBasic)

0 commit comments

Comments
 (0)