[llvm] [Offload] Introduce offload-tblgen and initial new API implementation (PR #108413)
Callum Fare via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 18 03:15:12 PDT 2024
https://github.com/callumfare updated https://github.com/llvm/llvm-project/pull/108413
>From 841d00229858f15476089d866c95ad3795d117a0 Mon Sep 17 00:00:00 2001
From: Callum Fare <callum at codeplay.com>
Date: Thu, 12 Sep 2024 15:32:27 +0100
Subject: [PATCH 1/3] [Offload] Introduce offload-tblgen and initial new API
implementation
---
offload/CMakeLists.txt | 3 +
offload/new-api/API/APIDefs.td | 212 ++++++++++++++++
offload/new-api/API/Common.td | 75 ++++++
offload/new-api/API/Device.td | 101 ++++++++
offload/new-api/API/OffloadAPI.td | 15 ++
offload/new-api/API/Platform.td | 94 ++++++++
offload/new-api/API/README.md | 150 ++++++++++++
offload/new-api/CMakeLists.txt | 36 +++
offload/new-api/README.md | 8 +
offload/new-api/src/helpers.hpp | 96 ++++++++
offload/new-api/src/offload_impl.cpp | 187 +++++++++++++++
offload/new-api/src/offload_lib.cpp | 23 ++
.../common/include/PluginInterface.h | 7 +
offload/tools/offload-tblgen/APIGen.cpp | 196 +++++++++++++++
offload/tools/offload-tblgen/CMakeLists.txt | 24 ++
.../tools/offload-tblgen/EntryPointGen.cpp | 86 +++++++
offload/tools/offload-tblgen/FuncsGen.cpp | 54 +++++
offload/tools/offload-tblgen/GenCommon.hpp | 58 +++++
offload/tools/offload-tblgen/Generators.hpp | 18 ++
offload/tools/offload-tblgen/PrintGen.cpp | 206 ++++++++++++++++
offload/tools/offload-tblgen/RecordTypes.hpp | 227 ++++++++++++++++++
.../tools/offload-tblgen/offload-tblgen.cpp | 95 ++++++++
22 files changed, 1971 insertions(+)
create mode 100644 offload/new-api/API/APIDefs.td
create mode 100644 offload/new-api/API/Common.td
create mode 100644 offload/new-api/API/Device.td
create mode 100644 offload/new-api/API/OffloadAPI.td
create mode 100644 offload/new-api/API/Platform.td
create mode 100644 offload/new-api/API/README.md
create mode 100644 offload/new-api/CMakeLists.txt
create mode 100644 offload/new-api/README.md
create mode 100644 offload/new-api/src/helpers.hpp
create mode 100644 offload/new-api/src/offload_impl.cpp
create mode 100644 offload/new-api/src/offload_lib.cpp
create mode 100644 offload/tools/offload-tblgen/APIGen.cpp
create mode 100644 offload/tools/offload-tblgen/CMakeLists.txt
create mode 100644 offload/tools/offload-tblgen/EntryPointGen.cpp
create mode 100644 offload/tools/offload-tblgen/FuncsGen.cpp
create mode 100644 offload/tools/offload-tblgen/GenCommon.hpp
create mode 100644 offload/tools/offload-tblgen/Generators.hpp
create mode 100644 offload/tools/offload-tblgen/PrintGen.cpp
create mode 100644 offload/tools/offload-tblgen/RecordTypes.hpp
create mode 100644 offload/tools/offload-tblgen/offload-tblgen.cpp
diff --git a/offload/CMakeLists.txt b/offload/CMakeLists.txt
index 9ffe8f56b76e67..ba7be255cfe582 100644
--- a/offload/CMakeLists.txt
+++ b/offload/CMakeLists.txt
@@ -349,6 +349,9 @@ add_subdirectory(tools)
# Build target agnostic offloading library.
add_subdirectory(src)
+add_subdirectory(tools/offload-tblgen)
+add_subdirectory(new-api)
+
# Add tests.
add_subdirectory(test)
diff --git a/offload/new-api/API/APIDefs.td b/offload/new-api/API/APIDefs.td
new file mode 100644
index 00000000000000..410a28c4c90cfe
--- /dev/null
+++ b/offload/new-api/API/APIDefs.td
@@ -0,0 +1,212 @@
+//===-- APIDefs.td - Base definitions for Offload tablegen -*- tablegen -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the class definitions used to implement the Offload API,
+// as well as helper functions used to help populate relevant records.
+// See offload/API/README.md for more detailed documentation.
+//
+//===----------------------------------------------------------------------===//
+
+// Prefix for API naming. This could be hard-coded in the future when a value
+// is agreed upon.
+defvar PREFIX = "OFFLOAD";
+defvar prefix = !tolower(PREFIX);
+
+// Parameter flags
+defvar PARAM_IN = 0x1;
+defvar PARAM_OUT = 0x2;
+defvar PARAM_OPTIONAL = 0x4;
+defvar PARAM_IN_OPTIONAL = !or(PARAM_IN, PARAM_OPTIONAL);
+defvar PARAM_OUT_OPTIONAL = !or(PARAM_OUT, PARAM_OPTIONAL);
+
+// Does the type end with '_handle_t'?
+class IsHandleType<string Type> {
+ // size("_handle_t") == 9
+ bit ret = !if(!lt(!size(Type), 9), 0,
+ !ne(!find(Type, "_handle_t", !sub(!size(Type), 9)), -1));
+}
+
+// Does the type end with '*'?
+class IsPointerType<string Type> {
+ bit ret = !ne(!find(Type, "*", !sub(!size(Type), 1)), -1);
+}
+
+// Describes the valid range of a pointer parameter that reperesents an array
+class Range<string Begin, string End> {
+ string begin = Begin;
+ string end = End;
+}
+
+// Names the parameters that indicate the type and size of the data pointed to
+// by an opaque pointer parameter
+class TypeInfo<string TypeEnum, string TypeSize> {
+ string enum = TypeEnum;
+ string size = TypeSize;
+}
+
+class Param<string Type, string Name, string Desc, bits<3> Flags = 0> {
+ string type = Type;
+ string name = Name;
+ string desc = Desc;
+ bits<3> flags = Flags;
+ Range range = Range<"", "">;
+ TypeInfo type_info = TypeInfo<"", "">;
+ bit IsHandle = IsHandleType<type>.ret;
+ bit IsPointer = IsPointerType<type>.ret;
+}
+
+// A parameter whose range is described by other parameters in the function.
+class RangedParam<string Type, string Name, string Desc, bits<3> Flags, Range ParamRange> : Param<Type, Name, Desc, Flags> {
+ let range = ParamRange;
+}
+
+// A parameter (normally of type void*) which has its pointee type and size
+// described by other parameters in the function.
+class TypeTaggedParam<string Type, string Name, string Desc, bits<3> Flags, TypeInfo ParamTypeInfo> : Param<Type, Name, Desc, Flags> {
+ let type_info = ParamTypeInfo;
+}
+
+class Return<string Value, list<string> Conditions = []> {
+ string value = Value;
+ list<string> conditions = Conditions;
+}
+
+class ShouldCheckHandle<Param P> {
+ bit ret = !and(P.IsHandle, !eq(!and(PARAM_OPTIONAL, P.flags), 0));
+}
+
+class ShouldCheckPointer<Param P> {
+ bit ret = !and(P.IsPointer, !eq(!and(PARAM_OPTIONAL, P.flags), 0));
+}
+
+// For a list of returns that contains a specific return code, find and append
+// new conditions to that return
+class AppendConditionsToReturn<list<Return> Returns, string ReturnValue,
+ list<string> Conditions> {
+ list<Return> ret =
+ !foreach(Ret, Returns,
+ !if(!eq(Ret.value, ReturnValue),
+ Return<Ret.value, Ret.conditions#Conditions>, Ret));
+}
+
+// Add null handle checks to a function's return value descriptions
+class AddHandleChecksToReturns<list<Param> Params, list<Return> Returns> {
+ list<string> handle_params =
+ !foreach(P, Params, !if(ShouldCheckHandle<P>.ret, P.name, ""));
+ list<string> handle_params_filt =
+ !filter(param, handle_params, !ne(param, ""));
+ list<string> handle_param_conds =
+ !foreach(handle, handle_params_filt, "`NULL == "#handle#"`");
+
+ // Does the list of returns already contain ERROR_INVALID_NULL_HANDLE?
+ bit returns_has_inv_handle = !foldl(
+ 0, Returns, HasErr, Ret,
+ !or(HasErr, !eq(Ret.value, PREFIX#"_RESULT_ERROR_INVALID_NULL_HANDLE")));
+
+ list<Return> returns_out = !if(returns_has_inv_handle,
+ AppendConditionsToReturn<Returns, PREFIX # "_RESULT_ERROR_INVALID_NULL_HANDLE", handle_param_conds>.ret,
+ !listconcat(Returns, [Return<PREFIX # "_RESULT_ERROR_INVALID_NULL_HANDLE", handle_param_conds>])
+ );
+}
+
+// Add null pointer checks to a function's return value descriptions
+class AddPointerChecksToReturns<list<Param> Params, list<Return> Returns> {
+ list<string> ptr_params =
+ !foreach(P, Params, !if(ShouldCheckPointer<P>.ret, P.name, ""));
+ list<string> ptr_params_filt = !filter(param, ptr_params, !ne(param, ""));
+ list<string> ptr_param_conds =
+ !foreach(ptr, ptr_params_filt, "`NULL == "#ptr#"`");
+
+ // Does the list of returns already contain ERROR_INVALID_NULL_POINTER?
+ bit returns_has_inv_ptr = !foldl(
+ 0, Returns, HasErr, Ret,
+ !or(HasErr, !eq(Ret.value, PREFIX#"_RESULT_ERROR_INVALID_NULL_POINTER")));
+ list<Return> returns_out = !if(returns_has_inv_ptr,
+ AppendConditionsToReturn<Returns, PREFIX # "_RESULT_ERROR_INVALID_NULL_POINTER", ptr_param_conds>.ret,
+ !listconcat(Returns, [Return<PREFIX # "_RESULT_ERROR_INVALID_NULL_POINTER", ptr_param_conds>])
+ );
+}
+
+defvar DefaultReturns = [Return<PREFIX#"_RESULT_SUCCESS">,
+ Return<PREFIX#"_RESULT_ERROR_UNINITIALIZED">,
+ Return<PREFIX#"_RESULT_ERROR_DEVICE_LOST">];
+
+class APIObject {
+ string name;
+ string desc;
+}
+
+class Function : APIObject {
+ list<Param> params;
+ list<Return> returns;
+ list<string> details = [];
+ list<string> analogues = [];
+
+ list<Return> returns_with_def = !listconcat(DefaultReturns, returns);
+ list<Return> all_returns = AddPointerChecksToReturns<params,
+ AddHandleChecksToReturns<params, returns_with_def>.returns_out>.returns_out;
+}
+
+class Etor<string Name, string Desc> {
+ string name = Name;
+ string desc = Desc;
+ string tagged_type;
+}
+
+class TaggedEtor<string Name, string Type, string Desc> : Etor<Name, Desc> {
+ let tagged_type = Type;
+}
+
+class Enum : APIObject {
+ // This refers to whether the enumerator descriptions specify a return
+ // type for functions where this enum may be used as an output type. If set,
+ // all Etor values must be TaggedEtor records
+ bit is_typed = 0;
+
+ list<Etor> etors = [];
+}
+
+class StructMember<string Type, string Name, string Desc> {
+ string type = Type;
+ string name = Name;
+ string desc = Desc;
+}
+
+defvar DefaultPropStructMembers =
+ [StructMember<prefix#"_structure_type_t", "stype",
+ "type of this structure">,
+ StructMember<"void*", "pNext", "pointer to extension-specific structure">];
+
+class StructHasInheritedMembers<string BaseClass> {
+ bit ret = !or(!eq(BaseClass, prefix#"_base_properties_t"),
+ !eq(BaseClass, prefix#"_base_desc_t"));
+}
+
+class Struct : APIObject {
+ string base_class = "";
+ list<StructMember> members;
+ list<StructMember> all_members =
+ !if(StructHasInheritedMembers<base_class>.ret,
+ DefaultPropStructMembers, [])#members;
+}
+
+class Typedef : APIObject { string value; }
+
+class FptrTypedef : APIObject {
+ list<Param> params;
+ list<Return> returns;
+}
+
+class Macro : APIObject {
+ string value;
+
+ string condition;
+ string alt_value;
+}
+
+class Handle : APIObject;
diff --git a/offload/new-api/API/Common.td b/offload/new-api/API/Common.td
new file mode 100644
index 00000000000000..d293e4addfef8b
--- /dev/null
+++ b/offload/new-api/API/Common.td
@@ -0,0 +1,75 @@
+def : Macro {
+ let name = "OFFLOAD_APICALL";
+ let desc = "Calling convention for all API functions";
+ let condition = "defined(_WIN32)";
+ let value = "__cdecl";
+ let alt_value = "";
+}
+
+def : Macro {
+ let name = "OFFLOAD_APIEXPORT";
+ let desc = "Microsoft-specific dllexport storage-class attribute";
+ let condition = "defined(_WIN32)";
+ let value = "__declspec(dllexport)";
+ let alt_value = "";
+}
+
+def : Macro {
+ let name = "OFFLOAD_DLLEXPORT";
+ let desc = "Microsoft-specific dllexport storage-class attribute";
+ let condition = "defined(_WIN32)";
+ let value = "__declspec(dllexport)";
+}
+
+def : Macro {
+ let name = "OFFLOAD_DLLEXPORT";
+ let desc = "GCC-specific dllexport storage-class attribute";
+ let condition = "__GNUC__ >= 4";
+ let value = "__attribute__ ((visibility (\"default\")))";
+ let alt_value = "";
+}
+
+def : Typedef {
+ let name = "offload_bool_t";
+ let value = "uint8_t";
+ let desc = "compiler-independent type";
+}
+
+def : Handle {
+ let name = "offload_platform_handle_t";
+ let desc = "Handle of a platform instance";
+}
+
+def : Handle {
+ let name = "offload_device_handle_t";
+ let desc = "Handle of platform's device object";
+}
+
+def : Handle {
+ let name = "offload_context_handle_t";
+ let desc = "Handle of context object";
+}
+
+def : Enum {
+ let name = "offload_result_t";
+ let desc = "Defines Return/Error codes";
+ let etors =[
+ Etor<"SUCCESS", "Success">,
+ Etor<"ERROR_INVALID_VALUE", "Invalid Value">,
+ Etor<"ERROR_INVALID_PLATFORM", "Invalid platform">,
+ Etor<"ERROR_DEVICE_NOT_FOUND", "Device not found">,
+ Etor<"ERROR_INVALID_DEVICE", "Invalid device">,
+ Etor<"ERROR_DEVICE_LOST", "Device hung, reset, was removed, or driver update occurred">,
+ Etor<"ERROR_UNINITIALIZED", "plugin is not initialized or specific entry-point is not implemented">,
+ Etor<"ERROR_OUT_OF_RESOURCES", "Out of resources">,
+ Etor<"ERROR_UNSUPPORTED_VERSION", "[Validation] generic error code for unsupported versions">,
+ Etor<"ERROR_UNSUPPORTED_FEATURE", "[Validation] generic error code for unsupported features">,
+ Etor<"ERROR_INVALID_ARGUMENT", "[Validation] generic error code for invalid arguments">,
+ Etor<"ERROR_INVALID_NULL_HANDLE", "[Validation] handle argument is not valid">,
+ Etor<"ERROR_INVALID_NULL_POINTER", "[Validation] pointer argument may not be nullptr">,
+ Etor<"ERROR_INVALID_SIZE", "[Validation] invalid size or dimensions (e.g., must not be zero, or is out of bounds)">,
+ Etor<"ERROR_INVALID_ENUMERATION", "[Validation] enumerator argument is not valid">,
+ Etor<"ERROR_UNSUPPORTED_ENUMERATION", "[Validation] enumerator argument is not supported by the device">,
+ Etor<"ERROR_UNKNOWN", "Unknown or internal error">
+ ];
+}
\ No newline at end of file
diff --git a/offload/new-api/API/Device.td b/offload/new-api/API/Device.td
new file mode 100644
index 00000000000000..2a330d7ef7c4cb
--- /dev/null
+++ b/offload/new-api/API/Device.td
@@ -0,0 +1,101 @@
+//===-- Device.td - Device definitions for Offload ---------*- tablegen -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains Offload API definitions related to the Device handle
+//
+//===----------------------------------------------------------------------===//
+
+def : Enum {
+ let name = "offload_device_type_t";
+ let desc = "Supported device types";
+ let etors =[
+ Etor<"DEFAULT", "The default device type as preferred by the runtime">,
+ Etor<"ALL", "Devices of all types">,
+ Etor<"GPU", "GPU device type">,
+ Etor<"CPU", "CPU device type">,
+ ];
+}
+
+def : Enum {
+ let name = "offload_device_info_t";
+ let desc = "Supported device info";
+ let is_typed = 1;
+ let etors =[
+ TaggedEtor<"TYPE", "offload_device_type_t", "type of the device">,
+ TaggedEtor<"PLATFORM", "offload_platform_handle_t", "the platform associated with the device">,
+ TaggedEtor<"NAME", "char[]", "Device name">,
+ TaggedEtor<"VENDOR", "char[]", "Device vendor">,
+ TaggedEtor<"DRIVER_VERSION", "char[]", "Driver version">
+ ];
+}
+
+def : Function {
+ let name = "offloadDeviceGet";
+ let desc = "Retrieves devices within a platform";
+ let details = [
+ "Multiple calls to this function will return identical device handles, in the same order.",
+ "The number and order of handles returned from this function can be affected by environment variables that filter devices exposed through API.",
+ "The returned devices are taken a reference of and must be released with a subsequent call to olDeviceRelease.",
+ "The application may call this function from simultaneous threads, the implementation must be thread-safe"
+ ];
+ let params = [
+ Param<"offload_platform_handle_t", "hPlatform", "handle of the platform instance", PARAM_IN>,
+ Param<"offload_device_type_t", "DeviceType", "the type of the devices.", PARAM_IN>,
+ Param<"uint32_t", "NumEntries", "the number of devices to be added to phDevices."
+ "If phDevices is not NULL, then NumEntries should be greater than zero. Otherwise OFFLOAD_RESULT_ERROR_INVALID_SIZE"
+ "will be returned.", PARAM_IN>,
+ RangedParam<"offload_device_handle_t*", "phDevices", "array of handle of devices."
+ "If NumEntries is less than the number of devices available, then platform shall only retrieve that number of devices.", PARAM_OUT_OPTIONAL,
+ Range<"0", "NumEntries">>,
+ Param<"uint32_t*", "pNumDevices", "pointer to the number of devices."
+ "pNumDevices will be updated with the total number of devices available.", PARAM_OUT_OPTIONAL>
+ ];
+ let returns = [
+ Return<"OFFLOAD_RESULT_ERROR_INVALID_SIZE", [
+ "`NumEntries == 0 && phDevices != NULL`"
+ ]>,
+ Return<"OFFLOAD_RESULT_ERROR_INVALID_NULL_POINTER", [
+ "`NumEntries > 0 && phDevices == NULL`"
+ ]>,
+ Return<"OFFLOAD_RESULT_ERROR_INVALID_VALUE">
+ ];
+}
+
+def : Function {
+ let name = "offloadDeviceGetInfo";
+ let desc = "Retrieves various information about device";
+ let details = [
+ "The application may call this function from simultaneous threads.",
+ "The implementation of this function should be lock-free."
+ ];
+ let params = [
+ Param<"offload_device_handle_t", "hDevice", "handle of the device instance", PARAM_IN>,
+ Param<"offload_device_info_t", "propName", "type of the info to retrieve", PARAM_IN>,
+ Param<"size_t", "propSize", "the number of bytes pointed to by pPropValue.", PARAM_IN>,
+ TypeTaggedParam<"void*", "pPropValue", "array of bytes holding the info. If propSize is not equal to or greater than the real "
+ "number of bytes needed to return the info then the OFFLOAD_RESULT_ERROR_INVALID_SIZE error is returned and "
+ "pPropValue is not used.", PARAM_OUT_OPTIONAL, TypeInfo<"propName" , "propSize">>,
+ Param<"size_t*", "pPropSizeRet", "pointer to the actual size in bytes of the queried propName.", PARAM_OUT_OPTIONAL>
+ ];
+ let returns = [
+ Return<"OFFLOAD_RESULT_ERROR_UNSUPPORTED_ENUMERATION", [
+ "If `propName` is not supported by the adapter."
+ ]>,
+ Return<"OFFLOAD_RESULT_ERROR_INVALID_SIZE", [
+ "`propSize == 0 && pPropValue != NULL`",
+ "If `propSize` is less than the real number of bytes needed to return the info."
+ ]>,
+ Return<"OFFLOAD_RESULT_ERROR_INVALID_NULL_POINTER", [
+ "`propSize != 0 && pPropValue == NULL`",
+ "`pPropValue == NULL && pPropSizeRet == NULL`"
+ ]>,
+ Return<"OFFLOAD_RESULT_ERROR_INVALID_DEVICE">,
+ Return<"OFFLOAD_RESULT_ERROR_OUT_OF_RESOURCES">,
+ Return<"OFFLOAD_RESULT_ERROR_OUT_OF_HOST_MEMORY">
+ ];
+}
diff --git a/offload/new-api/API/OffloadAPI.td b/offload/new-api/API/OffloadAPI.td
new file mode 100644
index 00000000000000..8a0c3c40581223
--- /dev/null
+++ b/offload/new-api/API/OffloadAPI.td
@@ -0,0 +1,15 @@
+//===-- OffloadAPI.td - Root tablegen file for Offload -----*- tablegen -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Always include this file first
+include "APIDefs.td"
+
+// Add API definition files here
+include "Common.td"
+include "Platform.td"
+include "Device.td"
diff --git a/offload/new-api/API/Platform.td b/offload/new-api/API/Platform.td
new file mode 100644
index 00000000000000..71af04bb831998
--- /dev/null
+++ b/offload/new-api/API/Platform.td
@@ -0,0 +1,94 @@
+//===-- Platform.td - Platform definitions for Offload -----*- tablegen -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains Offload API definitions related to the Platform handle
+//
+//===----------------------------------------------------------------------===//
+def : Function {
+ let name = "offloadPlatformGet";
+ let desc = "Retrieves all available platforms";
+ let details = [
+ "Multiple calls to this function will return identical platforms handles, in the same order.",
+ ];
+ let params = [
+ Param<"uint32_t", "NumEntries",
+ "The number of platforms to be added to phPlatforms. If phPlatforms is not NULL, then"
+ "NumEntries should be greater than zero, otherwise OFFLOAD_RESULT_ERROR_INVALID_SIZE"
+ "will be returned.", PARAM_IN>,
+ RangedParam<"offload_platform_handle_t*", "phPlatforms",
+ "Array of handle of platforms. If NumEntries is"
+ "less than the number of platforms available, then offloadPlatformGet"
+ "shall only retrieve that number of platforms.",
+ PARAM_OUT_OPTIONAL, Range<"0", "NumEntries">>,
+ Param<"uint32_t*",
+ "pNumPlatforms", "returns the total number of platforms available.",
+ PARAM_OUT_OPTIONAL>
+ ];
+ let returns = [
+ Return<"OFFLOAD_RESULT_ERROR_INVALID_SIZE", [
+ "`NumEntries == 0 && phPlatforms != NULL`"
+ ]>
+ ];
+}
+
+def : Enum {
+ let name = "offload_platform_info_t";
+ let desc = "Supported platform info";
+ let is_typed = 1;
+ let etors = [
+ TaggedEtor<"NAME", "char[]", "The string denoting name of the platform. The size of the info needs to be dynamically queried.">,
+ TaggedEtor<"VENDOR_NAME", "char[]","The string denoting name of the vendor of the platform. The size of the info needs to be dynamically queried.">,
+ TaggedEtor<"VERSION", "char[]", "The string denoting the version of the platform. The size of the info needs to be dynamically queried.">,
+ TaggedEtor<"BACKEND", "offload_platform_backend_t", "The backend of the platform. Identifies the native backend adapter implementing this platform.">
+ ];
+}
+
+def : Enum {
+ let name = "offload_platform_backend_t";
+ let desc = "Identifies the native backend of the platform";
+ let etors =[
+ Etor<"UNKNOWN", "The backend is not recognized">,
+ Etor<"CUDA", "The backend is CUDA">,
+ Etor<"AMDGPU", "The backend is AMDGPU">,
+ ];
+}
+
+def : Function {
+ let name = "offloadPlatformGetInfo";
+ let desc = "Retrieves various information about platform";
+ let details = [
+ "The application may call this function from simultaneous threads.",
+ "The implementation of this function should be lock-free."
+ ];
+ let params = [
+ Param<"offload_platform_handle_t", "hPlatform", "handle of the platform", PARAM_IN>,
+ Param<"offload_platform_info_t", "propName", "type of the info to retrieve", PARAM_IN>,
+ Param<"size_t", "propSize", "the number of bytes pointed to by pPlatformInfo.", PARAM_IN>,
+ TypeTaggedParam<"void*", "pPropValue", "array of bytes holding the info."
+ "If Size is not equal to or greater to the real number of bytes needed to return the info"
+ "then the OFFLOAD_RESULT_ERROR_INVALID_SIZE error is returned and pPlatformInfo is not used.", PARAM_OUT_OPTIONAL,
+ TypeInfo<"propName" , "propSize">>,
+ Param<"size_t*", "pPropSizeRet", "pointer to the actual number of bytes being queried by pPlatformInfo.", PARAM_OUT_OPTIONAL>
+ ];
+ let returns = [
+ Return<"OFFLOAD_RESULT_ERROR_UNSUPPORTED_ENUMERATION", [
+ "If `propName` is not supported by the platform."
+ ]>,
+ Return<"OFFLOAD_RESULT_ERROR_INVALID_SIZE", [
+ "`propSize == 0 && pPropValue != NULL`",
+ "If `propSize` is less than the real number of bytes needed to return the info."
+ ]>,
+ Return<"OFFLOAD_RESULT_ERROR_INVALID_NULL_POINTER", [
+ "`propSize != 0 && pPropValue == NULL`",
+ "`pPropValue == NULL && pPropSizeRet == NULL`"
+ ]>,
+ Return<"OFFLOAD_RESULT_ERROR_INVALID_PLATFORM">,
+ Return<"OFFLOAD_RESULT_ERROR_OUT_OF_RESOURCES">,
+ Return<"OFFLOAD_RESULT_ERROR_OUT_OF_HOST_MEMORY">
+ ];
+}
diff --git a/offload/new-api/API/README.md b/offload/new-api/API/README.md
new file mode 100644
index 00000000000000..ab1db704404f35
--- /dev/null
+++ b/offload/new-api/API/README.md
@@ -0,0 +1,150 @@
+# Offload API definitions
+
+**Note**: This is a work-in-progress. It is loosely based on equivalent
+tooling in Unified Runtime.
+
+The Tablegen files in this directory are used to define the Offload API. They
+are used with the `offload-tblgen` tool to generate API headers, print headers,
+and other implementation details.
+
+The root file is `OffloadAPI.td` - additional `.td` files can be included in
+this file to add them to the API.
+
+## API Objects
+The API consists of a number of objects, which always have a *name* field and
+*description* field, and are one of the following types:
+
+### Function
+Represents an API entry point function. Has a list of returns and parameters.
+Also has fields for details (representing a bullet-point list of
+information about the function that would otherwise be too detailed for the
+description), and analogues (equivalent functions in other APIs).
+
+#### Parameter
+Represents a parameter to a function, has *type*, *name*, and *desc* fields.
+Also has a *flags* field containing flags representing whether the parameter is
+in, out, or optional.
+
+The *type* field is used to infer if the parameter is a pointer or handle type.
+A *handle* type is a pointer to an opaque struct, used to abstract over
+plugin-specific implementation details.
+
+There are two special variants of a *parameter*:
+* **RangedParameter** - Represents a parameter that has a range described by other parameters. Generally these are pointers to an arbitrary number of objects. The range is used for generating validation and printing code. E.g, a range might be between `(0, NumDevices)`
+* **TypeTaggedParameter** - Represents a parameter (usually of `void*` type) that has the type and size of its pointee data described by other function parameters. The type is usually described by a type-tagged enum. This allows functions (e.g. `offloadDeviceGetInfo`) to return data of an arbitrary type.
+
+#### Return
+A return represents a possible return code from the function, and optionally a
+list of conditions in which this value may be returned. The conditions list is
+not expected to be exhaustive. A condition is considered free-form text, but
+if it is wrapped in \`backticks\` then it is treated as literal code
+representing an error condition (e.g. `someParam < 1`). These conditions are
+used to automatically create validation checks by the `offload-tblgen`
+validation generator.
+
+Returns are automatically generated for functions with pointer or handle
+parameters, so API authors do not need to exhaustively add null checks for
+these types of parameters. All functions also get a number of default return
+values automatically.
+
+
+### Struct
+Represents a struct. Contains a list of members, which each have a *type*,
+*name*, and *desc*.
+
+Also optionally takes a *base_class* field. If this is either of the special
+`offload_base_properties_t` or `offload_base_desc_t` structs, then the struct
+will inherit members from those structs. The generated struct does **not** use
+actual C++ inheritance, but instead explicitly has those members copied in,
+which preserves ABI compatibility with C.
+
+### Enum
+Represents a C-style enum. Contains a list of `etor` values, which have a name
+and description.
+
+A `TaggedEtor` record type also exists which addtionally takes a type. This type
+is used when the enum is used as a parameter to a function with a type-tagged
+function parameter (e.g. `offloadDeviceGetInfo`).
+
+All enums automatically get a `<enum_name>_FORCE_UINT32 = 0x7fffffff` value,
+which forces the underlying type to be uint32.
+
+### Handle
+Represents a pointer to an opaque struct, as described in the Parameter section.
+It does not take any extra fields.
+
+### Typedef
+Represents a typedef, contains only a *value* field.
+
+### Macro
+Represents a C preprocessor `#define`. Contains a *value* field. Optionally
+takes a *condition* field, which allows the macro to be conditionally defined,
+and an *alt_value* field, which represents the value if the condition is false.
+
+Macro arguments are presented in the *name* field (e.g. name = `mymacro(arg)`).
+
+While there may seem little point generating a macro from tablegen, doing this
+allows the entire source of the header file to be generated from the tablegen
+files, rather than requiring a mix of C source and tablegen.
+
+## Generation
+
+### API header
+```
+./offload-tblgen -I <path-to-llvm>/offload/API <path-to-llvm>/offload/API/OffloadAPI.td --gen-api
+```
+The comments in the generated header are in Doxygen format, although
+generating documentation from them hasn't been implemented yet.
+
+The entirety of this header is generated by Tablegen, rather than having a predefined header file that includes one or more `.inc` files. This is because this header is expected to be part of the installation and distributed to end-users, so should be self-contained.
+
+### Entry Points
+```
+./offload-tblgen -I <path-to-llvm>/offload/API <path-to-llvm>/offload/API/OffloadAPI.td --gen-entry-points
+```
+These functions form the actual Offload interface, and are wrappers over the
+functions that contain the actual implementation (see
+'Adding a new entry point').
+
+They implement automatically generated validation checks, and tracing of
+function calls with arguments and results. The tracing can be enabled with the
+`OFFLOAD_TRACE` environment variable.
+
+### Implementation function declarations
+```
+./offload-tblgen -I <path-to-llvm>/offload/API <path-to-llvm>/offload/API/OffloadAPI.td --gen-impl-func-decls
+```
+Generates declarations of the implementation of functions of every entry point
+in the API, e.g. `offloadDeviceFoo_impl` for `offloadDeviceFoo`.
+
+### Print header
+```
+./offload-tblgen -I <path-to-llvm>/offload/API <path-to-llvm>/offload/API/OffloadAPI.td --gen-print-header
+```
+This header contains `std::ostream &operator<<(std::ostream&)` definitions for
+various API objects, including function parameters.
+
+As with the API header, it is expected that this header is part of the installed
+package, so it is entirely generated by Tablegen.
+
+For ease of implementation, and since it is not strictly part of the API, this
+is a C++ header file. If a C version is desirable it could be added.
+
+### Future Tablegen backends
+`RecordTypes.hpp` contains wrappers for all of the API object types, which will
+allow more backends to be easily added in future.
+
+## Adding to the API
+
+A new object can be added to the API by adding to one of the existing `.td`
+files. It is also possible to add a new tablegen file to the API by adding it
+to the includes in `OffloadAPI.td`. When the offload target is rebuilt, the
+new definition will be included in the generated files.
+
+### Adding a new entry point
+
+When a new entry point is added (e.g. `offloadDeviceFoo`), the actual entry
+point is automatically generated, which contains validation and tracing code.
+It expects an implementation function (`offloadDeviceFoo_impl`) to be defined,
+which it will call into. The definition of this implementation function should
+be added to `src/offload_impl.cpp`
\ No newline at end of file
diff --git a/offload/new-api/CMakeLists.txt b/offload/new-api/CMakeLists.txt
new file mode 100644
index 00000000000000..c27591d2af3ab3
--- /dev/null
+++ b/offload/new-api/CMakeLists.txt
@@ -0,0 +1,36 @@
+
+set(LLVM_TARGET_DEFINITIONS ${CMAKE_CURRENT_SOURCE_DIR}/API/OffloadAPI.td)
+list(APPEND LLVM_TABLEGEN_FLAGS -I ${CMAKE_CURRENT_SOURCE_DIR}/API)
+
+tablegen(OFFLOAD offload_api.h -gen-api)
+tablegen(OFFLOAD offload_funcs.inc -gen-func-names)
+tablegen(OFFLOAD offload_impl_func_decls.inc -gen-impl-func-decls)
+tablegen(OFFLOAD offload_entry_points.inc -gen-entry-points)
+tablegen(OFFLOAD offload_print.hpp -gen-print-header)
+
+
+add_public_tablegen_target(OffloadHeaderGen)
+
+add_llvm_library(offload_new SHARED
+ src/offload_lib.cpp
+ src/offload_impl.cpp
+ DEPENDS OffloadHeaderGen
+ LINK_LIBS omptarget omptarget.rtl.cuda omptarget.rtl.amdgpu)
+
+target_include_directories(offload_new PUBLIC
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}/../include
+ ${CMAKE_CURRENT_SOURCE_DIR}/../include
+ ${CMAKE_CURRENT_SOURCE_DIR}/../plugins-nextgen/common/include)
+
+# foreach(plugin IN LISTS LIBOMPTARGET_PLUGINS_TO_BUILD)
+# target_link_libraries(offload_new PRIVATE omptarget.rtl.${plugin})
+# endforeach()
+
+set_target_properties(offload_new PROPERTIES
+ POSITION_INDEPENDENT_CODE ON
+ INSTALL_RPATH "$ORIGIN"
+ BUILD_RPATH "$ORIGIN:${CMAKE_CURRENT_BINARY_DIR}/..")
+install(TARGETS offload_new LIBRARY COMPONENT offload_new DESTINATION "${OFFLOAD_INSTALL_LIBDIR}")
+
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/offload_api.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include/offload)
diff --git a/offload/new-api/README.md b/offload/new-api/README.md
new file mode 100644
index 00000000000000..95c9bf54d7badd
--- /dev/null
+++ b/offload/new-api/README.md
@@ -0,0 +1,8 @@
+# Offload New API
+
+This directory contains the implementation of the experimental work-in-progress
+new API for Offload. It builds on top of the existing plugin implementations but
+provides a single level of abstraction suitable for runtimes for languages other
+than OpenMP to be built on top of.
+
+See the [API definition readme](API/README.md) for implementation details.
\ No newline at end of file
diff --git a/offload/new-api/src/helpers.hpp b/offload/new-api/src/helpers.hpp
new file mode 100644
index 00000000000000..66148d9f210083
--- /dev/null
+++ b/offload/new-api/src/helpers.hpp
@@ -0,0 +1,96 @@
+//===- helpers.hpp- GetInfo return helpers for the new LLVM/Offload API ---===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// The getInfo*/ReturnHelper facilities provide shortcut way of writing return
+// data + size for the various getInfo APIs. Based on the equivalent
+// implementations in Unified Runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#include "offload_api.h"
+
+#include <cstring>
+
+
+template <typename T, typename Assign>
+offload_result_t getInfoImpl(size_t ParamValueSize, void *ParamValue,
+ size_t *ParamValueSizeRet, T Value,
+ size_t ValueSize, Assign &&AssignFunc) {
+ if (!ParamValue && !ParamValueSizeRet) {
+ return OFFLOAD_RESULT_ERROR_INVALID_NULL_POINTER;
+ }
+
+ if (ParamValue != nullptr) {
+ if (ParamValueSize < ValueSize) {
+ return OFFLOAD_RESULT_ERROR_INVALID_SIZE;
+ }
+ AssignFunc(ParamValue, Value, ValueSize);
+ }
+
+ if (ParamValueSizeRet != nullptr) {
+ *ParamValueSizeRet = ValueSize;
+ }
+
+ return OFFLOAD_RESULT_SUCCESS;
+}
+
+template <typename T>
+offload_result_t getInfo(size_t ParamValueSize, void *ParamValue,
+ size_t *ParamValueSizeRet, T Value) {
+ auto Assignment = [](void *ParamValue, T Value, size_t) {
+ *static_cast<T *>(ParamValue) = Value;
+ };
+
+ return getInfoImpl(ParamValueSize, ParamValue, ParamValueSizeRet, Value,
+ sizeof(T), Assignment);
+}
+
+template <typename T>
+offload_result_t getInfoArray(size_t array_length, size_t ParamValueSize,
+ void *ParamValue, size_t *ParamValueSizeRet,
+ const T *Value) {
+ return getInfoImpl(ParamValueSize, ParamValue, ParamValueSizeRet, Value,
+ array_length * sizeof(T), memcpy);
+}
+
+template <>
+inline offload_result_t
+getInfo<const char *>(size_t ParamValueSize, void *ParamValue,
+ size_t *ParamValueSizeRet, const char *Value) {
+ return getInfoArray(strlen(Value) + 1, ParamValueSize, ParamValue,
+ ParamValueSizeRet, Value);
+}
+
+class ReturnHelper {
+public:
+ ReturnHelper(size_t ParamValueSize, void *ParamValue,
+ size_t *ParamValueSizeRet)
+ : ParamValueSize(ParamValueSize), ParamValue(ParamValue),
+ ParamValueSizeRet(ParamValueSizeRet) {}
+
+ // A version where in/out info size is represented by a single pointer
+ // to a value which is updated on return
+ ReturnHelper(size_t *ParamValueSize, void *ParamValue)
+ : ParamValueSize(*ParamValueSize), ParamValue(ParamValue),
+ ParamValueSizeRet(ParamValueSize) {}
+
+ // Scalar return Value
+ template <class T> offload_result_t operator()(const T &t) {
+ return getInfo(ParamValueSize, ParamValue, ParamValueSizeRet, t);
+ }
+
+ // Array return Value
+ template <class T> offload_result_t operator()(const T *t, size_t s) {
+ return getInfoArray(s, ParamValueSize, ParamValue, ParamValueSizeRet, t);
+ }
+
+protected:
+ size_t ParamValueSize;
+ void *ParamValue;
+ size_t *ParamValueSizeRet;
+};
diff --git a/offload/new-api/src/offload_impl.cpp b/offload/new-api/src/offload_impl.cpp
new file mode 100644
index 00000000000000..165f2264ef7daa
--- /dev/null
+++ b/offload/new-api/src/offload_impl.cpp
@@ -0,0 +1,187 @@
+//===- offload_impl.cpp - Implementation of the new LLVM/Offload API ------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This contains the definitions of the new LLVM/Offload API entry points. See
+// new-api/API/README.md for more information.
+//
+//===----------------------------------------------------------------------===//
+
+#include "PluginManager.h"
+#include "helpers.hpp"
+#include "llvm/Support/FormatVariadic.h"
+#include <offload_api.h>
+
+#include <mutex>
+
+using namespace llvm;
+using namespace llvm::omp::target::plugin;
+
+// Handle type definitions. Ideally these would be 1:1 with the plugins
+struct offload_device_handle_t_ {
+ int DeviceNum;
+ GenericDeviceTy &Device;
+};
+
+struct offload_platform_handle_t_ {
+ std::unique_ptr<GenericPluginTy> Plugin;
+ std::vector<offload_device_handle_t_> Devices;
+};
+
+static std::vector<offload_platform_handle_t_> Platforms;
+
+// Every plugin exports this method to create an instance of the plugin type.
+#define PLUGIN_TARGET(Name) extern "C" GenericPluginTy *createPlugin_##Name();
+#include "Shared/Targets.def"
+
+void initPlugins() {
+ // Attempt to create an instance of each supported plugin.
+#define PLUGIN_TARGET(Name) \
+ do { \
+ Platforms.emplace_back(offload_platform_handle_t_{ \
+ std::unique_ptr<GenericPluginTy>(createPlugin_##Name()), {}}); \
+ } while (false);
+#include "Shared/Targets.def"
+
+ // Preemptively initialize all devices in the plugin so we can just return
+ // them from deviceGet
+ for (auto &Platform : Platforms) {
+ auto Err = Platform.Plugin->init();
+ [[maybe_unused]] std::string InfoMsg = toString(std::move(Err));
+ for (auto DevNum = 0; DevNum < Platform.Plugin->number_of_devices();
+ DevNum++) {
+ if (Platform.Plugin->init_device(DevNum) == OFFLOAD_SUCCESS) {
+ Platform.Devices.emplace_back(offload_device_handle_t_{
+ DevNum, Platform.Plugin->getDevice(DevNum)});
+ }
+ }
+ }
+}
+
+offload_result_t offloadPlatformGet_impl(uint32_t NumEntries,
+ offload_platform_handle_t *phPlatforms,
+ uint32_t *pNumPlatforms) {
+ // It is expected that offloadPlatformGet is the first function to be called.
+ // In future it may make sense to have a specific entry point for Offload
+ // initialization, or expose explicit initialization of plugins.
+ static std::once_flag InitFlag;
+ std::call_once(InitFlag, initPlugins);
+
+ if (NumEntries > Platforms.size()) {
+ return OFFLOAD_RESULT_ERROR_INVALID_SIZE;
+ }
+
+ if (phPlatforms) {
+ for (uint32_t PlatformIndex = 0; PlatformIndex < NumEntries;
+ PlatformIndex++) {
+ phPlatforms[PlatformIndex] = &Platforms[PlatformIndex];
+ }
+ }
+
+ if (pNumPlatforms) {
+ *pNumPlatforms = Platforms.size();
+ }
+
+ return OFFLOAD_RESULT_SUCCESS;
+}
+
+offload_result_t
+offloadPlatformGetInfo_impl(offload_platform_handle_t hPlatform,
+ offload_platform_info_t propName, size_t propSize,
+ void *pPropValue, size_t *pPropSizeRet) {
+ ReturnHelper ReturnValue(propSize, pPropValue, pPropSizeRet);
+
+ switch (propName) {
+ case OFFLOAD_PLATFORM_INFO_NAME:
+ return ReturnValue(hPlatform->Plugin->getName());
+ case OFFLOAD_PLATFORM_INFO_VENDOR_NAME:
+ // TODO: Implement this
+ return ReturnValue("Unknown platform vendor");
+ case OFFLOAD_PLATFORM_INFO_VERSION: {
+ // TODO: Implement this
+ return ReturnValue("v0.0.0");
+ }
+ case OFFLOAD_PLATFORM_INFO_BACKEND: {
+ auto PluginName = hPlatform->Plugin->getName();
+ if (PluginName == StringRef("CUDA")) {
+ return ReturnValue(OFFLOAD_PLATFORM_BACKEND_CUDA);
+ } else if (PluginName == StringRef("AMDGPU")) {
+ return ReturnValue(OFFLOAD_PLATFORM_BACKEND_AMDGPU);
+ } else {
+ return ReturnValue(OFFLOAD_PLATFORM_BACKEND_UNKNOWN);
+ }
+ }
+ default:
+ return OFFLOAD_RESULT_ERROR_INVALID_ENUMERATION;
+ }
+
+ return OFFLOAD_RESULT_SUCCESS;
+}
+
+offload_result_t offloadDeviceGet_impl(offload_platform_handle_t hPlatform,
+ offload_device_type_t,
+ uint32_t NumEntries,
+ offload_device_handle_t *phDevices,
+ uint32_t *pNumDevices) {
+
+ if (phDevices) {
+ for (uint32_t DeviceIndex = 0; DeviceIndex < NumEntries; DeviceIndex++) {
+ phDevices[DeviceIndex] = &(hPlatform->Devices[DeviceIndex]);
+ }
+ }
+
+ if (pNumDevices) {
+ *pNumDevices = static_cast<uint32_t>(hPlatform->Devices.size());
+ }
+
+ return OFFLOAD_RESULT_SUCCESS;
+}
+
+offload_result_t offloadDeviceGetInfo_impl(offload_device_handle_t hDevice,
+ offload_device_info_t propName,
+ size_t propSize, void *pPropValue,
+ size_t *pPropSizeRet) {
+
+ ReturnHelper ReturnValue(propSize, pPropValue, pPropSizeRet);
+
+ InfoQueueTy DevInfo;
+ if (auto Err = hDevice->Device.obtainInfoImpl(DevInfo))
+ return OFFLOAD_RESULT_ERROR_OUT_OF_RESOURCES;
+
+ // Find the info if it exists under any of the given names
+ auto GetInfo = [&DevInfo](std::vector<std::string> Names) {
+ for (auto Name : Names) {
+ auto InfoKeyMatches = [&](const InfoQueueTy::InfoQueueEntryTy &info) {
+ return info.Key == Name;
+ };
+ auto Item = std::find_if(DevInfo.getQueue().begin(),
+ DevInfo.getQueue().end(), InfoKeyMatches);
+
+ if (Item != std::end(DevInfo.getQueue())) {
+ return Item->Value;
+ }
+ }
+
+ return std::string("");
+ };
+
+ switch (propName) {
+ case OFFLOAD_DEVICE_INFO_TYPE:
+ return ReturnValue(OFFLOAD_DEVICE_TYPE_GPU);
+ case OFFLOAD_DEVICE_INFO_NAME:
+ return ReturnValue(GetInfo({"Device Name"}).c_str());
+ case OFFLOAD_DEVICE_INFO_VENDOR:
+ return ReturnValue(GetInfo({"Vendor Name"}).c_str());
+ case OFFLOAD_DEVICE_INFO_DRIVER_VERSION:
+ return ReturnValue(
+ GetInfo({"CUDA Driver Version", "HSA Runtime Version"}).c_str());
+ default:
+ return OFFLOAD_RESULT_ERROR_UNSUPPORTED_ENUMERATION;
+ }
+
+ return OFFLOAD_RESULT_SUCCESS;
+}
diff --git a/offload/new-api/src/offload_lib.cpp b/offload/new-api/src/offload_lib.cpp
new file mode 100644
index 00000000000000..b2452b2cdab950
--- /dev/null
+++ b/offload/new-api/src/offload_lib.cpp
@@ -0,0 +1,23 @@
+//===- offload_lib.cpp - Entry points for the new LLVM/Offload API --------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file pulls in the tablegen'd API entry point functions.
+//
+//===----------------------------------------------------------------------===//
+
+#include <offload_api.h>
+#include <offload_print.hpp>
+
+#include <iostream>
+
+// Pull in the declarations for the implementation funtions. The actual entry
+// points in this file wrap these.
+#include "offload_impl_func_decls.inc"
+
+// Pull in the tablegen'd entry point definitions.
+#include "offload_entry_points.inc"
diff --git a/offload/plugins-nextgen/common/include/PluginInterface.h b/offload/plugins-nextgen/common/include/PluginInterface.h
index 41cc0f286a581f..4cfe6cbf9ffe7f 100644
--- a/offload/plugins-nextgen/common/include/PluginInterface.h
+++ b/offload/plugins-nextgen/common/include/PluginInterface.h
@@ -124,6 +124,7 @@ enum InfoLevelKind { InfoLevel1 = 1, InfoLevel2, InfoLevel3 };
/// we use the level to determine the indentation of the key-value property at
/// printing time. See the enum InfoLevelKind for the list of accepted levels.
class InfoQueueTy {
+public:
struct InfoQueueEntryTy {
std::string Key;
std::string Value;
@@ -131,6 +132,8 @@ class InfoQueueTy {
uint64_t Level;
};
+
+private:
std::deque<InfoQueueEntryTy> Queue;
public:
@@ -153,6 +156,10 @@ class InfoQueueTy {
Queue.push_back({Key, Value, Units, L});
}
+ const std::deque<InfoQueueEntryTy> &getQueue() const {
+ return Queue;
+ }
+
/// Print all info entries added to the queue.
void print() const {
// We print four spances for each level.
diff --git a/offload/tools/offload-tblgen/APIGen.cpp b/offload/tools/offload-tblgen/APIGen.cpp
new file mode 100644
index 00000000000000..6fdf3025f25bb5
--- /dev/null
+++ b/offload/tools/offload-tblgen/APIGen.cpp
@@ -0,0 +1,196 @@
+//===- offload-tblgen/APIGen.cpp - Tablegen backend for Offload header ----===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This is a Tablegen backend that produces the contents of the Offload API
+// header. The generated comments are Doxygen compatible.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/TableGen/Record.h"
+#include "llvm/TableGen/TableGenBackend.h"
+
+#include "GenCommon.hpp"
+#include "RecordTypes.hpp"
+
+using namespace llvm;
+using namespace offload::tblgen;
+
+// Produce a possibly multi-line comment from the input string
+static std::string MakeComment(StringRef in) {
+ std::string out = "";
+ size_t LineStart = 0;
+ size_t LineBreak = 0;
+ while (LineBreak < in.size()) {
+ LineBreak = in.find_first_of("\n", LineStart);
+ if (LineBreak - LineStart <= 1) {
+ break;
+ }
+ out += std::string("\t///< ") +
+ in.substr(LineStart, LineBreak - LineStart).str() + "\n";
+ LineStart = LineBreak + 1;
+ }
+
+ return out;
+}
+
+static void ProcessHandle(const HandleRec &H, raw_ostream &OS) {
+ OS << CommentsHeader;
+ OS << formatv("/// @brief {0}\n", H.getDesc());
+ OS << formatv("typedef struct {0}_ *{0};\n", H.getName());
+}
+
+static void ProcessTypedef(const TypedefRec &T, raw_ostream &OS) {
+ OS << CommentsHeader;
+ OS << formatv("/// @brief {0}\n", T.getDesc());
+ OS << formatv("typedef {0} {1};\n", T.getValue(), T.getName());
+}
+
+static void ProcessMacro(const MacroRec &M, raw_ostream &OS) {
+ OS << CommentsHeader;
+ OS << formatv("#ifndef {0}\n", M.getName());
+ if (auto Condition = M.getCondition()) {
+ OS << formatv("#if {0}\n", *Condition);
+ }
+ OS << "/// @brief " << M.getDesc() << "\n";
+ OS << formatv("#define {0} {1}\n", M.getNameWithArgs(), M.getValue());
+ if (auto AltValue = M.getAltValue()) {
+ OS << "#else\n";
+ OS << formatv("#define {0} {1}\n", M.getNameWithArgs(), *AltValue);
+ }
+ if (auto Condition = M.getCondition()) {
+ OS << formatv("#endif // {0}\n", *Condition);
+ }
+ OS << formatv("#endif // {0}\n", M.getName());
+}
+
+static void ProcessFunction(const FunctionRec &F, raw_ostream &OS) {
+ OS << CommentsHeader;
+ OS << formatv("/// @brief {0}\n", F.getDesc());
+ OS << CommentsBreak;
+
+ OS << "/// @details\n";
+ for (auto &Detail : F.getDetails()) {
+ OS << formatv("/// - {0}\n", Detail);
+ }
+ OS << CommentsBreak;
+
+ // Emit analogue remarks
+ auto Analogues = F.getAnalogues();
+ if (!Analogues.empty()) {
+ OS << "/// @remarks\n/// _Analogues_\n";
+ for (auto &Analogue : Analogues) {
+ OS << formatv("/// - **{0}**\n", Analogue);
+ }
+ OS << CommentsBreak;
+ }
+
+ OS << "/// @returns\n";
+ auto Returns = F.getReturns();
+ for (auto &Ret : Returns) {
+ OS << formatv("/// - ::{0}\n", Ret.getValue());
+ auto RetConditions = Ret.getConditions();
+ for (auto &RetCondition : RetConditions) {
+ OS << formatv("/// + {0}\n", RetCondition);
+ }
+ }
+
+ OS << formatv("{0}_APIEXPORT {1}_result_t {0}_APICALL ", PrefixUpper,
+ PrefixLower);
+ OS << F.getName();
+ OS << "(\n";
+ auto Params = F.getParams();
+ for (auto &Param : Params) {
+ OS << " " << Param.getType() << " " << Param.getName();
+ if (Param != Params.back()) {
+ OS << ", ";
+ } else {
+ OS << " ";
+ }
+ OS << MakeParamComment(Param) << "\n";
+ }
+ OS << ");\n\n";
+}
+
+static void ProcessEnum(const EnumRec &Enum, raw_ostream &OS) {
+ OS << CommentsHeader;
+ OS << formatv("/// @brief {0}\n", Enum.getDesc());
+ OS << formatv("typedef enum {0} {{\n", Enum.getName());
+
+ uint32_t EtorVal = 0;
+ for (const auto &EnumVal : Enum.getValues()) {
+ auto Desc = MakeComment(EnumVal.getDesc());
+ OS << formatv(TAB_1 "{0}_{1} = {2}, {3}", Enum.getEnumValNamePrefix(),
+ EnumVal.getName(), EtorVal++, Desc);
+ }
+
+ // Add force uint32 val
+ OS << formatv(TAB_1 "/// @cond\n" TAB_1
+ "{0}_FORCE_UINT32 = 0x7fffffff\n" TAB_1
+ "/// @endcond\n\n",
+ Enum.getEnumValNamePrefix());
+
+ OS << formatv("} {0};\n", Enum.getName());
+}
+
+static void ProcessStruct(const StructRec &Struct, raw_ostream &OS) {
+ OS << CommentsHeader;
+ OS << formatv("/// @brief {0}\n", Struct.getDesc());
+ OS << formatv("typedef struct {0} {{\n", Struct.getName());
+
+ for (const auto &Member : Struct.getMembers()) {
+ OS << formatv(TAB_1 "{0} {1}; {2}", Member.getType(), Member.getName(),
+ MakeComment(Member.getDesc()));
+ }
+
+ OS << formatv("} {0};\n\n", Struct.getName());
+}
+
+static void ProcessFuncParamStruct(const FunctionRec &Func, raw_ostream &OS) {
+ auto FuncParamStructBegin = R"(
+///////////////////////////////////////////////////////////////////////////////
+/// @brief Function parameters for {0}
+/// @details Each entry is a pointer to the parameter passed to the function;
+typedef struct {1} {{
+)";
+
+ OS << formatv(FuncParamStructBegin, Func.getName(),
+ Func.getParamStructName());
+ for (const auto &Param : Func.getParams()) {
+ OS << TAB_1 << Param.getType() << "* p" << Param.getName() << ";\n";
+ }
+ OS << formatv("} {0};\n", Func.getParamStructName());
+}
+
+void EmitOffloadAPI(RecordKeeper &Records, raw_ostream &OS) {
+ OS << FileHeader;
+ // Generate main API definitions
+ for (auto *R : Records.getAllDerivedDefinitions("APIObject")) {
+ if (R->isSubClassOf("Macro")) {
+ ProcessMacro(MacroRec{R}, OS);
+ } else if (R->isSubClassOf("Typedef")) {
+ ProcessTypedef(TypedefRec{R}, OS);
+ } else if (R->isSubClassOf("Handle")) {
+ ProcessHandle(HandleRec{R}, OS);
+ } else if (R->isSubClassOf("Function")) {
+ ProcessFunction(FunctionRec{R}, OS);
+ } else if (R->isSubClassOf("Enum")) {
+ ProcessEnum(EnumRec{R}, OS);
+ } else if (R->isSubClassOf("Struct")) {
+ ProcessStruct(StructRec{R}, OS);
+ }
+ }
+
+ // Generate auxiliary definitions (func param structs etc)
+ for (auto *R : Records.getAllDerivedDefinitions("Function")) {
+ ProcessFuncParamStruct(FunctionRec{R}, OS);
+ }
+
+ OS << FileFooter;
+}
diff --git a/offload/tools/offload-tblgen/CMakeLists.txt b/offload/tools/offload-tblgen/CMakeLists.txt
new file mode 100644
index 00000000000000..52986cbbaa9187
--- /dev/null
+++ b/offload/tools/offload-tblgen/CMakeLists.txt
@@ -0,0 +1,24 @@
+##===----------------------------------------------------------------------===##
+#
+# 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
+#
+##===----------------------------------------------------------------------===##
+include(TableGen)
+
+add_tablegen(offload-tblgen OFFLOAD
+ EXPORT OFFLOAD
+ APIGen.cpp
+ EntryPointGen.cpp
+ FuncsGen.cpp
+ GenCommon.hpp
+ Generators.hpp
+ offload-tblgen.cpp
+ PrintGen.cpp
+ RecordTypes.hpp
+ )
+
+set(OFFLOAD_TABLEGEN_EXE "${OFFLOAD_TABLEGEN_EXE}" CACHE INTERNAL "")
+set(OFFLOAD_TABLEGEN_TARGET "${OFFLOAD_TABLEGEN_TARGET}" CACHE INTERNAL "")
+
diff --git a/offload/tools/offload-tblgen/EntryPointGen.cpp b/offload/tools/offload-tblgen/EntryPointGen.cpp
new file mode 100644
index 00000000000000..b89592187639e3
--- /dev/null
+++ b/offload/tools/offload-tblgen/EntryPointGen.cpp
@@ -0,0 +1,86 @@
+//===- offload-tblgen/EntryPointGen.cpp - Tablegen backend for Offload ----===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This is a Tablegen backend that produces the actual entry points for the
+// Offload API. It serves as a place to integrate functionality like tracing
+// and validation before dispatching to the actual implementations.
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/TableGen/Record.h"
+
+#include "GenCommon.hpp"
+#include "RecordTypes.hpp"
+
+using namespace llvm;
+using namespace offload::tblgen;
+
+static void EmitEntryPointFunc(const FunctionRec &F, raw_ostream &OS) {
+ OS << CommentsHeader;
+ // Emit preamble
+ OS << formatv("{1}_APIEXPORT {0}_result_t {1}_APICALL {2}(\n ", PrefixLower,
+ PrefixUpper, F.getName());
+ // Emit arguments
+ std::string ParamNameList = "";
+ for (auto &Param : F.getParams()) {
+ OS << Param.getType() << " " << Param.getName();
+ if (Param != F.getParams().back()) {
+ OS << ", ";
+ }
+ ParamNameList += Param.getName().str() + ", ";
+ }
+ OS << ") {\n";
+
+ OS << TAB_1 "if (true /*enableParameterValidation*/) {\n";
+
+ // Emit validation checks
+ for (const auto &Return : F.getReturns()) {
+ for (auto &Condition : Return.getConditions()) {
+ if (Condition.starts_with("`") && Condition.ends_with("`")) {
+ auto ConditionString = Condition.substr(1, Condition.size() - 2);
+ OS << formatv(TAB_2 "if ({0}) {{\n", ConditionString);
+ OS << formatv(TAB_3 "return {0};\n", Return.getValue());
+ OS << TAB_2 "}\n\n";
+ }
+ }
+ }
+ OS << " }\n\n";
+
+ // Emit pre-call prints
+ OS << TAB_1 "if (std::getenv(\"OFFLOAD_TRACE\")) {\n";
+ OS << formatv(TAB_2 "std::cout << \"---> {0}\";\n", F.getName());
+ OS << TAB_1 "}\n\n";
+
+ // Perform actual function call
+ ParamNameList = ParamNameList.substr(0, ParamNameList.size() - 2);
+ OS << formatv(TAB_1 "{0}_result_t result = {1}_impl({2});\n\n", PrefixLower,
+ F.getName(), ParamNameList);
+
+ // Emit post-call prints
+ OS << TAB_1 "if (std::getenv(\"OFFLOAD_TRACE\")) {\n";
+ OS << formatv(TAB_2 "{0} Params = {{ ", F.getParamStructName());
+ for (const auto &Param : F.getParams()) {
+ OS << "&" << Param.getName();
+ if (Param != F.getParams().back()) {
+ OS << ", ";
+ }
+ }
+ OS << formatv("};\n");
+ OS << TAB_2 "std::cout << \"(\" << &Params << \")\";\n";
+ OS << TAB_2 "std::cout << \"-> \" << result << \"\\n\";\n";
+ OS << TAB_1 "}\n";
+
+ OS << TAB_1 "return result;\n";
+ OS << "}\n";
+}
+
+void EmitOffloadEntryPoints(RecordKeeper &Records, raw_ostream &OS) {
+ for (auto *R : Records.getAllDerivedDefinitions("Function")) {
+ EmitEntryPointFunc(FunctionRec{R}, OS);
+ }
+}
diff --git a/offload/tools/offload-tblgen/FuncsGen.cpp b/offload/tools/offload-tblgen/FuncsGen.cpp
new file mode 100644
index 00000000000000..4dc74047ac49d8
--- /dev/null
+++ b/offload/tools/offload-tblgen/FuncsGen.cpp
@@ -0,0 +1,54 @@
+//===- offload-tblgen/APIGen.cpp - Tablegen backend for Offload validation ===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This is a Tablegen backend that produces validation functions for the Offload
+// API entry point functions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/TableGen/Record.h"
+
+#include "GenCommon.hpp"
+#include "RecordTypes.hpp"
+
+using namespace llvm;
+using namespace offload::tblgen;
+
+// Emit a list of just the API function names
+void EmitOffloadFuncNames(RecordKeeper &Records, raw_ostream &OS) {
+ OS << R"(
+#ifndef OFFLOAD_FUNC
+#error Please define the macro OFFLOAD_FUNC(Function)
+#endif
+
+)";
+ for (auto *R : Records.getAllDerivedDefinitions("Function")) {
+ FunctionRec FR{R};
+ OS << formatv("OFFLOAD_FUNC({0})", FR.getName()) << "\n";
+ }
+
+ OS << "\n#undef OFFLOAD_FUNC\n";
+}
+
+void EmitOffloadImplFuncDecls(RecordKeeper &Records, raw_ostream &OS) {
+ for (auto *R : Records.getAllDerivedDefinitions("Function")) {
+ FunctionRec F{R};
+ OS << formatv("{0}_result_t {1}_impl(", PrefixLower, F.getName());
+ auto Params = F.getParams();
+ for (auto &Param : Params) {
+ OS << Param.getType() << " " << Param.getName();
+ if (Param != Params.back()) {
+ OS << ", ";
+ } else {
+ OS << ");";
+ }
+ }
+ OS << "\n";
+ }
+}
diff --git a/offload/tools/offload-tblgen/GenCommon.hpp b/offload/tools/offload-tblgen/GenCommon.hpp
new file mode 100644
index 00000000000000..fb24ef06f9ec8f
--- /dev/null
+++ b/offload/tools/offload-tblgen/GenCommon.hpp
@@ -0,0 +1,58 @@
+//===- offload-tblgen/GenCommon.cpp - Common defs for Offload generators --===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#pragma once
+
+#include "RecordTypes.hpp"
+#include "llvm/Support/FormatVariadic.h"
+
+// Having inline bits of tabbed code is hard to read, provide some definitions
+// so we can keep things tidier
+#define TAB_1 " "
+#define TAB_2 " "
+#define TAB_3 " "
+#define TAB_4 " "
+#define TAB_5 " "
+
+constexpr auto FileHeader = R"(
+// Auto-generated file, do not manually edit.
+
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+)";
+
+constexpr auto FileFooter = R"(
+#if defined(__cplusplus)
+} // extern "C"
+#endif
+
+)";
+
+constexpr auto CommentsHeader = R"(
+///////////////////////////////////////////////////////////////////////////////
+)";
+
+constexpr auto CommentsBreak = "///\n";
+
+constexpr auto PrefixLower = "offload";
+constexpr auto PrefixUpper = "OFFLOAD";
+
+inline std::string
+MakeParamComment(const llvm::offload::tblgen::ParamRec &Param) {
+ return llvm::formatv("///< {0}{1}{2} {3}", (Param.isIn() ? "[in]" : ""),
+ (Param.isOut() ? "[out]" : ""),
+ (Param.isOpt() ? "[optional]" : ""), Param.getDesc());
+}
+
diff --git a/offload/tools/offload-tblgen/Generators.hpp b/offload/tools/offload-tblgen/Generators.hpp
new file mode 100644
index 00000000000000..4b300f52ddf84d
--- /dev/null
+++ b/offload/tools/offload-tblgen/Generators.hpp
@@ -0,0 +1,18 @@
+//===- offload-tblgen/Generators.hpp - Offload generator declarations -----===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#pragma once
+
+#include "llvm/TableGen/Record.h"
+
+void EmitOffloadAPI(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+void EmitOffloadFuncNames(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+void EmitOffloadImplFuncDecls(llvm::RecordKeeper &Records,
+ llvm::raw_ostream &OS);
+void EmitOffloadEntryPoints(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+void EmitOffloadPrintHeader(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
diff --git a/offload/tools/offload-tblgen/PrintGen.cpp b/offload/tools/offload-tblgen/PrintGen.cpp
new file mode 100644
index 00000000000000..0c47695eca12b6
--- /dev/null
+++ b/offload/tools/offload-tblgen/PrintGen.cpp
@@ -0,0 +1,206 @@
+//===- offload-tblgen/APIGen.cpp - Tablegen backend for Offload printing --===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This is a Tablegen backend that produces print functions for the Offload API
+// entry point functions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/TableGen/Record.h"
+
+#include "GenCommon.hpp"
+#include "RecordTypes.hpp"
+
+using namespace llvm;
+using namespace offload::tblgen;
+
+constexpr auto PrintEnumHeader =
+ R"(///////////////////////////////////////////////////////////////////////////////
+/// @brief Print operator for the {0} type
+/// @returns std::ostream &
+)";
+
+constexpr auto PrintTaggedEnumHeader =
+ R"(///////////////////////////////////////////////////////////////////////////////
+/// @brief Print type-tagged {0} enum value
+/// @returns std::ostream &
+)";
+
+static void ProcessEnum(const EnumRec &Enum, raw_ostream &OS) {
+ OS << formatv(PrintEnumHeader, Enum.getName());
+ OS << formatv(
+ "inline std::ostream &operator<<(std::ostream &os, enum {0} value) "
+ "{{\n" TAB_1 "switch (value) {{\n",
+ Enum.getName());
+
+ for (const auto &Val : Enum.getValues()) {
+ auto Name = Enum.getEnumValNamePrefix() + "_" + Val.getName();
+ OS << formatv(TAB_1 "case {0}:\n", Name);
+ OS << formatv(TAB_2 "os << \"{0}\";\n", Name);
+ OS << formatv(TAB_2 "break;\n");
+ }
+
+ OS << TAB_1 "default:\n" TAB_2 "os << \"unknown enumerator\";\n" TAB_2
+ "break;\n" TAB_1 "}\n" TAB_1 "return os;\n}\n\n";
+
+ if (!Enum.isTyped()) {
+ return;
+ }
+
+ OS << formatv(PrintTaggedEnumHeader, Enum.getName());
+
+ OS << formatv(R"""(template <>
+inline void printTagged(std::ostream &os, const void *ptr, {0} value, size_t size) {{
+ if (ptr == NULL) {{
+ printPtr(os, ptr);
+ return;
+ }
+
+ switch (value) {{
+)""",
+ Enum.getName());
+
+ for (const auto &Val : Enum.getValues()) {
+ auto Name = Enum.getEnumValNamePrefix() + "_" + Val.getName();
+ auto Type = Val.getTaggedType();
+ OS << formatv(TAB_1 "case {0}: {{\n", Name);
+ // Special case for strings
+ if (Type == "char[]") {
+ OS << formatv(TAB_2 "printPtr(os, (const char*) ptr);\n");
+ } else {
+ OS << formatv(TAB_2 "const {0} * const tptr = (const {0} * const)ptr;\n",
+ Type);
+ // TODO: Handle other cases here
+ OS << TAB_2 "os << (const void *)tptr << \" (\";\n";
+ if (Type.ends_with("*")) {
+ OS << TAB_2 "os << printPtr(os, tptr);\n";
+ } else {
+ OS << TAB_2 "os << *tptr;\n";
+ }
+ OS << TAB_2 "os << \")\";\n";
+ }
+ OS << formatv(TAB_2 "break;\n" TAB_1 "}\n");
+ }
+
+ OS << TAB_1 "default:\n" TAB_2 "os << \"unknown enumerator\";\n" TAB_2
+ "break;\n" TAB_1 "}\n";
+
+ OS << "}\n";
+}
+
+static void EmitFunctionParamStructPrint(const FunctionRec &Func,
+ raw_ostream &OS) {
+ OS << formatv(R"(
+inline std::ostream &operator<<(std::ostream &os, [[maybe_unused]] const struct {0} *params) {{
+)",
+ Func.getParamStructName());
+
+ for (const auto &Param : Func.getParams()) {
+ OS << formatv(TAB_1 "os << \".{0} = \";\n", Param.getName());
+ if (auto Range = Param.getRange()) {
+ OS << formatv(TAB_1 "os << \"{{\";\n");
+ OS << formatv(TAB_1 "for (size_t i = {0}; i < *params->p{1}; i++){{\n",
+ Range->first, Range->second);
+ OS << TAB_2 "if (i > 0) {\n";
+ OS << TAB_3 " os << \", \";\n";
+ OS << TAB_2 "}\n";
+ OS << formatv(TAB_2 "printPtr(os, (*params->p{0})[i]);\n",
+ Param.getName());
+ OS << formatv(TAB_1 "}\n");
+ OS << formatv(TAB_1 "os << \"}\";\n");
+ } else if (auto TypeInfo = Param.getTypeInfo()) {
+ OS << formatv(
+ TAB_1
+ "printTagged(os, *params->p{0}, *params->p{1}, *params->p{2});\n",
+ Param.getName(), TypeInfo->first, TypeInfo->second);
+ } else if (Param.isPointerType() || Param.isHandleType()) {
+ OS << formatv(TAB_1 "printPtr(os, *params->p{0});\n", Param.getName());
+ } else {
+ OS << formatv(TAB_1 "os << *params->p{0};\n", Param.getName());
+ }
+ if (Param != Func.getParams().back()) {
+ OS << TAB_1 "os << \", \";\n";
+ }
+ }
+
+ OS << TAB_1 "return os;\n}\n";
+}
+
+void EmitOffloadPrintHeader(RecordKeeper &Records, raw_ostream &OS) {
+ OS << R"""(
+// Auto-generated file, do not manually edit.
+
+#pragma once
+
+#include <offload_api.h>
+#include <ostream>
+
+
+template <typename T> inline offload_result_t printPtr(std::ostream &os, const T *ptr);
+template <typename T> inline void printTagged(std::ostream &os, const void *ptr, T value, size_t size);
+)""";
+
+ // ==========
+ OS << "template <typename T> struct is_handle : std::false_type {};\n";
+ for (auto *R : Records.getAllDerivedDefinitions("Handle")) {
+ HandleRec H{R};
+ OS << formatv("template <> struct is_handle<{0}> : std::true_type {{};\n",
+ H.getName());
+ }
+ OS << "template <typename T> inline constexpr bool is_handle_v = "
+ "is_handle<T>::value;\n";
+ // =========
+
+ // Forward declare the operator<< overloads so their implementations can
+ // use each other.
+ OS << "\n";
+ for (auto *R : Records.getAllDerivedDefinitions("Enum")) {
+ OS << formatv(
+ "inline std::ostream &operator<<(std::ostream &os, enum {0} value);\n",
+ EnumRec{R}.getName());
+ }
+ OS << "\n";
+
+ // Create definitions
+ for (auto *R : Records.getAllDerivedDefinitions("Enum")) {
+ EnumRec E{R};
+ ProcessEnum(E, OS);
+ }
+
+ // Emit print functions for the function param structs
+ for (auto *R : Records.getAllDerivedDefinitions("Function")) {
+ EmitFunctionParamStructPrint(FunctionRec{R}, OS);
+ }
+
+ OS << R"""(
+///////////////////////////////////////////////////////////////////////////////
+// @brief Print pointer value
+template <typename T> inline offload_result_t printPtr(std::ostream &os, const T *ptr) {
+ if (ptr == nullptr) {
+ os << "nullptr";
+ } else if constexpr (std::is_pointer_v<T>) {
+ os << (const void *)(ptr) << " (";
+ printPtr(os, *ptr);
+ os << ")";
+ } else if constexpr (std::is_void_v<T> || is_handle_v<T *>) {
+ os << (const void *)ptr;
+ } else if constexpr (std::is_same_v<std::remove_cv_t< T >, char>) {
+ os << (const void *)(ptr) << " (";
+ os << ptr;
+ os << ")";
+ } else {
+ os << (const void *)(ptr) << " (";
+ os << *ptr;
+ os << ")";
+ }
+
+ return OFFLOAD_RESULT_SUCCESS;
+}
+ )""";
+}
diff --git a/offload/tools/offload-tblgen/RecordTypes.hpp b/offload/tools/offload-tblgen/RecordTypes.hpp
new file mode 100644
index 00000000000000..ccd45d472cf770
--- /dev/null
+++ b/offload/tools/offload-tblgen/RecordTypes.hpp
@@ -0,0 +1,227 @@
+//===- offload-tblgen/RecordTypes.cpp - Offload record type wrappers -----===-//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#pragma once
+
+#include <string>
+
+#include "llvm/TableGen/Record.h"
+
+namespace llvm {
+namespace offload {
+namespace tblgen {
+
+class HandleRec {
+public:
+ explicit HandleRec(Record *rec) : rec(rec) {}
+ StringRef getName() const { return rec->getValueAsString("name"); }
+ StringRef getDesc() const { return rec->getValueAsString("desc"); }
+
+private:
+ Record *rec;
+};
+
+class MacroRec {
+public:
+ explicit MacroRec(Record *rec) : rec(rec) {
+ auto Name = rec->getValueAsString("name");
+ auto OpenBrace = Name.find_first_of("(");
+ nameWithoutArgs = Name.substr(0, OpenBrace);
+ }
+ StringRef getName() const { return nameWithoutArgs; }
+ StringRef getNameWithArgs() const { return rec->getValueAsString("name"); }
+ StringRef getDesc() const { return rec->getValueAsString("desc"); }
+
+ std::optional<StringRef> getCondition() const {
+ return rec->getValueAsOptionalString("condition");
+ }
+ StringRef getValue() const { return rec->getValueAsString("value"); }
+ std::optional<StringRef> getAltValue() const {
+ return rec->getValueAsOptionalString("alt_value");
+ }
+
+private:
+ Record *rec;
+ std::string nameWithoutArgs;
+};
+
+class TypedefRec {
+public:
+ explicit TypedefRec(Record *rec) : rec(rec) {}
+ StringRef getName() const { return rec->getValueAsString("name"); }
+ StringRef getDesc() const { return rec->getValueAsString("desc"); }
+ StringRef getValue() const { return rec->getValueAsString("value"); }
+
+private:
+ Record *rec;
+};
+
+class EnumValueRec {
+public:
+ explicit EnumValueRec(Record *rec) : rec(rec) {}
+ std::string getName() const { return rec->getValueAsString("name").upper(); }
+ StringRef getDesc() const { return rec->getValueAsString("desc"); }
+ StringRef getTaggedType() const {
+ return rec->getValueAsString("tagged_type");
+ }
+
+private:
+ Record *rec;
+};
+
+class EnumRec {
+public:
+ explicit EnumRec(Record *rec) : rec(rec) {
+ for (auto *Val : rec->getValueAsListOfDefs("etors")) {
+ vals.emplace_back(EnumValueRec{Val});
+ }
+ }
+ StringRef getName() const { return rec->getValueAsString("name"); }
+ StringRef getDesc() const { return rec->getValueAsString("desc"); }
+ const std::vector<EnumValueRec> &getValues() const { return vals; }
+
+ std::string getEnumValNamePrefix() const {
+ return StringRef(getName().str().substr(0, getName().str().length() - 2))
+ .upper();
+ }
+
+ bool isTyped() const { return rec->getValueAsBit("is_typed"); }
+
+private:
+ Record *rec;
+ std::vector<EnumValueRec> vals;
+};
+
+class StructMemberRec {
+public:
+ explicit StructMemberRec(Record *rec) : rec(rec) {}
+ StringRef getType() const { return rec->getValueAsString("type"); }
+ StringRef getName() const { return rec->getValueAsString("name"); }
+ StringRef getDesc() const { return rec->getValueAsString("desc"); }
+
+private:
+ Record *rec;
+};
+
+class StructRec {
+public:
+ explicit StructRec(Record *rec) : rec(rec) {
+ for (auto *Member : rec->getValueAsListOfDefs("all_members")) {
+ members.emplace_back(StructMemberRec(Member));
+ }
+ }
+ StringRef getName() const { return rec->getValueAsString("name"); }
+ StringRef getDesc() const { return rec->getValueAsString("desc"); }
+ std::optional<StringRef> getBaseClass() const {
+ return rec->getValueAsOptionalString("base_class");
+ }
+ const std::vector<StructMemberRec> &getMembers() const { return members; }
+
+private:
+ Record *rec;
+ std::vector<StructMemberRec> members;
+};
+
+class ParamRec {
+public:
+ explicit ParamRec(Record *rec) : rec(rec) {
+ flags = rec->getValueAsBitsInit("flags");
+ auto *Range = rec->getValueAsDef("range");
+ auto RangeBegin = Range->getValueAsString("begin");
+ auto RangeEnd = Range->getValueAsString("end");
+ if (RangeBegin != "" && RangeEnd != "") {
+ range = {RangeBegin, RangeEnd};
+ } else {
+ range = std::nullopt;
+ }
+
+ auto *TypeInfo = rec->getValueAsDef("type_info");
+ auto TypeInfoEnum = TypeInfo->getValueAsString("enum");
+ auto TypeInfoSize = TypeInfo->getValueAsString("size");
+ if (TypeInfoEnum != "" && TypeInfoSize != "") {
+ typeinfo = {TypeInfoEnum, TypeInfoSize};
+ } else {
+ typeinfo = std::nullopt;
+ }
+ }
+ StringRef getName() const { return rec->getValueAsString("name"); }
+ StringRef getType() const { return rec->getValueAsString("type"); }
+ bool isPointerType() const { return getType().ends_with('*'); }
+ bool isHandleType() const { return getType().ends_with("_handle_t"); }
+ StringRef getDesc() const { return rec->getValueAsString("desc"); }
+ bool isIn() const { return dyn_cast<BitInit>(flags->getBit(0))->getValue(); }
+ bool isOut() const { return dyn_cast<BitInit>(flags->getBit(1))->getValue(); }
+ bool isOpt() const { return dyn_cast<BitInit>(flags->getBit(2))->getValue(); }
+
+ Record *getRec() const { return rec; }
+ std::optional<std::pair<StringRef, StringRef>> getRange() const {
+ return range;
+ }
+
+ std::optional<std::pair<StringRef, StringRef>> getTypeInfo() const {
+ return typeinfo;
+ }
+
+ // Needed to check whether we're at the back of a vector of params
+ bool operator!=(const ParamRec &p) const { return rec != p.getRec(); }
+
+private:
+ Record *rec;
+ BitsInit *flags;
+ std::optional<std::pair<StringRef, StringRef>> range;
+ std::optional<std::pair<StringRef, StringRef>> typeinfo;
+};
+
+class ReturnRec {
+public:
+ ReturnRec(Record *rec) : rec(rec) {}
+ StringRef getValue() const { return rec->getValueAsString("value"); }
+ std::vector<StringRef> getConditions() const {
+ return rec->getValueAsListOfStrings("conditions");
+ }
+
+private:
+ Record *rec;
+};
+
+class FunctionRec {
+public:
+ FunctionRec(Record *rec) : rec(rec) {
+ for (auto &Ret : rec->getValueAsListOfDefs("all_returns"))
+ rets.emplace_back(Ret);
+ for (auto &Param : rec->getValueAsListOfDefs("params"))
+ params.emplace_back(Param);
+ }
+
+ std::string getParamStructName() const {
+ return llvm::formatv("{0}_params_t",
+ llvm::convertToSnakeFromCamelCase(getName()));
+ }
+
+ StringRef getName() const { return rec->getValueAsString("name"); }
+ StringRef getClass() const { return rec->getValueAsString("api_class"); }
+ const std::vector<ReturnRec> &getReturns() const { return rets; }
+ const std::vector<ParamRec> &getParams() const { return params; }
+ StringRef getDesc() const { return rec->getValueAsString("desc"); }
+ std::vector<StringRef> getDetails() const {
+ return rec->getValueAsListOfStrings("details");
+ }
+ std::vector<StringRef> getAnalogues() const {
+ return rec->getValueAsListOfStrings("analogues");
+ }
+
+private:
+ std::vector<ReturnRec> rets;
+ std::vector<ParamRec> params;
+
+ Record *rec;
+};
+
+} // namespace tblgen
+} // namespace offload
+} // namespace llvm
diff --git a/offload/tools/offload-tblgen/offload-tblgen.cpp b/offload/tools/offload-tblgen/offload-tblgen.cpp
new file mode 100644
index 00000000000000..2e11ec08d0e93a
--- /dev/null
+++ b/offload/tools/offload-tblgen/offload-tblgen.cpp
@@ -0,0 +1,95 @@
+//===- offload-tblgen/offload-tblgen.cpp ----------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This is a Tablegen tool that produces source files for the Offload project.
+// See offload/API/README.md for more information.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/TableGen/Main.h"
+#include "llvm/TableGen/Record.h"
+
+#include "Generators.hpp"
+
+namespace llvm {
+namespace offload {
+namespace tblgen {
+
+enum ActionType {
+ PrintRecords,
+ DumpJSON,
+ GenAPI,
+ GenFuncNames,
+ GenImplFuncDecls,
+ GenEntryPoints,
+ GenPrintHeader
+};
+
+namespace {
+cl::opt<ActionType> Action(
+ cl::desc("Action to perform:"),
+ cl::values(
+ clEnumValN(PrintRecords, "print-records",
+ "Print all records to stdout (default)"),
+ clEnumValN(DumpJSON, "dump-json",
+ "Dump all records as machine-readable JSON"),
+ clEnumValN(GenAPI, "gen-api", "Generate Offload API header contents"),
+ clEnumValN(GenFuncNames, "gen-func-names",
+ "Generate a list of all Offload API function names"),
+ clEnumValN(
+ GenImplFuncDecls, "gen-impl-func-decls",
+ "Generate declarations for Offload API implementation functions"),
+ clEnumValN(GenEntryPoints, "gen-entry-points",
+ "Generate Offload API wrapper function definitions"),
+ clEnumValN(GenPrintHeader, "gen-print-header",
+ "Generate Offload API print header")));
+}
+
+static bool OffloadTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
+ switch (Action) {
+ case PrintRecords:
+ OS << Records;
+ break;
+ case DumpJSON:
+ EmitJSON(Records, OS);
+ break;
+ case GenAPI:
+ EmitOffloadAPI(Records, OS);
+ break;
+ case GenFuncNames:
+ EmitOffloadFuncNames(Records, OS);
+ break;
+ case GenImplFuncDecls:
+ EmitOffloadImplFuncDecls(Records, OS);
+ break;
+ case GenEntryPoints:
+ EmitOffloadEntryPoints(Records, OS);
+ break;
+ case GenPrintHeader:
+ EmitOffloadPrintHeader(Records, OS);
+ break;
+ }
+
+ return false;
+}
+
+int OffloadTblgenMain(int argc, char **argv) {
+ InitLLVM y(argc, argv);
+ cl::ParseCommandLineOptions(argc, argv);
+ return TableGenMain(argv[0], &OffloadTableGenMain);
+}
+} // namespace tblgen
+} // namespace offload
+} // namespace llvm
+
+using namespace llvm;
+using namespace offload::tblgen;
+
+int main(int argc, char **argv) { return OffloadTblgenMain(argc, argv); }
>From 2b142440f0c4bdbf78a46394c51b18241f2046dc Mon Sep 17 00:00:00 2001
From: Callum Fare <callum at codeplay.com>
Date: Fri, 13 Sep 2024 16:03:15 +0100
Subject: [PATCH 2/3] Fix liboffload_new linking and formatting
---
offload/new-api/CMakeLists.txt | 19 ++++++++++++-------
offload/new-api/src/helpers.hpp | 1 -
offload/new-api/src/offload_impl.cpp | 15 +++++++++------
.../common/include/PluginInterface.h | 5 +----
offload/tools/offload-tblgen/FuncsGen.cpp | 19 ++++++++++++++++---
offload/tools/offload-tblgen/GenCommon.hpp | 1 -
offload/tools/offload-tblgen/Generators.hpp | 1 +
.../tools/offload-tblgen/offload-tblgen.cpp | 10 ++++++++--
8 files changed, 47 insertions(+), 24 deletions(-)
diff --git a/offload/new-api/CMakeLists.txt b/offload/new-api/CMakeLists.txt
index c27591d2af3ab3..ac82e7db647601 100644
--- a/offload/new-api/CMakeLists.txt
+++ b/offload/new-api/CMakeLists.txt
@@ -1,4 +1,3 @@
-
set(LLVM_TARGET_DEFINITIONS ${CMAKE_CURRENT_SOURCE_DIR}/API/OffloadAPI.td)
list(APPEND LLVM_TABLEGEN_FLAGS -I ${CMAKE_CURRENT_SOURCE_DIR}/API)
@@ -7,15 +6,22 @@ tablegen(OFFLOAD offload_funcs.inc -gen-func-names)
tablegen(OFFLOAD offload_impl_func_decls.inc -gen-impl-func-decls)
tablegen(OFFLOAD offload_entry_points.inc -gen-entry-points)
tablegen(OFFLOAD offload_print.hpp -gen-print-header)
-
+tablegen(OFFLOAD offload_exports -gen-exports)
add_public_tablegen_target(OffloadHeaderGen)
add_llvm_library(offload_new SHARED
src/offload_lib.cpp
src/offload_impl.cpp
- DEPENDS OffloadHeaderGen
- LINK_LIBS omptarget omptarget.rtl.cuda omptarget.rtl.amdgpu)
+ DEPENDS OffloadHeaderGen)
+
+foreach(plugin IN LISTS LIBOMPTARGET_PLUGINS_TO_BUILD)
+ target_link_libraries(offload_new PRIVATE omptarget.rtl.${plugin})
+endforeach()
+
+if(LIBOMP_HAVE_VERSION_SCRIPT_FLAG)
+ target_link_libraries(offload_new PRIVATE "-Wl,--version-script=${CMAKE_CURRENT_BINARY_DIR}/offload_exports")
+endif()
target_include_directories(offload_new PUBLIC
${CMAKE_CURRENT_BINARY_DIR}
@@ -23,9 +29,8 @@ target_include_directories(offload_new PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../include
${CMAKE_CURRENT_SOURCE_DIR}/../plugins-nextgen/common/include)
-# foreach(plugin IN LISTS LIBOMPTARGET_PLUGINS_TO_BUILD)
-# target_link_libraries(offload_new PRIVATE omptarget.rtl.${plugin})
-# endforeach()
+target_compile_options(offload_new PRIVATE ${offload_compile_flags})
+target_link_options(offload_new PRIVATE ${offload_link_flags})
set_target_properties(offload_new PROPERTIES
POSITION_INDEPENDENT_CODE ON
diff --git a/offload/new-api/src/helpers.hpp b/offload/new-api/src/helpers.hpp
index 66148d9f210083..246de2b678015d 100644
--- a/offload/new-api/src/helpers.hpp
+++ b/offload/new-api/src/helpers.hpp
@@ -16,7 +16,6 @@
#include <cstring>
-
template <typename T, typename Assign>
offload_result_t getInfoImpl(size_t ParamValueSize, void *ParamValue,
size_t *ParamValueSizeRet, T Value,
diff --git a/offload/new-api/src/offload_impl.cpp b/offload/new-api/src/offload_impl.cpp
index 165f2264ef7daa..b3bf602607ac47 100644
--- a/offload/new-api/src/offload_impl.cpp
+++ b/offload/new-api/src/offload_impl.cpp
@@ -32,7 +32,10 @@ struct offload_platform_handle_t_ {
std::vector<offload_device_handle_t_> Devices;
};
-static std::vector<offload_platform_handle_t_> Platforms;
+std::vector<offload_platform_handle_t_> &Platforms() {
+ static std::vector<offload_platform_handle_t_> Platforms;
+ return Platforms;
+}
// Every plugin exports this method to create an instance of the plugin type.
#define PLUGIN_TARGET(Name) extern "C" GenericPluginTy *createPlugin_##Name();
@@ -42,14 +45,14 @@ void initPlugins() {
// Attempt to create an instance of each supported plugin.
#define PLUGIN_TARGET(Name) \
do { \
- Platforms.emplace_back(offload_platform_handle_t_{ \
+ Platforms().emplace_back(offload_platform_handle_t_{ \
std::unique_ptr<GenericPluginTy>(createPlugin_##Name()), {}}); \
} while (false);
#include "Shared/Targets.def"
// Preemptively initialize all devices in the plugin so we can just return
// them from deviceGet
- for (auto &Platform : Platforms) {
+ for (auto &Platform : Platforms()) {
auto Err = Platform.Plugin->init();
[[maybe_unused]] std::string InfoMsg = toString(std::move(Err));
for (auto DevNum = 0; DevNum < Platform.Plugin->number_of_devices();
@@ -71,19 +74,19 @@ offload_result_t offloadPlatformGet_impl(uint32_t NumEntries,
static std::once_flag InitFlag;
std::call_once(InitFlag, initPlugins);
- if (NumEntries > Platforms.size()) {
+ if (NumEntries > Platforms().size()) {
return OFFLOAD_RESULT_ERROR_INVALID_SIZE;
}
if (phPlatforms) {
for (uint32_t PlatformIndex = 0; PlatformIndex < NumEntries;
PlatformIndex++) {
- phPlatforms[PlatformIndex] = &Platforms[PlatformIndex];
+ phPlatforms[PlatformIndex] = &(Platforms())[PlatformIndex];
}
}
if (pNumPlatforms) {
- *pNumPlatforms = Platforms.size();
+ *pNumPlatforms = Platforms().size();
}
return OFFLOAD_RESULT_SUCCESS;
diff --git a/offload/plugins-nextgen/common/include/PluginInterface.h b/offload/plugins-nextgen/common/include/PluginInterface.h
index 4cfe6cbf9ffe7f..82efcdaf5ad3dc 100644
--- a/offload/plugins-nextgen/common/include/PluginInterface.h
+++ b/offload/plugins-nextgen/common/include/PluginInterface.h
@@ -132,7 +132,6 @@ class InfoQueueTy {
uint64_t Level;
};
-
private:
std::deque<InfoQueueEntryTy> Queue;
@@ -156,9 +155,7 @@ class InfoQueueTy {
Queue.push_back({Key, Value, Units, L});
}
- const std::deque<InfoQueueEntryTy> &getQueue() const {
- return Queue;
- }
+ const std::deque<InfoQueueEntryTy> &getQueue() const { return Queue; }
/// Print all info entries added to the queue.
void print() const {
diff --git a/offload/tools/offload-tblgen/FuncsGen.cpp b/offload/tools/offload-tblgen/FuncsGen.cpp
index 4dc74047ac49d8..567d784f320010 100644
--- a/offload/tools/offload-tblgen/FuncsGen.cpp
+++ b/offload/tools/offload-tblgen/FuncsGen.cpp
@@ -1,4 +1,4 @@
-//===- offload-tblgen/APIGen.cpp - Tablegen backend for Offload validation ===//
+//===- offload-tblgen/APIGen.cpp - Tablegen backend for Offload functions -===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -6,8 +6,8 @@
//
//===----------------------------------------------------------------------===//
//
-// This is a Tablegen backend that produces validation functions for the Offload
-// API entry point functions.
+// This is a Tablegen backend that handles generation of various small files
+// pertaining to the API functions.
//
//===----------------------------------------------------------------------===//
@@ -36,6 +36,19 @@ void EmitOffloadFuncNames(RecordKeeper &Records, raw_ostream &OS) {
OS << "\n#undef OFFLOAD_FUNC\n";
}
+void EmitOffloadExports(RecordKeeper &Records, raw_ostream &OS) {
+ OS << "VERS1.0 {\n";
+ OS << TAB_1 "global:\n";
+
+ for (auto *R : Records.getAllDerivedDefinitions("Function")) {
+ OS << formatv(TAB_2 "{0};\n", FunctionRec(R).getName());
+ }
+ OS << TAB_1 "local:\n";
+ OS << TAB_2 "*;\n";
+ OS << "};\n";
+}
+
+// Emit declarations for every implementation function
void EmitOffloadImplFuncDecls(RecordKeeper &Records, raw_ostream &OS) {
for (auto *R : Records.getAllDerivedDefinitions("Function")) {
FunctionRec F{R};
diff --git a/offload/tools/offload-tblgen/GenCommon.hpp b/offload/tools/offload-tblgen/GenCommon.hpp
index fb24ef06f9ec8f..16ffb3a4d667c8 100644
--- a/offload/tools/offload-tblgen/GenCommon.hpp
+++ b/offload/tools/offload-tblgen/GenCommon.hpp
@@ -55,4 +55,3 @@ MakeParamComment(const llvm::offload::tblgen::ParamRec &Param) {
(Param.isOut() ? "[out]" : ""),
(Param.isOpt() ? "[optional]" : ""), Param.getDesc());
}
-
diff --git a/offload/tools/offload-tblgen/Generators.hpp b/offload/tools/offload-tblgen/Generators.hpp
index 4b300f52ddf84d..4e347ba2fb2946 100644
--- a/offload/tools/offload-tblgen/Generators.hpp
+++ b/offload/tools/offload-tblgen/Generators.hpp
@@ -16,3 +16,4 @@ void EmitOffloadImplFuncDecls(llvm::RecordKeeper &Records,
llvm::raw_ostream &OS);
void EmitOffloadEntryPoints(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitOffloadPrintHeader(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+void EmitOffloadExports(llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
diff --git a/offload/tools/offload-tblgen/offload-tblgen.cpp b/offload/tools/offload-tblgen/offload-tblgen.cpp
index 2e11ec08d0e93a..856ebebfc93249 100644
--- a/offload/tools/offload-tblgen/offload-tblgen.cpp
+++ b/offload/tools/offload-tblgen/offload-tblgen.cpp
@@ -29,7 +29,8 @@ enum ActionType {
GenFuncNames,
GenImplFuncDecls,
GenEntryPoints,
- GenPrintHeader
+ GenPrintHeader,
+ GenExports
};
namespace {
@@ -49,7 +50,9 @@ cl::opt<ActionType> Action(
clEnumValN(GenEntryPoints, "gen-entry-points",
"Generate Offload API wrapper function definitions"),
clEnumValN(GenPrintHeader, "gen-print-header",
- "Generate Offload API print header")));
+ "Generate Offload API print header"),
+ clEnumValN(GenExports, "gen-exports",
+ "Generate export file for the Offload library")));
}
static bool OffloadTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
@@ -75,6 +78,9 @@ static bool OffloadTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
case GenPrintHeader:
EmitOffloadPrintHeader(Records, OS);
break;
+ case GenExports:
+ EmitOffloadExports(Records, OS);
+ break;
}
return false;
>From bb17c4fae86b5f90dde584a9e5d8d4021a5b9812 Mon Sep 17 00:00:00 2001
From: Callum Fare <callum at codeplay.com>
Date: Wed, 18 Sep 2024 11:15:00 +0100
Subject: [PATCH 3/3] Refactor entry point validation so failures are visible
in tracing output
---
.../tools/offload-tblgen/EntryPointGen.cpp | 34 +++++++++++++++----
1 file changed, 27 insertions(+), 7 deletions(-)
diff --git a/offload/tools/offload-tblgen/EntryPointGen.cpp b/offload/tools/offload-tblgen/EntryPointGen.cpp
index b89592187639e3..27f8a306e40ddf 100644
--- a/offload/tools/offload-tblgen/EntryPointGen.cpp
+++ b/offload/tools/offload-tblgen/EntryPointGen.cpp
@@ -20,11 +20,10 @@
using namespace llvm;
using namespace offload::tblgen;
-static void EmitEntryPointFunc(const FunctionRec &F, raw_ostream &OS) {
+static void EmitValidationFunc(const FunctionRec &F, raw_ostream &OS) {
OS << CommentsHeader;
// Emit preamble
- OS << formatv("{1}_APIEXPORT {0}_result_t {1}_APICALL {2}(\n ", PrefixLower,
- PrefixUpper, F.getName());
+ OS << formatv("{0}_result_t {1}_val(\n ", PrefixLower, F.getName());
// Emit arguments
std::string ParamNameList = "";
for (auto &Param : F.getParams()) {
@@ -37,7 +36,6 @@ static void EmitEntryPointFunc(const FunctionRec &F, raw_ostream &OS) {
OS << ") {\n";
OS << TAB_1 "if (true /*enableParameterValidation*/) {\n";
-
// Emit validation checks
for (const auto &Return : F.getReturns()) {
for (auto &Condition : Return.getConditions()) {
@@ -49,16 +47,37 @@ static void EmitEntryPointFunc(const FunctionRec &F, raw_ostream &OS) {
}
}
}
- OS << " }\n\n";
+ OS << TAB_1 "}\n\n";
+
+ // Perform actual function call to the implementation
+ ParamNameList = ParamNameList.substr(0, ParamNameList.size() - 2);
+ OS << formatv(TAB_1 "return {0}_impl({1});\n\n", F.getName(), ParamNameList);
+ OS << "}\n";
+}
+
+static void EmitEntryPointFunc(const FunctionRec &F, raw_ostream &OS) {
+ // Emit preamble
+ OS << formatv("{1}_APIEXPORT {0}_result_t {1}_APICALL {2}(\n ", PrefixLower,
+ PrefixUpper, F.getName());
+ // Emit arguments
+ std::string ParamNameList = "";
+ for (auto &Param : F.getParams()) {
+ OS << Param.getType() << " " << Param.getName();
+ if (Param != F.getParams().back()) {
+ OS << ", ";
+ }
+ ParamNameList += Param.getName().str() + ", ";
+ }
+ OS << ") {\n";
// Emit pre-call prints
OS << TAB_1 "if (std::getenv(\"OFFLOAD_TRACE\")) {\n";
OS << formatv(TAB_2 "std::cout << \"---> {0}\";\n", F.getName());
OS << TAB_1 "}\n\n";
- // Perform actual function call
+ // Perform actual function call to the validation wrapper
ParamNameList = ParamNameList.substr(0, ParamNameList.size() - 2);
- OS << formatv(TAB_1 "{0}_result_t result = {1}_impl({2});\n\n", PrefixLower,
+ OS << formatv(TAB_1 "{0}_result_t result = {1}_val({2});\n\n", PrefixLower,
F.getName(), ParamNameList);
// Emit post-call prints
@@ -81,6 +100,7 @@ static void EmitEntryPointFunc(const FunctionRec &F, raw_ostream &OS) {
void EmitOffloadEntryPoints(RecordKeeper &Records, raw_ostream &OS) {
for (auto *R : Records.getAllDerivedDefinitions("Function")) {
+ EmitValidationFunc(FunctionRec{R}, OS);
EmitEntryPointFunc(FunctionRec{R}, OS);
}
}
More information about the llvm-commits
mailing list