diff --git a/offload/CMakeLists.txt b/offload/CMakeLists.txt index dfd25bad60843..af2e07d514721 100644 --- a/offload/CMakeLists.txt +++ b/offload/CMakeLists.txt @@ -351,6 +351,9 @@ add_subdirectory(tools) # Build target agnostic offloading library. add_subdirectory(src) +add_subdirectory(tools/offload-tblgen) +add_subdirectory(liboffload) + # Add tests. add_subdirectory(test) diff --git a/offload/cmake/OpenMPTesting.cmake b/offload/cmake/OpenMPTesting.cmake index 6609d6301d0f9..f97def2c52eba 100644 --- a/offload/cmake/OpenMPTesting.cmake +++ b/offload/cmake/OpenMPTesting.cmake @@ -48,6 +48,17 @@ function(find_standalone_test_dependencies) return() endif() + find_program(OFFLOAD_TBLGEN_EXECUTABLE + NAMES offload-tblgen + PATHS ${OPENMP_LLVM_TOOLS_DIR}) + if (NOT OFFLOAD_TBLGEN_EXECUTABLE) + message(STATUS "Cannot find 'offload-tblgen'.") + message(STATUS "Please put 'not' in your PATH, set OFFLOAD_TBLGEN_EXECUTABLE to its full path, or point OPENMP_LLVM_TOOLS_DIR to its directory.") + message(WARNING "The check targets will not be available!") + set(ENABLE_CHECK_TARGETS FALSE PARENT_SCOPE) + return() + endif() + find_program(OPENMP_NOT_EXECUTABLE NAMES not PATHS ${OPENMP_LLVM_TOOLS_DIR}) @@ -82,6 +93,7 @@ else() set(OPENMP_FILECHECK_EXECUTABLE ${LLVM_RUNTIME_OUTPUT_INTDIR}/FileCheck) endif() set(OPENMP_NOT_EXECUTABLE ${LLVM_RUNTIME_OUTPUT_INTDIR}/not) + set(OFFLOAD_TBLGEN_EXECUTABLE ${LLVM_RUNTIME_OUTPUT_INTDIR}/offload-tblgen) set(OFFLOAD_DEVICE_INFO_EXECUTABLE ${LLVM_RUNTIME_OUTPUT_INTDIR}/llvm-offload-device-info) endif() diff --git a/offload/liboffload/API/APIDefs.td b/offload/liboffload/API/APIDefs.td new file mode 100644 index 0000000000000..60c1b85d26911 --- /dev/null +++ b/offload/liboffload/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 = "OL"; +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 { + // 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 { + 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 = 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 enum = TypeEnum; + string size = TypeSize; +} + +class Param 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.ret; + bit IsPointer = IsPointerType.ret; +} + +// A parameter whose range is described by other parameters in the function. +class RangedParam Flags, Range ParamRange> : Param { + 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 Flags, TypeInfo ParamTypeInfo> : Param { + let type_info = ParamTypeInfo; +} + +class Return Conditions = []> { + string value = Value; + list conditions = Conditions; +} + +class ShouldCheckHandle { + bit ret = !and(P.IsHandle, !eq(!and(PARAM_OPTIONAL, P.flags), 0)); +} + +class ShouldCheckPointer { + 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 Returns, string ReturnValue, + list Conditions> { + list ret = + !foreach(Ret, Returns, + !if(!eq(Ret.value, ReturnValue), + Return, Ret)); +} + +// Add null handle checks to a function's return value descriptions +class AddHandleChecksToReturns Params, list Returns> { + list handle_params = + !foreach(P, Params, !if(ShouldCheckHandle

.ret, P.name, "")); + list handle_params_filt = + !filter(param, handle_params, !ne(param, "")); + list 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#"_ERRC_INVALID_NULL_HANDLE"))); + + list returns_out = !if(returns_has_inv_handle, + AppendConditionsToReturn.ret, + !listconcat(Returns, [Return]) + ); +} + +// Add null pointer checks to a function's return value descriptions +class AddPointerChecksToReturns Params, list Returns> { + list ptr_params = + !foreach(P, Params, !if(ShouldCheckPointer

.ret, P.name, "")); + list ptr_params_filt = !filter(param, ptr_params, !ne(param, "")); + list 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#"_ERRC_INVALID_NULL_POINTER"))); + list returns_out = !if(returns_has_inv_ptr, + AppendConditionsToReturn.ret, + !listconcat(Returns, [Return]) + ); +} + +defvar DefaultReturns = [Return, + Return, + Return]; + +class APIObject { + string name; + string desc; +} + +class Function : APIObject { + list params; + list returns; + list details = []; + list analogues = []; + + list returns_with_def = !listconcat(DefaultReturns, returns); + list all_returns = AddPointerChecksToReturns.returns_out>.returns_out; +} + +class Etor { + string name = Name; + string desc = Desc; + string tagged_type; +} + +class TaggedEtor : Etor { + 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 etors = []; +} + +class StructMember { + string type = Type; + string name = Name; + string desc = Desc; +} + +defvar DefaultPropStructMembers = + [StructMember, + StructMember<"void*", "pNext", "pointer to extension-specific structure">]; + +class StructHasInheritedMembers { + bit ret = !or(!eq(BaseClass, prefix#"_base_properties_t"), + !eq(BaseClass, prefix#"_base_desc_t")); +} + +class Struct : APIObject { + string base_class = ""; + list members; + list all_members = + !if(StructHasInheritedMembers.ret, + DefaultPropStructMembers, [])#members; +} + +class Typedef : APIObject { string value; } + +class FptrTypedef : APIObject { + list params; + list returns; +} + +class Macro : APIObject { + string value; + + string condition; + string alt_value; +} + +class Handle : APIObject; diff --git a/offload/liboffload/API/CMakeLists.txt b/offload/liboffload/API/CMakeLists.txt new file mode 100644 index 0000000000000..8fd6cb539374a --- /dev/null +++ b/offload/liboffload/API/CMakeLists.txt @@ -0,0 +1,25 @@ +# The OffloadGenerate target is used to regenerate the generated files in the +# include directory. These files are checked in with the rest of the source, +# therefore it is only needed when making changes to the API. + +find_program(CLANG_FORMAT clang-format PATHS ${LLVM_TOOLS_BINARY_DIR} NO_DEFAULT_PATH) +if (CLANG_FORMAT) + set(LLVM_TARGET_DEFINITIONS ${CMAKE_CURRENT_SOURCE_DIR}/OffloadAPI.td) + + tablegen(OFFLOAD OffloadAPI.h -gen-api) + tablegen(OFFLOAD OffloadEntryPoints.inc -gen-entry-points) + tablegen(OFFLOAD OffloadFuncs.inc -gen-func-names) + tablegen(OFFLOAD OffloadImplFuncDecls.inc -gen-impl-func-decls) + tablegen(OFFLOAD OffloadPrint.hpp -gen-print-header) + + set(OFFLOAD_GENERATED_FILES ${TABLEGEN_OUTPUT}) + add_public_tablegen_target(OffloadGenerate) + add_custom_command(TARGET OffloadGenerate POST_BUILD COMMAND ${CLANG_FORMAT} + -i ${OFFLOAD_GENERATED_FILES}) + add_custom_command(TARGET OffloadGenerate POST_BUILD COMMAND ${CMAKE_COMMAND} + -E copy_if_different ${OFFLOAD_GENERATED_FILES} "${CMAKE_CURRENT_SOURCE_DIR}/../include/generated") +else() + message(WARNING "clang-format was not found, so the OffloadGenerate target\ + will not be available. Offload will still build, but you will not be\ + able to make changes to the API.") +endif() diff --git a/offload/liboffload/API/Common.td b/offload/liboffload/API/Common.td new file mode 100644 index 0000000000000..5b19d1d47129e --- /dev/null +++ b/offload/liboffload/API/Common.td @@ -0,0 +1,141 @@ +def : Macro { + let name = "OL_VERSION_MAJOR"; + let desc = "Major version of the Offload API"; + let value = "0"; +} + +def : Macro { + let name = "OL_VERSION_MINOR"; + let desc = "Minor version of the Offload API"; + let value = "0"; +} + +def : Macro { + let name = "OL_VERSION_PATCH"; + let desc = "Patch version of the Offload API"; + let value = "1"; +} + +def : Macro { + let name = "OL_APICALL"; + let desc = "Calling convention for all API functions"; + let condition = "defined(_WIN32)"; + let value = "__cdecl"; + let alt_value = ""; +} + +def : Macro { + let name = "OL_APIEXPORT"; + let desc = "Microsoft-specific dllexport storage-class attribute"; + let condition = "defined(_WIN32)"; + let value = "__declspec(dllexport)"; + let alt_value = ""; +} + +def : Macro { + let name = "OL_DLLEXPORT"; + let desc = "Microsoft-specific dllexport storage-class attribute"; + let condition = "defined(_WIN32)"; + let value = "__declspec(dllexport)"; +} + +def : Macro { + let name = "OL_DLLEXPORT"; + let desc = "GCC-specific dllexport storage-class attribute"; + let condition = "__GNUC__ >= 4"; + let value = "__attribute__ ((visibility (\"default\")))"; + let alt_value = ""; +} + +def : Handle { + let name = "ol_platform_handle_t"; + let desc = "Handle of a platform instance"; +} + +def : Handle { + let name = "ol_device_handle_t"; + let desc = "Handle of platform's device object"; +} + +def : Handle { + let name = "ol_context_handle_t"; + let desc = "Handle of context object"; +} + +def : Enum { + let name = "ol_errc_t"; + let desc = "Defines Return/Error codes"; + let etors =[ + Etor<"SUCCESS", "Success">, + Etor<"INVALID_VALUE", "Invalid Value">, + Etor<"INVALID_PLATFORM", "Invalid platform">, + Etor<"DEVICE_NOT_FOUND", "Device not found">, + Etor<"INVALID_DEVICE", "Invalid device">, + Etor<"DEVICE_LOST", "Device hung, reset, was removed, or driver update occurred">, + Etor<"UNINITIALIZED", "plugin is not initialized or specific entry-point is not implemented">, + Etor<"OUT_OF_RESOURCES", "Out of resources">, + Etor<"UNSUPPORTED_VERSION", "generic error code for unsupported versions">, + Etor<"UNSUPPORTED_FEATURE", "generic error code for unsupported features">, + Etor<"INVALID_ARGUMENT", "generic error code for invalid arguments">, + Etor<"INVALID_NULL_HANDLE", "handle argument is not valid">, + Etor<"INVALID_NULL_POINTER", "pointer argument may not be nullptr">, + Etor<"INVALID_SIZE", "invalid size or dimensions (e.g., must not be zero, or is out of bounds)">, + Etor<"INVALID_ENUMERATION", "enumerator argument is not valid">, + Etor<"UNSUPPORTED_ENUMERATION", "enumerator argument is not supported by the device">, + Etor<"UNKNOWN", "Unknown or internal error"> + ]; +} + +def : Struct { + let name = "ol_error_struct_t"; + let desc = "Details of the error condition returned by an API call"; + let members = [ + StructMember<"ol_errc_t", "Code", "The error code">, + StructMember<"const char*", "Details", "String containing error details"> + ]; +} + +def : Typedef { + let name = "ol_result_t"; + let desc = "Result type returned by all entry points."; + let value = "const ol_error_struct_t*"; +} + +def : Macro { + let name = "OL_SUCCESS"; + let desc = "Success condition"; + let value = "NULL"; +} + +def : Struct { + let name = "ol_code_location_t"; + let desc = "Code location information that can optionally be associated with an API call"; + let members = [ + StructMember<"const char*", "FunctionName", "Function name">, + StructMember<"const char*", "SourceFile", "Source code file">, + StructMember<"uint32_t", "LineNumber", "Source code line number">, + StructMember<"uint32_t", "ColumnNumber", "Source code column number"> + ]; +} + +def : Function { + let name = "olInit"; + let desc = "Perform initialization of the Offload library and plugins"; + let details = [ + "This must be the first API call made by a user of the Offload library", + "Each call will increment an internal reference count that is decremented by `olShutDown`" + ]; + let params = []; + let returns = []; +} + +def : Function { + let name = "olShutDown"; + let desc = "Release the resources in use by Offload"; + let details = [ + "This decrements an internal reference count. When this reaches 0, all resources will be released", + "Subsequent API calls made after this are not valid" + ]; + let params = []; + let returns = []; +} diff --git a/offload/liboffload/API/Device.td b/offload/liboffload/API/Device.td new file mode 100644 index 0000000000000..30c0b71fe7b37 --- /dev/null +++ b/offload/liboffload/API/Device.td @@ -0,0 +1,106 @@ +//===-- 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 = "ol_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 = "ol_device_info_t"; + let desc = "Supported device info"; + let is_typed = 1; + let etors =[ + TaggedEtor<"TYPE", "ol_device_type_t", "type of the device">, + TaggedEtor<"PLATFORM", "ol_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 = "olGetDeviceCount"; + let desc = "Retrieves the number of available devices within a platform"; + let params = [ + Param<"ol_platform_handle_t", "Platform", "handle of the platform instance", PARAM_IN>, + Param<"uint32_t*", "NumDevices", "pointer to the number of devices.", PARAM_OUT> + ]; + let returns = []; +} + +def : Function { + let name = "olGetDevice"; + let desc = "Retrieves devices within a platform"; + let details = [ + "Multiple calls to this function will return identical device handles, in the same order.", + ]; + let params = [ + Param<"ol_platform_handle_t", "Platform", "handle of the platform instance", PARAM_IN>, + Param<"uint32_t", "NumEntries", "the number of devices to be added to phDevices, which must be greater than zero", PARAM_IN>, + RangedParam<"ol_device_handle_t*", "Devices", "Array of device handles. " + "If NumEntries is less than the number of devices available, then this function shall only retrieve that number of devices.", PARAM_OUT, + Range<"0", "NumEntries">> + ]; + let returns = [ + Return<"OL_ERRC_INVALID_SIZE", [ + "`NumEntries == 0`" + ]> + ]; +} + +def : Function { + let name = "olGetDeviceInfo"; + let desc = "Queries the given property of the device"; + let details = []; + let params = [ + Param<"ol_device_handle_t", "Device", "handle of the device instance", PARAM_IN>, + Param<"ol_device_info_t", "PropName", "type of the info to retrieve", PARAM_IN>, + Param<"size_t", "PropSize", "the number of bytes pointed to by PropValue.", PARAM_IN>, + TypeTaggedParam<"void*", "PropValue", "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 OL_ERRC_INVALID_SIZE error is returned and " + "PropValue is not used.", PARAM_OUT, TypeInfo<"PropName" , "PropSize">> + ]; + let returns = [ + Return<"OL_ERRC_UNSUPPORTED_ENUMERATION", [ + "If `PropName` is not supported by the device." + ]>, + Return<"OL_ERRC_INVALID_SIZE", [ + "`PropSize == 0`", + "If `PropSize` is less than the real number of bytes needed to return the info." + ]>, + Return<"OL_ERRC_INVALID_DEVICE"> + ]; +} + +def : Function { + let name = "olGetDeviceInfoSize"; + let desc = "Returns the storage size of the given device query"; + let details = []; + let params = [ + Param<"ol_device_handle_t", "Device", "handle of the device instance", PARAM_IN>, + Param<"ol_device_info_t", "PropName", "type of the info to retrieve", PARAM_IN>, + Param<"size_t*", "PropSizeRet", "pointer to the number of bytes required to store the query", PARAM_OUT> + ]; + let returns = [ + Return<"OL_ERRC_UNSUPPORTED_ENUMERATION", [ + "If `PropName` is not supported by the device." + ]>, + Return<"OL_ERRC_INVALID_DEVICE"> + ]; +} diff --git a/offload/liboffload/API/OffloadAPI.td b/offload/liboffload/API/OffloadAPI.td new file mode 100644 index 0000000000000..8a0c3c4058122 --- /dev/null +++ b/offload/liboffload/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/liboffload/API/Platform.td b/offload/liboffload/API/Platform.td new file mode 100644 index 0000000000000..03e70cf96ac94 --- /dev/null +++ b/offload/liboffload/API/Platform.td @@ -0,0 +1,112 @@ +//===-- 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 = "olGetPlatform"; + 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 Platforms. NumEntries must be " + "greater than zero.", + PARAM_IN>, + RangedParam<"ol_platform_handle_t*", "Platforms", + "Array of handle of platforms. If NumEntries is less than the number of " + "platforms available, then olGetPlatform shall only retrieve that " + "number of platforms.", + PARAM_OUT, Range<"0", "NumEntries">> + ]; + let returns = [ + Return<"OL_ERRC_INVALID_SIZE", [ + "`NumEntries == 0`" + ]> + ]; +} + +def : Function { + let name = "olGetPlatformCount"; + let desc = "Retrieves the number of available platforms"; + let params = [ + Param<"uint32_t*", + "NumPlatforms", "returns the total number of platforms available.", + PARAM_OUT> + ]; + let returns = []; +} + +def : Enum { + let name = "ol_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", "ol_platform_backend_t", "The native backend of the platform."> + ]; +} + +def : Enum { + let name = "ol_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 = "olGetPlatformInfo"; + let desc = "Queries the given property of the platform"; + let details = [ + "`olGetPlatformInfoSize` can be used to query the storage size " + "required for the given query." + ]; + let params = [ + Param<"ol_platform_handle_t", "Platform", "handle of the platform", PARAM_IN>, + Param<"ol_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*", "PropValue", "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 OL_ERRC_INVALID_SIZE error is returned and pPlatformInfo is not used.", PARAM_OUT, + TypeInfo<"PropName" , "PropSize">> + ]; + let returns = [ + Return<"OL_ERRC_UNSUPPORTED_ENUMERATION", [ + "If `PropName` is not supported by the platform." + ]>, + Return<"OL_ERRC_INVALID_SIZE", [ + "`PropSize == 0`", + "If `PropSize` is less than the real number of bytes needed to return the info." + ]>, + Return<"OL_ERRC_INVALID_PLATFORM"> + ]; +} + +def : Function { + let name = "olGetPlatformInfoSize"; + let desc = "Returns the storage size of the given platform query"; + let details = []; + let params = [ + Param<"ol_platform_handle_t", "Platform", "handle of the platform", PARAM_IN>, + Param<"ol_platform_info_t", "PropName", "type of the info to query", PARAM_IN>, + Param<"size_t*", "PropSizeRet", "pointer to the number of bytes required to store the query", PARAM_OUT> + ]; + let returns = [ + Return<"OL_ERRC_UNSUPPORTED_ENUMERATION", [ + "If `PropName` is not supported by the platform." + ]>, + Return<"OL_ERRC_INVALID_PLATFORM"> + ]; +} diff --git a/offload/liboffload/API/README.md b/offload/liboffload/API/README.md new file mode 100644 index 0000000000000..38a055811b2d0 --- /dev/null +++ b/offload/liboffload/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. `olGetDeviceInfo`) 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. `olGetDeviceInfo`). + +All enums automatically get a `_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 /offload/API /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 /offload/API /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 /offload/API /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 /offload/API /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` diff --git a/offload/liboffload/CMakeLists.txt b/offload/liboffload/CMakeLists.txt new file mode 100644 index 0000000000000..27ba9e93c3675 --- /dev/null +++ b/offload/liboffload/CMakeLists.txt @@ -0,0 +1,32 @@ +add_subdirectory(API) + +add_llvm_library(LLVMOffload SHARED + src/OffloadLib.cpp + src/OffloadImpl.cpp) + +foreach(plugin IN LISTS LIBOMPTARGET_PLUGINS_TO_BUILD) + target_link_libraries(LLVMOffload PRIVATE omptarget.rtl.${plugin}) +endforeach() + +if(LIBOMP_HAVE_VERSION_SCRIPT_FLAG) + target_link_libraries(LLVMOffload PRIVATE "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/exports") +endif() + +target_include_directories(LLVMOffload PUBLIC + ${CMAKE_CURRENT_BINARY_DIR}/../include + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/include/generated + ${CMAKE_CURRENT_SOURCE_DIR}/../include + ${CMAKE_CURRENT_SOURCE_DIR}/../plugins-nextgen/common/include) + +target_compile_options(LLVMOffload PRIVATE ${offload_compile_flags}) +target_link_options(LLVMOffload PRIVATE ${offload_link_flags}) + +set_target_properties(LLVMOffload PROPERTIES + POSITION_INDEPENDENT_CODE ON + INSTALL_RPATH "$ORIGIN" + BUILD_RPATH "$ORIGIN:${CMAKE_CURRENT_BINARY_DIR}/..") +install(TARGETS LLVMOffload LIBRARY COMPONENT LLVMOffload DESTINATION "${OFFLOAD_INSTALL_LIBDIR}") + +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/OffloadAPI.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include/offload) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/OffloadPrint.hpp DESTINATION ${CMAKE_INSTALL_PREFIX}/include/offload) diff --git a/offload/liboffload/README.md b/offload/liboffload/README.md new file mode 100644 index 0000000000000..95c9bf54d7bad --- /dev/null +++ b/offload/liboffload/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/liboffload/exports b/offload/liboffload/exports new file mode 100644 index 0000000000000..168341aa7d938 --- /dev/null +++ b/offload/liboffload/exports @@ -0,0 +1,6 @@ +VERS1.0 { +global: + ol*; +local: + *; +}; diff --git a/offload/liboffload/include/OffloadImpl.hpp b/offload/liboffload/include/OffloadImpl.hpp new file mode 100644 index 0000000000000..6d745095f3105 --- /dev/null +++ b/offload/liboffload/include/OffloadImpl.hpp @@ -0,0 +1,94 @@ +//===- offload_impl.hpp- Implementation helpers for the Offload library ---===// +// +// 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 +#include +#include +#include +#include +#include +#include +#include + +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" + +struct OffloadConfig { + bool TracingEnabled = false; +}; + +OffloadConfig &offloadConfig(); + +// Use the StringSet container to efficiently deduplicate repeated error +// strings (e.g. if the same error is hit constantly in a long running program) +llvm::StringSet<> &errorStrs(); + +// Use an unordered_set to avoid duplicates of error structs themselves. +// We cannot store the structs directly as returned pointers to them must always +// be valid, and a rehash of the set may invalidate them. This requires +// custom hash and equal_to function objects. +using ErrPtrT = std::unique_ptr; +struct ErrPtrEqual { + bool operator()(const ErrPtrT &lhs, const ErrPtrT &rhs) const { + if (!lhs && !rhs) { + return true; + } + if (!lhs || !rhs) { + return false; + } + + bool StrsEqual = false; + if (lhs->Details == NULL && rhs->Details == NULL) { + StrsEqual = true; + } else if (lhs->Details != NULL && rhs->Details != NULL) { + StrsEqual = (std::strcmp(lhs->Details, rhs->Details) == 0); + } + return (lhs->Code == rhs->Code) && StrsEqual; + } +}; +struct ErrPtrHash { + size_t operator()(const ErrPtrT &e) const { + if (!e) { + // We shouldn't store empty errors (i.e. success), but just in case + return 0lu; + } else { + return std::hash{}(e->Code); + } + } +}; +using ErrSetT = std::unordered_set; +ErrSetT &errors(); + +struct ol_impl_result_t { + ol_impl_result_t(std::nullptr_t) : Result(OL_SUCCESS) {} + ol_impl_result_t(ol_errc_t Code) { + if (Code == OL_ERRC_SUCCESS) { + Result = nullptr; + } else { + auto Err = std::unique_ptr( + new ol_error_struct_t{Code, nullptr}); + Result = errors().emplace(std::move(Err)).first->get(); + } + } + + ol_impl_result_t(ol_errc_t Code, llvm::StringRef Details) { + assert(Code != OL_ERRC_SUCCESS); + Result = nullptr; + auto DetailsStr = errorStrs().insert(Details).first->getKeyData(); + auto Err = std::unique_ptr( + new ol_error_struct_t{Code, DetailsStr}); + Result = errors().emplace(std::move(Err)).first->get(); + } + + operator ol_result_t() { return Result; } + +private: + ol_result_t Result; +}; diff --git a/offload/liboffload/include/generated/OffloadAPI.h b/offload/liboffload/include/generated/OffloadAPI.h new file mode 100644 index 0000000000000..11fcc96625ab8 --- /dev/null +++ b/offload/liboffload/include/generated/OffloadAPI.h @@ -0,0 +1,610 @@ +//===- Auto-generated file, part of the LLVM/Offload project --------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// Auto-generated file, do not manually edit. + +#pragma once + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +/////////////////////////////////////////////////////////////////////////////// +#ifndef OL_VERSION_MAJOR +/// @brief Major version of the Offload API +#define OL_VERSION_MAJOR 0 +#endif // OL_VERSION_MAJOR + +/////////////////////////////////////////////////////////////////////////////// +#ifndef OL_VERSION_MINOR +/// @brief Minor version of the Offload API +#define OL_VERSION_MINOR 0 +#endif // OL_VERSION_MINOR + +/////////////////////////////////////////////////////////////////////////////// +#ifndef OL_VERSION_PATCH +/// @brief Patch version of the Offload API +#define OL_VERSION_PATCH 1 +#endif // OL_VERSION_PATCH + +/////////////////////////////////////////////////////////////////////////////// +#ifndef OL_APICALL +#if defined(_WIN32) +/// @brief Calling convention for all API functions +#define OL_APICALL __cdecl +#else +#define OL_APICALL +#endif // defined(_WIN32) +#endif // OL_APICALL + +/////////////////////////////////////////////////////////////////////////////// +#ifndef OL_APIEXPORT +#if defined(_WIN32) +/// @brief Microsoft-specific dllexport storage-class attribute +#define OL_APIEXPORT __declspec(dllexport) +#else +#define OL_APIEXPORT +#endif // defined(_WIN32) +#endif // OL_APIEXPORT + +/////////////////////////////////////////////////////////////////////////////// +#ifndef OL_DLLEXPORT +#if defined(_WIN32) +/// @brief Microsoft-specific dllexport storage-class attribute +#define OL_DLLEXPORT __declspec(dllexport) +#endif // defined(_WIN32) +#endif // OL_DLLEXPORT + +/////////////////////////////////////////////////////////////////////////////// +#ifndef OL_DLLEXPORT +#if __GNUC__ >= 4 +/// @brief GCC-specific dllexport storage-class attribute +#define OL_DLLEXPORT __attribute__((visibility("default"))) +#else +#define OL_DLLEXPORT +#endif // __GNUC__ >= 4 +#endif // OL_DLLEXPORT + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Handle of a platform instance +typedef struct ol_platform_handle_t_ *ol_platform_handle_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Handle of platform's device object +typedef struct ol_device_handle_t_ *ol_device_handle_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Handle of context object +typedef struct ol_context_handle_t_ *ol_context_handle_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Defines Return/Error codes +typedef enum ol_errc_t { + /// Success + OL_ERRC_SUCCESS = 0, + /// Invalid Value + OL_ERRC_INVALID_VALUE = 1, + /// Invalid platform + OL_ERRC_INVALID_PLATFORM = 2, + /// Device not found + OL_ERRC_DEVICE_NOT_FOUND = 3, + /// Invalid device + OL_ERRC_INVALID_DEVICE = 4, + /// Device hung, reset, was removed, or driver update occurred + OL_ERRC_DEVICE_LOST = 5, + /// plugin is not initialized or specific entry-point is not implemented + OL_ERRC_UNINITIALIZED = 6, + /// Out of resources + OL_ERRC_OUT_OF_RESOURCES = 7, + /// generic error code for unsupported versions + OL_ERRC_UNSUPPORTED_VERSION = 8, + /// generic error code for unsupported features + OL_ERRC_UNSUPPORTED_FEATURE = 9, + /// generic error code for invalid arguments + OL_ERRC_INVALID_ARGUMENT = 10, + /// handle argument is not valid + OL_ERRC_INVALID_NULL_HANDLE = 11, + /// pointer argument may not be nullptr + OL_ERRC_INVALID_NULL_POINTER = 12, + /// invalid size or dimensions (e.g., must not be zero, or is out of bounds) + OL_ERRC_INVALID_SIZE = 13, + /// enumerator argument is not valid + OL_ERRC_INVALID_ENUMERATION = 14, + /// enumerator argument is not supported by the device + OL_ERRC_UNSUPPORTED_ENUMERATION = 15, + /// Unknown or internal error + OL_ERRC_UNKNOWN = 16, + /// @cond + OL_ERRC_FORCE_UINT32 = 0x7fffffff + /// @endcond + +} ol_errc_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Details of the error condition returned by an API call +typedef struct ol_error_struct_t { + ol_errc_t Code; /// The error code + const char *Details; /// String containing error details +} ol_error_struct_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Result type returned by all entry points. +typedef const ol_error_struct_t *ol_result_t; + +/////////////////////////////////////////////////////////////////////////////// +#ifndef OL_SUCCESS +/// @brief Success condition +#define OL_SUCCESS NULL +#endif // OL_SUCCESS + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Code location information that can optionally be associated with an +/// API call +typedef struct ol_code_location_t { + const char *FunctionName; /// Function name + const char *SourceFile; /// Source code file + uint32_t LineNumber; /// Source code line number + uint32_t ColumnNumber; /// Source code column number +} ol_code_location_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Perform initialization of the Offload library and plugins +/// +/// @details +/// - This must be the first API call made by a user of the Offload library +/// - Each call will increment an internal reference count that is +/// decremented by `olShutDown` +/// +/// @returns +/// - ::OL_RESULT_SUCCESS +/// - ::OL_ERRC_UNINITIALIZED +/// - ::OL_ERRC_DEVICE_LOST +/// - ::OL_ERRC_INVALID_NULL_HANDLE +/// - ::OL_ERRC_INVALID_NULL_POINTER +OL_APIEXPORT ol_result_t OL_APICALL olInit(); + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Release the resources in use by Offload +/// +/// @details +/// - This decrements an internal reference count. When this reaches 0, all +/// resources will be released +/// - Subsequent API calls made after this are not valid +/// +/// @returns +/// - ::OL_RESULT_SUCCESS +/// - ::OL_ERRC_UNINITIALIZED +/// - ::OL_ERRC_DEVICE_LOST +/// - ::OL_ERRC_INVALID_NULL_HANDLE +/// - ::OL_ERRC_INVALID_NULL_POINTER +OL_APIEXPORT ol_result_t OL_APICALL olShutDown(); + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Retrieves all available platforms +/// +/// @details +/// - Multiple calls to this function will return identical platforms +/// handles, in the same order. +/// +/// @returns +/// - ::OL_RESULT_SUCCESS +/// - ::OL_ERRC_UNINITIALIZED +/// - ::OL_ERRC_DEVICE_LOST +/// - ::OL_ERRC_INVALID_SIZE +/// + `NumEntries == 0` +/// - ::OL_ERRC_INVALID_NULL_HANDLE +/// - ::OL_ERRC_INVALID_NULL_POINTER +/// + `NULL == Platforms` +OL_APIEXPORT ol_result_t OL_APICALL olGetPlatform( + // [in] The number of platforms to be added to Platforms. NumEntries must be + // greater than zero. + uint32_t NumEntries, + // [out] Array of handle of platforms. If NumEntries is less than the number + // of platforms available, then olGetPlatform shall only retrieve that + // number of platforms. + ol_platform_handle_t *Platforms); + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Retrieves the number of available platforms +/// +/// @details +/// +/// @returns +/// - ::OL_RESULT_SUCCESS +/// - ::OL_ERRC_UNINITIALIZED +/// - ::OL_ERRC_DEVICE_LOST +/// - ::OL_ERRC_INVALID_NULL_HANDLE +/// - ::OL_ERRC_INVALID_NULL_POINTER +/// + `NULL == NumPlatforms` +OL_APIEXPORT ol_result_t OL_APICALL olGetPlatformCount( + // [out] returns the total number of platforms available. + uint32_t *NumPlatforms); + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Supported platform info +typedef enum ol_platform_info_t { + /// [char[]] The string denoting name of the platform. The size of the info + /// needs to be dynamically queried. + OL_PLATFORM_INFO_NAME = 0, + /// [char[]] The string denoting name of the vendor of the platform. The size + /// of the info needs to be dynamically queried. + OL_PLATFORM_INFO_VENDOR_NAME = 1, + /// [char[]] The string denoting the version of the platform. The size of the + /// info needs to be dynamically queried. + OL_PLATFORM_INFO_VERSION = 2, + /// [ol_platform_backend_t] The native backend of the platform. + OL_PLATFORM_INFO_BACKEND = 3, + /// @cond + OL_PLATFORM_INFO_FORCE_UINT32 = 0x7fffffff + /// @endcond + +} ol_platform_info_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Identifies the native backend of the platform +typedef enum ol_platform_backend_t { + /// The backend is not recognized + OL_PLATFORM_BACKEND_UNKNOWN = 0, + /// The backend is CUDA + OL_PLATFORM_BACKEND_CUDA = 1, + /// The backend is AMDGPU + OL_PLATFORM_BACKEND_AMDGPU = 2, + /// @cond + OL_PLATFORM_BACKEND_FORCE_UINT32 = 0x7fffffff + /// @endcond + +} ol_platform_backend_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Queries the given property of the platform +/// +/// @details +/// - `olGetPlatformInfoSize` can be used to query the storage size required +/// for the given query. +/// +/// @returns +/// - ::OL_RESULT_SUCCESS +/// - ::OL_ERRC_UNINITIALIZED +/// - ::OL_ERRC_DEVICE_LOST +/// - ::OL_ERRC_UNSUPPORTED_ENUMERATION +/// + If `PropName` is not supported by the platform. +/// - ::OL_ERRC_INVALID_SIZE +/// + `PropSize == 0` +/// + If `PropSize` is less than the real number of bytes needed to +/// return the info. +/// - ::OL_ERRC_INVALID_PLATFORM +/// - ::OL_ERRC_INVALID_NULL_HANDLE +/// + `NULL == Platform` +/// - ::OL_ERRC_INVALID_NULL_POINTER +/// + `NULL == PropValue` +OL_APIEXPORT ol_result_t OL_APICALL olGetPlatformInfo( + // [in] handle of the platform + ol_platform_handle_t Platform, + // [in] type of the info to retrieve + ol_platform_info_t PropName, + // [in] the number of bytes pointed to by pPlatformInfo. + size_t PropSize, + // [out] 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 + // OL_ERRC_INVALID_SIZE error is returned and pPlatformInfo is not used. + void *PropValue); + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Returns the storage size of the given platform query +/// +/// @details +/// +/// @returns +/// - ::OL_RESULT_SUCCESS +/// - ::OL_ERRC_UNINITIALIZED +/// - ::OL_ERRC_DEVICE_LOST +/// - ::OL_ERRC_UNSUPPORTED_ENUMERATION +/// + If `PropName` is not supported by the platform. +/// - ::OL_ERRC_INVALID_PLATFORM +/// - ::OL_ERRC_INVALID_NULL_HANDLE +/// + `NULL == Platform` +/// - ::OL_ERRC_INVALID_NULL_POINTER +/// + `NULL == PropSizeRet` +OL_APIEXPORT ol_result_t OL_APICALL olGetPlatformInfoSize( + // [in] handle of the platform + ol_platform_handle_t Platform, + // [in] type of the info to query + ol_platform_info_t PropName, + // [out] pointer to the number of bytes required to store the query + size_t *PropSizeRet); + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Supported device types +typedef enum ol_device_type_t { + /// The default device type as preferred by the runtime + OL_DEVICE_TYPE_DEFAULT = 0, + /// Devices of all types + OL_DEVICE_TYPE_ALL = 1, + /// GPU device type + OL_DEVICE_TYPE_GPU = 2, + /// CPU device type + OL_DEVICE_TYPE_CPU = 3, + /// @cond + OL_DEVICE_TYPE_FORCE_UINT32 = 0x7fffffff + /// @endcond + +} ol_device_type_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Supported device info +typedef enum ol_device_info_t { + /// [ol_device_type_t] type of the device + OL_DEVICE_INFO_TYPE = 0, + /// [ol_platform_handle_t] the platform associated with the device + OL_DEVICE_INFO_PLATFORM = 1, + /// [char[]] Device name + OL_DEVICE_INFO_NAME = 2, + /// [char[]] Device vendor + OL_DEVICE_INFO_VENDOR = 3, + /// [char[]] Driver version + OL_DEVICE_INFO_DRIVER_VERSION = 4, + /// @cond + OL_DEVICE_INFO_FORCE_UINT32 = 0x7fffffff + /// @endcond + +} ol_device_info_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Retrieves the number of available devices within a platform +/// +/// @details +/// +/// @returns +/// - ::OL_RESULT_SUCCESS +/// - ::OL_ERRC_UNINITIALIZED +/// - ::OL_ERRC_DEVICE_LOST +/// - ::OL_ERRC_INVALID_NULL_HANDLE +/// + `NULL == Platform` +/// - ::OL_ERRC_INVALID_NULL_POINTER +/// + `NULL == NumDevices` +OL_APIEXPORT ol_result_t OL_APICALL olGetDeviceCount( + // [in] handle of the platform instance + ol_platform_handle_t Platform, + // [out] pointer to the number of devices. + uint32_t *NumDevices); + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Retrieves devices within a platform +/// +/// @details +/// - Multiple calls to this function will return identical device handles, +/// in the same order. +/// +/// @returns +/// - ::OL_RESULT_SUCCESS +/// - ::OL_ERRC_UNINITIALIZED +/// - ::OL_ERRC_DEVICE_LOST +/// - ::OL_ERRC_INVALID_SIZE +/// + `NumEntries == 0` +/// - ::OL_ERRC_INVALID_NULL_HANDLE +/// + `NULL == Platform` +/// - ::OL_ERRC_INVALID_NULL_POINTER +/// + `NULL == Devices` +OL_APIEXPORT ol_result_t OL_APICALL olGetDevice( + // [in] handle of the platform instance + ol_platform_handle_t Platform, + // [in] the number of devices to be added to phDevices, which must be + // greater than zero + uint32_t NumEntries, + // [out] Array of device handles. If NumEntries is less than the number of + // devices available, then this function shall only retrieve that number of + // devices. + ol_device_handle_t *Devices); + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Queries the given property of the device +/// +/// @details +/// +/// @returns +/// - ::OL_RESULT_SUCCESS +/// - ::OL_ERRC_UNINITIALIZED +/// - ::OL_ERRC_DEVICE_LOST +/// - ::OL_ERRC_UNSUPPORTED_ENUMERATION +/// + If `PropName` is not supported by the device. +/// - ::OL_ERRC_INVALID_SIZE +/// + `PropSize == 0` +/// + If `PropSize` is less than the real number of bytes needed to +/// return the info. +/// - ::OL_ERRC_INVALID_DEVICE +/// - ::OL_ERRC_INVALID_NULL_HANDLE +/// + `NULL == Device` +/// - ::OL_ERRC_INVALID_NULL_POINTER +/// + `NULL == PropValue` +OL_APIEXPORT ol_result_t OL_APICALL olGetDeviceInfo( + // [in] handle of the device instance + ol_device_handle_t Device, + // [in] type of the info to retrieve + ol_device_info_t PropName, + // [in] the number of bytes pointed to by PropValue. + size_t PropSize, + // [out] 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 + // OL_ERRC_INVALID_SIZE error is returned and PropValue is not used. + void *PropValue); + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Returns the storage size of the given device query +/// +/// @details +/// +/// @returns +/// - ::OL_RESULT_SUCCESS +/// - ::OL_ERRC_UNINITIALIZED +/// - ::OL_ERRC_DEVICE_LOST +/// - ::OL_ERRC_UNSUPPORTED_ENUMERATION +/// + If `PropName` is not supported by the device. +/// - ::OL_ERRC_INVALID_DEVICE +/// - ::OL_ERRC_INVALID_NULL_HANDLE +/// + `NULL == Device` +/// - ::OL_ERRC_INVALID_NULL_POINTER +/// + `NULL == PropSizeRet` +OL_APIEXPORT ol_result_t OL_APICALL olGetDeviceInfoSize( + // [in] handle of the device instance + ol_device_handle_t Device, + // [in] type of the info to retrieve + ol_device_info_t PropName, + // [out] pointer to the number of bytes required to store the query + size_t *PropSizeRet); + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Function parameters for olGetPlatform +/// @details Each entry is a pointer to the parameter passed to the function; +typedef struct ol_get_platform_params_t { + uint32_t *pNumEntries; + ol_platform_handle_t **pPlatforms; +} ol_get_platform_params_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Function parameters for olGetPlatformCount +/// @details Each entry is a pointer to the parameter passed to the function; +typedef struct ol_get_platform_count_params_t { + uint32_t **pNumPlatforms; +} ol_get_platform_count_params_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Function parameters for olGetPlatformInfo +/// @details Each entry is a pointer to the parameter passed to the function; +typedef struct ol_get_platform_info_params_t { + ol_platform_handle_t *pPlatform; + ol_platform_info_t *pPropName; + size_t *pPropSize; + void **pPropValue; +} ol_get_platform_info_params_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Function parameters for olGetPlatformInfoSize +/// @details Each entry is a pointer to the parameter passed to the function; +typedef struct ol_get_platform_info_size_params_t { + ol_platform_handle_t *pPlatform; + ol_platform_info_t *pPropName; + size_t **pPropSizeRet; +} ol_get_platform_info_size_params_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Function parameters for olGetDeviceCount +/// @details Each entry is a pointer to the parameter passed to the function; +typedef struct ol_get_device_count_params_t { + ol_platform_handle_t *pPlatform; + uint32_t **pNumDevices; +} ol_get_device_count_params_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Function parameters for olGetDevice +/// @details Each entry is a pointer to the parameter passed to the function; +typedef struct ol_get_device_params_t { + ol_platform_handle_t *pPlatform; + uint32_t *pNumEntries; + ol_device_handle_t **pDevices; +} ol_get_device_params_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Function parameters for olGetDeviceInfo +/// @details Each entry is a pointer to the parameter passed to the function; +typedef struct ol_get_device_info_params_t { + ol_device_handle_t *pDevice; + ol_device_info_t *pPropName; + size_t *pPropSize; + void **pPropValue; +} ol_get_device_info_params_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Function parameters for olGetDeviceInfoSize +/// @details Each entry is a pointer to the parameter passed to the function; +typedef struct ol_get_device_info_size_params_t { + ol_device_handle_t *pDevice; + ol_device_info_t *pPropName; + size_t **pPropSizeRet; +} ol_get_device_info_size_params_t; + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Variant of olInit that also sets source code location information +/// @details See also ::olInit +OL_APIEXPORT ol_result_t OL_APICALL +olInitWithCodeLoc(ol_code_location_t *CodeLocation); + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Variant of olShutDown that also sets source code location information +/// @details See also ::olShutDown +OL_APIEXPORT ol_result_t OL_APICALL +olShutDownWithCodeLoc(ol_code_location_t *CodeLocation); + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Variant of olGetPlatform that also sets source code location +/// information +/// @details See also ::olGetPlatform +OL_APIEXPORT ol_result_t OL_APICALL +olGetPlatformWithCodeLoc(uint32_t NumEntries, ol_platform_handle_t *Platforms, + ol_code_location_t *CodeLocation); + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Variant of olGetPlatformCount that also sets source code location +/// information +/// @details See also ::olGetPlatformCount +OL_APIEXPORT ol_result_t OL_APICALL olGetPlatformCountWithCodeLoc( + uint32_t *NumPlatforms, ol_code_location_t *CodeLocation); + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Variant of olGetPlatformInfo that also sets source code location +/// information +/// @details See also ::olGetPlatformInfo +OL_APIEXPORT ol_result_t OL_APICALL olGetPlatformInfoWithCodeLoc( + ol_platform_handle_t Platform, ol_platform_info_t PropName, size_t PropSize, + void *PropValue, ol_code_location_t *CodeLocation); + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Variant of olGetPlatformInfoSize that also sets source code location +/// information +/// @details See also ::olGetPlatformInfoSize +OL_APIEXPORT ol_result_t OL_APICALL olGetPlatformInfoSizeWithCodeLoc( + ol_platform_handle_t Platform, ol_platform_info_t PropName, + size_t *PropSizeRet, ol_code_location_t *CodeLocation); + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Variant of olGetDeviceCount that also sets source code location +/// information +/// @details See also ::olGetDeviceCount +OL_APIEXPORT ol_result_t OL_APICALL +olGetDeviceCountWithCodeLoc(ol_platform_handle_t Platform, uint32_t *NumDevices, + ol_code_location_t *CodeLocation); + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Variant of olGetDevice that also sets source code location +/// information +/// @details See also ::olGetDevice +OL_APIEXPORT ol_result_t OL_APICALL olGetDeviceWithCodeLoc( + ol_platform_handle_t Platform, uint32_t NumEntries, + ol_device_handle_t *Devices, ol_code_location_t *CodeLocation); + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Variant of olGetDeviceInfo that also sets source code location +/// information +/// @details See also ::olGetDeviceInfo +OL_APIEXPORT ol_result_t OL_APICALL olGetDeviceInfoWithCodeLoc( + ol_device_handle_t Device, ol_device_info_t PropName, size_t PropSize, + void *PropValue, ol_code_location_t *CodeLocation); + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Variant of olGetDeviceInfoSize that also sets source code location +/// information +/// @details See also ::olGetDeviceInfoSize +OL_APIEXPORT ol_result_t OL_APICALL olGetDeviceInfoSizeWithCodeLoc( + ol_device_handle_t Device, ol_device_info_t PropName, size_t *PropSizeRet, + ol_code_location_t *CodeLocation); + +#if defined(__cplusplus) +} // extern "C" +#endif diff --git a/offload/liboffload/include/generated/OffloadEntryPoints.inc b/offload/liboffload/include/generated/OffloadEntryPoints.inc new file mode 100644 index 0000000000000..49c1c8169615e --- /dev/null +++ b/offload/liboffload/include/generated/OffloadEntryPoints.inc @@ -0,0 +1,441 @@ +//===- Auto-generated file, part of the LLVM/Offload project --------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +/////////////////////////////////////////////////////////////////////////////// +ol_impl_result_t olInit_val() { + if (true /*enableParameterValidation*/) { + } + + return olInit_impl(); +} +OL_APIEXPORT ol_result_t OL_APICALL olInit() { + if (offloadConfig().TracingEnabled) { + std::cout << "---> olInit"; + } + + ol_result_t Result = olInit_val(); + + if (offloadConfig().TracingEnabled) { + std::cout << "()"; + std::cout << "-> " << Result << "\n"; + if (Result && Result->Details) { + std::cout << " *Error Details* " << Result->Details << " \n"; + } + } + return Result; +} +ol_result_t olInitWithCodeLoc(ol_code_location_t *CodeLocation) { + currentCodeLocation() = CodeLocation; + ol_result_t Result = olInit(); + + currentCodeLocation() = nullptr; + return Result; +} + +/////////////////////////////////////////////////////////////////////////////// +ol_impl_result_t olShutDown_val() { + if (true /*enableParameterValidation*/) { + } + + return olShutDown_impl(); +} +OL_APIEXPORT ol_result_t OL_APICALL olShutDown() { + if (offloadConfig().TracingEnabled) { + std::cout << "---> olShutDown"; + } + + ol_result_t Result = olShutDown_val(); + + if (offloadConfig().TracingEnabled) { + std::cout << "()"; + std::cout << "-> " << Result << "\n"; + if (Result && Result->Details) { + std::cout << " *Error Details* " << Result->Details << " \n"; + } + } + return Result; +} +ol_result_t olShutDownWithCodeLoc(ol_code_location_t *CodeLocation) { + currentCodeLocation() = CodeLocation; + ol_result_t Result = olShutDown(); + + currentCodeLocation() = nullptr; + return Result; +} + +/////////////////////////////////////////////////////////////////////////////// +ol_impl_result_t olGetPlatform_val(uint32_t NumEntries, + ol_platform_handle_t *Platforms) { + if (true /*enableParameterValidation*/) { + if (NumEntries == 0) { + return OL_ERRC_INVALID_SIZE; + } + + if (NULL == Platforms) { + return OL_ERRC_INVALID_NULL_POINTER; + } + } + + return olGetPlatform_impl(NumEntries, Platforms); +} +OL_APIEXPORT ol_result_t OL_APICALL +olGetPlatform(uint32_t NumEntries, ol_platform_handle_t *Platforms) { + if (offloadConfig().TracingEnabled) { + std::cout << "---> olGetPlatform"; + } + + ol_result_t Result = olGetPlatform_val(NumEntries, Platforms); + + if (offloadConfig().TracingEnabled) { + ol_get_platform_params_t Params = {&NumEntries, &Platforms}; + std::cout << "(" << &Params << ")"; + std::cout << "-> " << Result << "\n"; + if (Result && Result->Details) { + std::cout << " *Error Details* " << Result->Details << " \n"; + } + } + return Result; +} +ol_result_t olGetPlatformWithCodeLoc(uint32_t NumEntries, + ol_platform_handle_t *Platforms, + ol_code_location_t *CodeLocation) { + currentCodeLocation() = CodeLocation; + ol_result_t Result = olGetPlatform(NumEntries, Platforms); + + currentCodeLocation() = nullptr; + return Result; +} + +/////////////////////////////////////////////////////////////////////////////// +ol_impl_result_t olGetPlatformCount_val(uint32_t *NumPlatforms) { + if (true /*enableParameterValidation*/) { + if (NULL == NumPlatforms) { + return OL_ERRC_INVALID_NULL_POINTER; + } + } + + return olGetPlatformCount_impl(NumPlatforms); +} +OL_APIEXPORT ol_result_t OL_APICALL olGetPlatformCount(uint32_t *NumPlatforms) { + if (offloadConfig().TracingEnabled) { + std::cout << "---> olGetPlatformCount"; + } + + ol_result_t Result = olGetPlatformCount_val(NumPlatforms); + + if (offloadConfig().TracingEnabled) { + ol_get_platform_count_params_t Params = {&NumPlatforms}; + std::cout << "(" << &Params << ")"; + std::cout << "-> " << Result << "\n"; + if (Result && Result->Details) { + std::cout << " *Error Details* " << Result->Details << " \n"; + } + } + return Result; +} +ol_result_t olGetPlatformCountWithCodeLoc(uint32_t *NumPlatforms, + ol_code_location_t *CodeLocation) { + currentCodeLocation() = CodeLocation; + ol_result_t Result = olGetPlatformCount(NumPlatforms); + + currentCodeLocation() = nullptr; + return Result; +} + +/////////////////////////////////////////////////////////////////////////////// +ol_impl_result_t olGetPlatformInfo_val(ol_platform_handle_t Platform, + ol_platform_info_t PropName, + size_t PropSize, void *PropValue) { + if (true /*enableParameterValidation*/) { + if (PropSize == 0) { + return OL_ERRC_INVALID_SIZE; + } + + if (NULL == Platform) { + return OL_ERRC_INVALID_NULL_HANDLE; + } + + if (NULL == PropValue) { + return OL_ERRC_INVALID_NULL_POINTER; + } + } + + return olGetPlatformInfo_impl(Platform, PropName, PropSize, PropValue); +} +OL_APIEXPORT ol_result_t OL_APICALL +olGetPlatformInfo(ol_platform_handle_t Platform, ol_platform_info_t PropName, + size_t PropSize, void *PropValue) { + if (offloadConfig().TracingEnabled) { + std::cout << "---> olGetPlatformInfo"; + } + + ol_result_t Result = + olGetPlatformInfo_val(Platform, PropName, PropSize, PropValue); + + if (offloadConfig().TracingEnabled) { + ol_get_platform_info_params_t Params = {&Platform, &PropName, &PropSize, + &PropValue}; + std::cout << "(" << &Params << ")"; + std::cout << "-> " << Result << "\n"; + if (Result && Result->Details) { + std::cout << " *Error Details* " << Result->Details << " \n"; + } + } + return Result; +} +ol_result_t olGetPlatformInfoWithCodeLoc(ol_platform_handle_t Platform, + ol_platform_info_t PropName, + size_t PropSize, void *PropValue, + ol_code_location_t *CodeLocation) { + currentCodeLocation() = CodeLocation; + ol_result_t Result = + olGetPlatformInfo(Platform, PropName, PropSize, PropValue); + + currentCodeLocation() = nullptr; + return Result; +} + +/////////////////////////////////////////////////////////////////////////////// +ol_impl_result_t olGetPlatformInfoSize_val(ol_platform_handle_t Platform, + ol_platform_info_t PropName, + size_t *PropSizeRet) { + if (true /*enableParameterValidation*/) { + if (NULL == Platform) { + return OL_ERRC_INVALID_NULL_HANDLE; + } + + if (NULL == PropSizeRet) { + return OL_ERRC_INVALID_NULL_POINTER; + } + } + + return olGetPlatformInfoSize_impl(Platform, PropName, PropSizeRet); +} +OL_APIEXPORT ol_result_t OL_APICALL +olGetPlatformInfoSize(ol_platform_handle_t Platform, + ol_platform_info_t PropName, size_t *PropSizeRet) { + if (offloadConfig().TracingEnabled) { + std::cout << "---> olGetPlatformInfoSize"; + } + + ol_result_t Result = + olGetPlatformInfoSize_val(Platform, PropName, PropSizeRet); + + if (offloadConfig().TracingEnabled) { + ol_get_platform_info_size_params_t Params = {&Platform, &PropName, + &PropSizeRet}; + std::cout << "(" << &Params << ")"; + std::cout << "-> " << Result << "\n"; + if (Result && Result->Details) { + std::cout << " *Error Details* " << Result->Details << " \n"; + } + } + return Result; +} +ol_result_t olGetPlatformInfoSizeWithCodeLoc(ol_platform_handle_t Platform, + ol_platform_info_t PropName, + size_t *PropSizeRet, + ol_code_location_t *CodeLocation) { + currentCodeLocation() = CodeLocation; + ol_result_t Result = olGetPlatformInfoSize(Platform, PropName, PropSizeRet); + + currentCodeLocation() = nullptr; + return Result; +} + +/////////////////////////////////////////////////////////////////////////////// +ol_impl_result_t olGetDeviceCount_val(ol_platform_handle_t Platform, + uint32_t *NumDevices) { + if (true /*enableParameterValidation*/) { + if (NULL == Platform) { + return OL_ERRC_INVALID_NULL_HANDLE; + } + + if (NULL == NumDevices) { + return OL_ERRC_INVALID_NULL_POINTER; + } + } + + return olGetDeviceCount_impl(Platform, NumDevices); +} +OL_APIEXPORT ol_result_t OL_APICALL +olGetDeviceCount(ol_platform_handle_t Platform, uint32_t *NumDevices) { + if (offloadConfig().TracingEnabled) { + std::cout << "---> olGetDeviceCount"; + } + + ol_result_t Result = olGetDeviceCount_val(Platform, NumDevices); + + if (offloadConfig().TracingEnabled) { + ol_get_device_count_params_t Params = {&Platform, &NumDevices}; + std::cout << "(" << &Params << ")"; + std::cout << "-> " << Result << "\n"; + if (Result && Result->Details) { + std::cout << " *Error Details* " << Result->Details << " \n"; + } + } + return Result; +} +ol_result_t olGetDeviceCountWithCodeLoc(ol_platform_handle_t Platform, + uint32_t *NumDevices, + ol_code_location_t *CodeLocation) { + currentCodeLocation() = CodeLocation; + ol_result_t Result = olGetDeviceCount(Platform, NumDevices); + + currentCodeLocation() = nullptr; + return Result; +} + +/////////////////////////////////////////////////////////////////////////////// +ol_impl_result_t olGetDevice_val(ol_platform_handle_t Platform, + uint32_t NumEntries, + ol_device_handle_t *Devices) { + if (true /*enableParameterValidation*/) { + if (NumEntries == 0) { + return OL_ERRC_INVALID_SIZE; + } + + if (NULL == Platform) { + return OL_ERRC_INVALID_NULL_HANDLE; + } + + if (NULL == Devices) { + return OL_ERRC_INVALID_NULL_POINTER; + } + } + + return olGetDevice_impl(Platform, NumEntries, Devices); +} +OL_APIEXPORT ol_result_t OL_APICALL olGetDevice(ol_platform_handle_t Platform, + uint32_t NumEntries, + ol_device_handle_t *Devices) { + if (offloadConfig().TracingEnabled) { + std::cout << "---> olGetDevice"; + } + + ol_result_t Result = olGetDevice_val(Platform, NumEntries, Devices); + + if (offloadConfig().TracingEnabled) { + ol_get_device_params_t Params = {&Platform, &NumEntries, &Devices}; + std::cout << "(" << &Params << ")"; + std::cout << "-> " << Result << "\n"; + if (Result && Result->Details) { + std::cout << " *Error Details* " << Result->Details << " \n"; + } + } + return Result; +} +ol_result_t olGetDeviceWithCodeLoc(ol_platform_handle_t Platform, + uint32_t NumEntries, + ol_device_handle_t *Devices, + ol_code_location_t *CodeLocation) { + currentCodeLocation() = CodeLocation; + ol_result_t Result = olGetDevice(Platform, NumEntries, Devices); + + currentCodeLocation() = nullptr; + return Result; +} + +/////////////////////////////////////////////////////////////////////////////// +ol_impl_result_t olGetDeviceInfo_val(ol_device_handle_t Device, + ol_device_info_t PropName, size_t PropSize, + void *PropValue) { + if (true /*enableParameterValidation*/) { + if (PropSize == 0) { + return OL_ERRC_INVALID_SIZE; + } + + if (NULL == Device) { + return OL_ERRC_INVALID_NULL_HANDLE; + } + + if (NULL == PropValue) { + return OL_ERRC_INVALID_NULL_POINTER; + } + } + + return olGetDeviceInfo_impl(Device, PropName, PropSize, PropValue); +} +OL_APIEXPORT ol_result_t OL_APICALL olGetDeviceInfo(ol_device_handle_t Device, + ol_device_info_t PropName, + size_t PropSize, + void *PropValue) { + if (offloadConfig().TracingEnabled) { + std::cout << "---> olGetDeviceInfo"; + } + + ol_result_t Result = + olGetDeviceInfo_val(Device, PropName, PropSize, PropValue); + + if (offloadConfig().TracingEnabled) { + ol_get_device_info_params_t Params = {&Device, &PropName, &PropSize, + &PropValue}; + std::cout << "(" << &Params << ")"; + std::cout << "-> " << Result << "\n"; + if (Result && Result->Details) { + std::cout << " *Error Details* " << Result->Details << " \n"; + } + } + return Result; +} +ol_result_t olGetDeviceInfoWithCodeLoc(ol_device_handle_t Device, + ol_device_info_t PropName, + size_t PropSize, void *PropValue, + ol_code_location_t *CodeLocation) { + currentCodeLocation() = CodeLocation; + ol_result_t Result = olGetDeviceInfo(Device, PropName, PropSize, PropValue); + + currentCodeLocation() = nullptr; + return Result; +} + +/////////////////////////////////////////////////////////////////////////////// +ol_impl_result_t olGetDeviceInfoSize_val(ol_device_handle_t Device, + ol_device_info_t PropName, + size_t *PropSizeRet) { + if (true /*enableParameterValidation*/) { + if (NULL == Device) { + return OL_ERRC_INVALID_NULL_HANDLE; + } + + if (NULL == PropSizeRet) { + return OL_ERRC_INVALID_NULL_POINTER; + } + } + + return olGetDeviceInfoSize_impl(Device, PropName, PropSizeRet); +} +OL_APIEXPORT ol_result_t OL_APICALL olGetDeviceInfoSize( + ol_device_handle_t Device, ol_device_info_t PropName, size_t *PropSizeRet) { + if (offloadConfig().TracingEnabled) { + std::cout << "---> olGetDeviceInfoSize"; + } + + ol_result_t Result = olGetDeviceInfoSize_val(Device, PropName, PropSizeRet); + + if (offloadConfig().TracingEnabled) { + ol_get_device_info_size_params_t Params = {&Device, &PropName, + &PropSizeRet}; + std::cout << "(" << &Params << ")"; + std::cout << "-> " << Result << "\n"; + if (Result && Result->Details) { + std::cout << " *Error Details* " << Result->Details << " \n"; + } + } + return Result; +} +ol_result_t olGetDeviceInfoSizeWithCodeLoc(ol_device_handle_t Device, + ol_device_info_t PropName, + size_t *PropSizeRet, + ol_code_location_t *CodeLocation) { + currentCodeLocation() = CodeLocation; + ol_result_t Result = olGetDeviceInfoSize(Device, PropName, PropSizeRet); + + currentCodeLocation() = nullptr; + return Result; +} diff --git a/offload/liboffload/include/generated/OffloadFuncs.inc b/offload/liboffload/include/generated/OffloadFuncs.inc new file mode 100644 index 0000000000000..48115493c790f --- /dev/null +++ b/offload/liboffload/include/generated/OffloadFuncs.inc @@ -0,0 +1,34 @@ +//===- Auto-generated file, part of the LLVM/Offload project --------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef OFFLOAD_FUNC +#error Please define the macro OFFLOAD_FUNC(Function) +#endif + +OFFLOAD_FUNC(olInit) +OFFLOAD_FUNC(olShutDown) +OFFLOAD_FUNC(olGetPlatform) +OFFLOAD_FUNC(olGetPlatformCount) +OFFLOAD_FUNC(olGetPlatformInfo) +OFFLOAD_FUNC(olGetPlatformInfoSize) +OFFLOAD_FUNC(olGetDeviceCount) +OFFLOAD_FUNC(olGetDevice) +OFFLOAD_FUNC(olGetDeviceInfo) +OFFLOAD_FUNC(olGetDeviceInfoSize) +OFFLOAD_FUNC(olInitWithCodeLoc) +OFFLOAD_FUNC(olShutDownWithCodeLoc) +OFFLOAD_FUNC(olGetPlatformWithCodeLoc) +OFFLOAD_FUNC(olGetPlatformCountWithCodeLoc) +OFFLOAD_FUNC(olGetPlatformInfoWithCodeLoc) +OFFLOAD_FUNC(olGetPlatformInfoSizeWithCodeLoc) +OFFLOAD_FUNC(olGetDeviceCountWithCodeLoc) +OFFLOAD_FUNC(olGetDeviceWithCodeLoc) +OFFLOAD_FUNC(olGetDeviceInfoWithCodeLoc) +OFFLOAD_FUNC(olGetDeviceInfoSizeWithCodeLoc) + +#undef OFFLOAD_FUNC diff --git a/offload/liboffload/include/generated/OffloadImplFuncDecls.inc b/offload/liboffload/include/generated/OffloadImplFuncDecls.inc new file mode 100644 index 0000000000000..5b26b2653a05d --- /dev/null +++ b/offload/liboffload/include/generated/OffloadImplFuncDecls.inc @@ -0,0 +1,38 @@ +//===- Auto-generated file, part of the LLVM/Offload project --------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +ol_impl_result_t olInit_impl(); + +ol_impl_result_t olShutDown_impl(); + +ol_impl_result_t olGetPlatform_impl(uint32_t NumEntries, + ol_platform_handle_t *Platforms); + +ol_impl_result_t olGetPlatformCount_impl(uint32_t *NumPlatforms); + +ol_impl_result_t olGetPlatformInfo_impl(ol_platform_handle_t Platform, + ol_platform_info_t PropName, + size_t PropSize, void *PropValue); + +ol_impl_result_t olGetPlatformInfoSize_impl(ol_platform_handle_t Platform, + ol_platform_info_t PropName, + size_t *PropSizeRet); + +ol_impl_result_t olGetDeviceCount_impl(ol_platform_handle_t Platform, + uint32_t *NumDevices); + +ol_impl_result_t olGetDevice_impl(ol_platform_handle_t Platform, + uint32_t NumEntries, + ol_device_handle_t *Devices); + +ol_impl_result_t olGetDeviceInfo_impl(ol_device_handle_t Device, + ol_device_info_t PropName, + size_t PropSize, void *PropValue); + +ol_impl_result_t olGetDeviceInfoSize_impl(ol_device_handle_t Device, + ol_device_info_t PropName, + size_t *PropSizeRet); diff --git a/offload/liboffload/include/generated/OffloadPrint.hpp b/offload/liboffload/include/generated/OffloadPrint.hpp new file mode 100644 index 0000000000000..8981bb054a4cb --- /dev/null +++ b/offload/liboffload/include/generated/OffloadPrint.hpp @@ -0,0 +1,428 @@ +//===- Auto-generated file, part of the LLVM/Offload project --------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// Auto-generated file, do not manually edit. + +#pragma once + +#include +#include + +template +inline ol_result_t printPtr(std::ostream &os, const T *ptr); +template +inline void printTagged(std::ostream &os, const void *ptr, T value, + size_t size); +template struct is_handle : std::false_type {}; +template <> struct is_handle : std::true_type {}; +template <> struct is_handle : std::true_type {}; +template <> struct is_handle : std::true_type {}; +template inline constexpr bool is_handle_v = is_handle::value; + +inline std::ostream &operator<<(std::ostream &os, enum ol_errc_t value); +inline std::ostream &operator<<(std::ostream &os, + enum ol_platform_info_t value); +inline std::ostream &operator<<(std::ostream &os, + enum ol_platform_backend_t value); +inline std::ostream &operator<<(std::ostream &os, enum ol_device_type_t value); +inline std::ostream &operator<<(std::ostream &os, enum ol_device_info_t value); + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Print operator for the ol_errc_t type +/// @returns std::ostream & +inline std::ostream &operator<<(std::ostream &os, enum ol_errc_t value) { + switch (value) { + case OL_ERRC_SUCCESS: + os << "OL_ERRC_SUCCESS"; + break; + case OL_ERRC_INVALID_VALUE: + os << "OL_ERRC_INVALID_VALUE"; + break; + case OL_ERRC_INVALID_PLATFORM: + os << "OL_ERRC_INVALID_PLATFORM"; + break; + case OL_ERRC_DEVICE_NOT_FOUND: + os << "OL_ERRC_DEVICE_NOT_FOUND"; + break; + case OL_ERRC_INVALID_DEVICE: + os << "OL_ERRC_INVALID_DEVICE"; + break; + case OL_ERRC_DEVICE_LOST: + os << "OL_ERRC_DEVICE_LOST"; + break; + case OL_ERRC_UNINITIALIZED: + os << "OL_ERRC_UNINITIALIZED"; + break; + case OL_ERRC_OUT_OF_RESOURCES: + os << "OL_ERRC_OUT_OF_RESOURCES"; + break; + case OL_ERRC_UNSUPPORTED_VERSION: + os << "OL_ERRC_UNSUPPORTED_VERSION"; + break; + case OL_ERRC_UNSUPPORTED_FEATURE: + os << "OL_ERRC_UNSUPPORTED_FEATURE"; + break; + case OL_ERRC_INVALID_ARGUMENT: + os << "OL_ERRC_INVALID_ARGUMENT"; + break; + case OL_ERRC_INVALID_NULL_HANDLE: + os << "OL_ERRC_INVALID_NULL_HANDLE"; + break; + case OL_ERRC_INVALID_NULL_POINTER: + os << "OL_ERRC_INVALID_NULL_POINTER"; + break; + case OL_ERRC_INVALID_SIZE: + os << "OL_ERRC_INVALID_SIZE"; + break; + case OL_ERRC_INVALID_ENUMERATION: + os << "OL_ERRC_INVALID_ENUMERATION"; + break; + case OL_ERRC_UNSUPPORTED_ENUMERATION: + os << "OL_ERRC_UNSUPPORTED_ENUMERATION"; + break; + case OL_ERRC_UNKNOWN: + os << "OL_ERRC_UNKNOWN"; + break; + default: + os << "unknown enumerator"; + break; + } + return os; +} + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Print operator for the ol_platform_info_t type +/// @returns std::ostream & +inline std::ostream &operator<<(std::ostream &os, + enum ol_platform_info_t value) { + switch (value) { + case OL_PLATFORM_INFO_NAME: + os << "OL_PLATFORM_INFO_NAME"; + break; + case OL_PLATFORM_INFO_VENDOR_NAME: + os << "OL_PLATFORM_INFO_VENDOR_NAME"; + break; + case OL_PLATFORM_INFO_VERSION: + os << "OL_PLATFORM_INFO_VERSION"; + break; + case OL_PLATFORM_INFO_BACKEND: + os << "OL_PLATFORM_INFO_BACKEND"; + break; + default: + os << "unknown enumerator"; + break; + } + return os; +} + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Print type-tagged ol_platform_info_t enum value +/// @returns std::ostream & +template <> +inline void printTagged(std::ostream &os, const void *ptr, + ol_platform_info_t value, size_t size) { + if (ptr == NULL) { + printPtr(os, ptr); + return; + } + + switch (value) { + case OL_PLATFORM_INFO_NAME: { + printPtr(os, (const char *)ptr); + break; + } + case OL_PLATFORM_INFO_VENDOR_NAME: { + printPtr(os, (const char *)ptr); + break; + } + case OL_PLATFORM_INFO_VERSION: { + printPtr(os, (const char *)ptr); + break; + } + case OL_PLATFORM_INFO_BACKEND: { + const ol_platform_backend_t *const tptr = + (const ol_platform_backend_t *const)ptr; + os << (const void *)tptr << " ("; + os << *tptr; + os << ")"; + break; + } + default: + os << "unknown enumerator"; + break; + } +} +/////////////////////////////////////////////////////////////////////////////// +/// @brief Print operator for the ol_platform_backend_t type +/// @returns std::ostream & +inline std::ostream &operator<<(std::ostream &os, + enum ol_platform_backend_t value) { + switch (value) { + case OL_PLATFORM_BACKEND_UNKNOWN: + os << "OL_PLATFORM_BACKEND_UNKNOWN"; + break; + case OL_PLATFORM_BACKEND_CUDA: + os << "OL_PLATFORM_BACKEND_CUDA"; + break; + case OL_PLATFORM_BACKEND_AMDGPU: + os << "OL_PLATFORM_BACKEND_AMDGPU"; + break; + default: + os << "unknown enumerator"; + break; + } + return os; +} + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Print operator for the ol_device_type_t type +/// @returns std::ostream & +inline std::ostream &operator<<(std::ostream &os, enum ol_device_type_t value) { + switch (value) { + case OL_DEVICE_TYPE_DEFAULT: + os << "OL_DEVICE_TYPE_DEFAULT"; + break; + case OL_DEVICE_TYPE_ALL: + os << "OL_DEVICE_TYPE_ALL"; + break; + case OL_DEVICE_TYPE_GPU: + os << "OL_DEVICE_TYPE_GPU"; + break; + case OL_DEVICE_TYPE_CPU: + os << "OL_DEVICE_TYPE_CPU"; + break; + default: + os << "unknown enumerator"; + break; + } + return os; +} + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Print operator for the ol_device_info_t type +/// @returns std::ostream & +inline std::ostream &operator<<(std::ostream &os, enum ol_device_info_t value) { + switch (value) { + case OL_DEVICE_INFO_TYPE: + os << "OL_DEVICE_INFO_TYPE"; + break; + case OL_DEVICE_INFO_PLATFORM: + os << "OL_DEVICE_INFO_PLATFORM"; + break; + case OL_DEVICE_INFO_NAME: + os << "OL_DEVICE_INFO_NAME"; + break; + case OL_DEVICE_INFO_VENDOR: + os << "OL_DEVICE_INFO_VENDOR"; + break; + case OL_DEVICE_INFO_DRIVER_VERSION: + os << "OL_DEVICE_INFO_DRIVER_VERSION"; + break; + default: + os << "unknown enumerator"; + break; + } + return os; +} + +/////////////////////////////////////////////////////////////////////////////// +/// @brief Print type-tagged ol_device_info_t enum value +/// @returns std::ostream & +template <> +inline void printTagged(std::ostream &os, const void *ptr, + ol_device_info_t value, size_t size) { + if (ptr == NULL) { + printPtr(os, ptr); + return; + } + + switch (value) { + case OL_DEVICE_INFO_TYPE: { + const ol_device_type_t *const tptr = (const ol_device_type_t *const)ptr; + os << (const void *)tptr << " ("; + os << *tptr; + os << ")"; + break; + } + case OL_DEVICE_INFO_PLATFORM: { + const ol_platform_handle_t *const tptr = + (const ol_platform_handle_t *const)ptr; + os << (const void *)tptr << " ("; + os << *tptr; + os << ")"; + break; + } + case OL_DEVICE_INFO_NAME: { + printPtr(os, (const char *)ptr); + break; + } + case OL_DEVICE_INFO_VENDOR: { + printPtr(os, (const char *)ptr); + break; + } + case OL_DEVICE_INFO_DRIVER_VERSION: { + printPtr(os, (const char *)ptr); + break; + } + default: + os << "unknown enumerator"; + break; + } +} + +inline std::ostream &operator<<(std::ostream &os, + const ol_error_struct_t *Err) { + if (Err == nullptr) { + os << "OL_SUCCESS"; + } else { + os << Err->Code; + } + return os; +} + +inline std::ostream &operator<<(std::ostream &os, + const struct ol_get_platform_params_t *params) { + os << ".NumEntries = "; + os << *params->pNumEntries; + os << ", "; + os << ".Platforms = "; + os << "{"; + for (size_t i = 0; i < *params->pNumEntries; i++) { + if (i > 0) { + os << ", "; + } + printPtr(os, (*params->pPlatforms)[i]); + } + os << "}"; + return os; +} + +inline std::ostream & +operator<<(std::ostream &os, + const struct ol_get_platform_count_params_t *params) { + os << ".NumPlatforms = "; + printPtr(os, *params->pNumPlatforms); + return os; +} + +inline std::ostream & +operator<<(std::ostream &os, + const struct ol_get_platform_info_params_t *params) { + os << ".Platform = "; + printPtr(os, *params->pPlatform); + os << ", "; + os << ".PropName = "; + os << *params->pPropName; + os << ", "; + os << ".PropSize = "; + os << *params->pPropSize; + os << ", "; + os << ".PropValue = "; + printTagged(os, *params->pPropValue, *params->pPropName, *params->pPropSize); + return os; +} + +inline std::ostream & +operator<<(std::ostream &os, + const struct ol_get_platform_info_size_params_t *params) { + os << ".Platform = "; + printPtr(os, *params->pPlatform); + os << ", "; + os << ".PropName = "; + os << *params->pPropName; + os << ", "; + os << ".PropSizeRet = "; + printPtr(os, *params->pPropSizeRet); + return os; +} + +inline std::ostream & +operator<<(std::ostream &os, + const struct ol_get_device_count_params_t *params) { + os << ".Platform = "; + printPtr(os, *params->pPlatform); + os << ", "; + os << ".NumDevices = "; + printPtr(os, *params->pNumDevices); + return os; +} + +inline std::ostream &operator<<(std::ostream &os, + const struct ol_get_device_params_t *params) { + os << ".Platform = "; + printPtr(os, *params->pPlatform); + os << ", "; + os << ".NumEntries = "; + os << *params->pNumEntries; + os << ", "; + os << ".Devices = "; + os << "{"; + for (size_t i = 0; i < *params->pNumEntries; i++) { + if (i > 0) { + os << ", "; + } + printPtr(os, (*params->pDevices)[i]); + } + os << "}"; + return os; +} + +inline std::ostream & +operator<<(std::ostream &os, const struct ol_get_device_info_params_t *params) { + os << ".Device = "; + printPtr(os, *params->pDevice); + os << ", "; + os << ".PropName = "; + os << *params->pPropName; + os << ", "; + os << ".PropSize = "; + os << *params->pPropSize; + os << ", "; + os << ".PropValue = "; + printTagged(os, *params->pPropValue, *params->pPropName, *params->pPropSize); + return os; +} + +inline std::ostream & +operator<<(std::ostream &os, + const struct ol_get_device_info_size_params_t *params) { + os << ".Device = "; + printPtr(os, *params->pDevice); + os << ", "; + os << ".PropName = "; + os << *params->pPropName; + os << ", "; + os << ".PropSizeRet = "; + printPtr(os, *params->pPropSizeRet); + return os; +} + +/////////////////////////////////////////////////////////////////////////////// +// @brief Print pointer value +template +inline ol_result_t printPtr(std::ostream &os, const T *ptr) { + if (ptr == nullptr) { + os << "nullptr"; + } else if constexpr (std::is_pointer_v) { + os << (const void *)(ptr) << " ("; + printPtr(os, *ptr); + os << ")"; + } else if constexpr (std::is_void_v || is_handle_v) { + os << (const void *)ptr; + } else if constexpr (std::is_same_v, char>) { + os << (const void *)(ptr) << " ("; + os << ptr; + os << ")"; + } else { + os << (const void *)(ptr) << " ("; + os << *ptr; + os << ")"; + } + + return OL_SUCCESS; +} diff --git a/offload/liboffload/src/Helpers.hpp b/offload/liboffload/src/Helpers.hpp new file mode 100644 index 0000000000000..d003d30252462 --- /dev/null +++ b/offload/liboffload/src/Helpers.hpp @@ -0,0 +1,95 @@ +//===- 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 "OffloadAPI.h" + +#include + +template +ol_errc_t getInfoImpl(size_t ParamValueSize, void *ParamValue, + size_t *ParamValueSizeRet, T Value, size_t ValueSize, + Assign &&AssignFunc) { + if (!ParamValue && !ParamValueSizeRet) { + return OL_ERRC_INVALID_NULL_POINTER; + } + + if (ParamValue != nullptr) { + if (ParamValueSize < ValueSize) { + return OL_ERRC_INVALID_SIZE; + } + AssignFunc(ParamValue, Value, ValueSize); + } + + if (ParamValueSizeRet != nullptr) { + *ParamValueSizeRet = ValueSize; + } + + return OL_ERRC_SUCCESS; +} + +template +ol_errc_t getInfo(size_t ParamValueSize, void *ParamValue, + size_t *ParamValueSizeRet, T Value) { + auto Assignment = [](void *ParamValue, T Value, size_t) { + *static_cast(ParamValue) = Value; + }; + + return getInfoImpl(ParamValueSize, ParamValue, ParamValueSizeRet, Value, + sizeof(T), Assignment); +} + +template +ol_errc_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 ol_errc_t getInfo(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 ol_errc_t operator()(const T &t) { + return getInfo(ParamValueSize, ParamValue, ParamValueSizeRet, t); + } + + // Array return Value + template ol_errc_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/liboffload/src/OffloadImpl.cpp b/offload/liboffload/src/OffloadImpl.cpp new file mode 100644 index 0000000000000..457f1053f1634 --- /dev/null +++ b/offload/liboffload/src/OffloadImpl.cpp @@ -0,0 +1,247 @@ +//===- ol_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 "OffloadImpl.hpp" +#include "Helpers.hpp" +#include "PluginManager.h" +#include "llvm/Support/FormatVariadic.h" +#include + +#include + +using namespace llvm; +using namespace llvm::omp::target::plugin; + +// Handle type definitions. Ideally these would be 1:1 with the plugins +struct ol_device_handle_t_ { + int DeviceNum; + GenericDeviceTy &Device; + ol_platform_handle_t Platform; +}; + +struct ol_platform_handle_t_ { + std::unique_ptr Plugin; + std::vector Devices; +}; + +using PlatformVecT = SmallVector; +PlatformVecT &Platforms() { + static PlatformVecT Platforms; + return Platforms; +} + +// TODO: Some plugins expect to be linked into libomptarget which defines these +// symbols to implement ompt callbacks. The least invasive workaround here is to +// define them in libLLVMOffload as false/null so they are never used. In future +// it would be better to allow the plugins to implement callbacks without +// pulling in details from libomptarget. +#ifdef OMPT_SUPPORT +namespace llvm::omp::target { +namespace ompt { +bool Initialized = false; +ompt_get_callback_t lookupCallbackByCode = nullptr; +ompt_function_lookup_t lookupCallbackByName = nullptr; +} // namespace ompt +} // namespace llvm::omp::target +#endif + +// 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(ol_platform_handle_t_{ \ + std::unique_ptr(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(ol_device_handle_t_{ + DevNum, Platform.Plugin->getDevice(DevNum), &Platform}); + } + } + } + + offloadConfig().TracingEnabled = std::getenv("OFFLOAD_TRACE"); +} + +// TODO: We can properly reference count here and manage the resources in a more +// clever way +ol_impl_result_t olInit_impl() { + static std::once_flag InitFlag; + std::call_once(InitFlag, initPlugins); + + return OL_SUCCESS; +} +ol_impl_result_t olShutDown_impl() { return OL_SUCCESS; } + +ol_impl_result_t olGetPlatformCount_impl(uint32_t *NumPlatforms) { + *NumPlatforms = Platforms().size(); + return OL_SUCCESS; +} + +ol_impl_result_t olGetPlatform_impl(uint32_t NumEntries, + ol_platform_handle_t *PlatformsOut) { + if (NumEntries > Platforms().size()) { + return {OL_ERRC_INVALID_SIZE, + std::string{formatv("{0} platform(s) available but {1} requested.", + Platforms().size(), NumEntries)}}; + } + + for (uint32_t PlatformIndex = 0; PlatformIndex < NumEntries; + PlatformIndex++) { + PlatformsOut[PlatformIndex] = &(Platforms())[PlatformIndex]; + } + + return OL_SUCCESS; +} + +ol_impl_result_t olGetPlatformInfoImplDetail(ol_platform_handle_t Platform, + ol_platform_info_t PropName, + size_t PropSize, void *PropValue, + size_t *PropSizeRet) { + ReturnHelper ReturnValue(PropSize, PropValue, PropSizeRet); + + switch (PropName) { + case OL_PLATFORM_INFO_NAME: + return ReturnValue(Platform->Plugin->getName()); + case OL_PLATFORM_INFO_VENDOR_NAME: + // TODO: Implement this + return ReturnValue("Unknown platform vendor"); + case OL_PLATFORM_INFO_VERSION: { + return ReturnValue(formatv("v{0}.{1}.{2}", OL_VERSION_MAJOR, + OL_VERSION_MINOR, OL_VERSION_PATCH) + .str() + .c_str()); + } + case OL_PLATFORM_INFO_BACKEND: { + auto PluginName = Platform->Plugin->getName(); + if (PluginName == StringRef("CUDA")) { + return ReturnValue(OL_PLATFORM_BACKEND_CUDA); + } else if (PluginName == StringRef("AMDGPU")) { + return ReturnValue(OL_PLATFORM_BACKEND_AMDGPU); + } else { + return ReturnValue(OL_PLATFORM_BACKEND_UNKNOWN); + } + } + default: + return OL_ERRC_INVALID_ENUMERATION; + } + + return OL_SUCCESS; +} + +ol_impl_result_t olGetPlatformInfo_impl(ol_platform_handle_t Platform, + ol_platform_info_t PropName, + size_t PropSize, void *PropValue) { + return olGetPlatformInfoImplDetail(Platform, PropName, PropSize, PropValue, + nullptr); +} + +ol_impl_result_t olGetPlatformInfoSize_impl(ol_platform_handle_t Platform, + ol_platform_info_t PropName, + size_t *PropSizeRet) { + return olGetPlatformInfoImplDetail(Platform, PropName, 0, nullptr, + PropSizeRet); +} + +ol_impl_result_t olGetDeviceCount_impl(ol_platform_handle_t Platform, + uint32_t *pNumDevices) { + *pNumDevices = static_cast(Platform->Devices.size()); + + return OL_SUCCESS; +} + +ol_impl_result_t olGetDevice_impl(ol_platform_handle_t Platform, + uint32_t NumEntries, + ol_device_handle_t *Devices) { + if (NumEntries > Platform->Devices.size()) { + return OL_ERRC_INVALID_SIZE; + } + + for (uint32_t DeviceIndex = 0; DeviceIndex < NumEntries; DeviceIndex++) { + Devices[DeviceIndex] = &(Platform->Devices[DeviceIndex]); + } + + return OL_SUCCESS; +} + +ol_impl_result_t olGetDeviceInfoImplDetail(ol_device_handle_t Device, + ol_device_info_t PropName, + size_t PropSize, void *PropValue, + size_t *PropSizeRet) { + + ReturnHelper ReturnValue(PropSize, PropValue, PropSizeRet); + + InfoQueueTy DevInfo; + if (auto Err = Device->Device.obtainInfoImpl(DevInfo)) + return OL_ERRC_OUT_OF_RESOURCES; + + // Find the info if it exists under any of the given names + auto GetInfo = [&DevInfo](std::vector 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 OL_DEVICE_INFO_PLATFORM: + return ReturnValue(Device->Platform); + case OL_DEVICE_INFO_TYPE: + return ReturnValue(OL_DEVICE_TYPE_GPU); + case OL_DEVICE_INFO_NAME: + return ReturnValue(GetInfo({"Device Name"}).c_str()); + case OL_DEVICE_INFO_VENDOR: + return ReturnValue(GetInfo({"Vendor Name"}).c_str()); + case OL_DEVICE_INFO_DRIVER_VERSION: + return ReturnValue( + GetInfo({"CUDA Driver Version", "HSA Runtime Version"}).c_str()); + default: + return OL_ERRC_INVALID_ENUMERATION; + } + + return OL_SUCCESS; +} + +ol_impl_result_t olGetDeviceInfo_impl(ol_device_handle_t Device, + ol_device_info_t PropName, + size_t PropSize, void *PropValue) { + return olGetDeviceInfoImplDetail(Device, PropName, PropSize, PropValue, + nullptr); +} + +ol_impl_result_t olGetDeviceInfoSize_impl(ol_device_handle_t Device, + ol_device_info_t PropName, + size_t *PropSizeRet) { + return olGetDeviceInfoImplDetail(Device, PropName, 0, nullptr, PropSizeRet); +} diff --git a/offload/liboffload/src/OffloadLib.cpp b/offload/liboffload/src/OffloadLib.cpp new file mode 100644 index 0000000000000..37876713212c9 --- /dev/null +++ b/offload/liboffload/src/OffloadLib.cpp @@ -0,0 +1,44 @@ +//===- 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 "OffloadImpl.hpp" +#include +#include + +#include + +llvm::StringSet<> &errorStrs() { + static llvm::StringSet<> ErrorStrs; + return ErrorStrs; +} + +ErrSetT &errors() { + static ErrSetT Errors{}; + return Errors; +} + +ol_code_location_t *¤tCodeLocation() { + thread_local ol_code_location_t *CodeLoc = nullptr; + return CodeLoc; +} + +OffloadConfig &offloadConfig() { + static OffloadConfig Config{}; + return Config; +} + +// Pull in the declarations for the implementation funtions. The actual entry +// points in this file wrap these. +#include "OffloadImplFuncDecls.inc" + +// Pull in the tablegen'd entry point definitions. +#include "OffloadEntryPoints.inc" diff --git a/offload/plugins-nextgen/common/include/PluginInterface.h b/offload/plugins-nextgen/common/include/PluginInterface.h index 41cc0f286a581..82efcdaf5ad3d 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,7 @@ class InfoQueueTy { uint64_t Level; }; +private: std::deque Queue; public: @@ -153,6 +155,8 @@ class InfoQueueTy { Queue.push_back({Key, Value, Units, L}); } + const std::deque &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/test/lit.cfg b/offload/test/lit.cfg index 2f1ef3e98d817..658ae5f9653ba 100644 --- a/offload/test/lit.cfg +++ b/offload/test/lit.cfg @@ -66,7 +66,7 @@ def evaluate_bool_env(env): config.name = 'libomptarget :: ' + config.libomptarget_current_target # suffixes: A list of file extensions to treat as test files. -config.suffixes = ['.c', '.cpp', '.cc', '.f90', '.cu'] +config.suffixes = ['.c', '.cpp', '.cc', '.f90', '.cu', '.td'] # excludes: A list of directories to exclude from the testuites. config.excludes = ['Inputs'] @@ -418,3 +418,4 @@ config.substitutions.append(("%flags", config.test_flags)) config.substitutions.append(("%not", config.libomptarget_not)) config.substitutions.append(("%offload-device-info", config.offload_device_info)) +config.substitutions.append(("%offload-tblgen", config.offload_tblgen)) diff --git a/offload/test/lit.site.cfg.in b/offload/test/lit.site.cfg.in index a1cb5acc38a40..ce3f6abf50a13 100644 --- a/offload/test/lit.site.cfg.in +++ b/offload/test/lit.site.cfg.in @@ -28,5 +28,6 @@ config.libomptarget_debug = @LIBOMPTARGET_DEBUG@ config.has_libomptarget_ompt = @LIBOMPTARGET_OMPT_SUPPORT@ config.libomptarget_has_libc = @LIBOMPTARGET_GPU_LIBC_SUPPORT@ config.libomptarget_test_pgo = @LIBOMPTARGET_TEST_GPU_PGO@ +config.offload_tblgen = "@OFFLOAD_TBLGEN_EXECUTABLE@" # Let the main config do the real work. lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg") diff --git a/offload/test/tools/offload-tblgen/default_returns.td b/offload/test/tools/offload-tblgen/default_returns.td new file mode 100644 index 0000000000000..432063e0174af --- /dev/null +++ b/offload/test/tools/offload-tblgen/default_returns.td @@ -0,0 +1,40 @@ +// RUN: %offload-tblgen -gen-api -I %S/../../../new-api/API %s | %fcheck-generic --check-prefix=CHECK-API +// RUN: %offload-tblgen -gen-entry-points -I %S/../../../new-api/API %s | %fcheck-generic --check-prefix=CHECK-VALIDATION + +// Check implicit returns are included in documentation and the validation +// wrappers where applicable + +include "APIDefs.td" + +def : Handle { + let name = "offload_foo_handle_t"; + let desc = "Example handle type"; +} + +def : Function { + let name = "FunctionA"; + let desc = "Function A description"; + let details = [ "Function A detailed information" ]; + let params = [ + Param<"uint32_t", "ParamValue", "A plain value parameter">, + Param<"offload_foo_handle_t", "ParamHandle", "A handle parameter">, + Param<"uint32_t*", "ParamPointer", "A pointer parameter">, + Param<"uint32_t*", "ParamPointerOpt", "An optional pointer parameter", PARAM_OUT_OPTIONAL> + ]; + let returns = []; +} + +// CHECK-API: /// @returns +// CHECK-API: OFFLOAD_RESULT_SUCCESS +// CHECK-API: OFFLOAD_ERRC_INVALID_NULL_HANDLE +// CHECK-API-NEXT: `NULL == ParamHandle` +// CHECK-API: OFFLOAD_ERRC_INVALID_NULL_POINTER +// CHECK-API-NEXT: `NULL == ParamPointer` +// CHECK-API-NOT: `NULL == ParamPointerOpt` + +// CHECK-VALIDATION: FunctionA_val +// CHECK-VALIDATION: if (NULL == ParamHandle) +// CHECK-VALIDATION-NEXT: return OFFLOAD_ERRC_INVALID_NULL_HANDLE; +// CHECK-VALIDATION: if (NULL == ParamPointer) +// CHECK-VALIDATION-NEXT: return OFFLOAD_ERRC_INVALID_NULL_POINTER; +// CHECK-VALIDATION-NOT: if (NULL == ParamPointerOpt) diff --git a/offload/test/tools/offload-tblgen/entry_points.td b/offload/test/tools/offload-tblgen/entry_points.td new file mode 100644 index 0000000000000..2d2bd1f5e3bfc --- /dev/null +++ b/offload/test/tools/offload-tblgen/entry_points.td @@ -0,0 +1,37 @@ +// RUN: %offload-tblgen -gen-entry-points -I %S/../../../new-api/API %s | %fcheck-generic + +// Check entry point wrapper functions are generated correctly + +include "APIDefs.td" + +def : Function { + let name = "FunctionA"; + let desc = "Function A description"; + let details = [ "Function A detailed information" ]; + let params = [ + Param<"uint32_t", "ParamA", "Parameter A description">, + Param<"uint32_t*", "ParamB", "Parameter B description">, + ]; + let returns = [ + Return<"OFFLOAD_ERRC_INVALID_VALUE", ["When a value is invalid"]> + ]; +} + + +// The validation function should call the implementation function +// CHECK: FunctionA_val +// CHECK: return FunctionA_impl(ParamA, ParamB); + +// CHECK: offload_result_t{{.*}} FunctionA( + +// The entry point should print tracing output if enabled +// CHECK: if (offloadConfig().TracingEnabled) { +// CHECK-NEXT: "---> FunctionA"; + +// CHECK: Result = FunctionA_val(ParamA, ParamB); + +// Tracing should construct a param struct for printing +// CHECK: if (offloadConfig().TracingEnabled) { +// CHECK: function_a_params_t Params = {&ParamA, &ParamB}; + +// CHECK: return Result; diff --git a/offload/test/tools/offload-tblgen/functions_basic.td b/offload/test/tools/offload-tblgen/functions_basic.td new file mode 100644 index 0000000000000..6601746a727b0 --- /dev/null +++ b/offload/test/tools/offload-tblgen/functions_basic.td @@ -0,0 +1,39 @@ +// RUN: %offload-tblgen -gen-api -I %S/../../../new-api/API %s | %fcheck-generic --check-prefix=CHECK-API +// RUN: %offload-tblgen -gen-exports -I %S/../../../new-api/API %s | %fcheck-generic --check-prefix=CHECK-EXPORTS +// RUN: %offload-tblgen -gen-func-names -I %S/../../../new-api/API %s | %fcheck-generic --check-prefix=CHECK-FUNC-MACRO + +// Check basic support for API functions + +include "APIDefs.td" + +def : Function { + let name = "FunctionA"; + let desc = "Function A description"; + let details = [ "Function A detailed information" ]; + let params = [ + Param<"uint32_t", "ParamA", "Parameter A description">, + Param<"uint32_t*", "ParamB", "Parameter B description">, + ]; + let returns = [ + Return<"OFFLOAD_ERRC_INVALID_VALUE", ["When a value is invalid"]> + ]; +} + +// CHECK-API: /// @brief Function A description +// CHECK-API: /// @details +// CHECK-API-NEXT: Function A detailed information +// CHECK-API: /// @returns +// CHECK-API: OFFLOAD_ERRC_INVALID_VALUE +// CHECK-API-NEXT: When a value is invalid + +// CHECK-API: offload_result_t +// CHECK-API-SAME: FunctionA + +// CHECK-API: // Parameter A description +// CHECK-API-NEXT: uint32_t ParamA +// CHECK-API: // Parameter B description +// CHECK-API-NEXT: uint32_t* ParamB + +// CHECK-EXPORTS: FunctionA + +// CHECK-FUNC-MACRO: OFFLOAD_FUNC(FunctionA) diff --git a/offload/test/tools/offload-tblgen/functions_code_loc.td b/offload/test/tools/offload-tblgen/functions_code_loc.td new file mode 100644 index 0000000000000..4c8d3688566c3 --- /dev/null +++ b/offload/test/tools/offload-tblgen/functions_code_loc.td @@ -0,0 +1,26 @@ +// RUN: %offload-tblgen -gen-api -I %S/../../../new-api/API %s | %fcheck-generic --check-prefix=CHECK-API +// RUN: %offload-tblgen -gen-exports -I %S/../../../new-api/API %s | %fcheck-generic --check-prefix=CHECK-EXPORTS +// RUN: %offload-tblgen -gen-func-names -I %S/../../../new-api/API %s | %fcheck-generic --check-prefix=CHECK-FUNC-MACRO + +// Check that the function variant with code location information is generated +// and is otherwise the same as the regular function + +include "APIDefs.td" + +def : Function { + let name = "FunctionA"; + let desc = "Function A description"; + let details = [ "Function A detailed information" ]; + let params = [ + Param<"uint32_t", "ParamA", "Parameter A description">, + Param<"uint32_t*", "ParamB", "Parameter B description">, + ]; + let returns = [ + Return<"OFFLOAD_ERRC_INVALID_VALUE", ["When a value is invalid"]> + ]; +} + +// CHECK-API-DAG: offload_result_t{{.*}} FunctionA +// CHECK-API-DAG: offload_result_t{{.*}} FunctionAWithCodeLoc +// CHECK-EXPORTS: FunctionAWithCodeLoc +// CHECK-FUNC-MACRO: OFFLOAD_FUNC(FunctionAWithCodeLoc) diff --git a/offload/test/tools/offload-tblgen/functions_ranged_param.td b/offload/test/tools/offload-tblgen/functions_ranged_param.td new file mode 100644 index 0000000000000..efa8bae5290ec --- /dev/null +++ b/offload/test/tools/offload-tblgen/functions_ranged_param.td @@ -0,0 +1,36 @@ +// RUN: %offload-tblgen -gen-print-header -I %S/../../../new-api/API %s | %fcheck-generic + +// Check that ranged function parameters are implemented correctly. These +// are pointers to an array of an arbitrary size. Their size is described as a +// range between two values. This is typically between 0 and a parameter such +// as NumItems. The range information helps the printing code print the entire +// range of the output rather than just the pointer or the first element. + +include "APIDefs.td" + +def : Handle { + let name = "some_handle_t"; + let desc = "An example handle type"; +} + +def : Function { + let name = "FunctionA"; + let desc = "Function A description"; + let details = [ "Function A detailed information" ]; + let params = [ + Param<"size_t", "OutCount", "the number of things to write out", PARAM_IN>, + RangedParam<"some_handle_t*", "OutPtr", "pointer to the output things.", PARAM_OUT, + Range<"0", "OutCount">> + ]; + let returns = []; +} + +// CHECK: inline std::ostream &operator<<(std::ostream &os, const struct function_a_params_t *params) { +// CHECK: os << ".OutPtr = "; +// CHECK: for (size_t i = 0; i < *params->pOutCount; i++) { +// CHECK: if (i > 0) { +// CHECK: os << ", "; +// CHECK: } +// CHECK: printPtr(os, (*params->pOutPtr)[i]); +// CHECK: } +// CHECK: os << "}"; diff --git a/offload/test/tools/offload-tblgen/print_enum.td b/offload/test/tools/offload-tblgen/print_enum.td new file mode 100644 index 0000000000000..1e1d7f57218d0 --- /dev/null +++ b/offload/test/tools/offload-tblgen/print_enum.td @@ -0,0 +1,34 @@ +// RUN: %offload-tblgen -gen-print-header -I %S/../../../new-api/API %s | %fcheck-generic + +// Check that print helpers are created for enums + +include "APIDefs.td" + +def : Enum { + let name = "my_enum_t"; + let desc = "An example enum"; + let etors =[ + Etor<"VALUE_ONE", "The first enum value">, + Etor<"VALUE_TWO", "The second enum value">, + Etor<"VALUE_THREE", "The third enum value">, + Etor<"VALUE_FOUR", "The fourth enum value">, + ]; +} + +// CHECK: inline std::ostream &operator<<(std::ostream &os, enum my_enum_t value) +// CHECK: switch (value) { +// CHECK: case MY_ENUM_VALUE_ONE: +// CHECK: os << "MY_ENUM_VALUE_ONE"; +// CHECK: break; +// CHECK: case MY_ENUM_VALUE_TWO: +// CHECK: os << "MY_ENUM_VALUE_TWO"; +// CHECK: break; +// CHECK: case MY_ENUM_VALUE_THREE: +// CHECK: os << "MY_ENUM_VALUE_THREE"; +// CHECK: break; +// CHECK: case MY_ENUM_VALUE_FOUR: +// CHECK: os << "MY_ENUM_VALUE_FOUR"; +// CHECK: break; +// CHECK: default: +// CHECK: os << "unknown enumerator"; +// CHECK: break; diff --git a/offload/test/tools/offload-tblgen/print_function.td b/offload/test/tools/offload-tblgen/print_function.td new file mode 100644 index 0000000000000..2a9cce724eda9 --- /dev/null +++ b/offload/test/tools/offload-tblgen/print_function.td @@ -0,0 +1,38 @@ +// RUN: %offload-tblgen -gen-print-header -I %S/../../../new-api/API %s | %fcheck-generic --check-prefix=CHECK-PRINT +// RUN: %offload-tblgen -gen-api -I %S/../../../new-api/API %s | %fcheck-generic --check-prefix=CHECK-API + +// Check that print helpers are created for functions + +include "APIDefs.td" + +def : Handle { + let name = "offload_foo_handle_t"; + let desc = "Example handle type"; +} + +def : Function { + let name = "FunctionA"; + let desc = "Function A description"; + let details = [ "Function A detailed information" ]; + let params = [ + Param<"uint32_t", "ParamValue", "A plain value parameter">, + Param<"offload_foo_handle_t", "ParamHandle", "A handle parameter">, + Param<"uint32_t*", "ParamPointer", "A pointer parameter">, + ]; + let returns = []; +} + +// CHECK-API: typedef struct function_a_params_t { +// CHECK-API-NEXT: uint32_t* pParamValue; +// CHECK-API-NEXT: offload_foo_handle_t* pParamHandle; +// CHECK-API-NEXT: uint32_t** pParamPointer; + +// CHECK-PRINT: inline std::ostream &operator<<(std::ostream &os, const struct function_a_params_t *params) +// CHECK-PRINT: os << ".ParamValue = "; +// CHECK-PRINT: os << *params->pParamValue; +// CHECK-PRINT: os << ", "; +// CHECK-PRINT: os << ".ParamHandle = "; +// CHECK-PRINT: printPtr(os, *params->pParamHandle); +// CHECK-PRINT: os << ", "; +// CHECK-PRINT: os << ".ParamPointer = "; +// CHECK-PRINT: printPtr(os, *params->pParamPointer); diff --git a/offload/test/tools/offload-tblgen/type_tagged_enum.td b/offload/test/tools/offload-tblgen/type_tagged_enum.td new file mode 100644 index 0000000000000..ea83545e0a385 --- /dev/null +++ b/offload/test/tools/offload-tblgen/type_tagged_enum.td @@ -0,0 +1,76 @@ +// RUN: %offload-tblgen -gen-api -I %S/../../../new-api/API %s | %fcheck-generic --check-prefix=CHECK-API +// RUN: %offload-tblgen -gen-print-header -I %S/../../../new-api/API %s | %fcheck-generic --check-prefix=CHECK-PRINT + +// Check that type-tagged enumerators are implemented correctly. They enable +// functions to return data of an arbitrary type and size via a void*, using +// the value of an enum parameter to indicate which type is being returned. +// This allows, for example, for a single olGetDeviceInfo function, rather +// than requiring a separate entry point for every possible query. + +include "APIDefs.td" + +def : Handle { + let name = "some_handle_t"; + let desc = "An example handle type"; +} + +def : Enum { + let name = "my_type_tagged_enum_t"; + let desc = "Example type tagged enum"; + let is_typed = 1; + let etors = [ + TaggedEtor<"VALUE_ONE", "uint32_t", "Value one.">, + TaggedEtor<"VALUE_TWO", "char[]", "Value two.">, + TaggedEtor<"VALUE_THREE", "some_handle_t", "Value three."> + ]; +} + +// Check the tagged types appear in the comments +// CHECK-API: typedef enum my_type_tagged_enum_t { +// CHECK-API-NEXT: [uint32_t] Value one. +// CHECK-API-NEXT: MY_TYPE_TAGGED_ENUM_VALUE_ONE = 0, +// CHECK-API-NEXT: [char[]] Value two. +// CHECK-API-NEXT: MY_TYPE_TAGGED_ENUM_VALUE_TWO = 1, +// CHECK-API-NEXT: [some_handle_t] Value three. +// CHECK-API-NEXT: MY_TYPE_TAGGED_ENUM_VALUE_THREE = 2, + +def : Function { + let name = "FunctionA"; + let desc = "Function A description"; + let details = [ "Function A detailed information" ]; + let params = [ + Param<"my_type_tagged_enum_t", "PropName", "type of the info to retrieve", PARAM_IN>, + Param<"size_t", "PropSize", "the number of bytes pointed to by PropValue.", PARAM_IN>, + TypeTaggedParam<"void*", "PropValue", "array of bytes holding the info. " + "If PropSize is not equal to or greater to the real number of bytes needed to return the info " + "then the OFFLOAD_ERRC_INVALID_SIZE error is returned and PropValue is not used.", PARAM_OUT, + TypeInfo<"PropName" , "PropSize">> + ]; + let returns = []; +} + +// Check that a tagged enum print function definition is generated +// CHECK-PRINT: void printTagged(std::ostream &os, const void *ptr, my_type_tagged_enum_t value, size_t size) { +// CHECK-PRINT: case MY_TYPE_TAGGED_ENUM_VALUE_ONE: { +// CHECK-PRINT: const uint32_t * const tptr = (const uint32_t * const)ptr; +// CHECK-PRINT: os << (const void *)tptr << " ("; +// CHECK-PRINT: os << *tptr; +// CHECK-PRINT: os << ")"; +// CHECK-PRINT: break; +// CHECK-PRINT: } +// CHECK-PRINT: case MY_TYPE_TAGGED_ENUM_VALUE_TWO: { +// CHECK-PRINT: printPtr(os, (const char*) ptr); +// CHECK-PRINT: break; +// CHECK-PRINT: } +// CHECK-PRINT: case MY_TYPE_TAGGED_ENUM_VALUE_THREE: { +// CHECK-PRINT: const some_handle_t * const tptr = (const some_handle_t * const)ptr; +// CHECK-PRINT: os << (const void *)tptr << " ("; +// CHECK-PRINT: os << *tptr; +// CHECK-PRINT: os << ")"; +// CHECK-PRINT: break; +// CHECK-PRINT: } + +// Check that the tagged type information is used when printing function parameters +// CHECK-PRINT: std::ostream &operator<<(std::ostream &os, const struct function_a_params_t *params) { +// CHECK-PRINT: os << ".PropValue = " +// CHECK-PRINT-NEXT: printTagged(os, *params->pPropValue, *params->pPropName, *params->pPropSize); diff --git a/offload/tools/offload-tblgen/APIGen.cpp b/offload/tools/offload-tblgen/APIGen.cpp new file mode 100644 index 0000000000000..97a2464f7a75c --- /dev/null +++ b/offload/tools/offload-tblgen/APIGen.cpp @@ -0,0 +1,229 @@ +//===- 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("/// ") + + 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 << MakeParamComment(Param) << "\n"; + OS << " " << Param.getType() << " " << Param.getName(); + if (Param != Params.back()) { + OS << ",\n"; + } else { + OS << "\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()) { + if (Enum.isTyped()) { + OS << MakeComment( + formatv("[{0}] {1}", EnumVal.getTaggedType(), EnumVal.getDesc()) + .str()); + } else { + OS << MakeComment(EnumVal.getDesc()); + } + OS << formatv(TAB_1 "{0}_{1} = {2},\n", Enum.getEnumValNamePrefix(), + EnumVal.getName(), EtorVal++); + } + + // 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) { + if (Func.getParams().size() == 0) { + return; + } + + 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()); +} + +static void ProcessFuncWithCodeLocVariant(const FunctionRec &Func, + raw_ostream &OS) { + + auto FuncWithCodeLocBegin = R"( +/////////////////////////////////////////////////////////////////////////////// +/// @brief Variant of {0} that also sets source code location information +/// @details See also ::{0} +OL_APIEXPORT ol_result_t OL_APICALL {0}WithCodeLoc( +)"; + OS << formatv(FuncWithCodeLocBegin, Func.getName()); + auto Params = Func.getParams(); + for (auto &Param : Params) { + OS << " " << Param.getType() << " " << Param.getName(); + OS << ",\n"; + } + OS << "ol_code_location_t *CodeLocation);\n\n"; +} + +void EmitOffloadAPI(const RecordKeeper &Records, raw_ostream &OS) { + OS << GenericHeader; + 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); + } + + for (auto *R : Records.getAllDerivedDefinitions("Function")) { + ProcessFuncWithCodeLocVariant(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 0000000000000..52986cbbaa918 --- /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 0000000000000..990ff96a3121d --- /dev/null +++ b/offload/tools/offload-tblgen/EntryPointGen.cpp @@ -0,0 +1,138 @@ +//===- 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 EmitValidationFunc(const FunctionRec &F, raw_ostream &OS) { + OS << CommentsHeader; + // Emit preamble + OS << formatv("{0}_impl_result_t {1}_val(\n ", PrefixLower, 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 << 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 (offloadConfig().TracingEnabled) {\n"; + OS << formatv(TAB_2 "std::cout << \"---> {0}\";\n", F.getName()); + OS << TAB_1 "}\n\n"; + + // Perform actual function call to the validation wrapper + ParamNameList = ParamNameList.substr(0, ParamNameList.size() - 2); + OS << formatv(TAB_1 "{0}_result_t Result = {1}_val({2});\n\n", PrefixLower, + F.getName(), ParamNameList); + + // Emit post-call prints + OS << TAB_1 "if (offloadConfig().TracingEnabled) {\n"; + if (F.getParams().size() > 0) { + 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"; + } else { + OS << TAB_2 "std::cout << \"()\";\n"; + } + OS << TAB_2 "std::cout << \"-> \" << Result << \"\\n\";\n"; + OS << TAB_2 "if (Result && Result->Details) {\n"; + OS << TAB_3 "std::cout << \" *Error Details* \" << Result->Details " + "<< \" \\n\";\n"; + OS << TAB_2 "}\n"; + OS << TAB_1 "}\n"; + + OS << TAB_1 "return Result;\n"; + OS << "}\n"; +} + +static void EmitCodeLocWrapper(const FunctionRec &F, raw_ostream &OS) { + // Emit preamble + OS << formatv("{0}_result_t {1}WithCodeLoc(\n ", PrefixLower, F.getName()); + // Emit arguments + std::string ParamNameList = ""; + for (auto &Param : F.getParams()) { + OS << Param.getType() << " " << Param.getName() << ", "; + ParamNameList += Param.getName().str(); + if (Param != F.getParams().back()) { + ParamNameList += ", "; + } + } + OS << "ol_code_location_t *CodeLocation"; + OS << ") {\n"; + OS << TAB_1 "currentCodeLocation() = CodeLocation;\n"; + OS << formatv(TAB_1 "{0}_result_t Result = {1}({2});\n\n", PrefixLower, + F.getName(), ParamNameList); + OS << TAB_1 "currentCodeLocation() = nullptr;\n"; + OS << TAB_1 "return Result;\n"; + OS << "}\n"; +} + +void EmitOffloadEntryPoints(const RecordKeeper &Records, raw_ostream &OS) { + OS << GenericHeader; + for (auto *R : Records.getAllDerivedDefinitions("Function")) { + EmitValidationFunc(FunctionRec{R}, OS); + EmitEntryPointFunc(FunctionRec{R}, OS); + EmitCodeLocWrapper(FunctionRec{R}, OS); + } +} diff --git a/offload/tools/offload-tblgen/FuncsGen.cpp b/offload/tools/offload-tblgen/FuncsGen.cpp new file mode 100644 index 0000000000000..3238652176198 --- /dev/null +++ b/offload/tools/offload-tblgen/FuncsGen.cpp @@ -0,0 +1,74 @@ +//===- 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. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This is a Tablegen backend that handles generation of various small files +// pertaining to the API 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(const RecordKeeper &Records, raw_ostream &OS) { + OS << GenericHeader; + 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"; + } + for (auto *R : Records.getAllDerivedDefinitions("Function")) { + FunctionRec FR{R}; + OS << formatv("OFFLOAD_FUNC({0}WithCodeLoc)", FR.getName()) << "\n"; + } + + OS << "\n#undef OFFLOAD_FUNC\n"; +} + +void EmitOffloadExports(const 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()); + } + for (auto *R : Records.getAllDerivedDefinitions("Function")) { + OS << formatv(TAB_2 "{0}WithCodeLoc;\n", FunctionRec(R).getName()); + } + OS << TAB_1 "local:\n"; + OS << TAB_2 "*;\n"; + OS << "};\n"; +} + +// Emit declarations for every implementation function +void EmitOffloadImplFuncDecls(const RecordKeeper &Records, raw_ostream &OS) { + OS << GenericHeader; + for (auto *R : Records.getAllDerivedDefinitions("Function")) { + FunctionRec F{R}; + OS << formatv("{0}_impl_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 << ", "; + } + } + OS << ");\n\n"; + } +} diff --git a/offload/tools/offload-tblgen/GenCommon.hpp b/offload/tools/offload-tblgen/GenCommon.hpp new file mode 100644 index 0000000000000..db432e9958b5d --- /dev/null +++ b/offload/tools/offload-tblgen/GenCommon.hpp @@ -0,0 +1,67 @@ +//===- 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 GenericHeader = + R"(//===- Auto-generated file, part of the LLVM/Offload project --------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +)"; + +constexpr auto FileHeader = R"( +// Auto-generated file, do not manually edit. + +#pragma once + +#include +#include + +#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 = "ol"; +constexpr auto PrefixUpper = "OL"; + +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 0000000000000..8b6104c5cd9c6 --- /dev/null +++ b/offload/tools/offload-tblgen/Generators.hpp @@ -0,0 +1,23 @@ +//===- 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(const llvm::RecordKeeper &Records, llvm::raw_ostream &OS); +void EmitOffloadFuncNames(const llvm::RecordKeeper &Records, + llvm::raw_ostream &OS); +void EmitOffloadImplFuncDecls(const llvm::RecordKeeper &Records, + llvm::raw_ostream &OS); +void EmitOffloadEntryPoints(const llvm::RecordKeeper &Records, + llvm::raw_ostream &OS); +void EmitOffloadPrintHeader(const llvm::RecordKeeper &Records, + llvm::raw_ostream &OS); +void EmitOffloadExports(const 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 0000000000000..2a7c63c3dfd1f --- /dev/null +++ b/offload/tools/offload-tblgen/PrintGen.cpp @@ -0,0 +1,226 @@ +//===- 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 EmitResultPrint(raw_ostream &OS) { + OS << R""( +inline std::ostream &operator<<(std::ostream &os, + const ol_error_struct_t *Err) { + if (Err == nullptr) { + os << "OL_SUCCESS"; + } else { + os << Err->Code; + } + return os; +} +)""; +} + +static void EmitFunctionParamStructPrint(const FunctionRec &Func, + raw_ostream &OS) { + if (Func.getParams().size() == 0) { + return; + } + + OS << formatv(R"( +inline std::ostream &operator<<(std::ostream &os, 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(const RecordKeeper &Records, raw_ostream &OS) { + OS << GenericHeader; + OS << R"""( +// Auto-generated file, do not manually edit. + +#pragma once + +#include +#include + + +template inline ol_result_t printPtr(std::ostream &os, const T *ptr); +template inline void printTagged(std::ostream &os, const void *ptr, T value, size_t size); +)"""; + + // ========== + OS << "template 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 inline constexpr bool is_handle_v = " + "is_handle::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); + } + EmitResultPrint(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 inline ol_result_t printPtr(std::ostream &os, const T *ptr) { + if (ptr == nullptr) { + os << "nullptr"; + } else if constexpr (std::is_pointer_v) { + os << (const void *)(ptr) << " ("; + printPtr(os, *ptr); + os << ")"; + } else if constexpr (std::is_void_v || is_handle_v) { + os << (const void *)ptr; + } else if constexpr (std::is_same_v, char>) { + os << (const void *)(ptr) << " ("; + os << ptr; + os << ")"; + } else { + os << (const void *)(ptr) << " ("; + os << *ptr; + os << ")"; + } + + return OL_SUCCESS; +} + )"""; +} diff --git a/offload/tools/offload-tblgen/RecordTypes.hpp b/offload/tools/offload-tblgen/RecordTypes.hpp new file mode 100644 index 0000000000000..0bf3256c525d9 --- /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 + +#include "llvm/TableGen/Record.h" + +namespace llvm { +namespace offload { +namespace tblgen { + +class HandleRec { +public: + explicit HandleRec(const Record *rec) : rec(rec) {} + StringRef getName() const { return rec->getValueAsString("name"); } + StringRef getDesc() const { return rec->getValueAsString("desc"); } + +private: + const Record *rec; +}; + +class MacroRec { +public: + explicit MacroRec(const 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 getCondition() const { + return rec->getValueAsOptionalString("condition"); + } + StringRef getValue() const { return rec->getValueAsString("value"); } + std::optional getAltValue() const { + return rec->getValueAsOptionalString("alt_value"); + } + +private: + const Record *rec; + std::string nameWithoutArgs; +}; + +class TypedefRec { +public: + explicit TypedefRec(const 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: + const Record *rec; +}; + +class EnumValueRec { +public: + explicit EnumValueRec(const 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: + const Record *rec; +}; + +class EnumRec { +public: + explicit EnumRec(const Record *rec) : rec(rec) { + for (const 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 &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: + const Record *rec; + std::vector vals; +}; + +class StructMemberRec { +public: + explicit StructMemberRec(const 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: + const Record *rec; +}; + +class StructRec { +public: + explicit StructRec(const 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 getBaseClass() const { + return rec->getValueAsOptionalString("base_class"); + } + const std::vector &getMembers() const { return members; } + +private: + const Record *rec; + std::vector members; +}; + +class ParamRec { +public: + explicit ParamRec(const 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(flags->getBit(0))->getValue(); } + bool isOut() const { return dyn_cast(flags->getBit(1))->getValue(); } + bool isOpt() const { return dyn_cast(flags->getBit(2))->getValue(); } + + const Record *getRec() const { return rec; } + std::optional> getRange() const { + return range; + } + + std::optional> 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: + const Record *rec; + const BitsInit *flags; + std::optional> range; + std::optional> typeinfo; +}; + +class ReturnRec { +public: + ReturnRec(const Record *rec) : rec(rec) {} + StringRef getValue() const { return rec->getValueAsString("value"); } + std::vector getConditions() const { + return rec->getValueAsListOfStrings("conditions"); + } + +private: + const Record *rec; +}; + +class FunctionRec { +public: + FunctionRec(const 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 &getReturns() const { return rets; } + const std::vector &getParams() const { return params; } + StringRef getDesc() const { return rec->getValueAsString("desc"); } + std::vector getDetails() const { + return rec->getValueAsListOfStrings("details"); + } + std::vector getAnalogues() const { + return rec->getValueAsListOfStrings("analogues"); + } + +private: + std::vector rets; + std::vector params; + + const 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 0000000000000..1912abf5265c7 --- /dev/null +++ b/offload/tools/offload-tblgen/offload-tblgen.cpp @@ -0,0 +1,101 @@ +//===- 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, + GenExports +}; + +namespace { +cl::opt 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"), + clEnumValN(GenExports, "gen-exports", + "Generate export file for the Offload library"))); +} + +static bool OffloadTableGenMain(raw_ostream &OS, const 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; + case GenExports: + EmitOffloadExports(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); } diff --git a/offload/unittests/CMakeLists.txt b/offload/unittests/CMakeLists.txt index 73c87b708d25f..25ac4b2fa3675 100644 --- a/offload/unittests/CMakeLists.txt +++ b/offload/unittests/CMakeLists.txt @@ -5,4 +5,5 @@ function(add_libompt_unittest test_dirname) add_unittest(LibomptUnitTests ${test_dirname} ${ARGN}) endfunction() -add_subdirectory(Plugins) +# add_subdirectory(Plugins) +add_subdirectory(OffloadAPI) diff --git a/offload/unittests/OffloadAPI/CMakeLists.txt b/offload/unittests/OffloadAPI/CMakeLists.txt new file mode 100644 index 0000000000000..033ee2b6ec746 --- /dev/null +++ b/offload/unittests/OffloadAPI/CMakeLists.txt @@ -0,0 +1,16 @@ +set(PLUGINS_TEST_COMMON LLVMOffload) +set(PLUGINS_TEST_INCLUDE ${LIBOMPTARGET_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/common) + +add_libompt_unittest("offload.unittests" + ${CMAKE_CURRENT_SOURCE_DIR}/common/Environment.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/olGetPlatform.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/olGetPlatformCount.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/olGetPlatformInfo.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/platform/olGetPlatformInfoSize.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/device/olGetDevice.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/device/olGetDeviceCount.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/device/olGetDeviceInfo.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/device/olGetDeviceInfoSize.cpp) +add_dependencies("offload.unittests" ${PLUGINS_TEST_COMMON}) +target_link_libraries("offload.unittests" PRIVATE ${PLUGINS_TEST_COMMON}) +target_include_directories("offload.unittests" PRIVATE ${PLUGINS_TEST_INCLUDE}) diff --git a/offload/unittests/OffloadAPI/common/Environment.cpp b/offload/unittests/OffloadAPI/common/Environment.cpp new file mode 100644 index 0000000000000..f07a66cda2189 --- /dev/null +++ b/offload/unittests/OffloadAPI/common/Environment.cpp @@ -0,0 +1,96 @@ +//===------- Offload API tests - gtest environment ------------------------===// +// +// 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 "Environment.hpp" +#include "Fixtures.hpp" +#include "llvm/Support/CommandLine.h" +#include + +using namespace llvm; + +// Wrapper so we don't have to constantly init and shutdown Offload in every +// test, while having sensible lifetime for the platform environment +struct OffloadInitWrapper { + OffloadInitWrapper() { olInit(); } + ~OffloadInitWrapper() { olShutDown(); } +}; +static OffloadInitWrapper Wrapper{}; + +static cl::opt + SelectedPlatform("platform", cl::desc("Only test the specified platform"), + cl::value_desc("platform")); + +std::ostream &operator<<(std::ostream &Out, + const ol_platform_handle_t &Platform) { + size_t Size; + olGetPlatformInfoSize(Platform, OL_PLATFORM_INFO_NAME, &Size); + std::vector Name(Size); + olGetPlatformInfo(Platform, OL_PLATFORM_INFO_NAME, Size, Name.data()); + Out << Name.data(); + return Out; +} + +std::ostream &operator<<(std::ostream &Out, + const std::vector &Platforms) { + for (auto Platform : Platforms) { + Out << "\n * \"" << Platform << "\""; + } + return Out; +} + +const std::vector &TestEnvironment::getPlatforms() { + static std::vector Platforms{}; + + if (Platforms.empty()) { + uint32_t PlatformCount = 0; + olGetPlatformCount(&PlatformCount); + if (PlatformCount > 0) { + Platforms.resize(PlatformCount); + olGetPlatform(PlatformCount, Platforms.data()); + } + } + + return Platforms; +} + +// Get a single platform, which may be selected by the user. +ol_platform_handle_t TestEnvironment::getPlatform() { + static ol_platform_handle_t Platform = nullptr; + const auto &Platforms = getPlatforms(); + + if (!Platform) { + if (SelectedPlatform != "") { + for (const auto CandidatePlatform : Platforms) { + std::stringstream PlatformName; + PlatformName << CandidatePlatform; + if (SelectedPlatform == PlatformName.str()) { + Platform = CandidatePlatform; + return Platform; + } + } + std::cout << "No platform found with the name \"" << SelectedPlatform + << "\". Choose from:" << Platforms << "\n"; + std::exit(1); + } else { + // Pick a single platform. We prefer one that has available devices, but + // just pick the first initially in case none have any devices. + Platform = Platforms[0]; + for (auto CandidatePlatform : Platforms) { + uint32_t NumDevices = 0; + if (olGetDeviceCount(CandidatePlatform, &NumDevices) == OL_SUCCESS) { + if (NumDevices > 0) { + Platform = CandidatePlatform; + break; + } + } + } + } + } + + return Platform; +} diff --git a/offload/unittests/OffloadAPI/common/Environment.hpp b/offload/unittests/OffloadAPI/common/Environment.hpp new file mode 100644 index 0000000000000..6dba2381eb0b7 --- /dev/null +++ b/offload/unittests/OffloadAPI/common/Environment.hpp @@ -0,0 +1,17 @@ +//===------- Offload API tests - gtest environment ------------------------===// +// +// 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 +#include + +namespace TestEnvironment { +const std::vector &getPlatforms(); +ol_platform_handle_t getPlatform(); +} // namespace TestEnvironment diff --git a/offload/unittests/OffloadAPI/common/Fixtures.hpp b/offload/unittests/OffloadAPI/common/Fixtures.hpp new file mode 100644 index 0000000000000..410a435dee1b5 --- /dev/null +++ b/offload/unittests/OffloadAPI/common/Fixtures.hpp @@ -0,0 +1,64 @@ +//===------- Offload API tests - gtest fixtures --==-----------------------===// +// +// 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 +#include +#include + +#include "Environment.hpp" + +#pragma once + +#ifndef ASSERT_SUCCESS +#define ASSERT_SUCCESS(ACTUAL) ASSERT_EQ(OL_SUCCESS, ACTUAL) +#endif + +// TODO: rework this so the EXPECTED/ACTUAL results are readable +#ifndef ASSERT_ERROR +#define ASSERT_ERROR(EXPECTED, ACTUAL) \ + do { \ + ol_result_t Res = ACTUAL; \ + ASSERT_TRUE(Res && (Res->Code == EXPECTED)); \ + } while (0) +#endif + +#define RETURN_ON_FATAL_FAILURE(...) \ + __VA_ARGS__; \ + if (this->HasFatalFailure() || this->IsSkipped()) { \ + return; \ + } \ + (void)0 + +struct offloadTest : ::testing::Test { + // No special behavior now, but just in case we need to override it in future +}; + +struct offloadPlatformTest : offloadTest { + void SetUp() override { + RETURN_ON_FATAL_FAILURE(offloadTest::SetUp()); + + Platform = TestEnvironment::getPlatform(); + ASSERT_NE(Platform, nullptr); + } + + ol_platform_handle_t Platform; +}; + +struct offloadDeviceTest : offloadPlatformTest { + void SetUp() override { + RETURN_ON_FATAL_FAILURE(offloadPlatformTest::SetUp()); + + uint32_t NumDevices; + ASSERT_SUCCESS(olGetDeviceCount(Platform, &NumDevices)); + if (NumDevices == 0) + GTEST_SKIP() << "No available devices on this platform."; + ASSERT_SUCCESS(olGetDevice(Platform, 1, &Device)); + } + + ol_device_handle_t Device; +}; diff --git a/offload/unittests/OffloadAPI/device/olDeviceInfo.hpp b/offload/unittests/OffloadAPI/device/olDeviceInfo.hpp new file mode 100644 index 0000000000000..06915258da384 --- /dev/null +++ b/offload/unittests/OffloadAPI/device/olDeviceInfo.hpp @@ -0,0 +1,21 @@ +//===------- Offload API tests - Helpers for device info query testing ----===// +// +// 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 +#include + +// TODO: We could autogenerate these +inline std::vector DeviceQueries = { + OL_DEVICE_INFO_TYPE, OL_DEVICE_INFO_PLATFORM, OL_DEVICE_INFO_NAME, + OL_DEVICE_INFO_VENDOR, OL_DEVICE_INFO_DRIVER_VERSION}; + +inline std::unordered_map DeviceInfoSizeMap = { + {OL_DEVICE_INFO_TYPE, sizeof(ol_device_type_t)}, + {OL_DEVICE_INFO_PLATFORM, sizeof(ol_platform_handle_t)}, +}; diff --git a/offload/unittests/OffloadAPI/device/olGetDevice.cpp b/offload/unittests/OffloadAPI/device/olGetDevice.cpp new file mode 100644 index 0000000000000..68d4682dd3351 --- /dev/null +++ b/offload/unittests/OffloadAPI/device/olGetDevice.cpp @@ -0,0 +1,39 @@ +//===------- Offload API tests - olGetDevice -------------------------===// +// +// 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 "../common/Fixtures.hpp" +#include +#include + +using olGetDeviceTest = offloadPlatformTest; + +TEST_F(olGetDeviceTest, Success) { + uint32_t Count = 0; + ASSERT_SUCCESS(olGetDeviceCount(Platform, &Count)); + if (Count == 0) + GTEST_SKIP() << "No available devices on this platform."; + + std::vector Devices(Count); + ASSERT_SUCCESS(olGetDevice(Platform, Count, Devices.data())); + for (auto Device : Devices) { + ASSERT_NE(nullptr, Device); + } +} + +TEST_F(olGetDeviceTest, SuccessSubsetOfDevices) { + uint32_t Count; + ASSERT_SUCCESS(olGetDeviceCount(Platform, &Count)); + if (Count < 2) + GTEST_SKIP() << "Only one device is available on this platform."; + + std::vector Devices(Count - 1); + ASSERT_SUCCESS(olGetDevice(Platform, Count - 1, Devices.data())); + for (auto Device : Devices) { + ASSERT_NE(nullptr, Device); + } +} diff --git a/offload/unittests/OffloadAPI/device/olGetDeviceCount.cpp b/offload/unittests/OffloadAPI/device/olGetDeviceCount.cpp new file mode 100644 index 0000000000000..ef377d671bf60 --- /dev/null +++ b/offload/unittests/OffloadAPI/device/olGetDeviceCount.cpp @@ -0,0 +1,28 @@ +//===------- Offload API tests - olGetDeviceCount --------------------===// +// +// 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 "../common/Fixtures.hpp" +#include +#include + +using olGetDeviceCountTest = offloadPlatformTest; + +TEST_F(olGetDeviceCountTest, Success) { + uint32_t Count = 0; + ASSERT_SUCCESS(olGetDeviceCount(Platform, &Count)); +} + +TEST_F(olGetDeviceCountTest, InvalidNullPlatform) { + uint32_t Count = 0; + ASSERT_ERROR(OL_ERRC_INVALID_NULL_HANDLE, olGetDeviceCount(nullptr, &Count)); +} + +TEST_F(olGetDeviceCountTest, InvalidNullPointer) { + ASSERT_ERROR(OL_ERRC_INVALID_NULL_POINTER, + olGetDeviceCount(Platform, nullptr)); +} diff --git a/offload/unittests/OffloadAPI/device/olGetDeviceInfo.cpp b/offload/unittests/OffloadAPI/device/olGetDeviceInfo.cpp new file mode 100644 index 0000000000000..c936802fb1e4d --- /dev/null +++ b/offload/unittests/OffloadAPI/device/olGetDeviceInfo.cpp @@ -0,0 +1,76 @@ +//===------- Offload API tests - olGetDeviceInfo ---------------------===// +// +// 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 "../common/Fixtures.hpp" +#include "olDeviceInfo.hpp" +#include +#include + +struct olGetDeviceInfoTest : offloadDeviceTest, + ::testing::WithParamInterface { + + void SetUp() override { RETURN_ON_FATAL_FAILURE(offloadDeviceTest::SetUp()); } +}; + +INSTANTIATE_TEST_SUITE_P( + , olGetDeviceInfoTest, ::testing::ValuesIn(DeviceQueries), + [](const ::testing::TestParamInfo &info) { + std::stringstream ss; + ss << info.param; + return ss.str(); + }); + +TEST_P(olGetDeviceInfoTest, Success) { + ol_device_info_t InfoType = GetParam(); + size_t Size = 0; + + ASSERT_SUCCESS(olGetDeviceInfoSize(Device, InfoType, &Size)); + + std::vector InfoData(Size); + ASSERT_SUCCESS(olGetDeviceInfo(Device, InfoType, Size, InfoData.data())); + + if (InfoType == OL_DEVICE_INFO_PLATFORM) { + auto *ReturnedPlatform = + reinterpret_cast(InfoData.data()); + ASSERT_EQ(Platform, *ReturnedPlatform); + } +} + +TEST_F(olGetDeviceInfoTest, InvalidNullHandleDevice) { + ol_device_type_t DeviceType; + ASSERT_ERROR(OL_ERRC_INVALID_NULL_HANDLE, + olGetDeviceInfo(nullptr, OL_DEVICE_INFO_TYPE, + sizeof(ol_device_type_t), &DeviceType)); +} + +TEST_F(olGetDeviceInfoTest, InvalidEnumerationInfoType) { + ol_device_type_t DeviceType; + ASSERT_ERROR(OL_ERRC_INVALID_ENUMERATION, + olGetDeviceInfo(Device, OL_DEVICE_INFO_FORCE_UINT32, + sizeof(ol_device_type_t), &DeviceType)); +} + +TEST_F(olGetDeviceInfoTest, InvalidSizePropSize) { + ol_device_type_t DeviceType; + ASSERT_ERROR(OL_ERRC_INVALID_SIZE, + olGetDeviceInfo(Device, OL_DEVICE_INFO_TYPE, 0, &DeviceType)); +} + +TEST_F(olGetDeviceInfoTest, InvalidSizePropSizeSmall) { + ol_device_type_t DeviceType; + ASSERT_ERROR(OL_ERRC_INVALID_SIZE, + olGetDeviceInfo(Device, OL_DEVICE_INFO_TYPE, + sizeof(DeviceType) - 1, &DeviceType)); +} + +TEST_F(olGetDeviceInfoTest, InvalidNullPointerPropValue) { + ol_device_type_t DeviceType; + ASSERT_ERROR(OL_ERRC_INVALID_NULL_POINTER, + olGetDeviceInfo(Device, OL_DEVICE_INFO_TYPE, sizeof(DeviceType), + nullptr)); +} diff --git a/offload/unittests/OffloadAPI/device/olGetDeviceInfoSize.cpp b/offload/unittests/OffloadAPI/device/olGetDeviceInfoSize.cpp new file mode 100644 index 0000000000000..9e792d1c3e25e --- /dev/null +++ b/offload/unittests/OffloadAPI/device/olGetDeviceInfoSize.cpp @@ -0,0 +1,58 @@ +//===------- Offload API tests - olGetDeviceInfoSize -----------------===// +// +// 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 + +#include "../common/Fixtures.hpp" +#include "olDeviceInfo.hpp" + +struct olGetDeviceInfoSizeTest + : offloadDeviceTest, + ::testing::WithParamInterface { + + void SetUp() override { RETURN_ON_FATAL_FAILURE(offloadDeviceTest::SetUp()); } +}; + +// TODO: We could autogenerate the list of enum values +INSTANTIATE_TEST_SUITE_P( + , olGetDeviceInfoSizeTest, ::testing::ValuesIn(DeviceQueries), + [](const ::testing::TestParamInfo &info) { + std::stringstream ss; + ss << info.param; + return ss.str(); + }); + +TEST_P(olGetDeviceInfoSizeTest, Success) { + ol_device_info_t InfoType = GetParam(); + size_t Size = 0; + + ASSERT_SUCCESS(olGetDeviceInfoSize(Device, InfoType, &Size)); + auto ExpectedSize = DeviceInfoSizeMap.find(InfoType); + if (ExpectedSize != DeviceInfoSizeMap.end()) { + ASSERT_EQ(Size, ExpectedSize->second); + } else { + ASSERT_NE(Size, 0lu); + } +} + +TEST_F(olGetDeviceInfoSizeTest, InvalidNullHandle) { + size_t Size = 0; + ASSERT_ERROR(OL_ERRC_INVALID_NULL_HANDLE, + olGetDeviceInfoSize(nullptr, OL_DEVICE_INFO_TYPE, &Size)); +} + +TEST_F(olGetDeviceInfoSizeTest, InvalidDeviceInfoEnumeration) { + size_t Size = 0; + ASSERT_ERROR(OL_ERRC_INVALID_ENUMERATION, + olGetDeviceInfoSize(Device, OL_DEVICE_INFO_FORCE_UINT32, &Size)); +} + +TEST_F(olGetDeviceInfoSizeTest, InvalidNullPointer) { + ASSERT_ERROR(OL_ERRC_INVALID_NULL_POINTER, + olGetDeviceInfoSize(Device, OL_DEVICE_INFO_TYPE, nullptr)); +} diff --git a/offload/unittests/OffloadAPI/platform/olGetPlatform.cpp b/offload/unittests/OffloadAPI/platform/olGetPlatform.cpp new file mode 100644 index 0000000000000..4a2f9e8ac7741 --- /dev/null +++ b/offload/unittests/OffloadAPI/platform/olGetPlatform.cpp @@ -0,0 +1,28 @@ +//===------- Offload API tests - olGetPlatform -----------------------===// +// +// 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 "../common/Fixtures.hpp" +#include +#include + +using olGetPlatformTest = offloadTest; + +TEST_F(olGetPlatformTest, Success) { + uint32_t PlatformCount; + ASSERT_SUCCESS(olGetPlatformCount(&PlatformCount)); + std::vector Platforms(PlatformCount); + ASSERT_SUCCESS(olGetPlatform(PlatformCount, Platforms.data())); +} + +TEST_F(olGetPlatformTest, InvalidNumEntries) { + uint32_t PlatformCount; + ASSERT_SUCCESS(olGetPlatformCount(&PlatformCount)); + std::vector Platforms(PlatformCount); + ASSERT_ERROR(OL_ERRC_INVALID_SIZE, + olGetPlatform(PlatformCount + 1, Platforms.data())); +} diff --git a/offload/unittests/OffloadAPI/platform/olGetPlatformCount.cpp b/offload/unittests/OffloadAPI/platform/olGetPlatformCount.cpp new file mode 100644 index 0000000000000..15b4b6abcd70d --- /dev/null +++ b/offload/unittests/OffloadAPI/platform/olGetPlatformCount.cpp @@ -0,0 +1,22 @@ +//===------- Offload API tests - olGetPlatformCount ------------------===// +// +// 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 "../common/Fixtures.hpp" +#include +#include + +using olGetPlatformCountTest = offloadTest; + +TEST_F(olGetPlatformCountTest, Success) { + uint32_t PlatformCount; + ASSERT_SUCCESS(olGetPlatformCount(&PlatformCount)); +} + +TEST_F(olGetPlatformCountTest, InvalidNullPointer) { + ASSERT_ERROR(OL_ERRC_INVALID_NULL_POINTER, olGetPlatformCount(nullptr)); +} diff --git a/offload/unittests/OffloadAPI/platform/olGetPlatformInfo.cpp b/offload/unittests/OffloadAPI/platform/olGetPlatformInfo.cpp new file mode 100644 index 0000000000000..c646bdc50b7da --- /dev/null +++ b/offload/unittests/OffloadAPI/platform/olGetPlatformInfo.cpp @@ -0,0 +1,76 @@ +//===------- Offload API tests - olGetPlatformInfo -------------------===// +// +// 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 + +#include "../common/Fixtures.hpp" +#include "olPlatformInfo.hpp" + +struct olGetPlatformInfoTest + : offloadPlatformTest, + ::testing::WithParamInterface {}; + +INSTANTIATE_TEST_SUITE_P( + olGetPlatformInfo, olGetPlatformInfoTest, + ::testing::ValuesIn(PlatformQueries), + [](const ::testing::TestParamInfo &info) { + std::stringstream ss; + ss << info.param; + return ss.str(); + }); + +TEST_P(olGetPlatformInfoTest, Success) { + size_t Size = 0; + ol_platform_info_t InfoType = GetParam(); + + ASSERT_SUCCESS(olGetPlatformInfoSize(Platform, InfoType, &Size)); + std::vector InfoData(Size); + ASSERT_SUCCESS(olGetPlatformInfo(Platform, InfoType, Size, InfoData.data())); + + // Info types with a dynamic size are all char[] so we can verify the returned + // string is the expected size. + auto ExpectedSize = PlatformInfoSizeMap.find(InfoType); + if (ExpectedSize == PlatformInfoSizeMap.end()) { + ASSERT_EQ(Size, strlen(InfoData.data()) + 1); + } +} + +TEST_F(olGetPlatformInfoTest, InvalidNullHandle) { + ol_platform_backend_t Backend; + ASSERT_ERROR(OL_ERRC_INVALID_NULL_HANDLE, + olGetPlatformInfo(nullptr, OL_PLATFORM_INFO_BACKEND, + sizeof(Backend), &Backend)); +} + +TEST_F(olGetPlatformInfoTest, InvalidPlatformInfoEnumeration) { + ol_platform_backend_t Backend; + ASSERT_ERROR(OL_ERRC_INVALID_ENUMERATION, + olGetPlatformInfo(Platform, OL_PLATFORM_INFO_FORCE_UINT32, + sizeof(Backend), &Backend)); +} + +TEST_F(olGetPlatformInfoTest, InvalidSizeZero) { + ol_platform_backend_t Backend; + ASSERT_ERROR( + OL_ERRC_INVALID_SIZE, + olGetPlatformInfo(Platform, OL_PLATFORM_INFO_BACKEND, 0, &Backend)); +} + +TEST_F(olGetPlatformInfoTest, InvalidSizeSmall) { + ol_platform_backend_t Backend; + ASSERT_ERROR(OL_ERRC_INVALID_SIZE, + olGetPlatformInfo(Platform, OL_PLATFORM_INFO_BACKEND, + sizeof(Backend) - 1, &Backend)); +} + +TEST_F(olGetPlatformInfoTest, InvalidNullPointerPropValue) { + ol_platform_backend_t Backend; + ASSERT_ERROR(OL_ERRC_INVALID_NULL_POINTER, + olGetPlatformInfo(Platform, OL_PLATFORM_INFO_BACKEND, + sizeof(Backend), nullptr)); +} diff --git a/offload/unittests/OffloadAPI/platform/olGetPlatformInfoSize.cpp b/offload/unittests/OffloadAPI/platform/olGetPlatformInfoSize.cpp new file mode 100644 index 0000000000000..7c9274082e8e4 --- /dev/null +++ b/offload/unittests/OffloadAPI/platform/olGetPlatformInfoSize.cpp @@ -0,0 +1,57 @@ +//===------- Offload API tests - olGetPlatformInfoSize ---------------===// +// +// 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 + +#include "../common/Fixtures.hpp" +#include "olPlatformInfo.hpp" + +struct olGetPlatformInfoSizeTest + : offloadPlatformTest, + ::testing::WithParamInterface {}; + +INSTANTIATE_TEST_SUITE_P( + olGetPlatformInfoSize, olGetPlatformInfoSizeTest, + ::testing::ValuesIn(PlatformQueries), + [](const ::testing::TestParamInfo &info) { + std::stringstream ss; + ss << info.param; + return ss.str(); + }); + +TEST_P(olGetPlatformInfoSizeTest, Success) { + size_t Size = 0; + ol_platform_info_t InfoType = GetParam(); + + ASSERT_SUCCESS(olGetPlatformInfoSize(Platform, InfoType, &Size)); + auto ExpectedSize = PlatformInfoSizeMap.find(InfoType); + if (ExpectedSize != PlatformInfoSizeMap.end()) { + ASSERT_EQ(Size, ExpectedSize->second); + } else { + ASSERT_NE(Size, 0lu); + } +} + +TEST_F(olGetPlatformInfoSizeTest, InvalidNullHandle) { + size_t Size = 0; + ASSERT_ERROR(OL_ERRC_INVALID_NULL_HANDLE, + olGetPlatformInfoSize(nullptr, OL_PLATFORM_INFO_BACKEND, &Size)); +} + +TEST_F(olGetPlatformInfoSizeTest, InvalidPlatformInfoEnumeration) { + size_t Size = 0; + ASSERT_ERROR( + OL_ERRC_INVALID_ENUMERATION, + olGetPlatformInfoSize(Platform, OL_PLATFORM_INFO_FORCE_UINT32, &Size)); +} + +TEST_F(olGetPlatformInfoSizeTest, InvalidNullPointer) { + ASSERT_ERROR( + OL_ERRC_INVALID_NULL_POINTER, + olGetPlatformInfoSize(Platform, OL_PLATFORM_INFO_BACKEND, nullptr)); +} diff --git a/offload/unittests/OffloadAPI/platform/olPlatformInfo.hpp b/offload/unittests/OffloadAPI/platform/olPlatformInfo.hpp new file mode 100644 index 0000000000000..d49cdb90d321a --- /dev/null +++ b/offload/unittests/OffloadAPI/platform/olPlatformInfo.hpp @@ -0,0 +1,20 @@ +//===------- Offload API tests - Helpers for platform info query testing --===// +// +// 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 + +// TODO: We could autogenerate these + +inline std::vector PlatformQueries = { + OL_PLATFORM_INFO_NAME, OL_PLATFORM_INFO_VENDOR_NAME, + OL_PLATFORM_INFO_VERSION, OL_PLATFORM_INFO_BACKEND}; + +inline std::unordered_map PlatformInfoSizeMap = { + {OL_PLATFORM_INFO_BACKEND, sizeof(ol_platform_backend_t)}, +};