1//===-RTLs/generic-64bit/src/rtl.cpp - Target RTLs Implementation - 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// RTL NextGen for generic 64-bit machine
10//
11//===----------------------------------------------------------------------===//
12
13#include <cassert>
14#include <cstddef>
15#include <ffi.h>
16#include <string>
17#include <unordered_map>
18
19#include "Shared/Debug.h"
20#include "Shared/Environment.h"
21
22#include "GlobalHandler.h"
23#include "OpenMP/OMPT/Callback.h"
24#include "PluginInterface.h"
25#include "omptarget.h"
26
27#include "llvm/ADT/SmallVector.h"
28#include "llvm/Frontend/OpenMP/OMPConstants.h"
29#include "llvm/Frontend/OpenMP/OMPDeviceConstants.h"
30#include "llvm/Frontend/OpenMP/OMPGridValues.h"
31#include "llvm/Support/DynamicLibrary.h"
32
33// The number of devices in this plugin.
34#define NUM_DEVICES 4
35
36// The ELF ID should be defined at compile-time by the build system.
37#ifndef TARGET_ELF_ID
38#define TARGET_ELF_ID EM_NONE
39#endif
40
41// The target triple should be defined at compile-time by the build system.
42#ifndef LIBOMPTARGET_NEXTGEN_GENERIC_PLUGIN_TRIPLE
43#define LIBOMPTARGET_NEXTGEN_GENERIC_PLUGIN_TRIPLE ""
44#endif
45
46namespace llvm {
47namespace omp {
48namespace target {
49namespace plugin {
50
51/// Forward declarations for all specialized data structures.
52struct GenELF64KernelTy;
53struct GenELF64DeviceTy;
54struct GenELF64PluginTy;
55
56using llvm::sys::DynamicLibrary;
57
58/// Class implementing kernel functionalities for GenELF64.
59struct GenELF64KernelTy : public GenericKernelTy {
60 /// Construct the kernel with a name and an execution mode.
61 GenELF64KernelTy(const char *Name) : GenericKernelTy(Name), Func(nullptr) {}
62
63 /// Initialize the kernel.
64 Error initImpl(GenericDeviceTy &Device, DeviceImageTy &Image) override {
65 // Functions have zero size.
66 GlobalTy Global(getName(), 0);
67
68 // Get the metadata (address) of the kernel function.
69 GenericGlobalHandlerTy &GHandler = Device.Plugin.getGlobalHandler();
70 if (auto Err = GHandler.getGlobalMetadataFromDevice(Device, Image, Global))
71 return Err;
72
73 // Check that the function pointer is valid.
74 if (!Global.getPtr())
75 return Plugin::error("Invalid function for kernel %s", getName());
76
77 // Save the function pointer.
78 Func = (void (*)())Global.getPtr();
79
80 KernelEnvironment.Configuration.ExecMode = OMP_TGT_EXEC_MODE_GENERIC;
81 KernelEnvironment.Configuration.MayUseNestedParallelism = /*Unknown=*/2;
82 KernelEnvironment.Configuration.UseGenericStateMachine = /*Unknown=*/2;
83
84 // Set the maximum number of threads to a single.
85 MaxNumThreads = 1;
86 return Plugin::success();
87 }
88
89 /// Launch the kernel using the libffi.
90 Error launchImpl(GenericDeviceTy &GenericDevice, uint32_t NumThreads,
91 uint64_t NumBlocks, KernelArgsTy &KernelArgs, void *Args,
92 AsyncInfoWrapperTy &AsyncInfoWrapper) const override {
93 // Create a vector of ffi_types, one per argument.
94 SmallVector<ffi_type *, 16> ArgTypes(KernelArgs.NumArgs, &ffi_type_pointer);
95 ffi_type **ArgTypesPtr = (ArgTypes.size()) ? &ArgTypes[0] : nullptr;
96
97 // Prepare the cif structure before running the kernel function.
98 ffi_cif Cif;
99 ffi_status Status = ffi_prep_cif(&Cif, FFI_DEFAULT_ABI, KernelArgs.NumArgs,
100 &ffi_type_void, ArgTypesPtr);
101 if (Status != FFI_OK)
102 return Plugin::error("Error in ffi_prep_cif: %d", Status);
103
104 // Call the kernel function through libffi.
105 long Return;
106 ffi_call(cif: &Cif, fn: Func, rvalue: &Return, avalue: (void **)Args);
107
108 return Plugin::success();
109 }
110
111private:
112 /// The kernel function to execute.
113 void (*Func)(void);
114};
115
116/// Class implementing the GenELF64 device images properties.
117struct GenELF64DeviceImageTy : public DeviceImageTy {
118 /// Create the GenELF64 image with the id and the target image pointer.
119 GenELF64DeviceImageTy(int32_t ImageId, GenericDeviceTy &Device,
120 const __tgt_device_image *TgtImage)
121 : DeviceImageTy(ImageId, Device, TgtImage), DynLib() {}
122
123 /// Getter and setter for the dynamic library.
124 DynamicLibrary &getDynamicLibrary() { return DynLib; }
125 void setDynamicLibrary(const DynamicLibrary &Lib) { DynLib = Lib; }
126
127private:
128 /// The dynamic library that loaded the image.
129 DynamicLibrary DynLib;
130};
131
132/// Class implementing the device functionalities for GenELF64.
133struct GenELF64DeviceTy : public GenericDeviceTy {
134 /// Create the device with a specific id.
135 GenELF64DeviceTy(GenericPluginTy &Plugin, int32_t DeviceId,
136 int32_t NumDevices)
137 : GenericDeviceTy(Plugin, DeviceId, NumDevices, GenELF64GridValues) {}
138
139 ~GenELF64DeviceTy() {}
140
141 /// Initialize the device, which is a no-op
142 Error initImpl(GenericPluginTy &Plugin) override { return Plugin::success(); }
143
144 /// Deinitialize the device, which is a no-op
145 Error deinitImpl() override { return Plugin::success(); }
146
147 /// See GenericDeviceTy::getComputeUnitKind().
148 std::string getComputeUnitKind() const override { return "generic-64bit"; }
149
150 /// Construct the kernel for a specific image on the device.
151 Expected<GenericKernelTy &> constructKernel(const char *Name) override {
152 // Allocate and construct the kernel.
153 GenELF64KernelTy *GenELF64Kernel = Plugin.allocate<GenELF64KernelTy>();
154 if (!GenELF64Kernel)
155 return Plugin::error("Failed to allocate memory for GenELF64 kernel");
156
157 new (GenELF64Kernel) GenELF64KernelTy(Name);
158
159 return *GenELF64Kernel;
160 }
161
162 /// Set the current context to this device, which is a no-op.
163 Error setContext() override { return Plugin::success(); }
164
165 /// Load the binary image into the device and allocate an image object.
166 Expected<DeviceImageTy *> loadBinaryImpl(const __tgt_device_image *TgtImage,
167 int32_t ImageId) override {
168 // Allocate and initialize the image object.
169 GenELF64DeviceImageTy *Image = Plugin.allocate<GenELF64DeviceImageTy>();
170 new (Image) GenELF64DeviceImageTy(ImageId, *this, TgtImage);
171
172 // Create a temporary file.
173 char TmpFileName[] = "/tmp/tmpfile_XXXXXX";
174 int TmpFileFd = mkstemp(template: TmpFileName);
175 if (TmpFileFd == -1)
176 return Plugin::error("Failed to create tmpfile for loading target image");
177
178 // Open the temporary file.
179 FILE *TmpFile = fdopen(fd: TmpFileFd, modes: "wb");
180 if (!TmpFile)
181 return Plugin::error("Failed to open tmpfile %s for loading target image",
182 TmpFileName);
183
184 // Write the image into the temporary file.
185 size_t Written = fwrite(Image->getStart(), Image->getSize(), 1, TmpFile);
186 if (Written != 1)
187 return Plugin::error("Failed to write target image to tmpfile %s",
188 TmpFileName);
189
190 // Close the temporary file.
191 int Ret = fclose(stream: TmpFile);
192 if (Ret)
193 return Plugin::error("Failed to close tmpfile %s with the target image",
194 TmpFileName);
195
196 // Load the temporary file as a dynamic library.
197 std::string ErrMsg;
198 DynamicLibrary DynLib =
199 DynamicLibrary::getPermanentLibrary(filename: TmpFileName, errMsg: &ErrMsg);
200
201 // Check if the loaded library is valid.
202 if (!DynLib.isValid())
203 return Plugin::error("Failed to load target image: %s", ErrMsg.c_str());
204
205 // Save a reference of the image's dynamic library.
206 Image->setDynamicLibrary(DynLib);
207
208 return Image;
209 }
210
211 /// Allocate memory. Use std::malloc in all cases.
212 void *allocate(size_t Size, void *, TargetAllocTy Kind) override {
213 if (Size == 0)
214 return nullptr;
215
216 void *MemAlloc = nullptr;
217 switch (Kind) {
218 case TARGET_ALLOC_DEFAULT:
219 case TARGET_ALLOC_DEVICE:
220 case TARGET_ALLOC_HOST:
221 case TARGET_ALLOC_SHARED:
222 case TARGET_ALLOC_DEVICE_NON_BLOCKING:
223 MemAlloc = std::malloc(size: Size);
224 break;
225 }
226 return MemAlloc;
227 }
228
229 /// Free the memory. Use std::free in all cases.
230 int free(void *TgtPtr, TargetAllocTy Kind) override {
231 std::free(ptr: TgtPtr);
232 return OFFLOAD_SUCCESS;
233 }
234
235 /// This plugin does nothing to lock buffers. Do not return an error, just
236 /// return the same pointer as the device pointer.
237 Expected<void *> dataLockImpl(void *HstPtr, int64_t Size) override {
238 return HstPtr;
239 }
240
241 /// Nothing to do when unlocking the buffer.
242 Error dataUnlockImpl(void *HstPtr) override { return Plugin::success(); }
243
244 /// Indicate that the buffer is not pinned.
245 Expected<bool> isPinnedPtrImpl(void *HstPtr, void *&BaseHstPtr,
246 void *&BaseDevAccessiblePtr,
247 size_t &BaseSize) const override {
248 return false;
249 }
250
251 /// Submit data to the device (host to device transfer).
252 Error dataSubmitImpl(void *TgtPtr, const void *HstPtr, int64_t Size,
253 AsyncInfoWrapperTy &AsyncInfoWrapper) override {
254 std::memcpy(dest: TgtPtr, src: HstPtr, n: Size);
255 return Plugin::success();
256 }
257
258 /// Retrieve data from the device (device to host transfer).
259 Error dataRetrieveImpl(void *HstPtr, const void *TgtPtr, int64_t Size,
260 AsyncInfoWrapperTy &AsyncInfoWrapper) override {
261 std::memcpy(dest: HstPtr, src: TgtPtr, n: Size);
262 return Plugin::success();
263 }
264
265 /// Exchange data between two devices within the plugin. This function is not
266 /// supported in this plugin.
267 Error dataExchangeImpl(const void *SrcPtr, GenericDeviceTy &DstGenericDevice,
268 void *DstPtr, int64_t Size,
269 AsyncInfoWrapperTy &AsyncInfoWrapper) override {
270 // This function should never be called because the function
271 // GenELF64PluginTy::isDataExchangable() returns false.
272 return Plugin::error("dataExchangeImpl not supported");
273 }
274
275 /// All functions are already synchronous. No need to do anything on this
276 /// synchronization function.
277 Error synchronizeImpl(__tgt_async_info &AsyncInfo) override {
278 return Plugin::success();
279 }
280
281 /// All functions are already synchronous. No need to do anything on this
282 /// query function.
283 Error queryAsyncImpl(__tgt_async_info &AsyncInfo) override {
284 return Plugin::success();
285 }
286
287 /// This plugin does not support interoperability
288 Error initAsyncInfoImpl(AsyncInfoWrapperTy &AsyncInfoWrapper) override {
289 return Plugin::error("initAsyncInfoImpl not supported");
290 }
291
292 /// This plugin does not support interoperability
293 Error initDeviceInfoImpl(__tgt_device_info *DeviceInfo) override {
294 return Plugin::error("initDeviceInfoImpl not supported");
295 }
296
297 /// This plugin does not support the event API. Do nothing without failing.
298 Error createEventImpl(void **EventPtrStorage) override {
299 *EventPtrStorage = nullptr;
300 return Plugin::success();
301 }
302 Error destroyEventImpl(void *EventPtr) override { return Plugin::success(); }
303 Error recordEventImpl(void *EventPtr,
304 AsyncInfoWrapperTy &AsyncInfoWrapper) override {
305 return Plugin::success();
306 }
307 Error waitEventImpl(void *EventPtr,
308 AsyncInfoWrapperTy &AsyncInfoWrapper) override {
309 return Plugin::success();
310 }
311 Error syncEventImpl(void *EventPtr) override { return Plugin::success(); }
312
313 /// Print information about the device.
314 Error obtainInfoImpl(InfoQueueTy &Info) override {
315 Info.add("Device Type", "Generic-elf-64bit");
316 return Plugin::success();
317 }
318
319 /// This plugin should not setup the device environment or memory pool.
320 virtual bool shouldSetupDeviceEnvironment() const override { return false; };
321 virtual bool shouldSetupDeviceMemoryPool() const override { return false; };
322
323 /// Getters and setters for stack size and heap size not relevant.
324 Error getDeviceStackSize(uint64_t &Value) override {
325 Value = 0;
326 return Plugin::success();
327 }
328 Error setDeviceStackSize(uint64_t Value) override {
329 return Plugin::success();
330 }
331 Error getDeviceHeapSize(uint64_t &Value) override {
332 Value = 0;
333 return Plugin::success();
334 }
335 Error setDeviceHeapSize(uint64_t Value) override { return Plugin::success(); }
336
337private:
338 /// Grid values for Generic ELF64 plugins.
339 static constexpr GV GenELF64GridValues = {
340 .GV_Slot_Size: 1, // GV_Slot_Size
341 .GV_Warp_Size: 1, // GV_Warp_Size
342 .GV_Max_Teams: 1, // GV_Max_Teams
343 .GV_Default_Num_Teams: 1, // GV_Default_Num_Teams
344 .GV_SimpleBufferSize: 1, // GV_SimpleBufferSize
345 .GV_Max_WG_Size: 1, // GV_Max_WG_Size
346 .GV_Default_WG_Size: 1, // GV_Default_WG_Size
347 };
348};
349
350class GenELF64GlobalHandlerTy final : public GenericGlobalHandlerTy {
351public:
352 Error getGlobalMetadataFromDevice(GenericDeviceTy &GenericDevice,
353 DeviceImageTy &Image,
354 GlobalTy &DeviceGlobal) override {
355 const char *GlobalName = DeviceGlobal.getName().data();
356 GenELF64DeviceImageTy &GenELF64Image =
357 static_cast<GenELF64DeviceImageTy &>(Image);
358
359 // Get dynamic library that has loaded the device image.
360 DynamicLibrary &DynLib = GenELF64Image.getDynamicLibrary();
361
362 // Get the address of the symbol.
363 void *Addr = DynLib.getAddressOfSymbol(symbolName: GlobalName);
364 if (Addr == nullptr) {
365 return Plugin::error("Failed to load global '%s'", GlobalName);
366 }
367
368 // Save the pointer to the symbol.
369 DeviceGlobal.setPtr(Addr);
370
371 return Plugin::success();
372 }
373};
374
375/// Class implementing the plugin functionalities for GenELF64.
376struct GenELF64PluginTy final : public GenericPluginTy {
377 /// Create the GenELF64 plugin.
378 GenELF64PluginTy() : GenericPluginTy(getTripleArch()) {}
379
380 /// This class should not be copied.
381 GenELF64PluginTy(const GenELF64PluginTy &) = delete;
382 GenELF64PluginTy(GenELF64PluginTy &&) = delete;
383
384 /// Initialize the plugin and return the number of devices.
385 Expected<int32_t> initImpl() override {
386#ifdef OMPT_SUPPORT
387 ompt::connectLibrary();
388#endif
389
390#ifdef USES_DYNAMIC_FFI
391 if (auto Err = Plugin::check(ffi_init(), "Failed to initialize libffi"))
392 return std::move(Err);
393#endif
394
395 return NUM_DEVICES;
396 }
397
398 /// Deinitialize the plugin.
399 Error deinitImpl() override { return Plugin::success(); }
400
401 /// Creates a generic ELF device.
402 GenericDeviceTy *createDevice(GenericPluginTy &Plugin, int32_t DeviceId,
403 int32_t NumDevices) override {
404 return new GenELF64DeviceTy(Plugin, DeviceId, NumDevices);
405 }
406
407 /// Creates a generic global handler.
408 GenericGlobalHandlerTy *createGlobalHandler() override {
409 return new GenELF64GlobalHandlerTy();
410 }
411
412 /// Get the ELF code to recognize the compatible binary images.
413 uint16_t getMagicElfBits() const override { return ELF::TARGET_ELF_ID; }
414
415 /// This plugin does not support exchanging data between two devices.
416 bool isDataExchangable(int32_t SrcDeviceId, int32_t DstDeviceId) override {
417 return false;
418 }
419
420 /// All images (ELF-compatible) should be compatible with this plugin.
421 Expected<bool> isELFCompatible(StringRef) const override { return true; }
422
423 Triple::ArchType getTripleArch() const override {
424 return llvm::Triple(LIBOMPTARGET_NEXTGEN_GENERIC_PLUGIN_TRIPLE).getArch();
425 }
426};
427
428GenericPluginTy *PluginTy::createPlugin() { return new GenELF64PluginTy(); }
429
430template <typename... ArgsTy>
431static Error Plugin::check(int32_t Code, const char *ErrMsg, ArgsTy... Args) {
432 if (Code == 0)
433 return Error::success();
434
435 return createStringError<ArgsTy..., const char *>(
436 inconvertibleErrorCode(), ErrMsg, Args..., std::to_string(Code).data());
437}
438
439} // namespace plugin
440} // namespace target
441} // namespace omp
442} // namespace llvm
443

source code of offload/plugins-nextgen/host/src/rtl.cpp