1 /*
2 * Copyright (C) 2019 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 #include <android-base/logging.h>
18
19 #include <cstdlib>
20 #include <optional>
21 #include <utility>
22
23 #include "NeuralNetworksWrapper.h"
24 #include "TestHarness.h"
25
26 namespace {
27
28 using ::test_helper::TestModel;
29 using namespace ::android::nn::wrapper;
30 using namespace test_helper;
31
getOperandType(const TestOperand & op)32 OperandType getOperandType(const TestOperand& op) {
33 auto dims = op.dimensions;
34 if (op.type == TestOperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL) {
35 return OperandType(
36 static_cast<Type>(op.type), dims,
37 SymmPerChannelQuantParams(op.channelQuant.scales, op.channelQuant.channelDim));
38 } else {
39 return OperandType(static_cast<Type>(op.type), dims, op.scale, op.zeroPoint);
40 }
41 }
42
CreateModel(const TestModel & testModel)43 std::optional<Model> CreateModel(const TestModel& testModel) {
44 Model model;
45
46 // Operands.
47 // TODO(b/148605565): Add control flow support
48 CHECK_EQ(testModel.referenced.size(), 0u) << "Subgraphs not supported";
49 for (const auto& operand : testModel.main.operands) {
50 auto type = getOperandType(operand);
51 auto index = model.addOperand(&type);
52
53 switch (operand.lifetime) {
54 case TestOperandLifeTime::CONSTANT_COPY:
55 case TestOperandLifeTime::CONSTANT_REFERENCE:
56 model.setOperandValue(index, operand.data.get<void>(), operand.data.size());
57 break;
58 case TestOperandLifeTime::NO_VALUE:
59 model.setOperandValue(index, nullptr, 0);
60 break;
61 case TestOperandLifeTime::SUBGRAPH: {
62 CHECK(false);
63 } break;
64 case TestOperandLifeTime::SUBGRAPH_INPUT:
65 case TestOperandLifeTime::SUBGRAPH_OUTPUT:
66 case TestOperandLifeTime::TEMPORARY_VARIABLE:
67 // Nothing to do here.
68 break;
69 }
70 if (!model.isValid()) return std::nullopt;
71 }
72
73 // Operations.
74 CHECK_EQ(testModel.referenced.size(), 0u) << "Subgraphs not supported";
75 for (const auto& operation : testModel.main.operations) {
76 model.addOperation(static_cast<int>(operation.type), operation.inputs, operation.outputs);
77 if (!model.isValid()) return std::nullopt;
78 }
79
80 // Inputs and outputs.
81 model.identifyInputsAndOutputs(testModel.main.inputIndexes, testModel.main.outputIndexes);
82 if (!model.isValid()) return std::nullopt;
83
84 // Relaxed computation.
85 model.relaxComputationFloat32toFloat16(testModel.isRelaxed);
86 if (!model.isValid()) return std::nullopt;
87
88 if (model.finish() != Result::NO_ERROR) {
89 return std::nullopt;
90 }
91
92 return model;
93 }
94
CreateCompilation(const Model & model)95 std::optional<Compilation> CreateCompilation(const Model& model) {
96 Compilation compilation(&model);
97 if (compilation.finish() != Result::NO_ERROR) {
98 return std::nullopt;
99 }
100 return compilation;
101 }
102
CreateExecution(const Compilation & compilation,const TestModel & testModel)103 std::optional<Execution> CreateExecution(const Compilation& compilation,
104 const TestModel& testModel) {
105 Execution execution(&compilation);
106
107 // Model inputs.
108 for (uint32_t i = 0; i < testModel.main.inputIndexes.size(); i++) {
109 const auto& operand = testModel.main.operands[testModel.main.inputIndexes[i]];
110 if (execution.setInput(i, operand.data.get<void>(), operand.data.size()) !=
111 Result::NO_ERROR) {
112 return std::nullopt;
113 }
114 }
115
116 // Model outputs.
117 for (uint32_t i = 0; i < testModel.main.outputIndexes.size(); i++) {
118 const auto& operand = testModel.main.operands[testModel.main.outputIndexes[i]];
119 if (execution.setOutput(i, const_cast<void*>(operand.data.get<void>()),
120 operand.data.size()) != Result::NO_ERROR) {
121 return std::nullopt;
122 }
123 }
124
125 return execution;
126 }
127
128 } // anonymous namespace
129
nnapiFuzzTest(const TestModel & testModel)130 void nnapiFuzzTest(const TestModel& testModel) {
131 // set up model
132 auto model = CreateModel(testModel);
133 if (!model.has_value()) {
134 return;
135 }
136
137 // set up compilation
138 auto compilation = CreateCompilation(*model);
139 if (!compilation.has_value()) {
140 return;
141 }
142
143 // set up execution
144 auto execution = CreateExecution(*compilation, testModel);
145 if (!execution.has_value()) {
146 return;
147 }
148
149 // perform execution
150 execution->compute();
151 }
152