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