Move NNAPI HAL CommonUtils to nnapi/SharedMemory -- hal
This CL relocates utility code that transfers data between pointer-based
memory and shared memory for Request objects and Model objects, moving
it from nnapi/hal/CommonUtils.h (hal utilities) to nnapi/SharedMemory.h
(canonical library).
This change also adds a check for whether Model and Requests have
pointer-based data in neuralnetworks/aidl/utils/src/Conversions.cpp to
make it consistent with the HIDL utility conversions.
Bug: 217217023
Test: mma
Test: presubmit
Change-Id: I55a0fea186708d806bc709681e10027a9e4b0ffb
diff --git a/neuralnetworks/aidl/utils/src/Conversions.cpp b/neuralnetworks/aidl/utils/src/Conversions.cpp
index eb28db7..9b897c4 100644
--- a/neuralnetworks/aidl/utils/src/Conversions.cpp
+++ b/neuralnetworks/aidl/utils/src/Conversions.cpp
@@ -967,6 +967,11 @@
}
nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model) {
+ if (!hal::utils::hasNoPointerData(model)) {
+ return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+ << "Model cannot be unvalidatedConverted because it contains pointer-based memory";
+ }
+
return Model{
.main = NN_TRY(unvalidatedConvert(model.main)),
.referenced = NN_TRY(unvalidatedConvert(model.referenced)),
@@ -982,6 +987,11 @@
}
nn::GeneralResult<Request> unvalidatedConvert(const nn::Request& request) {
+ if (!hal::utils::hasNoPointerData(request)) {
+ return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+ << "Request cannot be unvalidatedConverted because it contains pointer-based memory";
+ }
+
return Request{
.inputs = NN_TRY(unvalidatedConvert(request.inputs)),
.outputs = NN_TRY(unvalidatedConvert(request.outputs)),
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h b/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
index ae0d092..c04294a 100644
--- a/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
+++ b/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
@@ -20,6 +20,7 @@
#include <nnapi/Result.h>
#include <nnapi/SharedMemory.h>
#include <nnapi/Types.h>
+
#include <functional>
#include <vector>
@@ -47,81 +48,10 @@
const nn::Capabilities::PerformanceInfo& float32Performance,
const nn::Capabilities::PerformanceInfo& quantized8Performance);
-// Indicates if the object contains no pointer-based data that could be relocated to shared memory.
-bool hasNoPointerData(const nn::Model& model);
-bool hasNoPointerData(const nn::Request& request);
-
-// Relocate pointer-based data to shared memory. If `model` has no Operand::LifeTime::POINTER data,
-// the function returns with a reference to `model`. If `model` has Operand::LifeTime::POINTER data,
-// the model is copied to `maybeModelInSharedOut` with the POINTER data relocated to a memory pool,
-// and the function returns with a reference to `*maybeModelInSharedOut`.
-nn::GeneralResult<std::reference_wrapper<const nn::Model>> flushDataFromPointerToShared(
- const nn::Model* model, std::optional<nn::Model>* maybeModelInSharedOut);
-
-// Record a relocation mapping between pointer-based data and shared memory.
-// Only two specializations of this template may exist:
-// - RelocationInfo<const void*> for request inputs
-// - RelocationInfo<void*> for request outputs
-template <typename PointerType>
-struct RelocationInfo {
- PointerType data;
- size_t length;
- size_t offset;
-};
-using InputRelocationInfo = RelocationInfo<const void*>;
-using OutputRelocationInfo = RelocationInfo<void*>;
-
-// Keep track of the relocation mapping between pointer-based data and shared memory pool,
-// and provide method to copy the data between pointers and the shared memory pool.
-// Only two specializations of this template may exist:
-// - RelocationTracker<InputRelocationInfo> for request inputs
-// - RelocationTracker<OutputRelocationInfo> for request outputs
-template <typename RelocationInfoType>
-class RelocationTracker {
- public:
- static nn::GeneralResult<std::unique_ptr<RelocationTracker>> create(
- std::vector<RelocationInfoType> relocationInfos, nn::SharedMemory memory) {
- auto mapping = NN_TRY(map(memory));
- return std::make_unique<RelocationTracker<RelocationInfoType>>(
- std::move(relocationInfos), std::move(memory), std::move(mapping));
- }
-
- RelocationTracker(std::vector<RelocationInfoType> relocationInfos, nn::SharedMemory memory,
- nn::Mapping mapping)
- : kRelocationInfos(std::move(relocationInfos)),
- kMemory(std::move(memory)),
- kMapping(std::move(mapping)) {}
-
- // Specializations defined in CommonUtils.cpp.
- // For InputRelocationTracker, this method will copy pointer data to the shared memory pool.
- // For OutputRelocationTracker, this method will copy shared memory data to the pointers.
- void flush() const;
-
- private:
- const std::vector<RelocationInfoType> kRelocationInfos;
- const nn::SharedMemory kMemory;
- const nn::Mapping kMapping;
-};
-using InputRelocationTracker = RelocationTracker<InputRelocationInfo>;
-using OutputRelocationTracker = RelocationTracker<OutputRelocationInfo>;
-
-struct RequestRelocation {
- std::unique_ptr<InputRelocationTracker> input;
- std::unique_ptr<OutputRelocationTracker> output;
-};
-
-// Relocate pointer-based data to shared memory. If `request` has no
-// Request::Argument::LifeTime::POINTER data, the function returns with a reference to `request`. If
-// `request` has Request::Argument::LifeTime::POINTER data, the request is copied to
-// `maybeRequestInSharedOut` with the POINTER data relocated to a memory pool, and the function
-// returns with a reference to `*maybeRequestInSharedOut`. The `relocationOut` will be set to track
-// the input and output relocations.
-//
-// Unlike `flushDataFromPointerToShared`, this method will not copy the input pointer data to the
-// shared memory pool. Use `relocationOut` to flush the input or output data after the call.
-nn::GeneralResult<std::reference_wrapper<const nn::Request>> convertRequestFromPointerToShared(
- const nn::Request* request, uint32_t alignment, uint32_t padding,
- std::optional<nn::Request>* maybeRequestInSharedOut, RequestRelocation* relocationOut);
+using nn::convertRequestFromPointerToShared;
+using nn::flushDataFromPointerToShared;
+using nn::hasNoPointerData;
+using nn::RequestRelocation;
} // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/CommonUtils.cpp b/neuralnetworks/utils/common/src/CommonUtils.cpp
index b249881..dd55add 100644
--- a/neuralnetworks/utils/common/src/CommonUtils.cpp
+++ b/neuralnetworks/utils/common/src/CommonUtils.cpp
@@ -31,59 +31,6 @@
#include <vector>
namespace android::hardware::neuralnetworks::utils {
-namespace {
-
-bool hasNoPointerData(const nn::Operand& operand);
-bool hasNoPointerData(const nn::Model::Subgraph& subgraph);
-bool hasNoPointerData(const nn::Request::Argument& argument);
-
-template <typename Type>
-bool hasNoPointerData(const std::vector<Type>& objects) {
- return std::all_of(objects.begin(), objects.end(),
- [](const auto& object) { return hasNoPointerData(object); });
-}
-
-bool hasNoPointerData(const nn::DataLocation& location) {
- return std::visit([](auto ptr) { return ptr == nullptr; }, location.pointer);
-}
-
-bool hasNoPointerData(const nn::Operand& operand) {
- return hasNoPointerData(operand.location);
-}
-
-bool hasNoPointerData(const nn::Model::Subgraph& subgraph) {
- return hasNoPointerData(subgraph.operands);
-}
-
-bool hasNoPointerData(const nn::Request::Argument& argument) {
- return hasNoPointerData(argument.location);
-}
-
-void copyPointersToSharedMemory(nn::Operand* operand, nn::ConstantMemoryBuilder* memoryBuilder) {
- CHECK(operand != nullptr);
- CHECK(memoryBuilder != nullptr);
-
- if (operand->lifetime != nn::Operand::LifeTime::POINTER) {
- return;
- }
-
- const void* data = std::visit([](auto ptr) { return static_cast<const void*>(ptr); },
- operand->location.pointer);
- CHECK(data != nullptr);
- operand->lifetime = nn::Operand::LifeTime::CONSTANT_REFERENCE;
- operand->location = memoryBuilder->append(data, operand->location.length);
-}
-
-void copyPointersToSharedMemory(nn::Model::Subgraph* subgraph,
- nn::ConstantMemoryBuilder* memoryBuilder) {
- CHECK(subgraph != nullptr);
- std::for_each(subgraph->operands.begin(), subgraph->operands.end(),
- [memoryBuilder](auto& operand) {
- copyPointersToSharedMemory(&operand, memoryBuilder);
- });
-}
-
-} // anonymous namespace
nn::Capabilities::OperandPerformanceTable makeQuantized8PerformanceConsistentWithP(
const nn::Capabilities::PerformanceInfo& float32Performance,
@@ -104,131 +51,4 @@
.value();
}
-bool hasNoPointerData(const nn::Model& model) {
- return hasNoPointerData(model.main) && hasNoPointerData(model.referenced);
-}
-
-bool hasNoPointerData(const nn::Request& request) {
- return hasNoPointerData(request.inputs) && hasNoPointerData(request.outputs);
-}
-
-nn::GeneralResult<std::reference_wrapper<const nn::Model>> flushDataFromPointerToShared(
- const nn::Model* model, std::optional<nn::Model>* maybeModelInSharedOut) {
- CHECK(model != nullptr);
- CHECK(maybeModelInSharedOut != nullptr);
-
- if (hasNoPointerData(*model)) {
- return *model;
- }
-
- // Make a copy of the model in order to make modifications. The modified model is returned to
- // the caller through `maybeModelInSharedOut` if the function succeeds.
- nn::Model modelInShared = *model;
-
- nn::ConstantMemoryBuilder memoryBuilder(modelInShared.pools.size());
- copyPointersToSharedMemory(&modelInShared.main, &memoryBuilder);
- std::for_each(modelInShared.referenced.begin(), modelInShared.referenced.end(),
- [&memoryBuilder](auto& subgraph) {
- copyPointersToSharedMemory(&subgraph, &memoryBuilder);
- });
-
- if (!memoryBuilder.empty()) {
- auto memory = NN_TRY(memoryBuilder.finish());
- modelInShared.pools.push_back(std::move(memory));
- }
-
- *maybeModelInSharedOut = modelInShared;
- return **maybeModelInSharedOut;
-}
-
-template <>
-void InputRelocationTracker::flush() const {
- // Copy from pointers to shared memory.
- uint8_t* memoryPtr = static_cast<uint8_t*>(std::get<void*>(kMapping.pointer));
- for (const auto& [data, length, offset] : kRelocationInfos) {
- std::memcpy(memoryPtr + offset, data, length);
- }
-}
-
-template <>
-void OutputRelocationTracker::flush() const {
- // Copy from shared memory to pointers.
- const uint8_t* memoryPtr = static_cast<const uint8_t*>(
- std::visit([](auto ptr) { return static_cast<const void*>(ptr); }, kMapping.pointer));
- for (const auto& [data, length, offset] : kRelocationInfos) {
- std::memcpy(data, memoryPtr + offset, length);
- }
-}
-
-nn::GeneralResult<std::reference_wrapper<const nn::Request>> convertRequestFromPointerToShared(
- const nn::Request* request, uint32_t alignment, uint32_t padding,
- std::optional<nn::Request>* maybeRequestInSharedOut, RequestRelocation* relocationOut) {
- CHECK(request != nullptr);
- CHECK(maybeRequestInSharedOut != nullptr);
- CHECK(relocationOut != nullptr);
-
- if (hasNoPointerData(*request)) {
- return *request;
- }
-
- // Make a copy of the request in order to make modifications. The modified request is returned
- // to the caller through `maybeRequestInSharedOut` if the function succeeds.
- nn::Request requestInShared = *request;
-
- RequestRelocation relocation;
-
- // Change input pointers to shared memory.
- nn::MutableMemoryBuilder inputBuilder(requestInShared.pools.size());
- std::vector<InputRelocationInfo> inputRelocationInfos;
- for (auto& input : requestInShared.inputs) {
- const auto& location = input.location;
- if (input.lifetime != nn::Request::Argument::LifeTime::POINTER) {
- continue;
- }
-
- input.lifetime = nn::Request::Argument::LifeTime::POOL;
- const void* data = std::visit([](auto ptr) { return static_cast<const void*>(ptr); },
- location.pointer);
- CHECK(data != nullptr);
- input.location = inputBuilder.append(location.length, alignment, padding);
- inputRelocationInfos.push_back({data, input.location.length, input.location.offset});
- }
-
- // Allocate input memory.
- if (!inputBuilder.empty()) {
- auto memory = NN_TRY(inputBuilder.finish());
- requestInShared.pools.push_back(memory);
- relocation.input = NN_TRY(
- InputRelocationTracker::create(std::move(inputRelocationInfos), std::move(memory)));
- }
-
- // Change output pointers to shared memory.
- nn::MutableMemoryBuilder outputBuilder(requestInShared.pools.size());
- std::vector<OutputRelocationInfo> outputRelocationInfos;
- for (auto& output : requestInShared.outputs) {
- const auto& location = output.location;
- if (output.lifetime != nn::Request::Argument::LifeTime::POINTER) {
- continue;
- }
-
- output.lifetime = nn::Request::Argument::LifeTime::POOL;
- void* data = std::get<void*>(location.pointer);
- CHECK(data != nullptr);
- output.location = outputBuilder.append(location.length, alignment, padding);
- outputRelocationInfos.push_back({data, output.location.length, output.location.offset});
- }
-
- // Allocate output memory.
- if (!outputBuilder.empty()) {
- auto memory = NN_TRY(outputBuilder.finish());
- requestInShared.pools.push_back(memory);
- relocation.output = NN_TRY(OutputRelocationTracker::create(std::move(outputRelocationInfos),
- std::move(memory)));
- }
-
- *maybeRequestInSharedOut = requestInShared;
- *relocationOut = std::move(relocation);
- return **maybeRequestInSharedOut;
-}
-
} // namespace android::hardware::neuralnetworks::utils