blob: 5ea4326862723807946d1e237e044f176f22947e [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.
#include "hal/heap_buffer.h"
#include <cstdint>
#include <cstdlib>
#include <string>
#include <utility>
#include "base/source_location.h"
#include "base/status.h"
#include "base/tracing.h"
#include "hal/allocator.h"
#include "hal/host/host_buffer.h"
namespace iree {
namespace hal {
namespace {
// An allocator that allocates or wraps host-only buffers.
// The resulting buffers are not usable by most devices without a copy and
// using a device allocator is strongly preferred.
class HeapAllocator : public Allocator {
public:
// Returns a singleton heap allocator that can provide buffers that have
// MemoryType::kHostLocal and are allocated with malloc/free.
// These buffers will not be usable by devices directly and may incur
// additional copies.
static Allocator* std_heap();
// TODO(benvanik): specify custom allocator (not malloc/free).
HeapAllocator();
~HeapAllocator() override;
bool CanUseBufferLike(Allocator* source_allocator,
MemoryTypeBitfield memory_type,
BufferUsageBitfield buffer_usage,
BufferUsageBitfield intended_usage) const override;
bool CanAllocate(MemoryTypeBitfield memory_type,
BufferUsageBitfield buffer_usage,
size_t allocation_size) const override;
StatusOr<ref_ptr<Buffer>> Allocate(MemoryTypeBitfield memory_type,
BufferUsageBitfield buffer_usage,
size_t allocation_size) override;
StatusOr<ref_ptr<Buffer>> WrapMutable(MemoryTypeBitfield memory_type,
MemoryAccessBitfield allowed_access,
BufferUsageBitfield buffer_usage,
void* data,
size_t data_length) override;
};
// static
Allocator* HeapAllocator::std_heap() {
static Allocator* std_heap_allocator = new HeapAllocator();
return std_heap_allocator;
}
HeapAllocator::HeapAllocator() = default;
HeapAllocator::~HeapAllocator() = default;
bool HeapAllocator::CanUseBufferLike(Allocator* source_allocator,
MemoryTypeBitfield memory_type,
BufferUsageBitfield buffer_usage,
BufferUsageBitfield intended_usage) const {
// The host can use anything with kHostVisible.
if (!AnyBitSet(memory_type & MemoryType::kHostVisible)) {
return false;
}
// Host currently uses mapping to copy buffers, which is done a lot.
if (!AnyBitSet(buffer_usage & BufferUsage::kMapping)) {
return false;
}
return true;
}
bool HeapAllocator::CanAllocate(MemoryTypeBitfield memory_type,
BufferUsageBitfield buffer_usage,
size_t allocation_size) const {
// This host only allocator cannot serve device visible allocation as we
// can't know which devices these buffers will be used with.
return (memory_type & MemoryType::kHostLocal) == MemoryType::kHostLocal &&
!AnyBitSet(memory_type & MemoryType::kDeviceLocal) &&
!AnyBitSet(memory_type & MemoryType::kDeviceVisible);
}
StatusOr<ref_ptr<Buffer>> HeapAllocator::Allocate(
MemoryTypeBitfield memory_type, BufferUsageBitfield buffer_usage,
size_t allocation_size) {
IREE_TRACE_SCOPE0("HeapAllocator::Allocate");
if (!CanAllocate(memory_type, buffer_usage, allocation_size)) {
return FailedPreconditionErrorBuilder(IREE_LOC)
<< "Allocation not supported; memory_type="
<< MemoryTypeString(memory_type)
<< ", buffer_usage=" << BufferUsageString(buffer_usage)
<< ", allocation_size=" << allocation_size;
}
void* malloced_data = std::calloc(1, allocation_size);
if (!malloced_data) {
return ResourceExhaustedErrorBuilder(IREE_LOC)
<< "Failed to malloc " << allocation_size << " bytes";
}
auto buffer =
make_ref<HostBuffer>(this, memory_type, MemoryAccess::kAll, buffer_usage,
allocation_size, malloced_data, true);
return buffer;
}
StatusOr<ref_ptr<Buffer>> HeapAllocator::WrapMutable(
MemoryTypeBitfield memory_type, MemoryAccessBitfield allowed_access,
BufferUsageBitfield buffer_usage, void* data, size_t data_length) {
auto buffer = make_ref<HostBuffer>(this, memory_type, allowed_access,
buffer_usage, data_length, data, false);
return buffer;
}
} // namespace
// static
ref_ptr<Buffer> HeapBuffer::Allocate(MemoryTypeBitfield memory_type,
BufferUsageBitfield usage,
size_t allocation_size) {
auto buffer_or =
HeapAllocator::std_heap()->Allocate(memory_type, usage, allocation_size);
return std::move(buffer_or.ValueOrDie());
}
// static
ref_ptr<Buffer> HeapBuffer::AllocateCopy(BufferUsageBitfield usage,
const void* data, size_t data_length) {
return AllocateCopy(usage, MemoryAccess::kAll, data, data_length);
}
// static
ref_ptr<Buffer> HeapBuffer::AllocateCopy(BufferUsageBitfield usage,
MemoryAccessBitfield allowed_access,
const void* data, size_t data_length) {
IREE_TRACE_SCOPE0("HeapBuffer::AllocateCopy");
// Ensure we can map so that we can copy into it.
usage |= BufferUsage::kMapping;
auto buffer_or = HeapAllocator::std_heap()->Allocate(MemoryType::kHostLocal,
usage, data_length);
auto buffer = std::move(buffer_or.ValueOrDie());
buffer->WriteData(0, data, data_length).IgnoreError();
buffer->set_allowed_access(allowed_access);
return buffer;
}
// static
ref_ptr<Buffer> HeapBuffer::Wrap(MemoryTypeBitfield memory_type,
BufferUsageBitfield usage, const void* data,
size_t data_length) {
auto buffer_or =
HeapAllocator::std_heap()->Wrap(memory_type, usage, data, data_length);
return std::move(buffer_or.ValueOrDie());
}
// static
ref_ptr<Buffer> HeapBuffer::WrapMutable(MemoryTypeBitfield memory_type,
MemoryAccessBitfield allowed_access,
BufferUsageBitfield usage, void* data,
size_t data_length) {
auto buffer_or = HeapAllocator::std_heap()->WrapMutable(
memory_type, allowed_access, usage, data, data_length);
return std::move(buffer_or.ValueOrDie());
}
} // namespace hal
} // namespace iree