| // Copyright 2023 The IREE Authors |
| // |
| // Licensed 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 |
| |
| // Demonstrates a standalone plugin exporting a single `simple_mul_workgroup` |
| // function. This models kernel libraries and custom intrinsics where there are |
| // only stateless functions. Standalone plugins can be compiled to portable ELFs |
| // that allow the same plugin file to be used on all platforms |
| // (linux/windows/mac/ bare-metal, etc) without the need to recompile or have |
| // platform-specific toolchains. As much as possible plugins should try to be in |
| // this form - when getting called in the CPU task system performing syscalls, |
| // blocking, or using TLS are either unsupported or extremely bad ideas. System |
| // linked plugins allow all those things but don't make it any safer. |
| // |
| // NOTE: in standalone mode the plugin cannot have side-effects: no allocations |
| // outside of the iree_hal_executable_plugin_allocator_t, no syscalls, no rwdata |
| // globals, and no TLS. |
| |
| // The only header required from IREE: |
| #include "iree/hal/local/executable_plugin.h" |
| |
| // `ret = lhs * rhs` |
| // |
| // Conforms to ABI: |
| // #hal.pipeline.layout<push_constants = 1, sets = [ |
| // <0, bindings = [ |
| // <0, storage_buffer, ReadOnly>, |
| // <1, storage_buffer, ReadOnly>, |
| // <2, storage_buffer> |
| // ]> |
| // ]> |
| // With a workgroup size of 64x1x1. |
| // |
| // |context| is whatever was set in out_fn_contexts. This could point to shared |
| // state or each import can have its own context (pointer into some JIT lookup |
| // table, etc). |
| // |
| // |params_ptr| points to a packed struct of all results followed by all args |
| // using native arch packing/alignment rules. Results should be set before |
| // returning. |
| // |
| // Expects a return of 0 on success and any other value indicates failure. |
| // Try not to fail! |
| static int simple_mul_workgroup(void* params_ptr, void* context, |
| void* reserved) { |
| typedef struct { |
| const float* restrict binding0; |
| size_t binding0_offset; |
| const float* restrict binding1; |
| size_t binding1_offset; |
| float* restrict binding2; |
| size_t binding2_offset; |
| size_t size; |
| size_t tid; |
| uint32_t processor_id; |
| const uint64_t* restrict processor_data; |
| } params_t; |
| const params_t* params = (const params_t*)params_ptr; |
| // The operation `iree_codegen.ukernel.generic` always operates |
| // on a slice of the inputs to produce a slice of the output, |
| // so the loop here just needs to iterate from `0` to `size`, |
| // where `size` is the size of the slice to be executed by this call. |
| for (size_t i = 0; i < params->size; ++i) { |
| // The operation `iree_codegen.ukernel.generic` takes a slice of |
| // the inputs and outputs as operands. So the `pointer` and `offset` |
| // passed into this function represent the starting location of |
| // where to read the data from for this invocation of the function. |
| params->binding2[params->binding2_offset + i] = |
| params->binding0[params->binding0_offset + i] * |
| params->binding1[params->binding2_offset + i]; |
| } |
| return 0; |
| } |
| |
| // Called once for each plugin load and paired with a future call to unload. |
| // We don't do anything special here as this plugin is meant to represent a |
| // pure/stateless kernel library. Even in standalone mode we could allocate |
| // using environment->host_allocator, set an out_self pointer, and parse |
| // parameters. |
| // |
| // If any state is required it should be allocated and stored in |out_self|. |
| // This self value will be passed to all future calls related to the particular |
| // instance. Note that there may be multiple instances of a plugin in any |
| // particular process and this must be thread-safe. |
| static iree_hal_executable_plugin_status_t standalone_plugin_load( |
| const iree_hal_executable_plugin_environment_v0_t* environment, |
| size_t param_count, const iree_hal_executable_plugin_string_pair_t* params, |
| void** out_self) { |
| *out_self = NULL; // no state in this plugin |
| return iree_hal_executable_plugin_ok_status(); |
| } |
| |
| // Called to free any plugin state allocated in load. |
| // In this sample it's a no-op as we don't have state. |
| static void standalone_plugin_unload(void* self) {} |
| |
| // Called to resolve one or more imports by symbol name. |
| // See the plugin API header for more information. Note that some of the |
| // functions may already be resolved and some may be optional. |
| static iree_hal_executable_plugin_status_t standalone_plugin_resolve( |
| void* self, const iree_hal_executable_plugin_resolve_params_v0_t* params, |
| iree_hal_executable_plugin_resolution_t* out_resolution) { |
| *out_resolution = 0; |
| bool any_required_not_found = false; |
| for (size_t i = 0; i < params->count; ++i) { |
| if (params->out_fn_ptrs[i]) continue; |
| const char* symbol_name = params->symbol_names[i]; |
| bool is_optional = |
| iree_hal_executable_plugin_import_is_optional(symbol_name); |
| if (is_optional) ++symbol_name; |
| if (iree_hal_executable_plugin_strcmp(symbol_name, |
| "simple_mul_workgroup") == 0) { |
| params->out_fn_ptrs[i] = simple_mul_workgroup; |
| params->out_fn_contexts[i] = NULL; // no context used, could be self |
| } else { |
| if (is_optional) { |
| *out_resolution |= |
| IREE_HAL_EXECUTABLE_PLUGIN_RESOLUTION_MISSING_OPTIONAL; |
| } else { |
| any_required_not_found = true; |
| } |
| } |
| } |
| return any_required_not_found |
| ? iree_hal_executable_plugin_status_from_code( |
| IREE_HAL_EXECUTABLE_PLUGIN_STATUS_NOT_FOUND) |
| : iree_hal_executable_plugin_ok_status(); |
| } |
| |
| // Exported on the shared library and used by the runtime to query the plugin |
| // interface. When statically linking the plugin this is just a function that |
| // can be called and can have any name to allow for multiple plugins. When |
| // dynamically linking the exported symbol must be exactly this with no C++ |
| // name mangling. |
| IREE_HAL_EXECUTABLE_PLUGIN_EXPORT const iree_hal_executable_plugin_header_t** |
| iree_hal_executable_plugin_query( |
| iree_hal_executable_plugin_version_t max_version, void* reserved) { |
| static const iree_hal_executable_plugin_header_t header = { |
| // Declares what library version is present: newer runtimes may support |
| // loading older plugins but newer plugins cannot load on older runtimes. |
| .version = IREE_HAL_EXECUTABLE_PLUGIN_VERSION_LATEST, |
| // Name and description are used for tracing/logging/diagnostics. |
| .name = "sample_standalone", |
| .description = |
| "standalone plugin sample " |
| "(custom_dispatch/cpu/plugin/standalone_plugin.c)", |
| // Standalone plugins must declare that they are standalone so that the |
| // runtime can verify support. |
| .features = IREE_HAL_EXECUTABLE_PLUGIN_FEATURE_STANDALONE, |
| // Standalone plugins don't support sanitizers. |
| .sanitizer = IREE_HAL_EXECUTABLE_PLUGIN_SANITIZER_NONE, |
| }; |
| static const iree_hal_executable_plugin_v0_t plugin = { |
| .header = &header, |
| .load = standalone_plugin_load, |
| .unload = standalone_plugin_unload, |
| .resolve = standalone_plugin_resolve, |
| }; |
| return max_version <= IREE_HAL_EXECUTABLE_PLUGIN_VERSION_LATEST |
| ? (const iree_hal_executable_plugin_header_t**)&plugin |
| : NULL; |
| } |