This PR changes olLaunchKernel to accept an array of pointers to
arguments:
```
void *ArgPtrs[] = {&A, &B, &C};
size_t ArgSizes[] = {sizeof(A), sizeof(B), sizeof(C)};
olLaunchKernel(Queue, Device, Kernel, &LaunchArgs, std::size(ArgPtrs), ArgPtrs, ArgSizes);
```
The newly proposed interface is implementable by existing and
anticipated
backends, is familiar to CUDA programmers, eliminates the extraneous
construction of a contiguous arguments buffer, replacing it with
constructing
an array of pointers, sidesteps the alignment requirements, does not
require reading program image metadata where it's impractical, and
enables
a compliant SYCL implementation to be built on top of it.
The ArgSizes array is required to support OpenCL, which does not have
native support for launching a kernel with an argument pointer array, or
a reliable way of retrieving argument sizes for a kernel.
## Mapping the proposed API to backends
CUDA and Level-Zero both support accepting an array of pointers to
kernel arguments, through `cuLaunchKernel`
(https://docs.nvidia.com/cuda/cuda-driver-api/group__CUDA__EXEC.html#group__CUDA__EXEC_1gb8f3dc3031b40da29d5f9a7139e52e15)
and `zeCommandListAppendLaunchKernelWithArguments`
(https://oneapi-src.github.io/level-zero-spec/level-zero/latest/core/api.html#zecommandlistappendlaunchkernelwitharguments)
respectively.
For OpenCL, which requires the kernel arguments to be set separately
from the kernel launch, a potential implementation can extract the
number of arguments from the OpenCL API, and then iterate over the
argument pointer and size arrays:
```
cl_uint num_args = 0;
cl_int err = clGetKernelInfo(kernel, CL_KERNEL_NUM_ARGS,
sizeof(num_args), &num_args, NULL);
for (cl_uint i = 0; i < num_args; ++i) {
clSetKernelArg(kernel, i, ArgSizes[i], ArgPtrs[i]);
}
```
The AMDGPU plugin needs to construct a contiguous buffer using the array
of argument pointers. To do this conversion, we need to have offsets
at which to place the arguments. Here, luckily, as mentioned before,
the AMD plugin already reads kernel metadata from the program image.
The implementation simply retrieves size and offset for each argument
from the kernel image, and then uses it to populate a buffer.
115 lines
5.8 KiB
TableGen
115 lines
5.8 KiB
TableGen
//===-- Kernel.td - Kernel 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 kernels
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
def ol_kernel_launch_size_args_t : Struct {
|
|
let desc = "Size-related arguments for a kernel launch.";
|
|
let members = [
|
|
StructMember<"size_t", "Dimensions", "Number of work dimensions">,
|
|
StructMember<"struct ol_dimensions_t", "NumGroups", "Number of work groups in each dimension">,
|
|
StructMember<"struct ol_dimensions_t", "GroupSize", "Size of a work group in each dimension">,
|
|
StructMember<"size_t", "DynSharedMemory", "Size of dynamic shared memory in bytes.">
|
|
];
|
|
}
|
|
|
|
def OL_KERNEL_LAUNCH_PROP_END : Macro {
|
|
let desc = "last element of the ol_kernel_launch_prop_t array";
|
|
let value = "{OL_KERNEL_LAUNCH_PROP_TYPE_NONE, NULL}";
|
|
}
|
|
|
|
def ol_kernel_launch_prop_type_t : Enum {
|
|
let desc = "Defines structure type";
|
|
let is_typed = 1;
|
|
let etors =
|
|
[TaggedEtor<"none", "void *", "Used for null terminating property array">,
|
|
TaggedEtor<"is_cooperative ", "bool *", "Cooperative kernel launch">];
|
|
}
|
|
|
|
def ol_kernel_launch_prop_t : Struct {
|
|
let desc = "Optional properties for kernel launch.";
|
|
let members = [StructMember<"ol_kernel_launch_prop_type_t", "type",
|
|
"Type of the data field">,
|
|
StructMember<"void *", "data",
|
|
"Pointer to property-specific data.">];
|
|
}
|
|
|
|
def olLaunchKernel : Function {
|
|
let desc = "Enqueue a kernel launch with arguments specified as an array of pointers and sizes.";
|
|
let details = [
|
|
"If a queue is not specified, kernel execution happens synchronously",
|
|
"Each element of ArgPtrs points to the value of the corresponding kernel argument",
|
|
"Each element of ArgSizes specifies the size in bytes of the corresponding argument",
|
|
"NumArgs must match the number of arguments expected by the kernel",
|
|
"ArgPtrs and ArgSizes must both be NULL (when NumArgs == 0) or both be non-NULL"
|
|
];
|
|
let params = [
|
|
Param<"ol_queue_handle_t", "Queue", "handle of the queue", PARAM_IN_OPTIONAL>,
|
|
Param<"ol_device_handle_t", "Device", "handle of the device to execute on", PARAM_IN>,
|
|
Param<"ol_symbol_handle_t", "Kernel", "handle of the kernel", PARAM_IN>,
|
|
Param<"const ol_kernel_launch_size_args_t*", "LaunchSizeArgs", "pointer to the struct containing launch size parameters", PARAM_IN>,
|
|
Param<"const ol_kernel_launch_prop_t *", "Properties", "Array of optional properties, last element must be OL_KERNEL_LAUNCH_PROP_END", PARAM_IN_OPTIONAL>,
|
|
Param<"size_t", "NumArgs", "number of kernel arguments", PARAM_IN>,
|
|
Param<"void**", "ArgPtrs", "array of pointers, each pointing to a kernel argument value", PARAM_IN_OPTIONAL>,
|
|
Param<"const size_t*", "ArgSizes", "array of kernel argument sizes", PARAM_IN_OPTIONAL>,
|
|
];
|
|
let returns = [
|
|
Return<"OL_ERRC_INVALID_ARGUMENT", [
|
|
"`(ArgPtrs == NULL) != (ArgSizes == NULL)`",
|
|
"`NumArgs > 0 && (ArgPtrs == NULL || ArgSizes == NULL)`",
|
|
"NumArgs does not match the number of arguments expected by the kernel",
|
|
]>,
|
|
Return<"OL_ERRC_INVALID_DEVICE", ["If Queue is non-null but does not belong to Device"]>,
|
|
Return<"OL_ERRC_SYMBOL_KIND", ["The provided symbol is not a kernel"]>,
|
|
];
|
|
}
|
|
|
|
def olCalculateOptimalOccupancy : Function {
|
|
let desc = "Given dynamic memory size, query the device for a workgroup size that will result in optimal occupancy.";
|
|
let details = [
|
|
"For most devices, this will be the largest workgroup size that will result in all work items fitting on the device at once.",
|
|
];
|
|
let params = [
|
|
Param<"ol_device_handle_t", "Device", "device intended to run the kernel", PARAM_IN>,
|
|
Param<"ol_symbol_handle_t", "Kernel", "handle of the kernel", PARAM_IN>,
|
|
Param<"size_t", "SharedMemory", "dynamic shared memory required per work item in bytes", PARAM_IN>,
|
|
Param<"size_t*", "GroupSize", "optimal block size", PARAM_OUT>
|
|
];
|
|
let returns = [
|
|
Return<"OL_ERRC_SYMBOL_KIND", ["The provided symbol is not a kernel"]>,
|
|
Return<"OL_ERRC_UNSUPPORTED", ["The backend cannot provide this information"]>,
|
|
];
|
|
}
|
|
|
|
def olGetKernelMaxCooperativeGroupCount : Function {
|
|
let desc = "Query the maximum number of work groups that can be launched "
|
|
"cooperatively for a kernel.";
|
|
let details =
|
|
["This function returns the maximum number of work groups that can "
|
|
"participate in a cooperative launch for the given kernel.",
|
|
"The maximum count depends on the work group size and dynamic shared "
|
|
"memory usage.",
|
|
];
|
|
let params = [Param<"ol_device_handle_t", "Device",
|
|
"device intended to run the kernel", PARAM_IN>,
|
|
Param<"ol_symbol_handle_t", "Kernel", "handle of the kernel",
|
|
PARAM_IN>,
|
|
Param<"const ol_kernel_launch_size_args_t*", "LaunchSizeArgs",
|
|
"launch size parameters", PARAM_IN>,
|
|
Param<"uint32_t*", "MaxGroupCount",
|
|
"maximum number of cooperative groups", PARAM_OUT>];
|
|
let returns =
|
|
[Return<"OL_ERRC_SYMBOL_KIND", ["The provided symbol is not a kernel"]>,
|
|
Return<
|
|
"OL_ERRC_UNSUPPORTED", ["Cooperative launch is not supported or "
|
|
"backend cannot provide this information"]>,
|
|
];
|
|
}
|