Skip to content

Commit ac029e0

Browse files
authored
[Libomptarget] Remove __tgt_image_info and use the ELF directly (#75720)
Summary: This patch reorganizes a lot of the code used to check for compatibility with the current environment. The main bulk of this patch involves moving from using a separate `__tgt_image_info` struct (which just contains a string for the architecture) to instead simply checking this information from the ELF directly. Checking information in the ELF is very inexpensive as creating an ELF file is simply writing a base pointer. The main desire to do this was to reorganize everything into the ELF image. We can then do the majority of these checks without first initializing the plugin. A future patch will move the first ELF checks to happen without initializing the plugin so we no longer need to initialize and plugins that don't have needed images. This patch also adds a lot more sanity checks for whether or not the ELF is actually compatible. Such as if the images have a valid ABI, 64-bit width, executable, etc.
1 parent deab58d commit ac029e0

File tree

15 files changed

+166
-207
lines changed

15 files changed

+166
-207
lines changed

openmp/libomptarget/include/DeviceImage.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,13 @@ class DeviceImageTy {
3030

3131
__tgt_bin_desc *BinaryDesc;
3232
__tgt_device_image Image;
33-
__tgt_image_info ImageInfo;
3433

3534
public:
3635
DeviceImageTy(__tgt_bin_desc &BinaryDesc, __tgt_device_image &Image);
3736

3837
__tgt_device_image &getExecutableImage() { return Image; }
39-
__tgt_image_info &getImageInfo() { return ImageInfo; }
4038
__tgt_bin_desc &getBinaryDesc() { return *BinaryDesc; }
4139

42-
llvm::StringRef
43-
getArch(llvm::StringRef DefaultArch = llvm::StringRef()) const {
44-
return ImageInfo.Arch ? ImageInfo.Arch : DefaultArch;
45-
}
46-
4740
auto entries() { return llvm::make_pointee_range(OffloadEntries); }
4841
};
4942

openmp/libomptarget/include/Shared/APITypes.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,6 @@ struct __tgt_device_info {
4646
void *Device = nullptr;
4747
};
4848

49-
/// This struct contains information about a given image.
50-
struct __tgt_image_info {
51-
const char *Arch;
52-
};
53-
5449
/// This struct is a record of all the host code that may be offloaded to a
5550
/// target.
5651
struct __tgt_bin_desc {

openmp/libomptarget/include/Shared/PluginAPI.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,6 @@ int32_t __tgt_rtl_number_of_devices(void);
3535
// having to load the library, which can be expensive.
3636
int32_t __tgt_rtl_is_valid_binary(__tgt_device_image *Image);
3737

38-
// This provides the same functionality as __tgt_rtl_is_valid_binary except we
39-
// also use additional information to determine if the image is valid. This
40-
// allows us to determine if an image has a compatible architecture.
41-
int32_t __tgt_rtl_is_valid_binary_info(__tgt_device_image *Image,
42-
__tgt_image_info *Info);
43-
4438
// Return an integer other than zero if the data can be exchaned from SrcDevId
4539
// to DstDevId. If it is data exchangable, the device plugin should provide
4640
// function to move data from source device to destination device directly.

openmp/libomptarget/include/Shared/PluginAPI.inc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
PLUGIN_API_HANDLE(init_plugin, true);
1717
PLUGIN_API_HANDLE(is_valid_binary, true);
18-
PLUGIN_API_HANDLE(is_valid_binary_info, false);
1918
PLUGIN_API_HANDLE(is_data_exchangable, false);
2019
PLUGIN_API_HANDLE(number_of_devices, true);
2120
PLUGIN_API_HANDLE(init_device, true);

openmp/libomptarget/plugins-nextgen/amdgpu/src/rtl.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "Shared/Debug.h"
2424
#include "Shared/Environment.h"
2525
#include "Shared/Utils.h"
26+
#include "Utils/ELF.h"
2627

2728
#include "GlobalHandler.h"
2829
#include "OpenMP/OMPT/Callback.h"
@@ -3015,7 +3016,15 @@ struct AMDGPUPluginTy final : public GenericPluginTy {
30153016
uint16_t getMagicElfBits() const override { return ELF::EM_AMDGPU; }
30163017

30173018
/// Check whether the image is compatible with an AMDGPU device.
3018-
Expected<bool> isImageCompatible(__tgt_image_info *Info) const override {
3019+
Expected<bool> isELFCompatible(StringRef Image) const override {
3020+
// Get the associated architecture and flags from the ELF.
3021+
auto ElfOrErr =
3022+
ELF64LEObjectFile::create(MemoryBufferRef(Image, /*Identifier=*/""),
3023+
/*InitContent=*/false);
3024+
if (!ElfOrErr)
3025+
return ElfOrErr.takeError();
3026+
std::optional<StringRef> Processor = ElfOrErr->tryGetCPUName();
3027+
30193028
for (hsa_agent_t Agent : KernelAgents) {
30203029
std::string Target;
30213030
auto Err = utils::iterateAgentISAs(Agent, [&](hsa_isa_t ISA) {
@@ -3038,7 +3047,9 @@ struct AMDGPUPluginTy final : public GenericPluginTy {
30383047
if (Err)
30393048
return std::move(Err);
30403049

3041-
if (!utils::isImageCompatibleWithEnv(Info, Target))
3050+
if (!utils::isImageCompatibleWithEnv(Processor ? *Processor : "",
3051+
ElfOrErr->getPlatformFlags(),
3052+
Target))
30423053
return false;
30433054
}
30443055
return true;

openmp/libomptarget/plugins-nextgen/amdgpu/utils/UtilitiesRTL.h

Lines changed: 46 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <cstdint>
1414

1515
#include "Shared/Debug.h"
16+
#include "Utils/ELF.h"
1617

1718
#include "omptarget.h"
1819

@@ -58,92 +59,58 @@ uint32_t getImplicitArgsSize(uint16_t Version) {
5859
: sizeof(AMDGPUImplicitArgsTy);
5960
}
6061

61-
/// Parse a TargetID to get processor arch and feature map.
62-
/// Returns processor subarch.
63-
/// Returns TargetID features in \p FeatureMap argument.
64-
/// If the \p TargetID contains feature+, FeatureMap it to true.
65-
/// If the \p TargetID contains feature-, FeatureMap it to false.
66-
/// If the \p TargetID does not contain a feature (default), do not map it.
67-
StringRef parseTargetID(StringRef TargetID, StringMap<bool> &FeatureMap) {
68-
if (TargetID.empty())
69-
return llvm::StringRef();
70-
71-
auto ArchFeature = TargetID.split(":");
72-
auto Arch = ArchFeature.first;
73-
auto Features = ArchFeature.second;
74-
if (Features.empty())
75-
return Arch;
76-
77-
if (Features.contains("sramecc+")) {
78-
FeatureMap.insert(std::pair<StringRef, bool>("sramecc", true));
79-
} else if (Features.contains("sramecc-")) {
80-
FeatureMap.insert(std::pair<StringRef, bool>("sramecc", false));
81-
}
82-
if (Features.contains("xnack+")) {
83-
FeatureMap.insert(std::pair<StringRef, bool>("xnack", true));
84-
} else if (Features.contains("xnack-")) {
85-
FeatureMap.insert(std::pair<StringRef, bool>("xnack", false));
86-
}
87-
88-
return Arch;
89-
}
90-
91-
/// Check if an image is compatible with current system's environment.
92-
bool isImageCompatibleWithEnv(const __tgt_image_info *Info,
93-
StringRef EnvTargetID) {
94-
llvm::StringRef ImageTargetID(Info->Arch);
95-
96-
// Compatible in case of exact match.
97-
if (ImageTargetID == EnvTargetID) {
98-
DP("Compatible: Exact match \t[Image: %s]\t:\t[Env: %s]\n",
99-
ImageTargetID.data(), EnvTargetID.data());
100-
return true;
101-
}
102-
103-
// Incompatible if Archs mismatch.
104-
StringMap<bool> ImgMap, EnvMap;
105-
StringRef ImgArch = utils::parseTargetID(ImageTargetID, ImgMap);
106-
StringRef EnvArch = utils::parseTargetID(EnvTargetID, EnvMap);
107-
108-
// Both EnvArch and ImgArch can't be empty here.
109-
if (EnvArch.empty() || ImgArch.empty() || !ImgArch.contains(EnvArch)) {
110-
DP("Incompatible: Processor mismatch \t[Image: %s]\t:\t[Env: %s]\n",
111-
ImageTargetID.data(), EnvTargetID.data());
62+
/// Check if an image is compatible with current system's environment. The
63+
/// system environment is given as a 'target-id' which has the form:
64+
///
65+
/// <target-id> := <processor> ( ":" <target-feature> ( "+" | "-" ) )*
66+
///
67+
/// If a feature is not specific as '+' or '-' it is assumed to be in an 'any'
68+
/// and is compatible with either '+' or '-'. The HSA runtime returns this
69+
/// information using the target-id, while we use the ELF header to determine
70+
/// these features.
71+
inline bool isImageCompatibleWithEnv(StringRef ImageArch, uint32_t ImageFlags,
72+
StringRef EnvTargetID) {
73+
StringRef EnvArch = EnvTargetID.split(":").first;
74+
75+
// Trivial check if the base processors match.
76+
if (EnvArch != ImageArch)
11277
return false;
113-
}
11478

115-
// Incompatible if image has more features than the environment,
116-
// irrespective of type or sign of features.
117-
if (ImgMap.size() > EnvMap.size()) {
118-
DP("Incompatible: Image has more features than the Environment \t[Image: "
119-
"%s]\t:\t[Env: %s]\n",
120-
ImageTargetID.data(), EnvTargetID.data());
121-
return false;
79+
// Check if the image is requesting xnack on or off.
80+
switch (ImageFlags & EF_AMDGPU_FEATURE_XNACK_V4) {
81+
case EF_AMDGPU_FEATURE_XNACK_OFF_V4:
82+
// The image is 'xnack-' so the environment must be 'xnack-'.
83+
if (!EnvTargetID.contains("xnack-"))
84+
return false;
85+
break;
86+
case EF_AMDGPU_FEATURE_XNACK_ON_V4:
87+
// The image is 'xnack+' so the environment must be 'xnack+'.
88+
if (!EnvTargetID.contains("xnack+"))
89+
return false;
90+
break;
91+
case EF_AMDGPU_FEATURE_XNACK_UNSUPPORTED_V4:
92+
case EF_AMDGPU_FEATURE_XNACK_ANY_V4:
93+
default:
94+
break;
12295
}
12396

124-
// Compatible if each target feature specified by the environment is
125-
// compatible with target feature of the image. The target feature is
126-
// compatible if the iamge does not specify it (meaning Any), or if it
127-
// specifies it with the same value (meaning On or Off).
128-
for (const auto &ImgFeature : ImgMap) {
129-
auto EnvFeature = EnvMap.find(ImgFeature.first());
130-
if (EnvFeature == EnvMap.end() ||
131-
(EnvFeature->first() == ImgFeature.first() &&
132-
EnvFeature->second != ImgFeature.second)) {
133-
DP("Incompatible: Value of Image's non-ANY feature is not matching with "
134-
"the Environment's non-ANY feature \t[Image: %s]\t:\t[Env: %s]\n",
135-
ImageTargetID.data(), EnvTargetID.data());
97+
// Check if the image is requesting sramecc on or off.
98+
switch (ImageFlags & EF_AMDGPU_FEATURE_SRAMECC_V4) {
99+
case EF_AMDGPU_FEATURE_SRAMECC_OFF_V4:
100+
// The image is 'sramecc-' so the environment must be 'sramecc-'.
101+
if (!EnvTargetID.contains("sramecc-"))
136102
return false;
137-
}
103+
break;
104+
case EF_AMDGPU_FEATURE_SRAMECC_ON_V4:
105+
// The image is 'sramecc+' so the environment must be 'sramecc+'.
106+
if (!EnvTargetID.contains("sramecc+"))
107+
return false;
108+
break;
109+
case EF_AMDGPU_FEATURE_SRAMECC_UNSUPPORTED_V4:
110+
case EF_AMDGPU_FEATURE_SRAMECC_ANY_V4:
111+
break;
138112
}
139113

140-
// Image is compatible if all features of Environment are:
141-
// - either, present in the Image's features map with the same sign,
142-
// - or, the feature is missing from Image's features map i.e. it is
143-
// set to ANY
144-
DP("Compatible: Target IDs are compatible \t[Image: %s]\t:\t[Env: %s]\n",
145-
ImageTargetID.data(), EnvTargetID.data());
146-
147114
return true;
148115
}
149116

openmp/libomptarget/plugins-nextgen/common/include/PluginInterface.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1065,10 +1065,14 @@ struct GenericPluginTy {
10651065
return isValidDeviceId(SrcDeviceId) && isValidDeviceId(DstDeviceId);
10661066
}
10671067

1068+
/// Top level interface to verify if a given ELF image can be executed on a
1069+
/// given target. Returns true if the \p Image is compatible with the plugin.
1070+
Expected<bool> checkELFImage(__tgt_device_image &Image) const;
1071+
10681072
/// Indicate if an image is compatible with the plugin devices. Notice that
10691073
/// this function may be called before actually initializing the devices. So
10701074
/// we could not move this function into GenericDeviceTy.
1071-
virtual Expected<bool> isImageCompatible(__tgt_image_info *Info) const = 0;
1075+
virtual Expected<bool> isELFCompatible(StringRef Image) const = 0;
10721076

10731077
/// Indicate whether the plugin supports empty images.
10741078
virtual bool supportsEmptyImages() const { return false; }

openmp/libomptarget/plugins-nextgen/common/src/Utils/ELF.h renamed to openmp/libomptarget/plugins-nextgen/common/include/Utils/ELF.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@
2121
namespace utils {
2222
namespace elf {
2323

24-
/// Return non-zero, if the given \p image is an ELF object, which
25-
/// e_machine matches \p target_id; return zero otherwise.
26-
int32_t checkMachine(__tgt_device_image *Image, uint16_t TargetId);
24+
/// Returns true or false if the \p Buffer is an ELF file.
25+
bool isELF(llvm::StringRef Buffer);
26+
27+
/// Checks if the given \p Object is a valid ELF matching the e_machine value.
28+
llvm::Expected<bool> checkMachine(llvm::StringRef Object, uint16_t EMachine);
2729

2830
/// Returns a pointer to the given \p Symbol inside of an ELF object.
2931
llvm::Expected<const void *> getSymbolAddress(

openmp/libomptarget/plugins-nextgen/common/src/PluginInterface.cpp

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1632,6 +1632,26 @@ Error GenericPluginTy::deinitDevice(int32_t DeviceId) {
16321632
return Plugin::success();
16331633
}
16341634

1635+
Expected<bool> GenericPluginTy::checkELFImage(__tgt_device_image &Image) const {
1636+
StringRef Buffer(reinterpret_cast<const char *>(Image.ImageStart),
1637+
target::getPtrDiff(Image.ImageEnd, Image.ImageStart));
1638+
1639+
// First check if this image is a regular ELF file.
1640+
if (!utils::elf::isELF(Buffer))
1641+
return false;
1642+
1643+
// Check if this image is an ELF with a matching machine value.
1644+
auto MachineOrErr = utils::elf::checkMachine(Buffer, getMagicElfBits());
1645+
if (!MachineOrErr)
1646+
return MachineOrErr.takeError();
1647+
1648+
if (!*MachineOrErr)
1649+
return false;
1650+
1651+
// Perform plugin-dependent checks for the specific architecture if needed.
1652+
return isELFCompatible(Buffer);
1653+
}
1654+
16351655
const bool llvm::omp::target::plugin::libomptargetSupportsRPC() {
16361656
#ifdef LIBOMPTARGET_RPC_SUPPORT
16371657
return true;
@@ -1659,44 +1679,26 @@ int32_t __tgt_rtl_init_plugin() {
16591679
}
16601680

16611681
int32_t __tgt_rtl_is_valid_binary(__tgt_device_image *TgtImage) {
1682+
// TODO: We should be able to perform a trivial ELF machine check without
1683+
// initializing the plugin first to save time if the plugin is not needed.
16621684
if (!Plugin::isActive())
16631685
return false;
16641686

1665-
if (utils::elf::checkMachine(TgtImage, Plugin::get().getMagicElfBits()))
1666-
return true;
1667-
1668-
return Plugin::get().getJIT().checkBitcodeImage(*TgtImage);
1669-
}
1670-
1671-
int32_t __tgt_rtl_is_valid_binary_info(__tgt_device_image *TgtImage,
1672-
__tgt_image_info *Info) {
1673-
if (!Plugin::isActive())
1674-
return false;
1675-
1676-
if (!__tgt_rtl_is_valid_binary(TgtImage))
1687+
// Check if this is a valid ELF with a matching machine and processor.
1688+
auto MatchOrErr = Plugin::get().checkELFImage(*TgtImage);
1689+
if (Error Err = MatchOrErr.takeError()) {
1690+
[[maybe_unused]] std::string ErrStr = toString(std::move(Err));
1691+
DP("Failure to check validity of image %p: %s", TgtImage, ErrStr.c_str());
16771692
return false;
1678-
1679-
// A subarchitecture was not specified. Assume it is compatible.
1680-
if (!Info->Arch)
1693+
} else if (*MatchOrErr) {
16811694
return true;
1682-
1683-
// Check the compatibility with all the available devices. Notice the
1684-
// devices may not be initialized yet.
1685-
auto CompatibleOrErr = Plugin::get().isImageCompatible(Info);
1686-
if (!CompatibleOrErr) {
1687-
// This error should not abort the execution, so we just inform the user
1688-
// through the debug system.
1689-
std::string ErrString = toString(CompatibleOrErr.takeError());
1690-
DP("Failure to check whether image %p is valid: %s\n", TgtImage,
1691-
ErrString.data());
1692-
return false;
16931695
}
16941696

1695-
bool Compatible = *CompatibleOrErr;
1696-
DP("Image is %scompatible with current environment: %s\n",
1697-
(Compatible) ? "" : "not", Info->Arch);
1697+
// Check if this is a valid LLVM-IR file with matching triple.
1698+
if (Plugin::get().getJIT().checkBitcodeImage(*TgtImage))
1699+
return true;
16981700

1699-
return Compatible;
1701+
return false;
17001702
}
17011703

17021704
int32_t __tgt_rtl_supports_empty_images() {

0 commit comments

Comments
 (0)