1 /*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "ValidateHal"
18
19 #include "AidlValidateHal.h"
20
21 #include <android-base/logging.h>
22 #include <nnapi/hal/aidl/Conversions.h>
23
24 #include <algorithm>
25 #include <memory>
26 #include <set>
27 #include <utility>
28 #include <vector>
29
30 #include "LegacyUtils.h"
31 #include "nnapi/TypeUtils.h"
32
33 namespace android {
34 namespace nn {
35
validateMemoryDesc(const aidl_hal::BufferDesc & desc,const std::vector<std::shared_ptr<aidl_hal::IPreparedModel>> & preparedModels,const std::vector<aidl_hal::BufferRole> & inputRoles,const std::vector<aidl_hal::BufferRole> & outputRoles,std::function<const aidl_hal::Model * (const std::shared_ptr<aidl_hal::IPreparedModel> &)> getModel,std::set<AidlHalPreparedModelRole> * preparedModelRoles,aidl_hal::Operand * combinedOperand)36 bool validateMemoryDesc(
37 const aidl_hal::BufferDesc& desc,
38 const std::vector<std::shared_ptr<aidl_hal::IPreparedModel>>& preparedModels,
39 const std::vector<aidl_hal::BufferRole>& inputRoles,
40 const std::vector<aidl_hal::BufferRole>& outputRoles,
41 std::function<const aidl_hal::Model*(const std::shared_ptr<aidl_hal::IPreparedModel>&)>
42 getModel,
43 std::set<AidlHalPreparedModelRole>* preparedModelRoles,
44 aidl_hal::Operand* combinedOperand) {
45 NN_RET_CHECK(preparedModels.size() != 0);
46 NN_RET_CHECK(inputRoles.size() != 0 || outputRoles.size() != 0);
47
48 std::set<AidlHalPreparedModelRole> roles;
49 std::vector<aidl_hal::Operand> operands;
50 operands.reserve(inputRoles.size() + outputRoles.size());
51 for (const auto& role : inputRoles) {
52 NN_RET_CHECK_LT(role.modelIndex, preparedModels.size());
53 const auto& preparedModel = preparedModels[role.modelIndex];
54 NN_RET_CHECK(preparedModel != nullptr);
55 const auto* model = getModel(preparedModel);
56 NN_RET_CHECK(model != nullptr);
57 const auto& inputIndexes = model->main.inputIndexes;
58 NN_RET_CHECK_LT(role.ioIndex, inputIndexes.size());
59 NN_RET_CHECK_GT(role.probability, 0.0f);
60 NN_RET_CHECK_LE(role.probability, 1.0f);
61 const auto [it, success] = roles.emplace(preparedModel.get(), IOType::INPUT, role.ioIndex);
62 NN_RET_CHECK(success);
63 operands.push_back(model->main.operands[inputIndexes[role.ioIndex]]);
64 }
65 for (const auto& role : outputRoles) {
66 NN_RET_CHECK_LT(role.modelIndex, preparedModels.size());
67 const auto& preparedModel = preparedModels[role.modelIndex];
68 NN_RET_CHECK(preparedModel != nullptr);
69 const auto* model = getModel(preparedModel);
70 NN_RET_CHECK(model != nullptr);
71 const auto& outputIndexes = model->main.outputIndexes;
72 NN_RET_CHECK_LT(role.ioIndex, outputIndexes.size());
73 NN_RET_CHECK_GT(role.probability, 0.0f);
74 NN_RET_CHECK_LE(role.probability, 1.0f);
75 const auto [it, success] = roles.emplace(preparedModel.get(), IOType::OUTPUT, role.ioIndex);
76 NN_RET_CHECK(success);
77 operands.push_back(model->main.operands[outputIndexes[role.ioIndex]]);
78 }
79
80 CHECK(!operands.empty());
81 const auto opType = operands[0].type;
82 const auto canonicalOperandType = convert(opType);
83 NN_RET_CHECK(canonicalOperandType.has_value()) << canonicalOperandType.error().message;
84 const bool isExtensionOperand = isExtension(canonicalOperandType.value());
85
86 auto maybeDimensions = toUnsigned(desc.dimensions);
87 NN_RET_CHECK(maybeDimensions.has_value()) << maybeDimensions.error().message;
88 std::vector<uint32_t> dimensions = std::move(maybeDimensions).value();
89
90 for (const auto& operand : operands) {
91 NN_RET_CHECK(operand.type == operands[0].type)
92 << toString(operand.type) << " vs " << toString(operands[0].type);
93 NN_RET_CHECK_EQ(operand.scale, operands[0].scale);
94 NN_RET_CHECK_EQ(operand.zeroPoint, operands[0].zeroPoint);
95 // NOTE: validateMemoryDesc cannot validate extra parameters for extension operand type.
96 if (!isExtensionOperand) {
97 const auto& lhsExtraParams = operand.extraParams;
98 const auto& rhsExtraParams = operands[0].extraParams;
99 NN_RET_CHECK(lhsExtraParams == rhsExtraParams)
100 << (lhsExtraParams.has_value() ? lhsExtraParams.value().toString()
101 : "std::nullopt")
102 << " vs "
103 << (rhsExtraParams.has_value() ? rhsExtraParams.value().toString()
104 : "std::nullopt");
105 }
106 const auto maybeRhsDimensions = toUnsigned(operand.dimensions);
107 NN_RET_CHECK(maybeRhsDimensions.has_value()) << maybeRhsDimensions.error().message;
108 const auto combined = combineDimensions(dimensions, maybeRhsDimensions.value());
109 NN_RET_CHECK(combined.has_value());
110 dimensions = combined.value();
111 }
112
113 // NOTE: validateMemoryDesc cannot validate scalar dimensions with extension operand type.
114 if (!isExtensionOperand) {
115 NN_RET_CHECK(!nonExtensionOperandTypeIsScalar(static_cast<int>(opType)) ||
116 dimensions.empty())
117 << "invalid dimensions with scalar operand type.";
118 }
119
120 if (preparedModelRoles != nullptr) {
121 *preparedModelRoles = std::move(roles);
122 }
123 if (combinedOperand != nullptr) {
124 *combinedOperand = operands[0];
125 // No need to check that values fit int32_t here, since the original values are obtained
126 // from int32_t.
127 combinedOperand->dimensions = aidl_hal::utils::toSigned(dimensions).value();
128 }
129 return true;
130 }
131
132 } // namespace nn
133 } // namespace android
134