| // Copyright 2020 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. |
| |
| #include "iree/hal/buffer.h" |
| |
| #include "iree/base/tracing.h" |
| #include "iree/hal/allocator.h" |
| #include "iree/hal/detail.h" |
| |
| #define _VTABLE_DISPATCH(buffer, method_name) \ |
| IREE_HAL_VTABLE_DISPATCH(buffer, iree_hal_buffer, method_name) |
| |
| //===----------------------------------------------------------------------===// |
| // Subspan indirection buffer |
| //===----------------------------------------------------------------------===// |
| |
| static const iree_hal_buffer_vtable_t iree_hal_subspan_buffer_vtable; |
| |
| static iree_status_t iree_hal_subspan_buffer_create( |
| iree_hal_buffer_t* allocated_buffer, iree_device_size_t byte_offset, |
| iree_device_size_t byte_length, iree_hal_buffer_t** out_buffer) { |
| IREE_ASSERT_ARGUMENT(allocated_buffer); |
| IREE_ASSERT_ARGUMENT(out_buffer); |
| IREE_TRACE_ZONE_BEGIN(z0); |
| |
| iree_hal_buffer_t* buffer = NULL; |
| iree_status_t status = iree_allocator_malloc( |
| iree_hal_allocator_host_allocator(allocated_buffer->allocator), |
| sizeof(*buffer), (void**)&buffer); |
| if (iree_status_is_ok(status)) { |
| iree_hal_resource_initialize(&iree_hal_subspan_buffer_vtable, |
| &buffer->resource); |
| buffer->allocator = allocated_buffer->allocator; |
| buffer->allocated_buffer = allocated_buffer; |
| buffer->allocation_size = allocated_buffer->allocation_size; |
| buffer->byte_offset = byte_offset; |
| buffer->byte_length = byte_length; |
| buffer->memory_type = allocated_buffer->memory_type; |
| buffer->allowed_access = allocated_buffer->allowed_access; |
| buffer->allowed_usage = allocated_buffer->allowed_usage; |
| *out_buffer = buffer; |
| } |
| |
| IREE_TRACE_ZONE_END(z0); |
| return iree_ok_status(); |
| } |
| |
| static void iree_hal_subspan_buffer_destroy(iree_hal_buffer_t* base_buffer) { |
| iree_allocator_t host_allocator = |
| iree_hal_allocator_host_allocator(iree_hal_buffer_allocator(base_buffer)); |
| IREE_TRACE_ZONE_BEGIN(z0); |
| |
| iree_hal_buffer_release(base_buffer->allocated_buffer); |
| iree_allocator_free(host_allocator, base_buffer); |
| |
| IREE_TRACE_ZONE_END(z0); |
| } |
| |
| static iree_status_t iree_hal_subspan_buffer_fill( |
| iree_hal_buffer_t* buffer, iree_device_size_t byte_offset, |
| iree_device_size_t byte_length, const void* pattern, |
| iree_host_size_t pattern_length) { |
| return _VTABLE_DISPATCH(buffer, fill)(buffer->allocated_buffer, byte_offset, |
| byte_length, pattern, pattern_length); |
| } |
| |
| static iree_status_t iree_hal_subspan_buffer_read_data( |
| iree_hal_buffer_t* buffer, iree_device_size_t source_offset, |
| void* target_buffer, iree_device_size_t data_length) { |
| return _VTABLE_DISPATCH(buffer, read_data)( |
| buffer->allocated_buffer, source_offset, target_buffer, data_length); |
| } |
| |
| static iree_status_t iree_hal_subspan_buffer_write_data( |
| iree_hal_buffer_t* buffer, iree_device_size_t target_offset, |
| const void* source_buffer, iree_device_size_t data_length) { |
| return _VTABLE_DISPATCH(buffer, write_data)( |
| buffer->allocated_buffer, target_offset, source_buffer, data_length); |
| } |
| |
| static iree_status_t iree_hal_subspan_buffer_copy_data( |
| iree_hal_buffer_t* source_buffer, iree_device_size_t source_offset, |
| iree_hal_buffer_t* target_buffer, iree_device_size_t target_offset, |
| iree_device_size_t data_length) { |
| return _VTABLE_DISPATCH(target_buffer, copy_data)( |
| source_buffer, source_offset, target_buffer->allocated_buffer, |
| target_offset, data_length); |
| } |
| |
| static iree_status_t iree_hal_subspan_buffer_map_range( |
| iree_hal_buffer_t* buffer, iree_hal_mapping_mode_t mapping_mode, |
| iree_hal_memory_access_t memory_access, |
| iree_device_size_t local_byte_offset, iree_device_size_t local_byte_length, |
| void** out_data_ptr) { |
| return _VTABLE_DISPATCH(buffer, map_range)(buffer, mapping_mode, |
| memory_access, local_byte_offset, |
| local_byte_length, out_data_ptr); |
| } |
| |
| static void iree_hal_subspan_buffer_unmap_range( |
| iree_hal_buffer_t* buffer, iree_device_size_t local_byte_offset, |
| iree_device_size_t local_byte_length, void* data_ptr) { |
| return _VTABLE_DISPATCH(buffer, unmap_range)(buffer, local_byte_offset, |
| local_byte_length, data_ptr); |
| } |
| |
| static iree_status_t iree_hal_subspan_buffer_invalidate_range( |
| iree_hal_buffer_t* buffer, iree_device_size_t local_byte_offset, |
| iree_device_size_t local_byte_length) { |
| return _VTABLE_DISPATCH(buffer, invalidate_range)(buffer, local_byte_offset, |
| local_byte_length); |
| } |
| |
| static iree_status_t iree_hal_subspan_buffer_flush_range( |
| iree_hal_buffer_t* buffer, iree_device_size_t local_byte_offset, |
| iree_device_size_t local_byte_length) { |
| return _VTABLE_DISPATCH(buffer, flush_range)(buffer, local_byte_offset, |
| local_byte_length); |
| } |
| |
| static const iree_hal_buffer_vtable_t iree_hal_subspan_buffer_vtable = { |
| .destroy = iree_hal_subspan_buffer_destroy, |
| .fill = iree_hal_subspan_buffer_fill, |
| .read_data = iree_hal_subspan_buffer_read_data, |
| .write_data = iree_hal_subspan_buffer_write_data, |
| .copy_data = iree_hal_subspan_buffer_copy_data, |
| .map_range = iree_hal_subspan_buffer_map_range, |
| .unmap_range = iree_hal_subspan_buffer_unmap_range, |
| .invalidate_range = iree_hal_subspan_buffer_invalidate_range, |
| .flush_range = iree_hal_subspan_buffer_flush_range, |
| }; |
| |
| //===----------------------------------------------------------------------===// |
| // iree_hal_buffer_t |
| //===----------------------------------------------------------------------===// |
| |
| IREE_HAL_API_RETAIN_RELEASE(buffer); |
| |
| IREE_API_EXPORT iree_status_t IREE_API_CALL |
| iree_hal_buffer_validate_memory_type( |
| iree_hal_memory_type_t actual_memory_type, |
| iree_hal_memory_type_t expected_memory_type) { |
| if (IREE_UNLIKELY( |
| !iree_all_bits_set(actual_memory_type, expected_memory_type))) { |
| // Missing one or more bits. |
| return iree_make_status( |
| IREE_STATUS_PERMISSION_DENIED, |
| "buffer memory type is not compatible with the requested operation; " |
| "buffer has %s, operation requires %s", |
| iree_hal_memory_type_string(actual_memory_type), |
| iree_hal_memory_type_string(expected_memory_type)); |
| } |
| return iree_ok_status(); |
| } |
| |
| IREE_API_EXPORT iree_status_t IREE_API_CALL iree_hal_buffer_validate_access( |
| iree_hal_memory_access_t allowed_memory_access, |
| iree_hal_memory_access_t required_memory_access) { |
| if (IREE_UNLIKELY(!iree_any_bit_set( |
| required_memory_access, |
| IREE_HAL_MEMORY_ACCESS_READ | IREE_HAL_MEMORY_ACCESS_WRITE))) { |
| // No actual access bits defined. |
| return iree_make_status( |
| IREE_STATUS_INVALID_ARGUMENT, |
| "memory access must specify one or more of _READ or _WRITE"); |
| } else if (IREE_UNLIKELY(!iree_all_bits_set(allowed_memory_access, |
| required_memory_access))) { |
| // Bits must match exactly. |
| return iree_make_status( |
| IREE_STATUS_PERMISSION_DENIED, |
| "buffer does not support the requested access " |
| "type; buffer allows %s, operation requires %s", |
| iree_hal_memory_access_string(allowed_memory_access), |
| iree_hal_memory_access_string(required_memory_access)); |
| } |
| return iree_ok_status(); |
| } |
| |
| IREE_API_EXPORT iree_status_t IREE_API_CALL |
| iree_hal_buffer_validate_usage(iree_hal_buffer_usage_t allowed_usage, |
| iree_hal_buffer_usage_t required_usage) { |
| if (IREE_UNLIKELY(!iree_all_bits_set(allowed_usage, required_usage))) { |
| // Missing one or more bits. |
| return iree_make_status( |
| IREE_STATUS_PERMISSION_DENIED, |
| "requested usage was not specified when the buffer was allocated; " |
| "buffer allows %s, operation requires %s", |
| iree_hal_buffer_usage_string(allowed_usage), |
| iree_hal_buffer_usage_string(required_usage)); |
| } |
| return iree_ok_status(); |
| } |
| |
| IREE_API_EXPORT iree_status_t IREE_API_CALL iree_hal_buffer_validate_range( |
| iree_hal_buffer_t* buffer, iree_device_size_t byte_offset, |
| iree_device_size_t byte_length) { |
| // Check if the start of the range runs off the end of the buffer. |
| if (IREE_UNLIKELY(byte_offset > iree_hal_buffer_byte_length(buffer))) { |
| return iree_make_status( |
| IREE_STATUS_OUT_OF_RANGE, |
| "attempted to access an address off the end of the valid buffer range " |
| "(offset=%zu, length=%zu, buffer byte_length=%zu)", |
| byte_offset, byte_length, iree_hal_buffer_byte_length(buffer)); |
| } |
| |
| if (byte_length == 0) { |
| // Fine to have a zero length. |
| return iree_ok_status(); |
| } |
| |
| // Check if the end runs over the allocation. |
| iree_device_size_t end = byte_offset + byte_length; |
| if (IREE_UNLIKELY(end > iree_hal_buffer_byte_length(buffer))) { |
| return iree_make_status( |
| IREE_STATUS_OUT_OF_RANGE, |
| "attempted to access an address outside of the valid buffer range " |
| "(offset=%zu, length=%zu, end(inc)=%zu, buffer byte_length=%zu)", |
| byte_offset, byte_length, end - 1, iree_hal_buffer_byte_length(buffer)); |
| } |
| |
| return iree_ok_status(); |
| } |
| |
| static iree_status_t iree_hal_buffer_calculate_range( |
| iree_device_size_t base_offset, iree_device_size_t max_length, |
| iree_device_size_t offset, iree_device_size_t length, |
| iree_device_size_t* out_adjusted_offset, |
| iree_device_size_t* out_adjusted_length) { |
| // Check if the start of the range runs off the end of the buffer. |
| if (IREE_UNLIKELY(offset > max_length)) { |
| *out_adjusted_offset = 0; |
| if (out_adjusted_length) *out_adjusted_length = 0; |
| return iree_make_status( |
| IREE_STATUS_OUT_OF_RANGE, |
| "attempted to access an address off the end of the valid buffer " |
| "range (offset=%zu, length=%zu, buffer byte_length=%zu)", |
| offset, length, max_length); |
| } |
| |
| // Handle length as IREE_WHOLE_BUFFER by adjusting it (if allowed). |
| if (IREE_UNLIKELY(length == IREE_WHOLE_BUFFER) && |
| IREE_UNLIKELY(!out_adjusted_length)) { |
| *out_adjusted_offset = 0; |
| return iree_make_status(IREE_STATUS_INVALID_ARGUMENT, |
| "IREE_WHOLE_BUFFER may only be used with buffer " |
| "ranges, not external pointer ranges"); |
| } |
| |
| // Calculate the real ranges adjusted for our region within the allocation. |
| iree_device_size_t adjusted_offset = base_offset + offset; |
| iree_device_size_t adjusted_length = |
| length == IREE_WHOLE_BUFFER ? max_length - offset : length; |
| if (adjusted_length == 0) { |
| // Fine to have a zero length. |
| *out_adjusted_offset = adjusted_offset; |
| if (out_adjusted_length) *out_adjusted_length = adjusted_length; |
| return iree_ok_status(); |
| } |
| |
| // Check if the end runs over the allocation. |
| iree_device_size_t end = offset + adjusted_length - 1; |
| if (IREE_UNLIKELY(end >= max_length)) { |
| *out_adjusted_offset = 0; |
| if (out_adjusted_length) *out_adjusted_length = 0; |
| return iree_make_status( |
| IREE_STATUS_OUT_OF_RANGE, |
| "attempted to access an address outside of the valid buffer " |
| "range (offset=%zu, adjusted_length=%zu, end=%zu, buffer " |
| "byte_length=%zu)", |
| offset, adjusted_length, end, max_length); |
| } |
| |
| *out_adjusted_offset = adjusted_offset; |
| if (out_adjusted_length) *out_adjusted_length = adjusted_length; |
| return iree_ok_status(); |
| } |
| |
| IREE_API_EXPORT iree_hal_buffer_overlap_t IREE_API_CALL |
| iree_hal_buffer_test_overlap(iree_hal_buffer_t* lhs_buffer, |
| iree_device_size_t lhs_offset, |
| iree_device_size_t lhs_length, |
| iree_hal_buffer_t* rhs_buffer, |
| iree_device_size_t rhs_offset, |
| iree_device_size_t rhs_length) { |
| if (iree_hal_buffer_allocated_buffer(lhs_buffer) != |
| iree_hal_buffer_allocated_buffer(rhs_buffer)) { |
| // Not even the same buffers. |
| return IREE_HAL_BUFFER_OVERLAP_DISJOINT; |
| } |
| // Resolve offsets into the underlying allocation. |
| iree_device_size_t lhs_alloc_offset = |
| iree_hal_buffer_byte_offset(lhs_buffer) + lhs_offset; |
| iree_device_size_t rhs_alloc_offset = |
| iree_hal_buffer_byte_offset(rhs_buffer) + rhs_offset; |
| iree_device_size_t lhs_alloc_length = |
| lhs_length == IREE_WHOLE_BUFFER |
| ? iree_hal_buffer_byte_length(lhs_buffer) - lhs_offset |
| : lhs_length; |
| iree_device_size_t rhs_alloc_length = |
| rhs_length == IREE_WHOLE_BUFFER |
| ? iree_hal_buffer_byte_length(rhs_buffer) - rhs_offset |
| : rhs_length; |
| if (!lhs_alloc_length || !rhs_alloc_length) { |
| return IREE_HAL_BUFFER_OVERLAP_DISJOINT; |
| } |
| if (lhs_alloc_offset == rhs_alloc_offset && |
| lhs_alloc_length == rhs_alloc_length) { |
| return IREE_HAL_BUFFER_OVERLAP_COMPLETE; |
| } |
| return lhs_alloc_offset + lhs_alloc_length > rhs_alloc_offset && |
| rhs_alloc_offset + rhs_alloc_length > lhs_alloc_offset |
| ? IREE_HAL_BUFFER_OVERLAP_PARTIAL |
| : IREE_HAL_BUFFER_OVERLAP_DISJOINT; |
| } |
| |
| IREE_API_EXPORT iree_status_t IREE_API_CALL iree_hal_buffer_subspan( |
| iree_hal_buffer_t* buffer, iree_device_size_t byte_offset, |
| iree_device_size_t byte_length, iree_hal_buffer_t** out_buffer) { |
| IREE_ASSERT_ARGUMENT(buffer); |
| IREE_ASSERT_ARGUMENT(out_buffer); |
| *out_buffer = NULL; |
| |
| // Fast path: if we are requesting the whole buffer (usually via |
| // IREE_WHOLE_BUFFER) then we can just return the buffer itself. |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_calculate_range( |
| iree_hal_buffer_byte_offset(buffer), iree_hal_buffer_byte_length(buffer), |
| byte_offset, byte_length, &byte_offset, &byte_length)); |
| if (byte_offset == 0 && byte_length == iree_hal_buffer_byte_length(buffer)) { |
| iree_hal_buffer_retain(buffer); |
| *out_buffer = buffer; |
| return iree_ok_status(); |
| } |
| |
| // To avoid heavy nesting of subspans that just add indirection we go to the |
| // parent buffer directly. If we wanted better accounting (to track where |
| // buffers came from) we'd want to avoid this but I'm not sure that's worth |
| // the super deep indirection that could arise. |
| iree_hal_buffer_t* allocated_buffer = |
| iree_hal_buffer_allocated_buffer(buffer); |
| if (allocated_buffer != buffer) { |
| return iree_hal_buffer_subspan(allocated_buffer, byte_offset, byte_length, |
| out_buffer); |
| } |
| |
| return iree_hal_subspan_buffer_create(buffer, byte_offset, byte_length, |
| out_buffer); |
| } |
| |
| IREE_API_EXPORT iree_hal_allocator_t* IREE_API_CALL |
| iree_hal_buffer_allocator(const iree_hal_buffer_t* buffer) { |
| IREE_ASSERT_ARGUMENT(buffer); |
| return buffer->allocator; |
| } |
| |
| IREE_API_EXPORT iree_hal_buffer_t* IREE_API_CALL |
| iree_hal_buffer_allocated_buffer(const iree_hal_buffer_t* buffer) { |
| IREE_ASSERT_ARGUMENT(buffer); |
| return buffer->allocated_buffer; |
| } |
| |
| IREE_API_EXPORT iree_device_size_t IREE_API_CALL |
| iree_hal_buffer_allocation_size(const iree_hal_buffer_t* buffer) { |
| IREE_ASSERT_ARGUMENT(buffer); |
| return buffer->allocation_size; |
| } |
| |
| IREE_API_EXPORT iree_device_size_t IREE_API_CALL |
| iree_hal_buffer_byte_offset(const iree_hal_buffer_t* buffer) { |
| IREE_ASSERT_ARGUMENT(buffer); |
| return buffer->byte_offset; |
| } |
| |
| IREE_API_EXPORT iree_device_size_t IREE_API_CALL |
| iree_hal_buffer_byte_length(const iree_hal_buffer_t* buffer) { |
| IREE_ASSERT_ARGUMENT(buffer); |
| return buffer->byte_length; |
| } |
| |
| IREE_API_EXPORT |
| iree_hal_memory_type_t IREE_API_CALL |
| iree_hal_buffer_memory_type(const iree_hal_buffer_t* buffer) { |
| IREE_ASSERT_ARGUMENT(buffer); |
| return buffer->memory_type; |
| } |
| |
| IREE_API_EXPORT |
| iree_hal_memory_access_t IREE_API_CALL |
| iree_hal_buffer_allowed_access(const iree_hal_buffer_t* buffer) { |
| IREE_ASSERT_ARGUMENT(buffer); |
| return buffer->allowed_access; |
| } |
| |
| IREE_API_EXPORT |
| iree_hal_buffer_usage_t IREE_API_CALL |
| iree_hal_buffer_allowed_usage(const iree_hal_buffer_t* buffer) { |
| IREE_ASSERT_ARGUMENT(buffer); |
| return buffer->allowed_usage; |
| } |
| |
| IREE_API_EXPORT iree_status_t IREE_API_CALL |
| iree_hal_buffer_zero(iree_hal_buffer_t* buffer, iree_device_size_t byte_offset, |
| iree_device_size_t byte_length) { |
| const uint8_t zero = 0; |
| return iree_hal_buffer_fill(buffer, byte_offset, byte_length, &zero, 1); |
| } |
| |
| IREE_API_EXPORT iree_status_t IREE_API_CALL |
| iree_hal_buffer_fill(iree_hal_buffer_t* buffer, iree_device_size_t byte_offset, |
| iree_device_size_t byte_length, const void* pattern, |
| iree_host_size_t pattern_length) { |
| IREE_ASSERT_ARGUMENT(buffer); |
| IREE_ASSERT_ARGUMENT(pattern); |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_validate_memory_type( |
| iree_hal_buffer_memory_type(buffer), IREE_HAL_MEMORY_TYPE_HOST_VISIBLE)); |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_validate_access( |
| iree_hal_buffer_allowed_access(buffer), IREE_HAL_MEMORY_ACCESS_WRITE)); |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_validate_usage( |
| iree_hal_buffer_allowed_usage(buffer), IREE_HAL_BUFFER_USAGE_MAPPING)); |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_calculate_range( |
| iree_hal_buffer_byte_offset(buffer), iree_hal_buffer_byte_length(buffer), |
| byte_offset, byte_length, &byte_offset, &byte_length)); |
| if (IREE_UNLIKELY(pattern_length != 1 && pattern_length != 2 && |
| pattern_length != 4)) { |
| return iree_make_status(IREE_STATUS_INVALID_ARGUMENT, |
| "fill patterns must be 1, 2, or 4 bytes (got %zu)", |
| pattern_length); |
| } |
| if (IREE_UNLIKELY((byte_offset % pattern_length) != 0) || |
| IREE_UNLIKELY((byte_length % pattern_length) != 0)) { |
| return iree_make_status(IREE_STATUS_INVALID_ARGUMENT, |
| "attempting to fill a range with %zu byte values " |
| "that is not aligned (offset=%zu, length=%zu)", |
| pattern_length, byte_offset, byte_length); |
| } |
| if (byte_length == 0) { |
| return iree_ok_status(); // No-op. |
| } |
| const uint32_t zero_32 = 0; |
| if (memcmp(pattern, &zero_32, pattern_length) == 0) { |
| // We can turn all-zero values into single-byte fills as that can be much |
| // faster on devices (doing a fill8 vs fill32). |
| pattern_length = 1; |
| } |
| |
| IREE_TRACE_ZONE_BEGIN(z0); |
| iree_status_t status = _VTABLE_DISPATCH(buffer, fill)( |
| buffer, byte_offset, byte_length, pattern, pattern_length); |
| IREE_TRACE_ZONE_END(z0); |
| return status; |
| } |
| |
| IREE_API_EXPORT iree_status_t IREE_API_CALL iree_hal_buffer_read_data( |
| iree_hal_buffer_t* source_buffer, iree_device_size_t source_offset, |
| void* target_buffer, iree_device_size_t data_length) { |
| if (data_length == 0) { |
| return iree_ok_status(); // No-op. |
| } |
| IREE_ASSERT_ARGUMENT(source_buffer); |
| IREE_ASSERT_ARGUMENT(target_buffer); |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_validate_memory_type( |
| iree_hal_buffer_memory_type(source_buffer), |
| IREE_HAL_MEMORY_TYPE_HOST_VISIBLE)); |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_validate_access( |
| iree_hal_buffer_allowed_access(source_buffer), |
| IREE_HAL_MEMORY_ACCESS_READ)); |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_validate_usage( |
| iree_hal_buffer_allowed_usage(source_buffer), |
| IREE_HAL_BUFFER_USAGE_MAPPING)); |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_calculate_range( |
| iree_hal_buffer_byte_offset(source_buffer), |
| iree_hal_buffer_byte_length(source_buffer), source_offset, data_length, |
| &source_offset, NULL)); |
| IREE_TRACE_ZONE_BEGIN(z0); |
| iree_status_t status = _VTABLE_DISPATCH(source_buffer, read_data)( |
| source_buffer, source_offset, target_buffer, data_length); |
| IREE_TRACE_ZONE_END(z0); |
| return status; |
| } |
| |
| IREE_API_EXPORT iree_status_t IREE_API_CALL iree_hal_buffer_write_data( |
| iree_hal_buffer_t* target_buffer, iree_device_size_t target_offset, |
| const void* source_buffer, iree_device_size_t data_length) { |
| if (data_length == 0) { |
| return iree_ok_status(); // No-op. |
| } |
| IREE_ASSERT_ARGUMENT(target_buffer); |
| IREE_ASSERT_ARGUMENT(source_buffer); |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_validate_memory_type( |
| iree_hal_buffer_memory_type(target_buffer), |
| IREE_HAL_MEMORY_TYPE_HOST_VISIBLE)); |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_validate_access( |
| iree_hal_buffer_allowed_access(target_buffer), |
| IREE_HAL_MEMORY_ACCESS_WRITE)); |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_validate_usage( |
| iree_hal_buffer_allowed_usage(target_buffer), |
| IREE_HAL_BUFFER_USAGE_MAPPING)); |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_calculate_range( |
| iree_hal_buffer_byte_offset(target_buffer), |
| iree_hal_buffer_byte_length(target_buffer), target_offset, data_length, |
| &target_offset, NULL)); |
| IREE_TRACE_ZONE_BEGIN(z0); |
| iree_status_t status = _VTABLE_DISPATCH(target_buffer, write_data)( |
| target_buffer, target_offset, source_buffer, data_length); |
| IREE_TRACE_ZONE_END(z0); |
| return status; |
| } |
| |
| IREE_API_EXPORT iree_status_t IREE_API_CALL iree_hal_buffer_copy_data( |
| iree_hal_buffer_t* source_buffer, iree_device_size_t source_offset, |
| iree_hal_buffer_t* target_buffer, iree_device_size_t target_offset, |
| iree_device_size_t data_length) { |
| if (data_length == 0) { |
| return iree_ok_status(); // No-op. |
| } |
| IREE_ASSERT_ARGUMENT(source_buffer); |
| IREE_ASSERT_ARGUMENT(target_buffer); |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_validate_memory_type( |
| iree_hal_buffer_memory_type(source_buffer), |
| IREE_HAL_MEMORY_TYPE_HOST_VISIBLE)); |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_validate_access( |
| iree_hal_buffer_allowed_access(source_buffer), |
| IREE_HAL_MEMORY_ACCESS_READ)); |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_validate_usage( |
| iree_hal_buffer_allowed_usage(source_buffer), |
| IREE_HAL_BUFFER_USAGE_MAPPING)); |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_validate_memory_type( |
| iree_hal_buffer_memory_type(target_buffer), |
| IREE_HAL_MEMORY_TYPE_HOST_VISIBLE)); |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_validate_access( |
| iree_hal_buffer_allowed_access(target_buffer), |
| IREE_HAL_MEMORY_ACCESS_WRITE)); |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_validate_usage( |
| iree_hal_buffer_allowed_usage(target_buffer), |
| IREE_HAL_BUFFER_USAGE_MAPPING)); |
| |
| // We need to validate both buffers. |
| iree_device_size_t source_data_length = data_length; |
| iree_device_size_t target_data_length = data_length; |
| iree_device_size_t adjusted_source_offset; |
| iree_device_size_t adjusted_target_offset; |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_calculate_range( |
| iree_hal_buffer_byte_offset(source_buffer), |
| iree_hal_buffer_byte_length(source_buffer), source_offset, |
| source_data_length, &adjusted_source_offset, &source_data_length)); |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_calculate_range( |
| iree_hal_buffer_byte_offset(target_buffer), |
| iree_hal_buffer_byte_length(target_buffer), target_offset, |
| target_data_length, &adjusted_target_offset, &target_data_length)); |
| iree_device_size_t adjusted_data_length; |
| if (data_length == IREE_WHOLE_BUFFER) { |
| // Whole buffer copy requested - that could mean either, so take the min. |
| adjusted_data_length = iree_min(source_data_length, target_data_length); |
| } else { |
| // Specific length requested - validate that we have matching lengths. |
| // IREE_ASSERT_EQ(source_data_length, target_data_length); |
| adjusted_data_length = source_data_length; |
| } |
| |
| // Elide zero length copies. |
| if (adjusted_data_length == 0) { |
| return iree_ok_status(); |
| } |
| |
| // Check for overlap. |
| if (iree_hal_buffer_test_overlap( |
| source_buffer, adjusted_source_offset, adjusted_data_length, |
| target_buffer, adjusted_target_offset, |
| adjusted_data_length) != IREE_HAL_BUFFER_OVERLAP_DISJOINT) { |
| return iree_make_status( |
| IREE_STATUS_INVALID_ARGUMENT, |
| "source and target ranges overlap within the same buffer"); |
| } |
| |
| IREE_TRACE_ZONE_BEGIN(z0); |
| iree_status_t status = _VTABLE_DISPATCH(target_buffer, copy_data)( |
| source_buffer, adjusted_source_offset, target_buffer, |
| adjusted_target_offset, adjusted_data_length); |
| IREE_TRACE_ZONE_END(z0); |
| return status; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Mapping / iree_hal_buffer_mapping_impl_t |
| //===----------------------------------------------------------------------===// |
| |
| typedef struct { |
| // Must be first (as in iree_hal_buffer_mapping_t). |
| // Stores both the offset data pointer and the byte_length of the mapping. |
| iree_byte_span_t contents; |
| // Retained buffer providing the backing storage for the mapping. |
| iree_hal_buffer_t* backing_buffer; |
| // Byte offset within the buffer where the mapped data begins. |
| iree_device_size_t byte_offset; |
| // Used for validation only. |
| iree_hal_memory_access_t allowed_access; |
| uint32_t reserved0; // unused |
| uint64_t reserved1; // unused |
| } iree_hal_buffer_mapping_impl_t; |
| |
| // We overlay the impl onto the external iree_hal_buffer_mapping_t struct; |
| // ensure we match the fields that are exposed. |
| static_assert(sizeof(iree_hal_buffer_mapping_impl_t) <= |
| sizeof(iree_hal_buffer_mapping_t), |
| "buffer mapping impl must fit inside the external struct"); |
| static_assert(offsetof(iree_hal_buffer_mapping_impl_t, contents) == |
| offsetof(iree_hal_buffer_mapping_t, contents), |
| "contents byte span must match the external struct offset"); |
| |
| IREE_API_EXPORT iree_status_t IREE_API_CALL iree_hal_buffer_map_range( |
| iree_hal_buffer_t* buffer, iree_hal_memory_access_t memory_access, |
| iree_device_size_t byte_offset, iree_device_size_t byte_length, |
| iree_hal_buffer_mapping_t* out_buffer_mapping) { |
| IREE_ASSERT_ARGUMENT(buffer); |
| IREE_ASSERT_ARGUMENT(out_buffer_mapping); |
| memset(out_buffer_mapping, 0, sizeof(*out_buffer_mapping)); |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_validate_memory_type( |
| iree_hal_buffer_memory_type(buffer), IREE_HAL_MEMORY_TYPE_HOST_VISIBLE)); |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_validate_access( |
| iree_hal_buffer_allowed_access(buffer), memory_access)); |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_validate_usage( |
| iree_hal_buffer_allowed_usage(buffer), IREE_HAL_BUFFER_USAGE_MAPPING)); |
| |
| iree_hal_buffer_mapping_impl_t* buffer_mapping = |
| (iree_hal_buffer_mapping_impl_t*)out_buffer_mapping; |
| buffer_mapping->backing_buffer = buffer; |
| buffer_mapping->allowed_access = memory_access; |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_calculate_range( |
| iree_hal_buffer_byte_offset(buffer), iree_hal_buffer_byte_length(buffer), |
| byte_offset, byte_length, &buffer_mapping->byte_offset, |
| &buffer_mapping->contents.data_length)); |
| |
| // TODO(benvanik): add mode arg to the HAL API. |
| iree_hal_mapping_mode_t mapping_mode = IREE_HAL_MAPPING_MODE_SCOPED; |
| |
| IREE_TRACE_ZONE_BEGIN(z0); |
| iree_status_t status = _VTABLE_DISPATCH(buffer, map_range)( |
| buffer, mapping_mode, buffer_mapping->allowed_access, |
| buffer_mapping->byte_offset, buffer_mapping->contents.data_length, |
| (void**)&buffer_mapping->contents.data); |
| IREE_TRACE_ZONE_END(z0); |
| return status; |
| } |
| |
| IREE_API_EXPORT void IREE_API_CALL |
| iree_hal_buffer_unmap_range(iree_hal_buffer_mapping_t* base_buffer_mapping) { |
| IREE_ASSERT_ARGUMENT(base_buffer_mapping); |
| iree_hal_buffer_mapping_impl_t* buffer_mapping = |
| (iree_hal_buffer_mapping_impl_t*)base_buffer_mapping; |
| iree_hal_buffer_t* buffer = buffer_mapping->backing_buffer; |
| IREE_TRACE_ZONE_BEGIN(z0); |
| _VTABLE_DISPATCH(buffer, unmap_range) |
| (buffer, buffer_mapping->byte_offset, buffer_mapping->contents.data_length, |
| buffer_mapping->contents.data); |
| IREE_TRACE_ZONE_END(z0); |
| } |
| |
| IREE_API_EXPORT iree_status_t IREE_API_CALL iree_hal_buffer_invalidate_range( |
| iree_hal_buffer_mapping_t* base_buffer_mapping, |
| iree_device_size_t byte_offset, iree_device_size_t byte_length) { |
| IREE_ASSERT_ARGUMENT(base_buffer_mapping); |
| iree_hal_buffer_mapping_impl_t* buffer_mapping = |
| (iree_hal_buffer_mapping_impl_t*)base_buffer_mapping; |
| iree_hal_buffer_t* buffer = buffer_mapping->backing_buffer; |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_validate_access( |
| buffer_mapping->allowed_access, IREE_HAL_MEMORY_ACCESS_READ)); |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_calculate_range( |
| buffer_mapping->byte_offset, buffer_mapping->contents.data_length, |
| byte_offset, byte_length, &byte_offset, &byte_length)); |
| return _VTABLE_DISPATCH(buffer, invalidate_range)(buffer, byte_offset, |
| byte_length); |
| } |
| |
| IREE_API_EXPORT iree_status_t IREE_API_CALL iree_hal_buffer_flush_range( |
| iree_hal_buffer_mapping_t* base_buffer_mapping, |
| iree_device_size_t byte_offset, iree_device_size_t byte_length) { |
| IREE_ASSERT_ARGUMENT(base_buffer_mapping); |
| iree_hal_buffer_mapping_impl_t* buffer_mapping = |
| (iree_hal_buffer_mapping_impl_t*)base_buffer_mapping; |
| iree_hal_buffer_t* buffer = buffer_mapping->backing_buffer; |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_validate_access( |
| buffer_mapping->allowed_access, IREE_HAL_MEMORY_ACCESS_WRITE)); |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_calculate_range( |
| buffer_mapping->byte_offset, buffer_mapping->contents.data_length, |
| byte_offset, byte_length, &byte_offset, &byte_length)); |
| return _VTABLE_DISPATCH(buffer, flush_range)(buffer, byte_offset, |
| byte_length); |
| } |
| |
| IREE_API_EXPORT iree_status_t IREE_API_CALL iree_hal_buffer_mapping_subspan( |
| iree_hal_buffer_mapping_t* base_buffer_mapping, |
| iree_hal_memory_access_t memory_access, iree_device_size_t byte_offset, |
| iree_device_size_t byte_length, iree_byte_span_t* out_span) { |
| IREE_ASSERT_ARGUMENT(base_buffer_mapping); |
| iree_hal_buffer_mapping_impl_t* buffer_mapping = |
| (iree_hal_buffer_mapping_impl_t*)base_buffer_mapping; |
| IREE_ASSERT_ARGUMENT(out_span); |
| memset(out_span, 0, sizeof(*out_span)); |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_validate_access( |
| buffer_mapping->allowed_access, memory_access)); |
| IREE_RETURN_IF_ERROR(iree_hal_buffer_calculate_range( |
| 0, buffer_mapping->contents.data_length, byte_offset, byte_length, |
| &byte_offset, &out_span->data_length)); |
| out_span->data = buffer_mapping->contents.data + byte_offset; |
| return iree_ok_status(); |
| } |