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 | |
46 | namespace llvm { |
47 | namespace omp { |
48 | namespace target { |
49 | namespace plugin { |
50 | |
51 | /// Forward declarations for all specialized data structures. |
52 | struct GenELF64KernelTy; |
53 | struct GenELF64DeviceTy; |
54 | struct GenELF64PluginTy; |
55 | |
56 | using llvm::sys::DynamicLibrary; |
57 | |
58 | /// Class implementing kernel functionalities for GenELF64. |
59 | struct 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 | |
111 | private: |
112 | /// The kernel function to execute. |
113 | void (*Func)(void); |
114 | }; |
115 | |
116 | /// Class implementing the GenELF64 device images properties. |
117 | struct 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 | |
127 | private: |
128 | /// The dynamic library that loaded the image. |
129 | DynamicLibrary DynLib; |
130 | }; |
131 | |
132 | /// Class implementing the device functionalities for GenELF64. |
133 | struct 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 | |
337 | private: |
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 | |
350 | class GenELF64GlobalHandlerTy final : public GenericGlobalHandlerTy { |
351 | public: |
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. |
376 | struct 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 | |
428 | GenericPluginTy *PluginTy::createPlugin() { return new GenELF64PluginTy(); } |
429 | |
430 | template <typename... ArgsTy> |
431 | static 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 | |