#include <cassert>
#include <cstddef>
#include <ffi.h>
#include <string>
#include <unordered_map>
#include "Shared/Debug.h"
#include "Shared/Environment.h"
#include "Utils/ELF.h"
#include "GlobalHandler.h"
#include "OpenMP/OMPT/Callback.h"
#include "PluginInterface.h"
#include "omptarget.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Frontend/OpenMP/OMPConstants.h"
#include "llvm/Frontend/OpenMP/OMPDeviceConstants.h"
#include "llvm/Frontend/OpenMP/OMPGridValues.h"
#include "llvm/Support/DynamicLibrary.h"
#if !defined(__BYTE_ORDER__) || !defined(__ORDER_LITTLE_ENDIAN__) || \
!defined(__ORDER_BIG_ENDIAN__)
#error "Missing preprocessor definitions for endianness detection."
#endif
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
#define LITTLEENDIAN_CPU
#elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#define BIGENDIAN_CPU
#endif
#define NUM_DEVICES 4
namespace llvm {
namespace omp {
namespace target {
namespace plugin {
struct GenELF64KernelTy;
struct GenELF64DeviceTy;
struct GenELF64PluginTy;
using llvm::sys::DynamicLibrary;
struct GenELF64KernelTy : public GenericKernelTy {
GenELF64KernelTy(const char *Name) : GenericKernelTy(Name), Func(nullptr) {}
Error initImpl(GenericDeviceTy &Device, DeviceImageTy &Image) override {
GlobalTy Global(getName(), 0);
GenericGlobalHandlerTy &GHandler = Device.Plugin.getGlobalHandler();
if (auto Err = GHandler.getGlobalMetadataFromDevice(Device, Image, Global))
return Err;
if (!Global.getPtr())
return Plugin::error("Invalid function for kernel %s", getName());
Func = (void (*)())Global.getPtr();
KernelEnvironment.Configuration.ExecMode = OMP_TGT_EXEC_MODE_GENERIC;
KernelEnvironment.Configuration.MayUseNestedParallelism = 2;
KernelEnvironment.Configuration.UseGenericStateMachine = 2;
MaxNumThreads = 1;
return Plugin::success();
}
Error launchImpl(GenericDeviceTy &GenericDevice, uint32_t NumThreads,
uint64_t NumBlocks, KernelArgsTy &KernelArgs,
KernelLaunchParamsTy LaunchParams,
AsyncInfoWrapperTy &AsyncInfoWrapper) const override {
SmallVector<ffi_type *, 16> ArgTypes(KernelArgs.NumArgs, &ffi_type_pointer);
ffi_type **ArgTypesPtr = (ArgTypes.size()) ? &ArgTypes[0] : nullptr;
ffi_cif Cif;
ffi_status Status = ffi_prep_cif(&Cif, FFI_DEFAULT_ABI, KernelArgs.NumArgs,
&ffi_type_void, ArgTypesPtr);
if (Status != FFI_OK)
return Plugin::error("Error in ffi_prep_cif: %d", Status);
long Return;
ffi_call(&Cif, Func, &Return, (void **)LaunchParams.Ptrs);
return Plugin::success();
}
private:
void (*Func)(void);
};
struct GenELF64DeviceImageTy : public DeviceImageTy {
GenELF64DeviceImageTy(int32_t ImageId, GenericDeviceTy &Device,
const __tgt_device_image *TgtImage)
: DeviceImageTy(ImageId, Device, TgtImage), DynLib() {}
DynamicLibrary &getDynamicLibrary() { return DynLib; }
void setDynamicLibrary(const DynamicLibrary &Lib) { DynLib = Lib; }
private:
DynamicLibrary DynLib;
};
struct GenELF64DeviceTy : public GenericDeviceTy {
GenELF64DeviceTy(GenericPluginTy &Plugin, int32_t DeviceId,
int32_t NumDevices)
: GenericDeviceTy(Plugin, DeviceId, NumDevices, GenELF64GridValues) {}
~GenELF64DeviceTy() {}
Error initImpl(GenericPluginTy &Plugin) override { return Plugin::success(); }
Error deinitImpl() override { return Plugin::success(); }
std::string getComputeUnitKind() const override { return "generic-64bit"; }
Expected<GenericKernelTy &> constructKernel(const char *Name) override {
GenELF64KernelTy *GenELF64Kernel = Plugin.allocate<GenELF64KernelTy>();
if (!GenELF64Kernel)
return Plugin::error("Failed to allocate memory for GenELF64 kernel");
new (GenELF64Kernel) GenELF64KernelTy(Name);
return *GenELF64Kernel;
}
Error setContext() override { return Plugin::success(); }
Expected<DeviceImageTy *> loadBinaryImpl(const __tgt_device_image *TgtImage,
int32_t ImageId) override {
GenELF64DeviceImageTy *Image = Plugin.allocate<GenELF64DeviceImageTy>();
new (Image) GenELF64DeviceImageTy(ImageId, *this, TgtImage);
char TmpFileName[] = "/tmp/tmpfile_XXXXXX";
int TmpFileFd = mkstemp(TmpFileName);
if (TmpFileFd == -1)
return Plugin::error("Failed to create tmpfile for loading target image");
FILE *TmpFile = fdopen(TmpFileFd, "wb");
if (!TmpFile)
return Plugin::error("Failed to open tmpfile %s for loading target image",
TmpFileName);
size_t Written = fwrite(Image->getStart(), Image->getSize(), 1, TmpFile);
if (Written != 1)
return Plugin::error("Failed to write target image to tmpfile %s",
TmpFileName);
int Ret = fclose(TmpFile);
if (Ret)
return Plugin::error("Failed to close tmpfile %s with the target image",
TmpFileName);
std::string ErrMsg;
DynamicLibrary DynLib =
DynamicLibrary::getPermanentLibrary(TmpFileName, &ErrMsg);
if (!DynLib.isValid())
return Plugin::error("Failed to load target image: %s", ErrMsg.c_str());
Image->setDynamicLibrary(DynLib);
return Image;
}
void *allocate(size_t Size, void *, TargetAllocTy Kind) override {
if (Size == 0)
return nullptr;
void *MemAlloc = nullptr;
switch (Kind) {
case TARGET_ALLOC_DEFAULT:
case TARGET_ALLOC_DEVICE:
case TARGET_ALLOC_HOST:
case TARGET_ALLOC_SHARED:
case TARGET_ALLOC_DEVICE_NON_BLOCKING:
MemAlloc = std::malloc(Size);
break;
}
return MemAlloc;
}
int free(void *TgtPtr, TargetAllocTy Kind) override {
std::free(TgtPtr);
return OFFLOAD_SUCCESS;
}
Expected<void *> dataLockImpl(void *HstPtr, int64_t Size) override {
return HstPtr;
}
Error dataUnlockImpl(void *HstPtr) override { return Plugin::success(); }
Expected<bool> isPinnedPtrImpl(void *HstPtr, void *&BaseHstPtr,
void *&BaseDevAccessiblePtr,
size_t &BaseSize) const override {
return false;
}
Error dataSubmitImpl(void *TgtPtr, const void *HstPtr, int64_t Size,
AsyncInfoWrapperTy &AsyncInfoWrapper) override {
std::memcpy(TgtPtr, HstPtr, Size);
return Plugin::success();
}
Error dataRetrieveImpl(void *HstPtr, const void *TgtPtr, int64_t Size,
AsyncInfoWrapperTy &AsyncInfoWrapper) override {
std::memcpy(HstPtr, TgtPtr, Size);
return Plugin::success();
}
Error dataExchangeImpl(const void *SrcPtr, GenericDeviceTy &DstGenericDevice,
void *DstPtr, int64_t Size,
AsyncInfoWrapperTy &AsyncInfoWrapper) override {
return Plugin::error("dataExchangeImpl not supported");
}
Error synchronizeImpl(__tgt_async_info &AsyncInfo) override {
return Plugin::success();
}
Error queryAsyncImpl(__tgt_async_info &AsyncInfo) override {
return Plugin::success();
}
Error initAsyncInfoImpl(AsyncInfoWrapperTy &AsyncInfoWrapper) override {
return Plugin::error("initAsyncInfoImpl not supported");
}
Error initDeviceInfoImpl(__tgt_device_info *DeviceInfo) override {
return Plugin::error("initDeviceInfoImpl not supported");
}
Error createEventImpl(void **EventPtrStorage) override {
*EventPtrStorage = nullptr;
return Plugin::success();
}
Error destroyEventImpl(void *EventPtr) override { return Plugin::success(); }
Error recordEventImpl(void *EventPtr,
AsyncInfoWrapperTy &AsyncInfoWrapper) override {
return Plugin::success();
}
Error waitEventImpl(void *EventPtr,
AsyncInfoWrapperTy &AsyncInfoWrapper) override {
return Plugin::success();
}
Error syncEventImpl(void *EventPtr) override { return Plugin::success(); }
Error obtainInfoImpl(InfoQueueTy &Info) override {
Info.add("Device Type", "Generic-elf-64bit");
return Plugin::success();
}
virtual bool shouldSetupDeviceEnvironment() const override { return false; };
virtual bool shouldSetupDeviceMemoryPool() const override { return false; };
Error getDeviceStackSize(uint64_t &Value) override {
Value = 0;
return Plugin::success();
}
Error setDeviceStackSize(uint64_t Value) override {
return Plugin::success();
}
Error getDeviceHeapSize(uint64_t &Value) override {
Value = 0;
return Plugin::success();
}
Error setDeviceHeapSize(uint64_t Value) override { return Plugin::success(); }
private:
static constexpr GV GenELF64GridValues = {
1,
1,
1,
1,
1,
1,
1,
};
};
class GenELF64GlobalHandlerTy final : public GenericGlobalHandlerTy {
public:
Error getGlobalMetadataFromDevice(GenericDeviceTy &GenericDevice,
DeviceImageTy &Image,
GlobalTy &DeviceGlobal) override {
const char *GlobalName = DeviceGlobal.getName().data();
GenELF64DeviceImageTy &GenELF64Image =
static_cast<GenELF64DeviceImageTy &>(Image);
DynamicLibrary &DynLib = GenELF64Image.getDynamicLibrary();
void *Addr = DynLib.getAddressOfSymbol(GlobalName);
if (Addr == nullptr) {
return Plugin::error("Failed to load global '%s'", GlobalName);
}
DeviceGlobal.setPtr(Addr);
return Plugin::success();
}
};
struct GenELF64PluginTy final : public GenericPluginTy {
GenELF64PluginTy() : GenericPluginTy(getTripleArch()) {}
GenELF64PluginTy(const GenELF64PluginTy &) = delete;
GenELF64PluginTy(GenELF64PluginTy &&) = delete;
Expected<int32_t> initImpl() override {
#ifdef USES_DYNAMIC_FFI
if (auto Err = Plugin::check(ffi_init(), "Failed to initialize libffi"))
return std::move(Err);
#endif
return NUM_DEVICES;
}
Error deinitImpl() override { return Plugin::success(); }
GenericDeviceTy *createDevice(GenericPluginTy &Plugin, int32_t DeviceId,
int32_t NumDevices) override {
return new GenELF64DeviceTy(Plugin, DeviceId, NumDevices);
}
GenericGlobalHandlerTy *createGlobalHandler() override {
return new GenELF64GlobalHandlerTy();
}
uint16_t getMagicElfBits() const override {
return utils::elf::getTargetMachine();
}
bool isDataExchangable(int32_t SrcDeviceId, int32_t DstDeviceId) override {
return false;
}
Expected<bool> isELFCompatible(uint32_t, StringRef) const override {
return true;
}
Triple::ArchType getTripleArch() const override {
#if defined(__x86_64__)
return llvm::Triple::x86_64;
#elif defined(__s390x__)
return llvm::Triple::systemz;
#elif defined(__aarch64__)
#ifdef LITTLEENDIAN_CPU
return llvm::Triple::aarch64;
#else
return llvm::Triple::aarch64_be;
#endif
#elif defined(__powerpc64__)
#ifdef LITTLEENDIAN_CPU
return llvm::Triple::ppc64le;
#else
return llvm::Triple::ppc64;
#endif
#else
return llvm::Triple::UnknownArch;
#endif
}
const char *getName() const override { return GETNAME(TARGET_NAME); }
};
template <typename... ArgsTy>
static Error Plugin::check(int32_t Code, const char *ErrMsg, ArgsTy... Args) {
if (Code == 0)
return Error::success();
return createStringError<ArgsTy..., const char *>(
inconvertibleErrorCode(), ErrMsg, Args..., std::to_string(Code).data());
}
}
}
}
}
extern "C" {
llvm::omp::target::plugin::GenericPluginTy *createPlugin_host() {
return new llvm::omp::target::plugin::GenELF64PluginTy();
}
}