• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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