From 4996129fd08cface6628d030c1066a99dc6c125b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=91=D1=80=D0=B0=D0=BD=D0=B8=D0=BC=D0=B8=D1=80=20=D0=9A?= =?UTF-8?q?=D0=B0=D1=80=D0=B0=D1=9F=D0=B8=D1=9B?= Date: Fri, 3 Jul 2020 18:29:17 -0700 Subject: [PATCH] Updated spirv-tools. --- .../include/generated/build-version.inc | 2 +- .../spirv-tools/source/fuzz/CMakeLists.txt | 12 +- 3rdparty/spirv-tools/source/fuzz/fuzzer.cpp | 8 + .../source/fuzz/fuzzer_context.cpp | 7 + .../spirv-tools/source/fuzz/fuzzer_context.h | 8 + .../spirv-tools/source/fuzz/fuzzer_pass.cpp | 31 +-- .../spirv-tools/source/fuzz/fuzzer_pass.h | 7 + .../fuzz/fuzzer_pass_add_copy_memory.cpp | 82 +++++++ .../source/fuzz/fuzzer_pass_add_copy_memory.h | 40 ++++ .../fuzz/fuzzer_pass_add_parameters.cpp | 47 +--- ...uzzer_pass_invert_comparison_operators.cpp | 52 +++++ .../fuzzer_pass_invert_comparison_operators.h | 40 ++++ ...uzzer_pass_permute_function_parameters.cpp | 13 +- .../spirv-tools/source/fuzz/fuzzer_util.cpp | 186 +++++++++++++++- .../spirv-tools/source/fuzz/fuzzer_util.h | 96 +++++++++ .../source/fuzz/protobufs/spvtoolsfuzz.proto | 77 +++++-- .../source/fuzz/transformation.cpp | 14 +- .../fuzz/transformation_add_copy_memory.cpp | 193 +++++++++++++++++ .../fuzz/transformation_add_copy_memory.h | 74 +++++++ .../fuzz/transformation_add_parameter.cpp | 143 +++++++++++++ .../fuzz/transformation_add_parameter.h | 62 ++++++ .../fuzz/transformation_add_parameters.cpp | 201 ------------------ .../fuzz/transformation_add_parameters.h | 70 ------ .../fuzz/transformation_add_type_float.cpp | 9 +- .../fuzz/transformation_add_type_function.cpp | 47 +--- .../fuzz/transformation_add_type_int.cpp | 12 +- .../fuzz/transformation_add_type_struct.cpp | 11 +- .../fuzz/transformation_add_type_vector.cpp | 10 +- ...nsformation_invert_comparison_operator.cpp | 142 +++++++++++++ ...ransformation_invert_comparison_operator.h | 63 ++++++ ...sformation_permute_function_parameters.cpp | 83 ++++---- ...ansformation_permute_function_parameters.h | 6 +- .../opt/aggressive_dead_code_elim_pass.cpp | 27 ++- .../opt/aggressive_dead_code_elim_pass.h | 4 +- 34 files changed, 1390 insertions(+), 489 deletions(-) create mode 100644 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.cpp create mode 100644 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.h create mode 100644 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp create mode 100644 3rdparty/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.h create mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_add_copy_memory.cpp create mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_add_copy_memory.h create mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_add_parameter.cpp create mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_add_parameter.h delete mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_add_parameters.cpp delete mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_add_parameters.h create mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_invert_comparison_operator.cpp create mode 100644 3rdparty/spirv-tools/source/fuzz/transformation_invert_comparison_operator.h diff --git a/3rdparty/spirv-tools/include/generated/build-version.inc b/3rdparty/spirv-tools/include/generated/build-version.inc index bde64ffca..14a992f21 100644 --- a/3rdparty/spirv-tools/include/generated/build-version.inc +++ b/3rdparty/spirv-tools/include/generated/build-version.inc @@ -1 +1 @@ -"v2020.4-dev", "SPIRV-Tools v2020.4-dev 0dd31c89ea3db9b4acd6c39bb6f31cc30adf7dc6" +"v2020.4-dev", "SPIRV-Tools v2020.4-dev 7efb218fd1272e44be3bc1e891ac153da59e9871" diff --git a/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt b/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt index 8893d3ea0..7bd9ff2ef 100644 --- a/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt +++ b/3rdparty/spirv-tools/source/fuzz/CMakeLists.txt @@ -39,6 +39,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass.h fuzzer_pass_add_access_chains.h fuzzer_pass_add_composite_types.h + fuzzer_pass_add_copy_memory.h fuzzer_pass_add_dead_blocks.h fuzzer_pass_add_dead_breaks.h fuzzer_pass_add_dead_continues.h @@ -60,6 +61,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_construct_composites.h fuzzer_pass_copy_objects.h fuzzer_pass_donate_modules.h + fuzzer_pass_invert_comparison_operators.h fuzzer_pass_merge_blocks.h fuzzer_pass_obfuscate_constants.h fuzzer_pass_outline_functions.h @@ -87,6 +89,7 @@ if(SPIRV_BUILD_FUZZER) transformation_add_constant_composite.h transformation_add_constant_null.h transformation_add_constant_scalar.h + transformation_add_copy_memory.h transformation_add_dead_block.h transformation_add_dead_break.h transformation_add_dead_continue.h @@ -95,7 +98,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 @@ -114,6 +117,7 @@ if(SPIRV_BUILD_FUZZER) transformation_copy_object.h transformation_equation_instruction.h transformation_function_call.h + transformation_invert_comparison_operator.h transformation_load.h transformation_merge_blocks.h transformation_move_block_down.h @@ -147,6 +151,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass.cpp fuzzer_pass_add_access_chains.cpp fuzzer_pass_add_composite_types.cpp + fuzzer_pass_add_copy_memory.cpp fuzzer_pass_add_dead_blocks.cpp fuzzer_pass_add_dead_breaks.cpp fuzzer_pass_add_dead_continues.cpp @@ -168,6 +173,7 @@ if(SPIRV_BUILD_FUZZER) fuzzer_pass_construct_composites.cpp fuzzer_pass_copy_objects.cpp fuzzer_pass_donate_modules.cpp + fuzzer_pass_invert_comparison_operators.cpp fuzzer_pass_merge_blocks.cpp fuzzer_pass_obfuscate_constants.cpp fuzzer_pass_outline_functions.cpp @@ -194,6 +200,7 @@ if(SPIRV_BUILD_FUZZER) transformation_add_constant_composite.cpp transformation_add_constant_null.cpp transformation_add_constant_scalar.cpp + transformation_add_copy_memory.cpp transformation_add_dead_block.cpp transformation_add_dead_break.cpp transformation_add_dead_continue.cpp @@ -202,7 +209,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 @@ -221,6 +228,7 @@ if(SPIRV_BUILD_FUZZER) transformation_copy_object.cpp transformation_equation_instruction.cpp transformation_function_call.cpp + transformation_invert_comparison_operator.cpp transformation_load.cpp transformation_merge_blocks.cpp transformation_move_block_down.cpp diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp index 36e7f90c3..6cb620228 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer.cpp @@ -23,6 +23,7 @@ #include "source/fuzz/fuzzer_context.h" #include "source/fuzz/fuzzer_pass_add_access_chains.h" #include "source/fuzz/fuzzer_pass_add_composite_types.h" +#include "source/fuzz/fuzzer_pass_add_copy_memory.h" #include "source/fuzz/fuzzer_pass_add_dead_blocks.h" #include "source/fuzz/fuzzer_pass_add_dead_breaks.h" #include "source/fuzz/fuzzer_pass_add_dead_continues.h" @@ -43,6 +44,7 @@ #include "source/fuzz/fuzzer_pass_construct_composites.h" #include "source/fuzz/fuzzer_pass_copy_objects.h" #include "source/fuzz/fuzzer_pass_donate_modules.h" +#include "source/fuzz/fuzzer_pass_invert_comparison_operators.h" #include "source/fuzz/fuzzer_pass_merge_blocks.h" #include "source/fuzz/fuzzer_pass_obfuscate_constants.h" #include "source/fuzz/fuzzer_pass_outline_functions.h" @@ -201,6 +203,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run( MaybeAddPass( &passes, ir_context.get(), &transformation_context, &fuzzer_context, transformation_sequence_out); + MaybeAddPass( + &passes, ir_context.get(), &transformation_context, &fuzzer_context, + transformation_sequence_out); MaybeAddPass( &passes, ir_context.get(), &transformation_context, &fuzzer_context, transformation_sequence_out); @@ -246,6 +251,9 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run( MaybeAddPass( &passes, ir_context.get(), &transformation_context, &fuzzer_context, transformation_sequence_out, donor_suppliers); + MaybeAddPass( + &passes, ir_context.get(), &transformation_context, &fuzzer_context, + transformation_sequence_out); MaybeAddPass( &passes, ir_context.get(), &transformation_context, &fuzzer_context, transformation_sequence_out); diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp index 97e145be8..9f119f6e4 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_context.cpp @@ -27,6 +27,7 @@ const std::pair kChanceOfAddingAccessChain = {5, 50}; const std::pair kChanceOfAddingAnotherStructField = {20, 90}; const std::pair kChanceOfAddingArrayOrStructType = {20, 90}; +const std::pair kChanceOfAddingCopyMemory = {20, 50}; const std::pair kChanceOfAddingDeadBlock = {20, 90}; const std::pair kChanceOfAddingDeadBreak = {5, 80}; const std::pair kChanceOfAddingDeadContinue = {5, 80}; @@ -58,6 +59,8 @@ const std::pair kChanceOfCopyingObject = {20, 50}; const std::pair kChanceOfDonatingAdditionalModule = {5, 50}; const std::pair kChanceOfGoingDeeperWhenMakingAccessChain = {50, 95}; +const std::pair kChanceOfInvertingComparisonOperators = { + 20, 50}; const std::pair kChanceOfMakingDonorLivesafe = {40, 60}; const std::pair kChanceOfMergingBlocks = {20, 95}; const std::pair kChanceOfMovingBlockDown = {20, 50}; @@ -119,6 +122,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, ChooseBetweenMinAndMax(kChanceOfAddingAnotherStructField); chance_of_adding_array_or_struct_type_ = ChooseBetweenMinAndMax(kChanceOfAddingArrayOrStructType); + chance_of_adding_copy_memory_ = + ChooseBetweenMinAndMax(kChanceOfAddingCopyMemory); chance_of_adding_dead_block_ = ChooseBetweenMinAndMax(kChanceOfAddingDeadBlock); chance_of_adding_dead_break_ = @@ -164,6 +169,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator, ChooseBetweenMinAndMax(kChanceOfDonatingAdditionalModule); chance_of_going_deeper_when_making_access_chain_ = ChooseBetweenMinAndMax(kChanceOfGoingDeeperWhenMakingAccessChain); + chance_of_inverting_comparison_operators_ = + ChooseBetweenMinAndMax(kChanceOfInvertingComparisonOperators); chance_of_making_donor_livesafe_ = ChooseBetweenMinAndMax(kChanceOfMakingDonorLivesafe); chance_of_merging_blocks_ = ChooseBetweenMinAndMax(kChanceOfMergingBlocks); diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_context.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_context.h index 459812416..682ba9a19 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_context.h +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_context.h @@ -114,6 +114,9 @@ class FuzzerContext { uint32_t GetChanceOfAddingArrayOrStructType() { return chance_of_adding_array_or_struct_type_; } + uint32_t GetChanceOfAddingCopyMemory() { + return chance_of_adding_copy_memory_; + } uint32_t GetChanceOfAddingDeadBlock() { return chance_of_adding_dead_block_; } uint32_t GetChanceOfAddingDeadBreak() { return chance_of_adding_dead_break_; } uint32_t GetChanceOfAddingDeadContinue() { @@ -172,6 +175,9 @@ class FuzzerContext { uint32_t GetChanceOfGoingDeeperWhenMakingAccessChain() { return chance_of_going_deeper_when_making_access_chain_; } + uint32_t GetChanceOfInvertingComparisonOperators() { + return chance_of_inverting_comparison_operators_; + } uint32_t ChanceOfMakingDonorLivesafe() { return chance_of_making_donor_livesafe_; } @@ -274,6 +280,7 @@ class FuzzerContext { uint32_t chance_of_adding_access_chain_; uint32_t chance_of_adding_another_struct_field_; uint32_t chance_of_adding_array_or_struct_type_; + uint32_t chance_of_adding_copy_memory_; uint32_t chance_of_adding_dead_block_; uint32_t chance_of_adding_dead_break_; uint32_t chance_of_adding_dead_continue_; @@ -298,6 +305,7 @@ class FuzzerContext { uint32_t chance_of_copying_object_; uint32_t chance_of_donating_additional_module_; uint32_t chance_of_going_deeper_when_making_access_chain_; + uint32_t chance_of_inverting_comparison_operators_; uint32_t chance_of_making_donor_livesafe_; uint32_t chance_of_merging_blocks_; uint32_t chance_of_moving_block_down_; diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp index a8cebe559..c44a4eaa7 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.cpp @@ -28,6 +28,7 @@ #include "source/fuzz/transformation_add_type_int.h" #include "source/fuzz/transformation_add_type_matrix.h" #include "source/fuzz/transformation_add_type_pointer.h" +#include "source/fuzz/transformation_add_type_struct.h" #include "source/fuzz/transformation_add_type_vector.h" namespace spvtools { @@ -162,9 +163,8 @@ uint32_t FuzzerPass::FindOrCreateBoolType() { } uint32_t FuzzerPass::FindOrCreateIntegerType(uint32_t width, bool is_signed) { - opt::analysis::Integer int_type(width, is_signed); - auto existing_id = GetIRContext()->get_type_mgr()->GetId(&int_type); - if (existing_id) { + if (auto existing_id = + fuzzerutil::MaybeGetIntegerType(GetIRContext(), width, is_signed)) { return existing_id; } auto result = GetFuzzerContext()->GetFreshId(); @@ -173,9 +173,7 @@ uint32_t FuzzerPass::FindOrCreateIntegerType(uint32_t width, bool is_signed) { } uint32_t FuzzerPass::FindOrCreateFloatType(uint32_t width) { - opt::analysis::Float float_type(width); - auto existing_id = GetIRContext()->get_type_mgr()->GetId(&float_type); - if (existing_id) { + if (auto existing_id = fuzzerutil::MaybeGetFloatType(GetIRContext(), width)) { return existing_id; } auto result = GetFuzzerContext()->GetFreshId(); @@ -205,14 +203,8 @@ uint32_t FuzzerPass::FindOrCreateFunctionType( uint32_t FuzzerPass::FindOrCreateVectorType(uint32_t component_type_id, uint32_t component_count) { - assert(component_count >= 2 && component_count <= 4 && - "Precondition: component count must be in range [2, 4]."); - opt::analysis::Type* component_type = - GetIRContext()->get_type_mgr()->GetType(component_type_id); - assert(component_type && "Precondition: the component type must exist."); - opt::analysis::Vector vector_type(component_type, component_count); - auto existing_id = GetIRContext()->get_type_mgr()->GetId(&vector_type); - if (existing_id) { + if (auto existing_id = fuzzerutil::MaybeGetVectorType( + GetIRContext(), component_type_id, component_count)) { return existing_id; } auto result = GetFuzzerContext()->GetFreshId(); @@ -242,6 +234,17 @@ uint32_t FuzzerPass::FindOrCreateMatrixType(uint32_t column_count, return result; } +uint32_t FuzzerPass::FindOrCreateStructType( + const std::vector& component_type_ids) { + if (auto existing_id = + fuzzerutil::MaybeGetStructType(GetIRContext(), component_type_ids)) { + return existing_id; + } + auto new_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeStruct(new_id, component_type_ids)); + return new_id; +} + uint32_t FuzzerPass::FindOrCreatePointerType(uint32_t base_type_id, SpvStorageClass storage_class) { // We do not use the type manager here, due to problems related to isomorphic diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h index fae222c99..f1f68ed07 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass.h @@ -136,6 +136,13 @@ class FuzzerPass { // type itself do not exist, transformations are applied to add them. uint32_t FindOrCreateMatrixType(uint32_t column_count, uint32_t row_count); + // Returns the id of an OpTypeStruct instruction with |component_type_ids| as + // type ids for struct's components. If no such a struct type exists, + // transformations are applied to add it. |component_type_ids| may not contain + // a result id of an OpTypeFunction. + uint32_t FindOrCreateStructType( + const std::vector& component_type_ids); + // Returns the id of a pointer type with base type |base_type_id| (which must // already exist) and storage class |storage_class|. A transformation is // applied to add the pointer if it does not already exist. diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.cpp new file mode 100644 index 000000000..ed375a1d7 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.cpp @@ -0,0 +1,82 @@ +// 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/fuzzer_pass_add_copy_memory.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_add_copy_memory.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddCopyMemory::FuzzerPassAddCopyMemory( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddCopyMemory::~FuzzerPassAddCopyMemory() = default; + +void FuzzerPassAddCopyMemory::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) { + // Check that we can insert an OpCopyMemory before this instruction. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyMemory, + inst_it)) { + return; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingCopyMemory())) { + return; + } + + // Get all instructions available before |inst_it| according to the + // domination rules. + auto instructions = FindAvailableInstructions( + function, block, inst_it, + TransformationAddCopyMemory::IsInstructionSupported); + + if (instructions.empty()) { + return; + } + + const auto* inst = + instructions[GetFuzzerContext()->RandomIndex(instructions)]; + + // Decide whether to create global or local variable. + auto storage_class = GetFuzzerContext()->ChooseEven() + ? SpvStorageClassPrivate + : SpvStorageClassFunction; + + auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType( + GetIRContext(), inst->type_id()); + + // Create a pointer type with |storage_class| if needed. + FindOrCreatePointerType(pointee_type_id, storage_class); + + ApplyTransformation(TransformationAddCopyMemory( + instruction_descriptor, GetFuzzerContext()->GetFreshId(), + inst->result_id(), storage_class, + FindOrCreateZeroConstant(pointee_type_id))); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.h new file mode 100644 index 000000000..321e4a1d6 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.h @@ -0,0 +1,40 @@ +// 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_FUZZER_PASS_ADD_COPY_MEMORY_INSTRUCTIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_COPY_MEMORY_INSTRUCTIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Randomly decides whether to add OpCopyMemory before some instruction in the +// module. +class FuzzerPassAddCopyMemory : public FuzzerPass { + public: + FuzzerPassAddCopyMemory(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddCopyMemory() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_COPY_MEMORY_INSTRUCTIONS_H_ diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.cpp index c93130207..927345209 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.cpp +++ b/3rdparty/spirv-tools/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/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp new file mode 100644 index 000000000..de4ff1de3 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp @@ -0,0 +1,52 @@ +// 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/fuzzer_pass_invert_comparison_operators.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_invert_comparison_operator.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassInvertComparisonOperators::FuzzerPassInvertComparisonOperators( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassInvertComparisonOperators::~FuzzerPassInvertComparisonOperators() = + default; + +void FuzzerPassInvertComparisonOperators::Apply() { + GetIRContext()->module()->ForEachInst([this](const opt::Instruction* inst) { + if (!TransformationInvertComparisonOperator::IsInversionSupported( + inst->opcode())) { + return; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfInvertingComparisonOperators())) { + return; + } + + ApplyTransformation(TransformationInvertComparisonOperator( + inst->result_id(), GetFuzzerContext()->GetFreshId())); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.h new file mode 100644 index 000000000..9c80bbbb0 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.h @@ -0,0 +1,40 @@ +// 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_FUZZER_PASS_INVERT_COMPARISON_OPERATORS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_INVERT_COMPARISON_OPERATORS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Iterates over different comparison operators in the module (>=, <, > etc.) +// and randomly decides whether to invert each one or not. +class FuzzerPassInvertComparisonOperators : public FuzzerPass { + public: + FuzzerPassInvertComparisonOperators( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassInvertComparisonOperators() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_INVERT_COMPARISON_OPERATORS_H_ \ No newline at end of file diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp index 57d9cabbc..e15aef6e4 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp +++ b/3rdparty/spirv-tools/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/3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp b/3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp index 79881fcdd..80dff2d10 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_util.cpp +++ b/3rdparty/spirv-tools/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,184 @@ 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; +} + +uint32_t MaybeGetIntegerType(opt::IRContext* ir_context, uint32_t width, + bool is_signed) { + opt::analysis::Integer type(width, is_signed); + return ir_context->get_type_mgr()->GetId(&type); +} + +uint32_t MaybeGetFloatType(opt::IRContext* ir_context, uint32_t width) { + opt::analysis::Float type(width); + return ir_context->get_type_mgr()->GetId(&type); +} + +uint32_t MaybeGetVectorType(opt::IRContext* ir_context, + uint32_t component_type_id, + uint32_t element_count) { + const auto* component_type = + ir_context->get_type_mgr()->GetType(component_type_id); + assert(component_type && + (component_type->AsInteger() || component_type->AsFloat() || + component_type->AsBool()) && + "|component_type_id| is invalid"); + assert(element_count >= 2 && element_count <= 4 && + "Precondition: component count must be in range [2, 4]."); + opt::analysis::Vector type(component_type, element_count); + return ir_context->get_type_mgr()->GetId(&type); +} + +uint32_t MaybeGetStructType(opt::IRContext* ir_context, + const std::vector& component_type_ids) { + std::vector component_types; + component_types.reserve(component_type_ids.size()); + + for (auto type_id : component_type_ids) { + const auto* component_type = ir_context->get_type_mgr()->GetType(type_id); + assert(component_type && !component_type->AsFunction() && + "Component type is invalid"); + component_types.push_back(component_type); + } + + opt::analysis::Struct type(component_types); + return ir_context->get_type_mgr()->GetId(&type); +} + +void AddIntegerType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t width, bool is_signed) { + ir_context->module()->AddType(MakeUnique( + ir_context, SpvOpTypeInt, 0, result_id, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {width}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {is_signed ? 1u : 0u}}})); + + UpdateModuleIdBound(ir_context, result_id); +} + +void AddFloatType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t width) { + ir_context->module()->AddType(MakeUnique( + ir_context, SpvOpTypeFloat, 0, result_id, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {width}}})); + + UpdateModuleIdBound(ir_context, result_id); +} + +void AddVectorType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t component_type_id, uint32_t element_count) { + const auto* component_type = + ir_context->get_type_mgr()->GetType(component_type_id); + (void)component_type; // Make compiler happy in release mode. + assert(component_type && + (component_type->AsInteger() || component_type->AsFloat() || + component_type->AsBool()) && + "|component_type_id| is invalid"); + assert(element_count >= 2 && element_count <= 4 && + "Precondition: component count must be in range [2, 4]."); + ir_context->module()->AddType(MakeUnique( + ir_context, SpvOpTypeVector, 0, result_id, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {component_type_id}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {element_count}}})); + + UpdateModuleIdBound(ir_context, result_id); +} + +void AddStructType(opt::IRContext* ir_context, uint32_t result_id, + const std::vector& component_type_ids) { + opt::Instruction::OperandList operands; + operands.reserve(component_type_ids.size()); + + for (auto type_id : component_type_ids) { + const auto* type = ir_context->get_type_mgr()->GetType(type_id); + (void)type; // Make compiler happy in release mode. + assert(type && !type->AsFunction() && "Component's type id is invalid"); + operands.push_back({SPV_OPERAND_TYPE_ID, {type_id}}); + } + + ir_context->AddType(MakeUnique( + ir_context, SpvOpTypeStruct, 0, result_id, std::move(operands))); + + UpdateModuleIdBound(ir_context, result_id); +} + +uint32_t FindOrCreateIntegerType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t width, bool is_signed) { + if (auto existing_id = MaybeGetIntegerType(ir_context, width, is_signed)) { + return existing_id; + } + AddIntegerType(ir_context, result_id, width, is_signed); + return result_id; +} + +uint32_t FindOrCreateFloatType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t width) { + if (auto existing_id = MaybeGetFloatType(ir_context, width)) { + return existing_id; + } + AddFloatType(ir_context, result_id, width); + return result_id; +} + +uint32_t FindOrCreateVectorType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t component_type_id, + uint32_t element_count) { + if (auto existing_id = + MaybeGetVectorType(ir_context, component_type_id, element_count)) { + return existing_id; + } + AddVectorType(ir_context, result_id, component_type_id, element_count); + return result_id; +} + +uint32_t FindOrCreateStructType( + opt::IRContext* ir_context, uint32_t result_id, + const std::vector& component_type_ids) { + if (auto existing_id = MaybeGetStructType(ir_context, component_type_ids)) { + return existing_id; + } + AddStructType(ir_context, result_id, component_type_ids); + return result_id; +} + } // namespace fuzzerutil } // namespace fuzz diff --git a/3rdparty/spirv-tools/source/fuzz/fuzzer_util.h b/3rdparty/spirv-tools/source/fuzz/fuzzer_util.h index 0cc5d496e..5b471bd34 100644 --- a/3rdparty/spirv-tools/source/fuzz/fuzzer_util.h +++ b/3rdparty/spirv-tools/source/fuzz/fuzzer_util.h @@ -266,6 +266,102 @@ 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); + +// Returns a result id of an OpTypeInt instruction if present. Returns 0 +// otherwise. +uint32_t MaybeGetIntegerType(opt::IRContext* ir_context, uint32_t width, + bool is_signed); + +// Returns a result id of an OpTypeFloat instruction if present. Returns 0 +// otherwise. +uint32_t MaybeGetFloatType(opt::IRContext* ir_context, uint32_t width); + +// Returns a result id of an OpTypeVector instruction if present. Returns 0 +// otherwise. |component_type_id| must be a valid result id of an OpTypeInt, +// OpTypeFloat or OpTypeBool instruction in the module. |element_count| must be +// in the range [2, 4]. +uint32_t MaybeGetVectorType(opt::IRContext* ir_context, + uint32_t component_type_id, uint32_t element_count); + +// Returns a result id of an OpTypeStruct instruction if present. Returns 0 +// otherwise. |component_type_ids| may not contain a result id of an +// OpTypeFunction. +uint32_t MaybeGetStructType(opt::IRContext* ir_context, + const std::vector& component_type_ids); + +// Creates a new OpTypeInt instruction in the module. Updates module's id bound +// to accommodate for |result_id|. +void AddIntegerType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t width, bool is_signed); + +// Creates a new OpTypeFloat instruction in the module. Updates module's id +// bound to accommodate for |result_id|. +void AddFloatType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t width); + +// Creates a new OpTypeVector instruction in the module. |component_type_id| +// must be a valid result id of an OpTypeInt, OpTypeFloat or OpTypeBool +// instruction in the module. |element_count| must be in the range [2, 4]. +// Updates module's id bound to accommodate for |result_id|. +void AddVectorType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t component_type_id, uint32_t element_count); + +// Creates a new OpTypeStruct instruction in the module. Updates module's id +// bound to accommodate for |result_id|. |component_type_ids| may not contain +// a result id of an OpTypeFunction. +void AddStructType(opt::IRContext* ir_context, uint32_t result_id, + const std::vector& component_type_ids); + +// Returns a result id of an OpTypeInt instruction in the module. Creates a new +// instruction with |result_id|, if no required OpTypeInt is present in the +// module, and returns |result_id|. Updates module's id bound to accommodate for +// |result_id|. +uint32_t FindOrCreateIntegerType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t width, bool is_signed); + +// Returns a result id of an OpTypeFloat instruction in the module. Creates a +// new instruction with |result_id|, if no required OpTypeFloat is present in +// the module, and returns |result_id|. Updates module's id bound +// to accommodate for |result_id|. +uint32_t FindOrCreateFloatType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t width); + +// Returns a result id of an OpTypeVector instruction in the module. Creates a +// new instruction with |result_id|, if no required OpTypeVector is present in +// the module, and returns |result_id|. |component_type_id| must be a valid +// result id of an OpTypeInt, OpTypeFloat or OpTypeBool instruction in the +// module. |element_count| must be in the range [2, 4]. Updates module's id +// bound to accommodate for |result_id|. +uint32_t FindOrCreateVectorType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t component_type_id, + uint32_t element_count); + +// Returns a result id of an OpTypeStruct instruction in the module. Creates a +// new instruction with |result_id|, if no required OpTypeStruct is present in +// the module, and returns |result_id|. Updates module's id bound +// to accommodate for |result_id|. |component_type_ids| may not contain a result +// id of an OpTypeFunction. +uint32_t FindOrCreateStructType( + opt::IRContext* ir_context, uint32_t result_id, + const std::vector& component_type_ids); + } // namespace fuzzerutil } // namespace fuzz diff --git a/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto b/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto index 7b8adc08d..a7cc7c992 100644 --- a/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto +++ b/3rdparty/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -380,7 +380,9 @@ 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; + TransformationAddCopyMemory add_copy_memory = 53; + TransformationInvertComparisonOperator invert_comparison_operator = 54; // Add additional option using the next available number. } } @@ -458,6 +460,30 @@ message TransformationAddConstantScalar { } +message TransformationAddCopyMemory { + + // Adds an OpCopyMemory instruction into the module. + // Creates either a global or a local variable (based on + // |storage_class| field) to copy the target into. + + // OpCopyMemory will be inserted before this instruction. + InstructionDescriptor instruction_descriptor = 1; + + // Fresh id to copy memory into. + uint32 fresh_id = 2; + + // Source to copy memory from. + uint32 source_id = 3; + + // Storage class for the target variable. Can be either Function or Private. + uint32 storage_class = 4; + + // Result id for the variable's initializer operand. Its type must be equal to + // variable's pointee type. + uint32 initializer_id = 5; + +} + message TransformationAddDeadBlock { // Adds a new block to the module that is statically reachable from an @@ -624,24 +650,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; } @@ -916,6 +943,22 @@ message TransformationFunctionCall { } +message TransformationInvertComparisonOperator { + + // For some instruction with result id |operator_id| that + // represents a binary comparison operator (e.g. <, >, <=), this transformation + // will replace that instruction's result id with |fresh_id|, + // invert the opcode (< will become >=) and insert OpLogicalNot + // instruction with result id |operator_id| below. + + // Result id of the instruction to invert. + uint32 operator_id = 1; + + // Fresh id that will be used by the operator after the inversion. + uint32 fresh_id = 2; + +} + message TransformationLoad { // Transformation that adds an OpLoad instruction from a pointer into an id. @@ -1011,12 +1054,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/3rdparty/spirv-tools/source/fuzz/transformation.cpp b/3rdparty/spirv-tools/source/fuzz/transformation.cpp index 3177de0ac..26c93efb7 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation.cpp @@ -22,6 +22,7 @@ #include "source/fuzz/transformation_add_constant_composite.h" #include "source/fuzz/transformation_add_constant_null.h" #include "source/fuzz/transformation_add_constant_scalar.h" +#include "source/fuzz/transformation_add_copy_memory.h" #include "source/fuzz/transformation_add_dead_block.h" #include "source/fuzz/transformation_add_dead_break.h" #include "source/fuzz/transformation_add_dead_continue.h" @@ -30,7 +31,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" @@ -48,6 +49,7 @@ #include "source/fuzz/transformation_copy_object.h" #include "source/fuzz/transformation_equation_instruction.h" #include "source/fuzz/transformation_function_call.h" +#include "source/fuzz/transformation_invert_comparison_operator.h" #include "source/fuzz/transformation_load.h" #include "source/fuzz/transformation_merge_blocks.h" #include "source/fuzz/transformation_move_block_down.h" @@ -93,6 +95,8 @@ std::unique_ptr Transformation::FromMessage( case protobufs::Transformation::TransformationCase::kAddConstantScalar: return MakeUnique( message.add_constant_scalar()); + case protobufs::Transformation::TransformationCase::kAddCopyMemory: + return MakeUnique(message.add_copy_memory()); case protobufs::Transformation::TransformationCase::kAddDeadBlock: return MakeUnique(message.add_dead_block()); case protobufs::Transformation::TransformationCase::kAddDeadBreak: @@ -115,8 +119,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()); @@ -161,6 +165,10 @@ std::unique_ptr Transformation::FromMessage( message.equation_instruction()); case protobufs::Transformation::TransformationCase::kFunctionCall: return MakeUnique(message.function_call()); + case protobufs::Transformation::TransformationCase:: + kInvertComparisonOperator: + return MakeUnique( + message.invert_comparison_operator()); case protobufs::Transformation::TransformationCase::kLoad: return MakeUnique(message.load()); case protobufs::Transformation::TransformationCase::kMergeBlocks: diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_copy_memory.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_copy_memory.cpp new file mode 100644 index 000000000..e9c401d73 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_copy_memory.cpp @@ -0,0 +1,193 @@ +// 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_copy_memory.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/opt/instruction.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddCopyMemory::TransformationAddCopyMemory( + const protobufs::TransformationAddCopyMemory& message) + : message_(message) {} + +TransformationAddCopyMemory::TransformationAddCopyMemory( + const protobufs::InstructionDescriptor& instruction_descriptor, + uint32_t fresh_id, uint32_t source_id, SpvStorageClass storage_class, + uint32_t initializer_id) { + *message_.mutable_instruction_descriptor() = instruction_descriptor; + message_.set_fresh_id(fresh_id); + message_.set_source_id(source_id); + message_.set_storage_class(storage_class); + message_.set_initializer_id(initializer_id); +} + +bool TransformationAddCopyMemory::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // Check that target id is fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + // Check that instruction descriptor is valid. This also checks that + // |message_.instruction_descriptor| is not a global instruction. + auto* inst = FindInstruction(message_.instruction_descriptor(), ir_context); + if (!inst) { + return false; + } + + // Check that we can insert OpCopyMemory before |instruction_descriptor|. + auto iter = fuzzerutil::GetIteratorForInstruction( + ir_context->get_instr_block(inst), inst); + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyMemory, iter)) { + return false; + } + + // Check that source instruction exists and is valid. + auto* source_inst = + ir_context->get_def_use_mgr()->GetDef(message_.source_id()); + if (!source_inst || !IsInstructionSupported(ir_context, source_inst)) { + return false; + } + + // |storage_class| is either Function or Private. + if (message_.storage_class() != SpvStorageClassFunction && + message_.storage_class() != SpvStorageClassPrivate) { + return false; + } + + auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType( + ir_context, source_inst->type_id()); + + // OpTypePointer with |message_.storage_class| exists. + if (!fuzzerutil::MaybeGetPointerType( + ir_context, pointee_type_id, + static_cast(message_.storage_class()))) { + return false; + } + + // Check that |initializer_id| exists and has valid type. + const auto* initializer_inst = + ir_context->get_def_use_mgr()->GetDef(message_.initializer_id()); + if (!initializer_inst || initializer_inst->type_id() != pointee_type_id) { + return false; + } + + // Check that domination rules are satisfied. + return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, inst, + message_.source_id()); +} + +void TransformationAddCopyMemory::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Insert OpCopyMemory before |instruction_descriptor|. + auto* insert_before_inst = + FindInstruction(message_.instruction_descriptor(), ir_context); + assert(insert_before_inst); + + auto insert_before_iter = fuzzerutil::GetIteratorForInstruction( + ir_context->get_instr_block(insert_before_inst), insert_before_inst); + + insert_before_iter.InsertBefore(MakeUnique( + ir_context, SpvOpCopyMemory, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.source_id()}}})); + + // Add global or local variable to copy memory into. + auto storage_class = static_cast(message_.storage_class()); + auto type_id = fuzzerutil::MaybeGetPointerType( + ir_context, + fuzzerutil::GetPointeeTypeIdFromPointerType( + ir_context, fuzzerutil::GetTypeId(ir_context, message_.source_id())), + storage_class); + + if (storage_class == SpvStorageClassPrivate) { + fuzzerutil::AddGlobalVariable(ir_context, message_.fresh_id(), type_id, + storage_class, message_.initializer_id()); + } else { + assert(storage_class == SpvStorageClassFunction && + "Storage class can be either Private or Function"); + fuzzerutil::AddLocalVariable(ir_context, message_.fresh_id(), type_id, + ir_context->get_instr_block(insert_before_inst) + ->GetParent() + ->result_id(), + message_.initializer_id()); + } + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Even though the copy memory instruction will - at least temporarily - lead + // to the destination and source pointers referring to identical values, this + // fact is not guaranteed to hold throughout execution of the SPIR-V code + // since the source pointer could be over-written. We thus assume nothing + // about the destination pointer, and record this fact so that the destination + // pointer can be used freely by other fuzzer passes. + transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + message_.fresh_id()); + + // Make sure our changes are analyzed + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationAddCopyMemory::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_copy_memory() = message_; + return result; +} + +bool TransformationAddCopyMemory::IsInstructionSupported( + opt::IRContext* ir_context, opt::Instruction* inst) { + if (!inst->result_id() || !inst->type_id() || + inst->opcode() == SpvOpConstantNull || inst->opcode() == SpvOpUndef) { + return false; + } + + const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id()); + assert(type && "Instruction must have a valid type"); + + return type->AsPointer() && + CanUsePointeeWithCopyMemory(*type->AsPointer()->pointee_type()); +} + +bool TransformationAddCopyMemory::CanUsePointeeWithCopyMemory( + const opt::analysis::Type& type) { + switch (type.kind()) { + case opt::analysis::Type::kBool: + case opt::analysis::Type::kInteger: + case opt::analysis::Type::kFloat: + case opt::analysis::Type::kArray: + return true; + case opt::analysis::Type::kVector: + return CanUsePointeeWithCopyMemory(*type.AsVector()->element_type()); + case opt::analysis::Type::kMatrix: + return CanUsePointeeWithCopyMemory(*type.AsMatrix()->element_type()); + case opt::analysis::Type::kStruct: + return std::all_of(type.AsStruct()->element_types().begin(), + type.AsStruct()->element_types().end(), + [](const opt::analysis::Type* element) { + return CanUsePointeeWithCopyMemory(*element); + }); + default: + return false; + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_copy_memory.h b/3rdparty/spirv-tools/source/fuzz/transformation_add_copy_memory.h new file mode 100644 index 000000000..138d9925b --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_copy_memory.h @@ -0,0 +1,74 @@ +// 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_COPY_MEMORY_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_COPY_MEMORY_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 TransformationAddCopyMemory : public Transformation { + public: + explicit TransformationAddCopyMemory( + const protobufs::TransformationAddCopyMemory& message); + + TransformationAddCopyMemory( + const protobufs::InstructionDescriptor& instruction_descriptor, + uint32_t fresh_id, uint32_t source_id, SpvStorageClass storage_class, + uint32_t initializer_id); + + // - |instruction_descriptor| must point to a valid instruction in the module. + // - it should be possible to insert OpCopyMemory before + // |instruction_descriptor| (i.e. the module remains valid after the + // insertion). + // - |source_id| must be a result id for some valid instruction in the module. + // - |fresh_id| must be a fresh id to copy memory into. + // - type of |source_id| must be OpTypePointer where pointee can be used with + // OpCopyMemory. + // - |storage_class| must be either Private or Function. + // - type ids of instructions with result ids |source_id| and |initialize_id| + // must be the same. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // A global or local variable with id |target_id| and |storage_class| class is + // created. An 'OpCopyMemory %fresh_id %source_id' instruction is inserted + // before the |instruction_descriptor|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if we can copy memory from |instruction| using OpCopyMemory. + static bool IsInstructionSupported(opt::IRContext* ir_context, + opt::Instruction* inst); + + private: + // Returns whether the type, pointed to by some OpTypePointer, can be used + // with OpCopyMemory instruction. + static bool CanUsePointeeWithCopyMemory(const opt::analysis::Type& type); + + protobufs::TransformationAddCopyMemory message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_COPY_MEMORY_H_ diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_add_parameter.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_parameter.cpp new file mode 100644 index 000000000..b2dd7be9b --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_parameter.cpp @@ -0,0 +1,143 @@ +// 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()); + assert(parameter_type_id != 0 && "Initializer has invalid type"); + + // 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}}); + + // We must make sure that all dependencies of |old_function_type| are + // evaluated before |old_function_type| (i.e. the domination rules are not + // broken). Thus, we move |old_function_type| to the end of the list of all + // types in the module. + old_function_type->RemoveFromList(); + ir_context->AddType(std::unique_ptr(old_function_type)); + } 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/3rdparty/spirv-tools/source/fuzz/transformation_add_parameter.h b/3rdparty/spirv-tools/source/fuzz/transformation_add_parameter.h new file mode 100644 index 000000000..42b5fbb9a --- /dev/null +++ b/3rdparty/spirv-tools/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/3rdparty/spirv-tools/source/fuzz/transformation_add_parameters.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_parameters.cpp deleted file mode 100644 index 28af85490..000000000 --- a/3rdparty/spirv-tools/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/3rdparty/spirv-tools/source/fuzz/transformation_add_parameters.h b/3rdparty/spirv-tools/source/fuzz/transformation_add_parameters.h deleted file mode 100644 index 681195c19..000000000 --- a/3rdparty/spirv-tools/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/3rdparty/spirv-tools/source/fuzz/transformation_add_type_float.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_type_float.cpp index 80716e14f..c0c434bb9 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_type_float.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_type_float.cpp @@ -38,17 +38,12 @@ bool TransformationAddTypeFloat::IsApplicable( // Applicable if there is no float type with this width already declared in // the module. - opt::analysis::Float float_type(message_.width()); - return ir_context->get_type_mgr()->GetId(&float_type) == 0; + return fuzzerutil::MaybeGetFloatType(ir_context, message_.width()) == 0; } void TransformationAddTypeFloat::Apply( opt::IRContext* ir_context, TransformationContext* /*unused*/) const { - opt::Instruction::OperandList width = { - {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.width()}}}; - ir_context->module()->AddType(MakeUnique( - ir_context, SpvOpTypeFloat, 0, message_.fresh_id(), width)); - fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + fuzzerutil::AddFloatType(ir_context, message_.fresh_id(), message_.width()); // 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/3rdparty/spirv-tools/source/fuzz/transformation_add_type_function.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_type_function.cpp index 991a28b01..c878025c6 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_type_function.cpp +++ b/3rdparty/spirv-tools/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/3rdparty/spirv-tools/source/fuzz/transformation_add_type_int.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_type_int.cpp index a932a5f96..20759fc84 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_type_int.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_type_int.cpp @@ -40,18 +40,14 @@ bool TransformationAddTypeInt::IsApplicable( // Applicable if there is no int type with this width and signedness already // declared in the module. - opt::analysis::Integer int_type(message_.width(), message_.is_signed()); - return ir_context->get_type_mgr()->GetId(&int_type) == 0; + return fuzzerutil::MaybeGetIntegerType(ir_context, message_.width(), + message_.is_signed()) == 0; } void TransformationAddTypeInt::Apply(opt::IRContext* ir_context, TransformationContext* /*unused*/) const { - opt::Instruction::OperandList in_operands = { - {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.width()}}, - {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.is_signed() ? 1u : 0u}}}; - ir_context->module()->AddType(MakeUnique( - ir_context, SpvOpTypeInt, 0, message_.fresh_id(), in_operands)); - fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + fuzzerutil::AddIntegerType(ir_context, message_.fresh_id(), message_.width(), + message_.is_signed()); // 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/3rdparty/spirv-tools/source/fuzz/transformation_add_type_struct.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_type_struct.cpp index 6ce5ea11c..a7345a17a 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_type_struct.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_type_struct.cpp @@ -50,13 +50,10 @@ bool TransformationAddTypeStruct::IsApplicable( void TransformationAddTypeStruct::Apply( opt::IRContext* ir_context, TransformationContext* /*unused*/) const { - opt::Instruction::OperandList in_operands; - for (auto member_type : message_.member_type_id()) { - in_operands.push_back({SPV_OPERAND_TYPE_ID, {member_type}}); - } - ir_context->module()->AddType(MakeUnique( - ir_context, SpvOpTypeStruct, 0, message_.fresh_id(), in_operands)); - fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + fuzzerutil::AddStructType( + ir_context, message_.fresh_id(), + std::vector(message_.member_type_id().begin(), + message_.member_type_id().end())); // 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/3rdparty/spirv-tools/source/fuzz/transformation_add_type_vector.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_add_type_vector.cpp index f7b2fb59f..10a622412 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_add_type_vector.cpp +++ b/3rdparty/spirv-tools/source/fuzz/transformation_add_type_vector.cpp @@ -46,13 +46,9 @@ bool TransformationAddTypeVector::IsApplicable( void TransformationAddTypeVector::Apply( opt::IRContext* ir_context, TransformationContext* /*unused*/) const { - opt::Instruction::OperandList in_operands; - in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.component_type_id()}}); - in_operands.push_back( - {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.component_count()}}); - ir_context->module()->AddType(MakeUnique( - ir_context, SpvOpTypeVector, 0, message_.fresh_id(), in_operands)); - fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + fuzzerutil::AddVectorType(ir_context, message_.fresh_id(), + message_.component_type_id(), + message_.component_count()); // 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/3rdparty/spirv-tools/source/fuzz/transformation_invert_comparison_operator.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_invert_comparison_operator.cpp new file mode 100644 index 000000000..be8ce5e74 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_invert_comparison_operator.cpp @@ -0,0 +1,142 @@ +// 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_invert_comparison_operator.h" + +#include + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationInvertComparisonOperator::TransformationInvertComparisonOperator( + protobufs::TransformationInvertComparisonOperator message) + : message_(std::move(message)) {} + +TransformationInvertComparisonOperator::TransformationInvertComparisonOperator( + uint32_t operator_id, uint32_t fresh_id) { + message_.set_operator_id(operator_id); + message_.set_fresh_id(fresh_id); +} + +bool TransformationInvertComparisonOperator::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // |message_.operator_id| must be valid and inversion must be supported for + // it. + auto* inst = ir_context->get_def_use_mgr()->GetDef(message_.operator_id()); + if (!inst || !IsInversionSupported(inst->opcode())) { + return false; + } + + // Check that we can insert negation instruction. + auto* block = ir_context->get_instr_block(inst); + assert(block && "Instruction must have a basic block"); + + auto iter = fuzzerutil::GetIteratorForInstruction(block, inst); + ++iter; + assert(iter != block->end() && "Instruction can't be the last in the block"); + assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter) && + "Can't insert negation after comparison operator"); + + // |message_.fresh_id| must be fresh. + return fuzzerutil::IsFreshId(ir_context, message_.fresh_id()); +} + +void TransformationInvertComparisonOperator::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto* inst = ir_context->get_def_use_mgr()->GetDef(message_.operator_id()); + assert(inst && "Result id of an operator is invalid"); + + // Insert negation after |inst|. + auto iter = fuzzerutil::GetIteratorForInstruction( + ir_context->get_instr_block(inst), inst); + ++iter; + + iter.InsertBefore(MakeUnique( + ir_context, SpvOpLogicalNot, inst->type_id(), inst->result_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}})); + + // Change the result id of the original operator to |fresh_id|. + inst->SetResultId(message_.fresh_id()); + + // Invert the operator. + inst->SetOpcode(InvertOpcode(inst->opcode())); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +bool TransformationInvertComparisonOperator::IsInversionSupported( + SpvOp opcode) { + switch (opcode) { + case SpvOpSGreaterThan: + case SpvOpSGreaterThanEqual: + case SpvOpSLessThan: + case SpvOpSLessThanEqual: + case SpvOpUGreaterThan: + case SpvOpUGreaterThanEqual: + case SpvOpULessThan: + case SpvOpULessThanEqual: + case SpvOpIEqual: + case SpvOpINotEqual: + return true; + default: + return false; + } +} + +SpvOp TransformationInvertComparisonOperator::InvertOpcode(SpvOp opcode) { + assert(IsInversionSupported(opcode) && "Inversion must be supported"); + + switch (opcode) { + case SpvOpSGreaterThan: + return SpvOpSLessThanEqual; + case SpvOpSGreaterThanEqual: + return SpvOpSLessThan; + case SpvOpSLessThan: + return SpvOpSGreaterThanEqual; + case SpvOpSLessThanEqual: + return SpvOpSGreaterThan; + case SpvOpUGreaterThan: + return SpvOpULessThanEqual; + case SpvOpUGreaterThanEqual: + return SpvOpULessThan; + case SpvOpULessThan: + return SpvOpUGreaterThanEqual; + case SpvOpULessThanEqual: + return SpvOpUGreaterThan; + case SpvOpIEqual: + return SpvOpINotEqual; + case SpvOpINotEqual: + return SpvOpIEqual; + default: + // The program will fail in the debug mode because of the assertion + // at the beginning of the function. + return SpvOpNop; + } +} + +protobufs::Transformation TransformationInvertComparisonOperator::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_invert_comparison_operator() = message_; + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_invert_comparison_operator.h b/3rdparty/spirv-tools/source/fuzz/transformation_invert_comparison_operator.h new file mode 100644 index 000000000..9047a1489 --- /dev/null +++ b/3rdparty/spirv-tools/source/fuzz/transformation_invert_comparison_operator.h @@ -0,0 +1,63 @@ +// 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_INVERT_COMPARISON_OPERATOR_H_ +#define SOURCE_FUZZ_TRANSFORMATION_INVERT_COMPARISON_OPERATOR_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 TransformationInvertComparisonOperator : public Transformation { + public: + explicit TransformationInvertComparisonOperator( + protobufs::TransformationInvertComparisonOperator message); + + TransformationInvertComparisonOperator(uint32_t operator_id, + uint32_t fresh_id); + + // - |operator_id| should be a result id of some instruction for which + // IsInversionSupported returns true. + // - |fresh_id| must be a fresh id. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Inverts the opcode of the instruction with result id |operator_id| (e.g >= + // becomes <) and inserts OpLogicalNot instruction after |operator_id|. Also, + // changes the result id of OpLogicalNot to |operator_id| and the result id of + // the inverted operator to |fresh_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if |opcode| is supported by this transformation. + static bool IsInversionSupported(SpvOp opcode); + + private: + // Returns an inverted |opcode| (e.g. < becomes >=, == becomes != etc.) + static SpvOp InvertOpcode(SpvOp opcode); + + protobufs::TransformationInvertComparisonOperator message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_INVERT_COMPARISON_OPERATOR_H_ diff --git a/3rdparty/spirv-tools/source/fuzz/transformation_permute_function_parameters.cpp b/3rdparty/spirv-tools/source/fuzz/transformation_permute_function_parameters.cpp index 4b9561c67..5f0660535 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_permute_function_parameters.cpp +++ b/3rdparty/spirv-tools/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/3rdparty/spirv-tools/source/fuzz/transformation_permute_function_parameters.h b/3rdparty/spirv-tools/source/fuzz/transformation_permute_function_parameters.h index 994e4c24b..830805179 100644 --- a/3rdparty/spirv-tools/source/fuzz/transformation_permute_function_parameters.h +++ b/3rdparty/spirv-tools/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/3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp b/3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp index 760d8e843..2e90a2162 100644 --- a/3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp @@ -105,13 +105,17 @@ bool AggressiveDCEPass::IsLocalVar(uint32_t varId) { IsVarOfStorage(varId, SpvStorageClassWorkgroup); } -void AggressiveDCEPass::AddStores(uint32_t ptrId) { - get_def_use_mgr()->ForEachUser(ptrId, [this, ptrId](Instruction* user) { +void AggressiveDCEPass::AddStores(Function* func, uint32_t ptrId) { + get_def_use_mgr()->ForEachUser(ptrId, [this, ptrId, func](Instruction* user) { + // If the user is not a part of |func|, skip it. + BasicBlock* blk = context()->get_instr_block(user); + if (blk && blk->GetParent() != func) return; + switch (user->opcode()) { case SpvOpAccessChain: case SpvOpInBoundsAccessChain: case SpvOpCopyObject: - this->AddStores(user->result_id()); + this->AddStores(func, user->result_id()); break; case SpvOpLoad: break; @@ -169,13 +173,13 @@ bool AggressiveDCEPass::IsTargetDead(Instruction* inst) { return IsDead(tInst); } -void AggressiveDCEPass::ProcessLoad(uint32_t varId) { +void AggressiveDCEPass::ProcessLoad(Function* func, uint32_t varId) { // Only process locals if (!IsLocalVar(varId)) return; // Return if already processed if (live_local_vars_.find(varId) != live_local_vars_.end()) return; // Mark all stores to varId as live - AddStores(varId); + AddStores(func, varId); // Cache varId as processed live_local_vars_.insert(varId); } @@ -332,6 +336,7 @@ bool AggressiveDCEPass::AggressiveDCE(Function* func) { call_in_func_ = false; func_is_entry_point_ = false; private_stores_.clear(); + live_local_vars_.clear(); // Stacks to keep track of when we are inside an if- or loop-construct. // When immediately inside an if- or loop-construct, we do not initially // mark branches live. All other branches must be marked live. @@ -454,7 +459,7 @@ bool AggressiveDCEPass::AggressiveDCE(Function* func) { uint32_t varId; (void)GetPtr(liveInst, &varId); if (varId != 0) { - ProcessLoad(varId); + ProcessLoad(func, varId); } // Process memory copies like loads } else if (liveInst->opcode() == SpvOpCopyMemory || @@ -463,7 +468,7 @@ bool AggressiveDCEPass::AggressiveDCE(Function* func) { (void)GetPtr(liveInst->GetSingleWordInOperand(kCopyMemorySourceAddrInIdx), &varId); if (varId != 0) { - ProcessLoad(varId); + ProcessLoad(func, varId); } // If merge, add other branches that are part of its control structure } else if (liveInst->opcode() == SpvOpLoopMerge || @@ -471,23 +476,23 @@ bool AggressiveDCEPass::AggressiveDCE(Function* func) { AddBreaksAndContinuesToWorklist(liveInst); // If function call, treat as if it loads from all pointer arguments } else if (liveInst->opcode() == SpvOpFunctionCall) { - liveInst->ForEachInId([this](const uint32_t* iid) { + liveInst->ForEachInId([this, func](const uint32_t* iid) { // Skip non-ptr args if (!IsPtr(*iid)) return; uint32_t varId; (void)GetPtr(*iid, &varId); - ProcessLoad(varId); + ProcessLoad(func, varId); }); // If function parameter, treat as if it's result id is loaded from } else if (liveInst->opcode() == SpvOpFunctionParameter) { - ProcessLoad(liveInst->result_id()); + ProcessLoad(func, liveInst->result_id()); // We treat an OpImageTexelPointer as a load of the pointer, and // that value is manipulated to get the result. } else if (liveInst->opcode() == SpvOpImageTexelPointer) { uint32_t varId; (void)GetPtr(liveInst, &varId); if (varId != 0) { - ProcessLoad(varId); + ProcessLoad(func, varId); } } diff --git a/3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.h b/3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.h index 131e33a26..2ce5b5770 100644 --- a/3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.h +++ b/3rdparty/spirv-tools/source/opt/aggressive_dead_code_elim_pass.h @@ -85,7 +85,7 @@ class AggressiveDCEPass : public MemPass { // Add all store instruction which use |ptrId|, directly or indirectly, // to the live instruction worklist. - void AddStores(uint32_t ptrId); + void AddStores(Function* func, uint32_t ptrId); // Initialize extensions allowlist void InitExtensions(); @@ -99,7 +99,7 @@ class AggressiveDCEPass : public MemPass { bool IsTargetDead(Instruction* inst); // If |varId| is local, mark all stores of varId as live. - void ProcessLoad(uint32_t varId); + void ProcessLoad(Function* func, uint32_t varId); // If |bp| is structured header block, returns true and sets |mergeInst| to // the merge instruction, |branchInst| to the branch and |mergeBlockId| to the