Document, support, and test unspecified dimension.
A tensor operand type with some number of unspecified dimensions is
represented by setting each unspecified dimension to 0. A tensor operand
type of unspecified rank is represented by setting dimensionCount to 0
and dimensions to NULL.
Tensor with unspecified dimensions can not have value and can not be as
model input at execution time, but is allowed for output. Enable checks
and tests for it.
Bug: 77234888
Bug: 73506513
Test: NeuralNetworksTest_static
Change-Id: I4b6281cdd7758054a1a43ac34f39fc9c7bfead9b
Merged-In: I4b6281cdd7758054a1a43ac34f39fc9c7bfead9b
(cherry picked from commit 4dc7049baa2ef1780430918bdff7e7da59b2d6d1)
diff --git a/runtime/ExecutionBuilder.cpp b/runtime/ExecutionBuilder.cpp
index b1e787d..7825eb7 100644
--- a/runtime/ExecutionBuilder.cpp
+++ b/runtime/ExecutionBuilder.cpp
@@ -40,6 +40,35 @@
return execution->measureTiming() ? MeasureTiming::YES : MeasureTiming::NO;
}
+static bool checkDimensionInfo(const Operand& operand, const ANeuralNetworksOperandType* newType,
+ const char* tag, bool allowUnspecified) {
+ if (newType != nullptr) {
+ if (validateOperandType(*newType, tag, allowUnspecified) != ANEURALNETWORKS_NO_ERROR) {
+ LOG(ERROR) << tag << ": Invalid newType";
+ return false;
+ }
+ if (operand.dimensions.size() == 0) {
+ return true;
+ }
+ if (operand.dimensions.size() != newType->dimensionCount) {
+ LOG(ERROR) << tag << ": Setting with incompatible dimension count";
+ return false;
+ }
+ for (uint32_t i = 0; i < newType->dimensionCount; i++) {
+ if (operand.dimensions[i] != newType->dimensions[i] && operand.dimensions[i] != 0) {
+ LOG(ERROR) << tag << ": Overriding a fully specified dimension is disallowed";
+ return false;
+ }
+ }
+ } else {
+ if (!allowUnspecified && hasUnspecifiedDimensions(operand)) {
+ LOG(ERROR) << tag << ": Setting with operand type that is not fully specified";
+ return false;
+ }
+ }
+ return true;
+}
+
int ModelArgumentInfo::setFromPointer(const Operand& operand,
const ANeuralNetworksOperandType* type, void* data,
uint32_t length) {
@@ -55,7 +84,7 @@
NN_RETURN_IF_ERROR(updateDimensionInfo(operand, type));
if (!isExtensionOperandType(operand.type) && operand.type != OperandType::OEM) {
uint32_t neededLength = sizeOfData(operand.type, dimensions);
- if (neededLength != length) {
+ if (neededLength != length && neededLength != 0) {
LOG(ERROR) << "Setting argument with invalid length: " << length
<< ", expected length: " << neededLength;
return ANEURALNETWORKS_BAD_DATA;
@@ -73,7 +102,7 @@
NN_RETURN_IF_ERROR(updateDimensionInfo(operand, type));
if (!isExtensionOperandType(operand.type) && operand.type != OperandType::OEM) {
uint32_t neededLength = sizeOfData(operand.type, dimensions);
- if (neededLength != length) {
+ if (neededLength != length && neededLength != 0) {
LOG(ERROR) << "Setting argument with invalid length: " << length
<< ", expected length: " << neededLength;
return ANEURALNETWORKS_BAD_DATA;
@@ -110,32 +139,12 @@
int ModelArgumentInfo::updateDimensionInfo(const Operand& operand,
const ANeuralNetworksOperandType* newType) {
- nnAssert(dimensions.empty());
if (newType == nullptr) {
- for (auto i : operand.dimensions) {
- if (i == 0) {
- LOG(ERROR) << "Setting input/output with unspecified dimensions";
- return ANEURALNETWORKS_BAD_DATA;
- }
- }
dimensions = operand.dimensions;
} else {
- uint32_t count = newType->dimensionCount;
- if (static_cast<OperandType>(newType->type) != operand.type ||
- count != operand.dimensions.size()) {
- LOG(ERROR) << "Setting input/output with incompatible types";
- return ANEURALNETWORKS_BAD_DATA;
- }
-
+ const uint32_t count = newType->dimensionCount;
dimensions = hidl_vec<uint32_t>(count);
- for (uint32_t i = 0; i < count; i++) {
- if (operand.dimensions[i] != 0 && operand.dimensions[i] != newType->dimensions[i]) {
- LOG(ERROR) << "Overriding a fully specified dimension is disallowed";
- return ANEURALNETWORKS_BAD_DATA;
- } else {
- dimensions[i] = newType->dimensions[i];
- }
- }
+ std::copy(&newType->dimensions[0], &newType->dimensions[count], dimensions.begin());
}
return ANEURALNETWORKS_NO_ERROR;
}
@@ -157,11 +166,9 @@
LOG(ERROR) << "ANeuralNetworksExecution_setInput bad index " << index << " " << count;
return ANEURALNETWORKS_BAD_DATA;
}
- if (type != nullptr) {
- int n = validateOperandType(*type, "ANeuralNetworksExecution_setInput", false);
- if (n != ANEURALNETWORKS_NO_ERROR) {
- return n;
- }
+ if (!checkDimensionInfo(mModel->getInputOperand(index), type,
+ "ANeuralNetworksExecution_setInput", buffer == nullptr)) {
+ return ANEURALNETWORKS_BAD_DATA;
}
if (length > 0xFFFFFFFF) {
LOG(ERROR) << "ANeuralNetworksExecution_setInput input exceeds max length " << length;
@@ -182,6 +189,10 @@
<< count;
return ANEURALNETWORKS_BAD_DATA;
}
+ if (!checkDimensionInfo(mModel->getInputOperand(index), type,
+ "ANeuralNetworksExecution_setInputFromMemory", false)) {
+ return ANEURALNETWORKS_BAD_DATA;
+ }
// Both offset & length must be zero for Non-BLOB format AHardwareBuffer.
if (memory->getHidlMemory().name() == "hardware_buffer" && (offset != 0 || length != 0)) {
LOG(ERROR) << "ANeuralNetworksExecution_setInputFromMemory has non-zero offset and length"
@@ -203,11 +214,9 @@
LOG(ERROR) << "ANeuralNetworksExecution_setOutput bad index " << index << " " << count;
return ANEURALNETWORKS_BAD_DATA;
}
- if (type != nullptr) {
- int n = validateOperandType(*type, "ANeuralNetworksExecution_setOutput", false);
- if (n != ANEURALNETWORKS_NO_ERROR) {
- return n;
- }
+ if (!checkDimensionInfo(mModel->getOutputOperand(index), type,
+ "ANeuralNetworksExecution_setOutput", true)) {
+ return ANEURALNETWORKS_BAD_DATA;
}
if (length > 0xFFFFFFFF) {
LOG(ERROR) << "ANeuralNetworksExecution_setOutput input exceeds max length " << length;
@@ -227,6 +236,10 @@
<< count;
return ANEURALNETWORKS_BAD_DATA;
}
+ if (!checkDimensionInfo(mModel->getOutputOperand(index), type,
+ "ANeuralNetworksExecution_setOutputFromMemory", true)) {
+ return ANEURALNETWORKS_BAD_DATA;
+ }
// Both offset & length must be zero for Non-BLOB format AHardwareBuffer.
if (memory->getHidlMemory().name() == "hardware_buffer" && (offset != 0 || length != 0)) {
LOG(ERROR) << "ANeuralNetworksExecution_setOutputFromMemory has non-zero offset and length"
@@ -652,6 +665,7 @@
default:
nnAssert(!"unexpected ModelArgumentInfo::state");
break;
+ case ModelArgumentInfo::HAS_NO_VALUE:
case ModelArgumentInfo::POINTER:
case ModelArgumentInfo::UNSPECIFIED:
break;