From c979ec05642f292737d250c6682d85ed49bc7b6e Mon Sep 17 00:00:00 2001 From: Callum Fare Date: Wed, 27 Nov 2024 10:39:07 +0000 Subject: [PATCH] Reland - [Offload] Introduce offload-tblgen and initial new API implementation (#108413) (#117704) Relands changes from #108413 - this was reverted due to build issues. The problem was just that the `offload-tblgen` tool was behind recent changes to tablegen that ensure `const` records. This has been fixed and the PR is otherwise identical. ___ ### New API Previous discussions at the LLVM/Offload meeting have brought up the need for a new API for exposing the functionality of the plugins. This change introduces a very small subset of a new API, which is primarily for testing the offload tooling and demonstrating how a new API can fit into the existing code base without being too disruptive. Exact designs for these entry points and future additions can be worked out over time. The new API does however introduce the bare minimum functionality to implement device discovery for Unified Runtime and SYCL. This means that the `urinfo` and `sycl-ls` tools can be used on top of Offload. A (rough) implementation of a Unified Runtime adapter (aka plugin) for Offload is available [here](https://github.com/callumfare/unified-runtime/tree/offload_adapter). Our intention is to maintain this and use it to implement and test Offload API changes with SYCL. ### Demoing the new API ```sh # From the runtime build directory $ ninja LibomptUnitTests $ OFFLOAD_TRACE=1 ./offload/unittests/OffloadAPI/offload.unittests ``` ### Open questions and future work * Only some of the available device info is exposed, and not all the possible device queries needed for SYCL are implemented by the plugins. A sensible next step would be to refactor and extend the existing device info queries in the plugins. The existing info queries are all strings, but the new API introduces the ability to return any arbitrary type. * It may be sensible at some point for the plugins to implement the new API directly, and the higher level code on top of it could be made generic, but this is more of a long-term possibility. --- offload/CMakeLists.txt | 3 + offload/cmake/OpenMPTesting.cmake | 12 + offload/liboffload/API/APIDefs.td | 212 ++++++ offload/liboffload/API/CMakeLists.txt | 25 + offload/liboffload/API/Common.td | 141 ++++ offload/liboffload/API/Device.td | 106 +++ offload/liboffload/API/OffloadAPI.td | 15 + offload/liboffload/API/Platform.td | 112 ++++ offload/liboffload/API/README.md | 150 +++++ offload/liboffload/CMakeLists.txt | 32 + offload/liboffload/README.md | 8 + offload/liboffload/exports | 6 + offload/liboffload/include/OffloadImpl.hpp | 94 +++ .../liboffload/include/generated/OffloadAPI.h | 610 ++++++++++++++++++ .../include/generated/OffloadEntryPoints.inc | 441 +++++++++++++ .../include/generated/OffloadFuncs.inc | 34 + .../generated/OffloadImplFuncDecls.inc | 38 ++ .../include/generated/OffloadPrint.hpp | 428 ++++++++++++ offload/liboffload/src/Helpers.hpp | 95 +++ offload/liboffload/src/OffloadImpl.cpp | 232 +++++++ offload/liboffload/src/OffloadLib.cpp | 44 ++ .../common/include/PluginInterface.h | 4 + offload/test/lit.cfg | 3 +- offload/test/lit.site.cfg.in | 1 + .../tools/offload-tblgen/default_returns.td | 40 ++ .../test/tools/offload-tblgen/entry_points.td | 37 ++ .../tools/offload-tblgen/functions_basic.td | 39 ++ .../offload-tblgen/functions_code_loc.td | 26 + .../offload-tblgen/functions_ranged_param.td | 36 ++ .../test/tools/offload-tblgen/print_enum.td | 34 + .../tools/offload-tblgen/print_function.td | 38 ++ .../tools/offload-tblgen/type_tagged_enum.td | 76 +++ offload/tools/offload-tblgen/APIGen.cpp | 229 +++++++ offload/tools/offload-tblgen/CMakeLists.txt | 24 + .../tools/offload-tblgen/EntryPointGen.cpp | 138 ++++ offload/tools/offload-tblgen/FuncsGen.cpp | 74 +++ offload/tools/offload-tblgen/GenCommon.hpp | 67 ++ offload/tools/offload-tblgen/Generators.hpp | 23 + offload/tools/offload-tblgen/PrintGen.cpp | 226 +++++++ offload/tools/offload-tblgen/RecordTypes.hpp | 227 +++++++ .../tools/offload-tblgen/offload-tblgen.cpp | 101 +++ offload/unittests/CMakeLists.txt | 3 +- offload/unittests/OffloadAPI/CMakeLists.txt | 16 + .../OffloadAPI/common/Environment.cpp | 96 +++ .../OffloadAPI/common/Environment.hpp | 17 + .../unittests/OffloadAPI/common/Fixtures.hpp | 64 ++ .../OffloadAPI/device/olDeviceInfo.hpp | 21 + .../OffloadAPI/device/olGetDevice.cpp | 39 ++ .../OffloadAPI/device/olGetDeviceCount.cpp | 28 + .../OffloadAPI/device/olGetDeviceInfo.cpp | 76 +++ .../OffloadAPI/device/olGetDeviceInfoSize.cpp | 58 ++ .../OffloadAPI/platform/olGetPlatform.cpp | 28 + .../platform/olGetPlatformCount.cpp | 22 + .../OffloadAPI/platform/olGetPlatformInfo.cpp | 76 +++ .../platform/olGetPlatformInfoSize.cpp | 57 ++ .../OffloadAPI/platform/olPlatformInfo.hpp | 20 + 56 files changed, 4900 insertions(+), 2 deletions(-) create mode 100644 offload/liboffload/API/APIDefs.td create mode 100644 offload/liboffload/API/CMakeLists.txt create mode 100644 offload/liboffload/API/Common.td create mode 100644 offload/liboffload/API/Device.td create mode 100644 offload/liboffload/API/OffloadAPI.td create mode 100644 offload/liboffload/API/Platform.td create mode 100644 offload/liboffload/API/README.md create mode 100644 offload/liboffload/CMakeLists.txt create mode 100644 offload/liboffload/README.md create mode 100644 offload/liboffload/exports create mode 100644 offload/liboffload/include/OffloadImpl.hpp create mode 100644 offload/liboffload/include/generated/OffloadAPI.h create mode 100644 offload/liboffload/include/generated/OffloadEntryPoints.inc create mode 100644 offload/liboffload/include/generated/OffloadFuncs.inc create mode 100644 offload/liboffload/include/generated/OffloadImplFuncDecls.inc create mode 100644 offload/liboffload/include/generated/OffloadPrint.hpp create mode 100644 offload/liboffload/src/Helpers.hpp create mode 100644 offload/liboffload/src/OffloadImpl.cpp create mode 100644 offload/liboffload/src/OffloadLib.cpp create mode 100644 offload/test/tools/offload-tblgen/default_returns.td create mode 100644 offload/test/tools/offload-tblgen/entry_points.td create mode 100644 offload/test/tools/offload-tblgen/functions_basic.td create mode 100644 offload/test/tools/offload-tblgen/functions_code_loc.td create mode 100644 offload/test/tools/offload-tblgen/functions_ranged_param.td create mode 100644 offload/test/tools/offload-tblgen/print_enum.td create mode 100644 offload/test/tools/offload-tblgen/print_function.td create mode 100644 offload/test/tools/offload-tblgen/type_tagged_enum.td create mode 100644 offload/tools/offload-tblgen/APIGen.cpp create mode 100644 offload/tools/offload-tblgen/CMakeLists.txt create mode 100644 offload/tools/offload-tblgen/EntryPointGen.cpp create mode 100644 offload/tools/offload-tblgen/FuncsGen.cpp create mode 100644 offload/tools/offload-tblgen/GenCommon.hpp create mode 100644 offload/tools/offload-tblgen/Generators.hpp create mode 100644 offload/tools/offload-tblgen/PrintGen.cpp create mode 100644 offload/tools/offload-tblgen/RecordTypes.hpp create mode 100644 offload/tools/offload-tblgen/offload-tblgen.cpp create mode 100644 offload/unittests/OffloadAPI/CMakeLists.txt create mode 100644 offload/unittests/OffloadAPI/common/Environment.cpp create mode 100644 offload/unittests/OffloadAPI/common/Environment.hpp create mode 100644 offload/unittests/OffloadAPI/common/Fixtures.hpp create mode 100644 offload/unittests/OffloadAPI/device/olDeviceInfo.hpp create mode 100644 offload/unittests/OffloadAPI/device/olGetDevice.cpp create mode 100644 offload/unittests/OffloadAPI/device/olGetDeviceCount.cpp create mode 100644 offload/unittests/OffloadAPI/device/olGetDeviceInfo.cpp create mode 100644 offload/unittests/OffloadAPI/device/olGetDeviceInfoSize.cpp create mode 100644 offload/unittests/OffloadAPI/platform/olGetPlatform.cpp create mode 100644 offload/unittests/OffloadAPI/platform/olGetPlatformCount.cpp create mode 100644 offload/unittests/OffloadAPI/platform/olGetPlatformInfo.cpp create mode 100644 offload/unittests/OffloadAPI/platform/olGetPlatformInfoSize.cpp create mode 100644 offload/unittests/OffloadAPI/platform/olPlatformInfo.hpp diff --git a/offload/CMakeLists.txt b/offload/CMakeLists.txt index dfd25bad608436..af2e07d5147216 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 6609d6301d0f93..f97def2c52eba9 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 00000000000000..60c1b85d26911e --- /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 00000000000000..8fd6cb539374a5 --- /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 00000000000000..5b19d1d47129ef --- /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 00000000000000..30c0b71fe7b37c --- /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 00000000000000..8a0c3c40581223 --- /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 00000000000000..03e70cf96ac94f --- /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 00000000000000..38a055811b2d00 --- /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 00000000000000..27ba9e93c36753 --- /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 00000000000000..95c9bf54d7badd --- /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 00000000000000..168341aa7d9380 --- /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 00000000000000..6d745095f3105a --- /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 00000000000000..11fcc96625ab8d --- /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 00000000000000..49c1c8169615e5 --- /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 00000000000000..48115493c790f4 --- /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 00000000000000..5b26b2653a05d9 --- /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 00000000000000..8981bb054a4cb1 --- /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 00000000000000..d003d302524625 --- /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 00000000000000..79a4e766f2ce92 --- /dev/null +++ b/offload/liboffload/src/OffloadImpl.cpp @@ -0,0 +1,232 @@ +//===- 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; +} + +// 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 00000000000000..37876713212c98 --- /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 41cc0f286a581f..82efcdaf5ad3dc 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 2f1ef3e98d8172..658ae5f9653ba9 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 a1cb5acc38a405..ce3f6abf50a132 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 00000000000000..432063e0174afa --- /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 00000000000000..2d2bd1f5e3bfcc --- /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 00000000000000..6601746a727b00 --- /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 00000000000000..4c8d3688566c35 --- /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 00000000000000..efa8bae5290ec2 --- /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 00000000000000..1e1d7f57218d0a --- /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 00000000000000..2a9cce724eda94 --- /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 00000000000000..ea83545e0a3859 --- /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 00000000000000..97a2464f7a75c1 --- /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 00000000000000..52986cbbaa9187 --- /dev/null +++ b/offload/tools/offload-tblgen/CMakeLists.txt @@ -0,0 +1,24 @@ +##===----------------------------------------------------------------------===## +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +##===----------------------------------------------------------------------===## +include(TableGen) + +add_tablegen(offload-tblgen OFFLOAD + EXPORT OFFLOAD + APIGen.cpp + EntryPointGen.cpp + FuncsGen.cpp + GenCommon.hpp + Generators.hpp + offload-tblgen.cpp + PrintGen.cpp + RecordTypes.hpp + ) + +set(OFFLOAD_TABLEGEN_EXE "${OFFLOAD_TABLEGEN_EXE}" CACHE INTERNAL "") +set(OFFLOAD_TABLEGEN_TARGET "${OFFLOAD_TABLEGEN_TARGET}" CACHE INTERNAL "") + diff --git a/offload/tools/offload-tblgen/EntryPointGen.cpp b/offload/tools/offload-tblgen/EntryPointGen.cpp new file mode 100644 index 00000000000000..990ff96a3121d9 --- /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 00000000000000..32386521761984 --- /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 00000000000000..db432e9958b5d5 --- /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 00000000000000..8b6104c5cd9c61 --- /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 00000000000000..2a7c63c3dfd1f7 --- /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 00000000000000..0bf3256c525d9e --- /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 00000000000000..1912abf5265c7c --- /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 73c87b708d25fd..25ac4b2fa36752 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 00000000000000..033ee2b6ec746a --- /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 00000000000000..f07a66cda21892 --- /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 00000000000000..6dba2381eb0b71 --- /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 00000000000000..410a435dee1b5f --- /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 00000000000000..06915258da3842 --- /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 00000000000000..68d4682dd3351c --- /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 00000000000000..ef377d671bf60d --- /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 00000000000000..c936802fb1e4d6 --- /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 00000000000000..9e792d1c3e25ef --- /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 00000000000000..4a2f9e8ac77414 --- /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 00000000000000..15b4b6abcd70da --- /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 00000000000000..c646bdc50b7da1 --- /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 00000000000000..7c9274082e8e49 --- /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 00000000000000..d49cdb90d321ad --- /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)}, +};