blob: 3fba82c3ae12ce2437c5768e95fd301562f6da05 [file] [log] [blame]
//===-RTLs/generic-64bit/src/rtl.cpp - Target RTLs Implementation - C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// RTL for generic 64-bit machine
//
//===----------------------------------------------------------------------===//
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/DynamicLibrary.h"
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ffi.h>
#include <link.h>
#include <list>
#include <string>
#include <vector>
#include "Debug.h"
#include "omptargetplugin.h"
using namespace llvm;
using namespace llvm::sys;
#ifndef TARGET_NAME
#define TARGET_NAME Generic ELF - 64bit
#endif
#define DEBUG_PREFIX "TARGET " GETNAME(TARGET_NAME) " RTL"
#ifndef TARGET_ELF_ID
#define TARGET_ELF_ID 0
#endif
#include "elf_common.h"
#define NUMBER_OF_DEVICES 4
#define OFFLOAD_SECTION_NAME "omp_offloading_entries"
/// Array of Dynamic libraries loaded for this target.
struct DynLibTy {
std::string FileName;
std::unique_ptr<DynamicLibrary> DynLib;
};
/// Keep entries table per device.
struct FuncOrGblEntryTy {
__tgt_target_table Table;
SmallVector<__tgt_offload_entry> Entries;
};
/// Class containing all the device information.
class RTLDeviceInfoTy {
std::vector<std::list<FuncOrGblEntryTy>> FuncGblEntries;
public:
std::list<DynLibTy> DynLibs;
// Record entry point associated with device.
void createOffloadTable(int32_t DeviceId,
SmallVector<__tgt_offload_entry> &&Entries) {
assert(DeviceId < (int32_t)FuncGblEntries.size() &&
"Unexpected device id!");
FuncGblEntries[DeviceId].emplace_back();
FuncOrGblEntryTy &E = FuncGblEntries[DeviceId].back();
E.Entries = Entries;
E.Table.EntriesBegin = E.Entries.begin();
E.Table.EntriesEnd = E.Entries.end();
}
// Return true if the entry is associated with device.
bool findOffloadEntry(int32_t DeviceId, void *Addr) {
assert(DeviceId < (int32_t)FuncGblEntries.size() &&
"Unexpected device id!");
FuncOrGblEntryTy &E = FuncGblEntries[DeviceId].back();
for (__tgt_offload_entry *I = E.Table.EntriesBegin,
*End = E.Table.EntriesEnd;
I < End; ++I) {
if (I->addr == Addr)
return true;
}
return false;
}
// Return the pointer to the target entries table.
__tgt_target_table *getOffloadEntriesTable(int32_t DeviceId) {
assert(DeviceId < (int32_t)FuncGblEntries.size() &&
"Unexpected device id!");
FuncOrGblEntryTy &E = FuncGblEntries[DeviceId].back();
return &E.Table;
}
RTLDeviceInfoTy(int32_t NumDevices) { FuncGblEntries.resize(NumDevices); }
~RTLDeviceInfoTy() {
// Close dynamic libraries
for (auto &Lib : DynLibs) {
if (Lib.DynLib->isValid())
remove(Lib.FileName.c_str());
}
}
};
static RTLDeviceInfoTy DeviceInfo(NUMBER_OF_DEVICES);
#ifdef __cplusplus
extern "C" {
#endif
int32_t __tgt_rtl_is_valid_binary(__tgt_device_image *Image) {
// If we don't have a valid ELF ID we can just fail.
#if TARGET_ELF_ID < 1
return 0;
#else
return elf_check_machine(Image, TARGET_ELF_ID);
#endif
}
int32_t __tgt_rtl_number_of_devices() { return NUMBER_OF_DEVICES; }
int32_t __tgt_rtl_init_device(int32_t DeviceId) { return OFFLOAD_SUCCESS; }
__tgt_target_table *__tgt_rtl_load_binary(int32_t DeviceId,
__tgt_device_image *Image) {
DP("Dev %d: load binary from " DPxMOD " image\n", DeviceId,
DPxPTR(Image->ImageStart));
assert(DeviceId >= 0 && DeviceId < NUMBER_OF_DEVICES && "bad dev id");
size_t ImageSize = (size_t)Image->ImageEnd - (size_t)Image->ImageStart;
// load dynamic library and get the entry points. We use the dl library
// to do the loading of the library, but we could do it directly to avoid the
// dump to the temporary file.
//
// 1) Create tmp file with the library contents.
// 2) Use dlopen to load the file and dlsym to retrieve the symbols.
char TmpName[] = "/tmp/tmpfile_XXXXXX";
int TmpFd = mkstemp(TmpName);
if (TmpFd == -1)
return nullptr;
FILE *Ftmp = fdopen(TmpFd, "wb");
if (!Ftmp)
return nullptr;
fwrite(Image->ImageStart, ImageSize, 1, Ftmp);
fclose(Ftmp);
std::string ErrMsg;
auto DynLib = std::make_unique<sys::DynamicLibrary>(
sys::DynamicLibrary::getPermanentLibrary(TmpName, &ErrMsg));
DynLibTy Lib = {TmpName, std::move(DynLib)};
if (!Lib.DynLib->isValid()) {
DP("Target library loading error: %s\n", ErrMsg.c_str());
return NULL;
}
__tgt_offload_entry *HostBegin = Image->EntriesBegin;
__tgt_offload_entry *HostEnd = Image->EntriesEnd;
// Create a new offloading entry list using the device symbol address.
SmallVector<__tgt_offload_entry> Entries;
for (__tgt_offload_entry *E = HostBegin; E != HostEnd; ++E) {
if (!E->addr)
return nullptr;
__tgt_offload_entry Entry = *E;
void *DevAddr = Lib.DynLib->getAddressOfSymbol(E->name);
Entry.addr = DevAddr;
DP("Entry point " DPxMOD " maps to global %s (" DPxMOD ")\n",
DPxPTR(E - HostBegin), E->name, DPxPTR(DevAddr));
Entries.emplace_back(Entry);
}
DeviceInfo.createOffloadTable(DeviceId, std::move(Entries));
DeviceInfo.DynLibs.emplace_back(std::move(Lib));
return DeviceInfo.getOffloadEntriesTable(DeviceId);
}
void __tgt_rtl_print_device_info(int32_t DeviceId) {
printf(" This is a generic-elf-64bit device\n");
}
// Sample implementation of explicit memory allocator. For this plugin all kinds
// are equivalent to each other.
void *__tgt_rtl_data_alloc(int32_t DeviceId, int64_t Size, void *HstPtr,
int32_t Kind) {
void *Ptr = NULL;
switch (Kind) {
case TARGET_ALLOC_DEVICE:
case TARGET_ALLOC_HOST:
case TARGET_ALLOC_SHARED:
case TARGET_ALLOC_DEFAULT:
Ptr = malloc(Size);
break;
default:
REPORT("Invalid target data allocation kind");
}
return Ptr;
}
int32_t __tgt_rtl_data_submit(int32_t DeviceId, void *TgtPtr, void *HstPtr,
int64_t Size) {
memcpy(TgtPtr, HstPtr, Size);
return OFFLOAD_SUCCESS;
}
int32_t __tgt_rtl_data_retrieve(int32_t DeviceId, void *HstPtr, void *TgtPtr,
int64_t Size) {
memcpy(HstPtr, TgtPtr, Size);
return OFFLOAD_SUCCESS;
}
int32_t __tgt_rtl_data_delete(int32_t DeviceId, void *TgtPtr, int32_t) {
free(TgtPtr);
return OFFLOAD_SUCCESS;
}
int32_t __tgt_rtl_launch_kernel(int32_t DeviceId, void *TgtEntryPtr,
void **TgtArgs, ptrdiff_t *TgtOffsets,
KernelArgsTy *KernelArgs,
__tgt_async_info *AsyncInfoPtr) {
assert(!KernelArgs->NumTeams[1] && !KernelArgs->NumTeams[2] &&
!KernelArgs->ThreadLimit[1] && !KernelArgs->ThreadLimit[2] &&
"Only one dimensional kernels supported.");
// ignore team num and thread limit.
// Use libffi to launch execution.
ffi_cif Cif;
// All args are references.
std::vector<ffi_type *> ArgsTypes(KernelArgs->NumArgs, &ffi_type_pointer);
std::vector<void *> Args(KernelArgs->NumArgs);
std::vector<void *> Ptrs(KernelArgs->NumArgs);
for (uint32_t I = 0; I < KernelArgs->NumArgs; ++I) {
Ptrs[I] = (void *)((intptr_t)TgtArgs[I] + TgtOffsets[I]);
Args[I] = &Ptrs[I];
}
ffi_status Status = ffi_prep_cif(&Cif, FFI_DEFAULT_ABI, KernelArgs->NumArgs,
&ffi_type_void, &ArgsTypes[0]);
assert(Status == FFI_OK && "Unable to prepare target launch!");
if (Status != FFI_OK)
return OFFLOAD_FAIL;
DP("Running entry point at " DPxMOD "...\n", DPxPTR(TgtEntryPtr));
void (*Entry)(void);
*((void **)&Entry) = TgtEntryPtr;
ffi_call(&Cif, Entry, NULL, &Args[0]);
return OFFLOAD_SUCCESS;
}
#ifdef __cplusplus
}
#endif