diff options
| author | Callum Fare <callum@codeplay.com> | 2024-12-05 08:34:04 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-12-05 09:34:04 +0100 |
| commit | fd3907ccb583df99e9c19d2fe84e4e7c52d75de9 (patch) | |
| tree | deaffb6b369c1ec87261df173b32717b07f7525c /offload/liboffload/API | |
| parent | 636beb6a2833ee0290935f679252c1b662721b31 (diff) | |
Reland #118503: [Offload] Introduce offload-tblgen and initial new API implementation (#118614)
Reland #118503. Added a fix for builds with `-DBUILD_SHARED_LIBS=ON`
(see last commit). Otherwise the changes are 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.
Diffstat (limited to 'offload/liboffload/API')
| -rw-r--r-- | offload/liboffload/API/APIDefs.td | 212 | ||||
| -rw-r--r-- | offload/liboffload/API/CMakeLists.txt | 25 | ||||
| -rw-r--r-- | offload/liboffload/API/Common.td | 141 | ||||
| -rw-r--r-- | offload/liboffload/API/Device.td | 106 | ||||
| -rw-r--r-- | offload/liboffload/API/OffloadAPI.td | 15 | ||||
| -rw-r--r-- | offload/liboffload/API/Platform.td | 112 | ||||
| -rw-r--r-- | offload/liboffload/API/README.md | 150 |
7 files changed, 761 insertions, 0 deletions
diff --git a/offload/liboffload/API/APIDefs.td b/offload/liboffload/API/APIDefs.td new file mode 100644 index 000000000000..60c1b85d2691 --- /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<string Type> { + // size("_handle_t") == 9 + bit ret = !if(!lt(!size(Type), 9), 0, + !ne(!find(Type, "_handle_t", !sub(!size(Type), 9)), -1)); +} + +// Does the type end with '*'? +class IsPointerType<string Type> { + bit ret = !ne(!find(Type, "*", !sub(!size(Type), 1)), -1); +} + +// Describes the valid range of a pointer parameter that reperesents an array +class Range<string Begin, string End> { + string begin = Begin; + string end = End; +} + +// Names the parameters that indicate the type and size of the data pointed to +// by an opaque pointer parameter +class TypeInfo<string TypeEnum, string TypeSize> { + string enum = TypeEnum; + string size = TypeSize; +} + +class Param<string Type, string Name, string Desc, bits<3> Flags = 0> { + string type = Type; + string name = Name; + string desc = Desc; + bits<3> flags = Flags; + Range range = Range<"", "">; + TypeInfo type_info = TypeInfo<"", "">; + bit IsHandle = IsHandleType<type>.ret; + bit IsPointer = IsPointerType<type>.ret; +} + +// A parameter whose range is described by other parameters in the function. +class RangedParam<string Type, string Name, string Desc, bits<3> Flags, Range ParamRange> : Param<Type, Name, Desc, Flags> { + let range = ParamRange; +} + +// A parameter (normally of type void*) which has its pointee type and size +// described by other parameters in the function. +class TypeTaggedParam<string Type, string Name, string Desc, bits<3> Flags, TypeInfo ParamTypeInfo> : Param<Type, Name, Desc, Flags> { + let type_info = ParamTypeInfo; +} + +class Return<string Value, list<string> Conditions = []> { + string value = Value; + list<string> conditions = Conditions; +} + +class ShouldCheckHandle<Param P> { + bit ret = !and(P.IsHandle, !eq(!and(PARAM_OPTIONAL, P.flags), 0)); +} + +class ShouldCheckPointer<Param P> { + bit ret = !and(P.IsPointer, !eq(!and(PARAM_OPTIONAL, P.flags), 0)); +} + +// For a list of returns that contains a specific return code, find and append +// new conditions to that return +class AppendConditionsToReturn<list<Return> Returns, string ReturnValue, + list<string> Conditions> { + list<Return> ret = + !foreach(Ret, Returns, + !if(!eq(Ret.value, ReturnValue), + Return<Ret.value, Ret.conditions#Conditions>, Ret)); +} + +// Add null handle checks to a function's return value descriptions +class AddHandleChecksToReturns<list<Param> Params, list<Return> Returns> { + list<string> handle_params = + !foreach(P, Params, !if(ShouldCheckHandle<P>.ret, P.name, "")); + list<string> handle_params_filt = + !filter(param, handle_params, !ne(param, "")); + list<string> handle_param_conds = + !foreach(handle, handle_params_filt, "`NULL == "#handle#"`"); + + // Does the list of returns already contain ERROR_INVALID_NULL_HANDLE? + bit returns_has_inv_handle = !foldl( + 0, Returns, HasErr, Ret, + !or(HasErr, !eq(Ret.value, PREFIX#"_ERRC_INVALID_NULL_HANDLE"))); + + list<Return> returns_out = !if(returns_has_inv_handle, + AppendConditionsToReturn<Returns, PREFIX # "_ERRC_INVALID_NULL_HANDLE", handle_param_conds>.ret, + !listconcat(Returns, [Return<PREFIX # "_ERRC_INVALID_NULL_HANDLE", handle_param_conds>]) + ); +} + +// Add null pointer checks to a function's return value descriptions +class AddPointerChecksToReturns<list<Param> Params, list<Return> Returns> { + list<string> ptr_params = + !foreach(P, Params, !if(ShouldCheckPointer<P>.ret, P.name, "")); + list<string> ptr_params_filt = !filter(param, ptr_params, !ne(param, "")); + list<string> ptr_param_conds = + !foreach(ptr, ptr_params_filt, "`NULL == "#ptr#"`"); + + // Does the list of returns already contain ERROR_INVALID_NULL_POINTER? + bit returns_has_inv_ptr = !foldl( + 0, Returns, HasErr, Ret, + !or(HasErr, !eq(Ret.value, PREFIX#"_ERRC_INVALID_NULL_POINTER"))); + list<Return> returns_out = !if(returns_has_inv_ptr, + AppendConditionsToReturn<Returns, PREFIX # "_ERRC_INVALID_NULL_POINTER", ptr_param_conds>.ret, + !listconcat(Returns, [Return<PREFIX # "_ERRC_INVALID_NULL_POINTER", ptr_param_conds>]) + ); +} + +defvar DefaultReturns = [Return<PREFIX#"_RESULT_SUCCESS">, + Return<PREFIX#"_ERRC_UNINITIALIZED">, + Return<PREFIX#"_ERRC_DEVICE_LOST">]; + +class APIObject { + string name; + string desc; +} + +class Function : APIObject { + list<Param> params; + list<Return> returns; + list<string> details = []; + list<string> analogues = []; + + list<Return> returns_with_def = !listconcat(DefaultReturns, returns); + list<Return> all_returns = AddPointerChecksToReturns<params, + AddHandleChecksToReturns<params, returns_with_def>.returns_out>.returns_out; +} + +class Etor<string Name, string Desc> { + string name = Name; + string desc = Desc; + string tagged_type; +} + +class TaggedEtor<string Name, string Type, string Desc> : Etor<Name, Desc> { + let tagged_type = Type; +} + +class Enum : APIObject { + // This refers to whether the enumerator descriptions specify a return + // type for functions where this enum may be used as an output type. If set, + // all Etor values must be TaggedEtor records + bit is_typed = 0; + + list<Etor> etors = []; +} + +class StructMember<string Type, string Name, string Desc> { + string type = Type; + string name = Name; + string desc = Desc; +} + +defvar DefaultPropStructMembers = + [StructMember<prefix#"_structure_type_t", "stype", + "type of this structure">, + StructMember<"void*", "pNext", "pointer to extension-specific structure">]; + +class StructHasInheritedMembers<string BaseClass> { + bit ret = !or(!eq(BaseClass, prefix#"_base_properties_t"), + !eq(BaseClass, prefix#"_base_desc_t")); +} + +class Struct : APIObject { + string base_class = ""; + list<StructMember> members; + list<StructMember> all_members = + !if(StructHasInheritedMembers<base_class>.ret, + DefaultPropStructMembers, [])#members; +} + +class Typedef : APIObject { string value; } + +class FptrTypedef : APIObject { + list<Param> params; + list<Return> returns; +} + +class Macro : APIObject { + string value; + + string condition; + string alt_value; +} + +class Handle : APIObject; diff --git a/offload/liboffload/API/CMakeLists.txt b/offload/liboffload/API/CMakeLists.txt new file mode 100644 index 000000000000..8fd6cb539374 --- /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 000000000000..5b19d1d47129 --- /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 000000000000..30c0b71fe7b3 --- /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 000000000000..8a0c3c405812 --- /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 000000000000..03e70cf96ac9 --- /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 000000000000..38a055811b2d --- /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 `<enum_name>_FORCE_UINT32 = 0x7fffffff` value, +which forces the underlying type to be uint32. + +### Handle +Represents a pointer to an opaque struct, as described in the Parameter section. +It does not take any extra fields. + +### Typedef +Represents a typedef, contains only a *value* field. + +### Macro +Represents a C preprocessor `#define`. Contains a *value* field. Optionally +takes a *condition* field, which allows the macro to be conditionally defined, +and an *alt_value* field, which represents the value if the condition is false. + +Macro arguments are presented in the *name* field (e.g. name = `mymacro(arg)`). + +While there may seem little point generating a macro from tablegen, doing this +allows the entire source of the header file to be generated from the tablegen +files, rather than requiring a mix of C source and tablegen. + +## Generation + +### API header +``` +./offload-tblgen -I <path-to-llvm>/offload/API <path-to-llvm>/offload/API/OffloadAPI.td --gen-api +``` +The comments in the generated header are in Doxygen format, although +generating documentation from them hasn't been implemented yet. + +The entirety of this header is generated by Tablegen, rather than having a predefined header file that includes one or more `.inc` files. This is because this header is expected to be part of the installation and distributed to end-users, so should be self-contained. + +### Entry Points +``` +./offload-tblgen -I <path-to-llvm>/offload/API <path-to-llvm>/offload/API/OffloadAPI.td --gen-entry-points +``` +These functions form the actual Offload interface, and are wrappers over the +functions that contain the actual implementation (see +'Adding a new entry point'). + +They implement automatically generated validation checks, and tracing of +function calls with arguments and results. The tracing can be enabled with the +`OFFLOAD_TRACE` environment variable. + +### Implementation function declarations +``` +./offload-tblgen -I <path-to-llvm>/offload/API <path-to-llvm>/offload/API/OffloadAPI.td --gen-impl-func-decls +``` +Generates declarations of the implementation of functions of every entry point +in the API, e.g. `offloadDeviceFoo_impl` for `offloadDeviceFoo`. + +### Print header +``` +./offload-tblgen -I <path-to-llvm>/offload/API <path-to-llvm>/offload/API/OffloadAPI.td --gen-print-header +``` +This header contains `std::ostream &operator<<(std::ostream&)` definitions for +various API objects, including function parameters. + +As with the API header, it is expected that this header is part of the installed +package, so it is entirely generated by Tablegen. + +For ease of implementation, and since it is not strictly part of the API, this +is a C++ header file. If a C version is desirable it could be added. + +### Future Tablegen backends +`RecordTypes.hpp` contains wrappers for all of the API object types, which will +allow more backends to be easily added in future. + +## Adding to the API + +A new object can be added to the API by adding to one of the existing `.td` +files. It is also possible to add a new tablegen file to the API by adding it +to the includes in `OffloadAPI.td`. When the offload target is rebuilt, the +new definition will be included in the generated files. + +### Adding a new entry point + +When a new entry point is added (e.g. `offloadDeviceFoo`), the actual entry +point is automatically generated, which contains validation and tracing code. +It expects an implementation function (`offloadDeviceFoo_impl`) to be defined, +which it will call into. The definition of this implementation function should +be added to `src/offload_impl.cpp` |
