blob: ab5b4cfd6d3e145f00e410fc413f3a42b8ae1478 [file]
// Copyright 2019 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
// See iree/base/api.h for documentation on the API conventions used.
#ifndef IREE_VM_INVOCATION_H_
#define IREE_VM_INVOCATION_H_
#include "iree/base/api.h"
#include "iree/vm/context.h"
#include "iree/vm/list.h"
#include "iree/vm/loop.h"
#include "iree/vm/module.h"
#include "iree/vm/ref.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
typedef struct iree_vm_invocation_t iree_vm_invocation_t;
typedef struct iree_vm_invocation_policy_t iree_vm_invocation_policy_t;
//===----------------------------------------------------------------------===//
// Synchronous invocation
//===----------------------------------------------------------------------===//
// Synchronously invokes a function in the VM.
// The function will be run to completion and may block on external resources.
// If more control is required or callers want to have multiple invocations
// in-flight then iree_vm_invocation_t should be used.
//
// |policy| is used to schedule the invocation relative to other pending or
// in-flight invocations. It may be omitted to leave the behavior up to the
// implementation.
//
// |inputs| is used to pass values and objects into the target function and must
// match the signature defined by the compiled function. List ownership remains
// with the caller.
//
// |outputs| is populated after the function completes execution with the
// output values and objects of the function. List ownership remains with the
// caller.
IREE_API_EXPORT iree_status_t iree_vm_invoke(
iree_vm_context_t* context, iree_vm_function_t function,
iree_vm_invocation_flags_t flags, const iree_vm_invocation_policy_t* policy,
const iree_vm_list_t* inputs, iree_vm_list_t* outputs,
iree_allocator_t host_allocator);
//===----------------------------------------------------------------------===//
// Asynchronous invocation
//===----------------------------------------------------------------------===//
// Invocation storage for passing state across invocation stages.
// This is intended to be embedded within higher-level invocation objects or
// used directly on the stack. Users should prefer iree_vm_invoke (which wraps
// up this sequence) or iree_vm_invocation_t. Deep integrations into existing
// schedulers can use this to perform fine-grained interleaving of invocations
// with external workloads.
//
// Usage (ala iree_vm_invoke):
// iree_vm_invoke_state_t state;
// iree_vm_wait_result_t wait_result;
// s = iree_vm_begin_invoke(&state, ..., function, ..., inputs, ...);
// while (iree_status_is_deferred(s)) {
// if (wait_frame) iree_vm_wait_invoke(&state, ...);
// s = iree_vm_resume_invoke(&state, outputs);
// }
// iree_vm_end_invoke(&state, outputs, &status);
//
// iree_vm_*_invoke calls return the status of the specific invoke operation and
// not the result of the invocation itself. A failure in one of the calls is
// generally non-recoverable and iree_vm_abort_invoke must be used to clean up
// resources. Otherwise if all return OK iree_vm_end_invoke must be called to
// retrieve the invocation result.
//
// Thread-compatible: steps in the invocation sequence may be made from any
// thread so long as none are made concurrently (this structure is not
// thread-safe).
typedef struct iree_vm_invoke_state_t {
// Retains the context the invocation is running within.
iree_vm_context_t* context;
// Status of the invocation. Returned by iree_vm_end_invoke.
iree_status_t status;
// Parsed calling convention results string for marshaling.
iree_string_view_t cconv_results;
// Pointer into stack storage containing the results.
iree_byte_span_t results;
// VM stack used during the invocation. Will retain required resources
// across invocation stages.
iree_vm_stack_t* stack;
// Inlined stack storage. If the stack grows larger than this amount
// additional storage will be allocated automatically.
uint8_t stack_storage[IREE_VM_STACK_DEFAULT_SIZE];
} iree_vm_invoke_state_t;
// Begins an invocation of |function| in |context| with the given |inputs|.
//
// |state| is caller-provided uninitialized storage for the stack and other
// invocation resources and must always be passed to iree_vm_end_invoke to
// clean up resources from the sequence.
//
// |policy| is used to schedule the invocation relative to other pending or
// in-flight invocations. It may be omitted to leave the behavior up to the
// implementation.
//
// |inputs| is used to pass values and objects into the target function and must
// match the signature defined by the target |function|. List contents are
// captured and the caller can reuse it immediately upon return.
//
// Returns OK if the invocation began regardless of the invocation result.
// When OK iree_vm_end_invoke is used to retrieve the invocation result.
// If IREE_STATUS_DEFERRED is returned one of iree_vm_resume_invoke or
// iree_vm_wait_invoke (or other wait frame handling) must be performed by the
// caller.
IREE_API_EXPORT iree_status_t iree_vm_begin_invoke(
iree_vm_invoke_state_t* state, iree_vm_context_t* context,
iree_vm_function_t function, iree_vm_invocation_flags_t flags,
const iree_vm_invocation_policy_t* policy, const iree_vm_list_t* inputs,
iree_allocator_t host_allocator);
// Resumes an invocation previously began with iree_vm_begin_invoke.
// Only valid to call if a prior call to iree_vm_begin_invoke or
// iree_vm_resume_invoke returned IREE_STATUS_DEFERRED.
//
// Returns OK if the invocation resumed regardless of the invocation result.
// When OK iree_vm_end_invoke is used to retrieve the invocation result.
// If IREE_STATUS_DEFERRED is returned one of iree_vm_resume_invoke or
// iree_vm_wait_invoke (or other wait frame handling) must be performed by the
// caller.
IREE_API_EXPORT iree_status_t
iree_vm_resume_invoke(iree_vm_invoke_state_t* state);
// Synchronously performs the operation specified by |wait_frame|.
// |deadline_ns| will be combined with the deadline specified in the wait frame
// to bound the wait operation. If successful the caller must use
// iree_vm_resume_invoke to allow the invocation to process the wait results.
//
// Hosting schedulers that can more efficiently perform the wait should do so,
// either synchronously or asynchronously. Wait frames are stored on the stack
// and will remain valid until iree_vm_resume_invoke is used to complete the
// wait.
//
// Returns OK if the wait operation was performed regardless of the wait result.
// Wait errors are stored on the |wait_frame| for processing after the
// invocation is resumed.
IREE_API_EXPORT iree_status_t
iree_vm_wait_invoke(iree_vm_invoke_state_t* state,
iree_vm_wait_frame_t* wait_frame, iree_time_t deadline_ns);
// Ends the invocation sequence and appends the returned values to |outputs|.
// Invocation resources in |state| will be released and upon return the storage
// can be reused for another invocation. Note that the context the invocation is
// operating in may be released and all pointers to stack resources will be
// invalid if they had not been previously retained by the caller.
//
// Returns OK if the invocation has ended and |state| has been cleaned up.
// The status of the invocation will be stored in |out_status| and ownership of
// the status handle is transferred to the caller.
IREE_API_EXPORT iree_status_t iree_vm_end_invoke(iree_vm_invoke_state_t* state,
iree_vm_list_t* outputs,
iree_status_t* out_status);
// Aborts an invocation sequence prior to it having successfully ended.
// Invocation resources in |state| will be released and upon return the storage
// can be reused for another invocation. Note that the context the invocation is
// operating in may be released and all pointers to stack resources will be
// invalid if they had not been previously retained by the caller.
//
// This is not to be used for signaling; user-level cancellation of invocations
// must happen via user-level mechanisms (such as HAL semaphores). The function
// invocation will not be notified of the abort and instead will just wink out
// of existence and comes with whatever implications that has for the program.
//
// Only use this if the invocation sequence failed and calling
// iree_vm_end_invoke would be invalid. If all iree_vm_*_invoke calls have
// succeeded then iree_vm_end_invoke must be used instead.
IREE_API_EXPORT void iree_vm_abort_invoke(iree_vm_invoke_state_t* state);
//===----------------------------------------------------------------------===//
// Loop-based asynchronous invocation
//===----------------------------------------------------------------------===//
typedef intptr_t iree_vm_invocation_id_t;
// Callback notifying the caller of an iree_vm_async_invoke that the invocation
// has completed. If successful then |outputs| will contain the results and
// ownership is transferred to the callee.
//
// |status| contains either the result of the invocation process if it failed or
// the result of the invocation itself if it succeeded.
//
// This is executed from within a |loop| context and must not block. Handlers
// are encouraged to defer all processing to another loop operation in order to
// reduce stack utilization.
typedef iree_status_t(IREE_API_PTR* iree_vm_async_invoke_callback_fn_t)(
void* user_data, iree_vm_loop_t loop, iree_status_t status,
iree_vm_list_t* outputs);
// Storage for iree_vm_async_invoke state.
// This is intended to be embedded within higher-level invocation objects or on
// the heap. When possible (and outputs are provided) the async invocation will
// not allocate any additional memory.
typedef struct iree_vm_async_invoke_state_t {
// Until we begin the invocation we don't need the state storage so we use
// that memory to store the parameters we'll need to begin. Kind of shady, but
// saves some ~128 bytes. Hooray C! 🥴
union {
struct {
// Retains the context the invocation is running within.
iree_vm_context_t* context;
// Target function.
iree_vm_function_t function;
// Flags controlling invocation behavior.
iree_vm_invocation_flags_t flags;
// TBD.
const iree_vm_invocation_policy_t* policy;
// Optional input storage list used to call the target function.
// Released after the function is entered.
iree_vm_list_t* inputs;
} begin_params;
// Base invoke state used to store the VM stack and resources.
iree_vm_invoke_state_t base;
};
// ID used for fiber tracing; either unique to the invocation or the context
// based on the context concurrency mode.
iree_vm_invocation_id_t invocation_id;
// TBD: deadline for when the invocation will be aborted.
iree_time_t deadline_ns;
// Allocator used for transient allocations required during invocation.
// If an arena it must remain valid for the duration of the invocation.
iree_allocator_t host_allocator;
// Optional preallocated output storage list that will receive the results.
iree_vm_list_t* outputs;
// Callback issued when the invocation completes.
iree_vm_async_invoke_callback_fn_t callback;
void* user_data;
} iree_vm_async_invoke_state_t;
// Asynchronously invokes |function| in |context| on the given |loop|.
// The call will return immediately with the invocation pending on the loop.
// Note that the |callback| may be issued before this function returns (such as
// when using an inline loop or one running on another thread).
//
// |state| is opaque storage that must remain live until the callback is issued.
// Callers should either allocate this from the heap to then free in the
// callback or embed the storage within their higher-level invocation data
// structures.
//
// |inputs| will be retained until no longer needed by the invocation and should
// generally not be modified until the callback is issued. |outputs| will be
// retained until the callback is made and must be released by the callback.
//
// The |callback| will receive |user_data| and is guaranteed to be called even
// if the invocation fails due to an internal error. If the loop is aborted due
// to a propagated scope failure the status passed to the callback will be
// IREE_STATUS_ABORTED and no new work can be scheduled to the provided loop.
//
// Multiple invocations to the same context are only allowed to overlap if the
// context was created with the IREE_VM_CONTEXT_FLAG_CONCURRENT flag set.
//
// Usage:
// iree_vm_async_invoke_state_t* state = malloc(...);
// iree_vm_async_invoke(
// loop, // loop to run in
// state, // state storage, must live until callback
// ...function..., // target function
// inputs, outputs, ..., // input values and output storage (if needed)
// callback, state); // user callback and user_data
// ...
// iree_status_t callback(
// void* user_data, // as passed to iree_vm_async_invoke
// iree_vm_loop_t loop, // if needing to schedule continuations
// iree_status_t status, // result of invocation
// iree_vm_list_t* outputs) { // retained output storage w/ result values
// if (iree_status_is_ok(status)) {
// // completed successfully! process outputs:
// do_something_with_outputs(outputs);
// }
// iree_vm_list_release(outputs); // must be released!
// free(user_data); // in this example the state storage
// return iree_ok_status(); // result propagated to loop scope
// }
IREE_API_EXPORT iree_status_t iree_vm_async_invoke(
iree_vm_loop_t loop, iree_vm_async_invoke_state_t* state,
iree_vm_context_t* context, iree_vm_function_t function,
iree_vm_invocation_flags_t flags, const iree_vm_invocation_policy_t* policy,
iree_vm_list_t* inputs, iree_vm_list_t* outputs,
iree_allocator_t host_allocator,
iree_vm_async_invoke_callback_fn_t callback, void* user_data);
//===----------------------------------------------------------------------===//
// Asynchronous stateful invocation
//===----------------------------------------------------------------------===//
// TODO(benvanik): document and implement.
IREE_API_EXPORT iree_status_t iree_vm_invocation_create(
iree_vm_context_t* context, iree_vm_function_t function,
iree_vm_invocation_flags_t flags, const iree_vm_invocation_policy_t* policy,
const iree_vm_list_t* inputs, iree_allocator_t allocator,
iree_vm_invocation_t** out_invocation);
// Retains the given |invocation| for the caller.
IREE_API_EXPORT iree_status_t
iree_vm_invocation_retain(iree_vm_invocation_t* invocation);
// Releases the given |invocation| from the caller.
IREE_API_EXPORT iree_status_t
iree_vm_invocation_release(iree_vm_invocation_t* invocation);
// Queries the completion status of the invocation.
// Returns one of the following:
// IREE_STATUS_OK: the invocation completed successfully.
// IREE_STATUS_DEFERRED: the invocation has not yet completed.
// IREE_STATUS_CANCELLED: the invocation was cancelled by the user.
// IREE_STATUS_ABORTED: the invocation was aborted by the executor.
// IREE_STATUS_*: an error occurred during invocation.
IREE_API_EXPORT iree_status_t
iree_vm_invocation_query_status(iree_vm_invocation_t* invocation);
// Returns a reference to the outputs of the invocation.
// The returned structure is valid for the lifetime of the invocation and
// callers must retain any refs they want to outlive the invocation once
// released.
//
// Returns NULL if the invocation has not yet completed or if it failed.
IREE_API_EXPORT const iree_vm_list_t* iree_vm_invocation_outputs(
iree_vm_invocation_t* invocation);
// Blocks the caller until the invocation completes (successfully or otherwise).
//
// Returns IREE_STATUS_DEADLINE_EXCEEDED if |deadline| elapses before the
// invocation completes and otherwise returns iree_vm_invocation_query_status.
IREE_API_EXPORT iree_status_t iree_vm_invocation_await(
iree_vm_invocation_t* invocation, iree_time_t deadline);
// Attempts to cancel the invocation if it is in-flight.
// Cancellation is not guaranteed to work and should be considered a hint.
// A no-op if the invocation has already completed.
IREE_API_EXPORT void iree_vm_invocation_cancel(
iree_vm_invocation_t* invocation);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
IREE_VM_DECLARE_CC_TYPE_ADAPTERS(iree_vm_invocation, iree_vm_invocation_t);
#endif // IREE_VM_INVOCATION_H_