mirror of
https://github.com/intel/llvm.git
synced 2026-01-26 21:53:12 +08:00
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.
This commit is contained in:
@@ -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))
|
||||
|
||||
@@ -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")
|
||||
|
||||
40
offload/test/tools/offload-tblgen/default_returns.td
Normal file
40
offload/test/tools/offload-tblgen/default_returns.td
Normal file
@@ -0,0 +1,40 @@
|
||||
// RUN: %offload-tblgen -gen-api -I %S/../../../liboffload/API %s | %fcheck-generic --check-prefix=CHECK-API
|
||||
// RUN: %offload-tblgen -gen-entry-points -I %S/../../../liboffload/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 = "ol_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<"ol_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: OL_RESULT_SUCCESS
|
||||
// CHECK-API: OL_ERRC_INVALID_NULL_HANDLE
|
||||
// CHECK-API-NEXT: `NULL == ParamHandle`
|
||||
// CHECK-API: OL_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 OL_ERRC_INVALID_NULL_HANDLE;
|
||||
// CHECK-VALIDATION: if (NULL == ParamPointer)
|
||||
// CHECK-VALIDATION-NEXT: return OL_ERRC_INVALID_NULL_POINTER;
|
||||
// CHECK-VALIDATION-NOT: if (NULL == ParamPointerOpt)
|
||||
37
offload/test/tools/offload-tblgen/entry_points.td
Normal file
37
offload/test/tools/offload-tblgen/entry_points.td
Normal file
@@ -0,0 +1,37 @@
|
||||
// RUN: %offload-tblgen -gen-entry-points -I %S/../../../liboffload/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<"OL_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: ol_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;
|
||||
39
offload/test/tools/offload-tblgen/functions_basic.td
Normal file
39
offload/test/tools/offload-tblgen/functions_basic.td
Normal file
@@ -0,0 +1,39 @@
|
||||
// RUN: %offload-tblgen -gen-api -I %S/../../../liboffload/API %s | %fcheck-generic --check-prefix=CHECK-API
|
||||
// RUN: %offload-tblgen -gen-exports -I %S/../../../liboffload/API %s | %fcheck-generic --check-prefix=CHECK-EXPORTS
|
||||
// RUN: %offload-tblgen -gen-func-names -I %S/../../../liboffload/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<"OL_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: OL_ERRC_INVALID_VALUE
|
||||
// CHECK-API-NEXT: When a value is invalid
|
||||
|
||||
// CHECK-API: ol_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)
|
||||
26
offload/test/tools/offload-tblgen/functions_code_loc.td
Normal file
26
offload/test/tools/offload-tblgen/functions_code_loc.td
Normal file
@@ -0,0 +1,26 @@
|
||||
// RUN: %offload-tblgen -gen-api -I %S/../../../liboffload/API %s | %fcheck-generic --check-prefix=CHECK-API
|
||||
// RUN: %offload-tblgen -gen-exports -I %S/../../../liboffload/API %s | %fcheck-generic --check-prefix=CHECK-EXPORTS
|
||||
// RUN: %offload-tblgen -gen-func-names -I %S/../../../liboffload/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<"OL_ERRC_INVALID_VALUE", ["When a value is invalid"]>
|
||||
];
|
||||
}
|
||||
|
||||
// CHECK-API-DAG: ol_result_t{{.*}} FunctionA
|
||||
// CHECK-API-DAG: ol_result_t{{.*}} FunctionAWithCodeLoc
|
||||
// CHECK-EXPORTS: FunctionAWithCodeLoc
|
||||
// CHECK-FUNC-MACRO: OFFLOAD_FUNC(FunctionAWithCodeLoc)
|
||||
36
offload/test/tools/offload-tblgen/functions_ranged_param.td
Normal file
36
offload/test/tools/offload-tblgen/functions_ranged_param.td
Normal file
@@ -0,0 +1,36 @@
|
||||
// RUN: %offload-tblgen -gen-print-header -I %S/../../../liboffload/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 << "}";
|
||||
34
offload/test/tools/offload-tblgen/print_enum.td
Normal file
34
offload/test/tools/offload-tblgen/print_enum.td
Normal file
@@ -0,0 +1,34 @@
|
||||
// RUN: %offload-tblgen -gen-print-header -I %S/../../../liboffload/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;
|
||||
38
offload/test/tools/offload-tblgen/print_function.td
Normal file
38
offload/test/tools/offload-tblgen/print_function.td
Normal file
@@ -0,0 +1,38 @@
|
||||
// RUN: %offload-tblgen -gen-print-header -I %S/../../../liboffload/API %s | %fcheck-generic --check-prefix=CHECK-PRINT
|
||||
// RUN: %offload-tblgen -gen-api -I %S/../../../liboffload/API %s | %fcheck-generic --check-prefix=CHECK-API
|
||||
|
||||
// Check that print helpers are created for functions
|
||||
|
||||
include "APIDefs.td"
|
||||
|
||||
def : Handle {
|
||||
let name = "ol_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<"ol_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: ol_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);
|
||||
76
offload/test/tools/offload-tblgen/type_tagged_enum.td
Normal file
76
offload/test/tools/offload-tblgen/type_tagged_enum.td
Normal file
@@ -0,0 +1,76 @@
|
||||
// RUN: %offload-tblgen -gen-api -I %S/../../../liboffload/API %s | %fcheck-generic --check-prefix=CHECK-API
|
||||
// RUN: %offload-tblgen -gen-print-header -I %S/../../../liboffload/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 OL_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);
|
||||
Reference in New Issue
Block a user