// Copyright (c) 2020 Vasyl Teliman // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "source/fuzz/transformation_add_parameters.h" #include #include "source/fuzz/fuzzer_util.h" namespace spvtools { namespace fuzz { TransformationAddParameters::TransformationAddParameters( const protobufs::TransformationAddParameters& message) : message_(message) {} TransformationAddParameters::TransformationAddParameters( uint32_t function_id, uint32_t new_type_id, const std::vector& new_parameter_type, const std::vector& new_parameter_id, const std::vector& constant_id) { message_.set_function_id(function_id); message_.set_new_type_id(new_type_id); for (auto id : new_parameter_type) { message_.add_new_parameter_type(id); } for (auto id : new_parameter_id) { message_.add_new_parameter_id(id); } for (auto id : constant_id) { message_.add_constant_id(id); } } bool TransformationAddParameters::IsApplicable( opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { // Check that function exists const auto* function = fuzzerutil::FindFunction(ir_context, message_.function_id()); if (!function || fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) { return false; } // Validate new parameters. const auto& new_type_ids = message_.new_parameter_type(); const auto& new_parameter_ids = message_.new_parameter_id(); const auto& constant_ids = message_.constant_id(); // All three vectors must have the same size. if (new_type_ids.size() != new_parameter_ids.size() || new_type_ids.size() != constant_ids.size()) { return false; } // Vectors must have at least one component. if (new_type_ids.empty()) { return false; } // Check that type ids exist in the module and are not OpTypeVoid. for (auto id : new_type_ids) { const auto* type = ir_context->get_type_mgr()->GetType(id); if (!type || type->AsVoid()) { return false; } } // Check that all parameter ids are fresh. for (auto id : new_parameter_ids) { if (!fuzzerutil::IsFreshId(ir_context, id)) { return false; } } // Check that constants exist and have valid type. for (int i = 0, n = constant_ids.size(); i < n; ++i) { const auto* inst = ir_context->get_def_use_mgr()->GetDef(constant_ids[i]); if (!inst || inst->type_id() != new_type_ids[i]) { return false; } } // Validate new function type. const auto* old_type_inst = fuzzerutil::GetFunctionType(ir_context, function); const auto* new_type_inst = ir_context->get_def_use_mgr()->GetDef(message_.new_type_id()); // Both types must exist. assert(old_type_inst && old_type_inst->opcode() == SpvOpTypeFunction); if (!new_type_inst || new_type_inst->opcode() != SpvOpTypeFunction) { return false; } auto num_old_parameters = old_type_inst->NumInOperands(); auto num_new_parameters = new_type_ids.size(); // New function type has been added to the module which means that it's valid. // Thus, we don't need to check whether the limit on the number of arguments // is satisfied. // New type = old type + new parameters. if (new_type_inst->NumInOperands() != num_old_parameters + num_new_parameters) { return false; } // Check that old parameters and the return type are preserved. for (uint32_t i = 0; i < num_old_parameters; ++i) { if (new_type_inst->GetSingleWordInOperand(i) != old_type_inst->GetSingleWordInOperand(i)) { return false; } } // Check that new parameters have been appended. for (int i = 0; i < num_new_parameters; ++i) { if (new_type_inst->GetSingleWordInOperand(i + num_old_parameters) != new_type_ids[i]) { return false; } } return true; } void TransformationAddParameters::Apply( opt::IRContext* ir_context, TransformationContext* /*unused*/) const { // Retrieve all data from the message auto function_id = message_.function_id(); const auto& new_parameter_type = message_.new_parameter_type(); const auto& new_parameter_id = message_.new_parameter_id(); const auto& constant_id = message_.constant_id(); // Find the function that will be transformed auto* function = fuzzerutil::FindFunction(ir_context, function_id); assert(function && "Can't find the function"); // Change function's type function->DefInst().SetInOperand(1, {message_.new_type_id()}); // Add new parameters to the function. for (int i = 0, n = new_parameter_id.size(); i < n; ++i) { function->AddParameter(MakeUnique( ir_context, SpvOpFunctionParameter, new_parameter_type[i], new_parameter_id[i], opt::Instruction::OperandList())); // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403): // Add an PointeeValueIsIrrelevant fact if the parameter is a pointer. } // Fix all OpFunctionCall instructions. ir_context->get_def_use_mgr()->ForEachUser( &function->DefInst(), [function_id, &constant_id](opt::Instruction* call) { if (call->opcode() != SpvOpFunctionCall || call->GetSingleWordInOperand(0) != function_id) { return; } for (auto id : constant_id) { // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3177): // it would be good to mark this usage of |id| as irrelevant, so that // we can perform some interesting transformations on it later. call->AddOperand({SPV_OPERAND_TYPE_ID, {id}}); } }); // Update module's id bound. We can safely dereference the result of // max_element since |new_parameter_id| is guaranteed to have elements. fuzzerutil::UpdateModuleIdBound( ir_context, *std::max_element(new_parameter_id.begin(), new_parameter_id.end())); // Make sure our changes are analyzed. ir_context->InvalidateAnalysesExceptFor( opt::IRContext::Analysis::kAnalysisNone); } protobufs::Transformation TransformationAddParameters::ToMessage() const { protobufs::Transformation result; *result.mutable_add_parameters() = message_; return result; } } // namespace fuzz } // namespace spvtools