blob: 7cf172b7c3d0eed8b8fb5d2f1b12a57614353c72 [file]
// 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_REF_H_
#define IREE_VM_REF_H_
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include "iree/base/api.h"
#include "iree/base/atomics.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// Defines the type of the reference-counted pointer.
// This is used to verify that operations dealing with the variant ref struct
// are correct at runtime. We don't allow control over the ref types from the
// VM ops and as such we can use the type specified as a safe way to avoid
// reinterpreting memory incorrectly.
typedef enum {
IREE_VM_REF_TYPE_NULL = 0,
// NOTE: these type values are assigned dynamically right now. Treat them as
// opaque and unstable across process invocations.
// Maximum type ID value. Type IDs are limited to 24-bits.
IREE_VM_REF_TYPE_MAX_VALUE = 0x00FFFFFEu,
// Wildcard type that indicates that a value may be a ref type but of an
// unspecified internal type.
IREE_VM_REF_TYPE_ANY = 0x00FFFFFFu,
} iree_vm_ref_type_t;
// Base for iree_vm_ref_t object targets.
//
// Usage (C):
// typedef struct {
// iree_vm_ref_object_t ref_object;
// int my_fields;
// } my_type_t;
// void my_type_destroy(void* ptr) {
// free(ptr);
// }
// static iree_vm_ref_type_descriptor_t my_type_descriptor;
// my_type_descriptor.type_name = iree_string_view_t{"my_type", 7};
// my_type_descriptor.destroy = my_type_destroy;
// my_type_descriptor.offsetof_counter = offsetof(my_type_t,
// ref_object.counter);
// iree_vm_ref_register_defined_type(&my_type_descriptor);
//
// Usage (C++):
// Prefer using RefObject as a base type.
typedef struct {
iree_atomic_ref_count_t counter;
} iree_vm_ref_object_t;
// A pointer reference to a reference-counted object.
// The counter is stored within the target object itself ala intrusive_ptr.
//
// NOTE: we try to keep this small so that we aren't wasting stack space or
// copying around too much when we pass it to functions by value. This also
// helps make the CPU caches happier as we need no indirections to check the
// type and adjusting the counter happens without needing to query a descriptor.
// Ideally the iree_vm_ref_t is in-cache on the stack and the target ptr is
// either in cache from a previous use or will be used again after manipulating
// its ref count.
typedef struct {
// Pointer to the object. Type is resolved based on the |type| field.
// Will be NULL if the reference points to nothing.
void* ptr;
// Offset from ptr, in bytes, to the start of an atomic_int32_t representing
// the current reference count. We store this here to avoid the need for an
// indirection in the (extremely common) case of just reference count inc/dec.
uint32_t offsetof_counter : 8;
// Registered type of the object pointed to by ptr.
iree_vm_ref_type_t type : 24;
} iree_vm_ref_t;
static_assert(
sizeof(iree_vm_ref_t) <= sizeof(void*) * 2,
"iree_vm_ref_t dominates stack space usage and should be kept tiny");
typedef void(IREE_API_PTR* iree_vm_ref_destroy_t)(void* ptr);
#define IREE_VM_REF_DESTROY_FREE free
#define IREE_VM_REF_DESTROY_CC_DELETE +[](void* ptr) { delete ptr; }
// Describes a type for the VM.
typedef struct {
// Function called when references of this type reach 0 and should be
// destroyed.
iree_vm_ref_destroy_t destroy;
// Offset from ptr, in bytes, to the start of an atomic_int32_t representing
// the current reference count.
uint32_t offsetof_counter : 8;
// The type ID assigned to this type from the iree_vm_ref_type_t table (or an
// external user source).
iree_vm_ref_type_t type : 24;
// Unretained type name that can be used for debugging.
iree_string_view_t type_name;
} iree_vm_ref_type_descriptor_t;
// Directly retains the object with base |ptr| with the given |type_descriptor|.
//
// Note that this avoids any kind of type checking; for untrusted inputs use
// the iree_vm_ref_t-based methods.
IREE_API_EXPORT void IREE_API_CALL iree_vm_ref_object_retain(
void* ptr, const iree_vm_ref_type_descriptor_t* type_descriptor);
// Directly release the object with base |ptr| with the given |type_descriptor|,
// possibly destroying it if it is the last reference. Assume that |ptr| is
// invalid after this function returns.
//
// Note that this avoids any kind of type checking; for untrusted inputs use
// the iree_vm_ref_t-based methods.
IREE_API_EXPORT void IREE_API_CALL iree_vm_ref_object_release(
void* ptr, const iree_vm_ref_type_descriptor_t* type_descriptor);
// Registers a user-defined type with the IREE C ref system.
// The provided destroy function will be used to destroy objects when their
// reference count goes to 0. NULL can be used to no-op the destruction if the
// type is not owned by the VM.
//
// TODO(benvanik): keep names alive for user types?
// NOTE: the name is not retained and must be kept live by the caller. Ideally
// it is stored in static read-only memory in the binary.
//
// WARNING: this function is not thread-safe and should only be used at startup
// to register the types. Do not call this while any refs may be alive.
IREE_API_EXPORT iree_status_t IREE_API_CALL
iree_vm_ref_register_type(iree_vm_ref_type_descriptor_t* descriptor);
// Returns the type name for the given type, if found.
IREE_API_EXPORT iree_string_view_t IREE_API_CALL
iree_vm_ref_type_name(iree_vm_ref_type_t type);
// Returns the registered type descriptor for the given type, if found.
IREE_API_EXPORT const iree_vm_ref_type_descriptor_t* IREE_API_CALL
iree_vm_ref_lookup_registered_type(iree_string_view_t full_name);
// Wraps a raw pointer in a iree_vm_ref_t reference and assigns it to |out_ref|.
// |out_ref| will be released if it already contains a reference. The target
// object will not be retained and must come in with a count >= 1.
//
// Usage (C):
// my_type_t* my_type = (my_type_t*)malloc(sizeof(my_type_t));
// my_type.ref_object.counter = IREE_ATOMIC_VAR_INIT(1);
// iree_vm_ref_t my_ref;
// iree_vm_ref_wrap_assign(my_type, IREE_VM_REF_TYPE_MY_TYPE, &my_ref);
// iree_vm_ref_release(&my_ref);
//
// Usage (C++):
// iree_vm_ref_t my_ref;
// iree_vm_ref_wrap_assign(new MyType(), IREE_VM_REF_TYPE_MY_TYPE, &my_ref);
// iree_vm_ref_release(&my_ref);
IREE_API_EXPORT iree_status_t IREE_API_CALL iree_vm_ref_wrap_assign(
void* ptr, iree_vm_ref_type_t type, iree_vm_ref_t* out_ref);
// Wraps a raw pointer in a iree_vm_ref_t reference and retains it in |out_ref|.
// |out_ref| will be released if it already contains a reference.
IREE_API_EXPORT iree_status_t IREE_API_CALL iree_vm_ref_wrap_retain(
void* ptr, iree_vm_ref_type_t type, iree_vm_ref_t* out_ref);
// Checks that the given reference-counted pointer |ref| is of |type|.
IREE_API_EXPORT iree_status_t IREE_API_CALL
iree_vm_ref_check(iree_vm_ref_t* ref, iree_vm_ref_type_t type);
#define IREE_VM_DEREF_OR_RETURN(value_type, value, ref, type) \
IREE_RETURN_IF_ERROR(iree_vm_ref_check(ref, type)); \
value_type* value = (value_type*)(ref)->ptr;
// Retains the reference-counted pointer |ref|.
// |out_ref| will be released if it already contains a reference.
IREE_API_EXPORT void IREE_API_CALL iree_vm_ref_retain(iree_vm_ref_t* ref,
iree_vm_ref_t* out_ref);
// Retains the reference-counted pointer |ref| and checks that it is of |type|.
// |out_ref| will be released if it already contains a reference.
IREE_API_EXPORT iree_status_t IREE_API_CALL iree_vm_ref_retain_checked(
iree_vm_ref_t* ref, iree_vm_ref_type_t type, iree_vm_ref_t* out_ref);
// Retains or moves |ref| to |out_ref|.
// |out_ref| will be released if it already contains a reference.
IREE_API_EXPORT void IREE_API_CALL iree_vm_ref_retain_or_move(
int is_move, iree_vm_ref_t* ref, iree_vm_ref_t* out_ref);
// Retains or moves |ref| to |out_ref| and checks that |ref| is of |type|.
// |out_ref| will be released if it already contains a reference.
IREE_API_EXPORT iree_status_t IREE_API_CALL iree_vm_ref_retain_or_move_checked(
int is_move, iree_vm_ref_t* ref, iree_vm_ref_type_t type,
iree_vm_ref_t* out_ref);
// Releases the reference-counted pointer |ref|, possibly freeing it.
IREE_API_EXPORT void IREE_API_CALL iree_vm_ref_release(iree_vm_ref_t* ref);
// Assigns the reference-counted pointer |ref| without incrementing the count.
// |out_ref| will be released if it already contains a reference.
IREE_API_EXPORT void IREE_API_CALL iree_vm_ref_assign(iree_vm_ref_t* ref,
iree_vm_ref_t* out_ref);
// Moves one reference to another without changing the reference count.
// Equivalent to an std::move of a ref_ptr.
// |out_ref| will be released if it already contains a reference.
IREE_API_EXPORT void IREE_API_CALL iree_vm_ref_move(iree_vm_ref_t* ref,
iree_vm_ref_t* out_ref);
// Returns true if the given |ref| is NULL.
IREE_API_EXPORT bool IREE_API_CALL iree_vm_ref_is_null(iree_vm_ref_t* ref);
// Returns true if the two references point at the same value (or are both
// null).
IREE_API_EXPORT bool IREE_API_CALL iree_vm_ref_equal(iree_vm_ref_t* lhs,
iree_vm_ref_t* rhs);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#ifdef __cplusplus
namespace iree {
namespace vm {
template <typename T>
struct ref_type_descriptor {
static const iree_vm_ref_type_descriptor_t* get();
};
} // namespace vm
} // namespace iree
#define IREE_VM_DECLARE_CC_TYPE_LOOKUP(name, T) \
namespace iree { \
namespace vm { \
template <> \
struct ref_type_descriptor<T> { \
static const iree_vm_ref_type_descriptor_t* get() { \
return name##_get_descriptor(); \
} \
}; \
} \
}
#define IREE_VM_REGISTER_CC_TYPE(type, name, descriptor) \
descriptor.type_name = iree_make_cstring_view(name); \
descriptor.offsetof_counter = type::offsetof_counter(); \
descriptor.destroy = type::DirectDestroy; \
IREE_RETURN_IF_ERROR(iree_vm_ref_register_type(&descriptor));
#else
#define IREE_VM_DECLARE_CC_TYPE_LOOKUP(name, T)
#define IREE_VM_REGISTER_CC_TYPE(type, name, descriptor)
#endif // __cplusplus
// TODO(benvanik): make these macros standard/document them.
#define IREE_VM_DECLARE_TYPE_ADAPTERS(name, T) \
IREE_API_EXPORT iree_vm_ref_t IREE_API_CALL name##_retain_ref(T* value); \
IREE_API_EXPORT iree_vm_ref_t IREE_API_CALL name##_move_ref(T* value); \
IREE_API_EXPORT T* IREE_API_CALL name##_deref(iree_vm_ref_t* ref); \
IREE_API_EXPORT iree_status_t IREE_API_CALL name##_check_deref( \
iree_vm_ref_t* ref, T** out_ptr); \
IREE_API_EXPORT const iree_vm_ref_type_descriptor_t* IREE_API_CALL \
name##_get_descriptor(); \
inline bool name##_isa(iree_vm_ref_t* ref) { \
return name##_get_descriptor()->type == ref->type; \
} \
IREE_API_EXPORT iree_vm_ref_type_t IREE_API_CALL name##_type_id(); \
IREE_VM_DECLARE_CC_TYPE_LOOKUP(name, T)
// TODO(benvanik): make these macros standard/document them.
#define IREE_VM_DEFINE_TYPE_ADAPTERS(name, T) \
IREE_API_EXPORT iree_vm_ref_t IREE_API_CALL name##_retain_ref(T* value) { \
iree_vm_ref_t ref = {0}; \
iree_vm_ref_wrap_retain(value, name##_descriptor.type, &ref); \
return ref; \
} \
IREE_API_EXPORT iree_vm_ref_t IREE_API_CALL name##_move_ref(T* value) { \
iree_vm_ref_t ref = {0}; \
iree_vm_ref_wrap_assign(value, name##_descriptor.type, &ref); \
return ref; \
} \
IREE_API_EXPORT T* IREE_API_CALL name##_deref(iree_vm_ref_t* ref) { \
iree_status_t status = iree_vm_ref_check(ref, name##_descriptor.type); \
if (!iree_status_is_ok(status)) { \
iree_status_ignore(status); \
return NULL; \
} \
return (T*)ref->ptr; \
} \
IREE_API_EXPORT iree_status_t IREE_API_CALL name##_check_deref( \
iree_vm_ref_t* ref, T** out_ptr) { \
IREE_RETURN_IF_ERROR(iree_vm_ref_check(ref, name##_descriptor.type)); \
*out_ptr = (T*)ref->ptr; \
return iree_ok_status(); \
} \
IREE_API_EXPORT const iree_vm_ref_type_descriptor_t* IREE_API_CALL \
name##_get_descriptor() { \
return &name##_descriptor; \
} \
IREE_API_EXPORT iree_vm_ref_type_t IREE_API_CALL name##_type_id() { \
return name##_descriptor.type; \
}
#endif // IREE_VM_REF_H_