diff options
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 0000000..60c1b85 --- /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 0000000..8fd6cb5 --- /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 0000000..5b19d1d --- /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 0000000..30c0b71 --- /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 0000000..8a0c3c40 --- /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 0000000..03e70cf9 --- /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 0000000..38a0558 --- /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` |
