Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions offload/liboffload/API/Device.td
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def ol_device_info_t : Enum {
TaggedEtor<"PLATFORM", "ol_platform_handle_t", "the platform associated with the device">,
TaggedEtor<"NAME", "char[]", "Device name">,
TaggedEtor<"PRODUCT_NAME", "char[]", "Device user-facing marketing name">,
TaggedEtor<"UID", "char[]", "Device UID">,
Copy link
Contributor

Choose a reason for hiding this comment

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

Should probably call this UUID.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

but it's not guaranteed to be a UUID (and doesn't need to be).
I don't think that all vendors can agree on UUIDs for their stuff?

Copy link
Contributor

Choose a reason for hiding this comment

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

It's not a UUID, as it's not necessarily what is understood with it. UUID is a 128-bit identifier, while the UID is vendor-specific string. E.g., ROCm has some thingy with GPU- and a hex number.

Copy link
Contributor Author

@ro-i ro-i Oct 23, 2025

Choose a reason for hiding this comment

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

What's bothering me a little bit, btw, is that my formatting of the Nvidia (U)UID is different from the formatting in nvidia-smi. But I don't know if it's smart to try to copy that format without a clear documentation of it. So ig it's better to just be consistent and to tell people to look up the UIDs with llvm-offload-device-info

TaggedEtor<"VENDOR", "char[]", "Device vendor">,
TaggedEtor<"DRIVER_VERSION", "char[]", "Driver version">,
TaggedEtor<"MAX_WORK_GROUP_SIZE", "uint32_t", "Maximum total work group size in work items">,
Expand Down
7 changes: 5 additions & 2 deletions offload/liboffload/src/OffloadImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,8 @@ llvm::Error ol_platform_impl_t::init() {
if (llvm::Error Err = Plugin->initDevice(Id))
return Err;

auto Device = &Plugin->getDevice(Id);
auto Info = Device->obtainInfoImpl();
GenericDeviceTy *Device = &Plugin->getDevice(Id);
llvm::Expected<InfoTreeNode> Info = Device->obtainInfo();
if (llvm::Error Err = Info.takeError())
return Err;
Devices.emplace_back(std::make_unique<ol_device_impl_t>(Id, Device, *this,
Expand Down Expand Up @@ -467,6 +467,7 @@ Error olGetDeviceInfoImplDetail(ol_device_handle_t Device,
switch (PropName) {
case OL_DEVICE_INFO_NAME:
case OL_DEVICE_INFO_PRODUCT_NAME:
case OL_DEVICE_INFO_UID:
case OL_DEVICE_INFO_VENDOR:
case OL_DEVICE_INFO_DRIVER_VERSION: {
// String values
Expand Down Expand Up @@ -544,6 +545,8 @@ Error olGetDeviceInfoImplDetailHost(ol_device_handle_t Device,
return Info.writeString("Virtual Host Device");
case OL_DEVICE_INFO_PRODUCT_NAME:
return Info.writeString("Virtual Host Device");
case OL_DEVICE_INFO_UID:
return Info.writeString(GenericPluginTy::getHostDeviceUid());
case OL_DEVICE_INFO_VENDOR:
return Info.writeString("Liboffload");
case OL_DEVICE_INFO_DRIVER_VERSION:
Expand Down
1 change: 1 addition & 0 deletions offload/plugins-nextgen/amdgpu/dynamic_hsa/hsa_ext_amd.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ typedef enum hsa_amd_agent_info_s {
HSA_AMD_AGENT_INFO_MAX_WAVES_PER_CU = 0xA00A,
HSA_AMD_AGENT_INFO_NUM_SIMDS_PER_CU = 0xA00B,
HSA_AMD_AGENT_INFO_COOPERATIVE_QUEUES = 0xA010,
HSA_AMD_AGENT_INFO_UUID = 0xA011,
HSA_AMD_AGENT_INFO_TIMESTAMP_FREQUENCY = 0xA016,
} hsa_amd_agent_info_t;

Expand Down
14 changes: 14 additions & 0 deletions offload/plugins-nextgen/amdgpu/src/rtl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2083,6 +2083,20 @@ struct AMDGPUDeviceTy : public GenericDeviceTy, AMDGenericDeviceTy {
return Err;
ComputeUnitKind = GPUName;

// From the ROCm HSA documentation:
// Query the UUID of the agent. The value is an Ascii string with a maximum
// of 21 chars including NUL. The string value consists of two parts: header
// and body. The header identifies the device type (GPU, CPU, DSP) while the
// body encodes the UUID as a 16 digit hex string.
//
// Agents that do not support UUID will return the string "GPU-XX" or
// "CPU-XX" or "DSP-XX" depending on their device type.
char UUID[24] = {0};
if (auto Err = getDeviceAttr(HSA_AMD_AGENT_INFO_UUID, UUID))
return Err;
if (!StringRef(UUID).ends_with("-XX"))
setDeviceUidFromVendorUid(UUID);

// Get the wavefront size.
uint32_t WavefrontSize = 0;
if (auto Err = getDeviceAttr(HSA_AGENT_INFO_WAVEFRONT_SIZE, WavefrontSize))
Expand Down
27 changes: 26 additions & 1 deletion offload/plugins-nextgen/common/include/PluginInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,9 @@ struct GenericDeviceTy : public DeviceAllocatorTy {
/// this id is not unique between different plugins; they may overlap.
int32_t getDeviceId() const { return DeviceId; }

/// Get the unique identifier of the device.
const char *getDeviceUid() const { return DeviceUid.c_str(); }
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 DeviceUid stored as a field in GenericDeviceTy? liboffload already stores the info tree (which contains copies of all the strings) in the device struct.

Copy link
Contributor Author

@ro-i ro-i Oct 21, 2025

Choose a reason for hiding this comment

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

Yes, but the info tree needs to be populated, which I do in obtainInfo() for the UID. And doesn't it make more sense to set the UID on creation of the object and not later while calling some method?


/// Set the context of the device if needed, before calling device-specific
/// functions. Plugins may implement this function as a no-op if not needed.
virtual Error setContext() = 0;
Expand Down Expand Up @@ -989,9 +992,12 @@ struct GenericDeviceTy : public DeviceAllocatorTy {
Error syncEvent(void *EventPtr);
virtual Error syncEventImpl(void *EventPtr) = 0;

/// Obtain information about the device.
Expected<InfoTreeNode> obtainInfo();
virtual Expected<InfoTreeNode> obtainInfoImpl() = 0;

/// Print information about the device.
Error printInfo();
virtual Expected<InfoTreeNode> obtainInfoImpl() = 0;

/// Return true if the device has work that is either queued or currently
/// running
Expand Down Expand Up @@ -1204,6 +1210,14 @@ struct GenericDeviceTy : public DeviceAllocatorTy {
/// global device id and is not the device id visible to the OpenMP user.
const int32_t DeviceId;

/// The unique identifier of the device.
/// Per default, the unique identifier of the device is set to the device id,
/// combined with the plugin name, since the offload device id may overlap
/// between different plugins.
std::string DeviceUid;
/// Construct the device UID from the vendor (U)UID.
void setDeviceUidFromVendorUid(StringRef VendorUid);
Comment on lines +1217 to +1219
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need a global for this? Figured we can just recompute it whenever we call this function, not like it's on a fast path.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hm, but who would own the string pointed to by the const char *?

Copy link
Contributor

Choose a reason for hiding this comment

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

The UUID is supposed to be a constant property of the device. Why do we need to set it and we can just return a std::string.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I just added this set function so that it can be consistently called from the initImpl method of the plugins.

Ok, I can also return std::string and thus remove the attribute

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, I can also return std::string and thus remove the attribute

But then, I have to do the hsa/cuda call every time. That doesn't seem appropriate?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I just added this set function so that it can be consistently called from the initImpl method of the plugins.

(and it's protected)

Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need to call it in the initimpl, do we need to use this before the devices are loaded?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I mean, I get the UUID in the initImpl, because that's where all the other info-retrieving calls to the vendor libs are.
I could also store that UUID as an attribute and construct the DeviceUid in getDeviceUid(). Would you feel better about that version compared to directly computing DeviceUid and just returning it in getDeviceUid() (aka the current state)?


/// The default grid values used for this device.
llvm::omp::GV GridValues;

Expand Down Expand Up @@ -1280,6 +1294,9 @@ struct GenericPluginTy {

return *Devices[DeviceId];
}
const GenericDeviceTy &getDevice(int32_t DeviceId) const {
return const_cast<GenericPluginTy *>(this)->getDevice(DeviceId);
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd prefer to repeat the implementation as above (which are three lines) than using the const cast.

}

/// Get the number of active devices.
int32_t getNumDevices() const { return NumDevices; }
Expand All @@ -1290,6 +1307,14 @@ struct GenericPluginTy {
return UserDeviceIds.at(DeviceId);
}

/// Get the UID for the given device.
const char *getDeviceUid(int32_t DeviceId) const {
return getDevice(DeviceId).getDeviceUid();
}

/// Get the UID for the host device.
static constexpr const char *getHostDeviceUid() { return "HOST"; }

/// Get the ELF code to recognize the binary image of this plugin.
virtual uint16_t getMagicElfBits() const = 0;

Expand Down
20 changes: 17 additions & 3 deletions offload/plugins-nextgen/common/src/PluginInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,9 @@ GenericDeviceTy::GenericDeviceTy(GenericPluginTy &Plugin, int32_t DeviceId,
DeviceId(DeviceId), GridValues(OMPGridValues),
PeerAccesses(NumDevices, PeerAccessState::PENDING), PeerAccessesLock(),
PinnedAllocs(*this), RPCServer(nullptr) {
DeviceUid = std::string(Plugin.getName()) + "-" +
Copy link
Contributor

Choose a reason for hiding this comment

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

This does not seem like a UUID, what does OpenMP say about this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That it's not a uuid as in "universal" uid (that's why I only call it "uid", too). Specifically, OpenMP 6.0, Section 9.1 says:

The uid trait specifies a unique identifier string of the device, for which the accepted values are implementation defined.

So it's only an identifier unique to a device on a given system.

Copy link
Contributor

Choose a reason for hiding this comment

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

Well we should probably use UUIDs anyway if possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I do. This is just the default implementation. It is overwritten in the amdgpu rtl, for example.
(And in the cuda rtl, once I add cuDeviceGetUuid() as you suggested below. Just needed to check for a test system.)

std::to_string(static_cast<uint64_t>(DeviceId));

#ifdef OMPT_SUPPORT
OmptInitialized.store(false);
// Bind the callbacks to this device's member functions
Expand Down Expand Up @@ -1524,15 +1527,22 @@ Error GenericDeviceTy::enqueueHostCall(void (*Callback)(void *), void *UserData,
return Err;
}

Expected<InfoTreeNode> GenericDeviceTy::obtainInfo() {
auto InfoOrErr = obtainInfoImpl();
if (InfoOrErr)
InfoOrErr->add("UID", getDeviceUid(), "", DeviceInfo::UID);
return InfoOrErr;
}

Error GenericDeviceTy::printInfo() {
auto Info = obtainInfoImpl();
auto InfoOrErr = obtainInfo();

// Get the vendor-specific info entries describing the device properties.
if (auto Err = Info.takeError())
if (auto Err = InfoOrErr.takeError())
return Err;

// Print all info entries.
Info->print();
InfoOrErr->print();

return Plugin::success();
}
Expand Down Expand Up @@ -1603,6 +1613,10 @@ Expected<bool> GenericDeviceTy::isAccessiblePtr(const void *Ptr, size_t Size) {
return isAccessiblePtrImpl(Ptr, Size);
}

void GenericDeviceTy::setDeviceUidFromVendorUid(StringRef VendorUid) {
DeviceUid = std::string(Plugin.getName()) + "-" + std::string(VendorUid);
}

Error GenericPluginTy::init() {
if (Initialized)
return Plugin::success();
Expand Down
1 change: 1 addition & 0 deletions offload/plugins-nextgen/cuda/dynamic_cuda/cuda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ DLWRAP(cuFuncSetAttribute, 3)

// Device info
DLWRAP(cuDeviceGetName, 3)
DLWRAP(cuDeviceGetUuid, 2)
DLWRAP(cuDeviceTotalMem, 2)
DLWRAP(cuDriverGetVersion, 1)

Expand Down
4 changes: 4 additions & 0 deletions offload/plugins-nextgen/cuda/dynamic_cuda/cuda.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ typedef struct CUfunc_st *CUfunction;
typedef void (*CUhostFn)(void *userData);
typedef struct CUstream_st *CUstream;
typedef struct CUevent_st *CUevent;
typedef struct CUuuid_st {
char bytes[16];
} CUuuid;

#define CU_DEVICE_INVALID ((CUdevice)(-2))

Expand Down Expand Up @@ -301,6 +304,7 @@ CUresult cuFuncSetAttribute(CUfunction, CUfunction_attribute, int);

// Device info
CUresult cuDeviceGetName(char *, int, CUdevice);
CUresult cuDeviceGetUuid(CUuuid *, CUdevice);
CUresult cuDeviceTotalMem(size_t *, CUdevice);
CUresult cuDriverGetVersion(int *);

Expand Down
7 changes: 7 additions & 0 deletions offload/plugins-nextgen/cuda/src/rtl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "PluginInterface.h"
#include "Utils/ELF.h"

#include "llvm/ADT/StringExtras.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Frontend/OpenMP/OMPConstants.h"
#include "llvm/Frontend/OpenMP/OMPGridValues.h"
Expand Down Expand Up @@ -293,6 +294,12 @@ struct CUDADeviceTy : public GenericDeviceTy {
if (auto Err = Plugin::check(Res, "error in cuDeviceGet: %s"))
return Err;

CUuuid UUID = {0};
Res = cuDeviceGetUuid(&UUID, Device);
if (auto Err = Plugin::check(Res, "error in cuDeviceGetUuid: %s"))
return Err;
setDeviceUidFromVendorUid(toHex(UUID.bytes, true));

// Query the current flags of the primary context and set its flags if
// it is inactive.
unsigned int FormerPrimaryCtxFlags = 0;
Expand Down
1 change: 1 addition & 0 deletions offload/tools/deviceinfo/llvm-offload-device-info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ ol_result_t printDevice(std::ostream &S, ol_device_handle_t D) {
printDeviceValue<const char *>(S, D, OL_DEVICE_INFO_NAME, "Name"));
OFFLOAD_ERR(printDeviceValue<const char *>(S, D, OL_DEVICE_INFO_PRODUCT_NAME,
"Product Name"));
OFFLOAD_ERR(printDeviceValue<const char *>(S, D, OL_DEVICE_INFO_UID, "UID"));
OFFLOAD_ERR(
printDeviceValue<ol_device_type_t>(S, D, OL_DEVICE_INFO_TYPE, "Type"));
OFFLOAD_ERR(printDeviceValue<const char *>(
Expand Down
20 changes: 20 additions & 0 deletions offload/unittests/OffloadAPI/device/olGetDeviceInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,16 @@ TEST_P(olGetDeviceInfoTest, SuccessProductName) {
ASSERT_EQ(std::strlen(Name.data()), Size - 1);
}

TEST_P(olGetDeviceInfoTest, SuccessUID) {
size_t Size = 0;
ASSERT_SUCCESS(olGetDeviceInfoSize(Device, OL_DEVICE_INFO_UID, &Size));
ASSERT_GT(Size, 0ul);
std::vector<char> UID;
UID.resize(Size);
ASSERT_SUCCESS(olGetDeviceInfo(Device, OL_DEVICE_INFO_UID, Size, UID.data()));
ASSERT_EQ(std::strlen(UID.data()), Size - 1);
}

TEST_P(olGetDeviceInfoTest, HostProductName) {
size_t Size = 0;
ASSERT_SUCCESS(olGetDeviceInfoSize(Host, OL_DEVICE_INFO_PRODUCT_NAME, &Size));
Expand All @@ -109,6 +119,16 @@ TEST_P(olGetDeviceInfoTest, HostProductName) {
ASSERT_EQ(std::strlen(Name.data()), Size - 1);
}

TEST_P(olGetDeviceInfoTest, HostUID) {
size_t Size = 0;
ASSERT_SUCCESS(olGetDeviceInfoSize(Host, OL_DEVICE_INFO_UID, &Size));
ASSERT_GT(Size, 0ul);
std::vector<char> UID;
UID.resize(Size);
ASSERT_SUCCESS(olGetDeviceInfo(Host, OL_DEVICE_INFO_UID, Size, UID.data()));
ASSERT_EQ(std::strlen(UID.data()), Size - 1);
}

TEST_P(olGetDeviceInfoTest, SuccessVendor) {
size_t Size = 0;
ASSERT_SUCCESS(olGetDeviceInfoSize(Device, OL_DEVICE_INFO_VENDOR, &Size));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ OL_DEVICE_INFO_SIZE_TEST_EQ(Platform, ol_platform_handle_t,
OL_DEVICE_INFO_PLATFORM);
OL_DEVICE_INFO_SIZE_TEST_NONZERO(Name, OL_DEVICE_INFO_NAME);
OL_DEVICE_INFO_SIZE_TEST_NONZERO(ProductName, OL_DEVICE_INFO_PRODUCT_NAME);
OL_DEVICE_INFO_SIZE_TEST_NONZERO(UID, OL_DEVICE_INFO_UID);
OL_DEVICE_INFO_SIZE_TEST_NONZERO(Vendor, OL_DEVICE_INFO_VENDOR);
OL_DEVICE_INFO_SIZE_TEST_NONZERO(DriverVersion, OL_DEVICE_INFO_DRIVER_VERSION);
OL_DEVICE_INFO_SIZE_TEST_EQ(MaxWorkGroupSize, uint32_t,
Expand Down
Loading