From ab10489a0297a8fe181d217f5eb0b807281e1977 Mon Sep 17 00:00:00 2001 From: Vasyl Teliman Date: Tue, 30 Jun 2020 17:20:27 +0300 Subject: [PATCH] spirv-fuzz: Add one parameter at a time (#3469) Fixes #3467. Fixes #3468. --- source/fuzz/CMakeLists.txt | 4 +- source/fuzz/fuzzer_pass_add_parameters.cpp | 47 +--- ...uzzer_pass_permute_function_parameters.cpp | 13 +- source/fuzz/fuzzer_util.cpp | 46 +++- source/fuzz/fuzzer_util.h | 18 ++ source/fuzz/protobufs/spvtoolsfuzz.proto | 35 ++- source/fuzz/transformation.cpp | 6 +- source/fuzz/transformation_add_parameter.cpp | 135 ++++++++++++ source/fuzz/transformation_add_parameter.h | 62 ++++++ source/fuzz/transformation_add_parameters.cpp | 201 ------------------ source/fuzz/transformation_add_parameters.h | 70 ------ .../fuzz/transformation_add_type_function.cpp | 47 +--- ...sformation_permute_function_parameters.cpp | 83 ++++---- ...ansformation_permute_function_parameters.h | 6 +- test/fuzz/CMakeLists.txt | 2 +- .../transformation_add_parameter_test.cpp | 126 +++++++++++ .../transformation_add_parameters_test.cpp | 177 --------------- ...ation_permute_function_parameters_test.cpp | 37 ++-- 18 files changed, 481 insertions(+), 634 deletions(-) create mode 100644 source/fuzz/transformation_add_parameter.cpp create mode 100644 source/fuzz/transformation_add_parameter.h delete mode 100644 source/fuzz/transformation_add_parameters.cpp delete mode 100644 source/fuzz/transformation_add_parameters.h create mode 100644 test/fuzz/transformation_add_parameter_test.cpp delete mode 100644 test/fuzz/transformation_add_parameters_test.cpp diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt index 8893d3ea0..d14740871 100644 --- a/source/fuzz/CMakeLists.txt +++ b/source/fuzz/CMakeLists.txt @@ -95,7 +95,7 @@ if(SPIRV_BUILD_FUZZER) transformation_add_global_variable.h transformation_add_local_variable.h transformation_add_no_contraction_decoration.h - transformation_add_parameters.h + transformation_add_parameter.h transformation_add_spec_constant_op.h transformation_add_type_array.h transformation_add_type_boolean.h @@ -202,7 +202,7 @@ if(SPIRV_BUILD_FUZZER) transformation_add_global_variable.cpp transformation_add_local_variable.cpp transformation_add_no_contraction_decoration.cpp - transformation_add_parameters.cpp + transformation_add_parameter.cpp transformation_add_spec_constant_op.cpp transformation_add_type_array.cpp transformation_add_type_boolean.cpp diff --git a/source/fuzz/fuzzer_pass_add_parameters.cpp b/source/fuzz/fuzzer_pass_add_parameters.cpp index c93130207..927345209 100644 --- a/source/fuzz/fuzzer_pass_add_parameters.cpp +++ b/source/fuzz/fuzzer_pass_add_parameters.cpp @@ -17,7 +17,7 @@ #include "source/fuzz/fuzzer_context.h" #include "source/fuzz/fuzzer_util.h" #include "source/fuzz/instruction_descriptor.h" -#include "source/fuzz/transformation_add_parameters.h" +#include "source/fuzz/transformation_add_parameter.h" namespace spvtools { namespace fuzz { @@ -57,50 +57,17 @@ void FuzzerPassAddParameters::Apply() { continue; } - const auto* type_inst = - fuzzerutil::GetFunctionType(GetIRContext(), &function); - assert(type_inst); - - // -1 because we don't take return type into account. - auto num_old_parameters = type_inst->NumInOperands() - 1; auto num_new_parameters = GetFuzzerContext()->GetRandomNumberOfNewParameters( GetNumberOfParameters(function)); - - std::vector all_types(num_old_parameters); - std::vector new_types(num_new_parameters); - std::vector parameter_ids(num_new_parameters); - std::vector constant_ids(num_new_parameters); - - // Get type ids for old parameters. - for (uint32_t i = 0; i < num_old_parameters; ++i) { - // +1 since we don't take return type into account. - all_types[i] = type_inst->GetSingleWordInOperand(i + 1); - } - for (uint32_t i = 0; i < num_new_parameters; ++i) { - // Get type ids for new parameters. - new_types[i] = - type_candidates[GetFuzzerContext()->RandomIndex(type_candidates)]; - - // Create constants to initialize new parameters from. - constant_ids[i] = FindOrCreateZeroConstant(new_types[i]); + ApplyTransformation(TransformationAddParameter( + function.result_id(), GetFuzzerContext()->GetFreshId(), + FindOrCreateZeroConstant( + type_candidates[GetFuzzerContext()->RandomIndex( + type_candidates)]), + GetFuzzerContext()->GetFreshId())); } - - // Append new parameters to the old ones. - all_types.insert(all_types.end(), new_types.begin(), new_types.end()); - - // Generate result ids for new parameters. - for (auto& id : parameter_ids) { - id = GetFuzzerContext()->GetFreshId(); - } - - auto result_type_id = type_inst->GetSingleWordInOperand(0); - ApplyTransformation(TransformationAddParameters( - function.result_id(), - FindOrCreateFunctionType(result_type_id, all_types), - std::move(new_types), std::move(parameter_ids), - std::move(constant_ids))); } } diff --git a/source/fuzz/fuzzer_pass_permute_function_parameters.cpp b/source/fuzz/fuzzer_pass_permute_function_parameters.cpp index 57d9cabbc..e15aef6e4 100644 --- a/source/fuzz/fuzzer_pass_permute_function_parameters.cpp +++ b/source/fuzz/fuzzer_pass_permute_function_parameters.cpp @@ -61,20 +61,9 @@ void FuzzerPassPermuteFunctionParameters::Apply() { std::iota(permutation.begin(), permutation.end(), 0); GetFuzzerContext()->Shuffle(&permutation); - // Create a new OpFunctionType instruction with permuted arguments - // if needed - auto result_type_id = function_type->GetSingleWordInOperand(0); - std::vector argument_ids; - - for (auto index : permutation) { - // +1 to take function's return type into account - argument_ids.push_back(function_type->GetSingleWordInOperand(index + 1)); - } - // Apply our transformation ApplyTransformation(TransformationPermuteFunctionParameters( - function_id, FindOrCreateFunctionType(result_type_id, argument_ids), - permutation)); + function_id, GetFuzzerContext()->GetFreshId(), permutation)); } } diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp index 79881fcdd..7b137cf30 100644 --- a/source/fuzz/fuzzer_util.cpp +++ b/source/fuzz/fuzzer_util.cpp @@ -12,11 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "source/fuzz/fuzzer_util.h" + #include #include -#include "source/fuzz/fuzzer_util.h" - #include "source/opt/build_module.h" namespace spvtools { @@ -590,6 +590,8 @@ void AddGlobalVariable(opt::IRContext* context, uint32_t result_id, uint32_t type_id, SpvStorageClass storage_class, uint32_t initializer_id) { // Check various preconditions. + assert(result_id != 0 && "Result id can't be 0"); + assert((storage_class == SpvStorageClassPrivate || storage_class == SpvStorageClassWorkgroup) && "Variable's storage class must be either Private or Workgroup"); @@ -631,6 +633,8 @@ void AddLocalVariable(opt::IRContext* context, uint32_t result_id, uint32_t type_id, uint32_t function_id, uint32_t initializer_id) { // Check various preconditions. + assert(result_id != 0 && "Result id can't be 0"); + auto* type_inst = context->get_def_use_mgr()->GetDef(type_id); (void)type_inst; // Variable becomes unused in release mode. assert(type_inst && type_inst->opcode() == SpvOpTypePointer && @@ -687,6 +691,44 @@ std::vector GetParameters(opt::IRContext* ir_context, return result; } +void AddFunctionType(opt::IRContext* ir_context, uint32_t result_id, + const std::vector& type_ids) { + assert(result_id != 0 && "Result id can't be 0"); + assert(!type_ids.empty() && + "OpTypeFunction always has at least one operand - function's return " + "type"); + assert(IsNonFunctionTypeId(ir_context, type_ids[0]) && + "Return type must not be a function"); + + for (size_t i = 1; i < type_ids.size(); ++i) { + const auto* param_type = ir_context->get_type_mgr()->GetType(type_ids[i]); + (void)param_type; // Make compiler happy in release mode. + assert(param_type && !param_type->AsVoid() && !param_type->AsFunction() && + "Function parameter can't have a function or void type"); + } + + opt::Instruction::OperandList operands; + operands.reserve(type_ids.size()); + for (auto id : type_ids) { + operands.push_back({SPV_OPERAND_TYPE_ID, {id}}); + } + + ir_context->AddType(MakeUnique( + ir_context, SpvOpTypeFunction, 0, result_id, std::move(operands))); + + UpdateModuleIdBound(ir_context, result_id); +} + +uint32_t FindOrCreateFunctionType(opt::IRContext* ir_context, + uint32_t result_id, + const std::vector& type_ids) { + if (auto existing_id = FindFunctionType(ir_context, type_ids)) { + return existing_id; + } + AddFunctionType(ir_context, result_id, type_ids); + return result_id; +} + } // namespace fuzzerutil } // namespace fuzz diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h index 0cc5d496e..7928df054 100644 --- a/source/fuzz/fuzzer_util.h +++ b/source/fuzz/fuzzer_util.h @@ -266,6 +266,24 @@ bool IsPermutationOfRange(const std::vector& arr, uint32_t lo, std::vector GetParameters(opt::IRContext* ir_context, uint32_t function_id); +// Creates new OpTypeFunction instruction in the module. |type_ids| may not be +// empty. It may not contain result ids of OpTypeFunction instructions. +// |type_ids[i]| may not be a result id of OpTypeVoid instruction for |i >= 1|. +// |result_id| may not equal to 0. Updates module's id bound to accommodate for +// |result_id|. +void AddFunctionType(opt::IRContext* ir_context, uint32_t result_id, + const std::vector& type_ids); + +// Returns a result id of an OpTypeFunction instruction in the module. Creates a +// new instruction if required and returns |result_id|. type_ids| may not be +// empty. It may not contain result ids of OpTypeFunction instructions. +// |type_ids[i]| may not be a result id of OpTypeVoid instruction for |i >= 1|. +// |result_id| must not be equal to 0. Updates module's id bound to accommodate +// for |result_id|. +uint32_t FindOrCreateFunctionType(opt::IRContext* ir_context, + uint32_t result_id, + const std::vector& type_ids); + } // namespace fuzzerutil } // namespace fuzz diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto index 7b8adc08d..7e0db3ec7 100644 --- a/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -380,7 +380,7 @@ message Transformation { TransformationReplaceLinearAlgebraInstruction replace_linear_algebra_instruction = 49; TransformationSwapConditionalBranchOperands swap_conditional_branch_operands = 50; TransformationPermutePhiOperands permute_phi_operands = 51; - TransformationAddParameters add_parameters = 52; + TransformationAddParameter add_parameter = 52; // Add additional option using the next available number. } } @@ -624,24 +624,25 @@ message TransformationAddNoContractionDecoration { } -message TransformationAddParameters { +message TransformationAddParameter { - // Adds new parameters into the function. + // Adds a new parameter into the function. // Result id of the function to add parameters to. uint32 function_id = 1; - // New type of the function. - uint32 new_type_id = 2; + // Fresh id for a new parameter. + uint32 parameter_fresh_id = 2; - // Type ids of parameters to add to the function. - repeated uint32 new_parameter_type = 3; + // Result id of the instruction, used to initializer new parameter + // in function calls. Type id of that instruction is the type id of the parameter. + // It may not be OpTypeVoid. + uint32 initializer_id = 3; - // Result ids for new parameters. - repeated uint32 new_parameter_id = 4; - - // Constants to initialize new parameters from. - repeated uint32 constant_id = 5; + // A fresh id for a new function type. This might not be used + // if a required function type already exists or if we can change + // the old function type. + uint32 function_type_fresh_id = 4; } @@ -1011,12 +1012,10 @@ message TransformationPermuteFunctionParameters { // Function, whose parameters will be permuted uint32 function_id = 1; - // |new_type_id| is a result id of a valid OpTypeFunction instruction. - // New type is valid if: - // - it has the same number of operands as the old one - // - function's result type is the same as the old one - // - function's arguments are permuted according to |permutation| vector - uint32 new_type_id = 2; + // Fresh id for a new type of the function. This might not be used + // if a required function type already exists or if we can change + // the old function type. + uint32 function_type_fresh_id = 2; // An array of size |n|, where |n| is a number of arguments to a function // with |function_id|. For each i: 0 <= permutation[i] < n. diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp index 3177de0ac..ac16a9ffb 100644 --- a/source/fuzz/transformation.cpp +++ b/source/fuzz/transformation.cpp @@ -30,7 +30,7 @@ #include "source/fuzz/transformation_add_global_variable.h" #include "source/fuzz/transformation_add_local_variable.h" #include "source/fuzz/transformation_add_no_contraction_decoration.h" -#include "source/fuzz/transformation_add_parameters.h" +#include "source/fuzz/transformation_add_parameter.h" #include "source/fuzz/transformation_add_spec_constant_op.h" #include "source/fuzz/transformation_add_type_array.h" #include "source/fuzz/transformation_add_type_boolean.h" @@ -115,8 +115,8 @@ std::unique_ptr Transformation::FromMessage( kAddNoContractionDecoration: return MakeUnique( message.add_no_contraction_decoration()); - case protobufs::Transformation::TransformationCase::kAddParameters: - return MakeUnique(message.add_parameters()); + case protobufs::Transformation::TransformationCase::kAddParameter: + return MakeUnique(message.add_parameter()); case protobufs::Transformation::TransformationCase::kAddSpecConstantOp: return MakeUnique( message.add_spec_constant_op()); diff --git a/source/fuzz/transformation_add_parameter.cpp b/source/fuzz/transformation_add_parameter.cpp new file mode 100644 index 000000000..c6ddc1c65 --- /dev/null +++ b/source/fuzz/transformation_add_parameter.cpp @@ -0,0 +1,135 @@ +// 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_parameter.h" + +#include + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddParameter::TransformationAddParameter( + const protobufs::TransformationAddParameter& message) + : message_(message) {} + +TransformationAddParameter::TransformationAddParameter( + uint32_t function_id, uint32_t parameter_fresh_id, uint32_t initializer_id, + uint32_t function_type_fresh_id) { + message_.set_function_id(function_id); + message_.set_parameter_fresh_id(parameter_fresh_id); + message_.set_initializer_id(initializer_id); + message_.set_function_type_fresh_id(function_type_fresh_id); +} + +bool TransformationAddParameter::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; + } + + // Check that |initializer_id| is valid. + const auto* initializer_inst = + ir_context->get_def_use_mgr()->GetDef(message_.initializer_id()); + + if (!initializer_inst) { + return false; + } + + // Check that initializer's type is valid. + const auto* initializer_type = + ir_context->get_type_mgr()->GetType(initializer_inst->type_id()); + + if (!initializer_type || initializer_type->AsVoid()) { + return false; + } + + return fuzzerutil::IsFreshId(ir_context, message_.parameter_fresh_id()) && + fuzzerutil::IsFreshId(ir_context, message_.function_type_fresh_id()) && + message_.parameter_fresh_id() != message_.function_type_fresh_id(); +} + +void TransformationAddParameter::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // Find the function that will be transformed + auto* function = fuzzerutil::FindFunction(ir_context, message_.function_id()); + assert(function && "Can't find the function"); + + auto parameter_type_id = + fuzzerutil::GetTypeId(ir_context, message_.initializer_id()); + + // Add new parameters to the function. + function->AddParameter(MakeUnique( + ir_context, SpvOpFunctionParameter, parameter_type_id, + message_.parameter_fresh_id(), opt::Instruction::OperandList())); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.parameter_fresh_id()); + + // 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(), [this](opt::Instruction* call) { + if (call->opcode() != SpvOpFunctionCall || + call->GetSingleWordInOperand(0) != message_.function_id()) { + return; + } + + // 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, {message_.initializer_id()}}); + }); + + auto* old_function_type = fuzzerutil::GetFunctionType(ir_context, function); + assert(old_function_type && "Function must have a valid type"); + + if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1) { + // Adjust existing function type if it is used only by this function. + old_function_type->AddOperand({SPV_OPERAND_TYPE_ID, {parameter_type_id}}); + } else { + // Otherwise, either create a new type or use an existing one. + std::vector type_ids; + type_ids.reserve(old_function_type->NumInOperands() + 1); + + for (uint32_t i = 0, n = old_function_type->NumInOperands(); i < n; ++i) { + type_ids.push_back(old_function_type->GetSingleWordInOperand(i)); + } + + type_ids.push_back(parameter_type_id); + + function->DefInst().SetInOperand( + 1, {fuzzerutil::FindOrCreateFunctionType( + ir_context, message_.function_type_fresh_id(), type_ids)}); + } + + // Make sure our changes are analyzed. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationAddParameter::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_parameter() = message_; + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/source/fuzz/transformation_add_parameter.h b/source/fuzz/transformation_add_parameter.h new file mode 100644 index 000000000..42b5fbb9a --- /dev/null +++ b/source/fuzz/transformation_add_parameter.h @@ -0,0 +1,62 @@ +// 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. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETER_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETER_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddParameter : public Transformation { + public: + explicit TransformationAddParameter( + const protobufs::TransformationAddParameter& message); + + TransformationAddParameter(uint32_t function_id, uint32_t parameter_fresh_id, + uint32_t initializer_id, + uint32_t function_type_fresh_id); + + // - |function_id| must be a valid result id of some non-entry-point function + // in the module. + // - |initializer_id| must be a valid result id of some instruction in the + // module. Instruction's type may not be OpTypeVoid. + // - |parameter_fresh_id| and |function_type_fresh_id| are fresh ids and are + // not equal. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - Creates a new OpFunctionParameter instruction with result id + // |parameter_fresh_id| for the function with |function_id|. + // - Adjusts function's type to include a new parameter. + // - Adds |initializer_id| as a new operand to every OpFunctionCall + // instruction that calls the function. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddParameter message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETER_H_ diff --git a/source/fuzz/transformation_add_parameters.cpp b/source/fuzz/transformation_add_parameters.cpp deleted file mode 100644 index 28af85490..000000000 --- a/source/fuzz/transformation_add_parameters.cpp +++ /dev/null @@ -1,201 +0,0 @@ -// 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 diff --git a/source/fuzz/transformation_add_parameters.h b/source/fuzz/transformation_add_parameters.h deleted file mode 100644 index 681195c19..000000000 --- a/source/fuzz/transformation_add_parameters.h +++ /dev/null @@ -1,70 +0,0 @@ -// 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. - -#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETERS_H_ -#define SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETERS_H_ - -#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" -#include "source/fuzz/transformation.h" -#include "source/fuzz/transformation_context.h" -#include "source/opt/ir_context.h" - -namespace spvtools { -namespace fuzz { - -class TransformationAddParameters : public Transformation { - public: - explicit TransformationAddParameters( - const protobufs::TransformationAddParameters& message); - - 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); - - // - |function_id| must be a valid result id of some non-entry-point function - // in the module. - // - |new_type_id| must be a result id of OpTypeFunction instruction. - // - New type of the function must have the same return type. New function - // parameters must be appended to the old ones. - // - |new_parameter_type| contains result ids of some OpType* instructions in - // the module. It may not contain result ids of OpTypeVoid. - // - |new_parameter_id| contains fresh ids. - // - |constant_id| contains result ids used to initialize new parameters. Type - // ids of these instructions must be the same as |new_parameter_type| (i.e. - // |new_parameter_type[i] == GetDef(constant_id[i])->type_id()|). - // - |new_parameter_id|, |new_parameter_type| and |constant_id| should all - // have the same size and may not be empty. - bool IsApplicable( - opt::IRContext* ir_context, - const TransformationContext& transformation_context) const override; - - // - Creates new OpFunctionParameter instructions for the function with - // |function_id|. - // - Changes type of the function to |new_type_id|. - // - Adds ids from |constant_id| to every OpFunctionCall instruction that - // calls the function. - void Apply(opt::IRContext* ir_context, - TransformationContext* transformation_context) const override; - - protobufs::Transformation ToMessage() const override; - - private: - protobufs::TransformationAddParameters message_; -}; - -} // namespace fuzz -} // namespace spvtools - -#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETERS_H_ diff --git a/source/fuzz/transformation_add_type_function.cpp b/source/fuzz/transformation_add_type_function.cpp index 991a28b01..c878025c6 100644 --- a/source/fuzz/transformation_add_type_function.cpp +++ b/source/fuzz/transformation_add_type_function.cpp @@ -55,48 +55,19 @@ bool TransformationAddTypeFunction::IsApplicable( // exactly the same return and argument type ids. (Note that the type manager // does not allow us to check this, as it does not distinguish between // function types with different but isomorphic pointer argument types.) - for (auto& inst : ir_context->module()->types_values()) { - if (inst.opcode() != SpvOpTypeFunction) { - // Consider only OpTypeFunction instructions. - continue; - } - if (inst.GetSingleWordInOperand(0) != message_.return_type_id()) { - // Different return types - cannot be the same. - continue; - } - if (inst.NumInOperands() != - 1 + static_cast(message_.argument_type_id().size())) { - // Different numbers of arguments - cannot be the same. - continue; - } - bool found_argument_mismatch = false; - for (uint32_t index = 1; index < inst.NumInOperands(); index++) { - if (message_.argument_type_id(index - 1) != - inst.GetSingleWordInOperand(index)) { - // Argument mismatch - cannot be the same. - found_argument_mismatch = true; - break; - } - } - if (found_argument_mismatch) { - continue; - } - // Everything matches - the type is already declared. - return false; - } - return true; + std::vector type_ids = {message_.return_type_id()}; + type_ids.insert(type_ids.end(), message_.argument_type_id().begin(), + message_.argument_type_id().end()); + return fuzzerutil::FindFunctionType(ir_context, type_ids) == 0; } void TransformationAddTypeFunction::Apply( opt::IRContext* ir_context, TransformationContext* /*unused*/) const { - opt::Instruction::OperandList in_operands; - in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.return_type_id()}}); - for (auto argument_type_id : message_.argument_type_id()) { - in_operands.push_back({SPV_OPERAND_TYPE_ID, {argument_type_id}}); - } - ir_context->module()->AddType(MakeUnique( - ir_context, SpvOpTypeFunction, 0, message_.fresh_id(), in_operands)); - fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + std::vector type_ids = {message_.return_type_id()}; + type_ids.insert(type_ids.end(), message_.argument_type_id().begin(), + message_.argument_type_id().end()); + + fuzzerutil::AddFunctionType(ir_context, message_.fresh_id(), type_ids); // We have added an instruction to the module, so need to be careful about the // validity of existing analyses. ir_context->InvalidateAnalysesExceptFor( diff --git a/source/fuzz/transformation_permute_function_parameters.cpp b/source/fuzz/transformation_permute_function_parameters.cpp index 4b9561c67..5f0660535 100644 --- a/source/fuzz/transformation_permute_function_parameters.cpp +++ b/source/fuzz/transformation_permute_function_parameters.cpp @@ -12,10 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "source/fuzz/transformation_permute_function_parameters.h" + #include #include "source/fuzz/fuzzer_util.h" -#include "source/fuzz/transformation_permute_function_parameters.h" namespace spvtools { namespace fuzz { @@ -28,10 +29,10 @@ TransformationPermuteFunctionParameters:: TransformationPermuteFunctionParameters:: TransformationPermuteFunctionParameters( - uint32_t function_id, uint32_t new_type_id, + uint32_t function_id, uint32_t function_type_fresh_id, const std::vector& permutation) { message_.set_function_id(function_id); - message_.set_new_type_id(new_type_id); + message_.set_function_type_fresh_id(function_type_fresh_id); for (auto index : permutation) { message_.add_permutation(index); @@ -77,49 +78,46 @@ bool TransformationPermuteFunctionParameters::IsApplicable( return false; } - // Check that new function's type is valid: - // - Has the same number of operands - // - Has the same result type as the old one - // - Order of arguments is permuted - auto new_type_id = message_.new_type_id(); - const auto* new_type = ir_context->get_def_use_mgr()->GetDef(new_type_id); - - if (!new_type || new_type->opcode() != SpvOpTypeFunction || - new_type->NumInOperands() != function_type->NumInOperands()) { - return false; - } - - // Check that both instructions have the same result type - if (new_type->GetSingleWordInOperand(0) != - function_type->GetSingleWordInOperand(0)) { - return false; - } - - // Check that new function type has its arguments permuted - for (int i = 0, n = static_cast(permutation.size()); i < n; ++i) { - // +1 to take return type into account - if (new_type->GetSingleWordInOperand(i + 1) != - function_type->GetSingleWordInOperand(permutation[i] + 1)) { - return false; - } - } - - return true; + return fuzzerutil::IsFreshId(ir_context, message_.function_type_fresh_id()); } void TransformationPermuteFunctionParameters::Apply( opt::IRContext* ir_context, TransformationContext* /*unused*/) const { - // Retrieve all data from the message - uint32_t function_id = message_.function_id(); - uint32_t new_type_id = message_.new_type_id(); - const auto& permutation = message_.permutation(); - // Find the function that will be transformed - auto* function = fuzzerutil::FindFunction(ir_context, function_id); + auto* function = fuzzerutil::FindFunction(ir_context, message_.function_id()); assert(function && "Can't find the function"); + auto* old_function_type_inst = + fuzzerutil::GetFunctionType(ir_context, function); + assert(old_function_type_inst && "Function must have a valid type"); + // Change function's type - function->DefInst().SetInOperand(1, {new_type_id}); + if (ir_context->get_def_use_mgr()->NumUsers(old_function_type_inst) == 1) { + // If only the current function uses |old_function_type_inst| - change it + // in-place. + opt::Instruction::OperandList permuted_operands = { + std::move(old_function_type_inst->GetInOperand(0))}; + for (auto index : message_.permutation()) { + // +1 since the first operand to OpTypeFunction is a return type. + permuted_operands.push_back( + std::move(old_function_type_inst->GetInOperand(index + 1))); + } + + old_function_type_inst->SetInOperands(std::move(permuted_operands)); + } else { + // Either use an existing type or create a new one. + std::vector type_ids = { + old_function_type_inst->GetSingleWordInOperand(0)}; + for (auto index : message_.permutation()) { + // +1 since the first operand to OpTypeFunction is a return type. + type_ids.push_back( + old_function_type_inst->GetSingleWordInOperand(index + 1)); + } + + function->DefInst().SetInOperand( + 1, {fuzzerutil::FindOrCreateFunctionType( + ir_context, message_.function_type_fresh_id(), type_ids)}); + } // Adjust OpFunctionParameter instructions @@ -133,7 +131,7 @@ void TransformationPermuteFunctionParameters::Apply( // Permute parameters' ids and types std::vector permuted_param_id, permuted_param_type; - for (auto index : permutation) { + for (auto index : message_.permutation()) { permuted_param_id.push_back(param_id[index]); permuted_param_type.push_back(param_type[index]); } @@ -149,10 +147,9 @@ void TransformationPermuteFunctionParameters::Apply( // Fix all OpFunctionCall instructions ir_context->get_def_use_mgr()->ForEachUser( - &function->DefInst(), - [function_id, &permutation](opt::Instruction* call) { + &function->DefInst(), [this](opt::Instruction* call) { if (call->opcode() != SpvOpFunctionCall || - call->GetSingleWordInOperand(0) != function_id) { + call->GetSingleWordInOperand(0) != message_.function_id()) { return; } @@ -160,7 +157,7 @@ void TransformationPermuteFunctionParameters::Apply( call->GetInOperand(0) // Function id }; - for (auto index : permutation) { + for (auto index : message_.permutation()) { // Take function id into account call_operands.push_back(call->GetInOperand(index + 1)); } diff --git a/source/fuzz/transformation_permute_function_parameters.h b/source/fuzz/transformation_permute_function_parameters.h index 994e4c24b..830805179 100644 --- a/source/fuzz/transformation_permute_function_parameters.h +++ b/source/fuzz/transformation_permute_function_parameters.h @@ -29,11 +29,11 @@ class TransformationPermuteFunctionParameters : public Transformation { const protobufs::TransformationPermuteFunctionParameters& message); TransformationPermuteFunctionParameters( - uint32_t function_id, uint32_t new_type_id, + uint32_t function_id, uint32_t function_type_fresh_id, const std::vector& permutation); // - |function_id| is a valid non-entry-point OpFunction instruction - // - |new_type_id| is a result id of a valid OpTypeFunction instruction. + // - |function_type_fresh_id| is a fresh id. // New type is valid if: // - it has the same number of operands as the old one // - function's result type is the same as the old one @@ -46,7 +46,7 @@ class TransformationPermuteFunctionParameters : public Transformation { // - OpFunction instruction with |result_id == function_id| is changed. // Its arguments are permuted according to the |permutation| vector - // - Changed function gets a new type specified by |type_id| + // - Adjusts function's type to accommodate for permuted parameters. // - Calls to the function are adjusted accordingly void Apply(opt::IRContext* ir_context, TransformationContext* transformation_context) const override; diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt index 7d9c35c5a..57d770dcb 100644 --- a/test/fuzz/CMakeLists.txt +++ b/test/fuzz/CMakeLists.txt @@ -37,7 +37,7 @@ if (${SPIRV_BUILD_FUZZER}) transformation_add_global_variable_test.cpp transformation_add_local_variable_test.cpp transformation_add_no_contraction_decoration_test.cpp - transformation_add_parameters_test.cpp + transformation_add_parameter_test.cpp transformation_add_type_array_test.cpp transformation_add_type_boolean_test.cpp transformation_add_type_float_test.cpp diff --git a/test/fuzz/transformation_add_parameter_test.cpp b/test/fuzz/transformation_add_parameter_test.cpp new file mode 100644 index 000000000..b95f5dd04 --- /dev/null +++ b/test/fuzz/transformation_add_parameter_test.cpp @@ -0,0 +1,126 @@ +// 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_parameter.h" + +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddParameterTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %7 = OpTypeBool + %11 = OpTypeInt 32 1 + %3 = OpTypeFunction %2 + %6 = OpTypeFunction %7 %7 + %8 = OpConstant %11 23 + %12 = OpConstantTrue %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpFunctionCall %7 %9 %12 + OpReturn + OpFunctionEnd + %9 = OpFunction %7 None %6 + %14 = OpFunctionParameter %7 + %10 = OpLabel + OpReturnValue %12 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(IsValid(env, context.get())); + + FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + // Can't modify entry point function. + ASSERT_FALSE(TransformationAddParameter(4, 15, 12, 16) + .IsApplicable(context.get(), transformation_context)); + + // There is no function with result id 29. + ASSERT_FALSE(TransformationAddParameter(29, 15, 8, 16) + .IsApplicable(context.get(), transformation_context)); + + // Parameter id is not fresh. + ASSERT_FALSE(TransformationAddParameter(9, 14, 8, 16) + .IsApplicable(context.get(), transformation_context)); + + // Function type id is not fresh. + ASSERT_FALSE(TransformationAddParameter(9, 15, 8, 14) + .IsApplicable(context.get(), transformation_context)); + + // Function type id and parameter type id are equal. + ASSERT_FALSE(TransformationAddParameter(9, 15, 8, 15) + .IsApplicable(context.get(), transformation_context)); + + // Parameter's initializer doesn't exist. + ASSERT_FALSE(TransformationAddParameter(9, 15, 15, 16) + .IsApplicable(context.get(), transformation_context)); + + // Correct transformation. + TransformationAddParameter correct(9, 15, 8, 16); + ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context)); + correct.Apply(context.get(), &transformation_context); + + // The module remains valid. + ASSERT_TRUE(IsValid(env, context.get())); + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %7 = OpTypeBool + %11 = OpTypeInt 32 1 + %3 = OpTypeFunction %2 + %6 = OpTypeFunction %7 %7 %11 + %8 = OpConstant %11 23 + %12 = OpConstantTrue %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpFunctionCall %7 %9 %12 %8 + OpReturn + OpFunctionEnd + %9 = OpFunction %7 None %6 + %14 = OpFunctionParameter %7 + %15 = OpFunctionParameter %11 + %10 = OpLabel + OpReturnValue %12 + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/test/fuzz/transformation_add_parameters_test.cpp b/test/fuzz/transformation_add_parameters_test.cpp deleted file mode 100644 index 37c2e9339..000000000 --- a/test/fuzz/transformation_add_parameters_test.cpp +++ /dev/null @@ -1,177 +0,0 @@ -// 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 "test/fuzz/fuzz_test_util.h" - -namespace spvtools { -namespace fuzz { -namespace { - -TEST(TransformationAddParametersTest, BasicTest) { - std::string shader = R"( - OpCapability Shader - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %4 "main" - OpExecutionMode %4 OriginUpperLeft - OpSource ESSL 310 - OpName %4 "main" - %2 = OpTypeVoid - %7 = OpTypeBool - %11 = OpTypeInt 32 1 - %3 = OpTypeFunction %2 - %20 = OpTypeFunction %2 %7 - %6 = OpTypeFunction %7 %7 - %12 = OpTypeFunction %7 %7 %11 - %13 = OpTypeFunction %7 %7 %7 - %14 = OpTypeFunction %11 %7 %11 - %15 = OpTypeFunction %7 %11 %11 - %16 = OpTypeFunction %7 %7 %11 %11 - %8 = OpConstant %11 23 - %17 = OpConstantTrue %7 - %4 = OpFunction %2 None %3 - %5 = OpLabel - %18 = OpFunctionCall %7 %9 %17 - OpReturn - OpFunctionEnd - %9 = OpFunction %7 None %6 - %19 = OpFunctionParameter %7 - %10 = OpLabel - OpReturnValue %17 - OpFunctionEnd - )"; - - const auto env = SPV_ENV_UNIVERSAL_1_3; - const auto consumer = nullptr; - const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); - ASSERT_TRUE(IsValid(env, context.get())); - - FactManager fact_manager; - spvtools::ValidatorOptions validator_options; - TransformationContext transformation_context(&fact_manager, - validator_options); - - // Can't modify entry point function. - ASSERT_FALSE(TransformationAddParameters(4, 6, {20}, {21}, {17}) - .IsApplicable(context.get(), transformation_context)); - - // There is no function with result id 10. - ASSERT_FALSE(TransformationAddParameters(29, 12, {11}, {21}, {8}) - .IsApplicable(context.get(), transformation_context)); - - // There is no OpTypeFunction instruction with result id 21. - ASSERT_FALSE(TransformationAddParameters(9, 21, {11}, {21}, {8}) - .IsApplicable(context.get(), transformation_context)); - - // Function type with id 6 has fewer parameters than required. - ASSERT_FALSE(TransformationAddParameters(9, 6, {11}, {21}, {8}) - .IsApplicable(context.get(), transformation_context)); - - // Function type with id 16 has more parameters than required. - ASSERT_FALSE(TransformationAddParameters(9, 16, {11}, {21}, {8}) - .IsApplicable(context.get(), transformation_context)); - - // New function type is not OpTypeFunction instruction. - ASSERT_FALSE(TransformationAddParameters(9, 11, {11}, {21}, {8}) - .IsApplicable(context.get(), transformation_context)); - - // Return type is invalid. - ASSERT_FALSE(TransformationAddParameters(9, 14, {11}, {21}, {8}) - .IsApplicable(context.get(), transformation_context)); - - // Types of original parameters are invalid. - ASSERT_FALSE(TransformationAddParameters(9, 15, {11}, {21}, {8}) - .IsApplicable(context.get(), transformation_context)); - - // Types of new parameters are invalid. - ASSERT_FALSE(TransformationAddParameters(9, 13, {11}, {21}, {8}) - .IsApplicable(context.get(), transformation_context)); - - // OpTypeVoid can't be the type of function parameter. - ASSERT_FALSE(TransformationAddParameters(9, 12, {2}, {21}, {8}) - .IsApplicable(context.get(), transformation_context)); - - // Vectors, that describe parameters, have different sizes. - ASSERT_FALSE(TransformationAddParameters(9, 12, {}, {21}, {8}) - .IsApplicable(context.get(), transformation_context)); - ASSERT_FALSE(TransformationAddParameters(9, 12, {11}, {}, {8}) - .IsApplicable(context.get(), transformation_context)); - ASSERT_FALSE(TransformationAddParameters(9, 12, {11}, {21}, {}) - .IsApplicable(context.get(), transformation_context)); - - // Vectors cannot be empty. - ASSERT_FALSE(TransformationAddParameters(9, 12, {}, {}, {}) - .IsApplicable(context.get(), transformation_context)); - - // Parameters' ids are not fresh. - ASSERT_FALSE(TransformationAddParameters(9, 12, {11}, {20}, {8}) - .IsApplicable(context.get(), transformation_context)); - - // Constants for parameters don't exist. - ASSERT_FALSE(TransformationAddParameters(9, 12, {11}, {21}, {21}) - .IsApplicable(context.get(), transformation_context)); - - // Constants for parameters have invalid type. - ASSERT_FALSE(TransformationAddParameters(9, 12, {11}, {21}, {17}) - .IsApplicable(context.get(), transformation_context)); - - // Correct transformation. - TransformationAddParameters correct(9, 12, {11}, {21}, {8}); - ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context)); - correct.Apply(context.get(), &transformation_context); - - // The module remains valid. - ASSERT_TRUE(IsValid(env, context.get())); - - std::string expected_shader = R"( - OpCapability Shader - %1 = OpExtInstImport "GLSL.std.450" - OpMemoryModel Logical GLSL450 - OpEntryPoint Fragment %4 "main" - OpExecutionMode %4 OriginUpperLeft - OpSource ESSL 310 - OpName %4 "main" - %2 = OpTypeVoid - %7 = OpTypeBool - %11 = OpTypeInt 32 1 - %3 = OpTypeFunction %2 - %20 = OpTypeFunction %2 %7 - %6 = OpTypeFunction %7 %7 - %12 = OpTypeFunction %7 %7 %11 - %13 = OpTypeFunction %7 %7 %7 - %14 = OpTypeFunction %11 %7 %11 - %15 = OpTypeFunction %7 %11 %11 - %16 = OpTypeFunction %7 %7 %11 %11 - %8 = OpConstant %11 23 - %17 = OpConstantTrue %7 - %4 = OpFunction %2 None %3 - %5 = OpLabel - %18 = OpFunctionCall %7 %9 %17 %8 - OpReturn - OpFunctionEnd - %9 = OpFunction %7 None %12 - %19 = OpFunctionParameter %7 - %21 = OpFunctionParameter %11 - %10 = OpLabel - OpReturnValue %17 - OpFunctionEnd - )"; - - ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); -} - -} // namespace -} // namespace fuzz -} // namespace spvtools diff --git a/test/fuzz/transformation_permute_function_parameters_test.cpp b/test/fuzz/transformation_permute_function_parameters_test.cpp index 13daff652..1443625ed 100644 --- a/test/fuzz/transformation_permute_function_parameters_test.cpp +++ b/test/fuzz/transformation_permute_function_parameters_test.cpp @@ -13,6 +13,7 @@ // limitations under the License. #include "source/fuzz/transformation_permute_function_parameters.h" + #include "test/fuzz/fuzz_test_util.h" namespace spvtools { @@ -73,7 +74,6 @@ TEST(TransformationPermuteFunctionParametersTest, BasicTest) { %18 = OpTypeFunction %8 %7 %15 %17 %24 = OpTypeBool %25 = OpTypeFunction %24 %15 %7 - %105 = OpTypeFunction %24 %7 %15 ; predefined type for %28 %31 = OpConstant %6 255 %33 = OpConstant %6 0 %34 = OpConstant %6 1 @@ -205,56 +205,46 @@ TEST(TransformationPermuteFunctionParametersTest, BasicTest) { validator_options); // Can't permute main function - ASSERT_FALSE(TransformationPermuteFunctionParameters(4, 0, {}).IsApplicable( - context.get(), transformation_context)); + ASSERT_FALSE(TransformationPermuteFunctionParameters(4, 105, {}) + .IsApplicable(context.get(), transformation_context)); // Can't permute invalid instruction - ASSERT_FALSE(TransformationPermuteFunctionParameters(101, 0, {}) + ASSERT_FALSE(TransformationPermuteFunctionParameters(101, 105, {}) .IsApplicable(context.get(), transformation_context)); // Permutation has too many values - ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {2, 1, 0, 3}) + ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 105, {2, 1, 0, 3}) .IsApplicable(context.get(), transformation_context)); // Permutation has too few values - ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {0, 1}) + ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 105, {0, 1}) .IsApplicable(context.get(), transformation_context)); // Permutation has invalid values 1 - ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {3, 1, 0}) + ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 105, {3, 1, 0}) .IsApplicable(context.get(), transformation_context)); #ifndef NDEBUG // Permutation has invalid values 2 - ASSERT_DEATH(TransformationPermuteFunctionParameters(22, 0, {2, 2, 1}) + ASSERT_DEATH(TransformationPermuteFunctionParameters(22, 105, {2, 2, 1}) .IsApplicable(context.get(), transformation_context), "Permutation has duplicates"); #endif - // Type id is not an OpTypeFunction instruction + // Result id for new function type is not fresh. ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 42, {2, 1, 0}) .IsApplicable(context.get(), transformation_context)); - // Type id has incorrect number of operands - ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 9, {2, 1, 0}) - .IsApplicable(context.get(), transformation_context)); - - // OpTypeFunction has operands out of order - ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 18, {2, 1, 0}) - .IsApplicable(context.get(), transformation_context)); - // Successful transformations { - // Function has two operands of the same type: - // initial OpTypeFunction should be enough - TransformationPermuteFunctionParameters transformation(12, 9, {1, 0}); + TransformationPermuteFunctionParameters transformation(12, 105, {1, 0}); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); transformation.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); } { - TransformationPermuteFunctionParameters transformation(28, 105, {1, 0}); + TransformationPermuteFunctionParameters transformation(28, 106, {1, 0}); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); transformation.Apply(context.get(), &transformation_context); @@ -313,8 +303,7 @@ TEST(TransformationPermuteFunctionParametersTest, BasicTest) { %17 = OpTypePointer Function %16 %18 = OpTypeFunction %8 %7 %15 %17 %24 = OpTypeBool - %25 = OpTypeFunction %24 %15 %7 - %105 = OpTypeFunction %24 %7 %15 ; predefined type for %28 + %25 = OpTypeFunction %24 %7 %15 %31 = OpConstant %6 255 %33 = OpConstant %6 0 %34 = OpConstant %6 1 @@ -422,7 +411,7 @@ TEST(TransformationPermuteFunctionParametersTest, BasicTest) { %55 = OpFunctionCall %8 %12 %54 %53 OpReturnValue %55 OpFunctionEnd - %28 = OpFunction %24 None %105 + %28 = OpFunction %24 None %25 %27 = OpFunctionParameter %7 %26 = OpFunctionParameter %15 %29 = OpLabel