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 "ValidateHal.h"
20 
21 #include "HalUtils.h"
22 
23 #include <android-base/logging.h>
24 #include <nnapi/TypeUtils.h>
25 #include <nnapi/hal/aidl/Conversions.h>
26 
27 #include <algorithm>
28 #include <memory>
29 #include <set>
30 #include <utility>
31 #include <vector>
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.empty());
46     NN_RET_CHECK(!inputRoles.empty() || !outputRoles.empty());
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_GE(role.modelIndex, 0);
53         NN_RET_CHECK_LT(static_cast<size_t>(role.modelIndex), preparedModels.size());
54         const auto& preparedModel = preparedModels[role.modelIndex];
55         NN_RET_CHECK(preparedModel != nullptr);
56         const auto* model = getModel(preparedModel);
57         NN_RET_CHECK(model != nullptr);
58         const auto& inputIndexes = model->main.inputIndexes;
59         NN_RET_CHECK_GE(role.ioIndex, 0);
60         NN_RET_CHECK_LT(static_cast<size_t>(role.ioIndex), inputIndexes.size());
61         NN_RET_CHECK_GT(role.probability, 0.0f);
62         NN_RET_CHECK_LE(role.probability, 1.0f);
63         const auto [it, success] = roles.emplace(preparedModel.get(), IOType::INPUT, role.ioIndex);
64         NN_RET_CHECK(success);
65         operands.push_back(model->main.operands[inputIndexes[role.ioIndex]]);
66     }
67     for (const auto& role : outputRoles) {
68         NN_RET_CHECK_GE(role.modelIndex, 0);
69         NN_RET_CHECK_LT(static_cast<size_t>(role.modelIndex), preparedModels.size());
70         const auto& preparedModel = preparedModels[role.modelIndex];
71         NN_RET_CHECK(preparedModel != nullptr);
72         const auto* model = getModel(preparedModel);
73         NN_RET_CHECK(model != nullptr);
74         const auto& outputIndexes = model->main.outputIndexes;
75         NN_RET_CHECK_GE(role.ioIndex, 0);
76         NN_RET_CHECK_LT(static_cast<size_t>(role.ioIndex), outputIndexes.size());
77         NN_RET_CHECK_GT(role.probability, 0.0f);
78         NN_RET_CHECK_LE(role.probability, 1.0f);
79         const auto [it, success] = roles.emplace(preparedModel.get(), IOType::OUTPUT, role.ioIndex);
80         NN_RET_CHECK(success);
81         operands.push_back(model->main.operands[outputIndexes[role.ioIndex]]);
82     }
83 
84     CHECK(!operands.empty());
85     const auto opType = operands[0].type;
86     const auto canonicalOperandType = convert(opType);
87     NN_RET_CHECK(canonicalOperandType.has_value()) << canonicalOperandType.error().message;
88     const bool isExtensionOperand = isExtension(canonicalOperandType.value());
89 
90     auto maybeDimensions = toUnsigned(desc.dimensions);
91     NN_RET_CHECK(maybeDimensions.has_value()) << maybeDimensions.error().message;
92     std::vector<uint32_t> dimensions = std::move(maybeDimensions).value();
93 
94     for (const auto& operand : operands) {
95         NN_RET_CHECK(operand.type == operands[0].type)
96                 << toString(operand.type) << " vs " << toString(operands[0].type);
97         NN_RET_CHECK_EQ(operand.scale, operands[0].scale);
98         NN_RET_CHECK_EQ(operand.zeroPoint, operands[0].zeroPoint);
99         // NOTE: validateMemoryDesc cannot validate extra parameters for extension operand type.
100         if (!isExtensionOperand) {
101             const auto& lhsExtraParams = operand.extraParams;
102             const auto& rhsExtraParams = operands[0].extraParams;
103             NN_RET_CHECK(lhsExtraParams == rhsExtraParams)
104                     << (lhsExtraParams.has_value() ? lhsExtraParams.value().toString()
105                                                    : "std::nullopt")
106                     << " vs "
107                     << (rhsExtraParams.has_value() ? rhsExtraParams.value().toString()
108                                                    : "std::nullopt");
109         }
110         const auto maybeRhsDimensions = toUnsigned(operand.dimensions);
111         NN_RET_CHECK(maybeRhsDimensions.has_value()) << maybeRhsDimensions.error().message;
112         const auto combined = combineDimensions(dimensions, maybeRhsDimensions.value());
113         NN_RET_CHECK(combined.has_value());
114         dimensions = combined.value();
115     }
116 
117     // NOTE: validateMemoryDesc cannot validate scalar dimensions with extension operand type.
118     if (!isExtensionOperand) {
119         NN_RET_CHECK(!isNonExtensionScalar(opType) || dimensions.empty())
120                 << "invalid dimensions with scalar operand type.";
121     }
122 
123     if (preparedModelRoles != nullptr) {
124         *preparedModelRoles = std::move(roles);
125     }
126     if (combinedOperand != nullptr) {
127         *combinedOperand = operands[0];
128         // No need to check that values fit int32_t here, since the original values are obtained
129         // from int32_t.
130         combinedOperand->dimensions = aidl_hal::utils::toSigned(dimensions).value();
131     }
132     return true;
133 }
134 
135 }  // namespace nn
136 }  // namespace android
137