| // 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 |
| |
| #ifndef IREE_BASE_ALLOCATOR_H_ |
| #define IREE_BASE_ALLOCATOR_H_ |
| |
| #include <memory.h> |
| #include <stdint.h> |
| #include <string.h> |
| |
| #include "iree/base/alignment.h" |
| #include "iree/base/attributes.h" |
| #include "iree/base/config.h" |
| #include "iree/base/status.h" |
| #include "iree/base/target_platform.h" |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif // __cplusplus |
| |
| //===----------------------------------------------------------------------===// |
| // Types and Enums |
| //===----------------------------------------------------------------------===// |
| |
| #if IREE_STATISTICS_ENABLE |
| // Evaluates the expression code only if statistics are enabled. |
| // |
| // Example: |
| // struct { |
| // IREE_STATISTICS(uint32_t stats_only_value); |
| // } my_object; |
| // IREE_STATISTICS(my_object.stats_only_value = 5); |
| // IREE_STATISTICS({ |
| // my_object.stats_only_value = 5; |
| // }); |
| #define IREE_STATISTICS(expr) expr |
| #else |
| #define IREE_STATISTICS(expr) |
| #endif // IREE_STATISTICS_ENABLE |
| |
| //===----------------------------------------------------------------------===// |
| // Byte buffers and memory utilities |
| //===----------------------------------------------------------------------===// |
| |
| // A span of mutable bytes (ala std::span of uint8_t). |
| typedef struct iree_byte_span_t { |
| 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; |
| } |
| |
| static inline iree_byte_span_t iree_byte_span_empty() { |
| iree_byte_span_t v = {NULL, 0}; |
| return v; |
| } |
| |
| static inline bool iree_byte_span_is_empty(iree_byte_span_t span) { |
| return span.data == NULL || span.data_length == 0; |
| } |
| |
| // A span of constant bytes (ala std::span of const uint8_t). |
| typedef struct iree_const_byte_span_t { |
| 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; |
| } |
| |
| static inline iree_const_byte_span_t iree_const_byte_span_empty() { |
| iree_const_byte_span_t v = {NULL, 0}; |
| return v; |
| } |
| |
| static inline bool iree_const_byte_span_is_empty(iree_const_byte_span_t span) { |
| return span.data == NULL || span.data_length == 0; |
| } |
| |
| static inline iree_const_byte_span_t iree_const_cast_byte_span( |
| iree_byte_span_t span) { |
| return iree_make_const_byte_span(span.data, span.data_length); |
| } |
| |
| static inline iree_byte_span_t iree_cast_const_byte_span( |
| iree_const_byte_span_t span) { |
| return iree_make_byte_span((uint8_t*)span.data, span.data_length); |
| } |
| |
| // Copies |size| bytes from |src| to |dst| without polluting the cache with |
| // |dst| lines. Used when streaming data that will not be read again. |
| static inline void iree_memcpy_stream_dst(void* IREE_RESTRICT dst, |
| const void* IREE_RESTRICT src, |
| iree_host_size_t size) { |
| // TODO(benvanik): implement a proper non-temporal copy. This will be |
| // architecture-specific and may have compiler-specific paths in order to emit |
| // the proper instructions. On x64 this should be using MOVNTDQ (or something |
| // in that family). |
| memcpy(dst, src, size); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Checked arithmetic for allocation size calculations |
| //===----------------------------------------------------------------------===// |
| |
| // Performs a checked addition of |a| and |b|, storing the result in |
| // |out_result|. Returns true if the addition succeeded without overflow, false |
| // if overflow occurred (|out_result| is undefined on overflow). |
| static inline bool iree_host_size_checked_add(iree_host_size_t a, |
| iree_host_size_t b, |
| iree_host_size_t* out_result) { |
| #if IREE_HAVE_BUILTIN(__builtin_add_overflow) |
| return !__builtin_add_overflow(a, b, out_result); |
| #else |
| if (a > IREE_HOST_SIZE_MAX - b) return false; |
| *out_result = a + b; |
| return true; |
| #endif |
| } |
| |
| // Performs a checked multiplication of |a| and |b|, storing the result in |
| // |out_result|. Returns true if the multiplication succeeded without overflow, |
| // false if overflow occurred (|out_result| is undefined on overflow). |
| static inline bool iree_host_size_checked_mul(iree_host_size_t a, |
| iree_host_size_t b, |
| iree_host_size_t* out_result) { |
| #if IREE_HAVE_BUILTIN(__builtin_mul_overflow) |
| return !__builtin_mul_overflow(a, b, out_result); |
| #else |
| if (b != 0 && a > IREE_HOST_SIZE_MAX / b) return false; |
| *out_result = a * b; |
| return true; |
| #endif |
| } |
| |
| // Performs checked computation of |base| + |count| * |element_size|, storing |
| // the result in |out_result|. Returns true if the computation succeeded without |
| // overflow, false if overflow occurred (|out_result| is undefined on overflow). |
| // This is the common pattern for computing allocation sizes with a header. |
| static inline bool iree_host_size_checked_mul_add( |
| iree_host_size_t base, iree_host_size_t count, |
| iree_host_size_t element_size, iree_host_size_t* out_result) { |
| iree_host_size_t product = 0; |
| if (!iree_host_size_checked_mul(count, element_size, &product)) return false; |
| return iree_host_size_checked_add(base, product, out_result); |
| } |
| |
| // Device size variants for remote device allocations. |
| |
| static inline bool iree_device_size_checked_add( |
| iree_device_size_t a, iree_device_size_t b, |
| iree_device_size_t* out_result) { |
| #if IREE_HAVE_BUILTIN(__builtin_add_overflow) |
| return !__builtin_add_overflow(a, b, out_result); |
| #else |
| if (a > IREE_DEVICE_SIZE_MAX - b) return false; |
| *out_result = a + b; |
| return true; |
| #endif |
| } |
| |
| static inline bool iree_device_size_checked_mul( |
| iree_device_size_t a, iree_device_size_t b, |
| iree_device_size_t* out_result) { |
| #if IREE_HAVE_BUILTIN(__builtin_mul_overflow) |
| return !__builtin_mul_overflow(a, b, out_result); |
| #else |
| if (b != 0 && a > IREE_DEVICE_SIZE_MAX / b) return false; |
| *out_result = a * b; |
| return true; |
| #endif |
| } |
| |
| static inline bool iree_device_size_checked_mul_add( |
| iree_device_size_t base, iree_device_size_t count, |
| iree_device_size_t element_size, iree_device_size_t* out_result) { |
| iree_device_size_t product = 0; |
| if (!iree_device_size_checked_mul(count, element_size, &product)) { |
| return false; |
| } |
| return iree_device_size_checked_add(base, product, out_result); |
| } |
| |
| // Aligns |value| up to |alignment| with overflow checking. |
| // |alignment| must be a power of two. |
| // Returns true if the alignment succeeded without overflow, false if overflow |
| // occurred (|out_aligned| is undefined on overflow). |
| static inline bool iree_host_size_checked_align(iree_host_size_t value, |
| iree_host_size_t alignment, |
| iree_host_size_t* out_aligned) { |
| iree_host_size_t padded = 0; |
| if (!iree_host_size_checked_add(value, alignment - 1, &padded)) { |
| return false; |
| } |
| *out_aligned = padded & ~(alignment - 1); |
| return true; |
| } |
| |
| static inline bool iree_device_size_checked_align( |
| iree_device_size_t value, iree_device_size_t alignment, |
| iree_device_size_t* out_aligned) { |
| iree_device_size_t padded = 0; |
| if (!iree_device_size_checked_add(value, alignment - 1, &padded)) { |
| return false; |
| } |
| *out_aligned = padded & ~(alignment - 1); |
| return true; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Struct layout calculation |
| //===----------------------------------------------------------------------===// |
| |
| // Descriptor for a single field in a struct layout calculation. |
| typedef struct iree_struct_field_t { |
| // Element count per dimension ([0]*[1] = total). |
| iree_host_size_t count[2]; |
| // Size of each element. |
| iree_host_size_t element_size; |
| // Required alignment (0 = none). |
| iree_host_size_t alignment; |
| // Output offset pointer (NULL to skip). |
| iree_host_size_t* out_offset; |
| } iree_struct_field_t; |
| |
| // Calculates the total allocation size for a struct with trailing fields. |
| // Each field can optionally capture its offset and specify alignment. |
| // All arithmetic is overflow-checked; returns IREE_STATUS_OUT_OF_RANGE on |
| // overflow. |
| // |
| // Example - struct with two trailing arrays: |
| // iree_host_size_t total = 0, handles_offset = 0, fds_offset = 0; |
| // IREE_RETURN_IF_ERROR(IREE_STRUCT_LAYOUT( |
| // iree_sizeof_struct(*set), &total, |
| // IREE_STRUCT_FIELD(capacity, iree_wait_handle_t, &handles_offset), |
| // IREE_STRUCT_FIELD(capacity, struct pollfd, &fds_offset))); |
| // |
| // Example - header with cache-aligned data: |
| // iree_host_size_t total = 0, data_offset = 0; |
| // IREE_RETURN_IF_ERROR(IREE_STRUCT_LAYOUT( |
| // 0, &total, |
| // IREE_STRUCT_FIELD_ALIGNED(1, header_t, iree_max_align_t, NULL), |
| // IREE_STRUCT_FIELD_ALIGNED(size, uint8_t, |
| // IREE_HAL_HEAP_BUFFER_ALIGNMENT, |
| // &data_offset))); |
| // |
| // Example - 2D array (num_states rows of 256 columns): |
| // iree_host_size_t total = 0, trans_offset = 0; |
| // IREE_RETURN_IF_ERROR(IREE_STRUCT_LAYOUT( |
| // sizeof(header_t), &total, |
| // IREE_STRUCT_ARRAY_FIELD(num_states, 256, uint16_t, &trans_offset))); |
| IREE_ATTRIBUTE_ALWAYS_INLINE static inline iree_status_t |
| iree_struct_layout_calculate(iree_host_size_t base_size, |
| const iree_struct_field_t* fields, |
| iree_host_size_t field_count, |
| iree_host_size_t* out_total) { |
| iree_host_size_t total = base_size; |
| for (iree_host_size_t i = 0; i < field_count; ++i) { |
| const iree_struct_field_t* field = &fields[i]; |
| // Align offset if required. |
| if (field->alignment > 0) { |
| if (IREE_UNLIKELY( |
| !iree_host_size_checked_align(total, field->alignment, &total))) { |
| return iree_make_status(IREE_STATUS_OUT_OF_RANGE, |
| "struct layout alignment overflow"); |
| } |
| } |
| // Record offset before adding this field. |
| if (field->out_offset) { |
| *field->out_offset = total; |
| } |
| // Checked multiply: count[0] * count[1] * element_size. |
| iree_host_size_t element_count = 0; |
| if (IREE_UNLIKELY(!iree_host_size_checked_mul( |
| field->count[0], field->count[1], &element_count))) { |
| return iree_make_status(IREE_STATUS_OUT_OF_RANGE, |
| "struct layout field count overflow"); |
| } |
| iree_host_size_t field_size = 0; |
| if (IREE_UNLIKELY(!iree_host_size_checked_mul( |
| element_count, field->element_size, &field_size))) { |
| return iree_make_status(IREE_STATUS_OUT_OF_RANGE, |
| "struct layout field size overflow"); |
| } |
| // Checked add to running total. |
| if (IREE_UNLIKELY(!iree_host_size_checked_add(total, field_size, &total))) { |
| return iree_make_status(IREE_STATUS_OUT_OF_RANGE, |
| "struct layout total size overflow"); |
| } |
| } |
| *out_total = total; |
| return iree_ok_status(); |
| } |
| |
| // Field descriptor for an unaligned array. |
| #define IREE_STRUCT_FIELD(count_expr, type, out_offset_ptr) \ |
| {{(count_expr), 1}, sizeof(type), 0, (out_offset_ptr)} |
| |
| // Field descriptor for an unaligned 2D array (count1 * count2 elements). |
| // Both count multiplications are overflow-checked. |
| #define IREE_STRUCT_ARRAY_FIELD(count1, count2, type, out_offset_ptr) \ |
| {{(count1), (count2)}, sizeof(type), 0, (out_offset_ptr)} |
| |
| // Field descriptor for an aligned array. |
| #define IREE_STRUCT_FIELD_ALIGNED(count_expr, type, align, out_offset_ptr) \ |
| {{(count_expr), 1}, sizeof(type), (align), (out_offset_ptr)} |
| |
| // Field descriptor for a flexible array member (FAM). FAMs are accessed via |
| // the struct member (e.g., foo->bar[]) so no offset is needed. The alignment |
| // ensures the FAM starts at an address suitable for its element type. |
| #define IREE_STRUCT_FIELD_FAM(count_expr, type) \ |
| {{(count_expr), 1}, sizeof(type), iree_alignof(type), NULL} |
| |
| // Calculates struct layout using inline field descriptors. |
| // C++ version uses a lambda to create a local array (compound literals are a |
| // GCC extension in C++ and taking their address fails with GCC). |
| #ifdef __cplusplus |
| #define IREE_STRUCT_LAYOUT(base_size, out_total, ...) \ |
| [&]() -> iree_status_t { \ |
| const iree_struct_field_t fields[] = {__VA_ARGS__}; \ |
| return iree_struct_layout_calculate( \ |
| (base_size), fields, sizeof(fields) / sizeof(iree_struct_field_t), \ |
| (out_total)); \ |
| }() |
| #else |
| #define IREE_STRUCT_LAYOUT(base_size, out_total, ...) \ |
| iree_struct_layout_calculate((base_size), \ |
| (const iree_struct_field_t[]){__VA_ARGS__}, \ |
| sizeof((iree_struct_field_t[]){__VA_ARGS__}) / \ |
| sizeof(iree_struct_field_t), \ |
| (out_total)) |
| #endif // __cplusplus |
| |
| //===----------------------------------------------------------------------===// |
| // Totally shady stack allocation |
| //===----------------------------------------------------------------------===// |
| // TODO(benvanik): remove our uses of this or make them more explicit. |
| |
| #if defined(IREE_PLATFORM_WINDOWS) |
| // 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 // IREE_COMPILER_MSVC |
| |
| //===----------------------------------------------------------------------===// |
| // iree_allocator_t (std::allocator-like interface) |
| //===----------------------------------------------------------------------===// |
| |
| // Controls the behavior of an iree_allocator_ctl_fn_t callback function. |
| typedef enum iree_allocator_command_e { |
| // Allocates |byte_length| of memory and stores the pointer in |inout_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_ctl_fn_t: |
| // params: iree_allocator_alloc_params_t |
| // inout_ptr: set to allocated pointer |
| IREE_ALLOCATOR_COMMAND_MALLOC = 0, |
| |
| // As with IREE_ALLOCATOR_COMMAND_MALLOC but zeros the memory. |
| // |
| // 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_allocator_ctl_fn_t: |
| // params: iree_allocator_alloc_params_t |
| // inout_ptr: set to allocated pointer |
| IREE_ALLOCATOR_COMMAND_CALLOC = 1, |
| |
| // Tries to resize an allocation provided via |inout_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. Only pointers previously |
| // received from the iree_allocator_t are valid. |
| // |
| // iree_allocator_ctl_fn_t: |
| // params: iree_allocator_alloc_params_t |
| // inout_ptr: pointer of existing allocation; updated to realloced pointer |
| IREE_ALLOCATOR_COMMAND_REALLOC = 2, |
| |
| // Frees the memory pointed to by |inout_ptr|. |
| // |
| // iree_allocator_ctl_fn_t: |
| // params: unused |
| // inout_ptr: pointer to free |
| IREE_ALLOCATOR_COMMAND_FREE = 3, |
| |
| // TODO(benvanik): add optional IREE_ALLOCATOR_COMMAND_BIND like mbind: |
| // https://man7.org/linux/man-pages/man2/mbind.2.html |
| // This would take a pointer/length and a NUMA node ID to bind the memory to. |
| // We may want flags for controlling whether this is a new allocation getting |
| // bound or an existing one that is migrating to use MPOL_MF_MOVE. |
| } iree_allocator_command_t; |
| |
| // Parameters for various allocation commands. |
| typedef struct iree_allocator_alloc_params_t { |
| // Minimum size, in bytes, of the allocation. The underlying allocator may |
| // pad the length out if needed. |
| iree_host_size_t byte_length; |
| } iree_allocator_alloc_params_t; |
| |
| // Function pointer for an iree_allocator_t control function. |
| // |command| provides the operation to perform. Optionally some commands may use |
| // |params| to pass additional operation-specific parameters. |inout_ptr| usage |
| // is defined by each operation but is generally a pointer to the pointer to |
| // set to the newly allocated memory or a pointer to the pointer to free. |
| typedef iree_status_t(IREE_API_PTR* iree_allocator_ctl_fn_t)( |
| void* self, iree_allocator_command_t command, const void* params, |
| void** inout_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 iree_allocator_t { |
| // Control function data. |
| void* self; |
| // ioctl-style control function servicing all allocator-related commands. |
| // See iree_allocator_command_t for more information. |
| iree_allocator_ctl_fn_t ctl; |
| } 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); |
| |
| // Allocates a block of |byte_length| bytes from the given allocator. |
| // The content of the buffer returned is undefined: it may be zeros, a |
| // debug-fill pattern, or random memory from elsewhere in the process. |
| // Only use this when immediately overwriting all memory. |
| IREE_API_EXPORT iree_status_t iree_allocator_malloc_uninitialized( |
| iree_allocator_t allocator, iree_host_size_t byte_length, void** out_ptr); |
| |
| // Reallocates |inout_ptr| to |byte_length| bytes with the given allocator. |
| // If the reallocation fails then the original |inout_ptr| is unmodified. |
| // |
| // WARNING: when extending the newly allocated bytes are undefined. |
| // TODO(benvanik): make them zeros; we should have an _uninitialized if needed. |
| IREE_API_EXPORT iree_status_t iree_allocator_realloc( |
| iree_allocator_t allocator, iree_host_size_t byte_length, void** inout_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. |
| // Safe to pass NULL (no-op). |
| IREE_API_EXPORT void iree_allocator_free(iree_allocator_t allocator, void* ptr); |
| |
| //===----------------------------------------------------------------------===// |
| // Array allocation helpers with overflow checking |
| //===----------------------------------------------------------------------===// |
| |
| // Allocates memory for |count| elements of |element_size| bytes each. |
| // The contents of the returned memory is guaranteed to be zeroed. |
| // Returns IREE_STATUS_OUT_OF_RANGE if the size calculation overflows. |
| IREE_API_EXPORT iree_status_t |
| iree_allocator_malloc_array(iree_allocator_t allocator, iree_host_size_t count, |
| iree_host_size_t element_size, void** out_ptr); |
| |
| // Allocates memory for |count| elements of |element_size| bytes each. |
| // The content of the buffer returned is undefined. |
| // Returns IREE_STATUS_OUT_OF_RANGE if the size calculation overflows. |
| IREE_API_EXPORT iree_status_t iree_allocator_malloc_array_uninitialized( |
| iree_allocator_t allocator, iree_host_size_t count, |
| iree_host_size_t element_size, void** out_ptr); |
| |
| // Reallocates memory for |count| elements of |element_size| bytes each. |
| // Returns IREE_STATUS_OUT_OF_RANGE if the size calculation overflows. |
| IREE_API_EXPORT iree_status_t |
| iree_allocator_realloc_array(iree_allocator_t allocator, iree_host_size_t count, |
| iree_host_size_t element_size, void** inout_ptr); |
| |
| // Allocates memory for a structure of |struct_size| bytes followed by |
| // |trailing_size| bytes of additional data. The combined allocation is |
| // aligned to iree_max_align_t. |
| // Returns IREE_STATUS_OUT_OF_RANGE if the size calculation overflows. |
| IREE_API_EXPORT iree_status_t iree_allocator_malloc_with_trailing( |
| iree_allocator_t allocator, iree_host_size_t struct_size, |
| iree_host_size_t trailing_size, void** out_ptr); |
| |
| // Allocates memory for a structure of |struct_size| bytes followed by an |
| // array of |count| elements of |element_size| bytes each. |
| // Returns IREE_STATUS_OUT_OF_RANGE if the size calculation overflows. |
| IREE_API_EXPORT iree_status_t iree_allocator_malloc_struct_array( |
| iree_allocator_t allocator, iree_host_size_t struct_size, |
| iree_host_size_t count, iree_host_size_t element_size, void** out_ptr); |
| |
| // Grows an array allocation using a 2x doubling strategy. |
| // The new capacity is max(|minimum_capacity|, |*inout_capacity| * 2). |
| // On success |*inout_capacity| is updated and |*inout_ptr| is reallocated. |
| // Returns IREE_STATUS_OUT_OF_RANGE if the capacity calculation overflows. |
| IREE_API_EXPORT iree_status_t iree_allocator_grow_array( |
| iree_allocator_t allocator, iree_host_size_t minimum_capacity, |
| iree_host_size_t element_size, iree_host_size_t* inout_capacity, |
| void** inout_ptr); |
| |
| //===----------------------------------------------------------------------===// |
| // Built-in iree_allocator_t implementations |
| //===----------------------------------------------------------------------===// |
| |
| // 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(void) { |
| iree_allocator_t v = {NULL, NULL}; |
| return v; |
| } |
| |
| // Returns true if the allocator is `iree_allocator_null()`. |
| static inline bool iree_allocator_is_null(iree_allocator_t allocator) { |
| return allocator.ctl == NULL; |
| } |
| |
| typedef struct { |
| iree_host_size_t capacity; |
| iree_host_size_t length; |
| iree_host_size_t head_size; |
| uint8_t* buffer; |
| } iree_allocator_inline_storage_t; |
| |
| // Stack storage for an inline arena-style allocator. |
| // |
| // Usage: |
| // IREE_ALLOCATOR_INLINE_STORAGE(inline_storage, 2048); |
| // something_allocating(iree_allocator_inline_arena(&inline_storage.header)); |
| #define IREE_ALLOCATOR_INLINE_STORAGE(var, storage_capacity) \ |
| struct { \ |
| iree_allocator_inline_storage_t header; \ |
| uint8_t data[storage_capacity]; \ |
| } var = { \ |
| .header = \ |
| { \ |
| .capacity = sizeof((var).data), \ |
| .length = 0, \ |
| .buffer = &(var).data[0], \ |
| }, \ |
| }; |
| |
| // Inline arena allocator controller used by iree_allocator_inline_arena. |
| IREE_API_EXPORT iree_status_t |
| iree_allocator_inline_arena_ctl(void* self, iree_allocator_command_t command, |
| const void* params, void** inout_ptr); |
| |
| // Allocates with arena semantics within the given fixed-size |storage|. |
| // Frees are ignored and all allocations will fail once the allocated length |
| // exceeds the capacity. A special case for reallocations of the entire |
| // outstanding memory is supported to allow the arena to be implicitly reset. |
| static inline iree_allocator_t iree_allocator_inline_arena( |
| iree_allocator_inline_storage_t* storage) { |
| iree_allocator_t v = {storage, iree_allocator_inline_arena_ctl}; |
| return v; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // System allocator support (builtin or user-provided) |
| //===----------------------------------------------------------------------===// |
| |
| #if defined(IREE_ALLOCATOR_SYSTEM_CTL) |
| |
| // System allocator provided by the user as part of build-time configuration. |
| // The implementation need only be linked into final executable binaries. |
| IREE_API_EXPORT iree_status_t |
| IREE_ALLOCATOR_SYSTEM_CTL(void* self, iree_allocator_command_t command, |
| const void* params, void** inout_ptr); |
| |
| #if defined(IREE_ALLOCATOR_SYSTEM_SELF) |
| // Optional self for the default allocator. |
| // Must be defined but may be NULL. |
| IREE_API_EXPORT void* IREE_ALLOCATOR_SYSTEM_SELF; |
| #else |
| #define IREE_ALLOCATOR_SYSTEM_SELF NULL |
| #endif // IREE_ALLOCATOR_SYSTEM_SELF |
| |
| // System allocator provided by the user as part of build-time configuration |
| // (or a fallback of `malloc` and `free`). |
| // |
| // Specified by defining `IREE_ALLOCATOR_SYSTEM_CTL`, an implementation of the |
| // allocator control function (see `iree_allocator_ctl_fn_t`). An optional |
| // `IREE_ALLOCATOR_SYSTEM_SELF` global `void*` variable can be defined if the |
| // allocator requires state and otherwise `NULL` will be passed as the `self` |
| // parameter to the control function. |
| static inline iree_allocator_t iree_allocator_system(void) { |
| iree_allocator_t v = { |
| IREE_ALLOCATOR_SYSTEM_SELF, |
| IREE_ALLOCATOR_SYSTEM_CTL, |
| }; |
| return v; |
| } |
| |
| #endif // IREE_ALLOCATOR_SYSTEM_CTL |
| |
| //===----------------------------------------------------------------------===// |
| // Aligned allocations via iree_allocator_t |
| //===----------------------------------------------------------------------===// |
| |
| // Allocates memory of size |byte_length| where the byte starting at |offset| |
| // has a minimum alignment of |min_alignment|. In many cases |offset| can be 0. |
| // |
| // The |offset| can be used to ensure the alignment-sensitive portion of a |
| // combined allocation is aligned while any prefix metadata has system |
| // alignment. For example: |
| // typedef struct { |
| // uint32_t some_metadata; |
| // uint8_t data[]; |
| // } buffer_t; |
| // buffer_t* buffer = NULL; |
| // iree_allocator_malloc_aligned(allocator, sizeof(buffer_t) + length, |
| // 4096, offsetof(buffer_t, data), &buffer); |
| // // `buffer` has system alignment, but the `data` will be aligned on at |
| // // least a 4096 boundary. |
| // |
| // The contents of the returned memory is guaranteed to be zeroed. |
| IREE_API_EXPORT iree_status_t iree_allocator_malloc_aligned( |
| iree_allocator_t allocator, iree_host_size_t byte_length, |
| iree_host_size_t min_alignment, iree_host_size_t offset, void** out_ptr); |
| |
| // Reallocates memory to |byte_length|, growing or shrinking as needed. |
| // Only valid on memory allocated with iree_allocator_malloc_aligned. |
| // The newly reallocated memory will have the byte at |offset| aligned to at |
| // least |min_alignment|. |
| IREE_API_EXPORT iree_status_t iree_allocator_realloc_aligned( |
| iree_allocator_t allocator, iree_host_size_t byte_length, |
| iree_host_size_t min_alignment, iree_host_size_t offset, void** inout_ptr); |
| |
| // Frees a |ptr| previously returned from iree_allocator_malloc_aligned. |
| IREE_API_EXPORT void iree_allocator_free_aligned(iree_allocator_t allocator, |
| void* ptr); |
| |
| #ifdef __cplusplus |
| } // extern "C" |
| #endif // __cplusplus |
| |
| #endif // IREE_BASE_ALLOCATOR_H_ |