Merge "Update libneuralnetworks_driver_fuzzer"
diff --git a/common/Android.bp b/common/Android.bp
index f36105d..13aae0e 100644
--- a/common/Android.bp
+++ b/common/Android.bp
@@ -129,7 +129,6 @@
"CpuExecutor.cpp",
"ExecutionBurstController.cpp",
"ExecutionBurstServer.cpp",
- "FlagUtils.cpp",
"GraphDump.cpp",
"HalBufferTracker.cpp",
"IndexedShapeWrapper.cpp",
@@ -275,7 +274,6 @@
srcs: [
"BufferTracker.cpp",
"CpuExecutor.cpp",
- "FlagUtils.cpp",
"GraphDump.cpp",
"IndexedShapeWrapper.cpp",
"LegacyUtils.cpp",
diff --git a/common/FlagUtils.cpp b/common/FlagUtils.cpp
deleted file mode 100644
index cb87ef7..0000000
--- a/common/FlagUtils.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * 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
- *
- * http://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.
- */
-
-#define LOG_TAG "FlagUtils"
-
-#include "FlagUtils.h"
-
-namespace android {
-namespace nn {
-
-namespace {
-int64_t getFeatureLevelFlag() {
- if (_getServerFeatureLevelFlag) {
- return _getServerFeatureLevelFlag();
- }
- return kDefaultFeatureLevelNum;
-}
-
-FeatureLevelCode flagToFeatureLevelCode(int64_t featureLevelFlag) {
- switch (featureLevelFlag) {
- case 1:
- return ANEURALNETWORKS_FEATURE_LEVEL_1;
- case 2:
- return ANEURALNETWORKS_FEATURE_LEVEL_2;
- case 3:
- return ANEURALNETWORKS_FEATURE_LEVEL_3;
- case 4:
- return ANEURALNETWORKS_FEATURE_LEVEL_4;
- case 5:
- return ANEURALNETWORKS_FEATURE_LEVEL_5;
- case 6:
- return ANEURALNETWORKS_FEATURE_LEVEL_6;
- case 7:
- return ANEURALNETWORKS_FEATURE_LEVEL_7;
- default:
- return KDefaultFeatureLevelCode;
- }
-}
-} // namespace
-
-FeatureLevelCode queryFeatureLevel() {
- static const int64_t featureLevelFlag = getFeatureLevelFlag();
- return flagToFeatureLevelCode(featureLevelFlag);
-}
-
-} // namespace nn
-} // namespace android
diff --git a/common/TypeUtils.cpp b/common/TypeUtils.cpp
index a8b76a7..31639b8 100644
--- a/common/TypeUtils.cpp
+++ b/common/TypeUtils.cpp
@@ -908,8 +908,8 @@
return os << optionalTimeoutDuration.value();
}
-static std::ostream& operator<<(std::ostream& os, const Version::Level& level) {
- switch (level) {
+std::ostream& operator<<(std::ostream& os, const Version::Level& versionLevel) {
+ switch (versionLevel) {
case Version::Level::FEATURE_LEVEL_1:
return os << "FEATURE_LEVEL_1";
case Version::Level::FEATURE_LEVEL_2:
@@ -929,7 +929,7 @@
return os << "FEATURE_LEVEL_EXPERIMENTAL";
#endif // NN_EXPERIMENTAL_FEATURE
}
- return os << "Version{" << static_cast<uint32_t>(underlyingType(level)) << "}";
+ return os << "Version{" << static_cast<uint32_t>(underlyingType(versionLevel)) << "}";
}
std::ostream& operator<<(std::ostream& os, const Version& version) {
diff --git a/common/include/FlagUtils.h b/common/include/FlagUtils.h
deleted file mode 100644
index 02f7fe3..0000000
--- a/common/include/FlagUtils.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * 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
- *
- * http://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.
- */
-
-#ifndef ANDROID_PACKAGES_MODULES_NEURALNETWORKS_COMMON_FLAG_UTILS_H
-#define ANDROID_PACKAGES_MODULES_NEURALNETWORKS_COMMON_FLAG_UTILS_H
-
-#include <stdint.h>
-
-#include "NeuralNetworks.h"
-
-namespace android {
-namespace nn {
-
-// Keep these values consistent with server side configuration in
-// google3/googledata/experiments/mobile/android_platform/nnapi_native/features/feature_level.gcl.
-constexpr char kExprCategoryName[] = "nnapi_native";
-constexpr char kCurrentFeatureLevelFlagName[] = "current_feature_level";
-constexpr int64_t kDefaultFeatureLevelNum = 5;
-constexpr FeatureLevelCode KDefaultFeatureLevelCode = ANEURALNETWORKS_FEATURE_LEVEL_5;
-constexpr int64_t kMinFeatureLevelNum = 5;
-constexpr int64_t kMaxFeatureLevelNum = 7;
-
-// Weak symbol to get server feature level flag so that other targets with different build options
-// (e.g. not vendor available) can implement this function.
-// Note that this function should NOT be used directly and may not be present in the final artifact.
-// Clients are expected to use queryFeatureLevel instead.
-int64_t _getServerFeatureLevelFlag() __attribute__((weak));
-
-// Queries system flag for the current feature level.
-FeatureLevelCode queryFeatureLevel();
-
-} // namespace nn
-} // namespace android
-
-#endif // ANDROID_PACKAGES_MODULES_NEURALNETWORKS_COMMON_FLAG_UTILS_H
diff --git a/common/include/nnapi/TypeUtils.h b/common/include/nnapi/TypeUtils.h
index 7cc4ea9..a9b302b 100644
--- a/common/include/nnapi/TypeUtils.h
+++ b/common/include/nnapi/TypeUtils.h
@@ -128,6 +128,7 @@
std::ostream& operator<<(std::ostream& os, const OptionalTimePoint& optionalTimePoint);
std::ostream& operator<<(std::ostream& os, const Duration& timeoutDuration);
std::ostream& operator<<(std::ostream& os, const OptionalDuration& optionalTimeoutDuration);
+std::ostream& operator<<(std::ostream& os, const Version::Level& versionLevel);
std::ostream& operator<<(std::ostream& os, const Version& version);
bool operator==(const Timing& a, const Timing& b);
diff --git a/common/operations/Dequantize.cpp b/common/operations/Dequantize.cpp
index a9a5e67..5fbc213 100644
--- a/common/operations/Dequantize.cpp
+++ b/common/operations/Dequantize.cpp
@@ -39,6 +39,7 @@
const float scale = inputShape.scale;
for (int i = 0; i < numElements; ++i) {
const int32_t value = inputData[i];
+ // This dequantization formula also appears in Elementwise.cpp.
outputData[i] = static_cast<OutputType>(scale * (value - zeroPoint));
}
return true;
diff --git a/common/operations/Elementwise.cpp b/common/operations/Elementwise.cpp
index a84e24e..9088a2b 100644
--- a/common/operations/Elementwise.cpp
+++ b/common/operations/Elementwise.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "Operations"
#include <cmath>
+#include <functional>
+#include <limits>
#include "OperationResolver.h"
#include "OperationsUtils.h"
@@ -35,8 +37,8 @@
namespace {
template <typename IntermediateType, typename T>
-inline bool compute(IntermediateType func(IntermediateType), const T* input, const Shape& shape,
- T* output) {
+inline bool compute(const std::function<IntermediateType(IntermediateType)>& func, const T* input,
+ const Shape& shape, T* output) {
const auto size = getNumberOfElements(shape);
for (uint32_t i = 0; i < size; ++i) {
output[i] = static_cast<T>(func(static_cast<IntermediateType>(input[i])));
@@ -44,6 +46,34 @@
return true;
}
+template <typename IntermediateType, typename T>
+inline bool compute(IntermediateType func(IntermediateType), const T* input, const Shape& shape,
+ T* output) {
+ return compute(std::function<IntermediateType(IntermediateType)>(func), input, shape, output);
+}
+
+template <typename IntermediateType, typename T>
+auto makeQuantized(const std::function<IntermediateType(IntermediateType)>& func, float inScale,
+ T inZeroPoint, float outScale, T outZeroPoint) {
+ return [func, inScale, inZeroPoint, outScale, outZeroPoint](T val) -> T {
+ // For dequantization formula, see Dequantize.cpp.
+ using WideT = int32_t;
+ static_assert(sizeof(T) < sizeof(WideT));
+ IntermediateType dequantizedVal =
+ (static_cast<WideT>(val) - static_cast<WideT>(inZeroPoint)) * inScale;
+
+ IntermediateType res = func(dequantizedVal);
+
+ // For quantization formula, see Quantize.cpp.
+ T quantizedRes = static_cast<T>(std::max<float>(
+ static_cast<IntermediateType>(std::numeric_limits<T>::min()),
+ std::min<float>(static_cast<IntermediateType>(std::numeric_limits<T>::max()),
+ outZeroPoint + std::round(res / outScale))));
+
+ return quantizedRes;
+ };
+}
+
bool execute(IOperationExecutionContext* context, float func(float)) {
switch (context->getInputType(kInputTensor)) {
case OperandType::TENSOR_FLOAT16:
@@ -82,6 +112,44 @@
}
}
+bool executeRsqrt(IOperationExecutionContext* context) {
+ const std::function<float(float)> frsqrt = [](float x) { return 1.f / std::sqrt(x); };
+ const auto tensorType = context->getInputType(kInputTensor);
+ switch (tensorType) {
+ case OperandType::TENSOR_FLOAT16:
+ return compute<float, _Float16>(frsqrt, context->getInputBuffer<_Float16>(kInputTensor),
+ context->getInputShape(kInputTensor),
+ context->getOutputBuffer<_Float16>(kOutputTensor));
+ case OperandType::TENSOR_FLOAT32:
+ return compute<float, float>(frsqrt, context->getInputBuffer<float>(kInputTensor),
+ context->getInputShape(kInputTensor),
+ context->getOutputBuffer<float>(kOutputTensor));
+ case OperandType::TENSOR_QUANT8_ASYMM: {
+ const Shape inShape = context->getInputShape(kInputTensor);
+ const Shape outShape = context->getOutputShape(kOutputTensor);
+ return compute<uint8_t, uint8_t>(
+ makeQuantized(frsqrt, inShape.scale, static_cast<uint8_t>(inShape.offset),
+ outShape.scale, static_cast<uint8_t>(outShape.offset)),
+ context->getInputBuffer<uint8_t>(kInputTensor),
+ context->getInputShape(kInputTensor),
+ context->getOutputBuffer<uint8_t>(kOutputTensor));
+ }
+ case OperandType::TENSOR_QUANT8_ASYMM_SIGNED: {
+ const Shape inShape = context->getInputShape(kInputTensor);
+ const Shape outShape = context->getOutputShape(kOutputTensor);
+ return compute<int8_t, int8_t>(
+ makeQuantized(frsqrt, inShape.scale, static_cast<int8_t>(inShape.offset),
+ outShape.scale, static_cast<int8_t>(outShape.offset)),
+ context->getInputBuffer<int8_t>(kInputTensor),
+ context->getInputShape(kInputTensor),
+ context->getOutputBuffer<int8_t>(kOutputTensor));
+ }
+ default:
+ NN_RET_CHECK_FAIL() << "Unsupported tensor type " << tensorType
+ << " for operation RSQRT";
+ }
+}
+
Result<Version> validate(const IOperationValidationContext* context) {
NN_RET_CHECK_EQ(context->getNumInputs(), kNumInputs);
NN_RET_CHECK_EQ(context->getNumOutputs(), kNumOutputs);
@@ -169,10 +237,6 @@
return execute(context, std::log);
}
-bool executeRsqrt(IOperationExecutionContext* context) {
- return execute(context, [](float x) { return 1.f / std::sqrt(x); });
-}
-
bool executeSin(IOperationExecutionContext* context) {
return execute(context, std::sin);
}
diff --git a/common/operations/Quantize.cpp b/common/operations/Quantize.cpp
index d1fcdbc..e2d0d96 100644
--- a/common/operations/Quantize.cpp
+++ b/common/operations/Quantize.cpp
@@ -36,6 +36,7 @@
namespace {
+// The quantization formula also appears in Elementwise.cpp.
template <typename T>
bool quantizeToQuant8(const T* inputData, uint8_t* outputData, const Shape& outputShape) {
NNTRACE_COMP("quantizeToQuant8");
@@ -48,6 +49,7 @@
return true;
}
+// The quantization formula also appears in Elementwise.cpp.
template <typename T>
bool quantizeToQuant8Signed(const T* inputData, int8_t* outputData, const Shape& outputShape) {
NNTRACE_COMP("quantizeToQuant8Signed");
diff --git a/driver/sample_shim/android_arm/neuralnetworks_sample_sl_driver_prebuilt.so b/driver/sample_shim/android_arm/neuralnetworks_sample_sl_driver_prebuilt.so
index c9eed54..fe35fcc 100755
--- a/driver/sample_shim/android_arm/neuralnetworks_sample_sl_driver_prebuilt.so
+++ b/driver/sample_shim/android_arm/neuralnetworks_sample_sl_driver_prebuilt.so
Binary files differ
diff --git a/driver/sample_shim/android_arm64/neuralnetworks_sample_sl_driver_prebuilt.so b/driver/sample_shim/android_arm64/neuralnetworks_sample_sl_driver_prebuilt.so
index 30d5e9b..4961c73 100755
--- a/driver/sample_shim/android_arm64/neuralnetworks_sample_sl_driver_prebuilt.so
+++ b/driver/sample_shim/android_arm64/neuralnetworks_sample_sl_driver_prebuilt.so
Binary files differ
diff --git a/driver/sample_shim/android_x86/neuralnetworks_sample_sl_driver_prebuilt.so b/driver/sample_shim/android_x86/neuralnetworks_sample_sl_driver_prebuilt.so
index 4b31162..0466bb3 100755
--- a/driver/sample_shim/android_x86/neuralnetworks_sample_sl_driver_prebuilt.so
+++ b/driver/sample_shim/android_x86/neuralnetworks_sample_sl_driver_prebuilt.so
Binary files differ
diff --git a/driver/sample_shim/android_x86_64/neuralnetworks_sample_sl_driver_prebuilt.so b/driver/sample_shim/android_x86_64/neuralnetworks_sample_sl_driver_prebuilt.so
index 7245626..e05a6d0 100755
--- a/driver/sample_shim/android_x86_64/neuralnetworks_sample_sl_driver_prebuilt.so
+++ b/driver/sample_shim/android_x86_64/neuralnetworks_sample_sl_driver_prebuilt.so
Binary files differ
diff --git a/runtime/Android.bp b/runtime/Android.bp
index d460aa9..3ce7da6 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -209,6 +209,7 @@
exclude_static_libs: [
"libneuralnetworks_common",
"neuralnetworks_types",
+ "server_configurable_flags",
],
static_libs: [
"libneuralnetworks_common_experimental",
@@ -240,6 +241,7 @@
"ModelArgumentInfo.cpp",
"ModelBuilder.cpp",
"NeuralNetworks.cpp",
+ "ServerFlag.cpp",
"SupportLibraryDiagnostic.cpp",
"Telemetry.cpp",
"TypeManager.cpp",
diff --git a/runtime/ExecutionPlan.cpp b/runtime/ExecutionPlan.cpp
index 5dffa3c..8d57e09 100644
--- a/runtime/ExecutionPlan.cpp
+++ b/runtime/ExecutionPlan.cpp
@@ -898,7 +898,8 @@
executionPreference, priority);
if (stepHasDynamicTemporaries) {
mHasDynamicTemporaries = true;
- if (step->getDevice()->getFeatureLevel() < kHalVersionV1_2ToApi.featureLevel) {
+ if (!isCompliantVersion(kHalVersionV1_2ToApi.canonical,
+ step->getDevice()->getFeatureLevel())) {
// Until HAL 1.2, an Operand with lifetime SUBGRAPH_OUTPUT
// must have fully specified dimensions either in the
// Operand or in the RequestArgument. In the case of a
diff --git a/runtime/FeatureLevel.h b/runtime/FeatureLevel.h
index edc6d66..bdeb778 100644
--- a/runtime/FeatureLevel.h
+++ b/runtime/FeatureLevel.h
@@ -19,21 +19,9 @@
#include "NeuralNetworks.h"
-#ifdef NN_EXPERIMENTAL_FEATURE
-#include "NeuralNetworksExperimentalFeatures.h"
-#endif // NN_EXPERIMENTAL_FEATURE
-
namespace android {
namespace nn {
-// TODO(b/201399117): Set this value based on feature level flag.
-// The current feature level of the NNAPI Runtime
-#ifdef NN_EXPERIMENTAL_FEATURE
-constexpr int64_t kCurrentNNAPIRuntimeFeatureLevel = ANEURALNETWORKS_FEATURE_LEVEL_EXPERIMENTAL;
-#else // NN_EXPERIMENTAL_FEATURE
-constexpr int64_t kCurrentNNAPIRuntimeFeatureLevel = ANEURALNETWORKS_FEATURE_LEVEL_7;
-#endif // NN_EXPERIMENTAL_FEATURE
-
// The current version of the NNAPI APEX module.
// Keep this value in sync with packages/modules/NeuralNetworks/apex/manifest.json.
constexpr int64_t kNnapiApexVersion = 319999900;
diff --git a/runtime/Manager.cpp b/runtime/Manager.cpp
index b5bb992..1c78b4c 100644
--- a/runtime/Manager.cpp
+++ b/runtime/Manager.cpp
@@ -41,9 +41,9 @@
#include <vector>
#include "ExecutionCallback.h"
-#include "FeatureLevel.h"
#include "Memory.h"
#include "ModelArgumentInfo.h"
+#include "ServerFlag.h"
#include "TypeManager.h"
#ifndef NN_COMPATIBILITY_LIBRARY_BUILD
@@ -55,8 +55,39 @@
#include "AppInfoFetcher.h"
#endif // NN_COMPATIBILITY_LIBRARY_BUILD
+#ifdef NN_EXPERIMENTAL_FEATURE
+#include "NeuralNetworksExperimentalFeatures.h"
+#endif // NN_EXPERIMENTAL_FEATURE
+
namespace android {
namespace nn {
+namespace {
+
+Version getRuntimeFeatureLevelVersionHelper() {
+#if defined(NN_EXPERIMENTAL_FEATURE) && defined(NN_COMPATIBILITY_LIBRARY_BUILD)
+#error "NN_EXPERIMENTAL_FEATURE is not supported when NN_COMPATIBILITY_LIBRARY_BUILD is defined"
+#elif defined(NN_EXPERIMENTAL_FEATURE)
+ auto version = kVersionFeatureLevelExperimental;
+ // Enable "runtimeOnlyFeatures" to indicate that the runtime feature level version supports
+ // features that are only available in the runtime.
+ version.runtimeOnlyFeatures = true;
+#elif defined(NN_COMPATIBILITY_LIBRARY_BUILD)
+ auto version = serverFeatureLevelToVersion(kMaxFeatureLevelNum);
+#else // !defined(NN_COMPATIBILITY_LIBRARY_BUILD) && !defined(NN_EXPERIMENTAL_FEATURE)
+ auto version = serverFeatureLevelToVersion(getServerFeatureLevelFlag());
+ // Enable "runtimeOnlyFeatures" to indicate that the runtime feature level version supports
+ // features that are only available in the runtime.
+ version.runtimeOnlyFeatures = true;
+#endif // !defined(NN_COMPATIBILITY_LIBRARY_BUILD) && !defined(NN_EXPERIMENTAL_FEATURE)
+ return version;
+}
+
+Version getRuntimeFeatureLevelVersion() {
+ static const Version version = getRuntimeFeatureLevelVersionHelper();
+ return version;
+}
+
+} // namespace
// A Device with actual underlying driver
class DriverDevice : public Device {
@@ -70,7 +101,7 @@
const std::string& getName() const override { return kInterface->getName(); }
const std::string& getVersionString() const override { return kInterface->getVersionString(); }
- int64_t getFeatureLevel() const override;
+ Version getFeatureLevel() const override { return kInterface->getFeatureLevel(); }
int32_t getType() const override { return static_cast<int32_t>(kInterface->getType()); }
bool isUpdatable() const override { return kIsUpdatable; }
const std::vector<Extension>& getSupportedExtensions() const override {
@@ -173,7 +204,7 @@
}
MemoryPreference getMemoryPreference() const override {
- if (mDevice->getFeatureLevel() >= ANEURALNETWORKS_FEATURE_LEVEL_5) {
+ if (isCompliantVersion(kVersionFeatureLevel5, mDevice->getFeatureLevel())) {
return {kDefaultRequestMemoryAlignment, kDefaultRequestMemoryPadding};
} else {
// We are not able to pass memory padding information to HIDL drivers, so return the
@@ -191,7 +222,7 @@
public:
DriverExecution(SharedExecution execution, Request request,
std::vector<const RuntimeMemory*> memories, MeasureTiming measure,
- OptionalDuration loopTimeoutDuration, int64_t deviceFeatureLevel)
+ OptionalDuration loopTimeoutDuration, Version deviceFeatureLevel)
: kExecution(std::move(execution)),
kRequest(std::move(request)),
kMemories(std::move(memories)),
@@ -219,7 +250,7 @@
mutable std::map<const IBurst*, SharedExecution> mCachedBurstExecutions;
// For fenced execution.
- const int64_t kDeviceFeatureLevel;
+ const Version kDeviceFeatureLevel;
};
DriverDevice::DriverDevice(SharedDevice device, bool isUpdatable)
@@ -242,10 +273,8 @@
return std::make_shared<DriverDevice>(std::move(device), isUpdatable);
}
-int64_t DriverDevice::getFeatureLevel() const {
- Version version = kInterface->getFeatureLevel();
- CHECK(!version.runtimeOnlyFeatures);
- switch (version.level) {
+int64_t DeviceManager::versionToFeatureLevel(Version::Level versionLevel) {
+ switch (versionLevel) {
case Version::Level::FEATURE_LEVEL_1:
return ANEURALNETWORKS_FEATURE_LEVEL_1;
case Version::Level::FEATURE_LEVEL_2:
@@ -262,10 +291,10 @@
return ANEURALNETWORKS_FEATURE_LEVEL_7;
#ifdef NN_EXPERIMENTAL_FEATURE
case Version::Level::FEATURE_LEVEL_EXPERIMENTAL:
- break;
+ return ANEURALNETWORKS_FEATURE_LEVEL_EXPERIMENTAL;
#endif // NN_EXPERIMENTAL_FEATURE
}
- LOG(FATAL) << "Unsupported driver feature level: " << version;
+ LOG(FATAL) << "Unrecognized version " << versionLevel;
return -1;
}
@@ -605,7 +634,7 @@
SyncFence syncFence = SyncFence::createAsSignaled();
ExecuteFencedInfoCallback executeFencedInfoCallback = nullptr;
Timing timing = {};
- if (mDevice->getFeatureLevel() >= kHalVersionV1_3ToApi.featureLevel) {
+ if (isCompliantVersion(kHalVersionV1_3ToApi.canonical, mDevice->getFeatureLevel())) {
auto result = mPreparedModel->executeFenced(request, waitForHandles, measure, deadline,
loopTimeoutDuration, timeoutDurationAfterFence);
if (!result.ok()) {
@@ -743,7 +772,7 @@
SyncFence syncFence = SyncFence::createAsSignaled();
ExecuteFencedInfoCallback executeFencedInfoCallback = nullptr;
Timing timing = {};
- if (kDeviceFeatureLevel >= kHalVersionV1_3ToApi.featureLevel) {
+ if (isCompliantVersion(kHalVersionV1_3ToApi.canonical, kDeviceFeatureLevel)) {
auto result =
kExecution->computeFenced(waitForHandles, deadline, timeoutDurationAfterFence);
if (!result.ok()) {
@@ -838,7 +867,7 @@
const std::string& getName() const override { return kName; }
const std::string& getVersionString() const override { return kVersionString; }
- int64_t getFeatureLevel() const override { return kFeatureLevel; }
+ Version getFeatureLevel() const override { return kVersion; }
int32_t getType() const override { return ANEURALNETWORKS_DEVICE_CPU; }
bool isUpdatable() const override { return false; }
const std::vector<Extension>& getSupportedExtensions() const override {
@@ -873,7 +902,7 @@
private:
CpuDevice() = default;
- const int64_t kFeatureLevel = kCurrentNNAPIRuntimeFeatureLevel;
+ const Version kVersion = getRuntimeFeatureLevelVersion();
const std::string kName = "nnapi-reference";
#ifndef NN_COMPATIBILITY_LIBRARY_BUILD
const std::string kVersionString = build::GetBuildNumber();
@@ -979,6 +1008,17 @@
return result;
}
+template <typename Type>
+static Result<void> validateAndCheckCompliance(const Type& object) {
+ const auto version = NN_TRY(validate(object));
+ if (!isCompliantVersion(version, DeviceManager::get()->getRuntimeVersion())) {
+ return NN_ERROR() << "Object than is newer what is allowed. Version needed: " << version
+ << ", current runtime version supported: "
+ << DeviceManager::get()->getRuntimeVersion();
+ }
+ return {};
+}
+
std::pair<int, std::shared_ptr<RuntimePreparedModel>> CpuDevice::prepareModel(
const ModelFactory& makeModel, ExecutionPreference preference, Priority priority,
const OptionalTimePoint& deadline, const CacheInfo& /*cacheInfo*/,
@@ -987,15 +1027,15 @@
<< "Should never call prepareModel with cache information on CpuDevice";
const Model model = makeModel();
- if (auto result = validate(model); !result.ok()) {
+ if (auto result = validateAndCheckCompliance(model); !result.ok()) {
LOG(ERROR) << "Invalid Model: " << result.error();
return {ANEURALNETWORKS_OP_FAILED, nullptr};
}
- if (auto result = validate(preference); !result.ok()) {
+ if (auto result = validateAndCheckCompliance(preference); !result.ok()) {
LOG(ERROR) << "Invalid ExecutionPreference: " << result.error();
return {ANEURALNETWORKS_OP_FAILED, nullptr};
}
- if (auto result = validate(priority); !result.ok()) {
+ if (auto result = validateAndCheckCompliance(priority); !result.ok()) {
LOG(ERROR) << "Invalid Priority: " << result.error();
return {ANEURALNETWORKS_OP_FAILED, nullptr};
}
@@ -1219,6 +1259,10 @@
return {result, -1, nullptr, timing};
}
+int64_t DeviceManager::getRuntimeFeatureLevel() const {
+ return versionToFeatureLevel(mRuntimeVersion.level);
+}
+
DeviceManager* DeviceManager::get() {
static DeviceManager manager;
return &manager;
@@ -1293,6 +1337,7 @@
DeviceManager::DeviceManager() {
VLOG(MANAGER) << "DeviceManager::DeviceManager";
+ mRuntimeVersion = getRuntimeFeatureLevelVersion();
findAvailableDevices();
#ifdef NN_DEBUGGABLE
mStrictSlicing = (getProp("debug.nn.strict-slicing") != 0);
diff --git a/runtime/Manager.h b/runtime/Manager.h
index 2511e26..c92df8d 100644
--- a/runtime/Manager.h
+++ b/runtime/Manager.h
@@ -129,7 +129,7 @@
// Introspection methods returning device information
virtual const std::string& getName() const = 0;
virtual const std::string& getVersionString() const = 0;
- virtual int64_t getFeatureLevel() const = 0;
+ virtual Version getFeatureLevel() const = 0;
virtual int32_t getType() const = 0;
virtual bool isUpdatable() const = 0;
virtual const std::vector<Extension>& getSupportedExtensions() const = 0;
@@ -169,6 +169,15 @@
return mDevices;
}
+ // Gets the runtime version corresponding to getServerFeatureLevelFlag (in ServerFlag.h).
+ Version getRuntimeVersion() const { return mRuntimeVersion; }
+
+ // Gets the runtime feature level corresponding to getServerFeatureLevelFlag (in ServerFlag.h).
+ int64_t getRuntimeFeatureLevel() const;
+
+ // Convert the internal Version level representation to the NDK representation.
+ static int64_t versionToFeatureLevel(Version::Level versionLevel);
+
// For testing only:
void setUseCpuOnly(bool useCpuOnly) { mSetCpuOnly = useCpuOnly; }
bool getUseCpuOnly() const { return mSetCpuOnly; }
@@ -231,6 +240,9 @@
void findAvailableDevices();
+ // Runtime version corresponding to getServerFeatureLevelFlag (in ServerFlag.h).
+ Version mRuntimeVersion;
+
// List of all the devices we discovered (including CpuDevice).
std::vector<std::shared_ptr<Device>> mDevices;
diff --git a/runtime/Memory.cpp b/runtime/Memory.cpp
index 01adf6d..02b6fb8 100644
--- a/runtime/Memory.cpp
+++ b/runtime/Memory.cpp
@@ -25,6 +25,7 @@
#include <nnapi/SharedMemory.h>
#include <nnapi/TypeUtils.h>
#include <nnapi/Types.h>
+#include <nnapi/Validation.h>
#include <algorithm>
#include <memory>
@@ -465,7 +466,7 @@
}
#ifdef __ANDROID__
mSupportsAhwb = std::all_of(devices.begin(), devices.end(), [](const auto* device) {
- return device->getFeatureLevel() >= kHalVersionV1_3ToApi.featureLevel;
+ return isCompliantVersion(kHalVersionV1_3ToApi.canonical, device->getFeatureLevel());
});
#else // __ANDROID__
mSupportsAhwb = false;
diff --git a/runtime/ModelBuilder.cpp b/runtime/ModelBuilder.cpp
index 5ab182e..35f2f84 100644
--- a/runtime/ModelBuilder.cpp
+++ b/runtime/ModelBuilder.cpp
@@ -20,6 +20,7 @@
#include <GraphDump.h>
#include <LegacyUtils.h>
+#include <nnapi/Validation.h>
#include <algorithm>
#include <map>
@@ -535,8 +536,18 @@
// a CONSTANT_REFERENCE operand will not have correct .poolIndex, and
// validation will not work properly.
const Model modelForValidation = makeModel();
- if (auto result = validate(modelForValidation); !result.ok()) {
- LOG(ERROR) << "ANeuralNetworksModel_finish called on invalid model: " << result.error();
+ const auto maybeVersion = validate(modelForValidation);
+ if (!maybeVersion.ok()) {
+ LOG(ERROR) << "ANeuralNetworksModel_finish called on invalid model: "
+ << maybeVersion.error();
+ mInvalidModel = true;
+ return ANEURALNETWORKS_BAD_DATA;
+ }
+ if (!isCompliantVersion(maybeVersion.value(), DeviceManager::get()->getRuntimeVersion())) {
+ LOG(ERROR) << "ANeuralNetworksModel_finish called on a model that is newer what is "
+ "allowed. Model version needed: "
+ << maybeVersion.value() << ", current runtime version supported: "
+ << DeviceManager::get()->getRuntimeVersion();
mInvalidModel = true;
return ANEURALNETWORKS_BAD_DATA;
}
diff --git a/runtime/NeuralNetworks.cpp b/runtime/NeuralNetworks.cpp
index 9bdf3a7..fe487dc 100644
--- a/runtime/NeuralNetworks.cpp
+++ b/runtime/NeuralNetworks.cpp
@@ -39,7 +39,6 @@
#include "Event.h"
#include "ExecutionBuilder.h"
#include "ExecutionCallback.h"
-#include "FeatureLevel.h"
#include "Manager.h"
#include "Memory.h"
#include "ModelBuilder.h"
@@ -722,7 +721,7 @@
return ANEURALNETWORKS_UNEXPECTED_NULL;
}
Device* d = reinterpret_cast<Device*>(const_cast<ANeuralNetworksDevice*>(device));
- int64_t dFeatureLevel = d->getFeatureLevel();
+ int64_t dFeatureLevel = DeviceManager::versionToFeatureLevel(d->getFeatureLevel().level);
if (dFeatureLevel < 0) {
return ANEURALNETWORKS_BAD_STATE;
}
@@ -1651,7 +1650,7 @@
return sRuntimeFeatureLevel;
}
#endif
- return kCurrentNNAPIRuntimeFeatureLevel;
+ return DeviceManager::get()->getRuntimeFeatureLevel();
}
int ANeuralNetworksExecution_enableInputAndOutputPadding(ANeuralNetworksExecution* execution,
diff --git a/runtime/ServerFlag.cpp b/runtime/ServerFlag.cpp
index 0dbd369..17cac0c 100644
--- a/runtime/ServerFlag.cpp
+++ b/runtime/ServerFlag.cpp
@@ -16,36 +16,50 @@
#define LOG_TAG "ServerFlag"
-#include <FlagUtils.h>
+#include "ServerFlag.h"
+
#include <android-base/logging.h>
#include <android-base/parseint.h>
-#include <server_configurable_flags/get_flags.h>
+#include <nnapi/Types.h>
#include <stdint.h>
#include <string>
-namespace android {
-namespace nn {
+#if !defined(NN_COMPATIBILITY_LIBRARY_BUILD) && !defined(NN_EXPERIMENTAL_FEATURE)
+#include <server_configurable_flags/get_flags.h>
+#endif // !defined(NN_COMPATIBILITY_LIBRARY_BUILD) && !defined(NN_EXPERIMENTAL_FEATURE)
-namespace {
-int64_t getServerFlagInt(std::string flagName, int64_t defaultValue, int64_t minValue,
- int64_t maxValue) {
- int64_t flagValue = defaultValue;
- if (!android::base::ParseInt(
- server_configurable_flags::GetServerConfigurableFlag(
- std::string(kExprCategoryName), flagName, std::to_string(defaultValue)),
- &flagValue, minValue, maxValue)) {
- LOG(WARNING) << "Failed to parse flag " << flagName << " to int type. errno: " << errno;
+namespace android::nn {
+
+#if !defined(NN_COMPATIBILITY_LIBRARY_BUILD) && !defined(NN_EXPERIMENTAL_FEATURE)
+int64_t getServerFeatureLevelFlag() {
+ const std::string featureLevelString = server_configurable_flags::GetServerConfigurableFlag(
+ kExprCategoryName, kCurrentFeatureLevelFlagName,
+ std::to_string(kDefaultFeatureLevelNum));
+
+ int64_t featureLevel = kDefaultFeatureLevelNum;
+ const bool success = base::ParseInt(featureLevelString, &featureLevel, kMinFeatureLevelNum,
+ kMaxFeatureLevelNum);
+ if (!success) {
+ LOG(WARNING) << "Failed to parse result of GetServerConfigurableFlag, errno=" << errno;
}
- return flagValue;
+ return featureLevel;
+}
+#endif // !defined(NN_COMPATIBILITY_LIBRARY_BUILD) && !defined(NN_EXPERIMENTAL_FEATURE)
+
+Version serverFeatureLevelToVersion(int64_t serverFeatureLevel) {
+ Version version;
+ switch (serverFeatureLevel) {
+ case 5:
+ return kVersionFeatureLevel5;
+ case 6:
+ return kVersionFeatureLevel6;
+ case 7:
+ return kVersionFeatureLevel7;
+ default:
+ LOG(FATAL) << "Invalid feature level flag value " << serverFeatureLevel;
+ return {};
+ }
}
-} // namespace
-
-int64_t _getServerFeatureLevelFlag() {
- return getServerFlagInt(kCurrentFeatureLevelFlagName, kDefaultFeatureLevelNum,
- kMinFeatureLevelNum, kMaxFeatureLevelNum);
-}
-
-} // namespace nn
-} // namespace android
\ No newline at end of file
+} // namespace android::nn
diff --git a/runtime/ServerFlag.h b/runtime/ServerFlag.h
new file mode 100644
index 0000000..fdc4f52
--- /dev/null
+++ b/runtime/ServerFlag.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * 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
+ *
+ * http://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.
+ */
+
+#ifndef ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_SERVER_FLAG_H
+#define ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_SERVER_FLAG_H
+
+#include <nnapi/Types.h>
+#include <stdint.h>
+
+#include "NeuralNetworks.h"
+
+namespace android::nn {
+
+// Keep these values consistent with server side configuration in
+// google3/googledata/experiments/mobile/android_platform/nnapi_native/features/feature_level.gcl.
+constexpr char kExprCategoryName[] = "nnapi_native";
+constexpr char kCurrentFeatureLevelFlagName[] = "current_feature_level";
+constexpr int64_t kDefaultFeatureLevelNum = 5;
+// When this value is updated, update kMinFeatureLevelCode in runtime/test/TestUpdatability.cpp with
+// the corresponding ANEURALNETWORKS_FEATURE_LEVEL_* version.
+constexpr int64_t kMinFeatureLevelNum = 5;
+constexpr int64_t kMaxFeatureLevelNum = 7;
+
+// Function to get server feature level flag. Note that this function should NOT be used directly.
+// Instead, clients are expected to use DeviceManager::getRuntimeVersion or
+// DeviceManager::getRuntimeFeatureLevel in runtime/Manager.h.
+#if !defined(NN_COMPATIBILITY_LIBRARY_BUILD) && !defined(NN_EXPERIMENTAL_FEATURE)
+int64_t getServerFeatureLevelFlag();
+#endif // !defined(NN_COMPATIBILITY_LIBRARY_BUILD) && !defined(NN_EXPERIMENTAL_FEATURE)
+
+// Get the runtime version corresponding to the server feature flag value.
+Version serverFeatureLevelToVersion(int64_t serverFeatureLevel);
+
+} // namespace android::nn
+
+#endif // ANDROID_PACKAGES_MODULES_NEURALNETWORKS_RUNTIME_SERVER_FLAG_H
diff --git a/runtime/SupportLibraryDiagnostic.cpp b/runtime/SupportLibraryDiagnostic.cpp
index 372dabb..1ff03f7 100644
--- a/runtime/SupportLibraryDiagnostic.cpp
+++ b/runtime/SupportLibraryDiagnostic.cpp
@@ -20,7 +20,6 @@
#include <utility>
#include "ExecutionBuilder.h"
-#include "FeatureLevel.h"
#include "NeuralNetworksSupportLibraryImpl.h"
#include "Telemetry.h"
@@ -94,7 +93,7 @@
int64_t SL_ANeuralNetworksDiagnosticCompilationInfo_getNnApiVersion(
const ANeuralNetworksDiagnosticCompilationInfo* /*diagnosticCompilationInfo*/) {
- return android::nn::kCurrentNNAPIRuntimeFeatureLevel;
+ return android::nn::DeviceManager::get()->getRuntimeFeatureLevel();
}
const uint8_t* SL_ANeuralNetworksDiagnosticCompilationInfo_getModelArchHash(
@@ -149,7 +148,7 @@
int64_t SL_ANeuralNetworksDiagnosticExecutionInfo_getNnApiVersion(
const ANeuralNetworksDiagnosticExecutionInfo* /*diagnosticExecutionInfo*/) {
- return android::nn::kCurrentNNAPIRuntimeFeatureLevel;
+ return android::nn::DeviceManager::get()->getRuntimeFeatureLevel();
}
const uint8_t* SL_ANeuralNetworksDiagnosticExecutionInfo_getModelArchHash(
diff --git a/runtime/Telemetry.cpp b/runtime/Telemetry.cpp
index 698e7c5..0460918 100644
--- a/runtime/Telemetry.cpp
+++ b/runtime/Telemetry.cpp
@@ -25,7 +25,6 @@
#include <utility>
#include <vector>
-#include "FeatureLevel.h"
#include "Manager.h"
#include "NeuralNetworks.h"
diff --git a/runtime/test/Android.bp b/runtime/test/Android.bp
index 0205b18..c1425b8 100644
--- a/runtime/test/Android.bp
+++ b/runtime/test/Android.bp
@@ -58,6 +58,7 @@
"libneuralnetworks_generated_test_harness",
"libtextclassifier_hash_static",
"neuralnetworks_utils_hal_service",
+ "server_configurable_flags",
],
whole_static_libs: [
"libcrypto_static",
@@ -124,6 +125,7 @@
],
whole_static_libs: [
"neuralnetworks_generated_AIDL_V2_example",
+ "neuralnetworks_generated_AIDL_V3_example",
"neuralnetworks_generated_V1_0_example",
"neuralnetworks_generated_V1_1_example",
"neuralnetworks_generated_V1_2_example",
@@ -228,6 +230,7 @@
"libneuralnetworks_common",
"libneuralnetworks_static",
"neuralnetworks_types",
+ "server_configurable_flags",
],
static_libs: [
"libneuralnetworks_common_experimental",
@@ -406,6 +409,7 @@
],
whole_static_libs: [
"neuralnetworks_generated_AIDL_V2_example",
+ "neuralnetworks_generated_AIDL_V3_example",
"neuralnetworks_generated_V1_0_example",
"neuralnetworks_generated_V1_1_example",
"neuralnetworks_generated_V1_2_example",
@@ -565,6 +569,12 @@
}
cc_library_static {
+ name: "neuralnetworks_generated_AIDL_V3_example",
+ defaults: ["neuralnetworks_generated_defaults"],
+ srcs: ["generated/spec_AIDL_V3/*.example.cpp"],
+}
+
+cc_library_static {
name: "neuralnetworks_generated_V1_3_cts_only_example",
host_supported: true,
defaults: ["neuralnetworks_float16"],
diff --git a/runtime/test/TestGenerated.cpp b/runtime/test/TestGenerated.cpp
index 5673923..63fa970 100644
--- a/runtime/test/TestGenerated.cpp
+++ b/runtime/test/TestGenerated.cpp
@@ -35,6 +35,8 @@
#include "AndroidVersionUtil.h"
#include "GeneratedTestUtils.h"
+#include "NeuralNetworks.h"
+#include "NeuralNetworksTypes.h"
#include "TestHarness.h"
#include "TestNeuralNetworksWrapper.h"
#include "TestUtils.h"
@@ -401,6 +403,41 @@
}
}
+static int64_t getRuntimeFeatureLevel() {
+ if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) {
+ return ANeuralNetworks_getRuntimeFeatureLevel();
+ }
+#if defined(__BIONIC__)
+ return android_get_device_api_level();
+#else
+ return __ANDROID_API__;
+#endif // __BIONIC__
+}
+
+static std::optional<int64_t> halVersionToFeatureLevel(TestHalVersion halVersion) {
+ switch (halVersion) {
+ case TestHalVersion::UNKNOWN:
+ return std::nullopt;
+ case TestHalVersion::V1_0:
+ return ANEURALNETWORKS_FEATURE_LEVEL_1;
+ case TestHalVersion::V1_1:
+ return ANEURALNETWORKS_FEATURE_LEVEL_2;
+ case TestHalVersion::V1_2:
+ return ANEURALNETWORKS_FEATURE_LEVEL_3;
+ case TestHalVersion::V1_3:
+ return ANEURALNETWORKS_FEATURE_LEVEL_4;
+ case TestHalVersion::AIDL_V1:
+ return ANEURALNETWORKS_FEATURE_LEVEL_5;
+ case TestHalVersion::AIDL_V2:
+ return ANEURALNETWORKS_FEATURE_LEVEL_6;
+ case TestHalVersion::AIDL_V3:
+ return ANEURALNETWORKS_FEATURE_LEVEL_7;
+ }
+ LOG(FATAL) << "Unrecognized TestHalVersion "
+ << static_cast<std::underlying_type_t<TestHalVersion>>(halVersion);
+ return std::nullopt;
+}
+
bool GeneratedTests::shouldSkipTest() {
// A map of {min VNDK version -> tests that should be skipped with earlier VNDK versions}.
// The listed tests are added in a later release, but exercising old APIs. They should be
@@ -418,6 +455,13 @@
return true;
}
}
+
+ // Skip test cases that are newer than what is allowed by
+ // ANeuralNetworks_getRuntimeFeatureLevel.
+ if (const auto featureLevelNeeded = halVersionToFeatureLevel(testModel.minSupportedVersion)) {
+ return featureLevelNeeded.value() > getRuntimeFeatureLevel();
+ }
+
return false;
}
diff --git a/runtime/test/TestPartitioningRandom.cpp b/runtime/test/TestPartitioningRandom.cpp
index 73ab165..225ffdd 100644
--- a/runtime/test/TestPartitioningRandom.cpp
+++ b/runtime/test/TestPartitioningRandom.cpp
@@ -1122,7 +1122,8 @@
compilationResult == Result::OP_FAILED && hasUnknownDimensions &&
cNoFallback.getExecutionPlan().hasDynamicTemporaries() &&
std::any_of(devices.begin(), devices.end(), [](const std::shared_ptr<Device>& device) {
- return device->getFeatureLevel() < nn::kHalVersionV1_2ToApi.featureLevel;
+ return !isCompliantVersion(nn::kHalVersionV1_2ToApi.canonical,
+ device->getFeatureLevel());
});
const bool fallbackNeededForStepModelWithNoInputsOrNoOutputs =
cNoFallback.getExecutionPlan().forTest_hasStepModelWithNoInputsOrNoOutputs();
diff --git a/runtime/test/TestUpdatability.cpp b/runtime/test/TestUpdatability.cpp
index bd5b517..1825b3e 100644
--- a/runtime/test/TestUpdatability.cpp
+++ b/runtime/test/TestUpdatability.cpp
@@ -20,9 +20,15 @@
class UpdatabilityTest : public ::testing::Test {};
+// Keep this value in sync with the corresponding value of kMinFeatureLevelNum in
+// runtime/ServerFlag.h.
+constexpr int64_t kMinFeatureLevelCode = ANEURALNETWORKS_FEATURE_LEVEL_5;
+
TEST_F(UpdatabilityTest, GetFeatureLevel) {
if (__builtin_available(android __NNAPI_FL5_MIN_ANDROID_API__, *)) {
- EXPECT_GE(ANeuralNetworks_getRuntimeFeatureLevel(), ANEURALNETWORKS_FEATURE_LEVEL_5);
+ // Ensure that the feature level returned is never less than what is allowed by the server
+ // feature level flag.
+ EXPECT_GE(ANeuralNetworks_getRuntimeFeatureLevel(), kMinFeatureLevelCode);
} else {
GTEST_SKIP();
}
diff --git a/runtime/test/TestValidateOperations.cpp b/runtime/test/TestValidateOperations.cpp
index 61d11a9..44d2382 100644
--- a/runtime/test/TestValidateOperations.cpp
+++ b/runtime/test/TestValidateOperations.cpp
@@ -1025,6 +1025,16 @@
});
}
+// Test quantization parameters that are inconsistent among operands.
+enum class BadQuantization { NONE, zeroPoint, scale };
+void scramble(ANeuralNetworksOperandType* type, BadQuantization bad) {
+ if (bad == BadQuantization::zeroPoint) {
+ type->zeroPoint = 1;
+ } else if (bad == BadQuantization::scale) {
+ type->scale *= 2;
+ }
+};
+
void argMinMaxTest(ANeuralNetworksOperationType operationCode, int32_t inputOperandType) {
SCOPED_TRACE(inputOperandType);
uint32_t inputDimensions[4] = {2, 2, 2, 2};
@@ -1498,6 +1508,14 @@
activationOpTest(ANEURALNETWORKS_RSQRT, ANEURALNETWORKS_TENSOR_FLOAT32);
}
+TEST(OperationValidationTest, RSQRT_quant8) {
+ activationOpTest(ANEURALNETWORKS_RSQRT, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
+}
+
+TEST(OperationValidationTest, RSQRT_quant8_signed) {
+ activationOpTest(ANEURALNETWORKS_RSQRT, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED);
+}
+
TEST(OperationValidationTest, SIN_float16) {
activationOpTest(ANEURALNETWORKS_SIN, ANEURALNETWORKS_TENSOR_FLOAT16);
}
@@ -1741,25 +1759,44 @@
meanOpTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED);
}
-void padOpTest(int32_t inputOperandCode) {
+void padOpTest(ANeuralNetworksOperationType operationCode, int32_t inputOperandCode) {
SCOPED_TRACE(inputOperandCode);
+
uint32_t inputDimensions[4] = {2, 2, 2, 2};
ANeuralNetworksOperandType input = getOpType(inputOperandCode, 4, inputDimensions);
- uint32_t padSizeDimensions[1] = {4};
+ uint32_t padSizeDimensions[2] = {4, 2};
ANeuralNetworksOperandType padSize =
- getOpType(ANEURALNETWORKS_TENSOR_INT32, 1, padSizeDimensions);
+ getOpType(ANEURALNETWORKS_TENSOR_INT32, 2, padSizeDimensions);
+ std::vector<ANeuralNetworksOperandType> inputs = {input, padSize};
+ if (operationCode == ANEURALNETWORKS_MIRROR_PAD) {
+ inputs.push_back(getOpType(ANEURALNETWORKS_INT32));
+ }
+
uint32_t outputDimensions[4] = {4, 3, 4, 3};
ANeuralNetworksOperandType output = getOpType(inputOperandCode, 4, outputDimensions);
- OperationTestBase test(ANEURALNETWORKS_PAD, {input, padSize}, {output},
- {{TensorRankConstraint::UpTo(4)}});
+
+ std::vector<TensorRankMutator> inputRankMutators;
+ if (operationCode == ANEURALNETWORKS_PAD) {
+ inputRankMutators.push_back({TensorRankConstraint::UpTo(4)});
+ }
+
+ OperationTestBase test(operationCode, inputs, {output}, inputRankMutators);
test.testOpsValidations();
}
TEST(OperationValidationTest, PAD) {
- padOpTest(ANEURALNETWORKS_TENSOR_FLOAT16);
- padOpTest(ANEURALNETWORKS_TENSOR_FLOAT32);
- padOpTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
- padOpTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED);
+ padOpTest(ANEURALNETWORKS_PAD, ANEURALNETWORKS_TENSOR_FLOAT16);
+ padOpTest(ANEURALNETWORKS_PAD, ANEURALNETWORKS_TENSOR_FLOAT32);
+ padOpTest(ANEURALNETWORKS_PAD, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
+ padOpTest(ANEURALNETWORKS_PAD, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED);
+}
+
+TEST(OperationValidationTest, MIRROR_PAD) {
+ padOpTest(ANEURALNETWORKS_MIRROR_PAD, ANEURALNETWORKS_TENSOR_FLOAT16);
+ padOpTest(ANEURALNETWORKS_MIRROR_PAD, ANEURALNETWORKS_TENSOR_FLOAT32);
+ padOpTest(ANEURALNETWORKS_MIRROR_PAD, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
+ padOpTest(ANEURALNETWORKS_MIRROR_PAD, ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED);
+ padOpTest(ANEURALNETWORKS_MIRROR_PAD, ANEURALNETWORKS_TENSOR_INT32);
}
void padV2OpTest(int32_t inputOperandCode) {
@@ -4734,16 +4771,7 @@
}
// Test quantization parameters that are inconsistent among operands.
-enum class PackBadQuantization { NONE, zeroPoint, scale };
-void packTestBadQuantization(int32_t operandCode, PackBadQuantization bad) {
- auto scramble = [bad](ANeuralNetworksOperandType* type) {
- if (bad == PackBadQuantization::zeroPoint) {
- type->zeroPoint = 1;
- } else if (bad == PackBadQuantization::scale) {
- type->scale *= 2;
- }
- };
-
+void packTestBadQuantization(int32_t operandCode, BadQuantization bad) {
constexpr uint32_t inputTensorCount = 2;
const uint32_t inputDimensions[3] = {4, 5, 6};
constexpr size_t inputRank = sizeof(inputDimensions) / sizeof(inputDimensions[0]);
@@ -4767,12 +4795,12 @@
ANeuralNetworksOperandType outputType =
getOpType(operandCode, outputRank, outputDimensions);
if (deviant == inputTensorCount) {
- scramble(&outputType);
+ scramble(&outputType, bad);
} else {
- scramble(&inputTypes[1 + deviant]);
+ scramble(&inputTypes[1 + deviant], bad);
}
OperationTestBase packTest(ANEURALNETWORKS_PACK, inputTypes, {outputType});
- if (bad == PackBadQuantization::NONE) {
+ if (bad == BadQuantization::NONE) {
packTest.testSuccess();
return;
} else {
@@ -4784,24 +4812,23 @@
TEST(OperationValidationTest, PACK_quant8_bad_none) {
// Make sure packTestBadQuantization starts with a valid operation and only corrupts what it
// intends to.
- packTestBadQuantization(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM, PackBadQuantization::NONE);
+ packTestBadQuantization(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM, BadQuantization::NONE);
}
TEST(OperationValidationTest, PACK_quant8_bad_zeroPoint) {
- packTestBadQuantization(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM, PackBadQuantization::zeroPoint);
+ packTestBadQuantization(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM, BadQuantization::zeroPoint);
}
TEST(OperationValidationTest, PACK_quant8_bad_scale) {
- packTestBadQuantization(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM, PackBadQuantization::scale);
+ packTestBadQuantization(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM, BadQuantization::scale);
}
TEST(OperationValidationTest, PACK_quant8_signed_bad_zeroPoint) {
- packTestBadQuantization(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED,
- PackBadQuantization::zeroPoint);
+ packTestBadQuantization(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED, BadQuantization::zeroPoint);
}
TEST(OperationValidationTest, PACK_quant8_signed_bad_scale) {
- packTestBadQuantization(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED, PackBadQuantization::scale);
+ packTestBadQuantization(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED, BadQuantization::scale);
}
// Test ranks that are inconsistent among operands.
@@ -4880,4 +4907,160 @@
packTestBadRank(ANEURALNETWORKS_TENSOR_FLOAT32, 1);
}
+void reverseTest(int32_t operandCode) {
+ const uint32_t tensorDimensions[3] = {4, 5, 6};
+ constexpr size_t tensorRank = sizeof(tensorDimensions) / sizeof(tensorDimensions[0]);
+ const ANeuralNetworksOperandType tensorType =
+ getOpType(operandCode, tensorRank, tensorDimensions);
+
+ const uint32_t axisDimensions[1] = {0};
+ constexpr size_t axisRank = sizeof(axisDimensions) / sizeof(axisDimensions[0]);
+ const ANeuralNetworksOperandType axisType =
+ getOpType(ANEURALNETWORKS_TENSOR_INT32, axisRank, axisDimensions);
+
+ OperationTestBase reverseTest(ANEURALNETWORKS_REVERSE, {tensorType, axisType}, {tensorType});
+ reverseTest.testOpsValidations();
+}
+
+TEST(OperationValidationTest, REVERSE_float16) {
+ reverseTest(ANEURALNETWORKS_TENSOR_FLOAT16);
+}
+
+TEST(OperationValidationTest, REVERSE_float32) {
+ reverseTest(ANEURALNETWORKS_TENSOR_FLOAT32);
+}
+
+TEST(OperationValidationTest, REVERSE_quant8) {
+ reverseTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM);
+}
+
+TEST(OperationValidationTest, REVERSE_quant8_signed) {
+ reverseTest(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED);
+}
+
+TEST(OperationValidationTest, REVERSE_int32) {
+ reverseTest(ANEURALNETWORKS_TENSOR_INT32);
+}
+
+// Test quantization parameters that are inconsistent among operands.
+void reverseTestBadQuantization(int32_t operandCode, BadQuantization bad) {
+ const uint32_t tensorDimensions[3] = {4, 5, 6};
+ constexpr size_t tensorRank = sizeof(tensorDimensions) / sizeof(tensorDimensions[0]);
+ const ANeuralNetworksOperandType tensorType =
+ getOpType(operandCode, tensorRank, tensorDimensions);
+
+ const uint32_t axisDimensions[1] = {0};
+ constexpr size_t axisRank = sizeof(axisDimensions) / sizeof(axisDimensions[0]);
+ const ANeuralNetworksOperandType axisType =
+ getOpType(ANEURALNETWORKS_TENSOR_INT32, axisRank, axisDimensions);
+
+ ANeuralNetworksOperandType outputType = tensorType;
+ scramble(&outputType, bad);
+
+ OperationTestBase reverseTest(ANEURALNETWORKS_REVERSE, {tensorType, axisType}, {outputType});
+ if (bad == BadQuantization::NONE) {
+ reverseTest.testSuccess();
+ return;
+ } else {
+ reverseTest.testFailure(ANEURALNETWORKS_BAD_DATA);
+ }
+}
+
+TEST(OperationValidationTest, REVERSE_quant8_bad_none) {
+ // Make sure reverseTestBadQuantization starts with a valid operation and only corrupts what it
+ // intends to.
+ reverseTestBadQuantization(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM, BadQuantization::NONE);
+}
+
+TEST(OperationValidationTest, REVERSE_quant8_bad_zeroPoint) {
+ reverseTestBadQuantization(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM, BadQuantization::zeroPoint);
+}
+
+TEST(OperationValidationTest, REVERSE_quant8_bad_scale) {
+ reverseTestBadQuantization(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM, BadQuantization::scale);
+}
+
+TEST(OperationValidationTest, REVERSE_quant8_signed_bad_zeroPoint) {
+ reverseTestBadQuantization(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED,
+ BadQuantization::zeroPoint);
+}
+
+TEST(OperationValidationTest, REVERSE_quant8_signed_bad_scale) {
+ reverseTestBadQuantization(ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED, BadQuantization::scale);
+}
+
+// Test ranks that are inconsistent among operands or otherwise incorrect.
+void reverseTestBadRank(uint32_t operandCode, int adjustRank) {
+ const uint32_t tensorDimensions[3] = {4, 5, 6};
+ constexpr size_t tensorRank = sizeof(tensorDimensions) / sizeof(tensorDimensions[0]);
+ const ANeuralNetworksOperandType tensorType =
+ getOpType(operandCode, tensorRank, tensorDimensions);
+
+ const uint32_t axisDimensions[1] = {0};
+ constexpr size_t axisRank = sizeof(axisDimensions) / sizeof(axisDimensions[0]);
+ const ANeuralNetworksOperandType axisType =
+ getOpType(ANEURALNETWORKS_TENSOR_INT32, axisRank, axisDimensions);
+
+ constexpr size_t kOperandCount = 3; // 2 inputs, 1 output
+
+ // The "deviant" is the operand whose rank is to be changed.
+ for (uint32_t deviant = 0; deviant < kOperandCount; ++deviant) {
+ SCOPED_TRACE(deviant);
+
+ // input 0, input 1, output 0
+ std::vector<ANeuralNetworksOperandType> operands = {tensorType, axisType, tensorType};
+ ASSERT_EQ(operands.size(), kOperandCount);
+
+ std::vector<uint32_t> scrambledDimensions;
+ auto scramble = [adjustRank,
+ &scrambledDimensions](ANeuralNetworksOperandType* type) -> bool {
+ if (!adjustRank) {
+ return true;
+ }
+ if (adjustRank < 0) {
+ if (type->dimensionCount <= uint32_t(-adjustRank)) {
+ // not a valid test scenario
+ return false;
+ }
+ type->dimensionCount += adjustRank;
+ return true;
+ }
+ const uint32_t oldRank = type->dimensionCount;
+ const uint32_t newRank = oldRank + adjustRank;
+ EXPECT_EQ(scrambledDimensions.size(), size_t(0)); // only use this vector once
+ scrambledDimensions.assign(&type->dimensions[0], &type->dimensions[oldRank]);
+ scrambledDimensions.resize(newRank, /* arbitrary choice */ 7);
+ type->dimensionCount = newRank;
+ type->dimensions = &scrambledDimensions[0];
+ return true;
+ };
+
+ if (!scramble(&operands[deviant])) {
+ continue;
+ }
+ OperationTestBase reverseTest(ANEURALNETWORKS_REVERSE, {operands[0], operands[1]},
+ {operands[2]});
+ if (adjustRank) {
+ reverseTest.testFailure(ANEURALNETWORKS_BAD_DATA);
+ } else {
+ reverseTest.testSuccess();
+ return;
+ }
+ }
+}
+
+TEST(OperationValidationTest, REVERSE_float32_rank_good) {
+ // Make sure reverseTestBadRank starts with a valid operation and only corrupts it when it
+ // intends to.
+ reverseTestBadRank(ANEURALNETWORKS_TENSOR_FLOAT32, 0);
+}
+
+TEST(OperationValidationTest, REVERSE_float32_rank_lo) {
+ reverseTestBadRank(ANEURALNETWORKS_TENSOR_FLOAT32, -1);
+}
+
+TEST(OperationValidationTest, REVERSE_float32_rank_hi) {
+ reverseTestBadRank(ANEURALNETWORKS_TENSOR_FLOAT32, 1);
+}
+
} // end namespace
diff --git a/runtime/test/generated/spec_AIDL_V3/rsqrt_quant8.example.cpp b/runtime/test/generated/spec_AIDL_V3/rsqrt_quant8.example.cpp
new file mode 100644
index 0000000..e8828ab
--- /dev/null
+++ b/runtime/test/generated/spec_AIDL_V3/rsqrt_quant8.example.cpp
@@ -0,0 +1,518 @@
+// Generated from rsqrt_quant8.mod.py
+// DO NOT EDIT
+// clang-format off
+#include "TestHarness.h"
+using namespace test_helper;
+
+namespace generated_tests::rsqrt_quant8 {
+
+const TestModel& get_test_model_25h_0_25h_0() {
+ static TestModel model = {
+ .expectFailure = false,
+ .expectedMultinomialDistributionTolerance = 0,
+ .isRelaxed = false,
+ .main = {
+ .inputIndexes = {0},
+ .operands = {{ // input0
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<uint8_t>({1, 4, 16, 64}),
+ .dimensions = {4},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::SUBGRAPH_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 0.25f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM,
+ .zeroPoint = 0
+ }, { // output0
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<uint8_t>({8, 4, 2, 1}),
+ .dimensions = {4},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::SUBGRAPH_OUTPUT,
+ .numberOfConsumers = 0,
+ .scale = 0.25f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM,
+ .zeroPoint = 0
+ }},
+ .operations = {{
+ .inputs = {0},
+ .outputs = {1},
+ .type = TestOperationType::RSQRT
+ }},
+ .outputIndexes = {1}
+ },
+ .minSupportedVersion = TestHalVersion::AIDL_V3,
+ .referenced = {}
+ };
+ return model;
+}
+
+const auto dummy_test_model_25h_0_25h_0 = TestModelManager::get().add("rsqrt_quant8_25h_0_25h_0", get_test_model_25h_0_25h_0());
+
+} // namespace generated_tests::rsqrt_quant8
+
+namespace generated_tests::rsqrt_quant8 {
+
+const TestModel& get_test_model_25h_0_25h_0_all_inputs_as_internal() {
+ static TestModel model = {
+ .expectFailure = false,
+ .expectedMultinomialDistributionTolerance = 0,
+ .isRelaxed = false,
+ .main = {
+ .inputIndexes = {2},
+ .operands = {{ // input0
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<uint8_t>({}),
+ .dimensions = {4},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::TEMPORARY_VARIABLE,
+ .numberOfConsumers = 1,
+ .scale = 0.25f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM,
+ .zeroPoint = 0
+ }, { // output0
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<uint8_t>({8, 4, 2, 1}),
+ .dimensions = {4},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::SUBGRAPH_OUTPUT,
+ .numberOfConsumers = 0,
+ .scale = 0.25f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM,
+ .zeroPoint = 0
+ }, { // input0_new
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<uint8_t>({1, 4, 16, 64}),
+ .dimensions = {4},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::SUBGRAPH_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 0.25f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM,
+ .zeroPoint = 0
+ }, { // placeholder
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<uint8_t>({0}),
+ .dimensions = {1},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::CONSTANT_COPY,
+ .numberOfConsumers = 1,
+ .scale = 0.25f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM,
+ .zeroPoint = 0
+ }, { // param
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int32_t>({0}),
+ .dimensions = {},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::CONSTANT_COPY,
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .type = TestOperandType::INT32,
+ .zeroPoint = 0
+ }},
+ .operations = {{
+ .inputs = {2, 3, 4},
+ .outputs = {0},
+ .type = TestOperationType::ADD
+ }, {
+ .inputs = {0},
+ .outputs = {1},
+ .type = TestOperationType::RSQRT
+ }},
+ .outputIndexes = {1}
+ },
+ .minSupportedVersion = TestHalVersion::AIDL_V3,
+ .referenced = {}
+ };
+ return model;
+}
+
+const auto dummy_test_model_25h_0_25h_0_all_inputs_as_internal = TestModelManager::get().add("rsqrt_quant8_25h_0_25h_0_all_inputs_as_internal", get_test_model_25h_0_25h_0_all_inputs_as_internal());
+
+} // namespace generated_tests::rsqrt_quant8
+
+namespace generated_tests::rsqrt_quant8 {
+
+const TestModel& get_test_model_25h_0_1h_75() {
+ static TestModel model = {
+ .expectFailure = false,
+ .expectedMultinomialDistributionTolerance = 0,
+ .isRelaxed = false,
+ .main = {
+ .inputIndexes = {0},
+ .operands = {{ // input01
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<uint8_t>({16, 64}),
+ .dimensions = {2},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::SUBGRAPH_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 0.25f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM,
+ .zeroPoint = 0
+ }, { // output01
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<uint8_t>({125, 100}),
+ .dimensions = {2},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::SUBGRAPH_OUTPUT,
+ .numberOfConsumers = 0,
+ .scale = 0.01f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM,
+ .zeroPoint = 75
+ }},
+ .operations = {{
+ .inputs = {0},
+ .outputs = {1},
+ .type = TestOperationType::RSQRT
+ }},
+ .outputIndexes = {1}
+ },
+ .minSupportedVersion = TestHalVersion::AIDL_V3,
+ .referenced = {}
+ };
+ return model;
+}
+
+const auto dummy_test_model_25h_0_1h_75 = TestModelManager::get().add("rsqrt_quant8_25h_0_1h_75", get_test_model_25h_0_1h_75());
+
+} // namespace generated_tests::rsqrt_quant8
+
+namespace generated_tests::rsqrt_quant8 {
+
+const TestModel& get_test_model_25h_0_1h_75_all_inputs_as_internal() {
+ static TestModel model = {
+ .expectFailure = false,
+ .expectedMultinomialDistributionTolerance = 0,
+ .isRelaxed = false,
+ .main = {
+ .inputIndexes = {2},
+ .operands = {{ // input01
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<uint8_t>({}),
+ .dimensions = {2},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::TEMPORARY_VARIABLE,
+ .numberOfConsumers = 1,
+ .scale = 0.25f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM,
+ .zeroPoint = 0
+ }, { // output01
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<uint8_t>({125, 100}),
+ .dimensions = {2},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::SUBGRAPH_OUTPUT,
+ .numberOfConsumers = 0,
+ .scale = 0.01f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM,
+ .zeroPoint = 75
+ }, { // input01_new
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<uint8_t>({16, 64}),
+ .dimensions = {2},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::SUBGRAPH_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 0.25f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM,
+ .zeroPoint = 0
+ }, { // placeholder1
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<uint8_t>({0}),
+ .dimensions = {1},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::CONSTANT_COPY,
+ .numberOfConsumers = 1,
+ .scale = 0.25f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM,
+ .zeroPoint = 0
+ }, { // param1
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int32_t>({0}),
+ .dimensions = {},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::CONSTANT_COPY,
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .type = TestOperandType::INT32,
+ .zeroPoint = 0
+ }},
+ .operations = {{
+ .inputs = {2, 3, 4},
+ .outputs = {0},
+ .type = TestOperationType::ADD
+ }, {
+ .inputs = {0},
+ .outputs = {1},
+ .type = TestOperationType::RSQRT
+ }},
+ .outputIndexes = {1}
+ },
+ .minSupportedVersion = TestHalVersion::AIDL_V3,
+ .referenced = {}
+ };
+ return model;
+}
+
+const auto dummy_test_model_25h_0_1h_75_all_inputs_as_internal = TestModelManager::get().add("rsqrt_quant8_25h_0_1h_75_all_inputs_as_internal", get_test_model_25h_0_1h_75_all_inputs_as_internal());
+
+} // namespace generated_tests::rsqrt_quant8
+
+namespace generated_tests::rsqrt_quant8 {
+
+const TestModel& get_test_model_125t_10_25h_0() {
+ static TestModel model = {
+ .expectFailure = false,
+ .expectedMultinomialDistributionTolerance = 0,
+ .isRelaxed = false,
+ .main = {
+ .inputIndexes = {0},
+ .operands = {{ // input02
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<uint8_t>({12, 18, 42}),
+ .dimensions = {3},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::SUBGRAPH_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 0.125f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM,
+ .zeroPoint = 10
+ }, { // output02
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<uint8_t>({8, 4, 2}),
+ .dimensions = {3},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::SUBGRAPH_OUTPUT,
+ .numberOfConsumers = 0,
+ .scale = 0.25f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM,
+ .zeroPoint = 0
+ }},
+ .operations = {{
+ .inputs = {0},
+ .outputs = {1},
+ .type = TestOperationType::RSQRT
+ }},
+ .outputIndexes = {1}
+ },
+ .minSupportedVersion = TestHalVersion::AIDL_V3,
+ .referenced = {}
+ };
+ return model;
+}
+
+const auto dummy_test_model_125t_10_25h_0 = TestModelManager::get().add("rsqrt_quant8_125t_10_25h_0", get_test_model_125t_10_25h_0());
+
+} // namespace generated_tests::rsqrt_quant8
+
+namespace generated_tests::rsqrt_quant8 {
+
+const TestModel& get_test_model_125t_10_25h_0_all_inputs_as_internal() {
+ static TestModel model = {
+ .expectFailure = false,
+ .expectedMultinomialDistributionTolerance = 0,
+ .isRelaxed = false,
+ .main = {
+ .inputIndexes = {2},
+ .operands = {{ // input02
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<uint8_t>({}),
+ .dimensions = {3},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::TEMPORARY_VARIABLE,
+ .numberOfConsumers = 1,
+ .scale = 0.125f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM,
+ .zeroPoint = 10
+ }, { // output02
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<uint8_t>({8, 4, 2}),
+ .dimensions = {3},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::SUBGRAPH_OUTPUT,
+ .numberOfConsumers = 0,
+ .scale = 0.25f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM,
+ .zeroPoint = 0
+ }, { // input02_new
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<uint8_t>({12, 18, 42}),
+ .dimensions = {3},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::SUBGRAPH_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 0.125f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM,
+ .zeroPoint = 10
+ }, { // placeholder2
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<uint8_t>({10}),
+ .dimensions = {1},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::CONSTANT_COPY,
+ .numberOfConsumers = 1,
+ .scale = 0.125f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM,
+ .zeroPoint = 10
+ }, { // param2
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int32_t>({0}),
+ .dimensions = {},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::CONSTANT_COPY,
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .type = TestOperandType::INT32,
+ .zeroPoint = 0
+ }},
+ .operations = {{
+ .inputs = {2, 3, 4},
+ .outputs = {0},
+ .type = TestOperationType::ADD
+ }, {
+ .inputs = {0},
+ .outputs = {1},
+ .type = TestOperationType::RSQRT
+ }},
+ .outputIndexes = {1}
+ },
+ .minSupportedVersion = TestHalVersion::AIDL_V3,
+ .referenced = {}
+ };
+ return model;
+}
+
+const auto dummy_test_model_125t_10_25h_0_all_inputs_as_internal = TestModelManager::get().add("rsqrt_quant8_125t_10_25h_0_all_inputs_as_internal", get_test_model_125t_10_25h_0_all_inputs_as_internal());
+
+} // namespace generated_tests::rsqrt_quant8
+
+namespace generated_tests::rsqrt_quant8 {
+
+const TestModel& get_test_model_125t_10_1h_75() {
+ static TestModel model = {
+ .expectFailure = false,
+ .expectedMultinomialDistributionTolerance = 0,
+ .isRelaxed = false,
+ .main = {
+ .inputIndexes = {0},
+ .operands = {{ // input03
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<uint8_t>({42}),
+ .dimensions = {1},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::SUBGRAPH_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 0.125f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM,
+ .zeroPoint = 10
+ }, { // output03
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<uint8_t>({125}),
+ .dimensions = {1},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::SUBGRAPH_OUTPUT,
+ .numberOfConsumers = 0,
+ .scale = 0.01f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM,
+ .zeroPoint = 75
+ }},
+ .operations = {{
+ .inputs = {0},
+ .outputs = {1},
+ .type = TestOperationType::RSQRT
+ }},
+ .outputIndexes = {1}
+ },
+ .minSupportedVersion = TestHalVersion::AIDL_V3,
+ .referenced = {}
+ };
+ return model;
+}
+
+const auto dummy_test_model_125t_10_1h_75 = TestModelManager::get().add("rsqrt_quant8_125t_10_1h_75", get_test_model_125t_10_1h_75());
+
+} // namespace generated_tests::rsqrt_quant8
+
+namespace generated_tests::rsqrt_quant8 {
+
+const TestModel& get_test_model_125t_10_1h_75_all_inputs_as_internal() {
+ static TestModel model = {
+ .expectFailure = false,
+ .expectedMultinomialDistributionTolerance = 0,
+ .isRelaxed = false,
+ .main = {
+ .inputIndexes = {2},
+ .operands = {{ // input03
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<uint8_t>({}),
+ .dimensions = {1},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::TEMPORARY_VARIABLE,
+ .numberOfConsumers = 1,
+ .scale = 0.125f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM,
+ .zeroPoint = 10
+ }, { // output03
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<uint8_t>({125}),
+ .dimensions = {1},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::SUBGRAPH_OUTPUT,
+ .numberOfConsumers = 0,
+ .scale = 0.01f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM,
+ .zeroPoint = 75
+ }, { // input03_new
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<uint8_t>({42}),
+ .dimensions = {1},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::SUBGRAPH_INPUT,
+ .numberOfConsumers = 1,
+ .scale = 0.125f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM,
+ .zeroPoint = 10
+ }, { // placeholder3
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<uint8_t>({10}),
+ .dimensions = {1},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::CONSTANT_COPY,
+ .numberOfConsumers = 1,
+ .scale = 0.125f,
+ .type = TestOperandType::TENSOR_QUANT8_ASYMM,
+ .zeroPoint = 10
+ }, { // param3
+ .channelQuant = {},
+ .data = TestBuffer::createFromVector<int32_t>({0}),
+ .dimensions = {},
+ .isIgnored = false,
+ .lifetime = TestOperandLifeTime::CONSTANT_COPY,
+ .numberOfConsumers = 1,
+ .scale = 0.0f,
+ .type = TestOperandType::INT32,
+ .zeroPoint = 0
+ }},
+ .operations = {{
+ .inputs = {2, 3, 4},
+ .outputs = {0},
+ .type = TestOperationType::ADD
+ }, {
+ .inputs = {0},
+ .outputs = {1},
+ .type = TestOperationType::RSQRT
+ }},
+ .outputIndexes = {1}
+ },
+ .minSupportedVersion = TestHalVersion::AIDL_V3,
+ .referenced = {}
+ };
+ return model;
+}
+
+const auto dummy_test_model_125t_10_1h_75_all_inputs_as_internal = TestModelManager::get().add("rsqrt_quant8_125t_10_1h_75_all_inputs_as_internal", get_test_model_125t_10_1h_75_all_inputs_as_internal());
+
+} // namespace generated_tests::rsqrt_quant8
+
diff --git a/runtime/test/specs/AIDL_V3/rsqrt_quant8.mod.py b/runtime/test/specs/AIDL_V3/rsqrt_quant8.mod.py
new file mode 100644
index 0000000..edbc335
--- /dev/null
+++ b/runtime/test/specs/AIDL_V3/rsqrt_quant8.mod.py
@@ -0,0 +1,52 @@
+#
+# Copyright (C) 2021 The Android Open Source Project
+#
+# 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
+#
+# http://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.
+#
+
+# value = (8_bit_encoding - zeroPoint) * scale
+
+# If square roots are 0.5, 1, 2, 4
+# Then reciprocal square roots (outputs) are 2, 1, 0.5, 0.25
+# And squares (inputs) are 0.25, 1, 4, 16
+
+for inScale, inOffset, inToken in [(0.25, 0, "25h_0"),
+ (0.125, 10, "125t_10")]:
+ for outScale, outOffset, outToken in [(0.25, 0, "25h_0"),
+ (0.01, 75, "1h_75")]:
+
+ input0_values = []
+ output0_values = []
+ for in0, out0 in [(0.25, 2),
+ (1, 1),
+ (4, 0.5),
+ (16, 0.25)]:
+ input0_value = in0 / inScale + inOffset
+ output0_value = out0 / outScale + outOffset
+ if 0 <= input0_value < 128 and 0 <= output0_value < 128:
+ # We use [0, 128) as the range because the same values are used for
+ # both TENSOR_QUANT8_ASYMM and TENSOR_QUANT8_ASYMM_SIGNED testing
+ input0_values.append(input0_value)
+ output0_values.append(output0_value)
+
+ input0 = Input("input0", "TENSOR_QUANT8_ASYMM", "{%d}, %f, %d" % (len(input0_values), inScale, inOffset))
+ output0 = Output("output0", "TENSOR_QUANT8_ASYMM", "{%d}, %f, %d" % (len(output0_values), outScale, outOffset))
+ model = Model().Operation("RSQRT", input0).To(output0)
+
+ example_name = "%s_%s" % (inToken, outToken)
+ Example({
+ input0: input0_values,
+ output0: output0_values,
+ }, name=example_name)
+
+# We rely on QuantizationCouplingTest to replicate this test case for TENSOR_QUANT8_ASYMM_SIGNED.
diff --git a/runtime/test/specs/generate_all_tests.sh b/runtime/test/specs/generate_all_tests.sh
index a518abc..832131a 100755
--- a/runtime/test/specs/generate_all_tests.sh
+++ b/runtime/test/specs/generate_all_tests.sh
@@ -17,7 +17,7 @@
set -Eeuox pipefail
cd "$(dirname "$0")/.." # runtime/test
-NNAPI_VERSIONS="V1_0 V1_1 V1_2 V1_3 V1_3_cts_only AIDL_V2 experimental"
+NNAPI_VERSIONS="V1_0 V1_1 V1_2 V1_3 V1_3_cts_only AIDL_V2 AIDL_V3 experimental"
EXAMPLE_GENERATOR="../../tools/test_generator/example_generator.py"
for source_version in $NNAPI_VERSIONS; do