blob: 3a7e24cd7310188bbb6f48711e1751944d2904a1 [file] [log] [blame]
// Copyright 2021 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
#include "iree/vm/buffer.h"
#include <stddef.h>
#include <string.h>
#include "iree/base/tracing.h"
#include "iree/vm/instance.h"
IREE_VM_DEFINE_TYPE_ADAPTERS(iree_vm_buffer, iree_vm_buffer_t);
static iree_status_t iree_vm_buffer_map(const iree_vm_buffer_t* buffer,
iree_host_size_t offset,
iree_host_size_t length,
iree_host_size_t alignment,
uint8_t** out_data,
iree_host_size_t* out_data_length) {
// Force alignment.
offset &= ~(alignment - 1);
length &= ~(alignment - 1);
const iree_host_size_t end = offset + length;
if (IREE_UNLIKELY(end > buffer->data.data_length)) {
return iree_make_status(IREE_STATUS_OUT_OF_RANGE,
"out-of-bounds access detected (offset=%zu, "
"length=%zu, alignment=%zu, buffer length=%zu)",
offset, length, alignment,
buffer->data.data_length);
}
*out_data = buffer->data.data + offset;
*out_data_length = length;
return iree_ok_status();
}
IREE_API_EXPORT iree_status_t
iree_vm_buffer_map_ro(const iree_vm_buffer_t* buffer, iree_host_size_t offset,
iree_host_size_t length, iree_host_size_t alignment,
iree_const_byte_span_t* out_span) {
// Always allowed regardless of access.
return iree_vm_buffer_map(buffer, offset, length, alignment,
(uint8_t**)&out_span->data, &out_span->data_length);
}
// Maps a subrange to a span of bytes within the |buffer| for read/write access.
// |offset| and |length| must match the provided |alignment| (1, 2, 4, 8) and
// will be rounded toward zero if they do not.
IREE_API_EXPORT iree_status_t
iree_vm_buffer_map_rw(const iree_vm_buffer_t* buffer, iree_host_size_t offset,
iree_host_size_t length, iree_host_size_t alignment,
iree_byte_span_t* out_span) {
// Buffer requires mutable access.
if (IREE_UNLIKELY(
!iree_all_bits_set(buffer->access, IREE_VM_BUFFER_ACCESS_MUTABLE))) {
return iree_make_status(
IREE_STATUS_PERMISSION_DENIED,
"buffer is read-only and cannot be mapped for mutation");
}
return iree_vm_buffer_map(buffer, offset, length, alignment, &out_span->data,
&out_span->data_length);
}
IREE_API_EXPORT void iree_vm_buffer_initialize(iree_vm_buffer_access_t access,
iree_byte_span_t data,
iree_allocator_t allocator,
iree_vm_buffer_t* out_buffer) {
IREE_ASSERT_ARGUMENT(out_buffer);
iree_atomic_ref_count_init(&out_buffer->ref_object.counter);
out_buffer->access = access;
out_buffer->data = data;
out_buffer->allocator = allocator;
}
IREE_API_EXPORT void iree_vm_buffer_deinitialize(iree_vm_buffer_t* buffer) {
IREE_ASSERT_ARGUMENT(buffer);
iree_atomic_ref_count_abort_if_uses(&buffer->ref_object.counter);
iree_allocator_free(buffer->allocator, buffer->data.data);
}
IREE_API_EXPORT iree_status_t
iree_vm_buffer_create(iree_vm_buffer_access_t access, iree_host_size_t length,
iree_host_size_t alignment, iree_allocator_t allocator,
iree_vm_buffer_t** out_buffer) {
IREE_ASSERT_ARGUMENT(out_buffer);
*out_buffer = NULL;
IREE_TRACE_ZONE_BEGIN(z0);
// The actual buffer payload is prefixed with the buffer type so we need only
// a single allocation.
iree_host_size_t prefix_size = iree_sizeof_struct(**out_buffer);
iree_host_size_t total_size = prefix_size + length;
// Allocate combined [prefix | buffer] memory.
uint8_t* data_ptr = NULL;
IREE_RETURN_AND_END_ZONE_IF_ERROR(
z0, iree_allocator_malloc_aligned(allocator, total_size, alignment,
prefix_size, (void**)&data_ptr));
// Initialize the prefix buffer handle.
iree_vm_buffer_t* buffer = (iree_vm_buffer_t*)data_ptr;
memset(data_ptr, 0, prefix_size - sizeof(*buffer)); // padding
iree_byte_span_t target_span =
iree_make_byte_span(data_ptr + prefix_size, length);
iree_vm_buffer_initialize(access, target_span, allocator, buffer);
*out_buffer = buffer;
IREE_TRACE_ZONE_END(z0);
return iree_ok_status();
}
static void iree_vm_buffer_destroy(void* ptr) {
IREE_TRACE_ZONE_BEGIN(z0);
// Buffers are stored as [prefix | data]; freeing the prefix is all we need
// to do to free it all.
iree_vm_buffer_t* buffer = (iree_vm_buffer_t*)ptr;
iree_allocator_free_aligned(buffer->allocator, buffer);
IREE_TRACE_ZONE_END(z0);
}
IREE_API_EXPORT void iree_vm_buffer_retain(iree_vm_buffer_t* buffer) {
iree_vm_ref_object_retain(buffer, &iree_vm_buffer_descriptor);
}
IREE_API_EXPORT void iree_vm_buffer_release(iree_vm_buffer_t* buffer) {
iree_vm_ref_object_release(buffer, &iree_vm_buffer_descriptor);
}
IREE_API_EXPORT iree_status_t iree_vm_buffer_clone(
iree_vm_buffer_access_t access, const iree_vm_buffer_t* source_buffer,
iree_host_size_t source_offset, iree_host_size_t length,
iree_host_size_t alignment, iree_allocator_t allocator,
iree_vm_buffer_t** out_buffer) {
IREE_ASSERT_ARGUMENT(source_buffer);
IREE_ASSERT_ARGUMENT(out_buffer);
*out_buffer = NULL;
IREE_TRACE_ZONE_BEGIN(z0);
// Try to map the source buffer first; no use continuing if we can't read the
// data to clone.
iree_const_byte_span_t source_span = iree_const_byte_span_empty();
IREE_RETURN_AND_END_ZONE_IF_ERROR(
z0, iree_vm_buffer_map_ro(source_buffer, source_offset, length, 1,
&source_span));
// The actual buffer payload is prefixed with the buffer type so we need only
// a single allocation.
iree_host_size_t prefix_size =
iree_host_align(sizeof(iree_vm_buffer_t), iree_max_align_t);
iree_host_size_t total_size = prefix_size + source_span.data_length;
// Allocate combined [prefix | buffer] memory.
// TODO(benvanik): uninitialized allocation here to avoid the memset(0).
uint8_t* data_ptr = NULL;
IREE_RETURN_AND_END_ZONE_IF_ERROR(
z0, iree_allocator_malloc_aligned(allocator, total_size, alignment,
prefix_size, (void**)&data_ptr));
// Initialize the prefix buffer handle.
iree_vm_buffer_t* buffer = (iree_vm_buffer_t*)data_ptr;
memset(data_ptr, 0, prefix_size - sizeof(*buffer)); // padding
iree_byte_span_t target_span =
iree_make_byte_span(data_ptr + prefix_size, length);
iree_vm_buffer_initialize(access, target_span, allocator, buffer);
// Copy the data from the source buffer.
memcpy(target_span.data, source_span.data, target_span.data_length);
*out_buffer = buffer;
IREE_TRACE_ZONE_END(z0);
return iree_ok_status();
}
IREE_API_EXPORT iree_host_size_t
iree_vm_buffer_length(const iree_vm_buffer_t* buffer) {
IREE_ASSERT_ARGUMENT(buffer);
return buffer->data.data_length;
}
IREE_API_EXPORT uint8_t* iree_vm_buffer_data(const iree_vm_buffer_t* buffer) {
IREE_ASSERT_ARGUMENT(buffer);
return buffer->data.data;
}
IREE_API_EXPORT iree_byte_span_t
iree_vm_buffer_contents(const iree_vm_buffer_t* buffer) {
// Buffer requires mutable access.
if (!buffer ||
!iree_all_bits_set(buffer->access, IREE_VM_BUFFER_ACCESS_MUTABLE)) {
return iree_byte_span_empty();
}
return iree_make_byte_span(iree_vm_buffer_data(buffer),
iree_vm_buffer_length(buffer));
}
IREE_API_EXPORT iree_const_byte_span_t
iree_vm_buffer_const_contents(const iree_vm_buffer_t* buffer) {
return buffer ? iree_make_const_byte_span(iree_vm_buffer_data(buffer),
iree_vm_buffer_length(buffer))
: iree_const_byte_span_empty();
}
IREE_API_EXPORT iree_status_t iree_vm_buffer_copy_bytes(
const iree_vm_buffer_t* source_buffer, iree_host_size_t source_offset,
const iree_vm_buffer_t* target_buffer, iree_host_size_t target_offset,
iree_host_size_t length) {
IREE_ASSERT_ARGUMENT(source_buffer);
IREE_ASSERT_ARGUMENT(target_buffer);
iree_const_byte_span_t source_span = iree_const_byte_span_empty();
IREE_RETURN_IF_ERROR(iree_vm_buffer_map_ro(source_buffer, source_offset,
length, 1, &source_span));
iree_byte_span_t target_span = iree_byte_span_empty();
IREE_RETURN_IF_ERROR(iree_vm_buffer_map_rw(target_buffer, target_offset,
length, 1, &target_span));
memcpy(target_span.data, source_span.data, length);
return iree_ok_status();
}
IREE_API_EXPORT iree_status_t iree_vm_buffer_compare_bytes(
const iree_vm_buffer_t* lhs_buffer, iree_host_size_t lhs_offset,
const iree_vm_buffer_t* rhs_buffer, iree_host_size_t rhs_offset,
iree_host_size_t length, bool* out_result) {
IREE_ASSERT_ARGUMENT(lhs_buffer);
IREE_ASSERT_ARGUMENT(rhs_buffer);
iree_const_byte_span_t lhs_span = iree_const_byte_span_empty();
IREE_RETURN_IF_ERROR(
iree_vm_buffer_map_ro(lhs_buffer, lhs_offset, length, 1, &lhs_span));
iree_const_byte_span_t rhs_span = iree_const_byte_span_empty();
IREE_RETURN_IF_ERROR(
iree_vm_buffer_map_ro(rhs_buffer, rhs_offset, length, 1, &rhs_span));
*out_result = memcmp(lhs_span.data, rhs_span.data, length) == 0;
return iree_ok_status();
}
IREE_API_EXPORT iree_status_t iree_vm_buffer_fill_bytes(
const iree_vm_buffer_t* target_buffer, iree_host_size_t target_offset,
iree_host_size_t length, uint8_t value) {
return iree_vm_buffer_fill_elements(target_buffer, target_offset, length, 1,
&value);
}
IREE_API_EXPORT iree_status_t iree_vm_buffer_fill_elements(
const iree_vm_buffer_t* target_buffer, iree_host_size_t target_offset,
iree_host_size_t element_count, iree_host_size_t element_length,
const void* value) {
IREE_ASSERT_ARGUMENT(target_buffer);
iree_byte_span_t span = iree_byte_span_empty();
IREE_RETURN_IF_ERROR(iree_vm_buffer_map_rw(
target_buffer, target_offset * element_length,
element_count * element_length, element_length, &span));
switch (element_length) {
case 1: {
const uint8_t pattern_value = *(const uint8_t*)value;
memset(span.data, pattern_value, span.data_length);
} break;
case 2: {
const uint16_t pattern_value = *(const uint16_t*)value;
uint16_t* target_ptr = (uint16_t*)span.data;
for (iree_host_size_t i = 0; i < element_count; ++i) {
target_ptr[i] = pattern_value;
}
} break;
case 4: {
const uint32_t pattern_value = *(const uint32_t*)value;
uint32_t* target_ptr = (uint32_t*)span.data;
for (iree_host_size_t i = 0; i < element_count; ++i) {
target_ptr[i] = pattern_value;
}
} break;
case 8: {
const uint64_t pattern_value = *(const uint64_t*)value;
uint64_t* target_ptr = (uint64_t*)span.data;
for (iree_host_size_t i = 0; i < element_count; ++i) {
target_ptr[i] = pattern_value;
}
} break;
default:
return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
"invalid element length %" PRIhsz
"; expected one of [1, 2, 4, 8]",
element_length);
}
return iree_ok_status();
}
IREE_API_EXPORT iree_status_t iree_vm_buffer_read_elements(
const iree_vm_buffer_t* source_buffer, iree_host_size_t source_offset,
void* target_ptr, iree_host_size_t element_count,
iree_host_size_t element_length) {
IREE_ASSERT_ARGUMENT(source_buffer);
iree_const_byte_span_t source_span = iree_const_byte_span_empty();
IREE_RETURN_IF_ERROR(iree_vm_buffer_map_ro(
source_buffer, source_offset * element_length,
element_count * element_length, element_length, &source_span));
memcpy(target_ptr, source_span.data, source_span.data_length);
return iree_ok_status();
}
IREE_API_EXPORT iree_status_t iree_vm_buffer_write_elements(
const void* source_ptr, const iree_vm_buffer_t* target_buffer,
iree_host_size_t target_offset, iree_host_size_t element_count,
iree_host_size_t element_length) {
IREE_ASSERT_ARGUMENT(source_ptr);
IREE_ASSERT_ARGUMENT(target_buffer);
iree_byte_span_t target_span = iree_byte_span_empty();
IREE_RETURN_IF_ERROR(iree_vm_buffer_map_rw(
target_buffer, target_offset * element_length,
element_count * element_length, element_length, &target_span));
memcpy(target_span.data, source_ptr, target_span.data_length);
return iree_ok_status();
}
iree_status_t iree_vm_buffer_register_types(iree_vm_instance_t* instance) {
if (iree_vm_buffer_descriptor.type != IREE_VM_REF_TYPE_NULL) {
// Already registered.
return iree_ok_status();
}
iree_vm_buffer_descriptor.destroy = iree_vm_buffer_destroy;
iree_vm_buffer_descriptor.offsetof_counter =
offsetof(iree_vm_buffer_t, ref_object.counter);
iree_vm_buffer_descriptor.type_name = iree_make_cstring_view("vm.buffer");
return iree_vm_ref_register_type(&iree_vm_buffer_descriptor);
}