blob: d8635e012c5eb95296bbc8996ced6ee9ec8c8e1f [file] [log] [blame]
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef IREE_VM_MODULE_H_
#define IREE_VM_MODULE_H_
#include <stdint.h>
#include "iree/base/alignment.h"
#include "iree/base/api.h"
#include "iree/base/internal/atomics.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
typedef struct iree_vm_module iree_vm_module_t;
typedef struct iree_vm_stack iree_vm_stack_t;
typedef struct iree_vm_stack_frame iree_vm_stack_frame_t;
// An opaque offset into a source map that a source resolver can calculate.
// Do not assume that iree_vm_source_offset_t+1 means the next byte offset as
// backends are free to treat these as everything from pointers to machine code
// to hash codes.
typedef int64_t iree_vm_source_offset_t;
// A key-value pair of module/function reflection information.
typedef struct {
iree_string_view_t key;
iree_string_view_t value;
} iree_vm_reflection_attr_t;
// A variable-length list of registers.
//
// This structure is an overlay for the bytecode that is serialized in a
// matching format, though it can be stack allocated as needed.
//
// TODO(benvanik): this should be made private to the bytecode module, but is
// used for toll-free variadic argument lists here. We could just define an
// identical structure (and static_assert) to at least rename it to something
// sensible (iree_vm_segment_size_list_t).
typedef struct {
uint16_t size;
uint16_t registers[];
} iree_vm_register_list_t;
static_assert(iree_alignof(iree_vm_register_list_t) == 2,
"expecting byte alignment (to avoid padding)");
static_assert(offsetof(iree_vm_register_list_t, registers) == 2,
"expect no padding in the struct");
// Describes the type of a function reference.
enum iree_vm_function_linkage_e {
// Function is internal to the module and may not be reflectable.
IREE_VM_FUNCTION_LINKAGE_INTERNAL = 0,
// Function is an import from another module.
IREE_VM_FUNCTION_LINKAGE_IMPORT = 1,
// Function is an export from the module.
IREE_VM_FUNCTION_LINKAGE_EXPORT = 2,
// TODO(#1979): add linkage types for well-known functions like __init.
};
typedef uint16_t iree_vm_function_linkage_t;
// A function reference that can be used with the iree_vm_function_* methods.
// These should be treated as opaque and the accessor functions should be used
// instead.
//
// The register counts specify required internal storage used for VM for stack
// frame management and debugging. They must at least be able to contain all
// entry arguments for the function. The counts may be omitted if the function
// will not be referenced by a VM stack frame.
typedef struct {
// Module the function is contained within.
iree_vm_module_t* module;
// Linkage of the function. Note that IREE_VM_FUNCTION_LINKAGE_INTERNAL
// functions may be missing reflection information.
iree_vm_function_linkage_t linkage;
// Ordinal within the module in the linkage scope.
uint16_t ordinal;
} iree_vm_function_t;
static_assert(sizeof(iree_vm_function_t) <= 2 * sizeof(void*),
"Must remain small as stored on the stack");
// Returns true if the |function| is null (didn't exist, etc).
static inline bool iree_vm_function_is_null(iree_vm_function_t function) {
return function.module == NULL;
}
// Describes the expected calling convention and arguments/results of a
// function.
typedef struct {
// The VM calling convention declaration used to marshal arguments and
// results into and out of the function.
// Optional for imports and internal functions but required for exports.
//
// Format:
// - '0': version 0 prefix
// - Zero or more arguments:
// - 'i': int32_t integer (i32)
// - 'I': int64_t integer (i64)
// - 'r': ref-counted type pointer (!vm.ref<?>)
// - 'C' ... 'D': variadic list of flattened tuples of a specified type
// - EOL or '_'
// - Zero or more results:
// - 'i' or 'I'
// - 'r'
//
// Examples:
// `0` or `0_`: () -> ()
// `0i` or `0i_`: (i32) -> ()
// `0iiCiiD_i`: (i32, i32, tuple<i32, i32>...) -> i32
// `0irCirD_r`: (i32, !vm.ref<?>, tuple<i32, !vm.ref<?>>) -> !vm.ref<?>
//
// Users of this field must verify the version prefix in the first byte before
// using the declaration.
iree_string_view_t calling_convention;
} iree_vm_function_signature_t;
// Describes the imports, exports, and capabilities of a module.
typedef struct {
// Total number of imported functions.
iree_host_size_t import_function_count;
// Total number of exported functions.
iree_host_size_t export_function_count;
// Total number of internal functions, if debugging info is present and they
// can be queried.
iree_host_size_t internal_function_count;
} iree_vm_module_signature_t;
// Internal storage for the module state.
// Thread-compatible; it's expected that only one thread at a time is executing
// VM functions and accessing this state.
typedef struct iree_vm_module_state iree_vm_module_state_t;
// Function call data.
//
// Arguments and results are encoded following a standard format shared across
// all module types. This allows implementations that have different storage
// types (such as physical machine registers vs. virtual registers) to use the
// same cross-module calling convention.
//
// Callees can assume that callers have properly allocated and setup the
// argument and result buffers and need not verify them. This works only because
// the calling convention format is directly queried from the callee module.
//
// Encoding:
// - each int is encoded as a 4-byte aligned value
// - each ref is encoded as a 4-byte aligned iree_vm_ref_t value
// - variadic tuples are encoded as a 4-byte count prefix and the tuple values
//
// For example, (i32, tuple<!vm.ref<?>, i32>..., i32) is encoded as:
// 4b: i32
// 4b: tuple count
// repeated:
// 8b-16b: iree_vm_ref_t
// 4b: i32
// 4b: i32
//
// Example sequence:
// 1. ModuleA wants to call SomeFunction from ModuleB
// 2. ModuleA imports SomeFunction from ModuleB and gets its
// iree_vm_function_signature_t during import resolution
// 3. ModuleA checks that it understands/supports that calling convention
// with error handling if needed (e.g. if ModuleB is newer and uses a newer
// version that ModuleA wasn't compiled knowing about, or ModuleB is ancient
// and uses a deprecated version that ModuleA has already dropped)
// 4. ModuleA prepares argument and result buffers according to the calling
// convention defined by ModuleB and calls SomeFunction
// 5. ModuleB handles the call, trusting that the input and output buffers are
// as expected
//
// NOTE: we could switch to using libffi, but I didn't want to require that for
// all uses and didn't want to enable the issues that can arise when crossing
// device boundaries. With what we have here we can rather easily serialize the
// argument/result buffers and map them between independent address spaces.
// Instead, implementing a native_module-alike of libffi_module would be a
// better layering for callee modules.
typedef struct {
// Function to call.
iree_vm_function_t function;
// Argument buffer in the format described above.
// This is only read on beginning the function and need not live beyond that.
//
// Refs contained will be moved into the target function or released if not
// needed. Callers must ensure they move or retain arguments when populating
// the arguments buffer.
iree_byte_span_t arguments;
// Storage for the result buffer; assumed undefined and then populated with
// data in a format described above. This is required for both the beginning
// of function invocation as well as each resume (as any may actually return
// control flow).
//
// Refs contained will be retained in the results buffer and callers must
// either move or release them upon return from the call.
iree_byte_span_t results;
} iree_vm_function_call_t;
#define IREE_VM_CCONV_TYPE_VOID 'v'
#define IREE_VM_CCONV_TYPE_INT32 'i'
#define IREE_VM_CCONV_TYPE_INT64 'I'
#define IREE_VM_CCONV_TYPE_REF 'r'
#define IREE_VM_CCONV_TYPE_SPAN_START 'C'
#define IREE_VM_CCONV_TYPE_SPAN_END 'D'
// Returns the arguments and results fragments from the function signature.
// Either may be empty if they have no values.
//
// Example:
// `` -> arguments = ``, results = ``
// `0` -> arguments = ``, results = ``
// `0v` -> arguments = ``, results = ``
// `0ri` -> arguments = `ri`, results = ``
// `0_ir` -> arguments = ``, results = `ir`
// `0v_ir` -> arguments = ``, results = `ir`
// `0iCiD_rr` -> arguments = `iCiD`, results = `rr`
IREE_API_EXPORT iree_status_t IREE_API_CALL
iree_vm_function_call_get_cconv_fragments(
const iree_vm_function_signature_t* signature,
iree_string_view_t* out_arguments, iree_string_view_t* out_results);
// Returns true if the given cconv contains one or more variadic types.
IREE_API_EXPORT bool IREE_API_CALL
iree_vm_function_call_is_variadic_cconv(iree_string_view_t cconv);
// Returns the required size, in bytes, to store the data in the given cconv
// fragment (like `iICriDr`).
//
// The provided |segment_size_list| is used for variadic arguments/results. Each
// entry represents one of the top level arguments with spans being flattened.
IREE_API_EXPORT iree_status_t IREE_API_CALL
iree_vm_function_call_compute_cconv_fragment_size(
iree_string_view_t cconv_fragment,
const iree_vm_register_list_t* segment_size_list,
iree_host_size_t* out_required_size);
// Releases any retained refs within the call (either arguments or results).
// This needs only be called if a call fails as implementations are required to
// clean up the arguments as they are marshaled in and callers are required to
// clean up the results as they are marshaled out.
IREE_API_EXPORT void IREE_API_CALL
iree_vm_function_call_release(iree_vm_function_call_t* call,
const iree_vm_function_signature_t* signature);
// Results of an iree_vm_module_execute request.
typedef struct {
// TODO(benvanik): yield information.
// Yield modes:
// - yield (yield instruction)
// - await (with 1+ wait handles)
// - break
int reserved;
} iree_vm_execution_result_t;
// Defines an interface that can be used to reflect and execute functions on a
// module.
//
// Module implementations must be thread-safe as lookups and executions may
// occur in any order from any thread.
// TODO(benvanik): version this interface.
typedef struct iree_vm_module {
IREE_API_UNSTABLE
void* self;
iree_atomic_ref_count_t ref_count;
// Destroys |self| when all references to the module have been released.
void(IREE_API_PTR* destroy)(void* self);
// Returns the name of the module (used during resolution).
iree_string_view_t(IREE_API_PTR* name)(void* self);
// Returns the reflected signature of the module.
iree_vm_module_signature_t(IREE_API_PTR* signature)(void* self);
// Gets one or more pieces of function information:
// - |out_function| set to the function reference.
// - |out_name| set to the function name.
// - |out_signature| set to the function signature.
iree_status_t(IREE_API_PTR* get_function)(
void* self, iree_vm_function_linkage_t linkage, iree_host_size_t ordinal,
iree_vm_function_t* out_function, iree_string_view_t* out_name,
iree_vm_function_signature_t* out_signature);
// Looks up a function with the given name and linkage in the module.
// This may perform a linear scan and results should be cached.
iree_status_t(IREE_API_PTR* lookup_function)(
void* self, iree_vm_function_linkage_t linkage, iree_string_view_t name,
iree_vm_function_t* out_function);
// Allocates module state data.
iree_status_t(IREE_API_PTR* alloc_state)(
void* self, iree_allocator_t allocator,
iree_vm_module_state_t** out_module_state);
// Frees module state data.
void(IREE_API_PTR* free_state)(void* self,
iree_vm_module_state_t* module_state);
// Resolves the import with the given ordinal to |function|.
// The function is guaranteed to remain valid for the lifetime of the module
// state.
iree_status_t(IREE_API_PTR* resolve_import)(
void* self, iree_vm_module_state_t* module_state,
iree_host_size_t ordinal, const iree_vm_function_t* function,
const iree_vm_function_signature_t* signature);
// Begins a function call with the given |call| arguments.
// Execution may yield in the case of asynchronous code and require one or
// more calls to the resume method to complete.
iree_status_t(IREE_API_PTR* begin_call)(
void* self, iree_vm_stack_t* stack, const iree_vm_function_call_t* call,
iree_vm_execution_result_t* out_result);
// Resumes execution of a previously-yielded call.
iree_status_t(IREE_API_PTR* resume_call)(
void* self, iree_vm_stack_t* stack,
iree_vm_execution_result_t* out_result);
// TODO(benvanik): move this/refactor.
// Gets a reflection attribute for a function by index.
// The returned key and value strings are guaranteed valid for the life
// of the module. Note that not all modules and functions have reflection
// attributes.
// Returns IREE_STATUS_NOT_FOUND if index >= the number of attributes for
// the function.
// See: docs/design_docs/function_abi.md
iree_status_t(IREE_API_PTR* get_function_reflection_attr)(
void* self, iree_vm_function_linkage_t linkage, iree_host_size_t ordinal,
iree_host_size_t index, iree_string_view_t* key,
iree_string_view_t* value);
} iree_vm_module_t;
// Initializes the interface of a module handle.
// This should be called by module implementations after they allocate
// themselves to properly initialize the module interface prior to populating
// interface function pointers. This ensures that version adaptation can be
// performed by the library as needed.
// TODO(benvanik): version/module size.
IREE_API_EXPORT iree_status_t IREE_API_CALL
iree_vm_module_initialize(iree_vm_module_t* module, void* self);
// Retains the given |module| for the caller.
IREE_API_EXPORT void IREE_API_CALL
iree_vm_module_retain(iree_vm_module_t* module);
// Releases the given |module| from the caller.
IREE_API_EXPORT void IREE_API_CALL
iree_vm_module_release(iree_vm_module_t* module);
// Returns the name of the module (used during resolution).
IREE_API_EXPORT iree_string_view_t IREE_API_CALL
iree_vm_module_name(const iree_vm_module_t* module);
// Returns the signature of the module describing the contents.
IREE_API_EXPORT iree_vm_module_signature_t IREE_API_CALL
iree_vm_module_signature(const iree_vm_module_t* module);
// Looks up a function with the given name and linkage in the |module|.
// This may perform a linear scan and results should be cached.
IREE_API_EXPORT iree_status_t IREE_API_CALL
iree_vm_module_lookup_function_by_name(const iree_vm_module_t* module,
iree_vm_function_linkage_t linkage,
iree_string_view_t name,
iree_vm_function_t* out_function);
// Looks up a function with the given ordinal and linkage in the |module|.
// If |linkage_name| is not null, then it will be populated with the name
// of the linkage record (i.e. the actual exported name vs the internal
// name which would be returned in a subsequent call to iree_vm_function_name).
// TODO(laurenzo): Remove out_linkage_name in favore of a LINKAGE_PUBLIC (with
// the name that you'd get from a function_name call on that being the public
// name).
IREE_API_EXPORT iree_status_t IREE_API_CALL
iree_vm_module_lookup_function_by_ordinal(const iree_vm_module_t* module,
iree_vm_function_linkage_t linkage,
iree_host_size_t ordinal,
iree_vm_function_t* out_function,
iree_string_view_t* out_linkage_name);
// Returns the name of the given function or empty string if not available.
IREE_API_EXPORT iree_string_view_t IREE_API_CALL
iree_vm_function_name(const iree_vm_function_t* function);
// Returns the signature of the function if reflection metadata is available.
IREE_API_EXPORT iree_vm_function_signature_t IREE_API_CALL
iree_vm_function_signature(const iree_vm_function_t* function);
// Returns a value for the given reflection attribute |key|, if found.
// Returns the empty string if the reflection data in general or the specific
// key is not found.
//
// See: docs/design_docs/function_abi.md for documentation on the ABI.
IREE_API_EXPORT iree_string_view_t IREE_API_CALL
iree_vm_function_reflection_attr(const iree_vm_function_t* function,
iree_string_view_t key);
// TODO(#1979): remove this and use iree_vm_function_reflection_attr.
// Gets a reflection attribute for a function by index.
// The returned key and value strings are guaranteed valid for the life
// of the module. Note that not all modules and functions have reflection
// attributes.
// Returns IREE_STATUS_NOT_FOUND if index >= the number of attributes for
// the function.
// See: docs/design_docs/function_abi.md
IREE_API_EXPORT iree_status_t IREE_API_CALL
iree_vm_get_function_reflection_attr(iree_vm_function_t function,
iree_host_size_t index,
iree_string_view_t* key,
iree_string_view_t* value);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // IREE_VM_MODULE_H_