Skip to content

Commit dde4c8b

Browse files
authored
[OFFLOAD][L0] Use device ELF as image for L0ProgramTy (#20712)
Decouple building and linking the device image from creating the L0ProgramTy representing it. Use the GPU ELF image as the internal Image data for L0ProgramTy. This improves compatibility with the main library which expects to be an ELF in several places.
1 parent bc0ccaf commit dde4c8b

File tree

5 files changed

+90
-89
lines changed

5 files changed

+90
-89
lines changed

offload/plugins-nextgen/level_zero/include/L0Device.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -307,9 +307,14 @@ class L0DeviceTy final : public GenericDeviceTy {
307307
}
308308

309309
// add a new program to the device. Return a reference to the new program
310-
L0ProgramTy &addProgram(int32_t ImageId,
311-
std::unique_ptr<MemoryBuffer> &&Image) {
312-
Programs.emplace_back(ImageId, *this, std::move(Image));
310+
Expected<L0ProgramTy &> addProgram(int32_t ImageId,
311+
L0ProgramBuilderTy &Builder) {
312+
auto ImageOrErr = Builder.getELF();
313+
if (!ImageOrErr)
314+
return ImageOrErr.takeError();
315+
Programs.emplace_back(ImageId, *this, std::move(*ImageOrErr),
316+
Builder.getGlobalModule(),
317+
std::move(Builder.getModules()));
313318
return Programs.back();
314319
}
315320

offload/plugins-nextgen/level_zero/include/L0Program.h

Lines changed: 46 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,47 @@ struct ProgramDataTy {
3333
int TeamsThreadLimit = 0;
3434
};
3535

36+
class L0ProgramBuilderTy {
37+
L0DeviceTy &Device;
38+
std::unique_ptr<MemoryBuffer> Image;
39+
/// Handle multiple modules within a single target image
40+
llvm::SmallVector<ze_module_handle_t> Modules;
41+
42+
/// Module that contains global data including device RTL
43+
ze_module_handle_t GlobalModule = nullptr;
44+
45+
/// Requires module link
46+
bool RequiresModuleLink = false;
47+
48+
/// Build a single module with the given image, build option, and format.
49+
Error addModule(const size_t Size, const uint8_t *Image,
50+
const std::string_view BuildOption,
51+
ze_module_format_t Format);
52+
53+
Error linkModules();
54+
55+
public:
56+
L0ProgramBuilderTy(L0DeviceTy &Device, std::unique_ptr<MemoryBuffer> &&Image)
57+
: Device(Device), Image(std::move(Image)) {}
58+
~L0ProgramBuilderTy() = default;
59+
60+
L0DeviceTy &getL0Device() const { return Device; }
61+
ze_module_handle_t getGlobalModule() const { return GlobalModule; }
62+
llvm::SmallVector<ze_module_handle_t> &getModules() { return Modules; }
63+
64+
const void *getStart() const { return Image->getBufferStart(); }
65+
size_t getSize() const { return Image->getBufferSize(); }
66+
67+
MemoryBufferRef getMemoryBuffer() const {
68+
return MemoryBufferRef(StringRef((const char *)getStart(), getSize()),
69+
"Image");
70+
}
71+
Error buildModules(const std::string_view BuildOptions);
72+
73+
/// Retrieve the ELF binary for the program
74+
Expected<std::unique_ptr<MemoryBuffer>> getELF();
75+
};
76+
3677
/// Level Zero program that can contain multiple modules.
3778
class L0ProgramTy : public DeviceImageTy {
3879
/// Handle multiple modules within a single target image
@@ -49,32 +90,17 @@ class L0ProgramTy : public DeviceImageTy {
4990
/// Module that contains global data including device RTL
5091
ze_module_handle_t GlobalModule = nullptr;
5192

52-
/// Requires module link
53-
bool RequiresModuleLink = false;
54-
55-
/// Is this module library
56-
bool IsLibModule = false;
57-
58-
/// Build a single module with the given image, build option, and format.
59-
Error addModule(const size_t Size, const uint8_t *Image,
60-
const std::string_view BuildOption,
61-
ze_module_format_t Format);
62-
/// Read file and return the size of the binary if successful.
63-
size_t readFile(const char *FileName, std::vector<uint8_t> &OutFile) const;
64-
void replaceDriverOptsWithBackendOpts(const L0DeviceTy &Device,
65-
std::string &Options) const;
66-
67-
/// Check if the image should be handled as a library module
68-
void setLibModule();
69-
7093
L0DeviceTy &getL0Device() const;
7194

7295
public:
7396
L0ProgramTy() = delete;
7497

7598
L0ProgramTy(int32_t ImageId, GenericDeviceTy &Device,
76-
std::unique_ptr<MemoryBuffer> Image)
77-
: DeviceImageTy(ImageId, Device, std::move(Image)) {}
99+
std::unique_ptr<MemoryBuffer> Image,
100+
ze_module_handle_t GlobalModule,
101+
llvm::SmallVector<ze_module_handle_t> &&Modules)
102+
: DeviceImageTy(ImageId, Device, std::move(Image)),
103+
Modules(std::move(Modules)), GlobalModule(GlobalModule) {}
78104
~L0ProgramTy() {}
79105

80106
L0ProgramTy(const L0ProgramTy &other) = delete;
@@ -88,12 +114,6 @@ class L0ProgramTy : public DeviceImageTy {
88114
return static_cast<L0ProgramTy &>(Device);
89115
}
90116

91-
/// Build modules from the target image description
92-
Error buildModules(const std::string_view BuildOptions);
93-
94-
/// Link modules stored in \p Modules.
95-
Error linkModules();
96-
97117
/// Loads the kernels names from all modules
98118
Error loadModuleKernels();
99119

offload/plugins-nextgen/level_zero/src/L0Device.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -250,13 +250,15 @@ L0DeviceTy::loadBinaryImpl(std::unique_ptr<MemoryBuffer> &&TgtImage,
250250

251251
CompilationOptions += " ";
252252
CompilationOptions += Options.InternalCompilationOptions;
253-
auto &Program = addProgram(ImageId, std::move(TgtImage));
254253

255-
if (auto Err = Program.buildModules(CompilationOptions))
254+
L0ProgramBuilderTy Builder(*this, std::move(TgtImage));
255+
if (auto Err = Builder.buildModules(CompilationOptions))
256256
return std::move(Err);
257257

258-
if (auto Err = Program.linkModules())
259-
return std::move(Err);
258+
auto ProgramOrErr = addProgram(ImageId, Builder);
259+
if (!ProgramOrErr)
260+
return ProgramOrErr.takeError();
261+
auto &Program = *ProgramOrErr;
260262

261263
if (auto Err = Program.loadModuleKernels())
262264
return std::move(Err);

offload/plugins-nextgen/level_zero/src/L0DynWrapper.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ DLWRAP(zeMemGetAddressRange, 4)
8181
DLWRAP(zeMemGetAllocProperties, 4)
8282
DLWRAP(zeModuleDynamicLink, 3)
8383
DLWRAP(zeModuleGetGlobalPointer, 4)
84+
DLWRAP(zeModuleGetNativeBinary, 3)
8485
DLWRAP(zesDeviceEnumMemoryModules, 3)
8586
DLWRAP(zesMemoryGetState, 2)
8687
DLWRAP(zeCommandListHostSynchronize, 2)

offload/plugins-nextgen/level_zero/src/L0Program.cpp

Lines changed: 29 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -57,45 +57,16 @@ Error L0ProgramTy::deinit() {
5757
return Plugin::success();
5858
}
5959

60-
void L0ProgramTy::setLibModule() {
61-
#if _WIN32
62-
return;
63-
#else
64-
// Check if the image belongs to a dynamic library
65-
Dl_info DLI{nullptr, nullptr, nullptr, nullptr};
66-
if (dladdr(getStart(), &DLI) && DLI.dli_fname) {
67-
std::vector<uint8_t> FileBin;
68-
auto Size = readFile(DLI.dli_fname, FileBin);
69-
if (Size) {
70-
auto MB = MemoryBuffer::getMemBuffer(
71-
StringRef(reinterpret_cast<const char *>(FileBin.data()), Size),
72-
/*BufferName=*/"", /*RequiresNullTerminator=*/false);
73-
auto ELF = ELFObjectFileBase::createELFObjectFile(MB->getMemBufferRef());
74-
if (ELF) {
75-
if (auto *Obj = dyn_cast<ELF64LEObjectFile>((*ELF).get())) {
76-
const auto Header = Obj->getELFFile().getHeader();
77-
if (Header.e_type == ELF::ET_DYN) {
78-
DP("Processing current image as library\n");
79-
IsLibModule = true;
80-
}
81-
}
82-
}
83-
}
84-
}
85-
#endif // _WIN32
86-
}
87-
88-
Error L0ProgramTy::addModule(size_t Size, const uint8_t *Image,
89-
const std::string_view CommonBuildOptions,
90-
ze_module_format_t Format) {
60+
Error L0ProgramBuilderTy::addModule(size_t Size, const uint8_t *Image,
61+
const std::string_view CommonBuildOptions,
62+
ze_module_format_t Format) {
9163
const ze_module_constants_t SpecConstants =
9264
LevelZeroPluginTy::getOptions().CommonSpecConstants.getModuleConstants();
9365
auto &l0Device = getL0Device();
9466
std::string BuildOptions(CommonBuildOptions);
9567

96-
// Add required flag to enable dynamic linking.
97-
if (IsLibModule)
98-
BuildOptions += " -library-compilation ";
68+
bool IsLibModule =
69+
BuildOptions.find("-library-compilation") != std::string::npos;
9970

10071
ze_module_desc_t ModuleDesc{};
10172
ModuleDesc.stype = ZE_STRUCTURE_TYPE_MODULE_DESC;
@@ -128,7 +99,7 @@ Error L0ProgramTy::addModule(size_t Size, const uint8_t *Image,
12899
return Plugin::success();
129100
}
130101

131-
Error L0ProgramTy::linkModules() {
102+
Error L0ProgramBuilderTy::linkModules() {
132103
auto &l0Device = getL0Device();
133104
if (!RequiresModuleLink) {
134105
DP("Module link is not required\n");
@@ -147,24 +118,8 @@ Error L0ProgramTy::linkModules() {
147118
return Plugin::success();
148119
}
149120

150-
size_t L0ProgramTy::readFile(const char *FileName,
151-
std::vector<uint8_t> &OutFile) const {
152-
std::ifstream IFS(FileName, std::ios::binary);
153-
if (!IFS.good())
154-
return 0;
155-
IFS.seekg(0, IFS.end);
156-
auto FileSize = static_cast<size_t>(IFS.tellg());
157-
OutFile.resize(FileSize);
158-
IFS.seekg(0);
159-
if (!IFS.read(reinterpret_cast<char *>(OutFile.data()), FileSize)) {
160-
OutFile.clear();
161-
return 0;
162-
}
163-
return FileSize;
164-
}
165-
166-
void L0ProgramTy::replaceDriverOptsWithBackendOpts(const L0DeviceTy &Device,
167-
std::string &Options) const {
121+
static void replaceDriverOptsWithBackendOpts(const L0DeviceTy &Device,
122+
std::string &Options) {
168123
// Options that need to be replaced with backend-specific options
169124
static const struct {
170125
std::string Option;
@@ -256,7 +211,7 @@ bool isValidOneOmpImage(StringRef Image, uint64_t &MajorVer,
256211
return Res;
257212
}
258213

259-
Error L0ProgramTy::buildModules(const std::string_view BuildOptions) {
214+
Error L0ProgramBuilderTy::buildModules(const std::string_view BuildOptions) {
260215
auto &l0Device = getL0Device();
261216
auto Image = getMemoryBuffer();
262217
if (identify_magic(Image.getBuffer()) == file_magic::spirv_object) {
@@ -272,8 +227,6 @@ Error L0ProgramTy::buildModules(const std::string_view BuildOptions) {
272227
return Plugin::error(ErrorCode::UNKNOWN, "Invalid oneAPI OpenMP image");
273228
}
274229

275-
setLibModule();
276-
277230
// Iterate over the images and pick the first one that fits.
278231
uint64_t ImageCount = 0;
279232
struct V1ImageInfo {
@@ -473,12 +426,32 @@ Error L0ProgramTy::buildModules(const std::string_view BuildOptions) {
473426
}
474427
DP("Created module from image #%" PRIu64 ".\n", Idx);
475428

429+
if (RequiresModuleLink) {
430+
DP("Linking modules after adding image #%" PRIu64 ".\n", Idx);
431+
if (auto Err = linkModules())
432+
return Err;
433+
}
434+
476435
return Plugin::success();
477436
}
478437

479438
return Plugin::error(ErrorCode::UNKNOWN, "Failed to create program modules.");
480439
}
481440

441+
Expected<std::unique_ptr<MemoryBuffer>> L0ProgramBuilderTy::getELF() {
442+
assert(GlobalModule != nullptr && "GlobalModule is null");
443+
444+
size_t Size = 0;
445+
446+
CALL_ZE_RET_ERROR(zeModuleGetNativeBinary, GlobalModule, &Size, nullptr);
447+
std::vector<uint8_t> ELFData(Size);
448+
CALL_ZE_RET_ERROR(zeModuleGetNativeBinary, GlobalModule, &Size,
449+
ELFData.data());
450+
return MemoryBuffer::getMemBufferCopy(
451+
StringRef(reinterpret_cast<const char *>(ELFData.data()), Size),
452+
/*BufferName=*/"L0Program ELF");
453+
}
454+
482455
Expected<void *> L0ProgramTy::getOffloadVarDeviceAddr(const char *CName) const {
483456
DP("Looking up OpenMP global variable '%s'.\n", CName);
484457

0 commit comments

Comments
 (0)