1fd4e5da5Sopenharmony_ci// Copyright (c) 2020 Vasyl Teliman
2fd4e5da5Sopenharmony_ci//
3fd4e5da5Sopenharmony_ci// Licensed under the Apache License, Version 2.0 (the "License");
4fd4e5da5Sopenharmony_ci// you may not use this file except in compliance with the License.
5fd4e5da5Sopenharmony_ci// You may obtain a copy of the License at
6fd4e5da5Sopenharmony_ci//
7fd4e5da5Sopenharmony_ci//     http://www.apache.org/licenses/LICENSE-2.0
8fd4e5da5Sopenharmony_ci//
9fd4e5da5Sopenharmony_ci// Unless required by applicable law or agreed to in writing, software
10fd4e5da5Sopenharmony_ci// distributed under the License is distributed on an "AS IS" BASIS,
11fd4e5da5Sopenharmony_ci// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12fd4e5da5Sopenharmony_ci// See the License for the specific language governing permissions and
13fd4e5da5Sopenharmony_ci// limitations under the License.
14fd4e5da5Sopenharmony_ci
15fd4e5da5Sopenharmony_ci#include "source/fuzz/fuzzer_pass_add_parameters.h"
16fd4e5da5Sopenharmony_ci
17fd4e5da5Sopenharmony_ci#include "source/fuzz/fuzzer_context.h"
18fd4e5da5Sopenharmony_ci#include "source/fuzz/fuzzer_util.h"
19fd4e5da5Sopenharmony_ci#include "source/fuzz/instruction_descriptor.h"
20fd4e5da5Sopenharmony_ci#include "source/fuzz/transformation_add_parameter.h"
21fd4e5da5Sopenharmony_ci
22fd4e5da5Sopenharmony_cinamespace spvtools {
23fd4e5da5Sopenharmony_cinamespace fuzz {
24fd4e5da5Sopenharmony_ci
25fd4e5da5Sopenharmony_ciFuzzerPassAddParameters::FuzzerPassAddParameters(
26fd4e5da5Sopenharmony_ci    opt::IRContext* ir_context, TransformationContext* transformation_context,
27fd4e5da5Sopenharmony_ci    FuzzerContext* fuzzer_context,
28fd4e5da5Sopenharmony_ci    protobufs::TransformationSequence* transformations,
29fd4e5da5Sopenharmony_ci    bool ignore_inapplicable_transformations)
30fd4e5da5Sopenharmony_ci    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
31fd4e5da5Sopenharmony_ci                 transformations, ignore_inapplicable_transformations) {}
32fd4e5da5Sopenharmony_ci
33fd4e5da5Sopenharmony_civoid FuzzerPassAddParameters::Apply() {
34fd4e5da5Sopenharmony_ci  // Compute type candidates for the new parameter.
35fd4e5da5Sopenharmony_ci  std::vector<uint32_t> type_candidates;
36fd4e5da5Sopenharmony_ci  for (const auto& type_inst : GetIRContext()->module()->GetTypes()) {
37fd4e5da5Sopenharmony_ci    if (TransformationAddParameter::IsParameterTypeSupported(
38fd4e5da5Sopenharmony_ci            GetIRContext(), type_inst->result_id())) {
39fd4e5da5Sopenharmony_ci      type_candidates.push_back(type_inst->result_id());
40fd4e5da5Sopenharmony_ci    }
41fd4e5da5Sopenharmony_ci  }
42fd4e5da5Sopenharmony_ci
43fd4e5da5Sopenharmony_ci  if (type_candidates.empty()) {
44fd4e5da5Sopenharmony_ci    // The module contains no suitable types to use in new parameters.
45fd4e5da5Sopenharmony_ci    return;
46fd4e5da5Sopenharmony_ci  }
47fd4e5da5Sopenharmony_ci
48fd4e5da5Sopenharmony_ci  // Iterate over all functions in the module.
49fd4e5da5Sopenharmony_ci  for (const auto& function : *GetIRContext()->module()) {
50fd4e5da5Sopenharmony_ci    // Skip all entry-point functions - we don't want to change those.
51fd4e5da5Sopenharmony_ci    if (fuzzerutil::FunctionIsEntryPoint(GetIRContext(),
52fd4e5da5Sopenharmony_ci                                         function.result_id())) {
53fd4e5da5Sopenharmony_ci      continue;
54fd4e5da5Sopenharmony_ci    }
55fd4e5da5Sopenharmony_ci
56fd4e5da5Sopenharmony_ci    if (GetNumberOfParameters(function) >=
57fd4e5da5Sopenharmony_ci        GetFuzzerContext()->GetMaximumNumberOfFunctionParameters()) {
58fd4e5da5Sopenharmony_ci      continue;
59fd4e5da5Sopenharmony_ci    }
60fd4e5da5Sopenharmony_ci
61fd4e5da5Sopenharmony_ci    if (!GetFuzzerContext()->ChoosePercentage(
62fd4e5da5Sopenharmony_ci            GetFuzzerContext()->GetChanceOfAddingParameters())) {
63fd4e5da5Sopenharmony_ci      continue;
64fd4e5da5Sopenharmony_ci    }
65fd4e5da5Sopenharmony_ci
66fd4e5da5Sopenharmony_ci    auto num_new_parameters =
67fd4e5da5Sopenharmony_ci        GetFuzzerContext()->GetRandomNumberOfNewParameters(
68fd4e5da5Sopenharmony_ci            GetNumberOfParameters(function));
69fd4e5da5Sopenharmony_ci
70fd4e5da5Sopenharmony_ci    for (uint32_t i = 0; i < num_new_parameters; ++i) {
71fd4e5da5Sopenharmony_ci      auto current_type_id =
72fd4e5da5Sopenharmony_ci          type_candidates[GetFuzzerContext()->RandomIndex(type_candidates)];
73fd4e5da5Sopenharmony_ci      auto current_type =
74fd4e5da5Sopenharmony_ci          GetIRContext()->get_type_mgr()->GetType(current_type_id);
75fd4e5da5Sopenharmony_ci      std::map<uint32_t, uint32_t> call_parameter_ids;
76fd4e5da5Sopenharmony_ci
77fd4e5da5Sopenharmony_ci      // Consider the case when a pointer type was selected.
78fd4e5da5Sopenharmony_ci      if (current_type->kind() == opt::analysis::Type::kPointer) {
79fd4e5da5Sopenharmony_ci        auto storage_class = fuzzerutil::GetStorageClassFromPointerType(
80fd4e5da5Sopenharmony_ci            GetIRContext(), current_type_id);
81fd4e5da5Sopenharmony_ci        switch (storage_class) {
82fd4e5da5Sopenharmony_ci          case spv::StorageClass::Function: {
83fd4e5da5Sopenharmony_ci            // In every caller find or create a local variable that has the
84fd4e5da5Sopenharmony_ci            // selected type.
85fd4e5da5Sopenharmony_ci            for (auto* instr :
86fd4e5da5Sopenharmony_ci                 fuzzerutil::GetCallers(GetIRContext(), function.result_id())) {
87fd4e5da5Sopenharmony_ci              auto block = GetIRContext()->get_instr_block(instr);
88fd4e5da5Sopenharmony_ci              auto function_id = block->GetParent()->result_id();
89fd4e5da5Sopenharmony_ci              uint32_t variable_id =
90fd4e5da5Sopenharmony_ci                  FindOrCreateLocalVariable(current_type_id, function_id, true);
91fd4e5da5Sopenharmony_ci              call_parameter_ids[instr->result_id()] = variable_id;
92fd4e5da5Sopenharmony_ci            }
93fd4e5da5Sopenharmony_ci          } break;
94fd4e5da5Sopenharmony_ci          case spv::StorageClass::Private:
95fd4e5da5Sopenharmony_ci          case spv::StorageClass::Workgroup: {
96fd4e5da5Sopenharmony_ci            // If there exists at least one caller, find or create a global
97fd4e5da5Sopenharmony_ci            // variable that has the selected type.
98fd4e5da5Sopenharmony_ci            std::vector<opt::Instruction*> callers =
99fd4e5da5Sopenharmony_ci                fuzzerutil::GetCallers(GetIRContext(), function.result_id());
100fd4e5da5Sopenharmony_ci            if (!callers.empty()) {
101fd4e5da5Sopenharmony_ci              uint32_t variable_id =
102fd4e5da5Sopenharmony_ci                  FindOrCreateGlobalVariable(current_type_id, true);
103fd4e5da5Sopenharmony_ci              for (auto* instr : callers) {
104fd4e5da5Sopenharmony_ci                call_parameter_ids[instr->result_id()] = variable_id;
105fd4e5da5Sopenharmony_ci              }
106fd4e5da5Sopenharmony_ci            }
107fd4e5da5Sopenharmony_ci          } break;
108fd4e5da5Sopenharmony_ci          default:
109fd4e5da5Sopenharmony_ci            break;
110fd4e5da5Sopenharmony_ci        }
111fd4e5da5Sopenharmony_ci      } else {
112fd4e5da5Sopenharmony_ci        // If there exists at least one caller, find or create a zero constant
113fd4e5da5Sopenharmony_ci        // that has the selected type.
114fd4e5da5Sopenharmony_ci        std::vector<opt::Instruction*> callers =
115fd4e5da5Sopenharmony_ci            fuzzerutil::GetCallers(GetIRContext(), function.result_id());
116fd4e5da5Sopenharmony_ci        if (!callers.empty()) {
117fd4e5da5Sopenharmony_ci          uint32_t constant_id =
118fd4e5da5Sopenharmony_ci              FindOrCreateZeroConstant(current_type_id, true);
119fd4e5da5Sopenharmony_ci          for (auto* instr :
120fd4e5da5Sopenharmony_ci               fuzzerutil::GetCallers(GetIRContext(), function.result_id())) {
121fd4e5da5Sopenharmony_ci            call_parameter_ids[instr->result_id()] = constant_id;
122fd4e5da5Sopenharmony_ci          }
123fd4e5da5Sopenharmony_ci        }
124fd4e5da5Sopenharmony_ci      }
125fd4e5da5Sopenharmony_ci
126fd4e5da5Sopenharmony_ci      ApplyTransformation(TransformationAddParameter(
127fd4e5da5Sopenharmony_ci          function.result_id(), GetFuzzerContext()->GetFreshId(),
128fd4e5da5Sopenharmony_ci          current_type_id, std::move(call_parameter_ids),
129fd4e5da5Sopenharmony_ci          GetFuzzerContext()->GetFreshId()));
130fd4e5da5Sopenharmony_ci    }
131fd4e5da5Sopenharmony_ci  }
132fd4e5da5Sopenharmony_ci}
133fd4e5da5Sopenharmony_ci
134fd4e5da5Sopenharmony_ciuint32_t FuzzerPassAddParameters::GetNumberOfParameters(
135fd4e5da5Sopenharmony_ci    const opt::Function& function) const {
136fd4e5da5Sopenharmony_ci  const auto* type = GetIRContext()->get_type_mgr()->GetType(
137fd4e5da5Sopenharmony_ci      function.DefInst().GetSingleWordInOperand(1));
138fd4e5da5Sopenharmony_ci  assert(type && type->AsFunction());
139fd4e5da5Sopenharmony_ci
140fd4e5da5Sopenharmony_ci  return static_cast<uint32_t>(type->AsFunction()->param_types().size());
141fd4e5da5Sopenharmony_ci}
142fd4e5da5Sopenharmony_ci
143fd4e5da5Sopenharmony_ci}  // namespace fuzz
144fd4e5da5Sopenharmony_ci}  // namespace spvtools
145