blob: ef6c4ffef272e3ff1c0563f71852358ef09f9104 [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.
// API Versioning
// -----------------------------------------------------------------------------
//
// The C API is designed to be versioned such that breaking changes either in
// ABI (data types, struct sizes, etc) or signatures (function arguments change)
// will result in a bump of the IREE_API_VERSION_LATEST value.
//
// When linked in statically the runtime should never have a version conflict,
// however dynamic linking where the runtime is a shared object loaded at
// runtime (via dlopen/etc) must always verify the version is as expected.
//
// In the current experimental state of the runtime the API may break frequently
// and the version is pinned at 0.
//
// Example:
// void* library = dlopen("iree_rt.so", RTLD_LAZY | RTLD_LOCAL);
// iree_api_version_t actual_version;
// iree_status_t status = \
// ((PFN_iree_api_version_check)dlsym(library, "iree_api_version_check"))(
// IREE_API_VERSION_LATEST, &actual_version);
// IREE_CHECK_OK(status);
// dlclose(library);
//
// Object Ownership and Lifetime
// -----------------------------------------------------------------------------
//
// The API follows the CoreFoundation ownership policies:
// https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFMemoryMgmt/Concepts/Ownership.html
//
// These boil down to:
// * Objects returned from *_create or *_copy functions are owned by the caller
// and must be released when the caller no longer needs them.
// * Objects returned from accessors are not owned by the caller and must be
// retained by the caller if the object lifetime needs to be extended.
// * Objects passed to functions by argument may be retained by the callee if
// required.
//
// Example:
// iree_file_mapping_t* file_mapping;
// s = iree_file_mapping_open_read(..., &file_mapping);
// // file_mapping is now owned by this function.
// s = iree_file_mapping_some_call(file_mapping, ...);
// // Must release ownership when no longer required.
// s = iree_file_mapping_release(file_mapping);
//
// String Formatting
// -----------------------------------------------------------------------------
//
// Functions that produce variable-length strings follow a standard usage
// pattern with the arguments:
// `iree_host_size_t buffer_capacity`: total bytes including \0 available.
// `char* buffer`: optional buffer to write into.
// `iree_host_size_t* out_buffer_length`: required/actual length excluding \0.
//
// To query the size required for the output and allocate storage:
// iree_host_size_t required_length = 0;
// iree_format_xyz(/*buffer_capacity=*/0, /*buffer=*/NULL, &required_length);
// iree_host_size_t buffer_capacity = required_length + 1;
// char* buffer = iree_allocator_malloc(buffer_capacity);
// iree_host_size_t actual_length = 0;
// iree_format_xyz(buffer_capacity, buffer, &actual_length);
// ASSERT(required_length == actual_length);
//
// To handle fixed-length maximum strings (common):
// // Fails if the string is longer than 127 characters (127 + \0 >= 128).
// char buffer[128];
// IREE_RETURN_IF_ERROR(iree_format_xyz(sizeof(buffer), buffer, NULL));
//
// Try fixed-length and fallback to a dynamic allocation:
// char inline_buffer[128];
// iree_host_size_t required_length = 0;
// iree_status_t inline_status = iree_format_xyz(sizeof(inline_buffer),
// inline_buffer,
// &required_length);
// if (iree_status_is_out_of_range(inline_status)) {
// // Spilled inline_buffer, need to allocate required_length bytes and
// // try again.
// // ... see above for example ...
// } else if (iree_status_is_ok(inline_status)) {
// // Fit inside inline_buffer, required_length contains actual length.
// } else {
// return inline_status;
// }
#ifndef IREE_BASE_API_H_
#define IREE_BASE_API_H_
#include <assert.h>
#include <memory.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "iree/base/alignment.h"
#include "iree/base/attributes.h"
#include "iree/base/config.h"
#if defined(_WIN32)
// The safe malloca that may fall back to heap in the case of stack overflows:
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/malloca?view=vs-2019
// Because that gets really annoying to deal with during error handling we just
// go for _alloca which may generate SEH exceptions if we blow the stack.
#include <malloc.h>
#define iree_alloca(sz) _alloca(sz)
#else
#include <alloca.h>
#define iree_alloca(sz) alloca(sz)
#endif // _WIN32
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
//===----------------------------------------------------------------------===//
// Types and Enums
//===----------------------------------------------------------------------===//
// Returns the number of elements in an array as a compile-time constant, which
// can be used in defining new arrays. Fails at compile-time if |arr| is not a
// static array (such as if used on a pointer type).
//
// Example:
// uint8_t kConstantArray[512];
// assert(IREE_ARRAYSIZE(kConstantArray) == 512);
#define IREE_ARRAYSIZE(arr) (sizeof(arr) / sizeof(arr[0]))
#define iree_min(lhs, rhs) ((lhs) <= (rhs) ? (lhs) : (rhs))
#define iree_max(lhs, rhs) ((lhs) <= (rhs) ? (rhs) : (lhs))
// Returns true if any bit from |rhs| is set in |lhs|.
#define iree_any_bit_set(lhs, rhs) (((lhs) & (rhs)) != 0)
// Returns true iff all bits from |rhs| are set in |lhs|.
#define iree_all_bits_set(lhs, rhs) (((lhs) & (rhs)) == (rhs))
//===----------------------------------------------------------------------===//
// Byte buffers and memory utilities
//===----------------------------------------------------------------------===//
// A span of mutable bytes (ala std::span of uint8_t).
typedef struct {
uint8_t* data;
iree_host_size_t data_length;
} iree_byte_span_t;
static inline iree_byte_span_t iree_make_byte_span(
void* data, iree_host_size_t data_length) {
iree_byte_span_t v = {(uint8_t*)data, data_length};
return v;
}
// A span of constant bytes (ala std::span of const uint8_t).
typedef struct {
const uint8_t* data;
iree_host_size_t data_length;
} iree_const_byte_span_t;
static inline iree_const_byte_span_t iree_make_const_byte_span(
const void* data, iree_host_size_t data_length) {
iree_const_byte_span_t v = {(const uint8_t*)data, data_length};
return v;
}
//===----------------------------------------------------------------------===//
// iree_string_view_t (like std::string_view/absl::string_view)
//===----------------------------------------------------------------------===//
#define IREE_STRING_VIEW_NPOS SIZE_MAX
// A string view (ala std::string_view) into a non-NUL-terminated string.
typedef struct {
const char* data;
iree_host_size_t size;
} iree_string_view_t;
// Returns an empty string view ("").
static inline iree_string_view_t iree_string_view_empty() {
iree_string_view_t v = {0, 0};
return v;
}
// Returns true if the given string view is the empty string.
#define iree_string_view_is_empty(sv) (((sv).data == NULL) || ((sv).size == 0))
static inline iree_string_view_t iree_make_string_view(
const char* str, iree_host_size_t str_length) {
iree_string_view_t v = {str, str_length};
return v;
}
// Returns a string view initialized with a reference to the given
// NUL-terminated string literal.
static inline iree_string_view_t iree_make_cstring_view(const char* str) {
iree_string_view_t v = {str, strlen(str)};
return v;
}
#define iree_string_view_literal(str) \
{ .data = (str), .size = IREE_ARRAYSIZE(str) - 1 }
// Returns a string view initialized with the given cstring.
#define IREE_SV(cstr) iree_make_cstring_view(cstr)
// Returns true if the two strings are equal (compare == 0).
IREE_API_EXPORT bool iree_string_view_equal(iree_string_view_t lhs,
iree_string_view_t rhs);
// Like std::string::compare but with iree_string_view_t values.
IREE_API_EXPORT int iree_string_view_compare(iree_string_view_t lhs,
iree_string_view_t rhs);
// Finds the first occurrence of |c| in |value| starting at |pos|.
// Returns the found character position or IREE_STRING_VIEW_NPOS if not found.
IREE_API_EXPORT iree_host_size_t iree_string_view_find_char(
iree_string_view_t value, char c, iree_host_size_t pos);
// Returns the index of the first occurrence of one of the characters in |s| or
// -1 if none of the characters were found.
IREE_API_EXPORT iree_host_size_t iree_string_view_find_first_of(
iree_string_view_t value, iree_string_view_t s, iree_host_size_t pos);
// Returns the index of the last occurrence of one of the characters in |s| or
// -1 if none of the characters were found.
IREE_API_EXPORT iree_host_size_t iree_string_view_find_last_of(
iree_string_view_t value, iree_string_view_t s, iree_host_size_t pos);
// Returns true if the string starts with the given prefix.
IREE_API_EXPORT bool iree_string_view_starts_with(iree_string_view_t value,
iree_string_view_t prefix);
// Returns true if the string starts with the given suffix.
IREE_API_EXPORT bool iree_string_view_ends_with(iree_string_view_t value,
iree_string_view_t suffix);
// Removes the first |n| characters from the string view (not the data).
IREE_API_EXPORT iree_string_view_t
iree_string_view_remove_prefix(iree_string_view_t value, iree_host_size_t n);
// Removes the last |n| characters from the string view (not the data).
IREE_API_EXPORT iree_string_view_t
iree_string_view_remove_suffix(iree_string_view_t value, iree_host_size_t n);
// Removes the given substring prefix from the string view if present.
IREE_API_EXPORT iree_string_view_t iree_string_view_strip_prefix(
iree_string_view_t value, iree_string_view_t prefix);
// Removes the given substring suffix from the string view if present.
IREE_API_EXPORT iree_string_view_t iree_string_view_strip_suffix(
iree_string_view_t value, iree_string_view_t suffix);
// Removes the given substring prefix from the string view if present in-place.
// Returns true if the strip succeeded.
IREE_API_EXPORT bool iree_string_view_consume_prefix(iree_string_view_t* value,
iree_string_view_t prefix);
// Removes the given substring suffix from the string view if present in-place.
// Returns true if the strip succeeded.
IREE_API_EXPORT bool iree_string_view_consume_suffix(iree_string_view_t* value,
iree_string_view_t suffix);
// Removes leading and trailing whitespace.
IREE_API_EXPORT iree_string_view_t
iree_string_view_trim(iree_string_view_t value);
// Returns a substring of the string view at offset |pos| and length |n|.
// Use |n| == INTPTR_MAX to take the remaineder of the string after |pos|.
// Returns empty string on failure.
IREE_API_EXPORT iree_string_view_t iree_string_view_substr(
iree_string_view_t value, iree_host_size_t pos, iree_host_size_t n);
// Splits |value| into two parts based on the first occurrence of |split_char|.
// Returns the index of the |split_char| in the original |value| or -1 if not
// found.
IREE_API_EXPORT intptr_t iree_string_view_split(iree_string_view_t value,
char split_char,
iree_string_view_t* out_lhs,
iree_string_view_t* out_rhs);
// Replaces all occurrences of |old_char| with |new_char|.
IREE_API_EXPORT void iree_string_view_replace_char(iree_string_view_t value,
char old_char,
char new_char);
// Returns true if the given |value| matches |pattern| (normal * and ? rules).
// This accepts wildcards in the form of '*' and '?' for any delimited value.
// '*' will match zero or more of any character and '?' will match exactly one
// of any character.
//
// For example,
// 'foo-*-bar' matches: 'foo-123-bar', 'foo-456-789-bar'
// 'foo-10?' matches: 'foo-101', 'foo-102'
IREE_API_EXPORT bool iree_string_view_match_pattern(iree_string_view_t value,
iree_string_view_t pattern);
// Copies the string bytes into the target buffer and returns the number of
// characters copied. Does not include a NUL terminator.
IREE_API_EXPORT iree_host_size_t iree_string_view_append_to_buffer(
iree_string_view_t source_value, iree_string_view_t* target_value,
char* buffer);
//===----------------------------------------------------------------------===//
// IREE_STATUS_FEATURE flags and IREE_STATUS_MODE setting
//===----------------------------------------------------------------------===//
// Captures origin source information on a call to iree_make_status.
// Status storage will be allocated and reference the __FILE__ and __LINE__
// of where it is invoked.
#define IREE_STATUS_FEATURE_SOURCE_LOCATION (1 << 0)
// Captures annotation messages provided via iree_make_status or
// iree_status_annotate.
// Status storage will be allocated.
#define IREE_STATUS_FEATURE_ANNOTATIONS (1 << 1)
// Captures the current callstack on a call to iree_make_status.
// Status storage will be allocated.
#define IREE_STATUS_FEATURE_STACK_TRACE (1 << 2)
// If no status mode override is provided we'll change the behavior based on
// build configuration.
#if !defined(IREE_STATUS_MODE)
#ifdef NDEBUG
// Release mode: just source location.
#define IREE_STATUS_MODE 2
#else
// Debug mode: annotations and stack traces.
#define IREE_STATUS_MODE 3
#endif // NDEBUG
#endif // !IREE_STATUS_MODE
// Set IREE_STATUS_FEATURES based on IREE_STATUS_MODE if the user hasn't
// overridden it with more specific settings.
//
// IREE_STATUS_MODE = 0: statuses are just integers
// IREE_STATUS_MODE = 1: statuses have source location of error
// IREE_STATUS_MODE = 2: statuses also have custom annotations
// IREE_STATUS_MODE = 3: statuses also have stack traces of the error site
#if !defined(IREE_STATUS_FEATURES)
#if defined(IREE_STATUS_MODE) && IREE_STATUS_MODE == 1
#define IREE_STATUS_FEATURES (IREE_STATUS_FEATURE_SOURCE_LOCATION)
#elif defined(IREE_STATUS_MODE) && IREE_STATUS_MODE == 2
#define IREE_STATUS_FEATURES \
(IREE_STATUS_FEATURE_SOURCE_LOCATION | IREE_STATUS_FEATURE_ANNOTATIONS)
#elif defined(IREE_STATUS_MODE) && IREE_STATUS_MODE == 3
#define IREE_STATUS_FEATURES \
(IREE_STATUS_FEATURE_SOURCE_LOCATION | IREE_STATUS_FEATURE_ANNOTATIONS | \
IREE_STATUS_FEATURE_STACK_TRACE)
#else
#define IREE_STATUS_FEATURES 0
#endif // IREE_STATUS_MODE
#endif // !IREE_STATUS_FEATURES
//===----------------------------------------------------------------------===//
// iree_status_t and error reporting
//===----------------------------------------------------------------------===//
// Well-known status codes matching iree::StatusCode.
// Note that any code within IREE_STATUS_CODE_MASK is valid even if not
// enumerated here. Always check for unhandled errors/have default conditions.
typedef enum {
IREE_STATUS_OK = 0,
IREE_STATUS_CANCELLED = 1,
IREE_STATUS_UNKNOWN = 2,
IREE_STATUS_INVALID_ARGUMENT = 3,
IREE_STATUS_DEADLINE_EXCEEDED = 4,
IREE_STATUS_NOT_FOUND = 5,
IREE_STATUS_ALREADY_EXISTS = 6,
IREE_STATUS_PERMISSION_DENIED = 7,
IREE_STATUS_RESOURCE_EXHAUSTED = 8,
IREE_STATUS_FAILED_PRECONDITION = 9,
IREE_STATUS_ABORTED = 10,
IREE_STATUS_OUT_OF_RANGE = 11,
IREE_STATUS_UNIMPLEMENTED = 12,
IREE_STATUS_INTERNAL = 13,
IREE_STATUS_UNAVAILABLE = 14,
IREE_STATUS_DATA_LOSS = 15,
IREE_STATUS_UNAUTHENTICATED = 16,
IREE_STATUS_CODE_MASK = 0x1Fu,
} iree_status_code_t;
// Opaque status structure containing an iree_status_code_t and optional status
// object with more detailed information and payloads.
//
// The status value uses the lower 5 bits to store the iree_status_code_t and
// the remaining uintptr_t bits to store an optional status payload pointer.
// An OK status will always be bit-equivalent to 0 to make success/failure
// checks as cheap as an integer non-zero comparison. As the payload is optional
// it's legal to construct an iree_status_t from an iree_status_code_t directly
// meaning `return iree_status_from_code(IREE_STATUS_INTERNAL);` (etc) is valid,
// though not as useful as constructing via iree_make_status (which captures
// additional info).
typedef struct iree_status_handle_t* iree_status_t;
// Returns an iree_status_t from the an iree_status_code_t.
#define iree_status_from_code(code) \
((iree_status_t)((uintptr_t)((iree_status_code_t)(code)) & \
IREE_STATUS_CODE_MASK))
// Returns the iree_status_code_t from an iree_status_t.
#define iree_status_code(value) \
((iree_status_code_t)(((uintptr_t)(value)) & IREE_STATUS_CODE_MASK))
// Macros to check the value of a status code.
#define iree_status_is_ok(value) \
IREE_LIKELY((uintptr_t)(value) == IREE_STATUS_OK)
#define iree_status_is_cancelled(value) \
(iree_status_code(value) == IREE_STATUS_CANCELLED)
#define iree_status_is_unknown(value) \
(iree_status_code(value) == IREE_STATUS_UNKNOWN)
#define iree_status_is_invalid_argument(value) \
(iree_status_code(value) == IREE_STATUS_INVALID_ARGUMENT)
#define iree_status_is_deadline_exceeded(value) \
(iree_status_code(value) == IREE_STATUS_DEADLINE_EXCEEDED)
#define iree_status_is_not_found(value) \
(iree_status_code(value) == IREE_STATUS_NOT_FOUND)
#define iree_status_is_already_exists(value) \
(iree_status_code(value) == IREE_STATUS_ALREADY_EXISTS)
#define iree_status_is_permission_denied(value) \
(iree_status_code(value) == IREE_STATUS_PERMISSION_DENIED)
#define iree_status_is_resource_exhausted(value) \
(iree_status_code(value) == IREE_STATUS_RESOURCE_EXHAUSTED)
#define iree_status_is_failed_precondition(value) \
(iree_status_code(value) == IREE_STATUS_FAILED_PRECONDITION)
#define iree_status_is_aborted(value) \
(iree_status_code(value) == IREE_STATUS_ABORTED)
#define iree_status_is_out_of_range(value) \
(iree_status_code(value) == IREE_STATUS_OUT_OF_RANGE)
#define iree_status_is_unimplemented(value) \
(iree_status_code(value) == IREE_STATUS_UNIMPLEMENTED)
#define iree_status_is_internal(value) \
(iree_status_code(value) == IREE_STATUS_INTERNAL)
#define iree_status_is_unavailable(value) \
(iree_status_code(value) == IREE_STATUS_UNAVAILABLE)
#define iree_status_is_data_loss(value) \
(iree_status_code(value) == IREE_STATUS_DATA_LOSS)
#define iree_status_is_unauthenticated(value) \
(iree_status_code(value) == IREE_STATUS_UNAUTHENTICATED)
#define IREE_STATUS_IMPL_CONCAT_INNER_(x, y) x##y
#define IREE_STATUS_IMPL_CONCAT_(x, y) IREE_STATUS_IMPL_CONCAT_INNER_(x, y)
#define IREE_STATUS_IMPL_IDENTITY_(...) __VA_ARGS__
#define IREE_STATUS_IMPL_GET_EXPR_(expr, ...) expr
#define IREE_STATUS_IMPL_GET_ARGS_(expr, ...) __VA_ARGS__
#define IREE_STATUS_IMPL_GET_MACRO_(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, \
_10, _11, _12, _13, _14, ...) \
IREE_STATUS_IMPL_IDENTITY_( \
IREE_STATUS_IMPL_IDENTITY_(IREE_STATUS_IMPL_GET_EXPR_)(__VA_ARGS__))
#define IREE_STATUS_IMPL_MAKE_EMPTY_(file, line, status_code, ...) \
iree_status_allocate(status_code, file, line, iree_string_view_empty())
#define IREE_STATUS_IMPL_MAKE_ANNOTATE_(file, line, status_code, message) \
iree_status_allocate(status_code, file, line, iree_make_cstring_view(message))
#define IREE_STATUS_IMPL_MAKE_ANNOTATE_F_(file, line, status_code, ...) \
iree_status_allocate_f(status_code, file, line, __VA_ARGS__)
#define IREE_STATUS_IMPL_MAKE_SWITCH_(file, line, ...) \
IREE_STATUS_IMPL_IDENTITY_(IREE_STATUS_IMPL_IDENTITY_( \
IREE_STATUS_IMPL_GET_MACRO_)( \
__VA_ARGS__, IREE_STATUS_IMPL_MAKE_ANNOTATE_F_, \
IREE_STATUS_IMPL_MAKE_ANNOTATE_F_, IREE_STATUS_IMPL_MAKE_ANNOTATE_F_, \
IREE_STATUS_IMPL_MAKE_ANNOTATE_F_, IREE_STATUS_IMPL_MAKE_ANNOTATE_F_, \
IREE_STATUS_IMPL_MAKE_ANNOTATE_F_, IREE_STATUS_IMPL_MAKE_ANNOTATE_F_, \
IREE_STATUS_IMPL_MAKE_ANNOTATE_F_, IREE_STATUS_IMPL_MAKE_ANNOTATE_F_, \
IREE_STATUS_IMPL_MAKE_ANNOTATE_F_, IREE_STATUS_IMPL_MAKE_ANNOTATE_F_, \
IREE_STATUS_IMPL_MAKE_ANNOTATE_F_, IREE_STATUS_IMPL_MAKE_ANNOTATE_F_, \
IREE_STATUS_IMPL_MAKE_ANNOTATE_, IREE_STATUS_IMPL_MAKE_EMPTY_)) \
(file, line, IREE_STATUS_IMPL_GET_EXPR_(__VA_ARGS__), \
IREE_STATUS_IMPL_GET_ARGS_(__VA_ARGS__))
#define IREE_STATUS_IMPL_PASS_(var, ...) var
#define IREE_STATUS_IMPL_ANNOTATE_(var, ...) \
IREE_STATUS_IMPL_IDENTITY_(iree_status_annotate( \
var, iree_make_cstring_view(IREE_STATUS_IMPL_IDENTITY_( \
IREE_STATUS_IMPL_GET_ARGS_)(__VA_ARGS__))))
#define IREE_STATUS_IMPL_ANNOTATE_F_(var, ...) \
IREE_STATUS_IMPL_IDENTITY_(iree_status_annotate_f( \
var, \
IREE_STATUS_IMPL_IDENTITY_(IREE_STATUS_IMPL_GET_ARGS_)(__VA_ARGS__)))
#define IREE_STATUS_IMPL_ANNOTATE_SWITCH_(...) \
IREE_STATUS_IMPL_IDENTITY_(IREE_STATUS_IMPL_IDENTITY_( \
IREE_STATUS_IMPL_GET_MACRO_)( \
__VA_ARGS__, IREE_STATUS_IMPL_ANNOTATE_F_, IREE_STATUS_IMPL_ANNOTATE_F_, \
IREE_STATUS_IMPL_ANNOTATE_F_, IREE_STATUS_IMPL_ANNOTATE_F_, \
IREE_STATUS_IMPL_ANNOTATE_F_, IREE_STATUS_IMPL_ANNOTATE_F_, \
IREE_STATUS_IMPL_ANNOTATE_F_, IREE_STATUS_IMPL_ANNOTATE_F_, \
IREE_STATUS_IMPL_ANNOTATE_F_, IREE_STATUS_IMPL_ANNOTATE_F_, \
IREE_STATUS_IMPL_ANNOTATE_F_, IREE_STATUS_IMPL_ANNOTATE_F_, \
IREE_STATUS_IMPL_ANNOTATE_, IREE_STATUS_IMPL_PASS_)) \
(IREE_STATUS_IMPL_GET_EXPR_(__VA_ARGS__), \
IREE_STATUS_IMPL_GET_ARGS_(__VA_ARGS__))
#define IREE_STATUS_IMPL_RETURN_IF_API_ERROR_(var, ...) \
iree_status_t var = (IREE_STATUS_IMPL_IDENTITY_( \
IREE_STATUS_IMPL_IDENTITY_(IREE_STATUS_IMPL_GET_EXPR_)(__VA_ARGS__))); \
if (IREE_UNLIKELY(var)) { \
return IREE_STATUS_IMPL_ANNOTATE_SWITCH_(var, __VA_ARGS__); \
}
#define IREE_STATUS_IMPL_RETURN_AND_EVAL_IF_API_ERROR_(tail_expr, var, ...) \
iree_status_t var = (IREE_STATUS_IMPL_IDENTITY_( \
IREE_STATUS_IMPL_IDENTITY_(IREE_STATUS_IMPL_GET_EXPR_)(__VA_ARGS__))); \
if (IREE_UNLIKELY(var)) { \
(tail_expr); \
return IREE_STATUS_IMPL_ANNOTATE_SWITCH_(var, __VA_ARGS__); \
}
#define IREE_STATUS_IMPL_IGNORE_ERROR_(var, expr) \
iree_status_t var = (expr); \
if (IREE_UNLIKELY(var)) iree_status_ignore(var);
// We cut out all status storage code when not used.
#if IREE_STATUS_FEATURES == 0
#define IREE_STATUS_IMPL_MAKE_(code, ...) \
(iree_status_t)(uintptr_t)((code)&IREE_STATUS_CODE_MASK)
#undef IREE_STATUS_IMPL_RETURN_IF_API_ERROR_
#define IREE_STATUS_IMPL_RETURN_IF_API_ERROR_(var, ...) \
iree_status_t var = (IREE_STATUS_IMPL_IDENTITY_( \
IREE_STATUS_IMPL_IDENTITY_(IREE_STATUS_IMPL_GET_EXPR_)(__VA_ARGS__))); \
if (IREE_UNLIKELY(var)) return var;
#undef IREE_STATUS_IMPL_RETURN_AND_EVAL_IF_API_ERROR_
#define IREE_STATUS_IMPL_RETURN_AND_EVAL_IF_API_ERROR_(tail_expr, var, ...) \
iree_status_t var = (IREE_STATUS_IMPL_IDENTITY_( \
IREE_STATUS_IMPL_IDENTITY_(IREE_STATUS_IMPL_GET_EXPR_)(__VA_ARGS__))); \
if (IREE_UNLIKELY(var)) { \
(tail_expr); \
return var; \
}
#undef IREE_STATUS_IMPL_IGNORE_ERROR_
#define IREE_STATUS_IMPL_IGNORE_ERROR_(var, expr) \
iree_status_t var = (expr); \
(void)(var);
#else
#define IREE_STATUS_IMPL_MAKE_(...) \
IREE_STATUS_IMPL_MAKE_SWITCH_(__FILE__, __LINE__, __VA_ARGS__)
#endif // !IREE_STATUS_FEATURES
// Returns an IREE_STATUS_OK.
#define iree_ok_status() iree_status_from_code(IREE_STATUS_OK)
// Makes an iree_status_t with the given iree_status_code_t code and records
// the current source location.
//
// Optionally either a message string literal or printf-style format string may
// be associated with the status.
//
// Examples:
// return iree_make_status(IREE_STATUS_CANCELLED);
// return iree_make_status(IREE_STATUS_CANCELLED, "because reasons");
// return iree_make_status(IREE_STATUS_CANCELLED, "because %d > %d", a, b);
#define iree_make_status IREE_STATUS_IMPL_MAKE_
// Propagates the error returned by (expr) by returning from the current
// function on non-OK status. Optionally annotates the status with additional
// information (see iree_status_annotate for more information).
//
// Example:
// iree_status_t OtherFunc(...);
// iree_status_t MyFunc(...) {
// IREE_RETURN_IF_ERROR(OtherFunc(...));
// IREE_RETURN_IF_ERROR(OtherFunc(...), "with a message");
// IREE_RETURN_IF_ERROR(OtherFunc(...), "with a value: %d", 5);
// return iree_ok_status();
// }
#define IREE_RETURN_IF_ERROR(...) \
IREE_STATUS_IMPL_RETURN_IF_API_ERROR_( \
IREE_STATUS_IMPL_CONCAT_(__status_, __COUNTER__), \
IREE_STATUS_IMPL_IDENTITY_(IREE_STATUS_IMPL_IDENTITY_(__VA_ARGS__)))
// IREE_RETURN_IF_ERROR with a custom expression to evaluate before returning.
#define IREE_RETURN_AND_EVAL_IF_ERROR(tail_expr, ...) \
IREE_STATUS_IMPL_RETURN_AND_EVAL_IF_API_ERROR_( \
tail_expr, IREE_STATUS_IMPL_CONCAT_(__status_, __COUNTER__), \
IREE_STATUS_IMPL_IDENTITY_(IREE_STATUS_IMPL_IDENTITY_(__VA_ARGS__)))
// Ignores the status result of (expr) regardless of its value.
//
// Example:
// IREE_IGNORE_ERROR(some_fn_that_may_fail());
#define IREE_IGNORE_ERROR(expr) \
IREE_STATUS_IMPL_IGNORE_ERROR_( \
IREE_STATUS_IMPL_CONCAT_(__status_, __COUNTER__), (expr))
// TODO(#2843): better logging of status checks.
#define IREE_CHECK_OK(expr) \
IREE_CHECK_EQ(IREE_STATUS_OK, iree_status_consume_code(expr))
//===----------------------------------------------------------------------===//
// IREE_ASSERT macros
//===----------------------------------------------------------------------===//
// These are no-oped in builds with NDEBUG defined (by default anything but
// `-c dbg`/`-DCMAKE_BUILD_TYPE=Debug`). They differ from assert in that
// they avoid unused variable warnings when NDEBUG is defined. As with normal
// assert() ensure that side-effecting behavior is avoided as the expression
// will not be evaluated when the asserts are removed!
// TODO(benvanik): move to iree/base/assert.h.
#if defined(NDEBUG) // N(o) DEBUG
// Assertions disabled:
#define IREE_ASSERT(condition, ...) \
while (false && (condition)) { \
}
// TODO(benvanik): replace the status_matchers version with a test macro.
// #define IREE_ASSERT_OK(status) IREE_ASSERT(iree_status_is_ok(status))
// However, we still want the compiler to parse x and y because
// we don't want to lose potentially useful errors and warnings
// (and want to hide unused variable warnings when asserts are disabled).
// _IREE_ASSERT_CMP is a helper and should not be used outside of this file.
#define _IREE_ASSERT_CMP(x, op, y, ...) \
while (false && ((void)(x), (void)(y), 0)) { \
}
#else
// Assertions enabled:
#define IREE_ASSERT(condition, ...) assert(condition)
// TODO(#2843): better logging of status assertions.
// #define IREE_ASSERT_OK(status) IREE_ASSERT(iree_status_is_ok(status))
#define _IREE_ASSERT_CMP(x, op, y, ...) IREE_ASSERT(((x)op(y)), __VA_ARGS__)
#endif // NDEBUG
#define IREE_ASSERT_ARGUMENT(name) IREE_ASSERT(name)
#define IREE_ASSERT_TRUE(expr, ...) IREE_ASSERT(!!(expr), __VA_ARGS__)
#define IREE_ASSERT_FALSE(expr, ...) IREE_ASSERT(!(expr), __VA_ARGS__)
#define IREE_ASSERT_EQ(x, y, ...) _IREE_ASSERT_CMP(x, ==, y, __VA_ARGS__)
#define IREE_ASSERT_NE(x, y, ...) _IREE_ASSERT_CMP(x, !=, y, __VA_ARGS__)
#define IREE_ASSERT_LE(x, y, ...) _IREE_ASSERT_CMP(x, <=, y, __VA_ARGS__)
#define IREE_ASSERT_LT(x, y, ...) _IREE_ASSERT_CMP(x, <, y, __VA_ARGS__)
#define IREE_ASSERT_GE(x, y, ...) _IREE_ASSERT_CMP(x, >=, y, __VA_ARGS__)
#define IREE_ASSERT_GT(x, y, ...) _IREE_ASSERT_CMP(x, >, y, __VA_ARGS__)
// Returns the canonical status code for the given errno value.
// https://en.cppreference.com/w/cpp/error/errno_macros
IREE_API_EXPORT iree_status_code_t
iree_status_code_from_errno(int error_number);
#if defined(_WIN32) || defined(_WIN64)
// Returns the canonical status code for the given Win32 GetLastError code.
// https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror
IREE_API_EXPORT iree_status_code_t
iree_status_code_from_win32_error(uint32_t error);
#endif // _WIN32 || _WIN64
// Returns a NUL-terminated string constant for the given status code, such as
// IREE_STATUS_UNAVAILABLE = "UNAVAILABLE". Do not rely on string-matching the
// result as the exact text may change.
IREE_API_EXPORT const char* iree_status_code_string(iree_status_code_t code);
// Allocates a new status instance for a failing error |code|.
// |file| and |line| should be populated with __FILE__ and __LINE__ at the call
// site and an optional string |message| may be provided.
//
// The status will be allocated using the default system allocator and must be
// freed using either iree_status_free or iree_status_ignore.
IREE_API_EXPORT IREE_MUST_USE_RESULT iree_status_t
iree_status_allocate(iree_status_code_t code, const char* file, uint32_t line,
iree_string_view_t message);
// Allocates a new status instance for a failing error |code| and annotates it
// with a printf-style format string. Roughly equivalent (though more efficient)
// than iree_status_allocate + iree_status_annotate_f.
IREE_API_EXPORT IREE_MUST_USE_RESULT iree_status_t IREE_PRINTF_ATTRIBUTE(4, 5)
iree_status_allocate_f(iree_status_code_t code, const char* file,
uint32_t line, const char* format, ...);
IREE_API_EXPORT IREE_MUST_USE_RESULT iree_status_t iree_status_allocate_vf(
iree_status_code_t code, const char* file, uint32_t line,
const char* format, va_list varargs_0, va_list varargs_1);
// Clones |status| into a new status instance.
// No payloads, if present, will be cloned.
IREE_API_EXPORT IREE_MUST_USE_RESULT iree_status_t
iree_status_clone(iree_status_t status);
// Frees |status| if it has any associated storage.
IREE_API_EXPORT void iree_status_free(iree_status_t status);
// Ignores |status| regardless of its value and frees any associated payloads.
// Returns an OK status that can be used when chaining.
IREE_API_EXPORT iree_status_t iree_status_ignore(iree_status_t status);
// Consumes the |status| by freeing its storage and returning its code.
IREE_API_EXPORT iree_status_code_t
iree_status_consume_code(iree_status_t status);
// Annotates a status message with the given constant string message.
// Ignored if |base_status| is OK.
IREE_API_EXPORT IREE_MUST_USE_RESULT iree_status_t
iree_status_annotate(iree_status_t base_status, iree_string_view_t message);
// Annotates a status message with the given printf-style message.
// Ignored if |base_status| is OK.
IREE_API_EXPORT IREE_MUST_USE_RESULT iree_status_t IREE_PRINTF_ATTRIBUTE(2, 3)
iree_status_annotate_f(iree_status_t base_status, const char* format, ...);
IREE_API_EXPORT IREE_MUST_USE_RESULT iree_status_t
iree_status_annotate_vf(iree_status_t base_status, const char* format,
va_list varargs_0, va_list varargs_1);
// Formats the status as a multi-line string containing all associated payloads.
// Note that this may contain PII such as file paths and must only be used for
// presenting errors to users and not sent to a logs aggregation service.
IREE_API_EXPORT bool iree_status_format(iree_status_t status,
iree_host_size_t buffer_capacity,
char* buffer,
iree_host_size_t* out_buffer_length);
// Converts the status to an allocated string value.
// The caller must free the buffer with the system allocator.
IREE_API_EXPORT bool iree_status_to_string(iree_status_t status,
char** out_buffer,
iree_host_size_t* out_buffer_length);
//===----------------------------------------------------------------------===//
// IREE Core API
//===----------------------------------------------------------------------===//
// Sprinkle this wherever to make it easier to find structs/functions that are
// not yet stable.
#define IREE_API_UNSTABLE
// Known versions of the API that can be referenced in code.
// Out-of-bounds values are possible in forward-versioned changes.
enum iree_api_version_e {
IREE_API_VERSION_0 = 0u,
// Always set to the latest version of the library from source.
IREE_API_VERSION_LATEST = IREE_API_VERSION_0,
};
typedef uint32_t iree_api_version_t;
// Checks whether the |expected_version| of the caller matches the implemented
// version of |out_actual_version|. Forward compatibility of the API is
// supported but backward compatibility is not: newer binaries using older
// shared libraries of the runtime will fail.
//
// Returns IREE_STATUS_OUT_OF_RANGE if the actual version is not compatible with
// the expected version.
IREE_API_EXPORT iree_status_t
iree_api_version_check(iree_api_version_t expected_version,
iree_api_version_t* out_actual_version);
//===----------------------------------------------------------------------===//
// iree_time_t and iree_duration_t
//===----------------------------------------------------------------------===//
// A point in time represented as nanoseconds since unix epoch.
// TODO(benvanik): pick something easy to get into/outof time_t/etc.
typedef int64_t iree_time_t;
// A time in the infinite past used to indicate "already happened".
// This forces APIs that wait for a point in time to act as a poll and always
// return IREE_STATUS_DEADLINE_EXCEEDED instead of blocking the caller.
#define IREE_TIME_INFINITE_PAST INT64_MIN
// A time in the infinite future used to indicate "never".
// This causes APIs that wait for a point in time to wait however long is needed
// to satisfy the wait condition.
#define IREE_TIME_INFINITE_FUTURE INT64_MAX
// A duration represented as relative nanoseconds.
typedef int64_t iree_duration_t;
// A zero-length duration.
// Like IREE_TIME_INFINITE_FUTURE this forces APIs that would wait to instead
// return IREE_STATUS_DEADLINE_EXCEEDED immediately.
#define IREE_DURATION_ZERO 0
// An infinite-length duration.
// Like IREE_TIME_INFINITE_FUTURE this causes APIs that wait to do so until
// their wait condition is satisfied without returning early.
#define IREE_DURATION_INFINITE INT64_MAX
// Returns the current system time in unix nanoseconds.
// Depending on the system architecture and power mode this time may have a
// very coarse granularity (on the order of microseconds to milliseconds).
//
// The system timer may not be monotonic; users should ensure when comparing
// times they check for negative values in case the time moves backwards.
IREE_API_EXPORT iree_time_t iree_time_now();
// Converts a relative timeout duration to an absolute deadline time.
// This handles the special cases of IREE_DURATION_ZERO and
// IREE_DURATION_INFINITE to avoid extraneous time queries.
IREE_API_EXPORT iree_time_t
iree_relative_timeout_to_deadline_ns(iree_duration_t timeout_ns);
// Converts an absolute deadline time to a relative timeout duration.
// This handles the special cases of IREE_TIME_INFINITE_PAST and
// IREE_TIME_INFINITE_FUTURE to avoid extraneous time queries.
IREE_API_EXPORT iree_duration_t
iree_absolute_deadline_to_timeout_ns(iree_time_t deadline_ns);
typedef enum {
// Timeout is defined by an absolute value `deadline_ns`.
IREE_TIMEOUT_ABSOLUTE = 0,
// Timeout is defined by a relative value `timeout_ns`.
IREE_TIMEOUT_RELATIVE = 1,
} iree_timeout_type_t;
// A timeout defined either by an absolute or relative value.
typedef struct {
iree_timeout_type_t type;
iree_time_t nanos;
} iree_timeout_t;
// Returns a timeout that will be exceeded immediately.
// This can be used with APIs that would otherwise wait to cause them to poll.
//
// Example:
// status = iree_wait_for_signal_or_timeout(&obj, iree_immediate_timeout());
// if (iree_status_is_deadline_exceeded(status)) {
// // Would have waited indicating the signal has not occurred. If the
// // timeout was not immediate the call would have blocked the caller.
// }
static inline iree_timeout_t iree_immediate_timeout() {
iree_timeout_t timeout = {IREE_TIMEOUT_ABSOLUTE, IREE_TIME_INFINITE_PAST};
return timeout;
}
// Returns true if the |timeout| indicates an immediate/polling/nonblocking
// timeout.
static inline bool iree_timeout_is_immediate(iree_timeout_t timeout) {
return timeout.type == IREE_TIMEOUT_ABSOLUTE
? timeout.nanos == IREE_TIME_INFINITE_PAST
: timeout.nanos == IREE_DURATION_ZERO;
}
// Returns a timeout that will never be reached.
// This can be used with APIs that can wait to disable the early
// deadline-exceeded returns when a condition is not met. It should be used with
// care as it can complicate program state and make termination more prone to
// hangs. On the other hand, it's really useful to not bother with actual
// deadlines. YMMV.
static inline iree_timeout_t iree_infinite_timeout() {
iree_timeout_t timeout = {IREE_TIMEOUT_ABSOLUTE, IREE_TIME_INFINITE_FUTURE};
return timeout;
}
// Returns true if the |timeout| indicates an infinite/forever blocking timeout.
static inline bool iree_timeout_is_infinite(iree_timeout_t timeout) {
return timeout.type == IREE_TIMEOUT_ABSOLUTE
? timeout.nanos == IREE_TIME_INFINITE_FUTURE
: timeout.nanos == IREE_DURATION_INFINITE;
}
// Defines an absolute timeout with the given time in nanoseconds.
static inline iree_timeout_t iree_make_deadline(iree_time_t deadline_ns) {
iree_timeout_t timeout = {IREE_TIMEOUT_ABSOLUTE, deadline_ns};
return timeout;
}
// Defines a relative timeout with the given time in nanoseconds.
static inline iree_timeout_t iree_make_timeout(iree_duration_t timeout_ns) {
iree_timeout_t timeout = {IREE_TIMEOUT_RELATIVE, timeout_ns};
return timeout;
}
// Converts a timeout from relative to absolute (if it is).
//
// Absolute timeouts (deadlines) are better for long-running tasks or when
// making calls that may complete in stages as relative ones will tend to skew;
// if a wait is performed with a relative timeout of 10ms but it takes 5ms to
// get from the origin of the call to the actual wait using the timeout then
// the total latency of the call may be 15ms (5ms to prepare + 10ms on the
// wait). Instead if an absolute deadline is used the caller can ensure that
// the total time spent in the operation happens regardless of the intervening
// work that happens.
//
// For this reason IREE internal APIs try to convert to absolute times and users
// may be able to reduce overhead by populating the times as absolute to start
// with via iree_make_deadline.
static inline void iree_convert_timeout_to_absolute(iree_timeout_t* timeout) {
if (timeout->type == IREE_TIMEOUT_RELATIVE) {
timeout->type = IREE_TIMEOUT_ABSOLUTE;
timeout->nanos = iree_relative_timeout_to_deadline_ns(timeout->nanos);
}
}
// Returns an absolute deadline in nanoseconds from the given timeout.
static inline iree_time_t iree_timeout_as_deadline_ns(iree_timeout_t timeout) {
return timeout.type == IREE_TIMEOUT_ABSOLUTE
? timeout.nanos
: iree_relative_timeout_to_deadline_ns(timeout.nanos);
}
//===----------------------------------------------------------------------===//
// iree_allocator_t (std::allocator-like interface)
//===----------------------------------------------------------------------===//
// Defines how an allocation from an iree_allocator_t should be made.
typedef enum {
// The contents of the allocation *must* be zeroed by the allocator prior to
// returning. Allocators may be able to elide the zeroing if they allocate
// fresh pages from the system. It is always safe to zero contents if the
// behavior of the allocator is not under our control.
IREE_ALLOCATION_MODE_ZERO_CONTENTS = 1 << 0,
// Tries to reuse an existing allocation provided via |out_ptr| if possible.
// If the existing allocation is not reused then it is freed as if a call to
// iree_allocator_free had been called on it. If the allocation fails then
// the provided existing allocation is unmodified.
//
// This models the C realloc behavior.
IREE_ALLOCATION_MODE_TRY_REUSE_EXISTING = 1 << 1,
} iree_allocation_mode_t;
// TODO(benvanik): replace with a single method with the mode setting. This will
// reduce the overhead to just two pointers per allocator (from 3) and allow us
// to add more distinct behavior in the future. If we really wanted to stretch
// we could turn it into a pointer and require the allocator live somewhere
// (possibly in .text as a const static), but two pointers seems fine.
typedef iree_status_t(IREE_API_PTR* iree_allocator_alloc_fn_t)(
void* self, iree_allocation_mode_t mode, iree_host_size_t byte_length,
void** out_ptr);
typedef void(IREE_API_PTR* iree_allocator_free_fn_t)(void* self, void* ptr);
// An allocator for host-memory allocations.
// IREE will attempt to use this in place of the system malloc and free.
// Pass the iree_allocator_system() macro to use the system allocator.
typedef struct {
// User-defined pointer passed to all functions.
void* self;
// Allocates |byte_length| of memory and stores the pointer in |out_ptr|.
// Systems should align to 16 byte boundaries (or otherwise their natural
// SIMD alignment). The runtime pools internally and small allocations
// (usually) won't be made through this interface.
iree_allocator_alloc_fn_t alloc;
// Frees |ptr| from a previous alloc call.
iree_allocator_free_fn_t free;
} iree_allocator_t;
// Allocates a block of |byte_length| bytes from the given allocator.
// The contents of the returned memory is guaranteed to be zeroed.
IREE_API_EXPORT iree_status_t iree_allocator_malloc(
iree_allocator_t allocator, iree_host_size_t byte_length, void** out_ptr);
// Reallocates |out_ptr| to |byte_length| bytes with the given allocator.
// If the reallocation fails then the original |out_ptr| is unmodified.
IREE_API_EXPORT iree_status_t iree_allocator_realloc(
iree_allocator_t allocator, iree_host_size_t byte_length, void** out_ptr);
// Duplicates the given byte block by allocating memory and copying it in.
IREE_API_EXPORT iree_status_t
iree_allocator_clone(iree_allocator_t allocator,
iree_const_byte_span_t source_bytes, void** out_ptr);
// Frees a previously-allocated block of memory to the given allocator.
IREE_API_EXPORT void iree_allocator_free(iree_allocator_t allocator, void* ptr);
// Allocates a block of |byte_length| bytes from the default system allocator.
IREE_API_EXPORT iree_status_t
iree_allocator_system_allocate(void* self, iree_allocation_mode_t mode,
iree_host_size_t byte_length, void** out_ptr);
// Frees a previously-allocated block of memory to the default system allocator.
IREE_API_EXPORT void iree_allocator_system_free(void* self, void* ptr);
// Allocates using the iree_allocator_malloc and iree_allocator_free methods.
// These will usually be backed by malloc and free.
static inline iree_allocator_t iree_allocator_system() {
iree_allocator_t v = {NULL, iree_allocator_system_allocate,
iree_allocator_system_free};
return v;
}
// Does not perform any allocation or deallocation; used to wrap objects that
// are owned by external code/live in read-only memory/etc.
static inline iree_allocator_t iree_allocator_null() {
iree_allocator_t v = {NULL, NULL, NULL};
return v;
}
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // IREE_BASE_API_H_